Node-REDを使ってみる その4(MQTT通信、ロボットアーム)
はじめに
今回は以前Amazonで購入したロボットアームを、Node-REDでMQTT通信を利用して制御できるようにしてみました。
これまではボタンで制御していたのですが、ネットワークにつなぐことで画像処理と組み合わせたり、遠隔操作したりといった使い方ができるようになりそうです。
▼以前の記事はこちら
マイコン側の設定
ロボットアームの6つのサーボモータを、ESP32の開発ボードで制御できるようにします。
▼ロボットアームは以前Amazonで購入したものを使っています。
▼購入したものはこちら。6自由度と書かれていますが、エンドエフェクタを除くと5軸のロボットアームですね。
▼アームは既製品ですが、基板は作成しました。

以前作成したプログラムでは各サーボモータの最小値と最大値を設定できなかったので、動作範囲を確認して設定できるようにしました。
▼サーボモータの動作範囲の制限もできるようにしたプログラムはこちら
動作を確認しておきました。
▼最小値と最大値の処理がうまくいっています。
結構揺れていたので、ネジをしっかり締めておきました。
▼ネジ締め後はこんな感じ。
MQTT通信で操作できるように、ChatGPTにプログラムを書いてもらいました。servo/controlトピックでサーボモータの角度を受け取り、servo/stateトピックに現在の角度を送信するようになっています。
▼MQTT通信で操作できるようにしたプログラムはこちら
Wi-FiのSSID、パスワード、この後Node-REDでMQTTブローカーを起動するPCのIPアドレスは設定してください。
サーボモータの名前は、エンドエフェクタからservo0、servo1...と順番になっています。
▼例えばservo/controlに以下のようなJSON形式のデータを送信すると、対応したサーボモータの角度が変化します。
{
"servo1": 90,
"servo2": 122,
"servo3": 180,
"servo4": 80,
"servo5": 0
}
Node-RED側の設定
▼マイコンとNode-REDのMQTT通信については、以前の記事でも扱っています。
MQTTブローカーはNode-RED側で立てます。
▼Aedes MQTT brokerを使っています。

先程マイコン側のプログラムでも設定した、MQTT通信のトピックでデータの送受信をできるようにします。
▼全体のフローはこちら

[{"id":"8828483952647dd6","type":"mqtt out","z":"d6fad9e77ecc8414","name":"","topic":"servo/control","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"f414466379b9f58d","x":3770,"y":360,"wires":[]},{"id":"df70bf060f723483","type":"template","z":"d6fad9e77ecc8414","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"servo1\": 90,\n \"servo2\": 120,\n \"servo3\": 180,\n \"servo4\": 0,\n \"servo5\": 90\n}\n","output":"str","x":3600,"y":360,"wires":[["8828483952647dd6"]]},{"id":"428693fe787214d3","type":"inject","z":"d6fad9e77ecc8414","name":"Reset","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":3450,"y":360,"wires":[["df70bf060f723483"]]},{"id":"8ac15462c00e5f8c","type":"template","z":"d6fad9e77ecc8414","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"servo1\": 90,\n \"servo2\": 122,\n \"servo3\": 180,\n \"servo4\": 80,\n \"servo5\": 0\n}\n","output":"str","x":3600,"y":420,"wires":[["8828483952647dd6"]]},{"id":"a4fe867ee59b182d","type":"inject","z":"d6fad9e77ecc8414","name":"Left","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":3450,"y":420,"wires":[["8ac15462c00e5f8c"]]},{"id":"23351b36d970eeda","type":"template","z":"d6fad9e77ecc8414","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"servo0\": 80\n}\n","output":"str","x":3600,"y":520,"wires":[["8828483952647dd6"]]},{"id":"b8cd299953240969","type":"inject","z":"d6fad9e77ecc8414","name":"Open","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":3450,"y":520,"wires":[["23351b36d970eeda"]]},{"id":"159b1ecb24fd168c","type":"template","z":"d6fad9e77ecc8414","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"servo0\": 140\n}\n","output":"str","x":3600,"y":560,"wires":[["8828483952647dd6"]]},{"id":"2e8f8708a70a37af","type":"inject","z":"d6fad9e77ecc8414","name":"Close","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":3450,"y":560,"wires":[["159b1ecb24fd168c"]]},{"id":"1aaa9d20579e4610","type":"template","z":"d6fad9e77ecc8414","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"servo1\": 90,\n \"servo2\": 122,\n \"servo3\": 180,\n \"servo4\": 70,\n \"servo5\": 180\n}\n","output":"str","x":3600,"y":460,"wires":[["8828483952647dd6"]]},{"id":"e8f023d397ebcc1a","type":"inject","z":"d6fad9e77ecc8414","name":"Right","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":3450,"y":460,"wires":[["1aaa9d20579e4610"]]},{"id":"2dd039d63dd1a155","type":"debug","z":"d6fad9e77ecc8414","name":"debug 417","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":3610,"y":300,"wires":[]},{"id":"7cc09dd250abb10b","type":"mqtt in","z":"d6fad9e77ecc8414","name":"","topic":"servo/state","qos":"2","datatype":"auto-detect","broker":"f414466379b9f58d","nl":false,"rap":true,"rh":0,"inputs":0,"x":3440,"y":300,"wires":[["2dd039d63dd1a155"]]},{"id":"f414466379b9f58d","type":"mqtt-broker","name":"","broker":"localhost","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]
templateノードでマイコンに送信するデータ形式に加工しています。
▼例えばResetのinjectノードにつながっているtemplateノードの中身は以下のようになっています。

▼エンドエフェクタであるservo0だけ別で設定するようにしました。OpenとCloseのinjectノードを実行すると、開閉します。

接続できると、サーボモータの状態を受信できます。
▼JSON形式で受信できています。

後で使いやすいように、サーボモータの状態をflowオブジェクトに代入しておきました。
▼changeノードでservo/stateの値をプロパティ毎に分けます。

▼flowオブジェクトにそれぞれのサーボモータの値を代入しています。

動作を確認する
実際に動かしてみました。
▼MQTT通信のレスポンスが速く、思っていたよりも機敏に動きます。配線が短すぎますね。
エンドエフェクタは過負荷状態にならないよう、完全には閉じないようにしています。薄い物体だと取りこぼすようです。
▼小さいブレットボードの向きを変えると、掴むことができました。電力供給が不安定になったのか、少しだけ姿勢を崩した状態になっています。
▼小さいブレットボードを掴んで、左から右に置くことができました。

ベルトコンベアで流れてきた物体の移動作業のような動きができそうです。
最後に
今までボタンで1つずつ軸の角度を操作していたので気付かなかったのですが、意外と速く動きますね。負荷がかかった状態だと振動していることもありましたが、テスト用には十分使えそうです。
フレームが短くて重たそうなので、また3DCADで設計して3Dプリンターで作り直したいなと思っています。
通信してロボットアームを制御できたので、運動学の計算をROSで行い、その結果をロボットアームに反映させるといったこともできそうです。
▼Open Manipulatorの場合はROSとNode-REDで通信して制御したことがあります。Open Manipulatorは18万円ぐらいするので、値段が全然違いますよね。