CANopen 後編 ―― 仮想サーボを回す(3/5)

  • canopen
  • python
  • サーボ
  • 産業用通信
  • 組み込み

前編では CANopen の配管、NMT・SDO・PDO・SYNC を一通り通しました。今回はその配管に中身を流します。

サーボモータを回します。といっても実物は使いません。PCの中に、規格どおりに振る舞うサーボを1台作ります。

この記事で作るもの

仮想サーボを規格の手順どおりに起動し、位置指令を与えて追従させ、そのカーブをグラフにします。

  起動直後                               Statusword = 0x0250  ->  Switch on disabled
  Controlword = 0x06 (Shutdown)      Statusword = 0x0231  ->  Ready to switch on
  Controlword = 0x07 (Switch on)     Statusword = 0x0233  ->  Switched on
  Controlword = 0x0F (Enable operation) Statusword = 0x0237  ->  Operation enabled
  SDO write 0x6060 = 8  ->  0x6061 (実際のモード) = 8  (csp)
  t=0.101s  target= 10000  actual=  6229  status=0x0237
  t=0.302s  target= 10000  actual=  9854  status=0x0237
  t=0.591s  target= 10000  actual=  9999  status=0x0237

0x0F を書いた行で通電し、そこから位置が目標に寄っていきます。

使うもの(すべてオープンソース・費用ゼロ・ハード不要)

ライセンス
python-can 4.6.1 LGPL-3.0-only
matplotlib 3.10.7 PSF-based
./.venv/bin/pip install "python-can[multicast]==4.6.1" matplotlib==3.10.7

コードは3本に分かれています。実行するのは3つ目です。

ファイル 中身
02_canopen/cia402.py Controlword / Statusword の定義と状態機械
02_canopen/servo.py 仮想サーボ(SDO サーバ・PDO・SYNC 追従)
02_canopen/run_servo_demo.py マスタ側。これを実行します
./.venv/bin/python 02_canopen/run_servo_demo.py

グラフの見た目はリポジトリ直下の figstyle.py に切り出してあります。


① CiA 402 とは — サーボの共通語

前編で「0x6000 番台は規格で決まっている」と書きました。それを決めているのが CiA 402 で、サーボに必要な操作に番号を振って標準化したものです。

CiA 402 が動かすサーボ軸
CiA 402 が動かすのは、この実物です。サーボモータとドライブ(CANopen ノード)、回転する負荷が軸でつながっています。Controlword で命じ、Statusword で状態を受け取り、Target position で位置を指令します。

番号 名前 役割
0x6040 Controlword サーボに「こうしろ」と命じる(16bit)
0x6041 Statusword サーボが「今こうです」と答える(16bit)
0x6060 Modes of operation 動作モードの指定
0x6064 Position actual value 現在位置
0x607A Target position 目標位置
0x60FF Target velocity 目標速度
0x6071 Target torque 目標トルク

利点は、メーカーが違っても同じ番号に同じものが入っていることです。各社が公開しているサーボの取扱説明書を見ると、通電までの手順は共通しています。

安川のサーボでも、Beckhoff のサーボでも、0x6040 に 0x06、0x07、0x0F と順に書く形です。だからモータを別メーカーに載せ替えても、マスタ側のコードはほぼそのまま動きます。


② 状態機械と動作モード

いきなり回らない

CiA 402 のサーボは、電源を入れてもすぐには動きません。決められた順番で状態を進めないと通電しません。安全のためです。

本編では、動かすために最低限必要な3ステップだけ扱います。

stateDiagram-v2
    [*] --> SOD
    SOD : Switch on disabled
    RTSO : Ready to switch on
    SW : Switched on
    OE : Operation enabled

    SOD --> RTSO : Controlword = 0x06
    RTSO --> SW : Controlword = 0x07
    SW --> OE : Controlword = 0x0F
    note right of OE : ここで初めて通電

Operation enabled に入って初めて、モータに電流が流れます。

完全な状態遷移図(Quick Stop / Fault 系を含む)

実際には Quick stop active、Fault reaction active、Fault、Not ready to switch on を含む8状態あります。本文では扱いませんが、実機では必要になります。

stateDiagram-v2
    [*] --> NRSO
    NRSO : Not ready to switch on
    SOD : Switch on disabled
    RTSO : Ready to switch on
    SW : Switched on
    OE : Operation enabled
    QSA : Quick stop active
    FRA : Fault reaction active
    F : Fault

    NRSO --> SOD : 自動
    SOD --> RTSO : 0x06 Shutdown
    RTSO --> SW : 0x07 Switch on
    SW --> OE : 0x0F Enable operation
    OE --> SW : 0x07 Disable operation
    OE --> RTSO : 0x06 Shutdown
    RTSO --> SOD : 0x00 Disable voltage
    SW --> SOD : 0x00 Disable voltage
    OE --> SOD : 0x00 Disable voltage
    OE --> QSA : Quick stop (bit2 = 0)
    QSA --> SOD : 自動(停止完了)
    QSA --> OE : 0x0F(許可時のみ)
    OE --> FRA : Fault
    FRA --> F : 自動
    F --> SOD : 0x80 Fault reset
    note right of F : Fault はどの状態からでも入りうる

