Diagnostics Aggregator:把健康状态收成可行动的树
稳定命名、诚实级别、超时与降级联动;诊断是运维接口。

1. 灯黄了,但不知道哪一枝
复杂机器人几十个节点各自 RCLCPP_ERROR,展厅运维只看 LED 黄了,打开终端无从下口。diagnostic_msgs/DiagnosticArray + diagnostic_aggregator 把硬件、驱动、算法收成一棵树:谁 stale、谁 ERROR、父子依赖在哪。这是运维接口,不是日志的副产品;Nav2、安全层、UI 应订阅聚合结果做降级,而不是只给人看绿点。没有树,就只能「重启整机碰运气」。
2. 节点侧:诚实发布
每个关键子系统定期发布 DiagnosticArray(通常 1 Hz):
diagnostic_updater::Updater updater(this, 1.0);
updater.setHardwareID("lidar_vlp16_sn12345");
updater.add("lidar_driver", this, &LidarNode::produce_diagnostics);
void produce_diagnostics(
diagnostic_updater::DiagnosticStatusWrapper & stat)
{
if (last_scan_age_ms_ > 500) {
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR,
"No scan for 500ms");
stat.add("last_scan_age_ms", last_scan_age_ms_);
return;
}
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Scan OK");
stat.add("rate_hz", measured_hz_);
}名字稳定(lidar/driver 不要每周改);级别诚实——WARN 不是「先 OK 再慢慢查」;key-value 带可行动数据(超时 ms、USB 路径、标定过期日)。
3. Aggregator:分组与超时
diagnostic_aggregator 用 analyzer 配置把 flat /diagnostics 收成树:
# aggregator.yaml
analyzers:
lidar:
type: diagnostic_aggregator/GenericAnalyzer
path: Lidar
contains: 'lidar'
timeout: 3.0
navigation:
type: diagnostic_aggregator/GenericAnalyzer
path: Navigation
contains: 'nav'
timeout: 5.0timeout 必须配置。节点死后若仍显示上次 OK,比没有诊断更危险——运维以为传感器在线。超时后应变 STALE,上层触发降级。GenericAnalyzer 的 contains 是子串匹配,命名要有前缀约定,避免 nav 同时匹配 navigation 与 nav_lidar 造成误分组。
ros2 run diagnostic_aggregator aggregator_node --ros-args \
--params-file aggregator.yaml
ros2 topic echo /diagnostics_agg --once4. 与 Nav2、安全层联动
聚合结果 /diagnostics_agg(或等价)应被:
- 安全监控:ERROR 时切断 cmd_vel 或缓停
- Nav2:定位/感知 ERROR 时拒绝新 goal 或触发 recovery
- 运维 UI:展示树而非 flat 列表
诊断文案要对一线可执行:「检查网口 eth1 链路」优于「general error」。订阅 /diagnostics_agg 的监控节点也应发布自身诊断,否则 aggregator 进程 stale 时无人知晓。分析器只做分组与超时,业务降级逻辑放在独立 safety 节点,避免 YAML 里藏 if-else。
5. 故障字典
架构文档维护:诊断名 → 检查步骤(供电、网线、进程、QoS、权限)。换 robot 型号换字典,不是换「老师傅经验」。ERROR 的 key-value 尽量与字典键对齐,便于自动工单。WARN 与 ERROR 的边界要在设计期定:标定过期 7 天是 WARN 还是 ERROR,不能留给现场第一次遇到再猜。
6. 与 rqt_robot_monitor
开发期用 rqt_robot_monitor 看树;上车后看聚合 topic 或自有 UI。禁止新增「只打日志、不发诊断」的关键故障路径——旧节点逐步补 DiagnosticArray,迁移完成前在手册标注缺口。
聚合树层级不宜超过四层:Root → 子系统 → 设备 → 细项。过深时运维展开成本高于收益,ERROR 应能在第二层定位到子系统。
7. 验收
- 拔雷达电源:对应分支 3 s 内 ERROR,导航停或拒绝新任务
- kill 诊断发布者:变 STALE,非假 OK
- 人为注入 WARN:上层行为与手册一致(提示但不停 vs 停)
- aggregator.yaml 进 git,与型号 release 绑定
拔线测试应写入 release 门禁:自动化脚本 kill 驱动进程,断言 /diagnostics_agg 在 timeout 内 STALE/ERROR,避免只靠人工展厅演练。
8. 案例:无 timeout 的「永久 OK」
某驱动 crash 后不再发 DiagnosticArray,aggregator 无 timeout,树仍绿,车带 stale 定位跑。加 3 s timeout 后变 STALE,安全层切断 cmd_vel——事故变为可预期降级。诊断系统的谎言比无诊断更致命。
9. 默认策略
诊断是产品接口;超时必配;诚实级别;聚合结果驱动降级。当树能回答「哪一枝坏了」,再谈 heavier 可观测性栈;否则只是在日志海里溺水。
10. 频率与风暴
DiagnosticArray 发布过频(如 50 Hz)在弱 CPU 上占带宽;过低(0.2 Hz)则故障发现滞后。关键路径 1 Hz 是常见折中。避免在 ERROR 分支里每周期刷不同文案——aggregator 与 UI 难以 diff。批量 WARN 应合并为一条带计数的 key(drop_count_1min),便于趋势判断。
11. 与 systemd、容器
容器内 PID1 不是 ROS 节点时,进程 crash 不等于节点 orderly shutdown,更依赖 aggregator timeout。systemd 重启策略与诊断 STALE 应一致:若 systemd 正在重启,短暂 STALE 不应触发硬急停,或应区分「预期重启」与「异常死亡」——用 lifecycle 或显式 maintenance 诊断名表达。
相关
也可以看看
- ·15 分钟阅读
ContentFilteredTopic:把过滤下推到中间件还是应用层
对比订阅端丢弃、应用层条件判断与 DDS 内容过滤的 CPU/带宽边界,说明表达式能力与发现时序限制;用高频率噪声话题与延迟尖峰验收过滤位置选择。
- ·16 分钟阅读
ROS 2 Action 取消语义:收到 cancel 不等于执行器已经停下
梳理 goal handle 状态机、取消回调与硬件停止之间的异步边界;用可中断执行循环、截止时间和终态发布构建契约并以注入验收。
- ·9 分钟阅读
ROS 2 大消息内存路径:intra-process、loaned message 与 shared memory 不是一回事
以 4K 图像和点云管线为例,逐段核对 rclcpp、RMW 与进程边界上的所有权、分配和真实拷贝。
johan's blog