macOS Catalina/Monterey で PyVISA/PySerial を使う、に加えて初心者向けの丁寧な解説

macOS Catalina もしくは Monterey で PyVISA と PySerial を使うときの覚え書きです。PyVISA のほうは普通にやると、はまります。詳しい人向けの結論を先に書くと、NI-GPIB を先に入れないと NI-VISA が入らない、です。

※(2022/4/15 更新)Monterey の場合を加筆しました。

環境

  • macOS Catalina 10.15.5
  • Python 3.7.6(brew install python で)
  • PyVISA 1.10.1(pip3 install pyvisa で)
  • PySerial 3.4(pip3 install pyserial で)
  • NI-GPIB 19.5
  • NI-VISA Runtime 19.0.0

  • macOS Monterey 12.2.1

  • Python 3.7.7
  • PyVISA 1.11.1(pip3 install pyvisa で)
  • NI-GPIB 21.5
  • NI-VISA Runtime 21.0.0

経緯

以前にこういう記事を書いたのですが、かなり情報が古くなってしまいました。当時は 32 bit と 64 bit が混在している時代で、Python も 32 bit だったり、National Instruments も Mac 用のドライバの配布が遅かったりと、苦労していました。 oxon.hatenablog.com Mojave から Catalina 環境に移行するにあたり、少し PyVISA ではまったので、記録に残しておきます。

うちの学生向けに書くので、比較的丁寧です。

VISA とは

VISA(Virtual Instrument Software Architecture) というのは、いろいろな実験室の計測装置を共通のソフトウェアで簡単に使えるようにしましょうという規格です。「査証」やクレジットカードの VISA とは関係がありません。

en.wikipedia.org

実験室にある計測装置の背面を見てみると、電源ケーブル以外に色々なケーブルを接続できることに気がつきます。古い機種だと RS-232C と呼ばれる規格の端子がついています(シリアルポートとも呼ばれます)。これは 9 本の剥き出しのピン(オスの場合)だったり、9 本のピンが差し込めるようになっているメスのポートだったりします。昔は計算機側にもこのポートが 2 つずつ付いていることが多かったのですが、今どきは付いていません。しかし実験室では、特にデータ転送速度を重視しない機器(低圧電源や回転ステージなど)ではいまだに使われることが多いです。

ja.wikipedia.org

少し時代が経ち、オシロスコープのようにデータ量の多い装置では GPIB(IEEE の規格は IEEE 488)というケーブルの規格が使われるようになりました。最近はあまり見かけませんし、元々 GPIB 搭載の可能な機種でもオプション扱いのものが多かったと思うので、20〜30 年前くらいの高いデジタルオシロスコープなんかに付いていたりします(安いものにもついていたと思いますが、30 年前の安いものは既に廃棄処分されていると思います)。見た目がゴツくて格好いいやつです。写真はリンク先を参照。

ja.wikipedia.org

最近だと、USB 端子がつく機種も増えました。むしろ新しい機種で USB すら付いていない製品やそんなものを販売する会社は、手を出さないほうがいいんじゃないかという気がします。USB 1.1(最大 12 Mbps)は低速の用途にも良いですし、USB 2.0(最大 480 Mbps)ならオシロスコープのようなデータ量の多い装置にも十分耐えられます。USB 3.0(最大 5 Gbps)以降を使用している機種は、2020 年現在、まだ多くないと思います。

ja.wikipedia.org

オシロスコープのように通信量が大きい装置は、USB ではなく Ethernet ポートを備えたものもあります。機種によって 100 Mbps だったり 1 Gbps だったり 10 Gbps だったり、様々です。自分が実験で使う場合は USB 2 より Ethernet のほうが転送速度が早いため Ethernet を使いますが、この記事で説明するにはネットワークの設定の説明が面倒なのと、実験室ごとに設定がいろいろ変わるので、Ethernet の説明は省き USB 中心で説明します。

ja.wikipedia.org