Controlword のビット

bit 0 : Switch on
bit 1 : Enable voltage
bit 2 : Quick stop      ← アクティブ Low(0 で急停止が発動)
bit 3 : Enable operation
bit 7 : Fault reset     ← 立ち上がりエッジで作用

先ほどの3つの値を分解すると:

bit3 bit2 bit1 bit0 コマンド名
0x06 0 1 1 0 Shutdown
0x07 0 1 1 1 Switch on
0x0F 1 1 1 1 Enable operation

bit2(Quick stop)はアクティブ Low で、私は最初ここを読み違えました。1 が「急停止していない」を表すので、0x06 にも 0x07 にも bit2 が立っています。

同じ 0x07 が、2つの意味を持つ

CiA 402 を読んでいて面白かったのがここです。0x07 は "Switch on" であり、同時に "Disable operation" でもあります。

  • Ready to switch on で 0x07 を書くと Switched on に進む(投入)
  • Operation enabled で 0x07 を書くと Switched on に戻る(停止)

同じ16ビットのパターンが、受け取った側の状態によって正反対の意味になります。第1回の Modbus では「番地に何が入っているかが線の上に書かれていない」という話でしたが、CiA 402 では意味そのものが文脈の中にあり、プロトコルを読むときはその文脈も一緒に追うことになります。

Statusword の読み方

サーボは 0x6041 で状態を返します。

bit 0 : Ready to switch on
bit 1 : Switched on
bit 2 : Operation enabled
bit 3 : Fault
bit 4 : Voltage enabled
bit 5 : Quick stop        ← アクティブ Low
bit 6 : Switch on disabled
bit 10: Target reached

状態の判定表:

bit6 bit5 bit3 bit2 bit1 bit0 状態
1 x 0 0 0 0 Switch on disabled
0 1 0 0 0 1 Ready to switch on
0 1 0 0 1 1 Switched on
0 1 0 1 1 1 Operation enabled
0 0 0 1 1 1 Quick stop active
0 x 1 0 0 0 Fault

この表が見ているのは下位7ビットだけです。実例の 0x02500x0237 にある上位バイト 0x02 は bit9(Remote。この実装では常に 1)で、bit4(Voltage enabled)も常に 1 なので、実際に返る Statusword は表の値に 0x200 と 0x10 が乗ります。

動作モード — 軌道を誰が作るか

0x6060 に書く番号でモードを選びます。

略号 名前
1 pp Profile position
3 pv Profile velocity
6 hm Homing
8 csp Cyclic synchronous position
9 csv Cyclic synchronous velocity
10 cst Cyclic synchronous torque

「csp は位置制御、csv は速度制御」という説明を見かけますが、それだけだと pp / pv との違いが残ります。私が分けやすいと感じたのは「軌道を誰が作るか」という軸のほうでした。

軌道生成 マスタがやること
pp / pv サーボの中 「そこまで、この速度で行け」と1回言う
csp / csv / cst マスタ側 毎周期、次の1点を送り続ける

csp は、マスタが「1ms 後はここ、その次はここ」と点を打ち続け、サーボはひたすら追従します。周期が確実に来ることが前提です。


③ 手を動かす

構成は前編と同じ、1プロセスの中にマスタ役(メイン)とサーボ役(スレッド)を同居させる形です。マスタは NMT で Operational にし、SDO で 0x06、0x07、0x0F を順に書いて、モードを csp にします。以降は RPDO で目標位置を送り、TPDO で現在位置を受け取ります。

状態機械の実装

規格の状態遷移は、ほぼそのまま辞書1つで書けます。

TRANSITIONS = {
    (State.SWITCH_ON_DISABLED, Command.SHUTDOWN):         State.READY_TO_SWITCH_ON,
    (State.READY_TO_SWITCH_ON, Command.SWITCH_ON):        State.SWITCHED_ON,
    (State.SWITCHED_ON,        Command.ENABLE_OPERATION): State.OPERATION_ENABLED,
    (State.OPERATION_ENABLED,  Command.SWITCH_ON):        State.SWITCHED_ON,   # ← 同じコマンド
    # …(Quick stop / Fault 系は付録)
}

(READY_TO_SWITCH_ON, SWITCH_ON) の行と (OPERATION_ENABLED, SWITCH_ON) の行を見比べると、同じ Command.SWITCH_ON(= 0x07)が、キーの左側の状態によって行き先を変えているのが分かります。これが先ほどの「同じビット列が正反対の意味を持つ」の実装です。

