LoginSignup
0
0

ROS 2においてC++を用いたPub&Sub通信を行う方法(コンポーネントを使用)

Last updated at Posted at 2024-01-10

はじめに

 今回の記事では、ROS 2でC++を用いてPub&Sub通信を行う方法について記載します。今回の書き方では、コンポーネントを用いた方法でコーディングを行っていきます。このコンポーネントは、ROSにおけるノードレットに相当するものになります。コンポーネントを使用しない書き方でもPub&Sub通信は実行できますが、ドキュメントに自身でmainを書くスタイルのノードは推奨できないといった旨の記載がありましたので、今回はコンポーネントを用いた方法でコーディングを行っていきます。

この記事はROSに関する投稿の一部です。
目次はこちら

前提条件

 今回の記事は、以下の環境で動かすことを前提に記載しています。

条件
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()が記述されています。この行でコンポーネントの登録を行い、このクラスがコンポーネントであることを明示します。

~/ros2_ws/src/pub_sub_cpp_01/include/pub_sub_cpp_01/publisher.hpp
#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
~/ros2_ws/src/pub_sub_cpp_01/include/pub_sub_cpp_01/subscriber.hpp
#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
~/ros2_ws/src/pub_sub_cpp_01/src/publisher.cpp
#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)
~/ros2_ws/src/pub_sub_cpp_01/src/subscriber.cpp
#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)
~/ros2_ws/src/pub_sub_cpp_01/CMakeLists.txt
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

実行

ターミナル1(publisher)
ros2 run pub_sub_cpp_01 publisher
ターミナル2(subscriber)
ros2 run pub_sub_cpp_01 subscriber

実行結果

・ターミナル1(publisher)
Screenshot from 2023-11-15 16-14-12.png

・ターミナル2(subscriber)
Screenshot from 2023-11-15 16-18-41.png

おわりに

 今回の記事ではコンポーネントを用いたPub&Sub通信の方法について記載しました。次回の記事では今回作成したコンポーネントをlaunchファイルから実行する方法について記載する予定ですので、興味がある方は是非ご覧ください。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0