はじめに
今回の記事では、ROS 2でC++を用いてPub&Sub通信を行う方法について記載します。今回の書き方では、コンポーネントを用いた方法でコーディングを行っていきます。このコンポーネントは、ROSにおけるノードレットに相当するものになります。コンポーネントを使用しない書き方でもPub&Sub通信は実行できますが、ドキュメントに自身でmainを書くスタイルのノードは推奨できないといった旨の記載がありましたので、今回はコンポーネントを用いた方法でコーディングを行っていきます。
前提条件
今回の記事は、以下の環境で動かすことを前提に記載しています。
条件 | |
---|---|
OS | Ubuntu 22.04 |
ROS | ROS 2 humble |
パッケージ作成
以下のコマンドでC++用パッケージを作成します。今回パッケージ名は「pub_sub_cpp_01」としています。
cd ~/ros2_ws/src/
ros2 pkg create --build-type ament_cmake pub_sub_cpp_01
ソースコード
以下がROS 2でコンポーネントを用いてPub&Sub通信を行うためのコードになります。ROSと比較した際に特徴的な箇所としては、QoSの設定があります。今回の場合ですと「rclcpp::QoS qos(rclcpp::KeepLast{10});」と記載していますが、これはROSでPub&Sub通信を行う際に設定していたqueue_sizeに相当します。この他にも、Durability、Livelinessなどの設定が存在しますが、少し量があるため今回は割愛させて頂きます。詳細は以下のリンクなどを参考にしてください。また、その他に特徴的な点としては、冒頭でもふれたコンポーネントを用いた書き方の部分が挙げられます。publisher.cppとsubscriber.cppはmain文を持ちませんが、代わりに最後の行にRCLCPP_COMPONENTS_REGISTER_NODE()が記述されています。この行でコンポーネントの登録を行い、このクラスがコンポーネントであることを明示します。
#ifndef PUBLISHER_H_
#define PUBLISHER_H_
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_components/register_node_macro.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
namespace kccs_pub_sub_cpp_01 {
class Publisher : public rclcpp::Node {
public:
Publisher(const rclcpp::NodeOptions& options);
private:
void timer_callback();
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
size_t count_;
};
} // namespace kccs_pub_sub_cpp_01
#endif
#ifndef SUBSCRIBER_H_
#define SUBSCRIBER_H_
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_components/register_node_macro.hpp"
#include "std_msgs/msg/string.hpp"
namespace kccs_pub_sub_cpp_01 {
class Subscriber : public rclcpp::Node {
public:
Subscriber(const rclcpp::NodeOptions& options);
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg);
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
};
} // namespace kccs_pub_sub_cpp_01
#endif
#include "pub_sub_cpp_01/publisher.hpp"
#include <chrono>
#include <cstdio>
#include <memory>
#include <utility>
using namespace std::chrono_literals;
namespace kccs_pub_sub_cpp_01 {
Publisher::Publisher(const rclcpp::NodeOptions& options)
: Node("publisher", options) {
rclcpp::QoS qos(rclcpp::KeepLast{10});
pub_ = this->create_publisher<std_msgs::msg::String>("chatter", qos);
timer_ =
this->create_wall_timer(500ms, [this]() -> void { timer_callback(); });
};
void Publisher::timer_callback() {
std::unique_ptr<std_msgs::msg::String> msg_ = std::make_unique<std_msgs::msg::String>();
msg_->data = "Hello World: " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg_->data.c_str());
pub_->publish(std::move(msg_));
}
} // namespace kccs_pub_sub_cpp_01
RCLCPP_COMPONENTS_REGISTER_NODE(kccs_pub_sub_cpp_01::Publisher)
#include "pub_sub_cpp_01/subscriber.hpp"
namespace kccs_pub_sub_cpp_01 {
Subscriber::Subscriber(const rclcpp::NodeOptions& options)
: Node("subscriber", options) {
rclcpp::QoS qos(rclcpp::KeepLast{10});
sub_ = this->create_subscription<std_msgs::msg::String>(
"chatter", qos,
[this](const std_msgs::msg::String::SharedPtr msg) -> void {
topic_callback(std::move(msg));
});
}
void Subscriber::topic_callback(const std_msgs::msg::String::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
} // namespace kccs_pub_sub_cpp_01
RCLCPP_COMPONENTS_REGISTER_NODE(kccs_pub_sub_cpp_01::Subscriber)
cmake_minimum_required(VERSION 3.8)
project(pub_sub_cpp_01)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(std_msgs REQUIRED)
include_directories(include)
function(add_dependencies_func library)
target_compile_definitions(${library}
PRIVATE "KCCS_PUB_SUB_TEST_NODES_CPP_BUILDING_LIBRARY")
ament_target_dependencies(${library}
"rclcpp"
"rclcpp_components"
"std_msgs")
endfunction()
add_library(pub_sub_cpp_01_lib SHARED
src/publisher.cpp
src/subscriber.cpp
)
add_dependencies_func(pub_sub_cpp_01_lib)
rclcpp_components_register_node(pub_sub_cpp_01_lib
PLUGIN "kccs_pub_sub_cpp_01::Publisher"
EXECUTABLE publisher)
rclcpp_components_register_node(pub_sub_cpp_01_lib
PLUGIN "kccs_pub_sub_cpp_01::Subscriber"
EXECUTABLE subscriber)
install(TARGETS
pub_sub_cpp_01_lib
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
if(BUILD_TESTING)
endif()
ament_package()
ビルド
以下のコマンドでビルドします。
cd ~/ros2_ws/src/
colcon build
実行
ros2 run pub_sub_cpp_01 publisher
ros2 run pub_sub_cpp_01 subscriber
実行結果
おわりに
今回の記事ではコンポーネントを用いたPub&Sub通信の方法について記載しました。次回の記事では今回作成したコンポーネントをlaunchファイルから実行する方法について記載する予定ですので、興味がある方は是非ご覧ください。
参考文献