【図解】ROS2の基礎について分かりやすく解説する（ノード、トピック、メッセージ）

お疲れ様です。秋並です。

今回は、ROS2の基礎的な概念である
- ノード (node)
- トピック (topic)
- メッセージ (message)
- パブリッシャー (publisher)
- サブスクライバー (subscriber)

について、解説したいと思います。

## 動作環境
ubuntu 22.04
ROS humble

# ノード

ノードとは、**ロボットシステムにおける最小単位の機能モジュール**を指します。

ノード間で通信することで、全体のシステムが構成されることになります。

言葉の説明だけでは分かりにくいと思うので、具体的な例を使って説明します。

**例）ノード1→ノード2に情報を渡し、受け取った情報をもとにノード2が処理を行う**

- ノード1：カメラから得た画像情報を他のノードに受け渡すノード
- ノード2：ノード1から取得した画像情報をもとにロボットアームを適切な位置に制御するノード

![node_ex1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/01cdc4c6-9e72-8d55-46de-c1794d35144f.gif)

---

ノードのイメージとしては上記の通りですが、

ここで、「カメラから取得した情報をもとに**物体認識**をしたい」という別の機能を実装したい場合を考えてみましょう。

それぞれのノードは独立しているので

- ノード1はそのままで、ノード2のみを別のノードに取り換える

ことで、この機能を実装することが出来ます。

![node_ex2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/8a569541-a7de-a97d-5c6a-2dad9cbed2df.gif)


このように、ノードを上手く構成することで

:::note 
他の機能を実装するときに、ノードを使いまわすことが出来る
:::

ことがうれしいポイントです。
<br>
そのため、既存のノードと自作のノードを組み合わせることで

**一からノードを作成するのと比較して、効率的に開発を進めることが可能**になります。

# トピック、メッセージ、パブリッシャー、サブスクライバー

ここまでで、「ノード間で情報の受け渡しを行う」ことでシステムが構成されることを説明しましたが、

次に、情報の受け渡しを行う際に必要になる

- トピック (topic)
- メッセージ (message)

について説明します。

先ほどの図でいうと、「情報の受け渡しを行うためのパイプ」がトピック、「受け渡される情報」がメッセージになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/48385e39-1074-79bd-dc94-a1147b4a0976.png)

---

この際に、「メッセージを特定のトピックに配信する機能」と「特定のトピックから流れてきたメッセージを購読する機能」が必要になるのですが、この機能を持つのが

- パブリッシャー (publisher)
- サブスクライバー (subscriber)

になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/4f17c6fb-b6ac-be01-4b3b-af59575ac371.png)


## メッセージの型

トピックで配信/購読するメッセージには変数と同様に型があります。

メッセージの型には、

- ROSで用意されているメッセージ
- 自身で型を定義するカスタムメッセージ

の2種類が存在します。
<br>
ここでは、ROSで標準で用意されているメッセージについて説明したいと思います。

### std_msgs
    
