ROS2话题:节点间传递数据的桥梁

0 阅读4分钟

一、什么是话题(Topic)?

1.1 话题的定义

在ROS2中,话题(Topic)是一种发布/订阅(Publish/Subscribe)模式的通信机制:

  • 发布者(Publisher):生产数据并发送到话题
  • 订阅者(Subscriber):从话题接收数据
  • 话题(Topic):数据传输的命名通道
发布者节点                话题                订阅者节点
┌────────┐          ┌─────────┐          ┌────────┐
│Publisher├─────────→│ /topic  ├─────────→│Subscriber│
│        │          │ (消息队列)│          │        │
└────────┘          └─────────┘          └────────┘
    数据源              数据通道             数据消费者

1.2 话题的本质特征

特性说明
异步通信发布者不等待订阅者响应
单向传输数据单向流动
多对多一个话题可有多个发布/订阅者
松耦合发布者不知道订阅者存在
强类型消息必须符合定义的类型

2、消息类型

2.1 消息类型定义

话题传输的数据必须符合预定义的消息类型(也被称为消息接口),消息类型其实就是一种数据类型,消息类型可以自定义。

消息类型例子

# std_msgs/msg/String.msg
string data

# geometry_msgs/msg/Twist.msg
Vector3 linear   # 线速度
Vector3 angular  # 角速度

# sensor_msgs/msg/LaserScan.msg
std_msgs/Header header
float32 angle_min
float32 angle_max
float32[] ranges

消息文件格式

  • 文件扩展名:.msg
  • 每个字段格式:类型 名称

2.2 常用消息类型

消息类型用途典型话题
std_msgs/String文本数据/diagnostics
geometry_msgs/Twist速度指令/cmd_vel
sensor_msgs/Image图像数据/camera/image
sensor_msgs/LaserScan激光数据/scan
nav_msgs/Odometry里程计数据/odom
sensor_msgs/JointState关节状态/joint_states

3.话题通信编程示例

3.1 创建发布者(Python)

1.创建发布者功能包:

#在ROS2工作空间的src目录下执行
ros2 pkg create --build-type ament_python topic_pub_pkg --license Apache-2.0

2.在topic_pub_pkg/topic_pub_pkg目录下创建topic_pub_node.py:

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

class VelocityPublisher(Node):
    """
    速度发布者节点
    功能:定期发布机器人速度指令
    """
    
    def __init__(self):
        super().__init__('velocity_publisher')
        
        # 创建发布者
        # 参数:消息类型、话题名称、QoS深度
        self.publisher = self.create_publisher(
            Twist,
            '/cmd_vel',
            10
        )
        
        # 创建定时器,周期性发布
        timer_period = 0.1  # 10Hz
        self.timer = self.create_timer(
            timer_period,
            self.timer_callback
        )
        
        self.linear_speed = 0.2   # 线速度 m/s
        self.angular_speed = 0.1  # 角速度 rad/s
        
        self.get_logger().info('速度发布者已启动')
    
    def timer_callback(self):
        """定时器回调函数"""
        msg = Twist()
        msg.linear.x = self.linear_speed
        msg.linear.y = 0.0
        msg.linear.z = 0.0
        msg.angular.x = 0.0
        msg.angular.y = 0.0
        msg.angular.z = self.angular_speed
        
        self.publisher.publish(msg)
        self.get_logger().info(
            f'发布速度: linear={msg.linear.x:.2f}, angular={msg.angular.z:.2f}'
        )

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

3.在setup.py中添加入口点

entry_points={
        'console_scripts': [
            'topic_pub_node=topic_pub_pkg.topic_pub_node:main',
        ],
    },

3.2 创建订阅者(Python)

1.创建订阅者功能包:

#在ROS2工作空间的src目录下执行
ros2 pkg create --build-type ament_python topic_sub_pkg --license Apache-2.0

2.在topic_sub_pkg/topic_sub_pkg目录下创建topic_sub_node.py:

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist

