TOPに戻る

ラズパイ5 +Python+CANopenでモータを回す ③ PythonでSDOの読み出し(仮)caninfo.py

 ここでは、CANopenの規格の話題に入る前に、Pythonでモータに関する情報をちょこっと読みます。

 CANopenは、

  • アプリケーション層のベーシックな規格がCiA 301/302で規定

されています。その上には、用途によって、たくさんの規格が用意されています。この連載ではモータを回すので、

  • CiA 401(汎用I/Oモジュール)とCiA 402(モーション・コントロール;サーボ・コントローラ、ステッピング・モータ、周波数インバータ)

これらの規格を利用してアプリケーションを作ります。

 これらの規格は、具体的なオブジェクト、例えば、速度、加速度といった項目が用意されていて、オブジェクト・ディクショナリという名称で提供されます。とても詳細なパラメタが用意されているうえ、メーカ独自のパラメタもたくさんあるので、眺めるだけで、気がめいってしまいます。

 メーカの用意したコントローラのWebページに、xxx.eds(エレクトロニック・データ・シート。XMLではない)というファイルがダウンロードできるようになっています。このテキスト・ファイルの内容が、オブジェクト・ディクショナリそのものです。メーカの用意したツールにオブジェクト・ディクショナリ・ビューワが提供されていて、位置や速度といったものは最新データを見ることができます。

 コントローラBLVD-KRD用のドキュメントは英語版のみで、HP-5143E.pdfです。

  Brushless Motor BLV series R type Driver CANopen communication profile 

ID(Identifier:識別子)

 プログラミングやツールの利用時に、複数の「ID」が出てきます。物理的に機材を認識するためにNode-ID(ノードID)が使われます。モータに一つのユニークなIDをふって区別します。モータといっても実際は、コントローラBLVDです。購入時、多くの機器では'1'がふられているので、何らかのツールで変更して使います(1~127)。ここで使っているモータはサポート・ソフトのMEXE02で変更できます。モータによっては、コントローラ上のDIPスイッチで変更します。

 この連載では、1台目はNode-ID=「12(10進数)」に設定しています。

 下の図は、MEXE02の(m13) CANopen通信フレームモニタの画面の一部です。

 CAN-IDという欄があります。CAN メッセージの先頭に位置します。SDO(RX)では0x60C、SDO(TX)では0x58Cが記録されています。これは、Node-IDとは違うものです。IXXATのUSB-to_CAN V2 Compactには、canAnalyser3 Miniというユーティリティでは単にIDという表記です。

 もし、Node-IDが12でないなら、0x60Cや0x58Cは異なったCAN-IDが使われます。具体的に見ていきましょう。

 CAN-IDは11ビットで構成されています。LSBの7ビットはNode-IDです。7ビットあるので、1~127が記入できます。'12'=0xCを書き入れます(メーカの資料で16進数はXXhという表記ですが、pythonのプログラミンをする際は0xXXという16進数表記を使うので、混在している)。

 MSBの4ビットはfunctionです。

        0 0 0 1 1 0 0

 functionです。

Communication
object (COB)
詳細 Code (バイナリ表記)
NMT Network Management
(broadcast)
0000
EMCY Emergency messages 0001
TIME   0010
PDO1 (tx) (TPDO1)   0011
PDO1 (rx) (RPDO1)   0100
PDO2 (tx) (TPDO2)   0101
PDO2 (rx) (RPDO2)   0110
PDO3 (tx) (TPDO3)    0111
PDO3 (rx)  (RPDO3)   1000
PDO4 (tx) (TPDO4)   1001
PDO4 (rx) (RPDO4)   1010
SDO (tx) Parameter 1011
SDO (rx) Parameter 1100
Guarding  HERTBEAT 1110
SYNC Synchronization message
(broadcast)
0001

NMTの000とSYNCの080は、それ自身がCAN-IDで、Node-IDは使われません

(※)COB-IDは、Connection Object IDのことで、メッセージまたはフレームにサービスを割り当てた際にメッセージの識別に使われます。
 COが、Connectionと表示されている場合とCommunicationと表示される場合があります。唯一の日本語書籍「CANおよびCANopenによる組み込みネットワーク」でも、前半はConnection、後半ではCommunicationが使われています。日本語ではCOB-IDでCは「通信」と訳されているようです。
 
 COB-IDをCAN-IDと同じとみなす説明を見ます。また、COB-IDは、CAN-IDに追加の制御ビットを含む32ビットのコードという説明も見ます。
 
 Online CANopen COB-ID converterを使って表示した具体例です。
 

60Cは、

COB-ID (dec)    1548
COB-ID (bin)    11000001100
#bits    11