さて、VISA の話に戻ると、こういう異なるケーブルや通信プロトコルごとにソフトウェアを書き換えるのは面倒なので、一括で同じように扱えるようにしようというのが、VISA です。例えば何か計算機からコマンドを周辺機器に送るとき、もしくは周辺機器から計算機にデータが送られてくるとき、VISA 経由でやれば、どのケーブルやプロトコルを使用しているのかはほとんど気にしなくて良くなります。

PyVISA とは

PyVISA は、Python から VISA に対応した周辺機器とやり取りするための機能を提供してくれます。macOS Catalina に Homebrew で Python 3 を導入している場合、次のように pip3 コマンドで PyVISA をインストールします。

$ pip3 install pyvisa

後述する NI-GPIB や NI-VISA のインストールされた環境では、この PyVISA を使うことによって周辺機器と簡単に通信することができるようになります。次の例は、IPython から Tektronix の MSO4102B というオシロスコープに接続してシリアルナンバーなどの情報を得ているところです。

$ ipython
In [1]: import visa                                                                     
In [2]: rm = visa.ResourceManager()                                                     
In [3]: rm.list_resources()                                                             
Out[3]: ('USB0::0x0699::0x041B::C022756::INSTR',)
In [4]: usb = rm.open_resource(rm.list_resources()[0])                                  
In [5]: usb.query('*IDN?')                                                              
Out[5]: 'TEKTRONIX,MSO4102B-L,C022756,CF:91.1CT FV:v2.90 \n'

RS-232C での通信の場合は、ボーレート(baud rate)やデリミター(delimiter)などを気に掛ける必要がありましたが、VISA なら勝手にうまいことやってくれます。

デバイスドライバの部分は、PyVISA 自身はやっていません。macOS の場合、PyVISA は National Instruments(NI)の NI-VISA というデバイスドライバーをバックエンドとして使用しています。

PyVISA を macOS Catalina/Monterey に導入する手順

バージョン番号は適宜最新のもので読み替えてください。

0. Monterey の場合

macOS Monterey から、NI の機能拡張を使うには一手間かかるようになったので、事前にこの作業をします。この作業をすることで、macOS に NI を信頼できる開発者として認識させます。

まず Mac を再起動し、その際に Cmd-R を長押ししてリカバリーモード(recovery mode)に入ります。その後、Tools メニューから Terminal を開き、次のコマンドを実行します。

spctl kext-consent add SKTKK2QZ74

この SKTKK2QZ74は NI の developer ID です。

spctl kext-consent list

で、SKTKK2QZ74 が表示されるのを確認し、再起動しましょう(そのまま reboot と入力・実行すれば良い)。

1. NI-GPIB(NI-488.2)を入れる

PyVISA を使うには NI-VISA が必要で、NI-VISA を Catalina に入れるには NI-GPIB を先に入れる必要があります。リンク先から、Supported OS に MacOS、Version に 19.5 を選択してディスクイメージ(拡張子 .dmg)をダウンロードします。

www.ni.com

NI-488.2 19.5.dmg を開くと NI-488.2 19.5.pkg が現れるので、これを開き、あとは指示に従ってインストールします。これは GPIB を Mac で使用するための NI 社製のデバイスドライバーです。再起動が必要です。

macOS 10.13 と 10.14 しか対応していないとなっていますが、10.15 で動作します。

もしも .pkg を開けないとダイアログが出てきた場合、右クリックから開くようにすると次のダイアログが出るので、Open を選択してください。

f:id:oxon:20200609155806p:plain

2. NI-VISA Runtime を入れる

次に、NI-VISA を入れます。同様に Mac 版の 19.0 を入れます。この際、Included Editions は Runtime にすれば十分です。Full でも良いですが、PyVISA だけの使用であれば不要です。 www.ni.com

NI-VISA_Runtime_19.0.0.dmg を開くと NI-VISA_Runtime_19.0.0.pkg が現れるので、これも同様にインストールしてください。再起動は多分必要ありません。

3. PyVISA を入れる

Homebrew 環境の場合、pip から入れてください。Python 3 であれば pip3 です。

$ pip3 install pyvisa  

4. インストールの確認

PyVISA とともに pyvisa-info というコマンドがインストールされるので、これが /Library/Frameworks/visa.framework/visa にある NI-VISA を自動的に検出できていれば成功です。

