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

实施自定义接口

目标 了解在 ROS 2 中实现自定义接口的更多方法。

辅导水平: 初学者

时间 15 分钟

背景介绍

在一个 上一个教程您已学会如何创建自定义 msg 和 srv 接口。

虽然最佳做法是在专门的接口包中声明接口,但有时在一个包中声明、创建和使用接口也很方便。

回想一下,接口目前只能在 CMake 包中定义。不过,可以在 CMake 包中加入 Python 库和节点(使用 ament_cmake_python),因此您可以在一个软件包中定义接口和 Python 节点。为了简单起见,我们在这里使用 CMake 包和 C++ 节点。

本教程的重点是 msg 接口类型,但这里的步骤适用于所有接口类型。

先决条件

我们假设您已在 创建自定义 msg 和 srv 文件 在学习本教程之前,请先阅读本教程。

您应该 已安装 ROS 2, a 工作区并了解 创建软件包.

与往常一样,不要忘记 源 ROS 2 在您打开的每个新终端中。

任务

1 创建软件包

在您的工作区 来源 目录下,创建一个包 更多接口 并在其中建立一个 msg 文件目录:

ros2 pkg create --build-type ament_cmake --license Apache-2.0 more_interfaces
mkdir more_interfaces/msg

2 创建 msg 文件

内部 more_interfaces/msg, 创建一个新文件 地址簿.msg然后粘贴以下代码,创建一条用于传递个人相关信息的信息:

uint8 家庭电话类型=0
uint8 工作电话类型=1
uint8 手机类型=2

字符串 
字符串 姓氏
字符串 电话号码
uint8 电话类型

该信息由以下字段组成:

  • first_name:字符串类型

  • last_name:字符串类型

  • 电话号码:字符串类型

  • phone_type:uint8 类型,定义了多个命名常量值

请注意,可以为信息定义中的字段设置默认值。请参阅 接口 了解更多自定义界面的方法。

接下来,我们需要确保将 msg 文件转化为 C++、Python 和其他语言的源代码。

2.1 建立 msg 文件

开放 package.xml 并添加以下几行:

构建工具的依赖关系<buildtool_depend>;默认生成器</buildtool_depend>;

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

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

请注意,在构建时,我们需要 默认生成器而在运行时,我们只需要 默认运行时间.

开放 CMakeLists.txt 并添加以下几行:

查找从 msg/srv 文件生成信息代码的软件包:

查找软件包(默认生成器 要求)

声明要生成的信息列表:

设置(msg_files
  "msg/AddressBook.msg";
)

通过手动添加 .msg 文件,我们可以确保 CMake 在添加其他 .msg 文件后知道何时需要重新配置项目。

生成信息:

生成接口(${项目名称}
  ${msg_files}
)

同时确保导出了消息运行时依赖项:

ament_export_dependencies(默认运行时间)

现在你可以根据 msg 定义生成源文件了。我们暂时跳过编译步骤,因为我们将在下文第 4 步中一并完成。

2.2 (额外)设置多个接口

备注

您可以使用 设置CMakeLists.txt 来整齐地列出所有接口:

设置(msg_files
  "msg/Message1.msg";
  "msg/Message2.msg";
  # 等等
  )

设置(srv_files
  "srv/Service1.srv";
  "srv/Service2.srv";
   # 等等
  )

然后像这样一次性生成所有列表:

生成接口(${项目名称}
  ${msg_files}
  ${srv_files}
)

3 使用同一软件包中的接口

现在我们可以开始编写使用这条信息的代码了。

more_interfaces/src 创建一个名为 publish_address_book.cpp 并粘贴以下代码:

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

#include "rclcpp/rclcpp.hpp";
#include "more_interfaces/msg/address_book.hpp";

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

 地址簿发布者 :  rclcpp::节点
{
:
  地址簿发布者()
  : 节点("address_book_publisher";)
  {
    地址簿出版商 =
      ->;创建出版商<;更多接口::信息::地址簿>;("address_book";, 10);

    汽车 publish_msg = []() ->; 空白 {
        汽车 信息 = 更多接口::信息::地址簿();

        信息. = "约翰";;
        信息.姓氏 = 无名氏;;
        信息.电话号码 = "1234567890";
        信息.电话类型 = 信息.手机类型;

        标准::cout <<; 出版联系人\n首先:"; <<; 信息. <<;
          " Last:"; <<; 信息.姓氏 <<; 标准::endl;

        ->;地址簿出版商->;发布(信息);
      };
    定时器 = ->;创建隔离墙计时器(1s, publish_msg);
  }

私人:
  rclcpp::出版商<;更多接口::信息::地址簿>::SharedPtr 地址簿出版商;
  rclcpp::定时器基数::SharedPtr 定时器;
};


int 主要(int 参数, 烧焦 * 参数[])
{
  rclcpp::启动(参数, 参数);
  rclcpp::后旋(标准::共享<;地址簿发布者>;());
  rclcpp::关闭();

  返回 0;
}

3.1 法规解释

包括我们新创建的 地址簿.msg.

