回放录包时,图像、检测结果、TF 都出来了,查询变换却提示数据不存在,算出来的延迟还是负数。第一次碰到这种情况我花了大半天才反应过来:不是数据缺了,是几个节点对“现在几点”的理解不一样。
ROS 2 里的时间不是单一的。系统时间、稳态时间、仿真时间可以同时存在,节点通过参数选一个。
三种时间不能互相替
系统时间表示真实时刻,跟着对时走。拿它算两个事件的间隔,中间要是发生了对时跳变,差值可能异常甚至为负。
稳态时间单调递增,适合测时长,但它不代表任何真实时刻,跨机器比没有意义。
仿真时间由 /clock 驱动,只有存在发布者时才推进。回放和仿真场景用它,能让整条链路基于录包里的时间跑。
一个常见误解是“设了 use_sim_time 就自动对齐了”。实际上它是逐节点的,参与的每个节点都要设,而且订阅方得收到 /clock 才会推进。
先看每个节点现在用哪个时钟
1 | ros2 param get /planner use_sim_time |
前两条看设置是否一致,第三条确认 /clock 确实在发,第四条确认它在推进而不是卡在一个值上。
只设了一部分节点时,症状很典型:用仿真时间的节点认为现在是回放进度,用系统时间的认为就是当前时刻,两边算出来的消息年龄差得离谱。
回放的启动顺序
先起时钟发布,再起消费节点,最后开始回放。反过来的话,节点在启动阶段拿不到时间会报错。
1 | # 1. 用仿真时间启动节点 |
--clock 是关键。没有它包里就没有时钟来源,use_sim_time 为真的节点会停在时间零点或者一直等。
时间戳的含义得按发布方判断
同一条消息里的 header.stamp,含义取决于发布者怎么填。可能是传感器采样时刻、数据完成时刻,也可能是发布时刻,这三个对延迟计算的影响完全不同。
算端到端延迟时,要明确两端用的是不是同一个时钟,以及时间戳对应的是不是同一个物理事件。起点是采样时刻、终点是系统时间的话,结论只在没有时钟跳变且两者同源时才成立。
稳妥一点的做法是同时记时间戳和接收时刻,并在日志里标出时钟来源。事后发现异常时,能分清是数据本身慢,还是口径不一致。
排查顺序
先确认 /clock 存在且在推进;再确认相关节点 use_sim_time 一致;然后确认消息时间戳在推进;最后才算延迟指标。
跳过前两步直接看延迟数字,很容易把时钟口径问题当成性能问题,然后去优化一个根本不存在的瓶颈。
参考资料
证据边界:本文不含任何系统的回放性能数据。上面的命令用来确认当前状态。“时间一致”是延迟结论成立的前提,但即便一致,具体数值仍然取决于负载和环境,得单独测。