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 | 用途 |
|---|---|
GenericAnalyzer | 99% はこれ。startswith / contains / expected で分類 |
AnalyzerGroup | Analyzer 自体をネストしたい時 |
| 自作 | 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 test | self_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)関連
- runtime-verification-layers — 診断は runtime 層の 1 つ、CI / self-test / rosbag replay と対比
- image-topic-cost — image topic を diagnostic 対象にする時の Hz チェック注意
- agent-ros2-debug-loop — 診断を使ったデバッグ loop
出典
- ROS 2 wiki: diagnostic_updater
diagnostic_aggregator/GenericAnalyzer- diagnostic_msgs/DiagnosticStatus.msg
最終更新: