Writing a simple publisher and subscriber (C++)[译文ROS2 humble]


You're reading the documentation for an older, but still supported, version of ROS 2. For information on the latest version, please have a look at Iron.
您正在阅读有关较旧但仍受支持的 ROS 2 版本的文档。有关最新版本的信息,请查看 Iron。

Writing a simple publisher and subscriber (C++)
编写一个简单的发布者和订阅者 (C++) 

Goal: Create and run a publisher and subscriber node using C++.
目标: 使用 C++ 创建并运行发布方和订阅方节点。

Tutorial level: Beginner 教程级别:初级

Time: 20 minutes 时间:20分钟

Background 背景 

Nodes are executable processes that communicate over the ROS graph. In this tutorial, the nodes will pass information in the form of string messages to each other over a topic. The example used here is a simple “talker” and “listener” system; one node publishes data and the other subscribes to the topic so it can receive that data.
节点是通过 ROS 图进行通信的可执行进程。在本教程中,节点将通过主题以字符串消息的形式相互传递信息。这里使用的示例是一个简单的“说话者”和“听众”系统;一个节点发布数据,另一个节点订阅主题,以便它可以接收该数据。

The code used in these examples can be found here.
可在此处找到这些示例中使用的代码。

Prerequisites 先决条件 

In previous tutorials, you learned how to create a workspace and create a package.
在前面的教程中,你学习了如何创建工作区和创建包。

Tasks 任务 

1 Create a package
1 创建包 

Open a new terminal and source your ROS 2 installation so that ros2 commands will work.
打开一个新终端并获取 ROS 2 安装,以便 ros2 命令正常工作。

Navigate into the ros2_ws directory created in a previous tutorial.
导航到上一教程中创建的 ros2_ws 目录。

Recall that packages should be created in the src directory, not the root of the workspace. So, navigate into ros2_ws/src, and run the package creation command:
回想一下,包应该在目录中创建,而不是在工作区的根 src 目录中创建。因此,导航到 ros2_ws/src 并运行包创建命令:

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

Your terminal will return a message verifying the creation of your package cpp_pubsub and all its necessary files and folders.
您的终端将返回一条消息,验证您的软件包 cpp_pubsub 及其所有必要文件和文件夹的创建。

Navigate into ros2_ws/src/cpp_pubsub/src. Recall that this is the directory in any CMake package where the source files containing executables belong.
导航到 ros2_ws/src/cpp_pubsub/src 。回想一下,这是包含可执行文件的源文件所属的任何 CMake 包中的目录。

2 Write the publisher node
2 写入发布方节点 

Download the example talker code by entering the following command:
通过输入以下命令下载示例说话者代码:

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

Now there will be a new file named publisher_member_function.cpp. Open the file using your preferred text editor.
现在将有一个名为 publisher_member_function.cpp 的新文件。使用首选文本编辑器打开文件。

#include <chrono>
#include <functional>
#include <memory>
#include <string>

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

using namespace std::chrono_literals;

/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */

class MinimalPublisher : public rclcpp::Node
{
  public:
    MinimalPublisher()
    : Node("minimal_publisher"), count_(0)
    {
      publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
      timer_ = this->create_wall_timer(
      500ms, std::bind(&MinimalPublisher::timer_callback, this));
    }