Function Code (bin)    1100

Node ID (bin)    0001100
Node ID (hex)    c
Node ID (dec)    12

Communication Object    Receive SDO

58Cは、

COB-ID (dec)    1420
COB-ID (bin)    10110001100
#bits    11

Function Code (bin)    1011

Node ID (bin)    0001100
Node ID (hex)    c
Node ID (dec)    12

Communication Object    Transmit SDO

 Analog Devices TCML-IDEで表示した具体例です。

環境

  • ハードウェア Raspberry Pi 5(4GBモデル)
  • OS Raspberry Pi OS (64ビット)、リリース日December 5th 2023
  • ラズパイ環境 Python 3.11.2
  • Windows10 22H2にて、ssh(OpenSSH_9.2p1 Debian-2+deb12u2, OpenSSL 3.0.11 19 Sep 2023)および、VNC Viewerを動作させている
  • Windows環境 Python 3.10.5

pythonのライブラリを利用する

 ここでは、pythonのcanopenライブラリを使って、オブジェクト・ディクショナリのデータのいくつかを読み取ります。

 CANopenでは、各種設定/読み出しを行うために、

  • コントローラからの要求によって送受信されるSDO(Service Data Object)を使って通信
  • センサ側が自発的、同期的に配信するPDO(Process Data Object)を用いて通信

の2系統があります。SDOでは、設定項目がすべて読み出せます。書き込める項目もあります。PDOでは、登録してある送信と受信のパラメタ、例えば、現在位置、現在のスピードなどが読み出せ、リアルタイムな動作に利用されます。

 なぜ2種類あるかというのは、オブジェクト・ディクショナリのオブジェクト数が膨大で、アクセスがポーリングで行うと、とても時間がかかります。MEXE02では、USB経由でポーリングしているので、瞬間に全部のデータを読み出せます。Maxonのツールでは、CANバス経由でポーリングするので、全部読み出すのに分単位かかります。

 そこで、常に最新データを見たいものを登録しておくのがPDOという仕組みにした?と思われます。

 CANopenは同期通信です。例えば、syncのパラメタに100msを設定しておくと、PDOのパラメタは確実に100msごとに読み出せます。

 ここでは、SDOのいくつかの項目を、pythonで読み出します。まだモータを回転するようなレベルのプログラムではありません。

canopenのインストールと実行<Windows>

 赤色の経路でアクセスします。

 Windowsのコマンドプロンプトを起動します。canopenライブラリをインストールします。

>pip install canopen

>python -m pip freeze | findstr canopen
canopen==2.2.0

 次のプログラムをcaninfo.pyで保存します。

 モータのNode-IDは12にサポート・ソフトのMEXE02(後述)で設定しました。デフォルトは1だと思うので、変更しないときは、Node-IDを1でアクセスしてください。複数のモータが同じCANバスにつながるとき、Node-IDは、全部異なる必要があります。

 BLVD-KRD_CANopen_V200.edsは、https://www.orientalmotor.co.jp/ja/products/detail?hinmei=BLVD-KRDからダウンロードし、プログラムと同じディレクトリに保存します.

 次の記述では、CANのインターフェースにIXXATのUSB-to_CAN V2 Compactを利用したときの記述です。kvaser社であれば、bustype='kvaser'になります。この後のラズパイの場合、このconnectの記述1行だけが異なります。

# Connect to the CAN bus
network.connect(bustype='ixxat', channel=0, bitrate=250000)

 以降、edsファイル=オブジェクト・ディクショナリに登録されている値をSDOで読み出しています。indexが0x1018で、subが1にvendor idが登録されています。この1018はCiA 301/302で規定されていて、各社必須なオブジェクトなので、必ず読み出せます。

vendor_id = node.sdo[0x1018][1].raw

 この温度はこのコントローラ特有のオブジェクトなので、他社は、別のindexになっていると思われます。

Motor_temp    = node.sdo[0x407d].raw

 このオブジェクトは、モーションでは必要なので、各社共同じindexになっています。

Target_position    = node.sdo[0x607a].raw

import canopen
import time
import logging
#logging.basicConfig(level=logging.DEBUG)

# Start with creating a network representing one CAN bus
network = canopen.Network()

# Add some nodes with corresponding Object Dictionaries
node = canopen.RemoteNode(12,'BLVD-KRD_CANopen_V200.eds')
network.add_node(node)

network.check()

# Connect to the CAN bus
network.connect(bustype='ixxat', channel=0, bitrate=250000)

