この記事はTouchDesigner Advent Calendar 2020の4日目の記事です。
前日(3日目)の記事は @shks さんの「ParsecTOP : ParsecをTouchDesignerで使って低遅延60FPSストリーミング送受信」です。
翌日(5日目)の記事は@camikura さんの「SHOTOKU VRクレーンをTouchDesignerから使う」です。
こんにちは。フリーランスでハードウェア制御開発などをしているユースケと申します。
皆さんはTouchDesignerでビジュアルを作るか、ハードウェア制御ではDMXで照明制御などをする方が大半だと思います。どうもハードウェア制御は電子回路やマイコンプログラミングが絡んでくるのでとっつきにくいかと思いますが、DMX以外にも(比較的)手軽にTouchDesignerからハードウェア制御をする方法もあるんですよね。今回はチープにステッピングモーター制御する方法を書きたいと思います。
↑なんかモーター制御したときにテープ回すのが流行ってるみたいなのでしてみた。
最初に
最近Ponoor Experimentsの堀尾寛太さんの作られた高性能ステッピングモータードライバーのSTEP400がクラウドファンディング成功しました。(おめでとうございます!)とても使いやすい、かつ高性能なデバイスなのでクライアント案件等であればそちらをご利用ください。この記事は個人製作で、そこまでバジェットがないけど気軽にTDからモーターで何か制御してみたいという(僕のようなビンボーな)方向きです。
STEP400についてはこちら
今回使用するGrblは元々DIYのCNCマシンの制御のために書かれたものですが、Gーcodeというスクリプトをシリアル経由で通信することで比較的簡単にステッピングモーターを制御できます。また、制御地点を繋ぐようにスムーズに速度変化をしてくれるため(まあまあ)スムーズな速度変化が実現できます。その一方でCNC制御のためのソフトなので位置制御しかしてくれず、モーターをずっと回転させるような回転数制御には向いていません。思ったように制御するためには設定のちょっとした手間はかかりますが、位置制御くらいであればマイコンプログラミングをしなくてもステッピングモーターが制御できます。
準備
全てオープンソースソフトウェアです。(TouchDesigner以外…)
- ArduinoIDE おなじみのArduino。
- Universal Gcode Sender Platform 本制御には使わないが、接続確認、設定のために使用。
- Grbl シリアルで送られてきたGCodeをパースし、ステッピングモーターを制御するソフトウェア。読み方はよくわからない。(グルーブル?)
必要機材
- Arduino Uno
- CNC Shield
- (ステッピングモーター(NEMA17型の12Vくらいのやつ。1回転あたりのステップ数が200のものが多い)例えばこれとか
- (ステッピングモータードライバー(A4988など)
- 12V電源
- USB-Bケーブル
- ジャンパピン
とりあえず一式そろってるこういうものも売ってます。おっと、これだとジャンパピンと12V電源は追加で必要ですね。
タームの解説
- Gcode: 主にCNC(3Dプリンタも)の制御に使われるスクリプト。制御対象により色々拡張されている。
- ステッピングモーター:英語名Stepper Motor。正確な位置制御や回転数制御に適している。動作音は比較的大きい。効率は低い。NEMA17型というサイズで様々な出力のものが入手できる。他にも工業用のデカいものから模型用の小さいものまで色々ある。
- マイクロステッピング:ステッピングモーターは1回転あたりのステップが決まっており(例えば1回転200パルスなど)それにより1回転の解像度が決まってしまうが、入力するパルスをPWM制御することによりそれより細かい解像度で回転が制御できる。マイクロステッピングの単位は1/2, 1/4となり、200ステップのモーターの場合はそれぞれ400ステップ、800ステップの細かい制御ができる。ただしその分出力トルクが落ちるので注意。
セットアップ
Grblダウンロード、焼き込み
- ArduinoIDEをダウンロード、インストールする
- Grblプロジェクトをダウンロードする
- Grblプロジェクトのコンパイル、焼き込み。手順についてはこちらを参照。Arduinoのスケッチを焼きこむのと同じ手順です。
配線
-
CNC ShieldとモータードライバをArduinoに取り付け(電源切ってね!)
- Arduino UnoからUSBケーブルを抜く
- CNCShieldをArduino Unoの上に差し込む
- マイクロステッピングを制御するためにジャンパピンをショートする
- とりあえず1個だけモーター制御するので一番左上、X軸用のドライバだけセットします。他も同様。
- 右下の赤いソケットはZ軸と同じ信号が入るので注意、4軸制御はできません。3軸まで。
- ジャンパピンは1/8となるように設定。M0, M1だけつなげてM2を開放したままにする。
- 詳しい設定はこちらに
- ジャンパピンがない場合もマイクロステッピングができないだけで動作はする。ただし動きは荒くなる。
- モータードライバを1のソケットに差し込む(極性に注意)
-
モーターとCNCシールドを繋ぐ
- 今差したモータードライバーの右にあるピン(4ピン)にモーターからのケーブルを接続
- 逆接続もできてしまうが、しても逆回転するだけなのでとりあえず極性は気にしないでよし
- 本来はここでモーターの電流設定もする必要があるが今回はとりあえずテストするため省略。こちらのPololuのページに詳しく設定する方法があるので長時間稼働させる際など、必ず設定するようにしてください。設定していないと異常に発熱してしまう可能性があります。
- なおモータードライバーA4998にはセットでヒートシンクがついてくるのですが、これが基板上の端子と接触するとショートする可能性があるため、取り付ける際はほかの部品に接触していないか、よく確認して下さい。
Grblのセットアップ
-
Universal Gcode Sender Platformをダウンロード&インストール
-
CNC Shieldに12V電源をつなぐ。USBケーブルをArduinoに再度つなぐ
-
Universal Gcode Sender(以下UGS)を起動する
-
シリアルポートからデバイスを選択する
-
1mmあたりのステップ数設定
-
最大速度設定
- 大きめにしておいて問題なし。$110=5000 と入力。5000mm/minとしました。
-
最大加速度設定
- あまり大きくすると急激に速度変化する。50mm/s2程度で良い
- コンソールより$120=50と入力
-
ステッピングパルス関係のチューニング
-
セッティングはこんなところにしてテスト:移動量の確認
-
左右ボタン(X- X+)を押してモーターが1回転することを確認
-
シリアル接続を切ってUGSを終了する。設定はこれでとりあえず完了。このあたりのパラメータは動かす対象によって随時調整し直す必要あり。セッティングお疲れさま!
TouchDesignerからの制御
ここからようやくTouchDesignerが登場。
使うG-Code
実は先ほどUGSでモーターを回したとき、コンソールにはこんなG-Codeが出力されていました。
G21G91G1X8F3000は以下のコマンドで成り立っています。
G21: mm単位系
G91: 相対座標
G1X8F3000: モーションコマンド、X軸に+8単位、フィードレート(スピード)3000で移動
同じくG90G21は、G90が絶対座標系なので、mm単位系で絶対座標系に戻す、という命令ですね。
今回使うのはこれと、あと現在位置を返してくれるコマンド(後述)だけです。これだけの命令でとりあえず制御できるので結構らくちんですね。
TouchDesignerから動かしてみる
-
SerialDATを作って、UGSで設定したのと同じシリアルポート番号を設定、通信速度は115200bps
-
TextDATを作り上記の1回転するコマンド、G1X8F3000を入力します。
-
次にもう一つTextDATを作り、G-Codeの入っているテキストを参照してSerialDATに書き込むようにします。
gcode = op('text1').text
op('serial1').send(gcode, terminator = '\r\n')
- コードを書き込んだTextDATを右クリックから実行(Ctrl + r)します。一回転しましたか?
- というわけで簡単に制御できるのがわかったところでCHOPから制御してみましょう。
- Slider COMPからMathCHOPで出力レンジを-4 ~ 4にしてChopexecDatに渡します。
- ChopexecDatはonValueChangeをオンにしたうえで以下のコードをonValueChange内に書いておきます。speedはここでは1500にしておきました。
speed = 1500
nextPosition = val
code = 'G1X{0}F{1}'.format(nextPosition, speed)
op('serial1').send(code, terminator = '\r\n')
- スライダーをぐりぐりやっていただくとモーターが追随してきましたか?ただし操作に対して結構遅延するのに気づかれたと思います。また動かしすぎるとGrblがこのようなエラーを吐いてきたり、場合によってはG-Code用バッファがオーバーフローしてクラッシュします。
- エラーコード24はG-Codeの公式のエラーではなくGrbl独自のエラーで以下のようにXYZ軸の指定を必要とする命令がブロック上に二つ以上あったと説明があります。ただしモーションコマンドG1はXYZ座標で毎回指定しなくてもよく、以下のエラーはバッファオーバーフローにより引き起こされたものと思われます。
24 Two G-code commands that both require the use of the XYZ axis words were detected in the block.
- 今送っているコマンドをTextDatに出力してみるとFloatの有効数字があまりに大きいため適当な値で切り捨ててやります。Grblの公式ページにはmm単位の場合小数点3桁まで送るとよいとあるので、そこまでをキャストしてあげます。
Before -> code = 'G1X{0}F{1}'.format(nextPosition, speed)
After -> code = 'G1X{0:.3f}F{1}'.format(nextPosition, speed)
- また、制御位置を60FPSで送る必要は全くないため、ResampleDATでSampleRateを10程度に落とします。
- これでだいぶエラーは改善したかと思います。CNC制御ソフトなので制御点をすべて通過する制御となるため、遅延はある程度仕方ありません。
位置情報を取得する
とりあえず動かせるようにはなったのですが、もう少し改良してみましょう。モーターの追随速度が問題になりそうなため、現在のモーター位置をまず取得してみましょう。
- LfoCHOPで10Hzのパルスを作り、ChopexecDatでOnになったときに位置リクエストコマンド*?*を送るようにします。SerialDatに位置情報が帰ってくると思います。
op('serial1').send('?', terminator = '\r\n')
-
上のメッセージではMpos、マシンポジションがx軸-0.77であるとわかります。WPosはWorking Positionで、CNC加工時にマシン原点からのオフセットを入れられるものなのでここでは無視します。
-
メッセージから現在のポジションを常時読み出すようにしましょう。ConvertDATをつないで、メッセージ中のセパレータ、< > , : \t をSplit Cells atに入れます。
-
この後は制御する対象によって違うのですが、位置の指示(resample1)と現在位置の差分を取って、差分が一定以上になるとLogicCHOPが入るロジックを作ってみました。最後のNullは制御位置を送っているchopexec1を入り切りしています。(うーん。でも現在位置を制御に取り入れても遅延要素が強すぎて有効じゃないな。デモだしまあいっか!)
制御値にフィルタをかけて追随性能を良くする&バッファオーバーフロー対策
目標位置を順次追っていくGrblをここで使ってしまう時の問題は、例えばノイズ的に目標値に1点だけ離れた点が入ってきた場合、モーターが律儀にその点まで移動して、また戻ってくるような挙動をすることです。(CNCだったら当たり前ですよね。)この時のモーターの移動時間は制御遅れにつながり、また、その間にも次の目標位置がたまり続けてしまうため上記のバッファオーバーフローも引き起こしかねません。よくあるモーターのサーボ制御の場合は移動する目標値を逐次追っていけるのようPID制御を組んでいるのですが、これは信号処理的に見ればフィルタを入れていることと違いはありません。なので今回は入力信号に対してFilterCHOPを通してあげることで、急峻な目標値の変化があってもモーターが追随できる速度で信号を渡してあげられるようにしました。要はローパスを入れたわけです。
入力信号をNoiseCHOPにし、モーターが追いつけない程度の早い周期のノイズを入れてあげてもモーターが、バッファも溜めずについてきてくれるようになります。なお、バッファが溜まってしまっているか確認するには入力のNoiseCHOPを止めてみます。入力を止めてもモーターがしばらく動き続けているようであれば、バッファが溜まってしまっていると判断できます。(そしてそのまま動かしておくと多分Grblがクラッシュします)
サンプルファイルもご覧ください。
結論
- 最初に挙げたステッピングモータ制御ボード、STEP400とは圧倒的に機能が劣りますが、少数のモーターを、3000円程度で簡易的な制御ができるのは魅力です。
- また、制御のためのG-Codeはとても単純なうえマイコンプログラミングも不要です。
- 複数のモーターを制御することも可能ですが、フィードレートの指定とは、XY座標での移動距離の指定となるため、X, Yで2個のモーターを、1モーターあたり3000で動かしたい場合、フィードレートの指定は sqrt(3000^2 + 3000^2) = 4243となりますのでご注意ください。
- このようなシステムをもし常設で使うような場合は、シリアルポートの番号が何かの拍子に変わって動かなくなってしまうリスクがあります。(実際何回か遭遇しています)
- 複数Grblを制御することも出来なくはないのですが、制御の性質上Grbl同士が同期してくれません。通信および制御上の遅延が気になるほど差が出てしまうことがあります。
- これで制御が満足できない場合は自分で制御コードを書くか、STEP400を買いましょう!
- G-Codeではロボットアームとかも制御できます。こんどは3DプリンタにG-Codeをライブでぶち込んで悪いことしてみたいなぁ。
動かねーぞ、不明点等ありましたら hi@yuskegoto.netまたはTwitter: @donneideckerまで。