最初に、最もシンプルなメッセージが用意されている `std_msgs`について説明します（[ドキュメントリンク](https://docs.ros2.org/foxy/api/std_msgs/index-msg.html)）

例えば、 `std_msgs/msg/Int16.msg` は以下のような構成になっています（[ドキュメントリンク](https://docs.ros2.org/foxy/api/std_msgs/msg/Int16.html)）

```python
int16 data
```

メッセージの中身を確認すると、 `int16` 型の `data` という名前の変数で構成されていることが分かります。
<br>

### sensor_msgs
    
次に、センサー関連のメッセージが用意されている `sensor_msgs` について説明します（[ドキュメントリンク](https://docs.ros2.org/latest/api/sensor_msgs/index-msg.html)）

例えば、`sensor_msgs/msg/Image.msg` は以下のような構成になっています（[ドキュメントリンク](https://docs.ros2.org/latest/api/sensor_msgs/msg/Image.html)）

```python
std_msgs/msg/Header header
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
```

メッセージの中身を確認すると、 画像の高さ `height` 、画像の幅 `width` のような、画像に必要ないくつかのデータで構成されていることが分かります。
<br>
ここで、上記メッセージの `std_msgs/msg/Header header` という箇所に着目してみると、型が `std_msgs/msg/Header` というメッセージの型になっていることが分かります。

このように、

- メッセージの定義の中で、他のメッセージの型の変数を定義することが出来る

ということも抑えておくとよいと思います。
    

:::note warn 
パブリッシャーとサブスクライバーについては、以下の点に注意が必要です。

- 配信する側の「トピックの名前」「メッセージの型」と、購読する側の「トピックの名前」「メッセージの型」が同じでないと、メッセージのやり取りができない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/99dcebf0-0268-3267-7a5b-0fbb819d44ae.png)
:::

# テストノードを動かして確認してみる

ここからは実際にROS2を動かして、流れを確認してみましょう。

ターミナルにて以下のコマンドを実行して下さい。（テストコードは[ここ](https://github.com/ros2/demos/blob/rolling/demo_nodes_py/demo_nodes_py/topics/talker.py)で公開されています）

```bash
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_py talker
```

ターミナルをもう一つ開き以下コマンドを実行して下さい。（テストコードは[ここ](https://github.com/ros2/demos/blob/rolling/demo_nodes_py/demo_nodes_py/topics/listener.py)で公開されています）

```bash
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_py listener
```

listenerを起動したターミナルで以下のように表示されていたらOKです。

```bash
[INFO] [1704025540.482092194] [listener]: I heard: [Hello World: 0]
[INFO] [1704025541.449221536] [listener]: I heard: [Hello World: 1]
[INFO] [1704025542.448992738] [listener]: I heard: [Hello World: 2]
[INFO] [1704025543.453376645] [listener]: I heard: [Hello World: 3]
[INFO] [1704025544.449385635] [listener]: I heard: [Hello World: 4]
```

実行したノードの処理の流れは以下のようになります。

1. talker（ノード1）側で、メッセージに「Hello World: %d」というデータを格納（%dには0からカウントされていく整数が代入される）
2. talker（ノード1）がchatter（トピック）にメッセージを配信 (Publish)
3. listener（ノード2)はchatter（トピック）から流れてきたメッセージを受信 (Subscribe)
4. 受信したメッセージのデータをlistener（ノード2）側で表示

![node_ex3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/371b9ad5-0d4b-300f-44c5-8cc737bd5f36.gif)


## ノード、トピック、メッセージを確認する

ros2にはノードやトピックの状態を確認するために便利なコマンドが用意されています。

たくさんの種類のコマンドがありますが、ここでは頻繁に使用するコマンドを紹介したいと思います

### ros2 node

`ros2 node` コマンドはノードに関する情報を確認することが出来ます。

試しに、（2つのテストノードは起動したままの状態で）新しいターミナルを起動し以下のコマンドを入力してみてください。

```bash
ros2 node list
```

コマンドを実行すると以下のように、「現時点で存在しているノード」の一覧を確認することが出来ます。

```bash
/listener
/talker
```

---

`ros2 node info` コマンドは、ノードがどのようなパブリッシャーやサブスクライバーを保有しているか等の「ノードの構成」について確認することが出来ます。

```bash
ros2 node info /talker
```

コマンドを実行すると以下のように表示されます。

```bash
Subscribers:

  Publishers:
    /chatter: std_msgs/msg/String
    /parameter_events: rcl_interfaces/msg/ParameterEvent
    /rosout: rcl_interfaces/msg/Log
  Service Servers:
    /talker/describe_parameters: rcl_interfaces/srv/DescribeParameters
    /talker/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
    /talker/get_parameters: rcl_interfaces/srv/GetParameters
    /talker/list_parameters: rcl_interfaces/srv/ListParameters
    /talker/set_parameters: rcl_interfaces/srv/SetParameters
    /talker/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
  Service Clients:

  Action Servers:

  Action Clients:
```

### ros2 topic

 `ros2 topic` は、トピックに関する情報を確認できるコマンドです。

`ros2 topic list`コマンドは、「現時点で存在するトピックの一覧」を確認することが出来ます（ `/rosout` と `/parameter_events` は常に存在するトピックになります）

```bash
ros2 topic list
```

```bash
/chatter
/parameter_events
/rosout
```

---

`ros2 node info` コマンドは、「トピックに関する情報」を確認することが出来ます。

```bash
ros2 topic info /chatter
```

コマンドを実行すると以下のように表示されます。

```bash
Type: std_msgs/msg/String
Publisher count: 1
Subscription count: 1
```

:::note
上記の場合 `std_msgs/msg/String` という型のメッセージが流れるトピックであるということが分かります。
:::

---

最後に、以下のコマンドを実行してみてください。

```bash
ros2 topic echo /chatter
```

コマンドを実行すると、以下のような表示が続くと思います。

```bash
data: 'Hello World: 0'
---
data: 'Hello World: 1'
---
data: 'Hello World: 2'
---
data: 'Hello World: 3'
---
data: 'Hello World: 4'
```

これは、 `/chatter` トピックから流れてきたメッセージをリアルタイムで表示しており、トピックからどのようなメッセージが流れてきているかを確認するときに使用できます。

逆に、 `ros2 topic echo /トピック名` を実行しても何も表示されない場合は「指定したトピックにメッセージが上手く配信できていない」ということになります。   

:::note
`listener` ノードではこのメッセージの中の `data` という情報を表示しています。
:::

### ros2 interface

 `ros2 interface` は、メッセージに関する情報を確認できるコマンドです。

`ros2 interface show` コマンドは、メッセージの内容について確認することが出来ます。

```bash
ros2 interface show std_msgs/msg/String
```

コマンドを実行すると以下のように表示され、 `std_msgs/msg/String` は `string` という型の `data` という変数で構成されていることが分かります。

```bash
# This was originally provided as an example message.
# It is deprecated as of Foxy
# It is recommended to create your own semantically meaningful message.
# However if you would like to continue using this please use the equivalent in example_msgs.

string data
```

# 最後に
今回は、
- ROS2の基礎

について解説しました。

この記事が少しでも皆さんのお役に立てれば幸いです。