警告

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

编写简单的发布器和订阅器(C++)

目标 使用 C++ 创建并运行一个发布者和订阅者节点。

辅导水平: 初学者

时间 20 分钟

背景介绍

节点 是通过 ROS 图进行通信的可执行进程。在本教程中,节点将以字符串信息的形式通过 主题.这里使用的示例是一个简单的 "说者 "和 "听者 "系统;一个节点发布数据,另一个节点订阅主题以便接收数据。

先决条件

在前面的教程中,您学习了如何 创建工作区创建软件包.

任务

1 创建软件包

打开一个新终端,然后 为您的 ROS 2 安装提供源代码 以便 玫瑰2 命令即可运行。

导航进入 dev_ws 目录中创建的 上一个教程.

回顾一下,软件包应在 来源 目录,而不是工作区的根目录。因此,导航到 dev_ws/src然后运行软件包创建命令:

ros2 pkg create --build-type ament_cmake cpp_pubsub

您的终端将返回一条信息,验证是否创建了软件包 cpp_pubsub 及其所有必要的文件和文件夹。

导航进入 dev_ws/src/cpp_pubsub/src.请注意,这是 CMake 软件包中包含可执行文件的源文件所在的目录。

2 写入发布节点

输入以下命令下载通话代码示例:

wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/dashing/rclcpp/minimal_publisher/member_function.cpp

现在将有一个名为 publisher_member_function.cpp.使用您喜欢的文本编辑器打开文件。

#include 时间顺序<chrono>;
#include 功能强大;
#include <内存>;
#include <字符串>;

#include "rclcpp/rclcpp.hpp";
#include "std_msgs/msg/string.hpp";

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

/* 本例创建了一个 Node 子类,并使用 std::bind() 注册了一个
* 成员函数作为定时器的回调。*/

 最小出版商 :  rclcpp::节点
{
  :
    最小出版商()
    : 节点("minimal_publisher";), count_(0)
    {
      出版商_ = ->;创建出版商<;std_msgs::信息::字符串>;("主题";, 10);
      定时器 = ->;创建隔离墙计时器(
      500毫秒, 标准::约束(及样品;最小出版商::定时器回调, ));
    }

  私人:
    空白 定时器回调()
    {
      汽车 信息 = std_msgs::信息::字符串();
      信息.数据 = 世界,你好!"; + 标准::to_string(count_++);
      RCLCPP_INFO(->;get_logger(), "出版:'%s'";, 信息.数据.c_str());
      出版商_->;发布(信息);
    }
    rclcpp::定时器基数::SharedPtr 定时器;
    rclcpp::出版商<;std_msgs::信息::字符串>::SharedPtr 出版商_;
    size_t count_;
  };

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

2.1 检查代码

代码顶部包含您将使用的标准 C++ 头文件。标准 C++ 头文件之后是 rclcpp/rclcpp.hpp include 允许你使用 ROS 2 系统中最常用的组件。最后是 std_msgs/msg/string.hpp,其中包括用于发布数据的内置消息类型。

这些线条代表节点的依赖关系。请注意,必须将依赖关系添加到 package.xmlCMakeLists.txt您将在下一节中完成这一操作。

#include 时间顺序<chrono>;
#include 功能强大;
#include <内存>;
#include <字符串>;

#include "rclcpp/rclcpp.hpp";
#include "std_msgs/msg/string.hpp";

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

下一行创建节点类 最小出版商 继承自 rclcpp::Node.每 是指节点。

 最小出版商 :  rclcpp::节点

公共构造函数为节点命名 最小出版商 并初始化 count_ 初始化为 0。 字符串 消息类型、主题名称 主题以及所需的队列大小,以便在备份时限制信息。下一页 定时器 被初始化,这会导致 定时器回调 函数每秒执行两次。

:
  最小出版商()
  : 节点("minimal_publisher";), count_(0)
  {
    出版商_ = ->;创建出版商<;std_msgs::信息::字符串>;("主题";, 10);
    定时器 = ->;创建隔离墙计时器(
    500毫秒, 标准::约束(及样品;最小出版商::定时器回调, ));
  }

"(《世界人权宣言》) 定时器回调 函数是设置信息数据和实际发布信息的地方。该 RCLCPP_INFO 宏确保每一条发布的消息都会打印到控制台。

私人:
  空白 定时器回调()
  {
    汽车 信息 = std_msgs::信息::字符串();
    信息.数据 = 世界,你好!"; + 标准::to_string(count_++);
    RCLCPP_INFO(->;get_logger(), "出版:'%s'";, 信息.数据.c_str());
    出版商_->;发布(信息);
  }

最后是定时器、发布者和计数器字段的声明。

rclcpp::定时器基数::SharedPtr 定时器;
rclcpp::出版商<;std_msgs::信息::字符串>::SharedPtr 出版商_;
size_t count_;

最小出版商 类是 主要,节点实际执行的位置。 rclcpp::init 初始化 ROS 2,以及 rclcpp::spin 开始处理来自节点的数据,包括来自定时器的回调。

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

2.2 添加依赖项

导航一级回到 dev_ws/src/cpp_pubsub 目录中的 CMakeLists.txtpackage.xml 文件已为您创建。

开放 package.xml 使用文本编辑器。

正如 上一个教程请确保填写 <描述>;, <维护者>;许可证 标签

<描述>;实例  极少 出版商/订户 使用 rclcpp</description>;
维护者 电子邮件="[email protected]";>;您的 名称维护人员</maintainer>;
许可证阿帕奇 许可证 2.0</license>;

ament_cmake buildtool 依赖关系,并粘贴与节点的 include 语句相对应的下列依赖关系:

<依赖>;rclcpp依赖</depend>;
<依赖>;std_msgs依赖</depend>;