  private:
    void timer_callback()
    {
      auto message = std_msgs::msg::String();
      message.data = "Hello, world! " + std::to_string(count_++);
      RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
      publisher_->publish(message);
    }
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
2.1 Examine the code
2.1 检查代码 

The top of the code includes the standard C++ headers you will be using. After the standard C++ headers is the rclcpp/rclcpp.hpp include which allows you to use the most common pieces of the ROS 2 system. Last is std_msgs/msg/string.hpp, which includes the built-in message type you will use to publish data.
代码的顶部包括您将使用的标准C++标头。在标准C++标头之后是 rclcpp/rclcpp.hpp 包含,它允许您使用 ROS 2 系统中最常见的部分。最后一个是 std_msgs/msg/string.hpp ,它包括将用于发布数据的内置消息类型。

#include <chrono>
#include <functional>
#include <memory>
#include <string>

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

using namespace std::chrono_literals;

These lines represent the node’s dependencies. Recall that dependencies have to be added to package.xml and CMakeLists.txt, which you’ll do in the next section.
这些行表示节点的依赖项。回想一下,必须将依赖项添加到 package.xml 和 CMakeLists.txt 中,您将在下一节中执行此操作。

The next line creates the node class MinimalPublisher by inheriting from rclcpp::Node. Every this in the code is referring to the node.
下一行通过继承自 rclcpp::Node 来创建节点类 MinimalPublisher 。代码中的每个内容都 this 引用节点。

class MinimalPublisher : public rclcpp::Node

The public constructor names the node minimal_publisher and initializes count_ to 0. Inside the constructor, the publisher is initialized with the String message type, the topic name topic, and the required queue size to limit messages in the event of a backup. Next, timer_ is initialized, which causes the timer_callback function to be executed twice a second.
公共构造函数命名节点 minimal_publisher 并初始化 count_ 为 0。在构造函数中,发布者使用消息类型、主题名称和 topic 所需的队列大小进行初始化,以在备份时限制 String 消息。接下来,被初始化, timer_ 这会导致 timer_callback 函数每秒执行两次。

public:
  MinimalPublisher()
  : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
    timer_ = this->create_wall_timer(
    500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

The timer_callback function is where the message data is set and the messages are actually published. The RCLCPP_INFO macro ensures every published message is printed to the console.
该 timer_callback 函数是设置消息数据和实际发布消息的位置。该 RCLCPP_INFO 宏可确保将每条已发布的消息打印到控制台。

private:
  void timer_callback()
  {
    auto message = std_msgs::msg::String();
    message.data = "Hello, world! " + std::to_string(count_++);
    RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
    publisher_->publish(message);
  }

Last is the declaration of the timer, publisher, and counter fields.
最后是计时器、发布者和计数器字段的声明。

rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;

Following the MinimalPublisher class is main, where the node actually executes. rclcpp::init initializes ROS 2, and rclcpp::spin starts processing data from the node, including callbacks from the timer.
MinimalPublisher 类后面是 main ,节点实际执行的位置。 rclcpp::init 初始化 ROS 2,并开始 rclcpp::spin 处理来自节点的数据,包括来自计时器的回调。

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
2.2 Add dependencies 2.2 添加依赖 

Navigate one level back to the ros2_ws/src/cpp_pubsub directory, where the CMakeLists.txt and package.xml files have been created for you.
导航一个级别回到 ros2_ws/src/cpp_pubsub 目录,其中已为您创建了 和 CMakeLists.txt package.xml 文件。

Open package.xml with your text editor.
使用文本编辑器打开 package.xml 。

As mentioned in the previous tutorial, make sure to fill in the <description><maintainer> and <license> tags:
如上一教程所述,请确保填写 <description> 和 标记 <maintainer> <license> :

<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

Add a new line after the ament_cmake buildtool dependency and paste the following dependencies corresponding to your node’s include statements:
ament_cmake 在 buildtool 依赖项之后添加新行,并粘贴与节点的 include 语句对应的以下依赖项:

<depend>rclcpp</depend>
<depend>std_msgs</depend>

This declares the package needs rclcpp and std_msgs when its code is built and executed.
这声明了包的需求 rclcpp 以及 std_msgs 何时构建和执行其代码。

Make sure to save the file.
确保保存文件。

2.3 CMakeLists.txt 2.3 CMake列表.txt 

Now open the CMakeLists.txt file. Below the existing dependency find_package(ament_cmake REQUIRED), add the lines:
现在打开 CMakeLists.txt 文件。在现有依赖项下方 find_package(ament_cmake REQUIRED) ,添加以下行:

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

After that, add the executable and name it talker so you can run your node using ros2 run:
之后,添加可执行文件并命名它 talker ,以便您可以使用以下命令 ros2 run 运行节点:

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

Finally, add the install(TARGETS...) section so ros2 run can find your executable:
最后,添加该 install(TARGETS...) 部分,以便 ros2 run 可以找到可执行文件:

install(TARGETS
  talker
  DESTINATION lib/${PROJECT_NAME})

You can clean up your CMakeLists.txt by removing some unnecessary sections and comments, so it looks like this:
您可以通过删除一些不必要的部分和注释来清理您的 CMakeLists.txt ,因此它看起来像这样:

cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub)

# Default to 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
  talker
  DESTINATION lib/${PROJECT_NAME})

