ROS講座03 Pub & Sub 通信

# 環境
この記事は以下の環境で動いています。


| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 20.04 |
| ROS | noetic |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
このページはROSチュートリアルの[シンプルな配信者(Publisher)と購読者(Subscriber)を書く(C++)](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)とほぼ同等の内容です。このシリーズの流儀で少し書き直しています。

# Pub & Sub通信
ROSのメインコンセプトはプロセス間でデータ通信です。ROSのプログラムの最小単位はROSノードです（基本的にに1つのROSノードは1プロセスに対応します）。ROSノードは単純な処理を行い、ロボットの複雑な処理は多数のROSノードが通信しあって共同で実行します。本来プロセス間通信を行うのは意外と面倒ですが、ROSフレームワークではPub&Sub型の通信が用意されています。各経路には名前がついて区別され、ROSトピック呼びます。
以下の図の例では`basic_simple_talker`という名前のROSノードが送信をして、`basic_simple_listener`という名前のROSノードが受信をします。この2つは`chatter`という名前の付いたROSトピックの経路でデータの送受信をします。また受け渡しをするデータの型はROSメッセージと呼びます。今回使ったものは文字列を送る`std_msgs::String`型です。基本的なデータ型として`std_msgs::Float64`や`std_msgs::Int32`があったり、カメラ画像を表す`sensor_msgs::Image`やパスを表す`nav_msgs::Path`等があります。

![basic_pubsub.png](https://qiita-image-store.s3.amazonaws.com/0/254442/0b59b42c-6381-c5a2-25c7-6b22052386ec.png)

# 前準備
ROSのプログラムを作るまでの前準備を行います。

* ROSのインストール
* catkin_wsの作成
* ROSパッケージの作成

この3つは[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)で解説しているのでそちらをご覧ください。
今回は`~/catkin_ws`にあるcatkin_wsの中の`basic_lecture`というROSパッケージの中にプログラム作るということを前提に解説します。

# ソースファイル
## Publisherのコーディング
```c++:basic_lecture/src/basic_simple_talker.cpp
#include <ros/ros.h>
#include <std_msgs/String.h>

int main(int argc, char** argv)
{
  ros::init(argc, argv, "basic_simple_talker");
  ros::NodeHandle nh;
  ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 10);
  ros::Rate loop_rate(10);

  while (ros::ok())
  {
    std_msgs::String msg;
    msg.data = "hello world!";
    ROS_INFO("publish: %s", msg.data.c_str());
    chatter_pub.publish(msg);

    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}
```

* `ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 10);`でROSのPublisherを作成します。
    * 通信するROSトピックの名前は`chatter`
    * 通信するROSトピックの型は`std_msgs::String`
    * 最後の引数の数字はバッファーサイズです。基本的に10程度の値を入れておけば十分です。

## Subscriberのコーディング
```c++:basic_lecture/src/basic_simple_listener.cpp
#include <ros/ros.h>
#include <std_msgs/String.h>

void chatterCallback(const std_msgs::String& msg)
{
  ROS_INFO("subscribe: %s", msg.data.c_str());
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "basic_simple_listener");
  ros::NodeHandle nh;
  ros::Subscriber sub = nh.subscribe("chatter", 10, chatterCallback);

  ros::spin();
  return 0;
}
```

`ros::Subscriber sub = n.subscribe("chatter", 10, chatterCallback);`の2番目の引数の整数値は受信バッファーサイズです。リアルタイム性の高いトピック（=常に最新の値が欲しいトピック）では小さい数字にします。また、取りこぼしがあってほしくないトピックでは大きめの値を選びます。
公式のROSwikiのtutrialでは1000という非常に大きい値になっていますが、このように大きい数字にすると処理がバグることがあるので推奨しません。

## CmakeListの設定
パッケージ直下に`CmakeLists.txt`がありここにビルド用の情報を書き足します。今回の場合は`CmakeLists.txt`に以下の4行を書き加えるだけです。

```cmake:CmakeLists.txtに追加
project(basic_lecture)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

include_directories(
  ${catkin_INCLUDE_DIRS}
)

add_executable(basic_simple_talker   src/basic_simple_talker.cpp)
add_executable(basic_simple_listener src/basic_simple_listener.cpp)

target_link_libraries(basic_simple_talker
  ${catkin_LIBRARIES}
)
target_link_libraries(basic_simple_listener
  ${catkin_LIBRARIES}
)
```

* `project()`はこのROSパッケージの名前を入れます。
* `find_package()`ではこのROSパッケージが依存するROSパッケージ名を入れます
* `include_directories()`ではヘッダファイルのディレクトリの依存を記述します。基本的に`${catkin_INCLUDE_DIRS}`だけで充分です。
* `add_executable()`では実行ファイルの生成の設定を書きます。1番目が実行ファイル名（自由に付けられる）で、2番目ではソースファイルを列挙します。
* `target_link_libraries()`ではライブラリの依存を書きます。基本的に`${catkin_LIBRARIES}`だけで十分です。

# ビルド
```shell
cd ~/catkin_ws
catkin build
```
catkinワークスペースで`catkin build`コマンドを実行するとワークスペース中のすべてのパッケージのビルドを始めます。コンパイルエラーが出た場合はここで表示されます。

# 実行
roscoreを実行するターミナル、simple_talkerを実行するターミナル、simple_listenerを実行するターミナルの3つのターミナルを使います。rosノードを実行するためにはあらかじめroscoreを実行する必要があります。各ターミナルごとに実行前に`source ~/catkin_ws/devel/setup.bash`を実行する必要があります。

```shell:1つ目のターミナル
roscore
```
```shell:2つ目のターミナル
rosrun basic_lecture basic_simple_talker
```
```shell:3つ目のターミナル
rosrun basic_lecture basic_simple_listener
```

#結果
3つ目のターミナルで以下のような表示が出てきて、`/chatter`トピックが受信できていることが確認できました。

![basic_pubsub1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/e66d3c40-f517-983f-9af9-8deea05b6676.png)


# 参考
[ROS wiki: publishとsubscribe](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)