Help us understand the problem. What is going on with this article?

ROSをROS2へ

More than 1 year has passed since last update.

この記事では既存のROSパッケージをROS2で使用するためのコンバーティングについて日本語でまとめています。
内容はROS2のMiguration Guideを参考にさせていただきました。
開発環境はUbuntu 16.04を使用しています。
ROS1のバージョンはkineticですが、ほかのバージョンでも差異はないはずです。

既存のROSパッケージをROS2へ移植

ROS

ROS2へ移植する元となるROSパッケージ、その実行について説明します。

ROSコードの準備

ROS Tutorialsで用いられるシンプルなROSパッケージtalkerを準備します。
catkinワークスペース(ここでは~/ros1_talker)内にtalkerパッケージを作ります。ファイル階層は以下の通りです。

$cd ~/ros1_talker
$find .
.
./src
./src/talker
./src/talker/package.xml
./src/talker/CMakeLists.txt
./src/talker/talker.cpp

また、3つのファイルの中身は以下の通りです。
それぞれコピペします。
src/talker/package.xml:

package.xml
<package>
  <name>talker</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="gerkey@osrfoundation.org">Brian Gerkey</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>

/src/talker/CMakeLists.txt

CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(talker)
find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(talker talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
install(TARGETS talker
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})

src/talker/talker.cpp

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;
}

ROSコードのビルド

まずROS環境下でtalkerが動くか試します。
sourceコマンドでセットアップファイルを実行して環境変数設定を行ったのち、catkin_make installをつかいパッケージをビルドします。

. /opt/ros/kinetic/setup.bash
cd ~/ros1_talker
catkin_make install

ROSノードの実行

セットアップファイルをcatkinインストールツリーから実行しroscoreを起動させます。(セットアップファイルは/opt/ros/kinetic/setup.bashからでも実行できます)roscoreがすでに起動してある場合はこの操作は必要ありません。

. ~/ros1_talker/install/setup.bash
roscore

さらにシェルを開き、もう一度セットアップファイルを実行したのち、rosrunをつかいcatkinインストールスペースからノードを実行します。(この場合ノードはワークスペース内にある必要がります)

. ~/ros1_talker/install/setup.bash
rosrun talker talker

成功すればhello worldとメッセージが出力されます。
これでros環境下で動くことが確認できました。

hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
hello world 5
:

ROS2への移植

ROS2用のワークスペースをつくります。
ここへROSパッケージのソースをコピーします。

mkdir -p ~/ros2_talker/src
cd ~/ros2_talker
cp -a ~/ros1_talker/src/talker src

talker.cppの変更

ROSノード内のC++コードを変更していきます。rclcppと呼ばれるROS2C++ライブラリではroscppとは異なるAPIを提供していますが、2つのライブラリは似ているため変更は複雑ではありません。

ヘッダのインクルード

roscppライブラリAPIへアクセスするros/ros.hの代わりにrclcpp/rclcpp.hppをインクルードします。

//#include "ros/ros.h"
#include "rclcpp/rclcpp.hpp"

std_msgs/Stringメッセージを出力するため、メッセージ型のヘッダをインクルードします。

//#include "std_msgs/String.h"
#include "std_msgs/msg/string.hpp"

ライブラリコールの変更

ROSではノード名をライブラリの初期化呼び出しへ渡し、ノードの初期化をしていました。またros::NodeHandleを作成することによってノードの起動をおこなっていました。
ROS2では初期化を行い、ノードオブジェクトの作成時にノードに名前をつけます。(ROS2はC++11をつかうので変数宣言時に型名の代わりとしてautoを指定しています)

//  ros::init(argc, argv, "talker");
//  ros::NodeHandle n;
    rclcpp::init(argc, argv);
    auto node = rclcpp::Node::make_shared("talker");

Publisher、rateオブジェクトは名前空間とメソッド名が変更されているほかは、かなり似ています。Publisherの定義ではキューの数として渡していた整数の代わりにqosプロファイルを渡しメッセージ配信処理を柔軟に行えるようにしています。
ここでは、rmwで定義された名前空間を持たないグローバルな既定のプロファイルrmw_qos_profile_defaultを渡しています。(Cで記述されている)

//  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
//  ros::Rate loop_rate(10);
  auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter",
    rmw_qos_profile_default);
  rclcpp::Rate loop_rate(10);

スマートポインタを用いて送信するメッセージの保存をします。

//  std_msgs::String msg;
  auto msg = std::make_shared<std_msgs::msg::String>();

Publishループ内でdataフィールドにアクセスするにはmsgがスマートポインタなので、アロー演算子->をつかいます。

//    msg.data = ss.str();
    msg->data = ss.str();

データの送信でも、Publisherがスマートポインタとなっているのでアロー演算子をつかいます。

//    chatter_pub.publish(msg);
    chatter_pub->publish(msg);

ROS_INFO、spin等の定義もすべて変更すると以下のようになります。

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