ament_package()

You could build your package now, source the local setup files, and run it, but let’s create the subscriber node first so you can see the full system at work.
您现在可以构建软件包,获取本地安装文件并运行它,但让我们先创建订阅者节点,以便您可以看到完整的系统在工作。

3 Write the subscriber node
3 写入订阅节点 

Return to ros2_ws/src/cpp_pubsub/src to create the next node. Enter the following code in your terminal:
返回到 ros2_ws/src/cpp_pubsub/src 以创建下一个节点。在终端中输入以下代码:

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

Check to ensure that these files exist: 

publisher_member_function.cpp  subscriber_member_function.cpp

Open the subscriber_member_function.cpp with your text editor.
使用文本编辑器打开。 subscriber_member_function.cpp

#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;

class MinimalSubscriber : public rclcpp::Node
{
  public:
    MinimalSubscriber()
    : Node("minimal_subscriber")
    {
      subscription_ = this->create_subscription<std_msgs::msg::String>(
      "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
    }

  private:
    void topic_callback(const std_msgs::msg::String & msg) const
    {
      RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
    }
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalSubscriber>());
  rclcpp::shutdown();
  return 0;
}
3.1 Examine the code
3.1 检查代码 

The subscriber node’s code is nearly identical to the publisher’s. Now the node is named minimal_subscriber, and the constructor uses the node’s create_subscription class to execute the callback.
订阅者节点的代码与发布者的代码几乎相同。现在节点被命名 minimal_subscriber 为 ,构造函数使用该节点的 create_subscription 类来执行回调。

There is no timer because the subscriber simply responds whenever data is published to the topic topic.
没有计时器,因为订阅者只是在将数据发布到 topic 主题时做出响应。

public:
  MinimalSubscriber()
  : Node("minimal_subscriber")
  {
    subscription_ = this->create_subscription<std_msgs::msg::String>(
    "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
  }

Recall from the topic tutorial that the topic name and message type used by the publisher and subscriber must match to allow them to communicate.
从主题教程中回想一下,发布者和订阅者使用的主题名称和消息类型必须匹配才能允许它们进行通信。

The topic_callback function receives the string message data published over the topic, and simply writes it to the console using the RCLCPP_INFO macro.
该 topic_callback 函数接收通过主题发布的字符串消息数据,并简单地使用 RCLCPP_INFO 宏将其写入控制台。

The only field declaration in this class is the subscription.
此类中唯一的字段声明是订阅。

private:
  void topic_callback(const std_msgs::msg::String & msg) const
  {
    RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
  }
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;

The main function is exactly the same, except now it spins the MinimalSubscriber node. For the publisher node, spinning meant starting the timer, but for the subscriber it simply means preparing to receive messages whenever they come.
该 main 函数完全相同,只是现在它旋转 MinimalSubscriber 节点。对于发布方节点,旋转意味着启动计时器,但对于订阅者来说,它只是意味着准备在消息到来时接收消息。

Since this node has the same dependencies as the publisher node, there’s nothing new to add to package.xml.
由于此节点与发布方节点具有相同的依赖关系,因此无需向 添加 package.xml 任何新内容。

3.2 CMakeLists.txt 3.2 CMake列表.txt 

Reopen CMakeLists.txt and add the executable and target for the subscriber node below the publisher’s entries.
重新打开 CMakeLists.txt 订阅者节点的可执行文件和目标,并将其添加到发布者的条目下方。

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

install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})