print("\n===start  ID=12 OrientalMotor\n")
vendor_id = node.sdo[0x1018][1].raw
print("vendor_id " ,vendor_id) 
device_name    = node.sdo[0x1008].raw
print('Manufacturer device name ',device_name)
Motor_temp    = node.sdo[0x407d].raw
print('Motor_temp  ',float(Motor_temp/10.0))
print("\n #6000 \n") 
Max_torqe    = node.sdo[0x6072].raw
print('Max torqe',Max_torqe)
Target_position    = node.sdo[0x607a].raw
print('Target position',Target_position)

# Disconnect from CAN bus
network.disconnect()

 実行した様子です。

canopenのインストールと実行<ラズパイ>

 赤色の経路でアクセスします。

 アマゾンで購入したCAN HATで使われているmcp2515のデバイス・ドライバを組み込みます。/boot/firmware/config.txtの最後に、root権限で次の1行を追加してrebootします。

dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25

 2023年10月に更新されたラズパイOS bookwormでは、pythonは仮想環境を利用してプログラムを作ります。

python仮想環境 備忘録

仮想環境の作成

python -m venv envtest

有効化

source envtest/bin/activate

無効化

deactivate

初期状態に(いったん deactivate してから)

python -m venv --clear envtest

 

 envcanという仮想環境を作ります。

yoshi@ras05:~ $ python -m venv envcan

yoshi@ras05:~ $ source envcan/bin/activate

(envcan) yoshi@ras05:~ $ pip install canopen
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting canopen
  Using cached https://www.piwheels.org/simple/canopen/canopen-2.2.0-py3-none-any.whl (61 kB)
Collecting python-can>=3.0.0
  Using cached https://www.piwheels.org/simple/python-can/python_can-4.3.1-py3-none-any.whl (262 kB)
Collecting wrapt~=1.10
  Using cached wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (80 kB)
Collecting packaging>=23.1
  Using cached https://www.piwheels.org/simple/packaging/packaging-24.0-py3-none-any.whl (53 kB)
Collecting typing-extensions>=3.10.0.0
  Downloading https://www.piwheels.org/simple/typing-extensions/typing_extensions-4.11.0-py3-none-any.whl (34 kB)
Collecting msgpack~=1.0.0
  Using cached msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (400 kB)
Installing collected packages: wrapt, typing-extensions, packaging, msgpack, python-can, canopen
Successfully installed canopen-2.2.0 msgpack-1.0.8 packaging-24.0 python-can-4.3.1 typing-extensions-4.11.0 wrapt-1.16.0

(envcan) yoshi@ras05:~ $ pip list
Package           Version
----------------- -------
canopen           2.2.0
msgpack           1.0.8
packaging         24.0
pip               23.0.1
python-can        4.3.1
setuptools        66.1.1
typing_extensions 4.11.0
wrapt             1.16.0

 次のプログラムをcaninfo.pyの名称で保存しました。

import canopen
import time
import logging
#logging.basicConfig(level=logging.DEBUG)

# Start with creating a network representing one CAN bus
network = canopen.Network()

# Add some nodes with corresponding Object Dictionaries
node = canopen.RemoteNode(12,'Downloads/BLVD-KRD_CANopen_V200.eds')
network.add_node(node)

network.check()

# Connect to the CAN bus
# sudo ip link set can0 up type can bitrate 1000000
network.connect(bustype='socketcan', channel='can0')

print("\n=== start  ID=12 OrientalMotor ===\n")
vendor_id = node.sdo[0x1018][1].raw
print("vendor_id " ,vendor_id) 
device_name    = node.sdo[0x1008].raw
print('Manufacturer device name ',device_name)
Motor_temp    = node.sdo[0x407d].raw
print('Motor_temp  ',float(Motor_temp/10.0))

print("\n #6000 \n") 
Max_torqe    = node.sdo[0x6072].raw
print('Max torqe',Max_torqe)
Target_position    = node.sdo[0x607a].raw
print('Target position',Target_position)

# Disconnect from CAN bus
network.disconnect()

 実行します。最初に、ip link setコマンドでデバイスcan0の状態をUPに設定します。

(envcan) yoshi@ras05:~ $ sudo ip link set can0 up type can bitrate 1000000

(envcan) yoshi@ras05:~ $ python caninfo.py

=== start  ID=12 OrientalMotor ===

vendor_id  702
Manufacturer device name  BLVD-KRD
Motor_temp   33.5

 #6000 

Max torqe 10000
Target position 0

◆デバイス・ドライバの組み込みが正常に行われたかを確認。reboot後。

(envcan) yoshi@ras05:~ $ dmesg | grep mcp251x
[    2.881045] mcp251x spi0.0 can0: MCP2515 successfully initialized.

◆can0がネットワークデバイスに登録され、起動状態になったかを確認

