ROS2+Ubuntu22.04—— 编写简单的发布者和订阅者 (C++)案例

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

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

**教程级别:**初学者

**时间:**20分钟

内容

背景

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

这些示例中使用的代码可以在此处找到。

先决条件

在前面的教程中,你学习了如何创建工作区创建包

任务

1 创建包

打开一个新终端并获取您的 ROS 2 安装,以便命令正常工作。ros2

导航到在上一教程中创建的目录。ros2_ws

回想一下,包应该在目录中创建,而不是在工作区的根目录中创建。 因此,导航到 ,然后运行包创建命令:src``ros2_ws/src

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

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

导航到 。 回想一下,这是包含可执行文件的源文件所属的任何 CMake 包中的目录。ros2_ws/src/cpp_pubsub/src

2 写入发布者节点

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

Linux操作系统macOS 操作系统窗户

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

现在将有一个名为 . 使用您喜欢的文本编辑器打开文件。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

The top of the code includes the standard C++ headers you will be using. After the standard C++ headers is the include which allows you to use the most common pieces of the ROS 2 system. Last is , which includes the built-in message type you will use to publish data.rclcpp/rclcpp.hpp``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 and , which you’ll do in the next section.package.xml``CMakeLists.txt

The next line creates the node class by inheriting from . Every in the code is referring to the node.MinimalPublisher``rclcpp::Node``this

class MinimalPublisher : public rclcpp::Node

The public constructor names the node and initializes to 0. Inside the constructor, the publisher is initialized with the message type, the topic name , and the required queue size to limit messages in the event of a backup. Next, is initialized, which causes the function to be executed twice a second.minimal_publisher``count_``String``topic``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 function is where the message data is set and the messages are actually published. The 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 class is , where the node actually executes. initializes ROS 2, and starts processing data from the node, including callbacks from the timer.MinimalPublisher``main``rclcpp::init``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

Navigate one level back to the directory, where the and files have been created for you.ros2_ws/src/cpp_pubsub``CMakeLists.txt``package.xml

Open with your text editor.package.xml

As mentioned in the previous tutorial, make sure to fill in the , and tags:<description>``<maintainer>``<license>

<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="[email protected]">Your Name</maintainer>
<license>Apache License 2.0</license>

Add a new line after the buildtool dependency and paste the following dependencies corresponding to your node’s include statements:ament_cmake

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

This declares the package needs and when its code is built and executed.rclcpp``std_msgs

Make sure to save the file.

2.3 CMakeLists.txt

Now open the file. Below the existing dependency , 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 so you can run your node using :talker``ros2 run

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

Finally, add the section so can find your executable:install(TARGETS...)``ros2 run

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

You can clean up your 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()

您现在可以构建您的软件包,获取本地安装文件并运行它,但让我们先创建订阅者节点,以便您可以查看整个系统的工作情况。

3 写入订阅者节点

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

Linux操作系统macOS 操作系统窗户

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

检查以确保这些文件存在:

publisher_member_function.cpp  subscriber_member_function.cpp

打开“使用文本编辑器”。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 检查代码

订阅者节点的代码与发布者的代码几乎相同。 现在节点被命名为 ,构造函数使用节点的类来执行回调。minimal_subscriber``create_subscription

没有计时器,因为订阅者只是在将数据发布到主题时做出响应。topic

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

回想一下主题教程中的内容,发布者和订阅者使用的主题名称和消息类型必须匹配,才能允许他们进行通信。

该函数接收通过主题发布的字符串消息数据,并仅使用宏将其写入控制台。topic_callback``RCLCPP_INFO

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

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_;

函数完全相同,只是现在它旋转节点。 对于发布者节点来说,旋转意味着启动计时器,但对于订阅者来说,它只是意味着准备在消息到来时接收消息。main``MinimalSubscriber

Since this node has the same dependencies as the publisher node, there’s nothing new to add to .package.xml

3.2 CMakeLists.txt

Reopen 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

You likely already have the and packages installed as part of your ROS 2 system. It’s good practice to run in the root of your workspace () to check for missing dependencies before building:rclcpp``std_msgs``rosdep``ros2_ws

LinuxmacOSWindows

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

Still in the root of your workspace, , build your new package:ros2_ws

LinuxmacOSWindows

colcon build --packages-select cpp_pubsub

Open a new terminal, navigate to , and source the setup files:ros2_ws

LinuxmacOSWindows

. install/setup.bash

现在运行 talker 节点:

ros2 run 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"

打开另一个终端,再次从内部获取安装文件,然后启动侦听器节点:ros2_ws

ros2 run cpp_pubsub listener

侦听器将开始将消息打印到控制台,从发布者当时所在的任何消息计数开始,如下所示:

[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"

在每个终端中输入以阻止节点旋转。Ctrl+C

总结

您创建了两个节点来发布和订阅主题上的数据。 在编译和运行它们之前,已将其依赖项和可执行文件添加到包配置文件中。

原文链接:https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html

源码分析

目录:
在这里插入图片描述
修改文件
CMakeLists.txt

cmake_minimum_required(VERSION 3.8)

project(cpp_pubsub)

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)



find_package(tutorial_interfaces REQUIRED)                         # CHANGE

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces)         # CHANGE

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces)     # CHANGE

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

ament_package()

package.xml

<?xml version="1.0"?>   
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>



<package format="3">   
  <name>cpp_pubsub</name>   
  <version>0.0.0</version>   
  <description>TODO: Package description</description>



  <maintainer email="[email protected]">a</maintainer>   
  <license>Apache-2.0</license>   

  <buildtool_depend>ament_cmake</buildtool_depend>   
  
<depend>rclcpp</depend>   
<depend>std_msgs</depend>   

<depend>tutorial_interfaces</depend>   


  <test_depend>ament_lint_auto</test_depend>   
  <test_depend>ament_lint_common</test_depend>   

  <export>   
    <build_type>ament_cmake</build_type>   
  </export>   
</package>   

主要文件:

在这里插入图片描述

发布者
publisher_member_function.cpp
订阅者
subscriber_member_function.cpp

源码:
发布者
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;
}

订阅者
subscriber_member_function.cpp

// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <functional>
#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;
}

之后构建,编译,运行即可按照之前的说明来走。
在这里插入图片描述

最终的结果如下图 :
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/dongbao520/article/details/137474867