Make sure to save the file, and then your pub/sub system should be ready.
确保保存文件,然后您的发布/订阅系统应该准备就绪。

4 Build and run
4 构建和运行 

You likely already have the rclcpp and std_msgs packages installed as part of your ROS 2 system. It’s good practice to run rosdep in the root of your workspace (ros2_ws) to check for missing dependencies before building:
您可能已经将 和 std_msgs 软件包 rclcpp 作为 ROS 2 系统的一部分安装。最好在工作区的根目录 ( ros2_ws ) 中运行 rosdep ,以便在生成之前检查缺少的依赖项:

LinuxmacOSWindows
rosdep install -i --from-path src --rosdistro humble -y

Still in the root of your workspace, ros2_ws, build your new package:
仍在工作区的根目录中, ros2_ws 生成新包:

LinuxmacOSWindows
colcon build --packages-select cpp_pubsub

Open a new terminal, navigate to ros2_ws, and source the setup files:
打开一个新终端,导航到 ros2_ws ,并获取安装文件:

LinuxmacOSWindows
. install/setup.bash

Now run the talker node:
现在运行说话者节点:

ros2 run cpp_pubsub talker

The terminal should start publishing info messages every 0.5 seconds, like so:
终端应每 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"

Open another terminal, source the setup files from inside ros2_ws again, and then start the listener node:
打开另一个终端,再次从内部 ros2_ws 获取设置文件,然后启动侦听器节点:

ros2 run cpp_pubsub listener

The listener will start printing messages to the console, starting at whatever message count the publisher is on at that time, like so:
侦听器将开始将消息打印到控制台,从发布者当时所在的任何消息计数开始,如下所示:

[INFO] [minimal_subscriber]: I heard: "Hello World: 10"
[INFO] [minimal_subscriber]: I heard: "Hello World: 11"
[INFO] [minimal_subscriber]: I heard: "Hello World: 12"
[INFO] [minimal_subscriber]: I heard: "Hello World: 13"
[INFO] [minimal_subscriber]: I heard: "Hello World: 14"

Enter Ctrl+C in each terminal to stop the nodes from spinning.
输入 Ctrl+C 每个终端以阻止节点旋转。

Summary 摘要 

You created two nodes to publish and subscribe to data over a topic. Before compiling and running them, you added their dependencies and executables to the package configuration files.
您创建了两个节点来发布和订阅主题上的数据。在编译和运行它们之前,已将其依赖项和可执行文件添加到包配置文件中。

Next steps 后续步骤 

Next you’ll create another simple ROS 2 package using the service/client model. Again, you can choose to write it in either C++ or Python.
接下来,您将使用服务/客户端模型创建另一个简单的 ROS 2 包。同样,您可以选择用C++或Python编写它。

Related content 相关内容 

