返回专辑
·Johan·3 分钟阅读

Diagnostics Aggregator:把健康状态收成可行动的树

稳定命名、诚实级别、超时与降级联动;诊断是运维接口。

Diagnostics Aggregator:把健康状态收成可行动的树

1. 灯黄了,但不知道哪一枝

复杂机器人几十个节点各自 RCLCPP_ERROR,展厅运维只看 LED 黄了,打开终端无从下口。diagnostic_msgs/DiagnosticArray + diagnostic_aggregator 把硬件、驱动、算法收成一棵树:谁 stale、谁 ERROR、父子依赖在哪。这是运维接口,不是日志的副产品;Nav2、安全层、UI 应订阅聚合结果做降级,而不是只给人看绿点。没有树,就只能「重启整机碰运气」。

2. 节点侧:诚实发布

每个关键子系统定期发布 DiagnosticArray(通常 1 Hz):

cpp
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 收成树:

yaml
# 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.0

timeout 必须配置。节点死后若仍显示上次 OK,比没有诊断更危险——运维以为传感器在线。超时后应变 STALE,上层触发降级。GenericAnalyzer 的 contains 是子串匹配,命名要有前缀约定,避免 nav 同时匹配 navigationnav_lidar 造成误分组。

bash
ros2 run diagnostic_aggregator aggregator_node --ros-args \
  --params-file aggregator.yaml
ros2 topic echo /diagnostics_agg --once

4. 与 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 诊断名表达。

相关

也可以看看

← 全部文章

johan's blog