class VelocitySubscriber(Node):
    """
    速度订阅者节点
    功能:接收并处理速度指令
    """
    
    def __init__(self):
        super().__init__('velocity_subscriber')
        
        # 创建订阅者
        # 参数:消息类型、话题名称、回调函数、QoS深度
        self.subscription = self.create_subscription(
            Twist,
            '/cmd_vel',
            self.velocity_callback,
            10
        )
        
        self.get_logger().info('速度订阅者已启动')
    
    def velocity_callback(self, msg):
        """接收到消息时的回调函数"""
        self.get_logger().info(
            f'收到速度指令: linear.x={msg.linear.x:.2f}, '
            f'angular.z={msg.angular.z:.2f}'
        )
       

def main(args=None):
    rclpy.init(args=args)
    node = VelocitySubscriber()
    
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

3.在setup.py中添加入口点

entry_points={
        'console_scripts': [
            'topic_sub_node=topic_sub_pkg.topic_sub_node:main',
        ],
    },

3.3 构建和运行

3.3.1 构建发布者和订阅者节点

#构建发布者功能包
colcon build --packages-select topic_pub_pkg
#构建订阅者功能包
colcon build --packages-select topic_sub_pkg

3.3.2运行发布者和订阅者节点

#运行发布者节点
source install/setup.bash
ros2 run topic_pub_pkg topic_pub_node

#运行订阅者节点
source install/setup.bash
ros2 run topic_sub_pkg topic_sub_node

3.4 输出示例

发布者节点:

1.png

订阅者节点:

2.png

4.话题的命令行操作

4.1 列出所有活动话题

ros2 topic list

新开一个终端,输入上述指令,显示结果:

/cmd_vel
/parameter_events
/rosout

/cmd_vel就是发布者节点发布的话题。

/parameter_events是ROS 2 系统中一个内置的全局标准话题,用于实时监控和通知网络中所有节点的参数变化。

/rosout是 ROS2 中一个特殊的、全局内置的话题,承担着整个系统的日志收集与分发功能。

4.2 查看话题信息

ros2 topic info [话题名称]

输入:

ros2 topic info /cmd_vel

显示结果:

Type: geometry_msgs/msg/Twist
Publisher count: 1
Subscription count: 1

4.3 查看话题消息类型

ros2 topic type [话题名称]

输入:

ros2 topic type /cmd_vel

显示结果:

geometry_msgs/msg/Twist

4.4 查看话题发布频率

ros2 topic hz [话题名称]

输入:

ros2 topic hz /cmd_vel

显示结果:

average rate: 10.001
        min: 0.100s max: 0.100s std dev: 0.00017s window: 12
average rate: 10.001
        min: 0.100s max: 0.100s std dev: 0.00015s window: 23
average rate: 10.000
        min: 0.100s max: 0.100s std dev: 0.00015s window: 33
average rate: 10.000

4.5 查看话题带宽

ros2 topic bw [话题名称]

输入:

ros2 topic bw /cmd_vel

显示结果:

Subscribed to [/cmd_vel]
537 B/s from 10 messages
        Message size mean: 52 B min: 52 B max: 52 B
528 B/s from 20 messages
        Message size mean: 52 B min: 52 B max: 52 B
525 B/s from 30 messages
        Message size mean: 52 B min: 52 B max: 52 B

4.6 打印话题内容

ros2 topic echo [话题名称]

输入:

ros2 topic echo /cmd_vel

显示结果:

linear:
  x: 0.2
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.1

4.7 发布话题消息

ros2 topic pub [topic_name] [msg_type] [msg_data]

输入:

ros2 topic pub /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.5}, angular: {z: 0.5}}"

topic_sub_node显示结果:

INFO] [1775720799.149135035] [velocity_subscriber]: 收到速度指令: linear.x=0.50, angular.z=0.50
[INFO] [1775720800.149104853] [velocity_subscriber]: 收到速度指令: linear.x=0.50, angular.z=0.50
[INFO] [1775720801.148919754] [velocity_subscriber]: 收到速度指令: linear.x=0.50, angular.z=0.50