ROS1を使ってみる その1(Turtlesim、Node-RED)
はじめに
今回はROSをちゃんと学びたいということで、Turtlesimで通信周りの確認をしてみました。
これまでTurtlesimのカメをキーボードやGUIで操作したことはありますが、改めて試しています。また、Node-REDでデータを受信したことはありますが、送信はしたことが無かったので、ROSを利用できるノードで操作してみました。
▼以前WSL2のUbuntu 20.04にインストールした、ROS Noeticで試しています。
▼ROS NoeticのTurtlesimのページはこちら。
https://wiki.ros.org/turtlesim
▼以前の記事はこちら
Turtlesimを試してみる
早速Turtlesimを実行します。複数のターミナルを開いて、それぞれ以下のコマンドを実行します。
▼まずはroscoreを実行します。
roscore
▼以下のコマンドでカメが表示されます。
rosrun turtlesim turtlesim_node
▼キーボードで操作するためのteleop keyはこちら
rosrun turtlesim turtle_teleop_key
▼この状態で矢印キーを入力すると、操作できます。
rostopic listでこの状態でのTopicを確認してみました。
▼/turtle1/cmd_vel、turtle1/color_sensor、turtle1/poseなどがあります。
rostopic echo turtle1/poseで、turtle1/poseの値を見てみました。
▼x、y、theta、linear_velocity、angular_velocityの値が順次表示されました。
Node-REDで操作する
利用するノードについて
ROS関係のノードでnode-red-contrib-rosとnode-red-contrib-flowake-ros-nodesがあったのですが、前者ではバグのような現象が見られました。
▼node-red-contrib-rosだと、ros-publishなどの一部のノードの設定を開こうとすると、エディタの操作ができなくなることがありました。
https://flows.nodered.org/node/node-red-contrib-ros
今回は後者のnode-red-contrib-flowake-ros-nodesを利用しています。
▼こちらのノードです。
https://flows.nodered.org/node/node-red-contrib-flowake-ros-nodes
インストールする際はご注意ください。以降ではrosノードと呼称します。
▼以下のようなノードが追加されます。
ノードの設定
rosノードではROS Bridgeと通信することが前提となっています。まずはROS Bridgeを実行します。
▼ROS Bridgeのインストールについては、以前の記事をご覧ください。
▼コマンドはこちら
roslaunch rosbridge_server rosbridge_websocket.launch
▼実行すると、サーバーがws://0.0.0.0:9090で起動します。
ros subノードの設定を見てみます。
ROS ServerのUrlが最初はws://ipaddress:9090になっています。このipaddressの部分を変更する必要があります。
▼どのrosノードでもいいのですが、ROS Serverの欄にある鉛筆マークで編集します。
ROSを起動しているPCとNode-REDを起動しているPCが同じであれば、localhostに変更すれば通信できます。異なるPCで実行している場合は、ROSを起動しているPCのIPアドレスを設定してください。
▼今回はlocalhostに変更しました。
データを受信する
ros subノードを利用して、Subscribeします。
▼フローはこちら。
rostopic listを実行したときに含まれていた、/turtle1/cmd_velのTopicを受信してみます。
▼ros subノードの中身はこちら。
▼受信はできていますが、"[object Object]"のMessageとして受信されています。
jsonノードで一度変換してみました。
▼stringで値を取得できています。
さらにjsonノードを入れることで、Object型として受信することができました。
▼それぞれのプロパティの値を受信したりするなら、この形式が一番扱いやすいです。
データを送信する
ros pubノードを利用して、Publishします。
▼フローはこちら
injectノードのmsg.payloadに先程受信したデータ形式で送信します。
▼msg.payloadでJSONを選択し、右側の三つの点を選択すると、JSON形式の編集画面が開きます。
▼linearのxが2だったのを、10に変更してみました。
これでDeploy後、injectノードを実行するとカメを動かすことができました。
▼進みすぎて、ウィンドウに衝突していました。
▼linearとangularの値を設定すると、円を描いて動きました。
▼linearのyも設定すると、横向きに進みました。
ダッシュボードで操作してみる
データ形式は分かったので、Node-REDのdashboardノードで操作してみました。
▼全体のフローはこちら。
[{"id":"5d946bfdb80ee978","type":"ros-publish","z":"6f967b24d95ab8d8","server":"ed72b9d48a11624b","topicname":"/turtle1/cmd_vel","msgtype":"std_msgs/String","x":1240,"y":660,"wires":[]},{"id":"9667398f7ad70baa","type":"inject","z":"6f967b24d95ab8d8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"linear\":{\"x\":10,\"y\":0,\"z\":0},\"angular\":{\"x\":0,\"y\":0,\"z\":0}}","payloadType":"json","x":1050,"y":580,"wires":[["5d946bfdb80ee978"]]},{"id":"e52a7f0b631f5b7e","type":"inject","z":"6f967b24d95ab8d8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"linear\":{\"x\":2,\"y\":5,\"z\":0},\"angular\":{\"x\":0,\"y\":0,\"z\":0}}","payloadType":"json","x":1050,"y":620,"wires":[["5d946bfdb80ee978"]]},{"id":"7607c6c2e178c62c","type":"template","z":"6f967b24d95ab8d8","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"linear\": {\n \"x\": {{flow.turtle.linear.x}},\n \"y\": {{flow.turtle.linear.y}},\n \"z\": {{flow.turtle.linear.z}}\n },\n \"angular\": {\n \"x\": {{flow.turtle.angular.x}},\n \"y\": {{flow.turtle.angular.y}},\n \"z\": {{flow.turtle.angular.z}}\n }\n}","output":"json","x":1060,"y":660,"wires":[["5d946bfdb80ee978"]]},{"id":"0ba4ce9d5055c330","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"linear x","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":660,"wires":[["9abf74891a5bcf1d"]]},{"id":"e53a8a520630e3fb","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"linear y","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":700,"wires":[["362072607bb35fe7"]]},{"id":"c40154d12e88e4e7","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"linear z","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":740,"wires":[["5e48592824959fba"]]},{"id":"d573f982d06b14e9","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"angular x","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":780,"wires":[["b64362cd66da4d39"]]},{"id":"0d534d53bcc2e62e","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"angular y","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":820,"wires":[["745383263e8cd6f7"]]},{"id":"8ba49015737643fe","type":"ui_slider","z":"6f967b24d95ab8d8","name":"","label":"angular z","tooltip":"","group":"f1fe02d39b684f8f","order":0,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"-10","max":10,"step":1,"className":"","x":620,"y":860,"wires":[["f54c5b7a210aa608"]]},{"id":"9abf74891a5bcf1d","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.linear.x","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":660,"wires":[["7607c6c2e178c62c"]]},{"id":"8a404091350e4a73","type":"ui_button","z":"6f967b24d95ab8d8","name":"","group":"f1fe02d39b684f8f","order":6,"width":0,"height":0,"passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"0","payloadType":"num","topic":"topic","topicType":"msg","x":390,"y":760,"wires":[["94e001c40acfac44"]]},{"id":"e958776a36abba1b","type":"inject","z":"6f967b24d95ab8d8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":390,"y":720,"wires":[["94e001c40acfac44"]]},{"id":"362072607bb35fe7","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.linear.y","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":700,"wires":[["7607c6c2e178c62c"]]},{"id":"5e48592824959fba","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.linear.z","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":740,"wires":[["7607c6c2e178c62c"]]},{"id":"b64362cd66da4d39","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.angular.x","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":780,"wires":[["7607c6c2e178c62c"]]},{"id":"745383263e8cd6f7","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.angular.y","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":820,"wires":[["7607c6c2e178c62c"]]},{"id":"f54c5b7a210aa608","type":"change","z":"6f967b24d95ab8d8","name":"","rules":[{"t":"set","p":"turtle.angular.z","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":820,"y":860,"wires":[["7607c6c2e178c62c"]]},{"id":"94e001c40acfac44","type":"junction","z":"6f967b24d95ab8d8","x":480,"y":760,"wires":[["0ba4ce9d5055c330","e53a8a520630e3fb","c40154d12e88e4e7","d573f982d06b14e9","0d534d53bcc2e62e","8ba49015737643fe"]]},{"id":"ed72b9d48a11624b","type":"ros-server","url":"ws://localhost:9090"},{"id":"f1fe02d39b684f8f","type":"ui_group","name":"Turtlesim","tab":"7f2b0e1192b72dcc","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"7f2b0e1192b72dcc","type":"ui_tab","name":"ROS","icon":"dashboard","disabled":false,"hidden":false}]
スライダーでプロパティの値を設定し、先程のデータ形式に合わせてtemplateノードで送信するようになっています。
一番先頭にあるinjectノードとResetボタンは、スライダーの値をすべて0にするためのものです。
▼templateノードの中身はこちら。それぞれの変数をflow.turtleのプロパティから設定し、JSON形式で送信しています。
▼sliderノードでは、-10から10の値を設定しています。スライドするごとに値が送信されます。
▼changeノードでは、flow.turtleのプロパティにスライダーの値を設定しています。
▼実際にカメを操作したときの様子はこちら。今回は2次元なので、linear z、angular x、angular yの値は変更しても動いていません。
▼操作できました。
最後に
Node-REDでTurtlesimのカメを操作出来たので、同じようにしてROSに接続しているロボットを操作することができそうだなと思っています。まずはシミュレーションで試してみて、実機を動かしてみたいところです。