(envcan) yoshi@ras05:~ $ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 2c:cf:67:12:09:42 brd ff:ff:ff:ff:ff:ff
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
    link/can 
4: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DORMANT group default qlen 1000
    link/ether 2c:cf:67:12:09:43 brd ff:ff:ff:ff:ff:ff

 このプログラムは、何らかのエラーが起こったとに実行しても動かない可能性があります。コントローラBLVD-KRDの電源を入れた直後、PWR/SYS LEDが白色で点滅している状態で動かしてください。上記のプログラムの実行後は青色点滅(インフォメーションが発生)です。この状態でも、再度プログラムは正常に動作します。

 隣にあるCOMM LEDは、白色の点灯、点滅であればエラーは起こっていません。

 SDOの読み出しなどでは、timeoutやそのほかのエラーになる場合がありますが、それらの対応は全くしていません。

 本連載は、pythonを利用します。C++でも、同様にプログラムを記述できるようなライブラリは、コントローラBLVD-KRDには用意されていません。けれど、ステータスを読み、コントロール・ワードを設定する関数を作っていけば、実装可能だと思います。

 たとえば、MaxonのコントローラEPOS4やKvaser の製品には、そのようなライブラリやAPIが用意されています。

 MEXE02は、CANopenのプログラミングに必要なモニタ類、リモート運転、I/Oの入出力信号をテスト画面などが用意されていますが、プログラム自体は記述できないようです。

 プロトコルはModbusを利用し、「BLV シリーズ R タイプ 取扱説明書 機能編」に、細かな入出力の設定をして、ダイレクト・データ運転、ストアード・データ運転など、細かな制御方法が解説されています。Modbus自体はデータのフォーマットや通信手順だけの規格なので、CANopenのCiA 402のようなものはないと思われます。したがって、メーカが独自に実装したのかもしれません。

オブジェクト・ディクショナリ

 無料でダウンロードできるサポート・ソフトMEXE02には、オブジェクト・ディクショナリを表示する機能があります。

 開くと、index数値の低い項目が見れます。今回読み出したVendor IDが見れますね。

 読み出しだけができるもの、読み書きのできるものがあります。

 値は、UINT8、UINT16、INT32などの型と取りうる桁数が決まっています。

 それぞれの項目には単位がありますが、ここでは表示されていません。

 1000番台はCommunication Objectsと呼ばれていて、低めの数値の項目はメーカや機種の情報が書かれています。1400~1a03hがPDOのマッピング・パラメタで各社が使っている共通な領域です。

 2000番台、3000番台、もしくはこの機種のように4000番台が、Manufacturer Specific Objectsで、メーカが独自に割り当てている機能のパラメタです。

 スクロールしていくと、6000番台、6041hのStatuswordは、今後頻繁に利用します。6064hのPosition actual valueはリアルタイムに更新されるロータリ・エンコーダから得られる現在位置です。モータは動いてはいませんが、分解能が高いので、数字がちらちら変化します。

 この6000番台は、モータの運転パラメタが用意されていて、各社ほぼ同じになっています。

 次回詳しく説明します。

資料

<モータ> https://www.orientalmotor.co.jp/ja/products/detail?hinmei=BLVD-KRD

 下記の四つのファイルをダウンロードします。

<MEXE02> https://www.orientalmotor.co.jp/ja/products/detail?hinmei=MEXE02

<canopenライブラリ> https://github.com/christiansandberg/canopen

連載 ラズパイ5 +Python+CANopenでモータを回す

(1) 構成と環境(オリエンタルモーター の「BLMR5100K-A-B」 + 「BLVD-KRD」)

(2) サポート・ソフト MEXE02

(3) PythonでSDOの読み出し(仮)caninfo.py

(4) CANopenのベーシックな規格とSDO/PDO、オブジェクト・ディクショナリ<前編>

(5) CANopenのベーシックな規格とSDO/PDO、オブジェクト・ディクショナリ<後編>

(6) 二つの状態遷移(NMTとStatus Machine)

(7) CANバス信号を見る<前編>canreset.py candump0.py

(8) CANバス信号を見る<中編>Arduino

(9) CANバス信号を見る<後編>CANopenのデコードができ無償で使えるツールAnalog Devices TMCL-IDE

(10) モーション CiA 402の規格<Homing mode> canHome.py

(11) モーション CiA 402の規格<Profile Position Mode (PPM)-前編> canPPMread.py

(12) モーション CiA 402の規格<Profile Position Mode (PPM)-中編> caninfo2.py canppm.py

(13) モーション CiA 402の規格<Profile Position Mode (PPM)-後編> canppm2.py

(14) モーション CiA 402の規格<Profile Velocity Mode (PVM)> campvm.py