✉️

ROS 2: カスタムメッセージ の作成と他パッケージでの使用

2024/09/08に公開

海洋ロボコンをやってた人です。
今回はROS 2 カスタムメッセージ の作成と他パッケージでの使用について、久しぶりに実装したら忘れていたので、その手順を備忘録として記載しておきます。

特にCPPでのカスタムメッセージのサンプルとサマリが一目でわかる資料が欲しかったのが経緯です。

サンプルコードも置いてあるので、動かしながら理解していただければ幸いです。

以下箇条書きになりますが、何卒宜しくお願い致します。

また、誤記等あればご指摘ください。

必要に応じて、適宜追加します。

https://github.com/tasada038/ros2_custom_msg_sample

1. Custom Msg の作成と他パッケージの利用

1.1 Custom Msgの作成

以下のディレクトリ構造となるようにパッケージを作成します。

terminal
ros2 pkg create --build-type ament_cmake pos_array_msgs --dependencies rclcpp

include, srcは不要なので削除してください。

続いて、作成したいカスタムメッセージを記述します。カスタムメッセージは

パスカルケース: PascalCaseでmsgファイルを作成してください。

PosArray.msg
int32 id                       # Pos Array ID
geometry_msgs/Point[] positions # Pos Array (x, y, z)

また、カスタムメッセージの依存関係は以下のように記述します。

CMakeLsits.txt
cmake_minimum_required(VERSION 3.5)
project(pos_array_msgs)

set(CMAKE_CXX_STANDARD 14)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

# Generate messages and services
rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/PosArray.msg"
  "srv/SetPosition.srv"
  DEPENDENCIES geometry_msgs
)

ament_export_dependencies(rosidl_default_runtime)

ament_package()
packages.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>pos_array_msgs</name>
  <version>0.1.0</version>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>rosidl_default_generators</buildtool_depend>

  <depend>geometry_msgs</depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>

  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->
    <build_type>ament_cmake</build_type>
  </export>
</package>

1.2 Custom Msgを他のパッケージで使用する

他パッケージでの使用サマリ

【.hpp / .cpp】
インクルード時はスネークケース: snake_caseで記述すること

#include "pos_array_msgs/msg/pos_array.hpp"

のようにパッケージ名/msg/スネークケース名.msg とする。

PosArray.msg → pos_array.hpp

【CMakeLists.txt】

find_package(pos_array_msgs REQUIRED)
ament_target_dependencies()

にカスタムメッセージのスネークケース名を追加する(pos_array_msgs)

【package.xml】

 <depend>pos_array_msgs</depend> 

を追加する

実装例

pos_array_publisher.cpp
#include "rclcpp/rclcpp.hpp"
#include "pos_array_msgs/msg/pos_array.hpp"
#include "geometry_msgs/msg/point.hpp"

using namespace std::chrono_literals;

class PosArrayPublisher : public rclcpp::Node
{
public:
  /**
   * @brief Constructor for the PosArrayPublisher class.
   * Initializes the node, creates publishers for four different topics, and sets up timers to periodically publish messages.
   */
  PosArrayPublisher()
  : Node("pos_array_publisher")
  {
    // Create publishers for different topics
    publisher_1_ = this->create_publisher<pos_array_msgs::msg::PosArray>("pos_array_topic_1", 10);
    publisher_2_ = this->create_publisher<pos_array_msgs::msg::PosArray>("pos_array_topic_2", 10);
    publisher_3_ = this->create_publisher<pos_array_msgs::msg::PosArray>("pos_array_topic_3", 10);
    publisher_4_ = this->create_publisher<pos_array_msgs::msg::PosArray>("pos_array_topic_4", 10);

    // Set up timers to periodically call the publish_message function for each publisher
    timer_1_ = this->create_wall_timer(
      1s, [this]() { this->publish_message(publisher_1_, 1); });

    timer_2_ = this->create_wall_timer(
      1s, [this]() { this->publish_message(publisher_2_, 2); });

    timer_3_ = this->create_wall_timer(
      1s, [this]() { this->publish_message(publisher_3_, 3); });

    timer_4_ = this->create_wall_timer(
      1s, [this]() { this->publish_message(publisher_4_, 4); });
  }

private:
  /**
   * @brief Publishes a message with a specific ID to a given publisher.
   * Fills the message with data based on the ID and publishes it to the associated topic.
   *
   * @param publisher The publisher to send the message to.
   * @param id The ID to set in the message and determine the data content.
   */
  void publish_message(rclcpp::Publisher<pos_array_msgs::msg::PosArray>::SharedPtr publisher, int id)
  {
    auto message = pos_array_msgs::msg::PosArray();
    message.id = id;

    // Set different data based on the ID
    if (id == 1) {
      for (int i = 0; i < 3; ++i) {
        geometry_msgs::msg::Point point;
        point.x = static_cast<double>(i);
        point.y = static_cast<double>(i * 2);
        point.z = static_cast<double>(i * 3);
        message.positions.push_back(point);
      }
    } else if (id == 2) {
      for (int i = 0; i < 3; ++i) {
        geometry_msgs::msg::Point point;
        point.x = static_cast<double>(i * 2);
        point.y = static_cast<double>(i * 4);
        point.z = static_cast<double>(i * 6);
        message.positions.push_back(point);
      }
    } else if (id == 3) {
      for (int i = 0; i < 3; ++i) {
        geometry_msgs::msg::Point point;
        point.x = static_cast<double>(i * 3);
        point.y = static_cast<double>(i * 6);
        point.z = static_cast<double>(i * 9);
        message.positions.push_back(point);
      }
    } else if (id == 4) {
      for (int i = 0; i < 3; ++i) {
        geometry_msgs::msg::Point point;
        point.x = static_cast<double>(i * 4);
        point.y = static_cast<double>(i * 8);
        point.z = static_cast<double>(i * 12);
        message.positions.push_back(point);
      }
    }

    RCLCPP_INFO(this->get_logger(), "Publishing ID: '%d' on topic '%s'", message.id, publisher->get_topic_name());
    for (const auto & pos : message.positions) {
      RCLCPP_INFO(this->get_logger(), "Position: [x: %f, y: %f, z: %f]", pos.x, pos.y, pos.z);
    }

    publisher->publish(message);
  }