$ pyvisa-info      

Machine Details:
   Platform ID:    Darwin-19.5.0-x86_64-i386-64bit
   Processor:      i386

Python:
   Implementation: CPython
   Executable:     /usr/local/opt/python/bin/python3.7
   Version:        3.7.6
   Compiler:       Clang 11.0.0 (clang-1100.0.33.16)
   Bits:           64bit
   Build:          Dec 30 2019 19:38:26 (#default)
   Unicode:        UCS4

PyVISA Version: 1.10.1

Backends:
   ni:
      Version: 1.10.1 (bundled with PyVISA)
      #1: /Library/Frameworks/visa.framework/visa:
         found by: auto
         bitness: 64
         Vendor: National Instruments
         Impl. Version: 19922944
         Spec. Version: 5244928

もし abort したというエラーが出る場合、System Preferences の Security & Privacy で NI 製のソフトウェアがブロックされていないか確認してください。もしこの画像のようにブロックされている場合、鍵を外して Allow を押して、許可してやりましょう。 f:id:oxon:20200616145248p:plain

5. 動作確認

先述したコマンド例の繰り返しになりますが、VISA に対応しているはずの適当な実験装置を USB ケーブルで Mac に接続し、次のように真似して *IDN? コマンドを送信してみましょう。普通の装置は後述の SCPI というコマンド体系に準拠しているはずで、ほとんどの装置は *IDN? コマンドに対して機種情報を返します。

$ ipython
In [1]: import visa                                                                     
In [2]: rm = visa.ResourceManager()                                                     
In [3]: rm.list_resources()                                                             
Out[3]: ('USB0::0x0699::0x041B::C022756::INSTR',)
In [4]: usb = rm.open_resource(rm.list_resources()[0])                                  
In [5]: usb.query('*IDN?')                                                              
Out[5]: 'TEKTRONIX,MSO4102B-L,C022756,CF:91.1CT FV:v2.90 \n'

これで、Tektronix の MSO4102B-L という機種でシリアルナンバー(S/N)C022756 のものが繋がっているというのが分かります。ファームウェアのバージョンがおそらく 2.90 なのだと思います。

SCPI

さて、周辺機器の接続情報だけ得ても面白くないので、*IDN? 以外の SCPI について調べましょう。Wikipedia の記事にもあるように、SCPI は実験室で使うような測定装置とやり取りするための標準的なコマンドの文法の規格です。メーカーが異なっても、例えば Tektronix でも Keithley でも岩通でも、SCPI で制御できる装置は似たようなコマンドで動作させることができます。

en.wikipedia.org

例えば上述の Tektronix のオシロスコープの電圧表示範囲を変更してみましょう。文法を調べるには、例えば「MSO4102 programmer's manual」などで Google 検索すると PDF が出てくるはずです。次のページが出てきたので、PDF をここから入手しましょう。

jp.tek.com

マニュアルを読むと、このような説明が出てきます。

CH<x>? Returns vertical parameters for the specified channel

それでは、実際にやってみましょう。ここで注意するのは、<x> の部分は適切な値に置き換えよという意味です。? が最後についているのは、問い合わせのコマンドということです。つまり、返り値があります。

In [6]: usb.query('CH1?')                                                       
Out[6]: '0;10.0000;"No probe detected";"";1.0000;"V";50.0000;"Other";0.0E+0;0.0E+0;1.0000E+9;DC;0.0E+0;0.0E+0;0;-2.9600;1.0000;"V";50.0000;""\n'

この読み方はマニュアルを参照するとして、例えば縦軸の分割幅は 1.0000 V であり、オフセットが -2.9600 V だということが分かります。カップリングは DC で終端は 50.0000 Ω です。

次に、電圧のオフセットを変更してみましょう。マニュアルによると、文法は CH<x>:OFFSet <NR3> および CH<x>:OFFSet? です。ここで小文字は省略可能という意味です。また <NR3>2.0E-3 などの指数表示を意味します。(<NR1> は整数値、<NR2> は小数です。)

In [15]: usb.write('CH1:OFFS 1.23E-1')                                                                                                                          
Out[15]: (18, <StatusCode.success: 0>)
In [16]: usb.query('CH1:OFFS?')                                                                                                                                 
Out[16]: '123.0000E-3\n'
In [17]: voffset_ch1 = float(usb.query('CH1:OFFS?')[:-1])                                                                                                       
In [18]: voffset_ch1
Out[19]: 0.123
In [20]: usb.write('CH1:OFFS %.2E' % voffset_ch1)

動作確認時を除いて、実際にプログラム中に書くときは省略せずに全て書くことをお勧めします。コマンドの可読性が増すからです。

次に波形を取得してみましょう。簡単のため、データフォーマットは ASCII にします。

In [31]: usb.write('WFMOutpre:ENCdg ASCii')
In [32]: usb.write('DATA:SOURCE CH1')
In [33]: data = usb.query('WAVFRM?')
In [34]: data[:250]                                                                                                                                             
Out[34]: '2;16;ASCII;RI;MSB;"Ch1, DC coupling, 1.000V/div, 4.000ms/div, 1000000 points, Sample mode";1000000;Y;LINEAR;"s";40.0000E-9;-20.0000E-3;0;"V";156.2500E-6;29.6960E+3;123.0000E-3;TIME;ANALOG;0.0E+0;0.0E+0;0.0E+0;27648,27904,27392,27648,27648,27904,27648'

あとは、マニュアルを読んでこのデータを波形情報に戻してみましょう。

PySerial

USB 端子を持っていても、VISA に対応していない機器があります。その多くは USB 端子の後ろに USB to Serial converter と呼ばれる、USB とシリアル通信(RS-232C)を変換するチップが搭載されています。このような機器は PyVISA ではなく PySerial を使って制御します。

また、USB 端子ではなく RS-232C 端子しかない機器もあるでしょう。これには、USB-RS232C の変換ケーブルを使用します。変換チップには様々なメーカーのものが存在しますが、FTDI 社製のものが一般的です。また FTDI 製であれば macOS でも Windows でも Linux でもデバイスドライバーのインストールなしに動作しますので、例えば Buffalo のこのケーブルは確実に動作します。

Mac に FTDI 製のチップを持つ周辺機器、もしくは FTDI 製チップの使われている変換ケーブルで RS-232C の機器を接続したときは、/dev/ 以下にデバイスファイルが作成されます。

例えば Keithley 社製のある機器の場合、FTDI のチップが使われているため次のように表示されます。表示されるデバイスファイル名はチップのシリアルナンバーにしたがって異なるものになります(Mac の場合)。

$ system_profiler SPUSBDataType
(略)
    USB 3.0 Hi-Speed Bus:

      Host Controller Location: Built-in USB
      Host Controller Driver: AppleUSBXHCI
      PCI Device ID: 0x1e31 
      PCI Revision ID: 0x0004 
      PCI Vendor ID: 0x8086 
      Bus Number: 0x14 

        USB HS SERIAL CONVERTER:

          Product ID: 0x6001
          Vendor ID: 0x0403  (Future Technology Devices International Limited)
          Version: 4.00
          Serial Number: FT123456
          Speed: Up to 12 Mb/sec
          Manufacturer: FTDI
          Location ID: 0x14100000 / 1
          Current Available (mA): 500
          Current Required (mA): 44
$ ls /dev/tty.usbserial*
/dev/tty.usbserial-FT123456

PySerial を入れると、同様に SCPI のコマンドで制御することができるようになります。

import serial
keithley = serial.Serial(port="/dev/tty.usbserial-FT123456",
                         baudrate=57600,timeout=1,writeTimeout=1)

keithley.write('*RST\n')
keithley.write(':SENS:FUNC "VOLT"\n')
keithley.write(':SOUR:FUNC VOLT\n')
keithley.write(':OUTP ON\n')

ここで、RS-232C の装置の場合は baudrate という値の設定をその機種ごとに揃えて必要があります(今の例の場合、57600)。装置のマニュアルに必ず書いてあるはずですので、読むようにしましょう。また使用する改行コードも機種ごとに異なるため、今の場合は \n を使用していますが、これもマニュアルで確認してください。