警告

您正在阅读的 ROS 2 文档版本已达到 EOL(生命周期结束),不再受官方支持。如果您想了解最新信息,请访问 Jazzy.

创建自定义 ROS 2 msg 和 srv 文件

目标 定义自定义界面文件 (.msg.srv) 并与 Python 和 C++ 节点一起使用。

辅导水平: 初学者

时间 20 分钟

背景介绍

在之前的教程中,您利用消息和服务接口了解了主题、服务以及简单的发布者/订阅者和服务/客户端节点。在这些情况下,您使用的接口都是预定义的。

虽然使用预定义的接口定义是一种良好的做法,但有时您可能也需要定义自己的消息和服务。本教程将向你介绍创建自定义接口定义的最简单方法。

先决条件

您应该有一个 ROS 2 工作区.

本教程还使用在发布者/订阅者 (C++Python) 和服务/客户 (C++Python)教程来试用新的自定义信息。

任务

1 创建新软件包

在本教程中,您将创建自定义 .msg.srv 文件在自己的软件包中,然后在另一个软件包中使用它们。两个软件包应在同一个工作区中。

由于我们将使用前面教程中创建的 pub/sub 和 service/client 软件包,请确保与这些软件包位于同一工作区 (dev_ws/src),然后运行以下命令创建一个新软件包:

ros2 pkg create --build-type ament_cmake tutorial_interfaces

接口教程 是新软件包的名称。请注意,这是一个 CMake 软件包;目前还没有办法生成一个 .msg.srv 文件。您可以在 CMake 包中创建自定义接口,然后在 Python 节点中使用它,这将在最后一节中介绍。

保持 .msg.srv 文件的目录中。在 dev_ws/src/tutorial_interfaces:

mkdir msg

mkdir srv

2 创建自定义定义

2.1 msg 定义

tutorial_interfaces/msg 目录,新建一个名为 Num.msg 只需一行代码声明其数据结构:

int64 num

这是您的自定义报文,传输一个名为 .

2.2 服务器定义

回到 tutorial_interfaces/srv 目录,新建一个名为 AddThreeInts.srv 其请求和响应结构如下

int64 a
int64 b
int64 c
---
int64 sum

这是您的自定义服务,请求三个名为 a, bc并响应一个名为 数额.

3 CMakeLists.txt

要将您定义的接口转换为特定语言(如 C++ 和 Python)的代码,以便在这些语言中使用,请将以下几行添加到 CMakeLists.txt:

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME})
  "msg/Num.msg";
  "srv/AddThreeInts.srv";
 )

4 package.xml

因为接口依赖于 默认生成器 生成特定语言代码时,需要声明对它的依赖关系。在 package.xml

构建依赖关系;默认生成器</build_depend>;

执行依赖关系;默认运行时间</exec_depend>;

<member_of_group>;接口包</member_of_group>;

5 建立 接口教程 包装

现在,自定义界面包的所有部分都已就位,您可以构建该包了。在工作区的根目录 (~/dev_ws),运行以下命令:

colcon build --packages-select tutorial_interfaces

现在,其他 ROS 2 软件包也能发现这些接口。

6 确认创建 msg 和 srv

在新终端中,从工作区 (dev_ws)来获取:

install/setup.bash

现在,您可以使用 玫瑰2 界面 展览 指挥:

ros2 interface show tutorial_interfaces/msg/Num

应该返回:

int64 num

还有

ros2 interface show tutorial_interfaces/srv/AddThreeInts

应该返回:

int64 a
int64 b
int64 c
---
int64 sum

7 测试新界面

在这一步中,您可以使用之前教程中创建的软件包。对节点做一些简单的修改、 CMakeLists包装 文件将允许您使用新界面。

7.1 测试 Num.msg 带酒吧/分会

对上一教程中创建的发布者/订阅者软件包稍作修改,就可以看到 Num.msg 的操作。由于要将标准字符串 msg 改为数字 msg,输出结果会略有不同。

出版商

#include 时间顺序<chrono>;
#include <内存>;

