您正在阅读的是旧版本但仍受支持的 ROS 2 文档。 Jazzy.

创建自定义 msg 和 srv 文件

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

辅导水平: 初学者

时间 20 分钟

背景介绍

在前面的教程中,您利用报文和服务接口了解了 主题, 服务和简单的发布者/订阅者 (C++/Python) 和服务/客户 (C++/Python) 节点。在这些情况下,您使用的接口都是预定义的。

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

先决条件

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

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

任务

1 创建新软件包

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

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

ros2 pkg create --build-type ament_cmake --license Apache-2.0 tutorial_interfaces

接口教程 是新软件包的名称。请注意,它是且只能是一个 CMake 包,但这并不限制你在哪种类型的包中使用你的信息和服务。你可以在 CMake 包中创建自己的自定义接口,然后在 C++ 或 Python 节点中使用,这将在最后一节中介绍。

"(《世界人权宣言》) .msg.srv 文件必须放在名为 信息服务 分别是在 ros2_ws/src/tutorial_interfaces:

mkdir msg srv

2 创建自定义定义

2.1 msg 定义

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

int64 num

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

还在 tutorial_interfaces/msg 目录,新建一个名为 Sphere.msg 内容如下

几何_msgs/点中心
float64 radius

该自定义报文使用了另一个报文包 (几何_msgs/点 在这种情况下)。

2.2 服务器定义

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

int64 a
int64 b
int64 c
---
int64 sum

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

3 CMakeLists.txt

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

查找软件包(几何参数 要求)
查找软件包(默认生成器 要求)

生成接口(${项目名称}
  "msg/Num.msg";
  "msg/Sphere.msg";
  "srv/AddThreeInts.srv";
  依赖 几何参数 # 添加上述信息所依赖的软件包,在本例中,geometry_msgs 用于 Sphere.msg
)

备注

rosidl_generate_interfaces 中的第一个参数(库名)必须与 ${PROJECT_NAME} 匹配(请参阅 "PROJECT_NAME")。(参见 https://github.com/ros2/rosidl/issues/441#issuecomment-591025515).

4 package.xml

因为接口依赖于 默认生成器 生成特定语言代码时,需要声明构建工具对它的依赖性。 默认运行时间 是运行时或执行阶段的依赖关系,是以后使用接口所必需的。接口 接口包 是软件包依赖组的名称、 接口教程,应与之相关联,并使用 <member_of_group>; 标签

<package>; 元素的 package.xml:

<依赖>;几何参数依赖</depend>;
构建工具的依赖关系<buildtool_depend>;默认生成器</buildtool_depend>;
执行依赖关系;默认运行时间</exec_depend>;
<member_of_group>;接口包</member_of_group>;

5 建立 接口教程 包装

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

colcon build --packages-select tutorial_interfaces

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

6 确认创建 msg 和 srv

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

source install/setup.bash

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

ros2 interface show tutorial_interfaces/msg/Num

应该返回:

int64 num

还有

ros2 interface show tutorial_interfaces/msg/Sphere

应该返回:

几何_msgs/点中心
        float64 x
        float64 y
        float64 z
float64 radius

还有

ros2 interface show tutorial_interfaces/srv/AddThreeInts

应该返回:

int64 a
int64 b
int64 c
---
int64 sum

7 测试新界面

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

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

对上一教程中创建的发布者/订阅者软件包稍作修改 (C++Python),你可以看到 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_stream(->;get_logger(), 出版:';"; <<; 信息. <<; "'";);    // 更改
    出版商_->;发布(信息);
  }
  rclcpp::定时器基数::SharedPtr 定时器;
  rclcpp::出版商<;接口教程::信息::编号>::SharedPtr 出版商_;             // 更改
  size_t count_;
};

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

订阅者

#include 功能强大;
#include <内存>;

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

使用 标准::占位符::_1;

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

私人:
  空白 topic_callback( 接口教程::信息::编号 及样品; 信息)   // 更改
  {
    rclcpp_info_stream(->;get_logger(), 我听说:';"; <<; 信息. <<; "'";);     // 更改
  }
  rclcpp::订阅<;接口教程::信息::编号>::SharedPtr 订阅_;  // 更改
};

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

CMakeLists.txt

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

#...

查找软件包(ament_cmake 要求)
查找软件包(rclcpp 要求)
查找软件包(接口教程 要求)                      # 改变

添加可执行(话匣子 src/publisher_member_function.cpp)
ament_target_dependencies(话匣子 rclcpp 接口教程)    # 改变

添加可执行(听众 src/subscriber_member_function.cpp)
ament_target_dependencies(听众 rclcpp 接口教程)  # 改变

安装(目标
  话匣子
  听众
  目的地 lib/${项目名称})

ament_package()

package.xml

添加以下一行

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

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

在 Linux/macOS 上:

colcon build --packages-select cpp_pubsub

在 Windows 上:

colcon build --merge-install --packages-select cpp_pubsub

然后打开两个新终端,源 ros2_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 与服务/客户

对上一教程中创建的服务/客户端软件包稍作修改 (C++Python),你可以看到 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++):

#...

查找软件包(ament_cmake 要求)
查找软件包(rclcpp 要求)
查找软件包(接口教程 要求)         # 改变

添加可执行(服务器 src/add_twoo_ints_server.cpp)
ament_target_dependencies(服务器
  rclcpp 接口教程)                      # 改变

添加可执行(客户 src/add_twoo_ints_client.cpp)
ament_target_dependencies(客户
  rclcpp 接口教程)                      # 改变

安装(目标
  服务器
  客户
  目的地 lib/${项目名称})

ament_package()

package.xml

添加以下一行

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

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

在 Linux/macOS 上:

colcon build --packages-select cpp_srvcli

在 Windows 上:

colcon build --merge-install --packages-select cpp_srvcli

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

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

摘要

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

本教程仅涉及定义自定义界面的皮毛。您可以在 关于 ROS 2 接口.

下一步工作

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