WSL2のROSとWebSocket通信(ROS Bridge、Node-RED、Gazebo)
はじめに
今回はWSL2のROS NoeticとNode-REDで、WebSocket通信を行いました。
ROSで計算した数値をNode-REDで取得したかったのですが、調べているとROS Bridgeというものを見つけました。WebSocketのサーバーを起動するようです。
ROS Bridgeを利用して、Node-REDのrosノードとの通信を試してみました。
▼以前WSL2で環境を構築した、Ubuntu 20.04のROS Noeticの環境で試しました。
▼以前の記事はこちら
Gazeboの起動
ROS側のシミュレーションを行うのに、Gazeboを起動しました。
▼こちらのページを参考にしました。
https://emanual.robotis.com/docs/en/platform/openmanipulator_x/ros_simulation/#launch-gazebo
▼ROS Kineticの環境構築を行ったときもGazeboを起動しました。
▼コマンドはこちら
roslaunch open_manipulator_gazebo open_manipulator_gazebo.launch
▼Kineticのときは黒色だったのですが、こちらは白色なんですね。

一度閉じてから再度起動しようとすると、画面にOpen Manipulatorが表示されないことがありました。この事象への対処方法がよく分かっていません。
roscoreを実行してから起動すると表示されたり、Gazeboを閉じてターミナルを終了させて時間を置くと表示されたりといった具合です。プロセスをちゃんと終わらせるなどの手順を踏む必要があるのかもしれません。
rostopic listを実行すると、/joint_statesがありました。
▼他にもgazeboやgripperのTopicがありました。

▼rostopic echoで値を表示してみました。
rostopic echo /joint_states
▼positionやvelocityなどの値が表示されています。

今回もGUIのアプリで操作しました。別のターミナルで、open_manipulator_controllerを起動します。
▼コマンドはこちら
roslaunch open_manipulator_controller open_manipulator_controller.launch use_platform:=false
▼open_manipulator_controllerが起動しました。

さらに別のターミナルで、open_manipulator_control_guiを起動しました。
▼コマンドはこちら
roslaunch open_manipulator_control_gui open_manipulator_control_gui.launch
▼GUIのアプリが起動しました。

▼GUIのアプリでGazebo上のOpen Manipulatorを操作できました。

ROS Bridgeの起動
後でROSとUnreal Engine 5、Node-RED間で通信できるようにしたかったのですが、Unreal Engine 4だとROSと通信している記事がありました。そこではROS Bridgeが使われていました。
▼Unreal Engine 4とROSの接続に関するページはこちら。しかもWSLで起動したROSです。
今回はROS BridgeでNode-REDと通信してみました。
▼インストールや起動に関するコマンドはこちらにまとまっていました。
https://qiita.com/bypenguinsan/items/f19ebf8686a9e1c81251
▼インストールするためのコマンドはこちら
sudo apt install ros-noetic-rosbridge-server
pip3 install pyyaml rospkg twisted cryptography six pyopenssl autobahn tornado bson pymongo
sudo apt install python3-pip
▼ROS Bridgeを起動するためのコマンドはこちら
roslaunch rosbridge_server rosbridge_websocket.launch
WSL2のROSとROS Bridgeで通信していた方の記事がありました。ポートフォワーディングなどのWSL2特有の問題は、そちらの記事が詳しかったのでぜひご覧ください。
▼こちらの2つの記事です。
https://qiita.com/koichi_baseball/items/fbab3acbcd41b18bae2e
https://qiita.com/koichi_baseball/items/8d15a06d296c09908f5f
2つ目の記事のWSL2を利用する場合という項目に従って、ポートフォワーディングとファイアフォールの設定を行っておきました。
▼実行したコマンドはこちら
netsh.exe interface portproxy add v4tov4 listenport=9090 connectaddress=(wsl -d Ubuntu-18.04 exec hostname -I).trimend()
New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort 9090 -Action Allow -Protocol TCP
New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort 9090 -Action Allow -Protocol TCP
▼管理者権限でないとエラーが出ます。

▼9090ポートに対して設定が行われました。

