第1章 ROS2与话题通信概述

ROS2 的话题通信,说白了就是机器人系统里最核心的「消息总线」。我刚开始接触 ROS 时,总觉得它像个黑盒子——节点之间怎么就能悄无声息地传数据?后来亲手搭了一遍才明白,话题通信就是一套发布-订阅模型,简单,但极其强大。

1.1 ROS2 架构概览

ROS2 的底层不再是 ROS1 那种自研的通信协议,而是换成了 DDS(数据分发服务)。嗯,这里要注意:DDS 是一个工业级中间件标准,不是 ROS 自己搞的。ROS2 只是把 DDS 作为传输层,所以天生就支持 QoS、发现、多播这些能力。

我个人习惯把 ROS2 的架构分成三层:应用层(你的节点代码)、中间件层(rcl、rmw 以及 DDS 实现)、操作系统层(Linux/Windows/RTOS)。你写的发布者、订阅者,其实都是在跟 rcl(ROS 客户端库)打交道,rcl 再通过 rmw 适配层调用底层的 DDS。

ROS2 话题通信架构 应用层 · 节点 (Publisher / Subscriber) rclcpp / rclpy · 话题 · 回调函数 中间件适配层 · rmw / rcl DDS 接口封装 · QoS 策略 · 序列化 DDS 中间件 (Fast DDS / Cyclone DDS / GurumDDS) 发现协议 · 发布/订阅 · 可靠性 · 多播 发布者节点 订阅者节点

1.2 节点与话题的关系

节点是 ROS2 里的最小执行单元。你可以把它想象成一个独立的小程序,专门干一件事——比如读取激光雷达数据,或者控制电机。节点之间怎么通信?就是通过话题。

一个节点可以发布多个话题,也可以订阅多个话题。话题本身只是一个字符串名字,比如 /scan、/cmd_vel。节点和话题之间是多对多的关系。我记得有一次调试多机器人系统,三个节点同时往 /odom 上发数据,结果订阅端全乱了——后来才意识到话题名冲突了。所以命名规范特别重要。

核心关系: 节点是执行者,话题是通道。发布者把数据塞进通道,订阅者从通道里取数据。双方不需要知道对方是谁。

1.3 发布-订阅模型

这个模型其实很生活化。你想想看,就像你订阅了一个技术公众号——作者(发布者)写了文章往平台上一丢,你(订阅者)就能收到推送。中间那个「平台」就是 ROS2 的话题。

发布者只管发,不管有没有人收;订阅者只管收,不管是谁发的。这种解耦让系统特别灵活。我做过一个项目,需要把摄像头图像同时送给视觉检测、显示和录像三个模块——只需要让它们都订阅同一个 /image_raw 话题就行,发布者完全不用改。

下面是发布-订阅的典型流程(伪代码风格):

# 发布者节点 (Python 示意)
import rclpy
from std_msgs.msg import String

node = rclpy.create_node('talker')
pub = node.create_publisher(String, 'chatter', 10)

while rclpy.ok():
    msg = String()
    msg.data = 'Hello ROS2'
    pub.publish(msg)      # 发布,不关心谁在收
# 订阅者节点
def callback(msg):
    print('收到:', msg.data)

node = rclpy.create_node('listener')
sub = node.create_subscription(String, 'chatter', callback, 10)
rclpy.spin(node)          # 等待回调

1.4 DDS 中间件简介

DDS 是 ROS2 跟 ROS1 最大的区别。ROS1 用的是自研的 TCPROS/UDPROS,而 ROS2 直接站在 DDS 的肩膀上。DDS 提供了什么?说白了就是「谁在哪儿、数据怎么传、传丢了怎么办」这些底层能力。

我个人最欣赏 DDS 的自动发现机制——节点启动后,不需要配置 IP 和端口,DDS 会自动在网络上广播自己的存在,找到匹配的发布/订阅。这在多机器人场景下特别爽。我曾经在四台机器人上部署同一个系统,零配置,话题自动连通。

DDS 还支持多种 QoS 策略,比如可靠性(可靠传输 vs 尽力传输)、历史记录(保留最近 N 个样本)、生命周期等。这些在后面的章节会详细展开。

刚开始接触 DDS 时,我建议先别纠结具体实现。你只需要知道:ROS2 的话题通信底层是 DDS,它帮你处理了网络发现、数据序列化、丢包重传这些脏活。你专注写业务逻辑就好。

1.5 话题通信的典型数据流

为了让你更直观地理解,我画了一张数据流图。发布者节点把消息序列化成字节流,通过 DDS 发送到网络;订阅者节点收到后反序列化,再触发你的回调函数。

发布者节点 序列化 DDS 网络 发现 · 传输 · QoS 反序列化 订阅者节点 msg.data = "hello" 回调函数触发
我曾经踩过一个坑:在强实时场景下用了默认的 QoS(可靠传输),结果网络抖动导致发布者阻塞。后来改成尽力传输 + 设置合适的超时,问题才解决。所以理解 DDS 的 QoS 不是锦上添花,是必修课。

1.6 本章小结

ROS2 的话题通信,本质上就是节点通过 DDS 中间件进行发布-订阅。节点解耦、自动发现、灵活 QoS 是它的三大亮点。你不需要一开始就精通 DDS 的所有细节,但一定要理解节点和话题的关系——这是后面所有实战的基石。

嗯,我个人习惯在开始一个新项目前,先用纸笔画一下话题图:哪些节点、哪些话题、谁发布谁订阅。画清楚了,代码写起来就顺了。