  rclcpp::Publisher<pos_array_msgs::msg::PosArray>::SharedPtr publisher_1_; ///< Publisher for topic 1
  rclcpp::Publisher<pos_array_msgs::msg::PosArray>::SharedPtr publisher_2_; ///< Publisher for topic 2
  rclcpp::Publisher<pos_array_msgs::msg::PosArray>::SharedPtr publisher_3_; ///< Publisher for topic 3
  rclcpp::Publisher<pos_array_msgs::msg::PosArray>::SharedPtr publisher_4_; ///< Publisher for topic 4

  rclcpp::TimerBase::SharedPtr timer_1_; ///< Timer for periodically publishing messages on topic 1
  rclcpp::TimerBase::SharedPtr timer_2_; ///< Timer for periodically publishing messages on topic 2
  rclcpp::TimerBase::SharedPtr timer_3_; ///< Timer for periodically publishing messages on topic 3
  rclcpp::TimerBase::SharedPtr timer_4_; ///< Timer for periodically publishing messages on topic 4
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);

  // Create and spin the PosArrayPublisher node
  rclcpp::spin(std::make_shared<PosArrayPublisher>());

  rclcpp::shutdown();
  return 0;
}
CMakeLsits.txt
cmake_minimum_required(VERSION 3.5)
project(pos_array_ros)

# Default to C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find packages
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(pos_array_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)

# Declare the executable
add_executable(pos_array_publisher src/pos_array_publisher.cpp)

# Specify libraries to link the executable against
ament_target_dependencies(pos_array_publisher
  rclcpp
  pos_array_msgs
  geometry_msgs
)


add_executable(set_position_server src/set_position_server.cpp)
ament_target_dependencies(set_position_server
  rclcpp
  pos_array_msgs
)

add_executable(set_position_client src/set_position_client.cpp)
ament_target_dependencies(set_position_client
  rclcpp
  pos_array_msgs
)


# Install the executable
install(TARGETS
  pos_array_publisher
  set_position_server
  set_position_client
  DESTINATION lib/${PROJECT_NAME})

# Macro to register the package with ament
ament_package()

find_package(pos_array_msgs REQUIRED)
ament_target_dependencies()
にカスタムメッセージのスネークケース名を追加する(今回ならpos_array_msgs)

packages.xml
<?xml version="1.0"?>
<package format="2">
  <name>pos_array_ros</name>
  <version>0.0.0</version>
  <description>ROS samples for custom PosArray messages</description>
  <license>Apache-2.0</license>

  <depend>rclcpp</depend>
  <!-- add custom msgs depended -->
  <depend>pos_array_msgs</depend>
  <depend>geometry_msgs</depend>

  <buildtool_depend>ament_cmake</buildtool_depend>

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

<depend>pos_array_msgs</depend>
を追加する


srvの説明や、他にもカスタムメッセージについて書きたい内容は別途追加予定としておきます。(T.B.D)

Reference

以下のmsgsファイルの書き方などは参考になると思います。

https://github.com/MORAI-Autonomous/MORAI-ROS_morai_msgs


以上です。
Likeいただけると大変励みになりますので、よろしくお願いいたします。

Discussion