ESC
输入关键词搜索文章标题和内容

ROS诊断系统全链路:从节点状态到可视化监控

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

ROS诊断系统全链路:从节点状态到可视化监控

导读:ROS诊断系统解决一个核心问题——机器人有几十个节点,哪个在报错?在哪里报错?本文用流程图拆解诊断数据的产生、聚合、可视化全链路,基于 ROSCon 2024 最新分享和 ros/diagnostics v4.0 源码。


一、系统架构全景

ROS诊断系统围绕 /diagnostics Topic 构建,数据从各节点出发,经由 aggregator 聚合,最终由 rqt_robot_monitor 可视化呈现。

flowchart TB subgraph 节点层["🔵 各ROS节点"] Lidar["lidar_driver<br/>激光雷达驱动"] Camera["camera_driver<br/>相机驱动"] Motor["motor_controller<br/>电机控制器"] Nav["nav2_node<br/>导航节点"] end subgraph 采集层["🟡 diagnostic_updater"] direction TB UP["Updater<br/>定时采集+发布"] STATUS["DiagnosticStatusWrapper<br/>OK=0 WARN=1 ERROR=2 STALE=3"] PUB["DiagnosedPublisher<br/>监控Topic频率"] end subgraph 通道["🟡 /diagnostics Topic"] RAW["DiagnosticArray<br/>原始诊断数据"] end subgraph 聚合层["🟢 diagnostic_aggregator"] direction TB AGG["Aggregator<br/>按规则分组"] RULE["YAML配置规则<br/>startswith/path/type"] ANALYZER["GenericAnalyzer<br/>插件化分析器"] end subgraph 可视化["🟢 rqt_robot_monitor"] TREE["树状视图<br/>按设备/功能分组"] PANEL["状态面板<br/>颜色区分OK/WARN/ERROR"] end Lidar -->|"diagnostic_updater"| UP Camera -->|"diagnostic_updater"| UP Motor -->|"diagnostic_updater"| UP Nav -->|"diagnostic_updater"| UP UP --> STATUS STATUS --> PUB PUB --> RAW RAW --> AGG RULE --> ANALYZER ANALYZER --> AGG AGG --> TREE AGG --> PANEL style 节点层 fill:#E3F2FD,stroke:#1976D2 style UP fill:#FFF8E1,stroke:#F57C00 style STATUS fill:#FFF8E1,stroke:#F57C00 style RAW fill:#FFF8E1,stroke:#F57C00 style AGG fill:#E8F5E9,stroke:#388E3C style TREE fill:#E8F5E9,stroke:#388E3C

二、诊断消息结构

2.1 三层消息模型

诊断数据有三层嵌套结构,从外到内:DiagnosticArray → DiagnosticStatus → KeyValue。

flowchart TB DA["DiagnosticArray<br/>外层容器<br/>Header + Status数组"] DS["DiagnosticStatus<br/>单个设备状态<br/>level / name / message<br/>hardware_id / values"] KV["KeyValue<br/>键值对<br/>key + value<br/>用于承载具体指标"] DA -->|"status[]"| DS DS -->|"values[]"| KV style DA fill:#E3F2FD,stroke:#1976D2 style DS fill:#FFF8E1,stroke:#F57C00 style KV fill:#E8F5E9,stroke:#388E3C

2.2 四级状态定义

状态值 含义 颜色 说明
OK=0 正常 绿色 设备运行正常,无需干预
WARN=1 警告 黄色 状态异常但可继续运行
ERROR=2 错误 红色 必须立即处理,否则功能丧失
STALE=3 数据过期 灰色 数据超时未更新,可能已断开

三、diagnostic_updater:诊断数据如何产生

3.1 Updater 工作流程

Updater 类负责定期调用用户注册的诊断函数,将诊断结果打包成 DiagnosticStatusWrapper 并发布到 /diagnostics。

flowchart TB START(["节点启动"]) --> INIT["创建 Updater 实例<br/>设置 period=1.0s"] INIT --> REG["注册诊断函数<br/>add / update 方法<br/>每个设备一条"] REG --> LOOP["进入主循环<br/>每 period 秒执行一次"] LOOP --> CALL["调用各注册函数<br/>采集硬件/软件状态"] CALL --> WRAP["DiagnosticStatusWrapper<br/>设置 level + name + message"] WRAP --> ADD["add 附加键值<br/>add Motor RPM: 1500<br/>add Temperature: 65"] ADD --> PUB["发布到 /diagnostics<br/>DiagnosticArray 消息"] PUB --> LOOP style INIT fill:#E3F2FD,stroke:#1976D2 style REG fill:#E3F2FD,stroke:#1976D2 style CALL fill:#FFF8E1,stroke:#F57C00 style WRAP fill:#FFF8E1,stroke:#F57C00 style ADD fill:#FFF8E1,stroke:#F57C00 style PUB fill:#E8F5E9,stroke:#388E3C

