【ROS/WSL2】ROS Bridgeを使用した複数マシン(複数ROS master)間の通信

# はじめに
[ROS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ros)の2日目の記事です！

前回の記事([【ROS/WSL2】WSL2上のROSと外部PCで通信してみたかった](https://qiita.com/koichi_baseball/items/fbab3acbcd41b18bae2e))でWSL2上のROSと、外部PCとのROSの通信を試みたのですが、うまくいきませんでした。

今回は、[ROS Bridge](http://wiki.ros.org/rosbridge_suite)を使用することで通信することができたので紹介します！

ROSノードは以下で公開しています。


<a href="https://github.com/koichirokato/ros_to_rosbridge"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/koichirokato/ros_to_rosbridge.png" width="460px"></a>

超概要はこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426354/f30ff6c2-dca7-da87-62f5-ed6ba5af58fc.png)

これを使用すると、
 - 複数マシン＆複数ROS master(roscore)間の通信が可能になります。
 - 任意のトピックをもう一方へpublishすることができます。
 - トピック名を区別することも可能です。もちろん、そのままもできます。
   例(どちらのパターンもできます)：
     /cmd_vel => /cmd_vel
     /cmd_vel => /robot_a/cmd_vel


# 環境
以下の環境での動作を確認しています。

 - PC1  
   - Jetson Nano
   - ROS Melodic
 - PC2
   - Windows10
   - WSL2
   - ROS Melodic

:::note alert
Ubuntu 16.04では動作の確認ができていません
(roslibpyのインストールが正常にできない)
:::

# 環境の準備
## 依存関係のインストール
使用するソフトウェアは
 - ROS Bridge (サーバー)
 - ROS Bridge (Pythonクライアント)
 - ROSpy Message Converter (http://wiki.ros.org/rospy_message_converter)
になります。
以下のコマンドでインストールします。


```shell
$ sudo apt-get install ros-<rosdistro>-rosbridge-server
$ pip install roslibpy
$ sudo apt-get install ros-<rosdistro>-rospy-message-converter
```

## ※WSL2を使用する場合
<details><summary>WSL2用の設定</summary><div>

WSL2を使用する場合、WindowsからWSL2上のUbuntuへポートフォワーディングの設定とファイアフォールの設定を行う必要があるため以下のコマンドを実行してください。ここではROS bridgeを9090ポートで使用する前提になります。

```shell:ポートフォワーディングの設定
$ netsh.exe interface portproxy add v4tov4 listenport=9090 connectaddress=(wsl -d Ubuntu-18.04 exec hostname -I).trimend()
```

```shell:Firewallの設定
$ 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
```

</div></details>


# 起動
ここで起動方法を紹介するROSノードは、デフォルトでは起動時に`rostopic list`で確認できる**すべてのトピック**をブリッジします。
もちろん除外するトピックの指定や、特定のトピックのみをブリッジすることも可能です。

ROS bridgeのWebsocketサーバを起動しておきましょう
**複数マシンで試す際はどちらのマシンでも起動してください。**

```shell
$ roslaunch rosbridge_server rosbridge_websocket.launch
```

※確認のため、例えばカメを動かしたい場合はこのタイミングで起動しておきます。

```shell:PC1でカメのシミュレーションを起動
$ rosrun turtlesim turtlesim_node
```

```shell:PC2でカメのTeleopを起動
$ rosrun turtlesim turtle_teleop_key
```

## 起動方法
詳細は後述しますが起動方法です。ローカルでsubscribeし、publishさせたい方(ブリッジさせたい方)のみの起動で動作の確認はできます。
起動時のパラメータも以下にまとめます。リモートのホストのIPアドレスを入力する`remote_host`を変更することで外部のPCへ接続します。他のパラメータは必要に応じて変更してください。

```shell
$ roslaunch ros_to_rosbridge rosbridge_to_rosbridge.launch remote_host:={remote PC ip address}
```

| パラメータ名 | デフォルト | 説明 |
|:-:|:-:|:-:|
| local_host | 127.0.0.1 | ローカルのWebsocketサーバが起動しているのIPアドレス |
| local_port | 9090 |  ローカルのWebsocketサーバが起動しているポート |
| remote_host | 127.0.0.1 | リモートのWebsocketサーバが起動しているIPアドレス(**※多くの場合は要変更**) |
| remote_port | 9090 | リモートのWebsocketサーバが起動しているのポート |
| use_id_for_ns | false | confファイルのidをトピックにつけるか否か(/cmd_velトピックが、/id/cmd_velのようになります。) |

## 設定ファイル
`ros_to_rosbridge/conf`フォルダにブリッジするトピックなどの設定ファイルがあります。
デフォルトは以下のようになっています。
`/turtle1/*`のように、ワイルドカードの指定も可能です。

**`include_topics`でトピックを指定すると、そのトピックのみブリッジします。**

```text:ros_to_rosbridge/conf/config.yaml
id : robot_a
exclude_topics:
  - /rosout
  - /rosout_agg
  - /client_count
  - /connected_clients
  # - /turtle1/*
include_topics:
  # - name : /cmd_vel 
  #   type : geometry_msgs/Twist
  # - name : /odom
  #   type : nav_msgs/Odometry
```

| 項目 | 説明 |
|:-:|:-:|
| id | ブリッジ先のトピック名冒頭につけるid |
| exclude_topics | ブリッジが除外されるトピック |
| include_topics | ブリッジされるトピック(トピック名とデータ型) |

# ROSノードの概要
実は今回は、2種類の方法で実装してみました。
↑の起動は1の方です。2はGitHubの[ここ](https://github.com/koichirokato/ros_to_rosbridge/blob/main/scripts/ros_to_rosbridge.py)にひっそりとあります。
1. ローカルのROS bridge から リモートのROS Bridgeへ
2. ローカルのROSの通信からリモートのROS bridgeへ

メリット(特徴)とデメリットは以下だと思っています。

| # | メリット(特徴) | デメリット |
|:-:|:-:|:-:|
| 1 | コード内でメッセージ型を呼び出さずに実装できる | ローカルの通信でもわざわざwebsocket通信を使う |
| 2 | ローカル通信はROS | メッセージ型はハードコーディングになる |

以下にイメージ図を示します。赤い資格で囲まれたROSノードが今回紹介しているノードです。
1のイメージは以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426354/08c36eb4-fd24-6df8-6a8e-52a28184173d.png)


2のイメージは以下になります。赤矢印部分が1と異なる部分です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426354/1255de9f-0543-6cc3-592b-a1dda24ed071.png)

## WSL2上のROSと通信する
このシステムは、WSL2上のROSでも使用することができます。
というかそれがやりたくて色々探していたら思っていたより大きめなシステムになってしまいました。

ポートフォワーディングの設定をすることで、以下のように通信することができます。
おそらく9090番という特定のポートだけを開ければよかったので通信が実現できたのではないかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426354/8c47f106-d92f-9989-2e98-acf45625d723.png)


# おわりに
なんとか目指していたシステムを作ることができました！
GitHubにちゃんと上げて公開するのは初めてです。何もないかもしれませんが覗くだけでも覗いていってくれたらうれしいです。

LGTMなども励みになります！最後までご覧いただきありがとうございました。

# 参考
https://github.com/RyanDraves/wsl2_ros_bridge


https://gitlab-extern.ivi.fraunhofer.de/keppler/ros_to_rosbridge/-/tree/master