#include "more_interfaces/msg/address_book.hpp";

创建一个节点和一个 地址簿 出版商

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

 地址簿发布者 :  rclcpp::节点
{
:
  地址簿发布者()
  : 节点("address_book_publisher";)
  {
    地址簿出版商 =
      ->;创建出版商<;更多接口::信息::地址簿>;("address_book";);

创建一个回调,定期发布信息。

汽车 publish_msg = []() ->; 空白 {

创建一个 地址簿 我们稍后将发布该消息实例。

汽车 信息 = 更多接口::信息::地址簿();

填充 地址簿 领域。

信息. = "约翰";;
信息.姓氏 = 无名氏;;
信息.电话号码 = "1234567890";
信息.电话类型 = 信息.手机类型;

最后定期发送信息。

标准::cout <<; 出版联系人\n首先:"; <<; 信息. <<;
  " Last:"; <<; 信息.姓氏 <<; 标准::endl;

->;地址簿出版商->;发布(信息);

创建一个 1 秒计时器,调用我们的 publish_msg 每秒执行一次。

定时器 = ->;创建隔离墙计时器(1s, publish_msg);

3.2 建立出版商

我们需要在 CMakeLists.txt:

查找软件包(rclcpp 要求)

添加可执行(发布地址簿 src/publish_address_book.cpp)
ament_target_dependencies(发布地址簿 rclcpp)

安装(目标
    发布地址簿
  目的地 lib/${项目名称})

4 试用

返回工作区的根目录以构建软件包:

cd ~/ros2_ws
colcon build --packages-up-to more_interfaces

然后创建工作区并运行发布器:

source install/local_setup.bash
ros2 run more_interfaces publish_address_book

您应该会看到发布者转发您定义的 msg,包括您在 publish_address_book.cpp.

确认信息正在 地址簿 主题,打开另一个终端,获取工作区的源代码,然后调用 主题 回响:

source install/setup.bash
ros2 topic echo /address_book

在本教程中,我们不会创建订阅程序,但您可以尝试自己编写一个订阅程序进行练习(使用 编写简单的发布器和订阅器(C++) 帮助)。

5(额外)使用现有接口定义

备注

你可以在新的接口定义中使用现有的接口定义。例如,假设有一条名为 联系信息 的 ROS 2 软件包。 ROSIDL_Tutorials_MSGS.假设它的定义与我们定制的 地址簿.msg 接口。

在这种情况下,您可以定义 地址簿.msg (软件包中的一个接口 你的节点)的类型为 联系方式 (的一个接口 单独 软件包)。您甚至可以定义 地址簿.msg 作为 矩阵 属于 联系方式就像这样:

ROSIDL_Tutorials_MSGS/联系方式[] 地址簿

要生成这条信息,您需要声明依赖于 联系信息 包装 ROSIDL_Tutorials_MSGSpackage.xml:

构建依赖关系;ROSIDL_Tutorials_MSGS</build_depend>;

执行依赖关系;ROSIDL_Tutorials_MSGS</exec_depend>;

而在 CMakeLists.txt:

查找软件包(ROSIDL_Tutorials_MSGS 要求)

生成接口(${项目名称}
  ${msg_files}
  依赖 ROSIDL_Tutorials_MSGS
)

您还需要包含 联系信息 在您的发布者节点中添加 联系人 到您的 地址簿.

#include "rosidl_tutorials_msgs/msg/contact.hpp";

您可以将回调改成类似这样:

汽车 publish_msg = []() ->; 空白 {
   汽车 信息 = 标准::共享<;更多接口::信息::地址簿>;();
   {
     ROSIDL_Tutorials_MSGS::信息::联系方式 联系;
     联系. = "约翰";;
     联系.姓氏 = 无名氏;;
     联系.电话号码 = "1234567890";
     联系.电话类型 = 信息.手机类型;
     信息->;地址簿.推回(联系);
   }
   {
     ROSIDL_Tutorials_MSGS::信息::联系方式 联系;
     联系. = "简";;
     联系.姓氏 = 无名氏;;
     联系.电话号码 = "4254242424";
     联系.电话类型 = 信息.家庭电话类型;
     信息->;地址簿.推回(联系);
   }

   标准::cout <<; 发布地址簿:"; <<; 标准::endl;
   对于 (汽车 联系 : 信息->;地址簿) {
     标准::cout <<; "第一:"; <<; 联系. <<; " Last:"; <<; 联系.姓氏 <<;
       标准::endl;
   }

   地址簿出版商->;发布(*信息);
 };

构建并运行这些更改后,将显示所定义的 msg 和上面定义的 msg 数组。

摘要

在本教程中,您尝试了定义接口的不同字段类型,然后在使用接口的同一软件包中构建了一个接口。

您还学习了如何使用另一个界面作为字段类型,以及 package.xml, CMakeLists.txt#include 使用该功能所需的语句。

下一步工作

接下来,您将创建一个简单的 ROS 2 软件包,并学习从启动文件中设置自定义参数。同样,你可以选择以 C++Python.