#include "rclcpp/rclcpp.hpp";
#include "tutorial_interfaces/msg/num.hpp";     // 更改

使用 命名空间 标准::计时器;

 最小出版商 :  rclcpp::节点
{
:
  最小出版商()
  : 节点("minimal_publisher";), count_(0)
  {
    出版商_ = ->;创建出版商<;接口教程::信息::编号>;("主题";, 10);    // 更改
    定时器 = ->;创建隔离墙计时器(
      500毫秒, 标准::约束(及样品;最小出版商::定时器回调, ));
  }

私人:
  空白 定时器回调()
  {
    汽车 信息 = 接口教程::信息::编号();                               // 更改
    信息. = ->;count_++;                                        // 更改
    RCLCPP_INFO(->;get_logger(), "出版:'%d'";, 信息.);    // 更改
    出版商_->;发布(信息);
  }
  rclcpp::定时器基数::SharedPtr 定时器;
  rclcpp::出版商<;接口教程::信息::编号>::SharedPtr 出版商_;         // 更改
  size_t count_;
};

int 主要(int 参数, 烧焦 * 参数[])
{
  rclcpp::启动(参数, 参数);
  rclcpp::后旋(标准::共享<;最小出版商>;());
  rclcpp::关闭();
  返回 0;
}

订阅者:

#include <内存>;

#include "rclcpp/rclcpp.hpp";
#include "tutorial_interfaces/msg/num.hpp";     // 更改
使用 标准::占位符::_1;

 最小订阅者 :  rclcpp::节点
{
:
  最小订阅者()
  : 节点("minimal_subscriber";)
  {
    订阅_ = ->;创建订阅<;接口教程::信息::编号>;(          // 更改
      "主题";, 10, 标准::约束(及样品;最小订阅者::topic_callback, , _1));
  }

私人:
  空白 topic_callback( 接口教程::信息::编号::SharedPtr 信息)        // 更改
  {
    RCLCPP_INFO(->;get_logger(), 我听到:'%d'";, 信息->;);              // 更改
  }
  rclcpp::订阅<;接口教程::信息::编号>::SharedPtr 订阅_;       // 更改
};

int 主要(int 参数, 烧焦 * 参数[])
{
  rclcpp::启动(参数, 参数);
  rclcpp::后旋(标准::共享<;最小订阅者>;());
  rclcpp::关闭();
  返回 0;
}

CMakeLists.txt:

添加以下几行(仅限 C++):

...

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED) # 更改

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces) # 更改

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces) # 更改

install(TARGETS
  话匣子
  听众
  DESTINATION lib/${PROJECT_NAME})

ament_package()

package.xml:

添加以下一行

<;取决>;接口教程</取决>;

完成上述编辑并保存所有更改后,构建软件包:

colcon build --packages-select cpp_pubsub

然后打开两个新终端,源 dev_ws 然后运行:

ros2 运行 cpp_pubsub talker
ros2 运行 cpp_pubsub 监听器

因为 Num.msg 转发的只是一个整数,因此对话者应该只发布整数值,而不是之前发布的字符串:

[INFO] [minimal_publisher]:Publishing: '0';
[INFO] [minimal_publisher]:出版:'1';
[INFO] [minimal_publisher]:出版: '2';

7.2 测试 AddThreeInts.srv 与服务/客户

对之前教程中创建的服务/客户端软件包稍作修改,就可以看到 AddThreeInts.srv 的操作。由于要将原来的两整数请求 srv 改为三整数请求 srv,因此输出结果会略有不同。

服务:

#include "rclcpp/rclcpp.hpp";
#include "tutorial_interfaces/srv/add_three_ints.hpp";     // 更改

#include <内存>;

空白 增加( 标准::共享_ptr<;接口教程::服务::添加三位数::要求>; 要求,     // 更改
          标准::共享_ptr<;接口教程::服务::添加三位数::回应>;       回应)  // 更改
{
  回应->;数额 = 要求->;a + 要求->;b + 要求->;c;                                       // 更改
  RCLCPP_INFO(rclcpp::get_logger("rclcpp";), 接收请求\na: %ld"; " b: %ld"; " c: %ld";,   // 更改
                要求->;a, 要求->;b, 要求->;c);                                          // 更改
  RCLCPP_INFO(rclcpp::get_logger("rclcpp";), "发回响应:[%ld]";, ( int)回应->;数额);
}

