4. 话题通信深入
话题(Topic)是 ROS2 中最常用的通信方式,采用“发布/订阅”模型实现节点间的异步数据流。它适合传感器数据、状态广播、控制指令等持续、单向、多对多的场景。本章从代码实现、消息定义、QoS 策略、多线程回调到实时性优化,逐层深入话题通信的工程细节。
一、核心原理:发布者与订阅者的数据通路
发布者通过 create_publisher 注册话题,订阅者通过 create_subscription 注册回调。二者并不直接建立连接,而是由 DDS 中间件根据话题名和 QoS 匹配后建立数据通道。发布者调用 publish() 将消息写入 DDS,订阅者由执行器(Executor)调度回调消费消息。理解这条通路,是排查“收不到数据”问题的关键。
二、消息定义:接口即契约
ROS2 消息定义在 .msg 文件中,字段类型包括基础类型(int32、float64、string、bool)和复合类型(其他消息或数组)。消息是发布者与订阅者之间的“契约”,一旦发布,字段顺序和类型必须严格一致。
# 文件:msg/RobotStatus.msg
std_msgs/Header header
string robot_id
float32 battery_voltage
float32[] joint_positions
bool is_emergency
在 package.xml 中声明 rosidl_default_generators 依赖,在 CMakeLists.txt 中调用 rosidl_generate_interfaces 生成 C++/Python 代码。修改消息后必须重新编译,否则运行时会报类型不匹配。
三、QoS 配置:决定通信行为
QoS(Quality of Service)是 ROS2 区别于 ROS1 的核心特性。它通过策略组合控制可靠性、持久性和资源占用。下表列出常用策略及其适用场景:
| 策略 | 取值 | 适用场景 |
|---|---|---|
| Reliability | RELIABLE / BEST_EFFORT | 控制指令用 RELIABLE;高频传感器用 BEST_EFFORT |
| Durability | VOLATILE / TRANSIENT_LOCAL | 地图、参数等“晚加入”数据用 TRANSIENT_LOCAL |
| History | KEEP_LAST / KEEP_ALL | 实时控制用 KEEP_LAST,深度 1~10 |
| Deadline | 时间周期 | 监控数据是否按时到达 |
| Liveliness | AUTOMATIC / MANUAL | 检测发布者是否存活 |
SensorDataQoS(BEST_EFFORT + KEEP_LAST 5),控制话题使用 ReliableQoS,静态地图使用 TransientLocalQoS。四、多线程回调与实时性优化
默认单线程执行器(SingleThreadedExecutor)按顺序处理所有回调,一个耗时回调会阻塞其他话题。使用 MultiThreadedExecutor 并配合回调组(CallbackGroup)可实现并发:
auto executor = rclcpp::executors::MultiThreadedExecutor();
auto cb_group = create_callback_group(
rclcpp::CallbackGroupType::Reentrant);
auto sub = create_subscription<Msg>(
"topic", qos,
[this](Msg::SharedPtr msg){ /* 耗时处理 */ },
rclcpp::SubscriptionOptions().callback_group = cb_group);
executor.add_node(shared_from_this());
executor.spin();
实时性优化要点:
- 将高频传感器回调与低频控制回调放入不同回调组,避免相互阻塞。
- 回调内避免动态内存分配、日志打印和阻塞 I/O。
- 使用
KEEP_LAST且深度设为 1,减少队列延迟。 - 对时间敏感任务,可设置线程优先级或使用实时内核。
五、案例与复盘:高频激光雷达数据丢失
某机器人订阅 10 Hz 激光雷达话题,发现偶尔丢帧。排查发现:订阅者使用默认 RELIABLE + KEEP_LAST 10,而发布者使用 BEST_EFFORT。QoS 不兼容导致部分数据被丢弃。将订阅者改为 SensorDataQoS 后恢复正常。复盘结论:发布者与订阅者的 QoS 必须成对设计,不能只改一端。
另一个常见问题是回调阻塞:导航节点在单线程执行器中同时订阅地图和里程计,地图回调耗时 200 ms,导致里程计回调延迟。改用多线程执行器并将两者放入不同回调组后,里程计延迟降至 5 ms 以内。
ros2 topic info /topic --verbose 查看 QoS 匹配情况,再用 ros2 topic hz 和 ros2 topic delay 量化实时性,最后决定是否引入多线程或调整 QoS。掌握发布/订阅实现、消息契约、QoS 策略与多线程回调,是构建稳定、低延迟 ROS2 话题通信的基础。实际工程中应结合场景选择 QoS,并通过工具持续观测通信质量。