
1. QoS 是分布式合约,不是预设别名
发布者 Reliable + Volatile + KeepLast(10),订阅者 Best Effort + Transient Local + KeepLast(1)——图里「有话题、无回调」,ros2 topic hz 永远 zero,日志无弹窗。DDS 兼容规则:reliability 必须一致;durability 订阅不能低于发布;部分 history 组合非法。不匹配常常是静默的,这是联调地狱之源。
只记 SensorDataQoS 与 SystemDefaultsQoS 两套别名不够;团队要为每类数据写剖面行,MR 填行号,否则 QoS 会按话题数平方腐烂。
2. 五个旋钮各自解决什么
- Reliability:传感流常用
Best Effort(丢旧帧换低延迟);命令/地图用Reliable。 - Durability:
Transient Local让晚订阅者拿到发布者最近样本(静态 TF、地图、参数事件);Volatile只收订阅后的新样本。 - History + depth:
KeepLast(N)控制缓存深度;传感几乎总是小 N,大 N 常意味着延迟堆积而非更安全。 - Deadline:周期内无新样本则触发回调——把「话题还在、数据已死」变成可观测事件,比人工盯 hz 适合上车。
- Lifespan:样本超过 TTL 不再投递,防止把 stale 数据当新数据。
rclcpp::SensorDataQoS() 是便捷入口,不等于团队剖面——它仍是 BE + Volatile + KeepLast(5) 的打包,改 depth 仍要显式链式调用。自定义剖面用具名工厂函数,禁止每个节点 copy-paste 五行。
rclcpp::QoS qos(1);
qos.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
qos.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
qos.deadline(std::chrono::milliseconds(100));
pub_ = create_publisher<sensor_msgs::msg::LaserScan>("scan", qos);3. 读兼容矩阵,不要背口诀
验收时用 verbose 看双方合约:
ros2 topic info /scan -v输出里 Publisher count、Subscription count 同为 1 但 hz 为零,几乎总是 QoS 或类型不匹配。故意改订阅为 Reliable 复现一次静默失败,全组见过就不至于每次从零猜。
常见合法组合:激光 BE + Volatile + KeepLast(5);地图 Reliable + Transient Local + KeepLast(1);里程计 Reliable + Volatile + KeepLast(10) + deadline 500ms。
4. 剖面表:教程会被忘,门禁不会
团队维护 qos_profiles.yaml 或表格,列至少包括:
| 行号 | 数据类 | reliability | durability | history | depth | deadline | 示例话题 |
|---|---|---|---|---|---|---|---|
| P1 | 激光流 | BE | Volatile | KeepLast | 5 | 100ms | /scan |
| P2 | 静态 TF | Reliable | Transient Local | KeepLast | 1 | — | /tf_static |
| P3 | 占用栅格 | Reliable | Transient Local | KeepLast | 1 | — | /map |
MR 模板:「本次 QoS 变更对应 P?_」。没有行号的不合并。代码里引用具名常量,禁止 magic number 散落:
inline rclcpp::QoS kProfileLidar() { /* P1 */ }5. 录包、桥接与降级
ros2 bag record 默认 QoS 可能与 live 图不同——回放「有影无回调」时先 ros2 bag info 看存储 QoS。ROS 1 桥接、zenoh 桥 frequently 只支持 reliability 子集,迁移时按话题清单逐条验证,不能假设「能 echo 就能算法」。
上车允许对非安全话题降级(BE、减 depth),但必须在剖面表标注「录包模式」行,与 live 行成对,避免调试与现场行为分裂。回放时用 --qos-profile-overrides-path 显式对齐 live 剖面,而不是依赖 bag 内嵌的旧合约。
6. Deadline 与诊断联动
订阅端注册 deadline 回调:
sub_->set_on_requested_deadline_callback(
[](rclcpp::QOSRequestedDeadlineInfo & info) {
RCLCPP_WARN(rclcpp::get_logger("qos"), "missed deadline, total %zu",
info.total_count);
});配合 diagnostic_updater 上报,HMI 可显示「激光超时」而非神秘停走。注意:deadline 过紧在负载高时误报,应基于 P99 间隔 + 余量标定。发布端也可设 deadline:双方都有 periodic 合约时,违反能定位是发布慢还是订阅处理慢。
7. 迁移与验收
现有仓库按话题清单填剖面行号,一次改一类(先所有激光,再所有地图)。完成标志:任意故障单可贴出匹配对的 Offered/Requested;关键 topic 在负载测试下 deadline 零误报或误报可解释。
新节点 default 构造函数里不要写私有 QoS——从剖面表 #include 工厂。Code review 看到裸 create_subscription(..., 10) 即问对应哪一行。老节点迁移时先加 -v 快照进 wiki,再改代码,避免「改完不知道以前长什么样」。
8. 案例:Transient Local 救晚订阅
Nav2 起得比 map_server 晚,没有 TL 时 /map 永远空。发布改 Transient Local + KeepLast(1) 后晚订阅立即拿到最后一帧。但若激光也误设 TL,带宽与内存徒增——按数据类选,不是越持久越好。
先分类,再选预设;用 deadline 管理沉默;禁止私有 QoS 传说。兼容矩阵读一次,胜过十篇教程背诵。
相关
也可以看看
- ·15 分钟阅读
ContentFilteredTopic:把过滤下推到中间件还是应用层
对比订阅端丢弃、应用层条件判断与 DDS 内容过滤的 CPU/带宽边界,说明表达式能力与发现时序限制;用高频率噪声话题与延迟尖峰验收过滤位置选择。
- ·9 分钟阅读
ROS 2 大消息内存路径:intra-process、loaned message 与 shared memory 不是一回事
以 4K 图像和点云管线为例,逐段核对 rclcpp、RMW 与进程边界上的所有权、分配和真实拷贝。
- ·4 分钟阅读
传感器 QoS:最新帧语义,不是默认可靠
Best Effort + 小 depth 服务实时流;用 topic info -v 证明无数据可纯由 QoS 造成。
johan's blog