package.xmlの変更

ROS2のマニフェストはバージョン2のみサポートしています。まずフォーマットバージョンを定義します。

<!-- <package> -->
<package format="2">

ROS2はcatkinに代わりamentバージョンを使っています。buildtool_dependタグを変更します。

<!--  <buildtool_depend>catkin</buildtool_depend> -->
  <buildtool_depend>ament_cmake</buildtool_depend>

ビルド等の依存関係を変更していきます。また、ビルドタイプ宣言をします。

<!--  <build_depend>roscpp</build_depend> -->
  <build_depend>rclcpp</build_depend>
  <build_depend>rmw_implementation</build_depend>
<!--  <run_depend>roscpp</run_depend> -->
  <exec_depend>rclcpp</exec_depend>
  <exec_depend>rmw_implementation</exec_depend>
<!--  <run_depend>std_msgs</run_depend> -->
  <exec_depend>std_msgs</exec_depend>

 <export>
    <build_type>ament_cmake</build_type>
  </export>

変更後のpackage.xmlは以下の通りです。

package.xml
<!-- <package> -->
<package format="2">
  <name>talker</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="gerkey@osrfoundation.org">Brian Gerkey</maintainer>
  <license>Apache License 2.0</license>
<!--  <buildtool_depend>catkin</buildtool_depend> -->
  <buildtool_depend>ament_cmake</buildtool_depend>
<!--  <build_depend>roscpp</build_depend> -->
  <build_depend>rclcpp</build_depend>
  <build_depend>rmw_implementation</build_depend>
  <build_depend>std_msgs</build_depend>
<!--  <run_depend>roscpp</run_depend> -->
  <exec_depend>rclcpp</exec_depend>
  <exec_depend>rmw_implementation</exec_depend>
<!--  <run_depend>std_msgs</run_depend> -->
  <exec_depend>std_msgs</exec_depend>
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

CMakeLists.txtの変更

ROS2ではC++11をつかうので以下の行を追加して有効にします。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

ROSではcatkinパッケージを探す際にコンポーネントしてほかのパッケージを指定しました。
ROS2ではament_cmakeから個別にパッケージを探し出します。

#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(std_msgs REQUIRED)

CMakeではcatkin_package()を呼び出してパッケージを登録していました。これをament_package()に変更し、さらにターゲットを行ったあとに記述します。

# catkin_package()
# ファイルのボトムに記す:
ament_package()

依存パッケージは、すべてインクルードパスを指定しなければいけません。
以下のように指定しておきます。

#include_directories(${catkin_INCLUDE_DIRS})
include_directories(${rclcpp_INCLUDE_DIRS}
                    ${rmw_implementation_INCLUDE_DIRS}
                    ${std_msgs_INCLUDE_DIRS})

また、それぞれリンクするパッケージライブラリを追加します。target_link_librariesで追加することができますが、これはターゲット名も指定するのでadd_executableのうしろにもってきます。

#target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(talker
                      ${rclcpp_LIBRARIES}
                      ${rmw_implementation_LIBRARIES}
                      ${std_msgs_LIBRARIES})

インストールターゲットを追加します。binフォルダ内に実行ファイルを生成します。catkinではCATKIN_PACKAGE_BIN_DESTINATIONのように変数を定義していました。

#install(TARGETS talker
#  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker RUNTIME DESTINATION bin)

変更後のCMakeLists.txtは以下の通りです。

CMakeLists.txt
#CMakeのVERSIONは最新にしてください
cmake_minimum_required(VERSION 3.5)
project(talker)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(std_msgs REQUIRED)
#catkin_package()
#include_directories(${catkin_INCLUDE_DIRS})
include_directories(${rclcpp_INCLUDE_DIRS}
                    ${rmw_implementation_INCLUDE_DIRS}
                    ${std_msgs_INCLUDE_DIRS})
add_executable(talker talker.cpp)
#target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(talker
                      ${rclcpp_LIBRARIES}
                      ${rmw_implementation_LIBRARIES}
                      ${std_msgs_LIBRARIES})
#install(TARGETS talker
#  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker RUNTIME DESTINATION bin)
ament_package()

ROS2コードのビルド

環境設定ファイルを実行し、ament buildをつかいパッケージをビルドします。

. ~/ros2_ws/install/setup.bash
cd ~/ros2_talker
ament build

ROS2ノードの実行

CMakeLists.txtファイルの変更でtalkerの実行ファイルをbinにインストールしました。セットアップファイルを実行したあと、talkerを名前ディレクトリから直接呼び出すことができます。

. ~/ros2_ws/install/setup.bash
~/ros2_talker/install/bin/talker

うまくいけば以下のように表示されます。

hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
hello world 5
:

以上で終わりです。
説明の誤り、意見等ありましたらコメントよろしくお願いします。

参考サイト

https://github.com/ros2/ros2/wiki/Migration-Guide

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした