ROS2话题介绍与实现

 


ROS 2 话题(Topic)全面介绍与实现教程


1. 什么是 ROS 2 话题?

在 ROS 2 中,话题(Topic) 是节点之间异步通信的核心机制。它采用 发布/订阅(Publish/Subscribe)模型

  • Publisher(发布者):负责向指定话题发送消息。
  • Subscriber(订阅者):订阅某个话题并接收消息。
  • Topic(话题):消息传输的通道,类似一个“广播频道”。
  • Message(消息):消息内容的格式与数据结构,由 .msg 文件定义。

为什么使用话题?

  • 组件之间解耦:发布者和订阅者不需要直接知道对方存在,只需约定话题名和消息类型。
  • 一对多通信:多个订阅者可同时接收同一个话题的消息。
  • 适合传感器数据流:如激光雷达、相机图像、IMU 数据。

举例说明:
想象一个电台广播,发布者相当于电台节目主持人,订阅者相当于收听节目的听众,而“话题”就是广播的频道频率。


2. 话题通信流程图

flowchart LR
    A[Publisher 节点] -->|发布消息| B[(Topic 话题)]
    B -->|订阅消息| C[Subscriber 节点1]
    B -->|订阅消息| D[Subscriber 节点2]

解释:

  • 发布者节点将消息发送到话题。
  • 所有订阅该话题的节点都能接收到相同消息。

3. 话题相关基础命令

ROS 2 提供一系列命令用于查看、调试话题

3.1 查看所有话题

ros2 topic list

3.2 查看某话题的消息类型

ros2 topic type /话题名

3.3 实时查看话题消息内容

ros2 topic echo /话题名

3.4 发布测试消息(调试用)

ros2 topic pub /话题名 消息类型 "{字段: 值}"

示例:

ros2 topic pub /chatter std_msgs/msg/String "{data: 'Hello ROS2'}"

4. 使用 Python 实现话题

4.1 发布者(publisher.py)

此节点会每隔 0.5 秒发布一条消息chatter 话题。

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class MinimalPublisher(Node):
    def __init__(self):
        super().__init__('minimal_publisher')
        # 创建发布者,话题名为 "chatter",消息类型为 String
        self.publisher_ = self.create_publisher(String, 'chatter', 10)
        # 定时器:每 0.5 秒执行一次回调函数
        self.timer = self.create_timer(0.5, self.timer_callback)
        self.count = 0

    def timer_callback(self):
        msg = String()
        msg.data = f'Hello ROS2 (Python): {self.count}'
        self.publisher_.publish(msg)
        self.get_logger().info(f'Publishing: "{msg.data}"')
        self.count += 1

def main(args=None):
    rclpy.init(args=args)
    node = MinimalPublisher()
    rclpy.spin(node)  # 循环运行节点
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

4.2 订阅者(subscriber.py)

此节点会订阅 chatter 话题并打印接收到的消息

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class MinimalSubscriber(Node):
    def __init__(self):
        super().__init__('minimal_subscriber')
        self.subscription = self.create_subscription(
            String,
            'chatter',
            self.listener_callback,
            10)

    def listener_callback(self, msg):
        self.get_logger().info(f'I heard: "{msg.data}"')

def main(args=None):
    rclpy.init(args=args)
    node = MinimalSubscriber()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

5. 使用 C++ 实现话题

5.1 发布者(publisher.cpp)

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

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher() : Node("minimal_publisher"), count_(0)
  {
    // 创建发布者
    publisher_ = this->create_publisher("chatter", 10);
    // 每 500 毫秒触发一次回调
    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 ROS2 (C++): " + std::to_string(count_++);
    RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
    publisher_->publish(message);
  }

  rclcpp::Publisher::SharedPtr publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
  size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared());
  rclcpp::shutdown();
  return 0;
}

5.2 订阅者(subscriber.cpp)

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

class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber() : Node("minimal_subscriber")
  {
    subscription_ = this->create_subscription(
      "chatter", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1));
  }

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

  rclcpp::Subscription::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared());
  rclcpp::shutdown();
  return 0;
}

6. 工程结构与配置

6.1 工程结构

ros2_ws/
└── src/
    └── topic_demo/
        ├── CMakeLists.txt
        ├── package.xml
        ├── src/
        │   ├── publisher.cpp
        │   └── subscriber.cpp
        └── topic_demo/
            ├── __init__.py
            ├── publisher.py
            └── subscriber.py

6.2 package.xml 关键内容


  topic_demo
  0.0.0
  ROS2 Topic Demo
  Your Name
  Apache-2.0

  ament_cmake
  rclcpp
  rclpy
  std_msgs


6.3 CMakeLists.txt 关键内容

cmake_minimum_required(VERSION 3.5)
project(topic_demo)

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(publisher src/publisher.cpp)
ament_target_dependencies(publisher rclcpp std_msgs)

add_executable(subscriber src/subscriber.cpp)
ament_target_dependencies(subscriber rclcpp std_msgs)

install(TARGETS
  publisher
  subscriber
  DESTINATION lib/${PROJECT_NAME})

ament_package()

7. 编译与运行

7.1 编译

cd ~/ros2_ws
colcon build
source install/setup.bash

7.2 运行 Python 版本

ros2 run topic_demo publisher.py
ros2 run topic_demo subscriber.py

7.3 运行 C++ 版本

ros2 run topic_demo publisher
ros2 run topic_demo subscriber

8. 常见调试方法

  • 查看话题是否存在

    ros2 topic list
    
  • 查看话题消息

    ros2 topic echo /chatter
    
  • 确认消息类型

    ros2 topic type /chatter
    
  • 手动发布调试

    ros2 topic pub /chatter std_msgs/msg/String "{data: 'test'}"
    

 

你可能感兴趣的:(机器人,ROS,c++,机器人,python,c++)