3. ROS2内置性能工具:从现象到本质

做ROS2性能优化,最怕什么?

怕的是「凭感觉调优」。代码改了一堆,启动参数调了又调,结果性能反而更差了。我见过太多团队,花了两周优化一个节点,最后发现是工具本身在拖后腿。

这一章,我们就来聊聊ROS2自带的几个性能工具。它们不花哨,但很实用。我会告诉你每个工具怎么用,更重要的是——它们对系统性能到底有多大影响。

3.1 ros2 topic hz:最直接的频率监控

先说最简单的。你想知道一个话题的发布频率,用ros2 topic hz就够了。

# 监控 /camera/image 话题的发布频率
ros2 topic hz /camera/image

# 输出示例
average rate: 30.002
    min: 0.033s max: 0.034s std dev: 0.00021s window: 100

这个工具的原理很简单:订阅话题,记录每条消息的时间戳,然后算频率。但有个坑——它本身就是一个订阅者。

注意:ros2 topic hz 会占用一个订阅者名额。如果你的发布者设置了 depth=1 且只有一个订阅者,那这个工具就会抢走数据,导致你的业务节点收不到消息。

我记得有一次排查一个机器人底盘控制问题,发现cmd_vel的发布频率总是跳变。折腾了半天,最后发现是同时开了三个终端在跑ros2 topic hz。三个订阅者抢数据,频率自然不稳。

所以我的建议是:用这个工具做快速验证可以,但别长期开着。尤其是生产环境,开完就关。

3.2 ros2 topic delay:端到端延迟的照妖镜

频率没问题,但机器人就是反应慢?这时候该看延迟了。

# 监控话题的端到端延迟
ros2 topic delay /sensor/scan

# 输出示例
average delay: 0.045s
    min: 0.012s max: 0.089s std dev: 0.015s window: 100

这个工具测量的是「消息从发布到被接收的时间差」。它依赖消息头部的timestamp字段。如果你的消息没有时间戳,那这个工具就没法用。

小技巧:自定义消息时,记得加上 builtin_interfaces/Time stamp 字段。这是ROS2的约定,也是性能分析的基石。

实际项目中,我遇到过最离谱的情况:一个激光雷达话题,平均延迟200ms,但频率是正常的10Hz。你想想看,频率正常但数据是200ms前的,这对导航来说就是灾难。后来发现是发布者节点里有个阻塞的I/O操作,把消息队列堵住了。

这里有个关键点:ros2 topic delay 本身也会引入延迟。因为它需要接收消息、解析时间戳、计算差值。在低延迟场景(比如1ms以内),这个工具的测量误差可能达到20%以上。

3.3 ros2 bag record/play:性能影响比你想象的大

录包和回放,是调试的必备技能。但很多人不知道,这两个操作对系统性能的影响完全不同。

3.3.1 ros2 bag record:写入瓶颈

# 录制所有话题
ros2 bag record -a

# 录制指定话题
ros2 bag record /camera/image /lidar/points -o my_bag

录包时,系统要做三件事:

  1. 订阅话题,接收消息
  2. 序列化消息(把ROS2消息转成二进制)
  3. 写入磁盘

这三步里,磁盘写入是最大的瓶颈。尤其是机械硬盘,写入速度可能只有100MB/s。如果你录的是高带宽话题(比如摄像头30fps的1080p图像),磁盘很快就会成为瓶颈。

我的经验:录包时,尽量用SSD,并且不要和系统盘共用。我曾经在一个项目里,录包导致系统卡死,就是因为磁盘I/O占满了,ROS2的DDS通信都受影响。

还有一个容易被忽略的点:录包会占用DDS的带宽。ROS2的DDS默认是尽力传输,录包工具作为一个订阅者,会和其他订阅者竞争带宽。如果你的网络环境不好,录包可能导致其他节点收不到消息。

3.3.2 ros2 bag play:回放的陷阱

# 回放录制的包
ros2 bag play my_bag

# 以2倍速回放
ros2 bag play my_bag --rate 2.0

# 循环回放
ros2 bag play my_bag --loop

回放看起来简单,但坑很多。我挑几个常见的说:

  • 时间戳问题:回放默认使用录制时的时间戳。如果你用--clock参数,它会发布/clock话题,模拟时间。但有些节点不监听/clock,就会用系统时间,导致时序错乱。
  • 频率失真:回放时,如果系统负载高,实际发布频率可能低于录制频率。用--rate 2.0加速回放时,CPU占用会翻倍,容易丢包。
  • 内存占用:回放工具会把整个bag文件索引加载到内存。一个10GB的bag文件,索引可能占几百MB内存。我见过有人回放50GB的bag,直接OOM。
避坑指南:我曾经回放一个包含高分辨率图像的话题,结果回放节点CPU跑到100%,发布频率只有录制时的三分之一。后来发现是回放时没有限制发布队列深度,导致内存暴涨。解决办法是加 --topics /camera/image --qos-depth 1。

3.4 rqt_graph:可视化节点通信

说了这么多命令行工具,来点直观的。rqt_graph可以画出节点之间的通信关系图。

# 启动rqt_graph
rqt_graph

# 或者直接启动rqt,然后选择Plugins -> Introspection -> Node Graph

这个工具不消耗太多性能,因为它只是从ROS2的图发现机制中读取信息。但要注意:它显示的是「当前时刻」的通信关系。如果你的节点是动态创建/销毁话题的,rqt_graph可能来不及更新。

我个人习惯在调试初期先跑一下rqt_graph,看看节点之间的连接是不是预期的。有一次我发现一个话题有3个发布者,但只有1个订阅者,明显是代码里忘了取消订阅。

3.5 性能影响对比表

为了方便你选择,我把这几个工具的性能影响整理成了一张表:

工具 CPU占用 内存占用 网络带宽影响 适用场景
ros2 topic hz 低(<1%) 低(<10MB) 低(仅订阅) 快速验证频率
ros2 topic delay 中(1-3%) 低(<10MB) 低(仅订阅) 延迟问题排查
ros2 bag record 高(5-20%) 中(取决于缓存) 高(增加订阅者) 数据录制
ros2 bag play 中高(3-15%) 中高(索引+缓存) 高(发布数据) 数据回放
rqt_graph 极低(<0.5%) 低(<5MB) 无 通信拓扑查看

3.6 核心逻辑:工具的选择策略

说了这么多,其实就一个核心逻辑:用最小的代价,获取最需要的信息。

下面这张图,是我自己总结的工具选择流程:

ROS2性能工具选择策略 遇到性能问题 频率是否稳定? 否 ros2 topic hz 是 延迟是否正常? ros2 topic delay 需要录包分析? ros2 bag record/play rqt_graph 可视化

这张图的核心思路是:先判断问题类型,再选工具。频率问题用hz,延迟问题用delay,需要深入分析才用bag。别一上来就开录包,那是在给自己找麻烦。

我的习惯:调试时先开rqt_graph看一眼拓扑,确认节点连接没问题。然后用topic hz快速扫一遍关键话题的频率。如果频率正常但效果不对,再用topic delay看延迟。最后才考虑录包。这个顺序,能帮你省下至少一半的调试时间。

嗯,这一章的内容就到这里。工具本身不复杂,但用对工具、用对时机,才是性能优化的关键。下一章我们会深入DDS的配置,聊聊怎么从通信层面榨干ROS2的性能。