3.2 三种典型诊断场景

flowchart TB subgraph 场景1["场景一:Topic频率监控"] DP["DiagnosedPublisher<br/>包装任意Publisher<br/>自动监控发布频率"] DP --> OK1["频率正常<br/>OK 状态"] DP --> WARN1["频率偏低<br/>WARN 状态"] end subgraph 场景2["场景二:数值阈值监控"] FN["自定义函数<br/>读取传感器数值"] FN --> CHECK["阈值判断<br/>temp > 80?"] CHECK --> OK2["温度正常<br/>OK 状态"] CHECK --> ERROR2["温度超标<br/>ERROR 状态<br/>Motor too hot"] end subgraph 场景3["场景三:设备连接状态"] DEV["设备驱动节点<br/>检测硬件连接"] DEV --> CONN["连接正常?"] CONN -->|"是"| OK3["Device open<br/>OK 状态"] CONN -->|"否"| ERROR3["Device closed<br/>ERROR 状态"] end style DP fill:#E3F2FD,stroke:#1976D2 style FN fill:#E3F2FD,stroke:#1976D2 style DEV fill:#E3F2FD,stroke:#1976D2 style OK1 fill:#E8F5E9,stroke:#388E3C style WARN1 fill:#FFF8E1,stroke:#F57C00 style OK2 fill:#E8F5E9,stroke:#388E3C style ERROR2 fill:#FFEBEE,stroke:#D32F2F style OK3 fill:#E8F5E9,stroke:#388E3C style ERROR3 fill:#FFEBEE,stroke:#D32F2F

3.3 diagnostic_common_diagnostics:开箱即用的诊断节点

系统自带一批预制诊断节点,覆盖 Linux 系统层面的常见监控指标:

来自 linuxros.cn · linuxROS
flowchart TB subgraph 预制诊断节点["🟡 diagnostic_common_diagnostics"] CPU["cpu_monitor.py<br/>监控CPU使用率<br/>>90% WARN<br/>>95% ERROR"] NTP["ntp_monitor.py<br/>检查NTP时间同步<br/>偏移 >500ms WARN<br/>>5s ERROR"] HD["hd_monitor.py<br/>监控磁盘空间<br/><5% free WARN<br/><1% free ERROR"] MEM["memory_monitor.py<br/>监控内存使用率"] TEMP["temperature_monitor.py<br/>监控CPU/硬件温度"] end subgraph 输出["统一输出 /diagnostics"] OUT["DiagnosticArray<br/>各节点独立发布"] end CPU --> OUT NTP --> OUT HD --> OUT MEM --> OUT TEMP --> OUT style CPU fill:#FFF8E1,stroke:#F57C00 style NTP fill:#FFF8E1,stroke:#F57C00 style HD fill:#FFF8E1,stroke:#F57C00 style MEM fill:#FFF8E1,stroke:#F57C00 style TEMP fill:#FFF8E1,stroke:#F57C00

四、diagnostic_aggregator:诊断数据如何聚合

4.1 为什么需要聚合?

一个机器人可能有上百个诊断项,全部平铺无法定位问题。aggregator 将其按规则分组合并,形成层次化的诊断视图。

flowchart TB RAW["/diagnostics<br/>原始数据<br/>上百条DiagnosticStatus"] AGG["Aggregator<br/>接收原始数据"] CFG["YAML配置规则<br/>定义分组逻辑"] ANAL["Analyzer插件<br/>GenericAnalyzer<br/>startswith匹配<br/>path重命名<br/>type分类"] OUT["/diagnostics_agg<br/>/diagnostics_toplevel_state<br/>聚合后输出"] RAW --> AGG CFG --> ANAL ANAL --> AGG AGG --> OUT style RAW fill:#FFEBEE,stroke:#D32F2F style AGG fill:#FFF8E1,stroke:#F57C00 style CFG fill:#E3F2FD,stroke:#1976D2 style ANAL fill:#E3F2FD,stroke:#1976D2 style OUT fill:#E8F5E9,stroke:#388E3C

