【備忘録】ROSパッケージのROS2移行

既存のROSパッケージをROS2で使用するためのコンバーティングに関する備忘録。

# ROSパッケージの準備
ROSのチュートリアルのパッケージを利用する。
まずcatkin形式のパッケージを作る。
以下のコマンドを実行する。

```
cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
```
次にtalkerノードとlistenerノードのソースを格納する。
~/catkin_ws/src/beginner_tutorials/src配下に以下のlistener.cppとtalker.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;
}
```

```:talker.cpp
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
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;
  std_msgs::String msg;
  while (ros::ok())
  {
    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();
  }
  return 0;
}
```
次に~/catkin_ws/src/beginner_tutorials/package.xmlを編集する。
以下のものを張り付ける。

```:package.xml
<package>
  <name>beginner_tutorials</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="you@yourdomain.tld">Your Name</maintainer>
  <license>Apache 2.0</license>
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
</package>
```

最後に~/catkin_ws/src/beginner_tutorials/CMakeLists.txtを編集する。
以下のものを張り付ける。

```:CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare a catkin package
catkin_package()

## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
```

# ROSパッケージの動作確認
パッケージが正しく動作するかを確かめる。
以下のコマンドを実行しパッケージをビルドする。

```
cd ~/catkin_make
catkin_make
```

次に3つのターミナルを起動してそれぞれ以下のコマンドを実行する。以下が正常に動いているものである．

```:roscore
roscore
```

```:talkerノード
rosrun beginner_tutorials talker
[ INFO] [1618961980.031075257]: hello world 0
[ INFO] [1618961980.131142666]: hello world 1
[ INFO] [1618961980.231113532]: hello world 2
[ INFO] [1618961980.331107085]: hello world 3
[ INFO] [1618961980.431122565]: hello world 4
[ INFO] [1618961980.531117988]: hello world 5
```

```:listenerノード
rosrun beginner_tutorials listener
[ INFO] [1618961980.332236166]: I heard: [hello world 0]
[ INFO] [1618961980.432181902]: I heard: [hello world 1]
[ INFO] [1618961980.532188363]: I heard: [hello world 2]
[ INFO] [1618961980.632173786]: I heard: [hello world 3]
[ INFO] [1618961980.732066074]: I heard: [hello world 4]
[ INFO] [1618961980.832214154]: I heard: [hello world 5]
```

# ROS2への移植
ROS2用のワークスペースに先ほど作成したROSパッケージ（beginner_tutorials）をコピーする。

## talkerノードの移植作業
修正する点を列挙する。

|#|ROS|ROS2|備考|
|---|---|---|---|
|1|#include "ros/ros.h"|#include "rclcpp/rclcpp.hpp"|ROS2用C++用クライアントライブラリに変更|
|2|#include "std_msgs/String.h"|#include "std_msgs/msg/string.hpp"|std_msgs/String型のメッセージのヘッダをインクルード|
|3|ros::init(argc, argv, "talker"); <br> ros::NodeHandle n;|rclcpp::init(argc, argv); <br> auto node = rclcpp::Node::make_shared("talker");|ノードの初期化の書式を変更|
|4|ros::Publisher chatter_pub = n.advertise&lt;std_msgs::String&gt;("chatter", 1000);|auto chatter_pub = node->create_publisher&lt;std_msgs::msg::String&gt;("chatter",rmw_qos_profile_default);|パブリッシャの書式を変更|
|5|ros::Rate loop_rate(10);|rclcpp::Rate loop_rate(10);|rosからrclcppに変更|
|6|std_msgs::String msg;|auto msg = std::make_shared&lt;std_msgs::msg::String&gt;();|std_msgs/String型でmsgという名前の変数を登録する際の書式を変更|
|7|while (ros::ok())|while (rclcpp::ok())|rosからrclcppに変更|
|8|msg.data = ss.str();|msg->data = ss.str();|アローに変更|
|9|chatter_pub.publish(msg);|chatter_pub->publish(msg);|アローに変更|
|10|ROS_INFO("%s", msg.data.c_str());|RCLCPP_INFO(node->get_logger(),"%s", msg->data.c_str());|ROS_INFOをRCLCPP_INFOの書式に変更|
|11|ros::spinOnce();|rclcpp::spin_some(node);|スピンの書式の変更|

以下が上記の点を修正したコードである。

```:talker.cpp
#include <sstream>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("talker");
  auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter", rmw_qos_profile_default);
  rclcpp::Rate loop_rate(10);
  int count = 0;
  auto msg = std::make_shared<std_msgs::msg::String>();
  while (rclcpp::ok())
  {
    std::stringstream ss;
    ss << "hello world " << count++;
    msg->data = ss.str();
    RCLCPP_INFO(node->get_logger(),"%s", msg->data.c_str());
    chatter_pub->publish(msg);
    rclcpp::spin_some(node);
    loop_rate.sleep();
  }
 
  return 0;
}
```

## listenerノードの移植作業
修正する点を列挙する。

|#|ROS|ROS2|備考|
|---|---|---|---|
|1|#include "ros/ros.h"|#include "rclcpp/rclcpp.hpp"|ROS2用C++用クライアントライブラリに変更|
|2|#include "std_msgs/String.h"|#include "std_msgs/msg/string.hpp"|std_msgs/String型のメッセージのヘッダをインクルード|
|3|記述なし|std::shared_ptr&lt;rclcpp::Node> node;|nodeをグローバル変数に登録|
|4|void chatterCallback(const std_msgs::msg::String::ConstPtr msg)|void chatterCallback(const std_msgs::msg::String::SharedPtr msg)|ConstPtrからSharedPtrに変更|
|5|ROS_INFO("%s", msg.data.c_str());|RCLCPP_INFO(node->get_logger(),"%s", msg->data.c_str());|ROS_INFOをRCLCPP_INFOの書式に変更|
|6|ros::init(argc, argv, "listener"); <br> ros::NodeHandle n;|rclcpp::init(argc, argv); <br> node = std::make_shared&lt;rclcpp::Node&gt;("listener");|ノードの初期化の書式を変更|
|7|ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);|auto chatter_sub = node->create_subscription<std_msgs::msg::String>("chatter", rclcpp::QoS(10),chatterCallback);|サブスクライバの書式を変更|
|8|ros::spin(node);|rclcpp::spin(node);|rosからrclcppに変更|

以下が上記の点を修正したコードである。

```:listener.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

