Dynamixel を RaspberryPi Picoでいごかす (PlatformIO編)

おのみちです。弊社が代理店を務める ROBOTIS社のサーボモーター「Dynamixelシリーズ」は、Arduino Uno 用の DYNAMIXEL Shield や Arduino MKRシリーズ向けの DYNAMIXEL Shield for Arduino MKR series など、比較的 Arduino のサポートが充実しています。
そのため、「Arduino 系のマイコンなら問題なく動くだろう」と 高を括っていた のですが…
Raspberry Pi Pico(RP2040) で動作させようとした際に、思わぬ問題に直面しました。


【はじめに(結論)】

Raspberry Pi Pico(RP2040)で Dynamixel を動かす際、Return Delay Time を “0” にすると通信が不安定になる ことが分かりました。

原因は、TxEnable の切り替えタイミングにバラつきが生じるため で、これにより 送受信エラー が発生していました。そのため、Pico では Return Delay Time を “0” にせず、ある程度大きめの値に設定すること を推奨します。

どうしても Return Delay Time を 0 にしたい場合 は、TxEnable の制御を Arduino2Dynamixel ではなく電子回路で行う必要があります。

以下、この検証の詳細をまとめます。


【開発環境】

開発中のテスト基板ですが、Raspberry Pi Pico(RP2040) をベースに W5500 を使用し、有線 LAN 経由で Dynamixel を動作させる構成 になっています。有線 LAN での通信には Meridian を使用しています。

Meridian はもともと ESP32とTeensy をターゲットとして設計されており、PlatformIO の開発環境 を推奨しています。そのため、RP2040 向けの PlatformIO 環境を構築し、今回の実験を行いました。

Dynamixelの送受信回路は、DYNAMIXEL Shield for Arduino MKR series を参考に、74HC125 と 74HC126 を使用しました。送受信の切り替え信号(TxEnable)は、汎用IOを使って制御します。ライブラリには Dynamixel2Arduino を使用しました。

サーボモーターの通信速度は、制御周期を可能な限り速くするために 3Mbps に設定し、return_delay_time は “0” にしました。これは一般的な設定であり、ROSのROBOTIS公式パッケージ 「DYNAMIXEL Workbench」 のeマニュアルにも、「return_delay_time は ‘0’ にしましょう」と記載されています。


【発生した問題】

いざ動かしてみると、一応動作はするものの、定期的に送受信に失敗 し、一瞬停止しては再び動くという挙動を繰り返しました。その結果、ロボットが ガクガクと動く 状態になってしまいました。

「なんだこれは??」と思い、オシロスコープで波形を計測 したところ、TxEnable の信号が意図しないタイミングで遷移 していることを確認しました。具体的には、TxD の送信完了後も、しばらく Low にならずに遅延が発生 していました。

この影響で、受信パケットの一部が欠損 し、結果的に 受信パケットエラー が発生。その後、タイムアウト待ちが発生する ことで、周期的な動作不良につながっていたようです。

そこで、return_delay_time の値をいろいろ試してみました。

  • 設定値 20(40µs) → NG
  • 設定値 30(60µs) → OK

どうやら、この 20~30 の間に閾値 があるようです。

これは、もしかして ライブラリのバグ なのか?
「return_delay_time の設定値は ‘0’ にしましょう」との推奨設定は、一体何だったのか……?

そこで、Raspberry Pi Pico ではなく、本家が推奨しているArduino MKR ZeroDYNAMIXEL Shield for Arduino MKR series の組み合わせで、同じ実験を行ってみました。
(※ピンの配置や信号の組み合わせが異なる点はご了承ください)

return_delay_time の設定値が “0” の状態でも、TxEnable は送信完了と同時に正確に立ち下がっていますね!

となると、問題があるのは Raspberry Pi Pico の方 なのか……?

そこで、単純なソースコードで検証 してみることにしました。

void setup() {

    pinMode(22, OUTPUT);    // 22番を出力ピンに
    Serial1.begin(3000000); // Serial1を 3Mbpsに設定
}

void loop() {

  digitalWrite(22, HIGH);   // 送信前に22番pinをHighへ
  Serial1.print("HELLO WORLD");

  Serial1.flush();          // TxDの送信完了まで待つ
  digitalWrite(22, LOW);    // 送信完了後に22番pinをLowへ
  delay(100);
}

結果として、オシロスコープで波形を確認 したところ、以下のような結果が得られました。

(黄色:22番ピン、青色:TxDピン)

見てわかる通り、送信完了後の 22番ピンの立ち下がるタイミングが一定ではなく、バラつき があります。

この現象が、Dynamixel2Arduino ライブラリで Return Delay Time を “0” に設定した際に、送受信失敗のフラグが立ってしまう原因 でした。

Serial.flush() 関数は、Arduino のバージョンによって仕様が変更された経緯のある関数 で、現在は 「シリアル出力の完了を待つ」 という動作をします。
この関数は 送信完了までブロッキングする ものの、関数を抜けるタイミングについての明確な仕様はありません。
そのため、Serial.flush() の完了タイミングが環境によってバラつく場合、Return Delay Time を 0 に設定すると通信エラーが発生する可能性があります。環境ごとに挙動が異なるため、Return Delay Time は 0 にこだわらず、適切な値を探して設定すること を推奨します。

ちなみに、Return Delay Time のデフォルト値は 500µs で、これは非常に安全マージンを取った設定になっています。


【Raspberry Pi Pico での対策】

この結果から、Raspberry Pi Pico で Dynamixel を動作させる際の推奨設定 は以下の通りです。

  1. Return Delay Time を 0 にせず、十分大きい値を設定する(例:30 = 60µs 以上)
  2. どうしても Return Delay Time を 0 にしたい場合、TxEnable の信号を Arduino2Dynamixel のライブラリで制御せず、電子回路で実装する (参考:OpenRB-150)

という感じで、付き合っていく必要がありそうです。