ROS講座10 カスタムROSメッセージ

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

| 項目 | 値 |
|:-:|:-:|
| 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ノード間ではROSメッセージに基づいた型で通信が行われます。様々なROSメッセージ型がデフォルトで用意されていますが、自分でオリジナルのROSメッセージを作りたくなることがあります。自分で定義したROSメッセージ自体の作成、そのROSメッセージを使ったROSノードの作成を解説していきます。

# 流れ
1. カスタムROSメッセージの定義：ROSパッケージを作りmsgファイルの定義とビルドの設定を追加する。
2. カスタムＲOＳメッセージ使用：msgファイルをincludeして使います。

カスタムmsgを定義するROSパッケージと使うROSパッケージは別々の物にすることが推奨されてます。カスタムROSメッセージを定義するROSパッケージは`***_msgs`という名前にすることが推奨されています。

# カスタムROSメッセージの定義

ここでは`ros_lecture_msgs`というROSパッケージを作成します。

## msgファイル
パッケージ直下の`msg`ディレクトリの中にカスタムROSメッセージを定義するファイルを置きます。例えば以下のようなものになります。この時に要素として使えるのは基本型(以下のstringやint32)とROSメッセージの型（例えばgeometry_msgs/Pose等）のみです。
今回は以下のような型を定義します。カスタムROSメッセージの名前はCustomRosMsgのようにパスカルケースで書くのが一般的です。
ここで書けるのはこの記事の下に書いた基本型か他のROSメッセージ型になります(例えばgeometry_msgs/Twist型を持ちたいなら`geometry_msgs/Twist target_twist`の様に書きます)。

```text:ros_lecture_msgs/msg/Custom.msg
string word
int32  number
```

## CMakeListの設定

まずfind packageにメッセージをビルドするためのmessage_generationとmsgファイルで依存するＲOSパッケージを記述します。（厳密にはCostom.msgはstd_msgsには依存しませんが説明のために書いておきます。）

```cmake:ros_lecture_msgs/CMakeLists.txtに追加
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs # msgファイルが依存するpkgを追加
  message_generation #この行を追加
)
```

またcatkin_packageに必要なパッケージを加えます。`#`でコメントアウトしているものの`#`を外して、以下のように`message_runtime`を追加します。

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更前）
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES ros_lecture
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)
```

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更後）
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES ros_lecture
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)
```

後のほうの`## Generate messages in the 'msg' folder`とある下のブロックを`#`のコメントウアウトを外して、以下のように`FILES`と自分で作ったmsgファイルを書きます

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更前）
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )
```

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更後）
add_message_files(
  FILES
  Custom.msg
)
```

generate_messageのブロックのコメントアウトを解除します。また上記のカスタムmsgの定義の時に使ったROSの型が所属するROSパッケージ（今回の例ではstd_msgs）をここに追加します。今回はstd_msgs以外の内容の追加は必要ありません。

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更前）
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )
```

```cmake:ros_lecture_msgs/CMakeLists.txtの一部（変更後）
generate_messages(
  DEPENDENCIES
  std_msgs
)
```

## package.xmlの設定
message_generationへの依存を追加します。

```xml:ros_lecture_msgs/package.xmlに追加
  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
```

コメントアウトを外して上の記述を復活させます。
昔は`<run_depend>`だったみたいですが今は`<exec_depend>`を使いましょう。
(package.xmlのver1、ver2の違いです)

## ビルド
```shell
cd ~/catkin_ws
catkin build
```

## カスタムROSメッセージの確認
カスタムROSメッセージを確認するには`rosmsg`コマンドを使用します。

```shell:カスタムROSメッセージの確認
rosmsg show ros_lecture_msgs/Custom
```

以下のように.msgファイルの中身が表示されます。

```text:rosmsgの結果
string word
int32 number
```

# カスタムROSメッセージの使用
カスタムROSメッセージを定義した`ros_lecture_msgs`とは別の`basic_lecture`の中のノードで使用していきます。

## カスタムROSメッセージを使用するROSノード
```c++:basic_lecture/src/basic_cmsg_talker.cpp
#include <ros/ros.h>
#include <ros_lecture_msgs/Custom.h>

int main(int argc, char** argv)
{
  ros::init(argc, argv, "basic_cmsg_talker");
  ros::NodeHandle nh;
  ros::Publisher pub = nh.advertise<ros_lecture_msgs::Custom>("chatter", 10);

  ros::Rate loop_rate(1);
  while (ros::ok())
  {
    ros_lecture_msgs::Custom data;
    data.word = "hello";
    data.number = 10;
    pub.publish(data);
    ros::spinOnce();
    loop_rate.sleep();
  }
}
```

カスタムメッセージは`#include <ROSパッケージ名/カスタムメッセージ名.h>`のようにincludeします。

## CmakeListの設定

まず`find_package`の中でカスタムROSメッセージの定義を行ったROSパッケージ（今回の例では`ros_lecture_msgs`）をここに追加します。

```cmake:basic_lecture/CmakeList.txtに追加
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  ros_lecture_msgs #この行を追加
)
```

次にROSノードのビルドの設定です。いつもと違うのは3行目の`add_dependencies()`です。上記でincludeしているROSメッセージのヘッダファイルはビルド時に生成されます。この行はそのビルド生成物を利用するための依存の記述です。今回のように同時にビルドする時に必要となります。

```cmake:basic_lecture/CmakeList.txtに追加
add_executable(basic_cmsg_talker src/basic_cmsg_talker.cpp)

add_dependencies(basic_cmsg_talker ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(basic_cmsg_talker
  ${catkin_LIBRARIES}
)
```

## package.xmlの設定
カスタムROSメッセージが含まれるROSパッケージへの依存をpackage.xmlへの記述します。

```xml:basic_lecture/package.xmlに追加
  <build_depend>ros_lecture_msgs</build_depend>
```

## ビルド
```shell
cd ~/catkin_ws
catkin build
```

## 実行
各ターミナルごとに実行前に`source ~/catkin_ws/devel/setup.bash`を実行する必要があります。

```shell:1つ目のターミナル
roscore
```

```shell:2つ目のターミナル
rosrun basic_lecture basic_cmsg_talker
```

```shell:3つ目のターミナル
 rostopic echo /chatter 
```

このように実行すると3つ目のターミナルから以下のような表示が出てきて、カスタムメッセージがpublishされていることがわかります。

```text
word: "hello"
number: 10
---
word: "hello"
number: 10
---
word: "hello"
number: 10
---
```

# (参考)ROSの基本型
rosのmsgファイルで記述できる型は以下の基本型か他のrosmsgになります。つまりROSの型は必ず以下の型かその組み合わせになります。

| ROSの基本型 | C++対応 | python対応 |
|:--|--:|--:|
| bool     | uint8_t       | bool |
| int8     | int8_t        | int  |
| uint8    | uint8_t       | int  |
| int16    | int16_t       | int  |
| uint16   | uint16_t      | int  |
| int32    | int32_t       | int  |
| uint32   | uint32_t      | int  |
| int64    | int64_t       | int(long)  |
| uint64   | uint64_t      | int(long)  |
| float32  | float         | float  |
| float64  | double        | float  |
| string   | std::string   | byte(str)  |
| time     | ros::Time     | rospy.Time |
| duration | ros::Duration | rospy.Duraiton  |


#参照
[ROSwiki: msgを作る](http://wiki.ros.org/ja/ROS/Tutorials/CreatingMsgAndSrv)
[カスタムmsgを定義する](http://wiki.ros.org/ROS/Tutorials/DefiningCustomMessages)
[カスタムmsgの依存](https://blog.ueda.tech/?p=9946)

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