4.2 YAML 配置示例

analyzers:
  path: Aggregation          # 顶层分组名称
  arms:                      # 分组名:Arms
    type: diagnostic_aggregator/GenericAnalyzer
    path: Arms              # 聚合后显示的路径
    startswith: ['/arms']   # 原始路径以 /arms 开头
  sensors:
    type: diagnostic_aggregator/GenericAnalyzer
    path: Sensors
    startswith: ['/camera', '/lidar']
  motors:
    type: diagnostic_aggregator/GenericAnalyzer
    path: Motors
    startswith: ['/motor']

4.3 聚合后的树状结构

flowchart TB TOP["/diagnostics_toplevel_state<br/>总体状态:OK/WARN/ERROR"] subgraph 聚合结果["聚合后诊断树"] AGG["/Aggregation"] ARM["/Aggregation/Arms"] SEN["/Aggregation/Sensors"] MOT["/Aggregation/Motors"] SYS["/Aggregation/System"] end subgraph 原始叶子["原始叶子节点"] L1["/arms/left_motor<br/>WARN"] L2["/arms/right_motor<br/>OK"] L3["/sensors/left_cam<br/>ERROR"] L4["/sensors/lidar<br/>OK"] L5["/motors/joint1<br/>OK"] L6["/motors/joint2<br/>WARN"] end TOP --> AGG AGG --> ARM AGG --> SEN AGG --> MOT AGG --> SYS ARM --> L1 ARM --> L2 SEN --> L3 SEN --> L4 MOT --> L5 MOT --> L6 style TOP fill:#F3E5F5,stroke:#7B1FA2 style AGG fill:#FFF8E1,stroke:#F57C00 style L3 fill:#FFEBEE,stroke:#D32F2F style L1 fill:#FFF8E1,stroke:#F57C00 style L6 fill:#FFF8E1,stroke:#F57C00 style L2 fill:#E8F5E9,stroke:#388E3C style L4 fill:#E8F5E9,stroke:#388E3C style L5 fill:#E8F5E9,stroke:#388E3C

聚合逻辑:父节点状态 = 子节点最严重状态。任意一个叶子为 ERROR,父节点即为 ERROR。


五、rqt_robot_monitor:如何可视化

5.1 可视化界面布局

flowchart TB subgraph rqt_robot_monitor界面 DIRECT["Directory 面板<br/>树状展示诊断层次<br/>双击进入任意节点"] STATUS["Status 面板<br/>当前选中项详情<br/>hardware_id / values"] SUMMARY["Summary 面板<br/>ALL OK / WARN x2 / ERROR x1<br/>全局故障统计"] TIMELINE["Timeline 面板<br/>状态变化时间线<br/>辅助追溯故障时刻"] end DIRECT --> STATUS DIRECT --> SUMMARY STATUS --> TIMELINE style DIRECT fill:#E3F2FD,stroke:#1976D2 style STATUS fill:#FFF8E1,stroke:#F57C00 style SUMMARY fill:#F3E5F5,stroke:#7B1FA2 style TIMELINE fill:#E8F5E9,stroke:#388E3C

5.2 故障定位工作流

flowchart TB A(["打开 rqt_robot_monitor"]) --> B["看 Summary:ERROR x1"] B --> C["展开 Aggregation 树<br/>找到 /sensors/left_cam ERROR"] C --> D["双击进入 Status 面板<br/>查看 hardware_id + values"] D --> E{"错误类型?"} E -->|"温度过高"| F["去 hardware 层检查散热"] E -->|"连接断开"| G["检查 USB/CAN 线缆"] E -->|"频率异常"| H["检查 Topic 发布率<br/>dmesg 看驱动日志"] style A fill:#E3F2FD,stroke:#1976D2 style B fill:#FFF8E1,stroke:#F57C00 style C fill:#FFF8E1,stroke:#F57C00 style D fill:#FFF8E1,stroke:#F57C00 style E fill:#FFF8E1,stroke:#F57C00 style F fill:#FFEBEE,stroke:#D32F2F style G fill:#FFEBEE,stroke:#D32F2F style H fill:#FFEBEE,stroke:#D32F2F

六、代码示例

6.1 C++ 节点注册诊断