std::shared_ptr<rclcpp::Node> node;

void chatterCallback(const std_msgs::msg::String::SharedPtr msg)
{
  RCLCPP_INFO(node->get_logger(),"%s", msg->data.c_str());
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  node = std::make_shared<rclcpp::Node>("listener");
  auto chatter_sub = node->create_subscription<std_msgs::msg::String>(
    "chatter", 
    rclcpp::QoS(10),
    chatterCallback
  );
  rclcpp::spin(node);

  return 0;
}
```

## package.xmlの移植作業
修正する点を列挙する。

|#|ROS|ROS2|備考|
|---|---|---|---|
|1|&lt;package&gt;|&lt;package format="2"&gt;|マニュフェストのバージョンを2に変更|
|2|&lt;buildtool_depend&gt;catkin&lt;/buildtool_depend&gt;|&lt;buildtool_depend&gt;ament_cmake&lt;/buildtool_depend&gt;|catkinではなくamentを使用|
|3|&lt;build_depend&gt;roscpp&lt;/build_depend&gt;|&lt;build_depend&gt;rclcpp&lt;/build_depend&gt;<br>&lt;build_depend&gt;rmw_implementation&lt;/build_depend&gt;|ビルドの依存関係を修正|
|4|&lt;run_depend&gt;roscpp&lt;/run_depend&gt;<br>&lt;run_depend&gt;std_msgs&lt;/run_depend&gt;|&lt;exec_depend&gt;rclcpp&lt;/exec_depend&gt;<br>&lt;exec_depend&gt;rmw_implementation&lt;/exec_depend&gt;<br>&lt;exec_depend&gt;std_msgs&lt;/exec_depend&gt;|実行の依存関係を修正|
|5|記述なし| &lt;export&gt;<br>&lt;build_type&gt;ament_cmake&lt;/build_type&gt;<br>&lt;/export&gt;|ビルドタイプ宣言を追加|

以下が上記の点を修正したコードである。

```:package.xml
<package format="2">
  <name>beginner_tutorials</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="you@yourdomain.tld">Your Name</maintainer>
  <license>Apache 2.0</license>
  <buildtool_depend>ament_cmake</buildtool_depend>
  <build_depend>rclcpp</build_depend>
  <build_depend>rmw_implementation</build_depend>
  <build_depend>std_msgs</build_depend>
  <exec_depend>rclcpp</exec_depend>
  <exec_depend>rmw_implementation</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
