本記事はNode-RED Advent Calendar 2019 19日目の記事です。
#概要
ラピッドプロトタイピング用モーターKeiganMotorをNode-REDから操作する方法を紹介します。
KeiganMotorとは「ラピッドプロトタイピング」(迅速な試作)用に開発された、無線モジュールやセンサーが全て一体化され、位置制御・トルク制御等がコマンドで制御できる「モーターモジュール®」です。
本記事では以下の内容を紹介します。
- RaspberryPi上のNode-RED用からKeiganMotorを制御できるノードのインストール方法。
- 回転、停止、距離を指定して往復運動を行うフローの作成。
- ReSpeakerとKeiganMotorを使用した例。
サンプルのフロー、ソースコードは記事の最後尾に掲載しています。
##作業環境
- Raspberry Pi 3 B+
- Node-RED v1.0.2
- Node.js v10.16.3
##node-red-contrib-keigan-motor-sequencerのインストール
「パレットの管理」>「ノードを追加」から
「node-red-contrib-keigan-motor-sequencer」で検索してインストールします
インストールに成功すると、左側のノードに「Keigan Motor」カテゴリ内に2つのノードが出てきます。
インストール直後はUSBを認識しない場合があります。その場合はraspberry Piを再起動して下さい。
- USB Selector
- USB接続したKeiganMotorを検出し接続するノード。接続したモーターを出力する。
- Sequencer
- モーターを操作するコマンドが設定でき、入力から送られてきたモーターに対して設定したコマンド操作を行う。「USB Selector」の後方に繋いで使用する。
##モーターの準備
KeiganMotorの給電ポート(USB Type-C)をバッテリーに、通信用ポート(USB MicroB)をRaspberry Piに接続します。
モバイルバッテリーの場合、過充電防止の為、モーターの負荷が高くなると、給電を停止する物が多いです。
負荷の高い動作や、車輪に使う場合は、3A出力の物やUSB充電器等の使用をお勧めします。
##取りあえず回してみる
フローに「inject」「USB Selector」「Sequencer」ノードを以下のように配置し、Sequencerノードの設定パネルで以下のようにします
- USB Selectorの入力オプション
-
- USB Selectorノードの入力msg.payloadに"scan"(injectノードに"scan")を設定する事で、Raspiに接続されているUSBモーターをNode-REDに認識させます。
- モーターが認識されている場合は「USB Selector」に検出したモーター名が表示されます。一度認識すれば、Node-REDが再起動するまで有効です。
- フロー上のUSB Selector設定パネルのスキャンでも認識しますが、Node-REDを起動させて自動実行する場合には、必要になります。
#####Sequencerノード
Sequencerノードのモーターコマンドは設定パネルからGUIで選択できます。
- {"cmd":"cmdEnable"}
- モーターの動作を許可する(安全装置。一度設定すれば、再起動するまで有効)
- {"cmd":"cmdRun_rpm","arg":[10]}
- 速度設定 10rpmで回転
- {"cmd":"cmdStop"}
- モーターの動作を止める(その場で停止・磁力がかかり固まる)
- {"cmd":"cmdDisable"}
- モーターへの通電を止める(その場で停止・空転する)
#####完成した物はこちら(YouTube)
「回転開始」「停止」で動作を確認(モーターが認識されていない場合は先に「scan」を押す)
##往復運動をさせる
一定の距離の往復を繰り返してみます。
以下のようにUSB Selector、Sequencerノードを繋ぎます。
- cmdMoveByDistanceSync(移動先座標,移動速度,タイムアウト)
- 移動先の座標に移動を完了したら、次のノードを実行します。 従って、2つの「cmdMoveByDistanceSync」ノードに移動先の上限・下限を指定してループで繋ぐと、モーターが往復運動をおこないます。 単位はradianで以下の例では、左1回転(6.28)、右1回転(-6.28)の合計2回転分を往復します。
注意点として「エラー時にメッセージの伝播を停止する」のチェックは必ず入れて下さい。
Sequencerノードは入力があった場合、直ちにコマンドを実行して、その結果を出力します。
上記の場合、ノードがループして接続されている為、失敗した場合にも失敗の結果が出力され、無限ループに陥ります。
##応用 (音の方向に追従)
これだけだと面白くないので、ReSpeaker Mic Array v2.0と組み合わせて、音の方向を向くようにしてみました。
ReSpeakerは複数のマイクを組み合わせて、音声を正確にキャプチャーする事が可能なデバイスです。ReSpeakerには音声の方向を出力するAPIがあるので、今回はそれを使用してモーターの座標にマップしました。
ReSpeakerの音声方向の取得には以下の記事を参考にpythonで方向のみを出力するスクリプトを作成し、execノードで値を取得しています。
https://tech.motoki-watanabe.net/entry/2018/09/02/034951
音声方向の取得部分のソースコードを一式アップしますので、ReSpeaker Mic Array v2.0をお持ちの方は試してみて下さい。
https://cdn-awscloudfrontorigin.keigan-motor.com/sample/usb_4_mic_array.zip
- /home/pi/usb_4_mic_array にアップロード
-
$sudo pip3 install pyusb
でpythonの依存ライブラリのインストール
##フロー
今回作成したフローです
[{"id":"461d5a5f.bdbf04","type":"tab","label":"デモ","disabled":false,"info":""},{"id":"a85f5d59.ee50c","type":"km-motor-usb-selector","z":"461d5a5f.bdbf04","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":true,"x":580,"y":220,"wires":[["437b9cc6.b48824"]]},{"id":"437b9cc6.b48824","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdEnable\"}","isErrStop":true,"x":800,"y":220,"wires":[["f8aed4cf.501a98"]]},{"id":"f8aed4cf.501a98","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdRun_rpm\",\"arg\":[10]}","isErrStop":true,"x":1080,"y":220,"wires":[[]]},{"id":"bd03af87.d6137","type":"inject","z":"461d5a5f.bdbf04","name":"","topic":"","payload":"scan","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":"1","x":350,"y":100,"wires":[["a3b1d26b.7cf2f"]]},{"id":"a3b1d26b.7cf2f","type":"km-motor-usb-selector","z":"461d5a5f.bdbf04","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":true,"x":520,"y":100,"wires":[[]]},{"id":"bf3d74f.3985088","type":"inject","z":"461d5a5f.bdbf04","name":"回転開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":360,"y":220,"wires":[["a85f5d59.ee50c"]]},{"id":"4bf19bff.ed8f24","type":"comment","z":"461d5a5f.bdbf04","name":"Node-REDの初回起動等でモーターが認識していない状態の場合は実行する必要がある","info":"","x":920,"y":100,"wires":[]},{"id":"6a2c141f.27400c","type":"inject","z":"461d5a5f.bdbf04","name":"停止","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":350,"y":300,"wires":[["8b98e729.14d018"]]},{"id":"8b98e729.14d018","type":"km-motor-usb-selector","z":"461d5a5f.bdbf04","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":true,"x":580,"y":300,"wires":[["ea2c8c37.a4752"]]},{"id":"ea2c8c37.a4752","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdStop\"}","isErrStop":true,"x":790,"y":300,"wires":[[]]},{"id":"65bb9f42.762f5","type":"inject","z":"461d5a5f.bdbf04","name":"動作を無効","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":360,"y":380,"wires":[["7a734311.09939c"]]},{"id":"7a734311.09939c","type":"km-motor-usb-selector","z":"461d5a5f.bdbf04","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":true,"x":580,"y":380,"wires":[["683bd3fb.82971c"]]},{"id":"683bd3fb.82971c","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdDisable\"}","isErrStop":true,"x":800,"y":380,"wires":[[]]},{"id":"8c219e06.26f5c","type":"km-motor-usb-selector","z":"461d5a5f.bdbf04","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":true,"x":580,"y":540,"wires":[["f78175ad.17a8b8"]]},{"id":"4d336553.d6205c","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdMoveByDistanceSync\",\"arg\":[6.28,6.28,null]}","isErrStop":true,"x":550,"y":640,"wires":[["ca0ae616.66f0c8"]]},{"id":"ca0ae616.66f0c8","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdMoveByDistanceSync\",\"arg\":[-6.28,6.28,null]}","isErrStop":true,"x":1020,"y":640,"wires":[["4d336553.d6205c"]]},{"id":"e0cff2d0.0b945","type":"inject","z":"461d5a5f.bdbf04","name":"回転開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":360,"y":540,"wires":[["8c219e06.26f5c"]]},{"id":"f78175ad.17a8b8","type":"km-motor-sequencer","z":"461d5a5f.bdbf04","name":"","cmdJson":"{\"cmd\":\"cmdEnable\"}","isErrStop":true,"x":840,"y":540,"wires":[["4d336553.d6205c"]]}]
[{"id":"5355c38e.29b0fc","type":"tab","label":"音声追従","disabled":false,"info":""},{"id":"935c2204.3a753","type":"km-motor-usb-selector","z":"5355c38e.29b0fc","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":false,"x":320,"y":100,"wires":[["cb9290cf.9b10e","7039f831.af4a88"]]},{"id":"1f863b5f.ee7785","type":"inject","z":"5355c38e.29b0fc","name":"開始","topic":"","payload":"scan","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"2","x":130,"y":100,"wires":[["935c2204.3a753","790d341a.700ccc"]]},{"id":"9b1dc88.90e2a38","type":"exec","z":"5355c38e.29b0fc","command":"sudo python3 -u /home/pi/usb_4_mic_array/main.py","addpay":false,"append":"","useSpawn":"true","timer":"","oldrc":false,"name":"","x":700,"y":400,"wires":[["b981447d.08d408"],[],[]]},{"id":"166a8b93.b9b714","type":"inject","z":"5355c38e.29b0fc","name":"モーターの停止","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":700,"wires":[["e13e8686.b77c28"]]},{"id":"e13e8686.b77c28","type":"km-motor-usb-selector","z":"5355c38e.29b0fc","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":false,"isMotorKeepalive":false,"x":380,"y":700,"wires":[["dec50a53.443498"]]},{"id":"dec50a53.443498","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"cmdDisable\"}","isErrStop":false,"x":600,"y":700,"wires":[[]]},{"id":"cb9290cf.9b10e","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"cmdPresetPosition\",\"arg\":[0]}","isErrStop":false,"x":630,"y":220,"wires":[["985be5bd.f26bb8"]]},{"id":"985be5bd.f26bb8","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"cmdSpeed_rpm\",\"arg\":[30]}","isErrStop":false,"x":970,"y":220,"wires":[["7a973b74.12a404"]]},{"id":"ebe0c4d2.4a3948","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"cmdMoveToPosition\",\"arg\":\"msg.payload\"}","isErrStop":false,"x":940,"y":560,"wires":[[]]},{"id":"b981447d.08d408","type":"function","z":"5355c38e.29b0fc","name":"msg.payload *= (Math.PI/180);","func":"msg.payload =(msg.payload* (Math.PI/180));\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":560,"wires":[["62e0ac5c.450bc4"]]},{"id":"7a973b74.12a404","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"cmdEnable\"}","isErrStop":false,"x":1260,"y":220,"wires":[[]]},{"id":"62e0ac5c.450bc4","type":"km-motor-usb-selector","z":"5355c38e.29b0fc","name":"","selectMotorName":"","selectMotorNameself":false,"isInitializScan":true,"isMotorKeepalive":false,"x":620,"y":560,"wires":[["ebe0c4d2.4a3948"]]},{"id":"885b9856.d8b128","type":"status","z":"5355c38e.29b0fc","name":"","scope":["9b1dc88.90e2a38"],"x":1020,"y":400,"wires":[["790d341a.700ccc"]]},{"id":"790d341a.700ccc","type":"function","z":"5355c38e.29b0fc","name":"main.pyの二重起動防止","func":"if(msg.status&&msg.status.text.indexOf(\"pid\")===0){\n context.set(\"pid\", msg.status.text);\n return null;\n}else if(!context.get(\"pid\")){\n return msg; \n}else{\n return null; \n}\n","outputs":1,"noerr":0,"x":370,"y":400,"wires":[["9b1dc88.90e2a38"]]},{"id":"2c7dac0.0887854","type":"comment","z":"5355c38e.29b0fc","name":"ReSpeaker Mic Array v2.0からマイク座標を取得","info":"","x":440,"y":340,"wires":[]},{"id":"446889f1.b532c8","type":"comment","z":"5355c38e.29b0fc","name":"マイク座標をモーターの座標へ変換してその位置へ移動させる","info":"","x":480,"y":500,"wires":[]},{"id":"9d6b4af2.fcfd88","type":"comment","z":"5355c38e.29b0fc","name":"モーターの位置と速度を設定","info":"","x":600,"y":180,"wires":[]},{"id":"bd91ad02.d8dd5","type":"comment","z":"5355c38e.29b0fc","name":"現在の座標 (デバッグ用)","info":"","x":580,"y":60,"wires":[]},{"id":"7039f831.af4a88","type":"km-motor-sequencer","z":"5355c38e.29b0fc","name":"","cmdJson":"{\"cmd\":\"onMotorMeasurement\"}","isErrStop":false,"x":620,"y":100,"wires":[["2dcc41bd.ded7fe"]]},{"id":"2dcc41bd.ded7fe","type":"debug","z":"5355c38e.29b0fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":870,"y":100,"wires":[]}]
##参考