この記事では既存の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>
<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
:
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
:
#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等の定義もすべて変更すると以下のようになります。
#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> -->
<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
は以下の通りです。
#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
:
以上で終わりです。
説明の誤り、意見等ありましたらコメントよろしくお願いします。