2. ROS2通信基础
ROS2 的通信体系并不是“一个话题走天下”,而是由多种通信原语共同构成:节点是运行单元,话题、服务、动作是三种面向不同交互模式的通信方式,参数用于运行时配置,执行器负责调度回调,回调组则决定并发边界。理解这些对象之间的关系,是后续掌握 DDS 与 QoS 的前提。
一、核心对象与通信原语
ROS2 中一切可运行的计算单元都是节点(Node)。节点通过发布/订阅、请求/响应、目标/反馈/结果等方式交换数据。它们底层都建立在 DDS 的发布订阅模型之上,但抽象层级和语义不同。
- 话题(Topic):异步、单向、多对多,适合高频传感器流、状态广播。发布者与订阅者解耦,通过消息类型匹配。
- 服务(Service):同步、双向、一对一,适合“请求—应答”式短操作,如查询参数、触发标定。
- 动作(Action):长时任务、可取消、带周期反馈,适合导航、抓取、轨迹执行等需要过程监控的场景。
- 参数(Parameter):节点级键值配置,支持声明、读取、设置与变更通知,常用于阈值、增益、模式开关。
- 执行器(Executor):负责从等待集中取出就绪回调并执行,决定单线程或多线程调度。
- 回调组(Callback Group):把回调划分到互斥组或可重入组,控制并发与死锁风险。
下图给出这些对象在同一个节点内的结构关系,以及它们与 DDS 的映射位置。
二、三种通信方式的选型方法
选择通信方式时,先问三个问题:是否需要应答?任务是否长时?是否需要过程反馈?据此可得到清晰的判断路径。
- 只需持续广播数据、不关心谁接收:用话题。
- 需要一次明确的请求并等待结果,且耗时短:用服务。
- 任务耗时长、需要中途反馈、可能取消:用动作。
- 只是配置项、阈值、模式:用参数,不要为它单独建话题。
| 通信方式 | 方向 | 典型时延 | 是否可取消 | 典型场景 |
|---|---|---|---|---|
| 话题 Topic | 单向、多对多 | 微秒~毫秒 | 否 | 激光雷达、IMU、图像流 |
| 服务 Service | 双向、一对一 | 毫秒级 | 否 | 查询状态、触发标定 |
| 动作 Action | 双向、带反馈 | 秒~分钟 | 是 | 导航、抓取、轨迹执行 |
| 参数 Parameter | 节点级配置 | — | — | 增益、阈值、模式开关 |
三、执行器与回调组的并发控制
执行器决定“谁来跑回调”,回调组决定“哪些回调不能同时跑”。默认情况下,一个节点使用单线程执行器,所有回调串行执行;一旦某个回调阻塞,整个节点都会卡住。多线程执行器可以并发执行不同回调组中的回调,但同一互斥组内仍然串行。
// 创建互斥回调组,保证同一组内回调不并发
auto cb_group = create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
// 订阅时绑定回调组
rclcpp::SubscriptionOptions sub_opt;
sub_opt.callback_group = cb_group;
auto sub = create_subscription<std_msgs::msg::String>(
"chatter", 10,
[](std_msgs::msg::String::SharedPtr msg) { /* ... */ },
sub_opt);
// 多线程执行器
rclcpp::executors::MultiThreadedExecutor exec;
exec.add_node(node);
exec.spin();
经验法则:高频传感器回调放入独立可重入组,参数服务与状态查询放入互斥组,长时任务回调不要与高频回调共享同一互斥组,否则会互相拖慢。
四、DDS 在 ROS2 中的角色
ROS2 并不自己实现网络传输,而是通过 RMW(ROS Middleware)接口把话题、服务、动作映射到 DDS 的 DataWriter/DataReader。DDS 负责发现、匹配、序列化、传输与 QoS 执行。也就是说,ROS2 定义语义,DDS 负责通信质量。
- 话题 → DDS Topic,发布者 → DataWriter,订阅者 → DataReader。
- 服务 → 两个 DDS Topic(请求与应答),动作 → 一组 Topic 加状态机。
- 参数 → 基于服务的配置接口,最终仍落到 DDS 通信。
- QoS 策略(可靠性、历史、深度、寿命、截止时间)由 DDS 在端点匹配时协商执行。
常见误区:认为“话题通了就万事大吉”。实际上,若发布端与订阅端 QoS 不兼容(如 RELIABLE 对 BEST_EFFORT),DDS 会拒绝匹配,表现为“节点在、话题在、就是收不到数据”。排障时应先看
ros2 topic info --verbose 中的 QoS 与端点匹配情况。
五、案例与复盘
某移动机器人节点同时订阅激光雷达(10 Hz)、IMU(100 Hz),并提供参数设置服务。最初使用单线程执行器,结果参数设置请求经常等待数百毫秒,因为高频 IMU 回调占满了执行线程。复盘后做了三件事:
- 把 IMU 与激光回调放入两个独立可重入组,避免互相阻塞。
- 参数服务放入互斥组,保证配置修改的原子性。
- 改用 MultiThreadedExecutor,并确认各组之间没有共享未加锁状态。