3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Raspberry Pi & Node-REDからモーターを制御する

Last updated at Posted at 2019-12-18

本記事は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」で検索してインストールします
search_palette.png

インストールに成功すると、左側のノードに「Keigan Motor」カテゴリ内に2つのノードが出てきます。

インストール直後はUSBを認識しない場合があります。その場合はraspberry Piを再起動して下さい。

p_node.png

USB Selector
USB接続したKeiganMotorを検出し接続するノード。接続したモーターを出力する。
Sequencer
モーターを操作するコマンドが設定でき、入力から送られてきたモーターに対して設定したコマンド操作を行う。「USB Selector」の後方に繋いで使用する。

##モーターの準備
KeiganMotorの給電ポート(USB Type-C)をバッテリーに、通信用ポート(USB MicroB)をRaspberry Piに接続します。
con.jpg

モバイルバッテリーの場合、過充電防止の為、モーターの負荷が高くなると、給電を停止する物が多いです。
負荷の高い動作や、車輪に使う場合は、3A出力の物やUSB充電器等の使用をお勧めします。

##取りあえず回してみる
フローに「inject」「USB Selector」「Sequencer」ノードを以下のように配置し、Sequencerノードの設定パネルで以下のようにします

1-1.png

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"}
モーターの動作を許可する(安全装置。一度設定すれば、再起動するまで有効)
![cmdEnable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103952/4722549d-24a2-67dd-5569-146219aa8524.png)
{"cmd":"cmdRun_rpm","arg":[10]}
速度設定 10rpmで回転
![cmdRun_rpm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103952/df564784-b276-f5a4-ba1d-8a92522b4852.png)

続いてモーターの停止フローも作成します
stopflow.png

{"cmd":"cmdStop"}
モーターの動作を止める(その場で停止・磁力がかかり固まる)
{"cmd":"cmdDisable"}
モーターへの通電を止める(その場で停止・空転する)

#####完成した物はこちら(YouTube)

「回転開始」「停止」で動作を確認(モーターが認識されていない場合は先に「scan」を押す)

YouTube

##往復運動をさせる

一定の距離の往復を繰り返してみます。
以下のようにUSB Selector、Sequencerノードを繋ぎます。

loopflow.png

cmdMoveByDistanceSync(移動先座標,移動速度,タイムアウト)
移動先の座標に移動を完了したら、次のノードを実行します。
 従って、2つの「cmdMoveByDistanceSync」ノードに移動先の上限・下限を指定してループで繋ぐと、モーターが往復運動をおこないます。
 単位はradianで以下の例では、左1回転(6.28)、右1回転(-6.28)の合計2回転分を往復します。
![syncp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103952/b824defe-48bc-cb52-4122-3d80aa3100c0.png)

注意点として「エラー時にメッセージの伝播を停止する」のチェックは必ず入れて下さい。
Sequencerノードは入力があった場合、直ちにコマンドを実行して、その結果を出力します。
上記の場合、ノードがループして接続されている為、失敗した場合にも失敗の結果が出力され、無限ループに陥ります。

#####完成した物はこちら(YouTube)
YouTube

##応用 (音の方向に追従)
これだけだと面白くないので、ReSpeaker Mic Array v2.0と組み合わせて、音の方向を向くようにしてみました。
moveflow.png
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の依存ライブラリのインストール

#####完成した物はこちら(YouTube)
YouTube

##フロー
今回作成したフローです

回転・往復運動.json
[{"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"]]}]
音の方向に追従.json
[{"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":[]}]

##参考

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?