这就声明了软件包需要 rclcppstd_msgs 当其代码被执行时。

确保保存文件。

2.3 CMakeLists.txt

现在打开 CMakeLists.txt 文件。在现有依赖关系的下方 find_package(ament_cmake 必填)添加这几行:

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

然后,添加可执行文件并将其命名为 话匣子 这样您就可以使用 玫瑰2 运行:

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)

最后,添加 install(TARGETS...) 因此 玫瑰2 运行 能找到你的可执行文件:

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

您可以清理您的 CMakeLists.txt 删除了一些不必要的部分和注释,所以看起来像这样:

cmake_minimum_required(VERSION 3.5)
项目(cpp_pubsub)

# 默认值  C++14
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(std_msgs REQUIRED)

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)

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

ament_package()

你可以现在就构建你的软件包,获取本地设置文件,然后运行它,但让我们先创建用户节点,这样你就能看到整个系统的运行情况。

3 写入用户节点

返回 dev_ws/src/cpp_pubsub/src 来创建下一个节点。在终端中输入以下代码

wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/dashing/rclcpp/minimal_subscriber/member_function.cpp

进入 ls 现在将返回控制台中的

publisher_member_function.cpp subscriber_member_function.cpp

打开 subscriber_member_function.cpp 使用文本编辑器。

#include <内存>;

#include "rclcpp/rclcpp.hpp";
#include "std_msgs/msg/string.hpp";
使用 标准::占位符::_1;

 最小订阅者 :  rclcpp::节点
{
  :
    最小订阅者()
    : 节点("minimal_subscriber";)
    {
      订阅_ = ->;创建订阅<;std_msgs::信息::字符串>;(
      "主题";, 10, 标准::约束(及样品;最小订阅者::topic_callback, , _1));
    }

  私人:
    空白 topic_callback( std_msgs::信息::字符串::SharedPtr 信息) 
    {
      RCLCPP_INFO(->;get_logger(), "我听说:'%s'";, 信息->;数据.c_str());
    }
    rclcpp::订阅<;std_msgs::信息::字符串>::SharedPtr 订阅_;
};

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

3.1 检查代码

订阅者节点的代码与发布者节点几乎完全相同。现在节点被命名为 最小订阅者,构造函数使用节点的 创建订阅 类来执行回调。

没有计时器,因为订阅者只需在数据发布到 主题 主题。

:
  最小订阅者()
  : 节点("minimal_subscriber";)
  {
    订阅_ = ->;创建订阅<;std_msgs::信息::字符串>;(
    "主题";, 10, 标准::约束(及样品;最小订阅者::topic_callback, , _1));
  }

回顾 主题教程 发布者和订阅者使用的主题名称和信息类型必须匹配,才能进行通信。

"(《世界人权宣言》) topic_callback 函数接收通过主题发布的字符串消息数据,并使用 RCLCPP_INFO 宏观。

该类中唯一的字段声明是订阅。

私人:
  空白 topic_callback( std_msgs::信息::字符串::SharedPtr 信息) 
  {
    RCLCPP_INFO(->;get_logger(), "我听说:'%s'";, 信息->;数据.c_str());
  }
  rclcpp::订阅<;std_msgs::信息::字符串>::SharedPtr 订阅_;

"(《世界人权宣言》) 主要 函数完全相同,只不过现在它会旋转 最小订阅者 节点。对发布节点来说,旋转意味着启动定时器,但对订阅节点来说,旋转只是意味着准备随时接收信息。

由于该节点与发布者节点具有相同的依赖关系,因此没有什么新内容需要添加到 package.xml.

3.2 CMakeLists.txt

重新开放 CMakeLists.txt 并在发布者条目下方为订阅者节点添加可执行文件和目标。

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)

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

确保保存文件,然后您的出版/子系统就可以使用了。

4 构建和运行

您可能已经拥有 rclcppstd_msgs 软件包是 ROS 2 系统的一部分。运行 rosdep 在工作区的根目录 (dev_ws) 在构建前检查是否缺少依赖项:

rosdep install -i --from-path src --rosdistro <distro> -y

还是在工作区的根目录下、 dev_ws, 构建您的新软件包:

colcon build --packages-select cpp_pubsub

打开一个新终端,导航至 dev_ws并获取设置文件:

install/setup.bash

现在运行 Talker 节点:

ros2 运行 cpp_pubsub talker

终端应该开始每 0.5 秒发布一次信息,就像这样:

[INFO] [minimal_publisher]:Publishing: "Hello World: 0";
[INFO] [minimal_publisher]:Publishing: "Hello World: 1";
[INFO] [minimal_publisher]:Publishing: "Hello World: 2";
[INFO] [minimal_publisher]:Publishing: "Hello World: 3";
[INFO] [minimal_publisher]:Publishing: "Hello World: 4";

打开另一个终端,从里面获取设置文件的源代码 dev_ws 然后启动监听节点:

ros2 运行 cpp_pubsub 监听器

监听器将开始向控制台打印消息,从发布者当时的消息计数开始,就像这样:

[INFO] [minimal_subscriber]:我听到: "Hello World: 10";
[INFO] [minimal_subscriber]:我听到: "Hello World: 11";
[INFO] [minimal_subscriber]:我听到: "Hello World: 12";
[INFO] [minimal_subscriber]:我听到: "Hello World: 13";
[INFO] [minimal_subscriber]:我听到: "Hello World: 14";

进入 Ctrl+C 以阻止节点旋转。

摘要

您创建了两个节点,用于通过主题发布和订阅数据。在编译和运行它们之前,您在软件包配置文件中添加了它们的依赖项和可执行文件。

下一步工作

接下来,您将使用服务/客户端模型创建另一个简单的 ROS 2 软件包。同样,你可以选择用 C++Python.