Node-REDとの通信
rosノードとの通信
今回はROSと通信するためのノードがあったので使ってみました。デフォルトで9090ポートに接続するようになっており、ROS Bridgeを使う前提になっていました。
▼こちらのノードです。
https://flows.nodered.org/node/node-red-contrib-ros
▼roslibjsを使って通信を行っているようです。
https://github.com/RobotWebTools/roslibjs
▼インストールすると、以下のノードが追加されました。

シミュレータの値を取得したいので、ros subノードを使いました。
シミュレータを起動しているとデバッグウィンドウが更新され続けるので、必要に応じて停止してください。
▼ノードはこちら

ダブルクリックすると、設定画面が開きます。
▼アドレスとTopicを設定できるようになっています。

▼IPアドレスは、hostname -Iコマンドで確認できます。Wi-Fiの接続先によって変わるかもしれないので、確認してください。

▼Topicは/joint_statesにしました。

▼Node-REDを起動しているPCでROSを起動していたので、localhostでも接続できました。

接続できれば、デバッグウィンドウに値が表示されます。
▼[object Object]と表示されています。

このままだとデータが分からないので、JSON形式に変換します。
▼間にjson parserノードを挟みました。

▼以下のようにデータが表示されました。

▼json parserノードでFormat JSON stringにチェックを入れると、JSON形式の表示になります。

▼改行が入って表示されました。

まだString扱いだったので、もう一つjson parserノードを入れてみました。
▼フローはこちら

▼Objectとして取得できています。

必要な値を取得する
JSON形式で取得できたので、必要な値だけ取得してみます。
データの構造を見てみます。
▼json parserノードを一回だけ使ったときのデータです。
{
"header": {
"seq": 369887,
"stamp": {
"secs": 380,
"nsecs": 534000000
},
"frame_id": ""
},
"name": [
"gripper",
"gripper_sub",
"joint1",
"joint2",
"joint3",
"joint4"
],
"position": [
-0.009999850612568705,
-0.01000003340841413,
0.0000074898467614659126,
-1.0500019201389437,
0.3499999391156736,
0.6999999294534911
],
"velocity": [
0.00010996376671220241,
-0.00007053669109618759,
0.00004075268444818835,
-0.0019201386276293708,
-0.00006088453076575967,
-0.00007054656478616781
],
"effort": [
-0.000003827597076962175,
-0.000005135684557310005,
0,
0,
0,
0
]
}
"name"に要素の名前、"position"に角度などの情報が入っています。これらの値を取り出してみます。
▼コントローラの数値と照らし合わせると、joint1~4の角度が入っていることが分かります。

▼こういったNode-REDでのデータの扱いについては、以前の記事が参考になると思います。
changeノードで値を取り出してみました。
▼フローはこちら

デバッグウィンドウに表示されるデータ量が多くて見づらいかもしれません。表示させたくないdebugノードは、右側のボタンを押してオフにするか削除してください。
changeノードでは、msg.nameにmsg.payload["name"]、msg.positionにmsg.payload["position"]を代入するようにしました。
▼changeノードの中身はこちら

debugノードはデフォルトだとmsg.payloadの値を出力するのですが、今回はmsg.nameとmsg.positionに代入したので、その値を表示するようにしました。
▼debugノードの中身はこちら


▼それぞれのプロパティに、配列として値が入っています。

見やすいように、要素名をkeyに、数値をvalueにした連想配列として出力してみます。今回はjointだけ表示するようにしました。
▼フローはこちら。functionノードを追加しました。

functionノードにはJavascriptでmsg.nameをkeyに、msg.positionをvalueとした連想配列を作成しました。
▼コードはこちら
let joint={}
let joint_name = ""
for(let i=2; i<6; i++) {
joint_name = msg.name[i]
joint[joint_name]= msg.position[i]
}
msg.joint = joint
return msg

▼このように表示されました。

GUIのアプリで操作すると、その値がちゃんと反映されていました。
▼すごいスピードで処理が行われていました。

最後に
ROSで起動したGazeboのJointの角度をNode-REDで取得できたので、さらにUnreal Engine 5と連携させる予定です。これで運動学や逆運動学の計算はROSで行い、画面の描画はUE5で行うことができると考えています。
データ量が多いとdebugノードへの表示が遅れることがあって、処理が追いつくのかが気になっています。通信量を制限するなどの工夫が必要になりそうです。
▼メッセージの流量制限については、delayノードを使った資料がありました。これで確かに制限できました。