在 ROS 2 Jazzy 的日常调试中,一个常见现象是:发布端看起来已经把消息发出去了,但订阅端始终没有收到任何数据。很多工程师第一反应是检查节点是否启动、话题名是否拼错、消息类型是否一致;这些当然重要,但在 ROS 2 里还有一个更隐蔽、也更结构性的原因——发布端与订阅端的 QoS 配置并不兼容,导致底层根本没有建立连接。本文围绕 Jazzy 文档中的 QoS 语义,说明为什么“消息发出去了”并不等于“订阅端会收到”,并给出非侵入式的观察与记录方法。
在 ROS 1 中,只要发布者和订阅者使用相同话题名与消息类型,通常就会建立连接;ROS 2 改变了这一模型。根据 Jazzy 官方文档,发布端与订阅端可以各自独立配置 QoS profile,而两者之间只有在“请求—提供”关系下互相兼容时才会建立连接。订阅端请求的是它愿意接受的最低质量,发布端提供的是它能够提供的最大质量;只有当被请求策略的每一项都不比被提供策略更严格时,连接才会成立。也就是说,话题名与类型只是入场券,QoS 兼容性才是连接是否真正发生的判据,见 ROS 2 Jazzy QoS 官方文档。
在各类策略里,reliability 与 durability 的不兼容尤其典型。官方文档给出的兼容性表显示:当发布端为 best effort、订阅端请求 reliable 时,结果为不兼容;反过来发布端 reliable、订阅端 best effort 则可以连接。durability 方面,发布端 volatile、订阅端请求 transient local 时会直接“无通信”,而发布端 transient local、订阅端 volatile 虽然兼容,但只传新消息,不保留历史样本给后加入的订阅者,见同一份 Jazzy QoS 文档。因此,一个使用 sensor data 风格、偏 best effort 的发布节点,若遇到默认 reliable 的订阅节点,就可能出现“发布在跑、订阅无输出”的静默失败。
除 reliability 与 durability 外,deadline、liveliness 与 lease duration 也会影响兼容性。Jazzy 文档指出,所有会影响兼容性的策略都必须同时兼容,连接才会建立;例如即使 reliability 匹配,只要 durability 不兼容,仍然不会传消息。deadline 与 lease duration 的规则类似:若订阅端请求的期限比发布端提供的更短,即更严格,就会不兼容;系统默认值对某些组合也可能造成限制。liveliness 方面,automatic 与 manual by topic 之间存在单向不兼容:manual 发布端可以满足 automatic 订阅端,但 automatic 发布端不能满足请求 manual 的订阅端,见 上述 QoS 兼容性章节。把这三项和 lifespan 等配置一起看,才能解释为什么“单独改一处设置”常常不是完整答案。
对已经运行的系统,最安全的方式是先观察、记录,再决定是否调整配置。Jazzy 的 topics 教程提供了非侵入式内省手段:ros2 topic list -t 可确认话题及消息类型;ros2 topic info <topic_name> --verbose 能显示该话题上每个发布端与订阅端的节点名、命名空间、端点类型,以及各自的 QoS profile,包括 reliability、history depth、durability、deadline、liveliness 等项目,见 ROS 2 Jazzy topics 官方教程。建议做法是:把发布端和订阅端的 verbose 输出分别复制到同一份记录文件中,逐项对照可靠性、持久性、截止时间与活跃性,而不是凭印象判断“应该能通”。
下面给出一个假设示例,仅用于说明记录方法,不代表任何真实测试结果。假设某 Jazzy 系统中有一个图像话题,发布端使用 sensor data 风格配置:reliability 为 best effort、durability 为 volatile、depth 较小;而订阅端沿用默认 publisher/subscription 风格,请求 reliable 与 volatile。此时用 ros2 topic info --verbose 观察,若发布端显示 Reliability: BEST_EFFORT,订阅端显示 Reliability: RELIABLE,根据官方兼容性表,这一对在 reliability 上就不兼容。记录表中应写清:观察时间、话题名、两端节点名、reliability 取值、durability 取值、是否实际收到消息;之后再判断是调整订阅端请求等级,还是让两端统一到同一 profile。这里不把“把可靠性别改成 reliable”说成万能修复,因为 deadline 或 liveliness 也可能同时不匹配。
基于上述文档信息,推荐采用以下非侵入式记录实践,而不是直接修改现场节点。第一,先运行 ros2 topic list -t,确认话题存在且类型一致。第二,对每个相关话题执行 ros2 topic info --verbose,分别保存发布端与订阅端的 QoS profile 文本。第三,建立一张对照表,逐项比较:reliability(best effort / reliable)、durability(volatile / transient local)、history 与 depth、deadline(finite / infinite)、liveliness(automatic / manual by topic)及 lease duration。第四,若发现某一策略不兼容,只记录该差异,不声称单一改动必然恢复通信;因为连接要求所有影响兼容性的策略同时兼容。第五,可结合 QoS events 的观察思路,在文档中留意 offered/requested incompatible QoS 这类事件名称,将其纳入长期记录模板。真正的目标是让“为什么没收到”从猜测变成可追溯的记录。