mavros2 CPU 消耗过大?原因在这
同一个 MAVROS、同一套插件、同样的流率配置,从 ROS 1 迁到 ROS 2 后 CPU 占用直接翻了 4 倍——这不是玄学,是架构和中间件的锅。
本文基于 mavlink/mavros#2031 的完整讨论整理。
背景
2025 年 4 月开始,ArduPilot 的 Ryan Friedman 在 mavros 仓库报告了一个issue:同样的插件集合(24 个插件)和流率配置下,mavros ROS 2 (Jazzy + FastDDS) 的 CPU 占用是 ROS 1 的 4 倍。
小舒(GitHub: shupx)也深度参与了这场讨论:在 Humble + FastDDS 环境下复现了问题,通过实测定位到两大根因——模拟时间下的 /clock 订阅冗余、FastDDS 发现机制开销,并提出了换用 CycloneDDS 等行之有效的解决办法。本文即基于该 issue 的完整讨论整理。
现象一:ROS 2 比 ROS 1 慢 4 倍,插件开得越多越明显
原始报告:同一台机器、同样的插件和话题流率,ROS 2 版 MAVROS 的 CPU 占用是 ROS 1 版的 4 倍。
更夸张的实测数据(小舒在 Humble 上的复现):
- FastDDS + 10 个 MAVROS 实例(UDP 连接飞控):Intel i9 24 核直接被打满 100%;
- 同一条件下切到 CycloneDDS:只占 20%。
而且这不止是 mavros 的问题——只要 ROS 2 节点数量多(50+),FastDDS 在启动阶段的 CPU 开销就明显高于 CycloneDDS,mavros 只是更容易踩中,因为一个 mavros 进程里就有 10+ 个节点。
现象二:SITL 模拟时间下开销翻倍
同一个 mavros_node,小舒在 SITL 下实测两种时间模式,差别巨大:
| 模式 | CPU 占用 |
|---|---|
use_sim_time=true(/clock 以 200Hz 发布) | ~20% |
use_sim_time=false(墙钟时间) | ~4% |
模拟时间模式下 CPU 开销大约是真实时间的 5 倍。如果你在 SITL 里开发调试觉得机器发烫,先查这个。
原因分析
根因 1:每个插件一个独立节点
ROS 2 版 mavros 的架构是每个 plugin 创建一个独立 ROS 节点,一个进程里塞了 10+ 个节点(从 issue 里贴的插件列表就能看到十几个 /mavros/xxx 节点)。
节点多带来两个连锁反应:
/clock订阅冗余:ROS 2 里当use_sim_time=true时,每个节点都会订阅/clock来同步自己的 ROS 时间——哪怕是同一个进程里的多个节点也一样。10 个插件节点 = 10 份/clock回调处理,纯属重复劳动。- DDS 发现机制开销放大:每个节点都要参与 DDS 的发现(discovery)流程,节点越多,发现相关的状态维护和网络流量越大。FastDDS 的发现机制开销尤其高。
根因 2:FastDDS 的发现机制开销大
同样是节点多,FastDDS 和 CycloneDDS 表现差异巨大(这也是小舒在实测中观察到的)。启动 50+ 节点的场景下,FastDDS 启动阶段 CPU 极高,换 CycloneDDS 明显缓解;用 FastDDS 的 Discovery Server 也改善有限。这是 FastDDS 发现机制本身的开销问题,不是 mavros 独有。
根因 3:执行器(Executor)选择不当
mavros ROS 2 版默认用 MultiThreadedExecutor。社区测试发现:
- 换成 EventsExecutor(ROS 2 Jazzy 引入)能大幅降低 CPU;
- 但 mavros 里部分插件的回调函数内有锁,在事件执行器下可能挂死,导致 service 调用失败——所以至少需要 2 个线程兜底;
- 另外还有一个独立的小 bug:
sys_time插件的参数回调里用了rclcpp::WallRate而不是标准计时方式,白白浪费 CPU(对应 PR #2212,修复后能显著降耗)。
小结:开销来自两个层面
按 issue 里的结论总结,mavros2 的 CPU 开销主要来自:
- 启动开销:节点数量越多,启动阶段 CPU 消耗越高;FastDDS 的发现机制进一步放大。
- 模拟时间开销:
use_sim_time开启时,节点越多 →/clock订阅越多 → 时钟回调处理越多。
一句话:"每个插件一个节点"的架构 + FastDDS + 模拟时间,三重 buff 叠满。
解决办法
按"见效快 → 治本"排序:
1. 换 RMW:FastDDS → CycloneDDS(效果最立竿见影)
sudo apt install ros-${ROS_DISTRO}-rmw-cyclonedds-cpp
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
# 或写进 ~/.bashrc 永久生效
小舒的实测数据:同条件下 FastDDS 启动50+节点时打满 24 核 → CycloneDDS 只占 20%。节点多的场景下 CycloneDDS 的发现机制明显更省。当然还有一个方法是每个节点延迟启动,会降低cpu消耗,但总启动时间延长很多。
2. 关掉模拟时间(SITL 场景)
如果不需要仿真时间同步,把 use_sim_time 设为 false,用墙钟时间。实测 mavros_node 从 ~20% 降到 ~4%。
3. 换执行器(Jazzy 及以上)
mavros 2.15 提供了执行器类型开关:
export MAVROS_EXECUTOR_TYPE=events
或用 PR #2189 的线程数覆盖,把 UAS 执行器限制为 2 个工作线程(低风险,不用全局切换执行器):
export MAVROS_UAS_EXECUTOR_THREADS=2
注意:
- EventsExecutor 在 Humble 不可用(Jazzy 才引入);
- mavros 部分回调有锁,纯事件执行器可能引发 service 失败,至少保留 2 个线程;
cm_executors的CBGEventsExecutor已 backport 到 Jazzy,社区实测效果很好。
4. 砍插件数量
只加载真正用到的插件。mavros 是插件化架构,MAVROS_PLUGINLIST 里没用的插件直接不加载,节点少了,启动和运行开销都会降。
5. 期待治本:架构回归"共享节点"
issue 讨论的最终建议:像 ROS 1 版那样,让每个插件复用同一个节点,而不是各建各的。这能从根本上减少节点数量,同时降低启动时间和 /clock 开销。mavros 后续版本已经在朝"减少线程数、合并节点"的方向改(2.15 就有大量相关改动)。
总结
mavros2 CPU 占用高的本质是:插件级节点架构放大了 ROS 2 中间件的固有开销。短期最有效的三板斧:
- 换 CycloneDDS(收益最大);
- SITL 下关
use_sim_time; - Jazzy+ 换 events 执行器或限制线程数。
如果这些还不够,那就得等 mavros 的架构重构了——或者自己动手,把多余节点合并掉。
参考:mavlink/mavros Issue #2031 — CPU usage 4x higher in ROS 2 than ROS 1、PR #2189、PR #2212

541

被折叠的 条评论
为什么被折叠?



