ZeroOne AI
← 返回文章列表

手把手做ROS2机器人系统开发(四):C++实现发布者与订阅者

👁 0
分类:机器人自动化

发布者(Publisher)与订阅者(Subscriber)是 ROS2 话题通信的核心模型,也是所有机器人应用(传感器数据分发、状态上报、控制指令下发等)的基础。本章节使用 C++ 编写一个最小可运行的发布者/订阅者示例,作为后续开发的标准模板,帮助大家清晰掌握节点程序的完整编写流程。

手把手做ROS2机器人系统开发(四):C++实现发布者与订阅者

教程背景

发布者(Publisher)与订阅者(Subscriber)是 ROS2 话题通信的核心模型,也是所有机器人应用(传感器数据分发、状态上报、控制指令下发等)的基础。本章节使用 C++ 编写一个最小可运行的发布者/订阅者示例,作为后续开发的标准模板,帮助大家清晰掌握节点程序的完整编写流程。

一、程序编写

1. 创建代码目录

mkdir src
cd src/

2. 创建软件包

ros2 pkg create --build-type ament_cmake cpp_pubsub

3. 测试编译

colcon build

注意:编译操作需在包含 src 目录的上级工作空间根目录执行。

4. 打印与还原包环境目录

当需要剔除已删除软件包的引用时,可先查看再重置环境变量:

printenv AMENT_PREFIX_PATH CMAKE_PREFIX_PATH
export AMENT_PREFIX_PATH=/opt/ros/humble
export CMAKE_PREFIX_PATH=/opt/ros/humble

5. 设置系统全局头文件目录

为了让 VS Code 正确识别 ROS2 头文件,将全局头文件目录加入配置:

/opt/ros/humble/include/

.vscode/c_cpp_properties.json 中配置:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/opt/ros/humble/include/**",
                "/opt/ros/humble/include/std_msgs"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c17",
            "cppStandard": "gnu++17",
            "intelliSenseMode": "linux-gcc-x64"
        }
    ],
    "version": 4
}

6. 发布者程序编写

创建文件 src/cpp_pubsub/src/publisher_member_function.cpp

/**
 * @file publisher_member_function.cpp
 * @author gmotion (motion_gui@126.com)
 * @brief 发布者实例程序
 * @version 0.1
 * @date 2022-06-09
 * @copyright gmotion Copyright (c) 2022
 */

#include <chrono>       //处理时间类
#include <functional>   //函数对象
#include <memory>       //内存管理
#include <string>       //字符串

#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>

using namespace std::chrono_literals;   //处理时间类

//ROS2 发布节点信息
class MinimalPublisher : public rclcpp::Node
{
public:
    //默认构造函数和析构函数
    MinimalPublisher();
    ~MinimalPublisher();

private:
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    size_t count_;
    void timer_callback();
};

//默认构造函数
MinimalPublisher::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));
}

//默认析构函数
MinimalPublisher::~MinimalPublisher()
{

}

//默认回调函数
void MinimalPublisher::timer_callback()
{
    auto message = std_msgs::msg::String();
    message.data = "Hello world,gmotion! =>" + std::to_string(count_ ++ );
    RCLCPP_INFO(this->get_logger(),"Publisher: '%s' ",message.data.c_str());
    publisher_->publish(message);
}

//主运行程序
int main(int argc,char * argv[])
{
    rclcpp::init(argc,argv);
    rclcpp::spin(std::make_shared<MinimalPublisher>());
    rclcpp::shutdown();
    return 0;
}

7. 订阅者程序编写

创建文件 src/cpp_pubsub/src/subscriber_member_function.cpp

/**
 * @file subscriber_member_function.cpp
 * @author gmotion (motion_gui@126.com)
 * @brief 订阅者实例程序
 * @version 0.1
 * @date 2022-06-10
 * @copyright Copyright (c) 2022
 */

#include <functional>   //函数对象
#include <memory>       //内存管理
#include <string>       //字符串

#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>

using std::placeholders::_1;    //C++占位符

//ROS2 订阅者节点信息
class MinimalSubscriber : public rclcpp::Node
{
public:
    //默认构造函数和析构函数
    MinimalSubscriber();
    ~MinimalSubscriber();

private:
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr Subscriber_;
    void topic_callback(std_msgs::msg::String::SharedPtr msg);
};

//默认构造函数
MinimalSubscriber::MinimalSubscriber()
: Node("minimal_Subscriber")
{
    Subscriber_ = this->create_subscription<std_msgs::msg::String>(
        "topic",10,std::bind(&MinimalSubscriber::topic_callback,this,_1));
}

//默认析构函数
MinimalSubscriber::~MinimalSubscriber()
{

}

//默认回调函数
void MinimalSubscriber::topic_callback(std_msgs::msg::String::SharedPtr msg)
{
    RCLCPP_INFO(this->get_logger(),"Subscriber Read: '%s' ",msg->data.c_str());
}

//主运行程序
int main(int argc,char * argv[])
{
    rclcpp::init(argc,argv);
    rclcpp::spin(std::make_shared<MinimalSubscriber>());
    rclcpp::shutdown();
    return 0;
}

8. 软件包依赖设置

编辑 src/cpp_pubsub/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>Examples of minimal publisher/subscriber using rclcpp</description>
  <maintainer email="motion_gui@126.com">Gmotion</maintainer>
  <license>Apache License 2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

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

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

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

9. 设置编译选项

编辑 src/cpp_pubsub/CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(cpp_pubsub)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# 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 dependencies
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)

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

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

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  set(ament_cmake_copyright_FOUND TRUE)
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

二、程序测试

1. 编译程序

在工作空间根目录执行:

colcon build

2. 引用软件包

. install/setup.bash

3. 开启两个节点测试

终端 1:运行发布者(talker)

. install/setup.bash
ros2 run cpp_pubsub talker

终端 2:运行订阅者(listener)

. install/setup.bash
ros2 run cpp_pubsub listener

程序准确执行,发布者持续发布消息、订阅者持续接收并打印消息,达到预期结果。

小结

本章节实现了 ROS2 中最基础的发布者/订阅者节点,完整覆盖了"创建包 → 编写代码 → 配置依赖 → 编译 → 运行测试"的标准化流程。该模板可直接复用于传感器数据采集、状态发布等场景。下一章节将讲解服务器与客户端的实现。

SEO关键词: ROS2 C++发布者订阅者, ROS2话题通信教程, rclcpp节点编程, ROS2 colcon编译, ROS2功能包创建

评论(0