int 主要(int 参数, 烧焦 **参数)
{
  rclcpp::启动(参数, 参数);

  标准::共享_ptr<;rclcpp::节点>; 网站 = rclcpp::节点::共享("add_three_ints_server";);  // 更改

  rclcpp::服务<;接口教程::服务::添加三位数>::SharedPtr 服务 =                 // 更改
    网站->;创建服务<;接口教程::服务::添加三位数>;("add_three_ints";,  及样品;增加);     // 更改

  RCLCPP_INFO(rclcpp::get_logger("rclcpp";), 准备添加三个 ints;);      // 更改

  rclcpp::后旋(网站);
  rclcpp::关闭();
}

客户:

#include "rclcpp/rclcpp.hpp";
#include "tutorial_interfaces/srv/add_three_ints.hpp";        // 更改

#include 时间顺序<chrono>;
#include cstdlib>;
#include <内存>;

使用 命名空间 标准::计时器;

int 主要(int 参数, 烧焦 **参数)
{
  rclcpp::启动(参数, 参数);

  如果 (参数 != 4) { // 更改
      RCLCPP_INFO(rclcpp::get_logger("rclcpp";), "usage: add_three_ints_client X Y Z";);      // 更改
      返回 1;
  }

  标准::共享_ptr<;rclcpp::节点>; 网站 = rclcpp::节点::共享("add_three_ints_client";); // 更改
  rclcpp::客户<;接口教程::服务::添加三位数>::SharedPtr 客户 =                        // 更改
    网站->;创建客户端<;接口教程::服务::添加三位数>;("add_three_ints";);                  // 更改

  汽车 要求 = 标准::共享<;接口教程::服务::添加三位数::要求>;();               // 更改
  要求->;a = 环礁(参数[1]);
  要求->;b = 环礁(参数[2]);
  要求->;c = 环礁(参数[3]);               // 更改

  虽然 (!客户->;等待服务(1s)) {
    如果 (!rclcpp::好的()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp";), 等待服务时被中断。退出;);
      返回 0;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp";), 服务不可用,请再次等待......";);
  }

  汽车 结果 = 客户->;异步发送请求(要求);
  // 等待结果。
  如果 (rclcpp::自旋直到未来完成(网站, 结果) ==
    rclcpp::执行人::未来返回代码::成功)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp";), "总和:%ld";, 结果.获取()->;数额);
  } 不然 {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp";), "调用 add_three_ints 服务失败";);    // 更改
  }

  rclcpp::关闭();
  返回 0;
}

CMakeLists.txt:

添加以下几行(仅限 C++):

...

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED) # 更改

add_executable(server src/add_twoo_ints_server.cpp)
ament_target_dependencies(server
  rclcpp tutorial_interfaces) #CHANGE

add_executable(client src/add_twoo_ints_client.cpp)
ament_target_dependencies(client
  rclcpp tutorial_interfaces) #CHANGE

install(TARGETS
  服务器
  客户
  DESTINATION lib/${PROJECT_NAME})

ament_package()

package.xml:

添加以下一行

<;取决>;接口教程</取决>;

完成上述编辑并保存所有更改后,构建软件包:

colcon build --packages-select cpp_srvcli

然后打开两个新终端,源 dev_ws 然后运行:

ros2 运行 cpp_srvcli 服务器
ros2 运行 cpp_srvcli 客户端 2 3 1

摘要

在本教程中,您将学习如何在自己的软件包中创建自定义接口,以及如何在其他软件包中使用这些接口。

这是一种创建和使用接口的简单方法。您可以了解有关接口的更多信息 这里.

下一步工作

"(《世界人权宣言》) 下一个教程 涵盖了在 ROS 2 中使用接口的更多方法。