notes
Ros

ROS 2 diagnostic エコシステム

DiagnosticStatus と Updater / Aggregator の関係、標準運用パターン、config yaml の書き方、よくある anti-pattern。

要点: 各ノードが diagnostic_updater で /diagnostics に 1 Hz publish、diagnostic_aggregator が name パターンで groupして roll-up、rqt_robot_monitor or foxglove で階層表示。Foxglove/rviz 側はデータを見るだけで、判定ロジックは常に aggregator config yaml に集約する。

30 秒サマリ#

  • 単位: diagnostic_msgs/DiagnosticStatus (level, name, message, hardware_id, values[])
  • level: 0=OK / 1=WARN / 2=ERROR / 3=STALE (stale はメッセージが古い)
  • 配布: node は /diagnostics (raw)、aggregator が /diagnostics_agg (階層) と /diagnostics_toplevel_state (ロールアップ 1 スカラ) を追加で publish
  • 名前空間: name フィールドを path っぽく (motors/base_yaw/temperature)。aggregator がここで分類する

標準パッケージ#

pkg役割
diagnostic_msgsメッセージ型 (DiagnosticStatus / DiagnosticArray / KeyValue)
diagnostic_updaterノード内から publish する側。C++ / Python 両対応
diagnostic_aggregator全ノードの status を group して roll-up 判定
rqt_robot_monitor階層 GUI (ツリー表示、赤 / 黄 / 緑)
self_testノード側で一発 self-test action を提供 (起動時チェック等)

Publish 側 (diagnostic_updater)#

#include <diagnostic_updater/diagnostic_updater.hpp>
#include <diagnostic_updater/publisher.hpp>

diagnostic_updater::Updater upd(node);
upd.setHardwareID("robot_arm_serial_042");

// 関数を task として登録: シグネチャ (DiagnosticStatusWrapper &) -> void
upd.add("motor.temperature", [this](auto & stat) {
  const double t = read_temp();
  if (t > 80.0) stat.summary(2, "over 80C");         // ERROR
  else if (t > 60.0) stat.summary(1, "warm");        // WARN
  else stat.summary(0, "ok");
  stat.add("temp_C", t);                              // KeyValue
});

// 便利ラッパ: publisher の Hz チェック
diagnostic_updater::FrequencyStatus freq({
  .min_freq = &min_hz, .max_freq = &max_hz, .tolerance = 0.1});
upd.add("cmd_pub_rate", freq);

// timer で 1 Hz 呼ぶ (updater は内部で 1 Hz 想定、rate をずらせるが aggregator が困る)
timer_ = create_wall_timer(1s, [&]{ upd.force_update(); });

Python 側: diagnostic_updater.Updater(node) 同じ API。

Aggregator 側#

config yaml (analyzers.yaml):

/**:
  ros__parameters:
    path: /aggregator
    pub_rate: 1.0
    analyzers:
      motors:
        type: diagnostic_aggregator/GenericAnalyzer
        path: Motors
        startswith: [motor_]              # /diagnostics の name がこれで始まるものを吸う
        remove_prefix: motor_             # 表示時に prefix を削る
        timeout: 5.0                      # 5s 音信不通で STALE
      sensors:
        type: diagnostic_aggregator/GenericAnalyzer
        path: Sensors
        contains: [camera, imu]

launch:

<node pkg="diagnostic_aggregator" exec="aggregator_node">
  <param from="$(find-pkg-share my_pkg)/config/analyzers.yaml"/>
</node>

Analyzer 種類:

type用途
GenericAnalyzer99% はこれ。startswith / contains / expected で分類
AnalyzerGroupAnalyzer 自体をネストしたい時
自作pluginlib で拡張、複雑な roll-up (投票、優先度)

roll-up 規則: グループ内の最悪の level がグループの level になる (ERROR > WARN > OK)。1 個でも ERROR があれば group ERROR。

可視化#

  • rqt_robot_monitor: 定番 GUI ツリー。ダブルクリックで KeyValue 詳細
  • foxglove: Diagnostic Summary パネル、Diagnostic Detail パネル。live + rosbag 両方
  • CLI:
    ros2 topic echo /diagnostics_toplevel_state     # roll-up (1 scalar)
    ros2 topic echo /diagnostics_agg --field status # 階層版

便利パターン#

1. FrequencyStatus (トピック Hz 監視)#

publisher が想定 Hz で回っているか自動判定。誰でも欲しくなるので diagnostic_updater に built-in。

2. TopicDiagnostic (Hz + timestamp 両方)#

diagnostic_updater::TopicDiagnostic td("scan", upd, freq, stamp_param);
scan_pub_->publish(msg); td.tick(msg.header.stamp);

3. HeaderlessTopicDiagnostic (stamp なしトピック)#

Hz だけで判定。

4. 一時的 downgrade (dead reckoning 中は OK 扱い)#

if (dead_reckoning_mode_) stat.summary(0, "DR (fusion suppressed)");

Anti-patterns#

  • 100 Hz publish: aggregator が詰まる。必ず 1 Hz、bursty な出来事は KeyValue の event_count で伝える
  • hardware_id が全 node 同じ: /diagnostics_agg の集計単位が壊れる。node ごとに違う値を付ける
  • level=OK で意味あるメッセージ: rqt_robot_monitor は OK 行を色付けしないので、有事以外のログはここに出さない
  • name にスペース or 全角: aggregator の startswith 判定が読みにくくなる。ASCII + snake_case で
  • 全部 ERROR に振る: WARN が使われないとオペレータが慣れて無視する。警告レベルの gradient を作る (60°C=WARN, 80°C=ERROR 等)
  • stale を判定しない: aggregator の timeout を切ると、node が落ちても roll-up が OK のまま。必ず timeout を設定

使い分け (代替 / 補完)#

目的選択
標準的な runtime 健康監視diagnostic_updater + aggregator
ヒステリシス・時系列相関自作 aggregator plugin
因果関係付きイベント (findings)別軸の watchdog (rosout filter or 自作) を追加、diagnostic とは分離
起動時 self testself_test action
CI / build-time 検査ここではなく別レイヤ (linters、静的解析、fibril_check みたいなもの)

参考データフロー図#

node A ─┐
node B ─┼─▶ /diagnostics (raw, 1 Hz)
node C ─┘
              │
              ▼
       aggregator (analyzers.yaml で分類)
              │
              ├─▶ /diagnostics_agg           (階層 tree)
              └─▶ /diagnostics_toplevel_state (roll-up, 1 スカラ)
                       │
                       ├─▶ rqt_robot_monitor / foxglove
                       └─▶ 上位判定 (safety supervisor / e-stop)

関連#

出典#

最終更新:

On this page