There are several ways you could write a publisher and subscriber in C++; check out the minimal_publisher and minimal_subscriber packages in the ros2/examples repo.
有几种方法可以用C++编写发布者和订阅者;查看 ROS2/示例存储库中的 minimal_publisher AND minimal_subscriber 包。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ROS中的publishersubscriber是用于实现节点之间通信的两个重要概念。 publisher是一个节点,它可以将数据发布到一个特定的主题(topic)上。其他节点可以订阅这个主题,以接收该节点发布的数据。publishersubscriber之间的通信是异步的,即publisher不需要等待subscriber的响应。 subscriber是一个节点,它可以订阅一个特定的主题(topic),以接收其他节点发布的数据。当有新的数据发布到该主题上时,subscriber会自动接收并处理这些数据。subscriber可以订阅多个主题,以接收来自不同节点的数据。 通过使用publishersubscriber,ROS节点可以实现分布式计算,从而更好地完成各种任务。 ### 回答2: ROS (Robot Operating System)是一个开源机器人操作系统,它提供了一系列工具和库,使得机器人的开发、测试和部署更加简单、高效。ROS的核心概念之一就是发布/订阅模式(Publisher-Subscriber),这是ROS实现消息传递机制的基础。 ROS Publisher(发布者)是指向ROS消息总线发布消息的节点。节点可以向一个或者多个主题(Topic)发布消息。主题是让多个节点之间进行通信的一种机制。当一个Publisher节点发布了一条消息后,所有订阅了该主题的Subscriber就会接收到这条消息。发布者将消息发布到主题上时,需要指定主题的名称和消息类型。ROS提供了各种不同类型的消息,如字符串、数组、图像等。发布者节点用于发布消息会向主题发送一个消息头(Header),包含时间戳(Stamp)、FRAME_ID等信息,并在这个消息头中注册一些回调函数,以便接收机制可以使用这些信息来更优雅、有效的处理消息。 ROS Subscriber(订阅者)是指向ROS消息总线订阅消息的节点。Subscriber将使用主题的名称和消息类型来订阅特定的主题。当主题被发布者发布消息时,所有订阅主题的Subscriber节点都将接收到这条消息。Subscriber节点使用回调函数处理消息。回调函数是将由节点执行的函数,这些函数会在消息到达时被调用。当Subscriber节点收到一条消息时,它会触发回调函数,并将接收到的消息作为参数传递给回调函数。回调函数然后处理消息,并根据需要执行其他操作。Subscriber节点的实现可以是同步的,也可以是异步的。同步的Subscriber节点会在每次接收到消息后立即处理,异步的Subscriber节点可能会在更长的时间内缓冲消息,以便将消息打包成一组或仅处理特定类别的消息。 总之,ROS PublisherSubscriber是ROS中非常重要的概念,它们允许节点之间通过主题交换消息,实现ROS系统的消息传递机制。通过发布/订阅模式,节点之间可以共享数据,实现对机器人各个部分的控制和监视。同时,发布/订阅模式提供了一种透明的、分布式的、去中心化的消息通信机制,允许ROS系统在各种硬件和操作系统平台上运行,并与外部系统长期保持稳定连接。 ### 回答3: ROS PublisherSubscriber是ROBOT OPERATING SYSTEM(ROS)中经常使用的两种通信方式,用于在ROS系统中的不同模块之间进行信息传递和数据共享。 PublisherROS中的一种发布者,它可以将数据发布到指定话题(topic)上。发布者可以是一个传感器,比如激光雷达,也可能是某个算法模块,比如机器人路径规划。发布者将数据发送到话题,然后该话题上的所有订阅者都可以接收到该数据。 Subscriber是ROS中的一种订阅者,它可以订阅某个话题上的数据。一旦订阅者订阅了某个话题,它就可以在该话题上接收该话题上所有的数据。订阅者可能是一个控制模块,用于接收激光雷达数据,并控制机器人的行动。当订阅者收到数据后,它会进行特定的处理,如执行行动或更新机器人的状态等。 ROS PublisherSubscriber的优点在于它们提供了一种分布式系统的通信方式,这使得多个系统之间的数据共享变得更加容易和方便。在ROS系统中,一个发布者可以同时向多个话题发布数据,而订阅者也可以订阅多个话题上的数据。这使得ROS在机器人控制和其他领域中都具有广泛的应用。 在ROS中,PublisherSubscriber的使用非常简单。开发人员只需要在ROS节点中定义一个发布者和一个订阅者即可。ROS提供了许多现成的库和工具,可以帮助开发人员轻松创建发布者和订阅者,使得开发过程更加简单和快速。 总之,ROS PublisherSubscriber是ROS系统中非常重要的两个组件,它们可以方便地完成机器人控制、感知和决策等任务。这些组件的优点在于可以实现分布式通信,从而促进信息的共享和机器人系统的自主性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值