```

## CMakeLists.txtの移植作業
修正する点を列挙する。

|#|ROS|ROS2|備考|
|---|---|---|---|
|1|cmake_minimum_required(VERSION 2.8.3)|cmake_minimum_required(VERSION 3.5)|Cmakeのバージョンの変更|
|2|記述なし|set(CMAKE_CXX_STANDARD 14)|C++14に設定|
|3|find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)|find_package(ament_cmake REQUIRED)<br>find_package(rclcpp REQUIRED)<br>find_package(rmw_implementation REQUIRED)<br>find_package(std_msgs REQUIRED)|ament_cmakeから個別にパッケージを探し出す|
|4|catkin_package()|ament_package()|パッケージの登録の宣言をcatkinからamentに変更し、末尾に記述する|
|5|include_directories(include ${catkin_INCLUDE_DIRS})|include_directories(${rclcpp_INCLUDE_DIRS}|依存パッケージのパスを指定(1)|
|6||${rmw_implementation_INCLUDE_DIRS}|依存パッケージのパスを指定(2)|
|7||${std_msgs_INCLUDE_DIRS})|依存パッケージのパスを指定(3)|
|8|target_link_libraries(talker ${catkin_LIBRARIES})|target_link_libraries(talker|パッケージライブラリを追加(1)|
|9||${rclcpp_LIBRARIES}|パッケージライブラリを追加(2)|
|10||${rmw_implementation_LIBRARIES}|パッケージライブラリを追加(3)|
|11||${std_msgs_LIBRARIES})|パッケージライブラリを追加(4)|
|12|記述なし|install(TARGETS talker DESTINATION lib/${PROJECT_NAME})|実行ファイルの保存先を追記|
|13|target_link_libraries(listener ${catkin_LIBRARIES})|target_link_libraries(listener |パッケージライブラリを追加(1)|
|14||${rclcpp_LIBRARIES}|パッケージライブラリを追加(2)|
|15||${rmw_implementation_LIBRARIES}|パッケージライブラリを追加(3)|
|16||${std_msgs_LIBRARIES})|パッケージライブラリを追加(4)|
|17|記述なし|install(TARGETS listener DESTINATION lib/${PROJECT_NAME})|実行ファイルの保存先を追記|

以下が上記の点を修正したコードである。

```:CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(beginner_tutorials)

set(CMAKE_CXX_STANDARD 14)

## Find ament and any ament_package packages
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(std_msgs REQUIRED)

## Build talker and listener
include_directories(${rclcpp_INCLUDE_DIRS}
                    ${rmw_implementation_INCLUDE_DIRS}
                    ${std_msgs_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker
                      ${rclcpp_LIBRARIES}
                      ${rmw_implementation_LIBRARIES}
                      ${std_msgs_LIBRARIES})
install(TARGETS talker DESTINATION lib/${PROJECT_NAME})        

add_executable(listener src/listener.cpp)
target_link_libraries(listener
                      ${rclcpp_LIBRARIES}
                      ${rmw_implementation_LIBRARIES}
                      ${std_msgs_LIBRARIES})
install(TARGETS listener DESTINATION lib/${PROJECT_NAME})

## Declare a ament package
ament_package()
```

## ROS2パッケージの動作確認
パッケージが正しく動作するかを確かめる。
以下のコマンドを実行しパッケージをビルドする。

```
cd ~/ros2-dashing
colcon build
```

次に2つのターミナルを起動してそれぞれ以下のコマンドを実行する。以下が正常に動いているものである．

```:talkerノード
source ~/ros2-dashing/install/setup.bash
ros2 run beginner_tutorials talker
[INFO] [talker]: hello world 0
[INFO] [talker]: hello world 1
[INFO] [talker]: hello world 2
[INFO] [talker]: hello world 3
[INFO] [talker]: hello world 4
[INFO] [talker]: hello world 5
```

```:listenerノード
source ~/ros2-dashing/install/setup.bash
ros2 run beginner_tutorials listener
[INFO] [listener]: hello world 0
[INFO] [listener]: hello world 1
[INFO] [listener]: hello world 2
[INFO] [listener]: hello world 3
[INFO] [listener]: hello world 4
[INFO] [listener]: hello world 5
```