
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 全图一致:
ros2 param get /my_controller use_sim_timeGazebo 暂停时墙钟 watchdog 仍走——真机维护模式若只停仿真不 disarm watchdog,会误触发安全停。维护流程里写清:先 lifecycle deactivate,再停仿真。
3. Watchdog:监控「上次成功周期」
模式:记录每次控制回调入口时间 t_last;独立轻量定时器或线程检查 now - t_last > T_timeout → 触发安全停。被监控回调可以重;监控路径必须更轻——watchdog 里不要同步写盘或调 heavy service。
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%。没有直方图,「感觉还行」在上车无意义。
const double dt = (now() - last_tick_).seconds();
hist_.add_sample(dt);
last_tick_ = now();7. 与 lifecycle 的交点
inactive 或 unconfigured 时 watchdog 应 disarm 或忽略 stale 心跳,避免维护模式误触发 fleet 告警。activate 后 2 s 内允许 grace period,再进入严格超时——写进状态机,不是 launch 里 sleep 凑数。
8. 验收
- 故意在控制回调里
sleep(500ms):watchdog 在 T_timeout 内 zero cmd。 - 停发心跳 publisher:下游在合约时间内安全停。
- 仿真
use_sim_timepause:墙钟 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 才是安全边界。
相关
也可以看看
- ·16 分钟阅读
ROS 2 Action 取消语义:收到 cancel 不等于执行器已经停下
梳理 goal handle 状态机、取消回调与硬件停止之间的异步边界;用可中断执行循环、截止时间和终态发布构建契约并以注入验收。
- ·15 分钟阅读
ContentFilteredTopic:把过滤下推到中间件还是应用层
对比订阅端丢弃、应用层条件判断与 DDS 内容过滤的 CPU/带宽边界,说明表达式能力与发现时序限制;用高频率噪声话题与延迟尖峰验收过滤位置选择。
- ·9 分钟阅读
ROS 2 大消息内存路径:intra-process、loaned message 与 shared memory 不是一回事
以 4K 图像和点云管线为例,逐段核对 rclcpp、RMW 与进程边界上的所有权、分配和真实拷贝。
johan's blog