上に載せたのは抜粋で、実装には Ready to switch on から直接 Operation enabled へ飛ぶ近道や Quick stop / Fault 系を含めて13エントリあります。

Statusword の判定は、②の表を (マスク, 一致値, 状態) の並びに移すだけです(cia402.pySTATE_PATTERNS)。マスクが 0x6F と 0x4F の2種類あるところも、そのままコードに出ます。

実行結果(出力から抜粋)

まず状態機械を進めます。

  起動直後                               Statusword = 0x0250  ->  Switch on disabled
  Controlword = 0x06 (Shutdown)          Statusword = 0x0231  ->  Ready to switch on
  Controlword = 0x07 (Switch on)         Statusword = 0x0233  ->  Switched on
  Controlword = 0x0F (Enable operation)  Statusword = 0x0237  ->  Operation enabled
  SDO write 0x6060 = 8  ->  0x6061 (実際のモード) = 8  (csp)

Statusword が 0x0250 から 0x02310x02330x0237 と変わっていくのが、規格の状態機械そのものです。続いて 10ms 周期で目標位置を送ります。

  t=0.001s  target=     0  actual=     0  status=0x0237
  t=0.101s  target= 10000  actual=  6229  status=0x0237
  t=0.302s  target= 10000  actual=  9854  status=0x0237
  t=0.591s  target= 10000  actual=  9999  status=0x0237
  取りこぼした SYNC 応答: 0 / 60

最後に、同じ 0x07 を Operation enabled の状態で書いてみます。

    0x0006 : Switch on disabled     -> Ready to switch on
    0x0007 : Ready to switch on     -> Switched on          ← 投入
    0x000F : Switched on            -> Operation enabled
    0x0007 : Operation enabled      -> Switched on          ← 停止(同じ 0x0007)

追従カーブ

CiA 402 csp モードの位置追従

階段状に見えるのは描画の粗さではなく、値が SYNC の瞬間にしか更新されないからです。10ms ごとに1段ずつ上がる形に、「マスタが毎周期1点を送る」という csp の性格がそのまま出ています。


④ 発展: EtherCAT との関係

ここまでのコードは、0x6040 に 0x06 / 0x07 / 0x0F を順に書き、0x6041 のビットで状態を判定し、0x607A に目標位置を流し込むだけです。これは EtherCAT のサーボでもそのまま通用します。EtherCAT には CoE(CANopen over EtherCAT)という仕組みがあり、CiA 402 の意味論をそのまま採用しているからです。

ただし同じなのは意味論だけで、配管は別物です。COB-ID は使わず SyncManager に直接置かれ、NMT は ESM になり、SYNC は Distributed Clocks(100ns 未満)に置き換わり、PDO の8バイト制限も無くなります。

EtherCAT をこのシリーズで動かさないのは、「PCの中では動かせないから」ではありません。KickCAT(CeCILL-C)というオープンソース実装には ESC のソフトウェアエミュレータが入っていて、ハードウェアなしでマスタとスレーブを会話させられます。扱わない理由は、仕様書(ETG.1000)が ETG 会員限定で引用できないことと、C++ と CMake のビルドが要ることです。このシリーズは pip install だけで完結することを条件にしているので、外しました。

なお本物のスレーブに ESC が必要なのは事実です。「フレームが通過している最中に処理する」という核心は専用ハードでしか成立せず、エミュレータで学べるのは手順と構造であってタイミングではありません。前編で仮想CANバスについて書いたのと、まったく同じ話です。

コード

この連載で動かすコードは、5回ぶんまとめて GitHub に置いてあります。今回の 02_canopen/ のほか、Modbus と OPC UA も同じリポジトリです。

https://github.com/logicia32/industrial-networks-labhttps://github.com/logicia32/industrial-networks-lab

次回

CANopen のサーボは動きました。しかしこのサーボは、制御盤の中に閉じています。外から使おうとすると、無いものが3つ出てきます。認可(誰が読んでよく、誰が書いてよいのか)、型と意味(0x6064 は何の値で単位は何か)、発見(このネットワークに何が何台いるのか)です。

EDS ファイルは2番目の答えを一部持っていますが、それはファイルであって、ネットワークの上にはありません。

次回は OPC UA です。この3つを正面から扱った規格です。

今回使ったもののライセンス

python-can 4.6.1(LGPL-3.0-only)/ matplotlib 3.10.7(PSF-based)。いずれもオープンソースです。

仕様書は、CiA 301(通信プロファイル)は無料(要・無料登録)ですが、CiA 402-2 / 402-3 と CiA 306(EDS)は CiA 会員限定です。これに由来する国際規格 IEC 61800-7-201 / -301 は有料(CHF 475 / 430)で、しかも IEC 版は2015年で止まっているため内容は同一ではありません(CiA 402-2 は 2024年に v5.0.0)。そのため、この記事では「由来する」と書いています。

この記事は Zenn に初出したものを加筆・補足したものです ── Zenn の元記事を見る