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 包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值