独自メッセージを用いたROS2 publisher/subscriber:初級 -class style-

【[ROS2関係トップページへ](https://qiita.com/NeK/items/7ac0f4ec10d51dbca084)】
【[ROS2レクチャー:初級 -class style-](https://qiita.com/NeK/items/14db31ff784a0bfafe3d)】

【前:[ROS2独自メッセージの作成]】
【次:[ROS2の最小構成service/client:初級 -class style-]】

[ROS2独自メッセージの作成]:https://qiita.com/NeK/items/26751b8ed12976ff977d
[ROS2の最小構成service/client:初級 -class style-]:https://qiita.com/NeK/items/9d15487d4853638394a3

[ここ](https://qiita.com/NeK/items/26751b8ed12976ff977d)で作成した独自メッセージのうちtopic用メッセージを用いたpublisherとsubscriberのプログラムを作成する．
ここでは一つのパッケージとしてpublisherとsubscriberを作る．

# 再度掲示:topic用メッセージ
パッケージ名:my_messages
収納ディレクトリ:msg
メッセージファイル名:TwoInts.msg

```TwoInts.msg
int64 a
int64 b
```

上記をビルドしたものなので，以下のように使用できる．

- インクルードファイル
    - #include "my_messages/msg/two_ints.msg"
- 型クラス
    - my_messages::msg::TwoInts

# 準備

```Bash:terminal
$ cd ~/ros2_studies_ws/
$ ros2 pkg create my_msg_class --dependencies rclcpp my_messages
```

# publisher
作成物:

- src/my_pub.hpp
- src/my_pub.cpp
- src/my_pub_main.cpp

## プログラム:src/my_pub.hpp

```c++:src/my_pub.hpp
#include <rclcpp/rclcpp.hpp>
#include "my_messages/msg/two_ints.hpp"

class MyPub : public rclcpp::Node{
private:
  rclcpp::Publisher<my_messages::msg::TwoInts>::SharedPtr pub_;
  rclcpp::TimerBase::SharedPtr timer_;
public:
  MyPub(
    const std::string& name_space="", 
    const rclcpp::NodeOptions& options = rclcpp::NodeOptions()
  );
};
```

### 概要
作成した独自メッセージを使用．
また必須ではないが，Timerを使用して定期的にメッセージを配信．

### 説明
2行目:作成したメッセージをインクルード
6行目:作成したメッセージを使ったPublisherを作成

## プログラム:src/my_pub.cpp

```c++:src/my_pub.cpp
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/qos.hpp>
#include <chrono>
#include "my_messages/msg/two_ints.hpp"
#include "my_pub.hpp"

MyPub::MyPub(
  const std::string& name_space, 
  const rclcpp::NodeOptions& options
): Node("my_pub_test",name_space,options){
  using namespace std::chrono_literals;
  pub_ = this->create_publisher<my_messages::msg::TwoInts>("msg_test",rclcpp::QoS(10));
  timer_ = this->create_wall_timer(
    500ms,
    [this]() ->void {
      auto msg = std::make_shared<my_messages::msg::TwoInts>();
      msg->a = 3;
      msg->b = 4;
      RCLCPP_INFO(this->get_logger(), "Pub: %d, %d",msg->a, msg->b);
      pub_->publish(*msg);
    }
  );
}
```

### 概要
独自メッセージは二つのint64型．ここでは適当な数(17行目の3と18行目の4)を発信するだけのもの．

### 説明
4行目:独自メッセージをインクルード
12行目:独自メッセージを発信するpublisherをトピック名:msg_testで作成
13行目からTimerを使って定期実行
16行目:独自メッセージを宣言
17,18行目:独自メッセージに値を代入
20行目:発信

## プログラム:src/my_pub_main.cpp

```c++:src/my_pub_main.cpp
#include <rclcpp/rclcpp.hpp>
#include "my_pub.hpp"

int main(int argc, char * argv[]){
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MyPub>());
  rclcpp::shutdown();
  return 0;
}
```

### 概要
これまでどおりnodeをつくってspin．

# subscriber
作成物:

- src/my_sub.hpp
- src/my_sub.cpp
- src/my_sub_main.cpp

## プログラム:src/my_sub.hpp

```c++:src/my_sub.hpp
#include <rclcpp/rclcpp.hpp>
#include "my_messages/msg/two_ints.hpp"

class MySub : public rclcpp::Node{
private:
  rclcpp::Subscription<my_messages::msg::TwoInts>::SharedPtr sub_;
public:
  MySub(
    const std::string& name_space="", 
    const rclcpp::NodeOptions& options = rclcpp::NodeOptions()
  );
};
```

### 概要
publisherと同様に独自メッセージをインクルードして使用する

## プログラム:src/my_sub.cpp

```c++:src/my_sub.cpp
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/qos.hpp>
#include "my_messages/msg/two_ints.hpp"
#include "my_sub.hpp"

MySub::MySub(
  const std::string& name_space, 
  const rclcpp::NodeOptions& options
):Node("my_sub_test",name_space,options){
  sub_ = this->create_subscription<my_messages::msg::TwoInts>(
    "msg_test",
    rclcpp::QoS(10),
    [this](const my_messages::msg::TwoInts::SharedPtr msg) ->void {
      RCLCPP_INFO(this->get_logger(), "Sub: %d, %d", msg->a, msg->b);
    }
  );
}
```

### 概要
publisherと同様に独自メッセージをインクルードして使用する

## プログラム:src/my_sub_main.cpp

```c++:src/my_sub_main.cpp
#include <rclcpp/rclcpp.hpp>
#include "my_sub.hpp"

int main(int argc, char * argv[]){
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MySub>());
  rclcpp::shutdown();
  return 0;
}
```

### 概要
これまでどおりnodeをつくってspin．

# package.xmlとCMakeLists.txt
以下は追加したところに関係する部分のみ抜粋

## package.xml

```xml:package.xml
<package format="3">
  <depend>rclcpp</depend>
  <depend>my_messages</depend>
```

依存関係で必要になってくる部分が，自分で作成したmy_messagesというパッケージなので追加．

## CMakeLists.txt

```CMakeLists.txt
find_package(rclcpp REQUIRED)
find_package(my_messages REQUIRED)

add_executable(my_sub_test
  src/my_sub_main.cpp
  src/my_sub.cpp
)
ament_target_dependencies(my_sub_test
  rclcpp
  my_messages
)

add_executable(my_pub_test
  src/my_pub_main.cpp
  src/my_pub.cpp
)
ament_target_dependencies(my_pub_test
  rclcpp
  my_messages
)

install(TARGETS
  my_sub_test
  my_pub_test
  DESTINATION lib/${PROJECT_NAME}
)
```

二つのターゲットがあるだけで，これまで通り以下を追加．

- find
- add_executable
- ament_target_dependencies
- install

# ビルド・実行
## ビルド

```Bash:terminal
$ cd ~/ros2_studies_ws/
$ colcon build --symlink-install --packages-up-to my_msg_class
$ . install/local_setup.bash
```

## 実行
publisher用のterminalとsubscriber用のterminalを起動．

```Bash:terminal1
$ cd ~/ros2_studies_ws/
$ . install/local_setup.bash
$ ros2 run my_msg_class my_pub_test
```

```Bash:terminal2
$ cd ~/ros2_studies_ws/
$ . install/local_setup.bash
$ ros2 run my_msg_class my_sub_test
```

# 独自メッセージと作成プログラムが同一パッケージの場合
基本的には独自メッセージ・ライブラリ・ターゲットは別パッケージが望ましい．
しかし例外はあるものなので，同一パッケージでどうしても，という場合は[こちら](https://qiita.com/NeK/items/583f1492a3faf85aa7db#%E7%8B%AC%E8%87%AA%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%9C%E6%88%90)を参照．
参考:[公式tutorialのメッセージに関係するページ](https://index.ros.org/doc/ros2/Tutorials/Rosidl-Tutorial/)の一番下．

【前:[ROS2独自メッセージの作成]】
【次:[ROS2の最小構成service/client:初級 -class style-]】