#include <diagnostic_updater/diagnostic_updater.hpp>

class MotorDriver {
    diagnostic_updater::Updater updater_;
    double rpm_, temp_;
public:
    MotorDriver() {
        updater_.setHardwareID("motor-controller-001");
        updater_.add("Motor Status", this, &MotorDriver::checkMotor);
    }

    void checkMotor(diagnostic_updater::DiagnosticStatusWrapper& stat) {
        stat.add("RPM", rpm_);
        stat.add("Temperature", temp_);

        if (temp_ > 80.0) {
            stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR,
                         "Motor overheated");
        } else if (temp_ > 60.0) {
            stat.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN,
                         "Motor temperature elevated");
        } else {
            stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Motor OK");
        }
    }
};

6.2 Python 节点注册诊断

import diagnostic_updater

class LaserDriver:
    def __init__(self):
        self.updater = diagnostic_updater.Updater()
        self.updater.setHardwareID("lidar-ls01b")
        self.updater.add("Laser Status", self.checkLaser)

    def checkLaser(self, stat):
        stat.add("Topic", "/scan")
        stat.add("Frame ID", "laser_link")
        if self.laser_ok:
            stat.summary(diagnostic_updater.DiagnosticStatus.OK, "Laser OK")
        else:
            stat.summary(diagnostic_updater.DiagnosticStatus.ERROR, "Laser error")
        return stat

七、全链路数据流总结

flowchart TB subgraph 节点层["节点层"] N1["激光雷达驱动<br/>diagnostic_updater"] N2["电机控制器<br/>diagnostic_updater"] N3["相机驱动<br/>diagnostic_updater"] N4["CPU/Memory<br/>diagnostic_common_diagnostics"] end subgraph 发布层["发布层 /diagnostics"] D1["DiagnosticArray<br/>原始状态数组<br/>level+name+message+values"] end subgraph 聚合层["聚合层"] A1["diagnostic_aggregator<br/>YAML规则配置<br/>startswith分组"] A2["输出:<br/>/diagnostics_agg<br/>/diagnostics_toplevel_state"] end subgraph 可视化层["可视化层"] V1["rqt_robot_monitor<br/>树状+状态面板"] V2["命令行:<br/>ros2 diagnostic utils<br/>--verb list/echo/csv"] end N1 --> D1 N2 --> D1 N3 --> D1 N4 --> D1 D1 --> A1 A1 --> A2 A2 --> V1 A2 --> V2 style N1 fill:#E3F2FD,stroke:#1976D2 style N2 fill:#E3F2FD,stroke:#1976D2 style N3 fill:#E3F2FD,stroke:#1976D2 style N4 fill:#E3F2FD,stroke:#1976D2 style D1 fill:#FFF8E1,stroke:#F57C00 style A1 fill:#FFF8E1,stroke:#F57C00 style A2 fill:#E8F5E9,stroke:#388E3C style V1 fill:#E8F5E9,stroke:#388E3C

八、开源库速查

仓库 包 语言 说明
ros/diagnostics diagnostic_updater C++/Python 诊断数据采集发布,v4.0.6
ros/diagnostics diagnostic_aggregator C++/Python YAML规则分组聚合
ros/diagnostics diagnostic_common_diagnostics Python CPU/NTP/磁盘/内存/温度监控
ros/diagnostics self_test C++ 节点自检框架
turtlebot4-user-manual rqt_robot_monitor Qt 可视化监控界面

九、总结

ROS诊断系统三句话:

  • 谁在报:各节点通过 diagnostic_updater 将状态(OK/WARN/ERROR)发布到 /diagnostics Topic,附带键值对描述具体指标
  • 怎么聚:diagnostic_aggregator 按 YAML 配置的 startswith 规则将上百条原始诊断归纳为树状结构,父节点状态等于子节点最严重状态
  • 怎么看:通过 rqt_robot_monitor 图形界面或 ros2 diagnostic 命令行工具定位具体故障节点

诊断系统是机器人的"控制面板"——一个面板看全貌,快速定位哪个节点、哪类硬件出了问题。ROSCon 2024 上官方表示后续将加入 LTL(线性时序逻辑)监控能力,支持对系统状态属性做形式化验证。


看完不点赞?今晚代码必跑飞。看完不转发?Bug跟你到白头。推荐给朋友,一起被坑到永久。评论区见,谁是今天的最强显眼包?👻


参考来源

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页