ROSで簡単な通信を行う(C++)

##はじめに

[こちらの記事](https://qiita.com/keinko/items/7d0f26c19414291660d9)でpythonによるROSの動かし方はまとめられたが、C++になると少しめんどくさくなるのでこちらの記事にまとめます。
「めんどくさくなる」ってよりもpythonでやるのが簡単すぎるのかもしれませんが…。
参考にするのは[公式チュートリアルの記事](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)です。
ROSのバージョンはmelodicです。

##通信システムの作成

###準備
いつもの準備。まずは作業用のディレクトリの作成。

```
$ cd ~/catkin_ws/src
$ catkin_create_pkg beginner_tutorials rospy roscpp std_msgs
$ cd ..
$ catkin_make
```

###コーディング
今回は`beginner_tutorials/src`の中に`talker.cpp`ファイルと`listener.cpp`ファイルを作成します。

```
$ cd ~/catkin_ws/src/beginner_tutorials/src
$ touch talker.cpp
$ listener.cpp
```

以下をコピペ。

```cpp:talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");
  ros::NodeHandle n;
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  ros::Rate loop_rate(10);
  int count = 0;
  while (ros::ok())
  {
    std_msgs::String msg;
    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
    ++count;
  }
  return 0;
}
```

```cpp:listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
  ros::init(argc, argv, "listener");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
  ros::spin();
  return 0;
}
```

###CMakeLists.txtの編集
pythonと違ってC++の場合は、上記で記述したコードから実行ファイルというものを生成する必要があります。
その実行ファイルの生成に関する内容を記述したものが、`beginner_tutorials`の中にある`CMakeLists.txt`というファイルで、実行ファイルの生成を行うコマンドが`catkin_make`です。

`CMakeLists.txt`を編集していきます。
ここではgeditを使いますが好きなエディタを使ってください。

```terminal
$ roscd beginner_tutorials
$ gedit CMakeLists.txt
```

`CMakeLists.txt`の一部を変更します。
とても長いので編集する該当部分だけ下記に記述します。

```cmake:CMakeLists.txt

...省略...

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/beginner_tutorials.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
add_executable(${PROJECT_NAME}_talker src/talker.cpp)
add_executable(${PROJECT_NAME}_listener src/listener.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_listener
  ${catkin_LIBRARIES}
)
target_link_libraries(${PROJECT_NAME}_talker
  ${catkin_LIBRARIES}
)

...省略...

```
`#`の行はコメントです。
見比べればわかるのですが、ほとんど元からあった`CMakeLists.txt`と変わりはなく、`## Declare a C++ executable`と`## Specify libraries to link a library or executable target against`の部分しか変更していません。

###ビルド
`CMakeLists.txt`を書き終えたらビルドを行います。

```
$ cd ~/catkin_ws
$ catkin_make
```

無事、ビルドが成功すると`beginner_tutorials_talker`と`beginner_tutorials_listener`という実行ファイルが生成されます。
確認方法は以下のとおりです。

```
$ ls ~/catkin_ws/devel/lib/beginner_tutorials
beginner_tutorials_listener  beginner_tutorials_talker
```

###実行テスト
ターミナルを3つ開いて以下を実行します。

```terminal:ターミナル1
$ roscore
```

```terminal:ターミナル2
$ rosrun beginner_tutorials beginner_tutorials_talker
```

```terminal:ターミナル3
$ rosrun beginner_tutorials beginner_tutorials_listener
```

実行結果が以下のとおり。
無事、`beginner_tutorials_talker`から取得した文字列を`beginner_tutorials_listener`が取得できていることがわかります。

```terminal:ターミナル3
$ rosrun beginner_tutorials beginner_tutorials_listener
[ INFO] [1562134298.736161500]: I heard: [hello world 251]
[ INFO] [1562134298.836232511]: I heard: [hello world 252]
[ INFO] [1562134298.936255416]: I heard: [hello world 253]
[ INFO] [1562134299.036248224]: I heard: [hello world 254]
[ INFO] [1562134299.136285550]: I heard: [hello world 255]
[ INFO] [1562134299.236255308]: I heard: [hello world 256]
[ INFO] [1562134299.336183640]: I heard: [hello world 257]
[ INFO] [1562134299.436183165]: I heard: [hello world 258]
```