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

定时器与 Watchdog:频率声明不等于截止时间

回调预算 + 超时安全停;跨节点心跳对齐 QoS 与时钟;看门狗动作表驱动。

定时器与 Watchdog:频率声明不等于截止时间

1. 写了 100 Hz 不是硬实时证书

create_wall_timer(10ms, ...) 只保证周期性提交回调请求,不保证回调在 10 ms 内完成,更不保证下一周期准点。负载高、同 MutuallyExclusive 组有长回调、非 RT 内核调度抖动——相位会漂。现场「控制环 100 Hz」若只靠日志自嗨,第一次真机负载 spike 就会 overshoot 或丢步。声明频率是意图;预算是契约:单次回调 CPU 上限 + 超时后果。

2. 定时器类型与时钟域

  • create_wall_timer:墙钟,仿真中与 /clock 无关——仿真应用 create_timer + node->get_clock()
  • create_timer:跟 ROS clock 走,use_sim_time 时自动随仿真暂停。

混用墙钟定时器与 sim clock 订阅,是「仿真里控制环狂飙、传感器不动」的经典原因。录包回放时确认 use_sim_time 全图一致:

bash
ros2 param get /my_controller use_sim_time

Gazebo 暂停时墙钟 watchdog 仍走——真机维护模式若只停仿真不 disarm watchdog,会误触发安全停。维护流程里写清:先 lifecycle deactivate,再停仿真。

3. Watchdog:监控「上次成功周期」

模式:记录每次控制回调入口时间 t_last;独立轻量定时器或线程检查 now - t_last > T_timeout → 触发安全停。被监控回调可以重;监控路径必须更轻——watchdog 里不要同步写盘或调 heavy service。

cpp
void control_loop() {
  last_ok_ = now();
  // ... 控制 ...
}
void watchdog_loop() {
  if ((now() - last_ok_) > 200ms) {
    publish_zero_cmd();
    RCLCPP_ERROR(get_logger(), "control watchdog trip");
  }
}

跨节点 watchdog 用心跳 topic:std_msgs/msg/Header 或专用 Heartbeat,订阅端同样计时。心跳 QoS 用 Reliable + KeepLast(1) + deadline;BE 心跳可能被静默丢弃造成假死。心跳 payload 带序号或单调时间戳,可检测「旧包重放」造成的假活——订阅端拒绝 stamp 不递增的样本。

4. 安全停动作表

触发后做什么必须表驱动,与超时阈值同页维护:

事件动作通知
控制回调超时cmd_vel 零 + 断使能diagnostics ERROR
心跳丢失同上 + 通知 fleet/fleet/alarm
仅传感器超时降级定位,限速WARN

无表时各节点各停各的——有的 zero cmd,有的还留着旧 cmd,姿态更危险。安全评审检查:凡能发 cmd 的节点,必须有 watchdog 或上游心跳覆盖

5. 与 Executor 的分诊

Watchdog trip 后要能区分:回调执行过长(tracing 见单周期 spike)、心跳丢失(topic hz 零)、时钟域错(sim time 未同步)。只 rclcpp::shutdown 重启而不分诊,偶发变频繁重启,现场失去信任。

控制定时器放独立 MutuallyExclusive callback group,与图像/点云解码隔离——这在合进程 composition 里比独立进程更关键。rclcpp::spin_some 手动驱动时也要喂 watchdog 检查路径;否则「主环在 spin_some、watchdog 从未执行」会造成假 trip 或永不 trip。

6. 直方图与负载对比

diagnostic_updater 或自研 metrics 记录周期间隔 P50/P99,空载与满负载各采 5 分钟。验收标准示例:100 Hz 环 P99 间隔 < 12 ms;超 15 ms 占比 < 0.1%。没有直方图,「感觉还行」在上车无意义。

cpp
const double dt = (now() - last_tick_).seconds();
hist_.add_sample(dt);
last_tick_ = now();

7. 与 lifecycle 的交点

inactiveunconfigured 时 watchdog 应 disarm 或忽略 stale 心跳,避免维护模式误触发 fleet 告警。activate 后 2 s 内允许 grace period,再进入严格超时——写进状态机,不是 launch 里 sleep 凑数。

8. 验收

  • 故意在控制回调里 sleep(500ms):watchdog 在 T_timeout 内 zero cmd。
  • 停发心跳 publisher:下游在合约时间内安全停。
  • 仿真 use_sim_time pause:墙钟 watchdog 不误触发(应使用 sim clock 定时器)。
  • 负载测试:P99 间隔符合预算;trip 有 audit log。

9. 案例:看门狗比主循环还重

某包 watchdog 回调里同步 call save_map service,负载高时 watchdog 自身超时,形成重启风暴。改为设 atomic flag,另线程异步处理。监控路径的预算应为主环的 1/10 以下。

10. 与录包复盘

控制事故袋应含周期直方图截图或原始样本:只有 cmd 曲线没有 jitter 数据,无法证明是 watchdog 误触还是真超时。录包旁路订阅 /diagnostics 或自建 /control_stats,复盘时对齐 watchdog trip 时间戳。

预算 + 看门狗 + 动作表;直方图说话;跨节点心跳对齐 QoS 与时钟。频率是意图,deadline 才是安全边界。

相关

也可以看看

← 全部文章

johan's blog