ROS2 性能优化指南

ROS2 性能优化完整指南

基于 ROS2 Jazzy 版本

目录

  1. 进程内通信 (Intra-Process)
  2. 零拷贝/借用消息 (Zero-Copy / Loaned Messages)
  3. 执行器优化
  4. [QoS 优化](#QoS 优化)
  5. 实时/实时安全特性
  6. 内存优化 (分配器/内存池)
  7. 序列化优化
  8. [DDS 厂商特定优化](#DDS 厂商特定优化)
  9. 发现机制优化
  10. 其他优化技巧

1. 进程内通信 (Intra-Process)

原理: 同一进程内的发布者和订阅者之间通过共享内存传递消息,避免序列化和反序列化开销。

启用方式

cpp 复制代码
// 在 NodeOptions 中启用
rclcpp::NodeOptions options;
options.use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("my_node", options);

配置选项

设置位置 参数 可选值 说明
NodeOptions use_intra_process_comms true/false 节点级别全局开关
PublisherOptions use_intra_process_comm Enable/Disable/NodeDefault 发布者级别控制
SubscriptionOptions use_intra_process_comm Enable/Disable/NodeDefault 订阅者级别控制
PublisherOptions intra_process_buffer_type SharedPtr/UniquePtr 进程内缓冲区所有权类型
SubscriptionOptions intra_process_buffer_type SharedPtr/UniquePtr/CallbackDefault 订阅缓冲区类型

性能收益

  • 避免序列化/反序列化: 消息不经过 DDS 层
  • 减少内存拷贝: IntraProcessManager 根据订阅者的所有权需求执行最少次数的拷贝
  • 降低延迟: 同进程通信延迟可降低 50-90%

注意事项

  • 仅适用于同一进程内的节点
  • UniquePtr 模式可实现真正的零拷贝(消息所有权转移)
  • SharedPtr 模式需要一次引用计数操作

2. 零拷贝/借用消息 (Zero-Copy / Loaned Messages)

原理: 直接从中间件借用预分配的内存缓冲区,发布和订阅时避免内存拷贝。

使用方式

cpp 复制代码
// 发布端:借用消息
auto loaned_msg = publisher->borrow_loaned_message();
loaned_msg.get().data = 42;  // 直接填充数据
publisher->publish(std::move(loaned_msg));  // 发布,无需拷贝

// 订阅端:借用接收
std::unique_ptr<rclcpp::LoanedMessage<std_msgs::msg::Int32>> loaned_msg;
subscription->take_loaned_message(loaned_msg);
// 使用 loaned_msg.get().data
subscription->return_loaned_message(std::move(loaned_msg));

配置选项

参数 类型 默认值 说明
rmw_publisher_options.can_loan_messages bool 取决于 RMW 实现 是否允许借用消息
rmw_subscription_options.can_loan_messages bool 取决于 RMW 实现 是否允许借用接收
disable_loaned_message bool false 显式禁用借用消息

支持的 RMW 实现

RMW 实现 支持情况
rmw_fastrtps_cpp ✅ 支持
rmw_cyclonedds_cpp ✅ 支持
rmw_connextdds ✅ 支持

性能收益

  • 零拷贝发布: 消息直接从中间件内存发布
  • 零拷贝订阅: 消息直接在中间件内存中读取
  • 减少内存分配: 避免每次消息传递的堆分配

3. 执行器优化

3.1 StaticSingleThreadedExecutor

原理: 静态版本,不在每次迭代时重建可执行实体列表。

cpp 复制代码
rclcpp::executors::StaticSingleThreadedExecutor executor;
executor.add_node(node);
executor.spin();
特性 说明
静态实体集合 spin() 前创建实体,仅在添加/删除时修改
减少动态分配 旋转周期内无动态内存分配
适用场景 节点和实体数量固定的场景

3.2 EventsExecutor(实验性)

原理: 事件驱动架构,实体通过回调函数将事件推入队列。

cpp 复制代码
#include <rclcpp/experimental/executors/events_executor/events_executor.hpp>

rclcpp::experimental::executors::EventsExecutor executor;
executor.add_node(node);
executor.spin();
特性 说明
事件队列 支持自定义 EventsQueue 实现
减少维护操作 不遍历所有实体检查就绪状态
可定制性 可实现低 CPU 使用率、有界内存、确定性等目标
定时器独立线程 execute_timers_separate_thread 可选

3.3 MultiThreadedExecutor 调优

cpp 复制代码
rclcpp::executors::MultiThreadedExecutor executor(
    rclcpp::ExecutorOptions{},
    4,           // number_of_threads: 0=自动检测CPU核心数
    false,       // yield_before_execute: 执行前是否让出CPU
    -1           // timeout: -1=无限等待
);
参数 建议
number_of_threads 设置为 CPU 核心数或回调密集型任务的需求数
yield_before_execute 高优先级实时场景设为 true,一般场景设为 false

3.4 实时执行器 (RttExecutor)

cpp 复制代码
// pendulum_control demo 中的实时执行器
pendulum_control::RttExecutor executor;
executor.add_node(node);
rttest_spin([](void *) { executor.spin_some(); }, nullptr);

配合 rttest 库进行实时性能测量和延迟统计。


4. QoS 优化

4.1 选择合适的预定义 QoS

QoS 配置 History Depth Reliability Durability 适用场景
SensorDataQoS KeepLast 5 BestEffort Volatile 高频传感器数据
ParametersQoS KeepLast 1000 Reliable Volatile 参数服务
ServicesQoS KeepLast 10 Reliable Volatile 服务调用
ClockQoS KeepLast 1 BestEffort Volatile 时钟同步
RosoutQoS KeepLast 1000 Reliable TransientLocal 日志输出

4.2 Best Available QoS

动态选择最佳 QoS,基于已发现的端点自动协商:

cpp 复制代码
rclcpp::QoS qos(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_best_available));
策略 说明
Reliability BestAvailable 自动选择 Reliable 或 BestEffort
Durability BestAvailable 自动选择 TransientLocal 或 Volatile
Liveliness BestAvailable 自动选择最佳活跃性策略

4.3 关键 QoS 参数调优

参数 优化建议
depth 高频低延迟场景设为小值(1-5),大数据吞吐场景设为大值
reliability 实时场景使用 BestEffort 避免重传延迟
durability 不需要历史数据时使用 Volatile 减少内存
deadline 设置合理的截止期限以检测消息丢失
lifespan 过期消息自动丢弃,避免处理陈旧数据

5. 实时/实时安全特性

5.1 内存锁定与预分配

cpp 复制代码
#include <rttest/rttest.h>

// 锁定内存到 RAM,防止页面错误
rttest_lock_and_prefault_dynamic();

// 设置实时调度优先级
rttest_set_sched_priority(90);

5.2 TLSF 分配器(确定性内存分配)

cpp 复制代码
#include <tlsf_cpp/tlsf.hpp>

// 使用 TLSF 分配器
using TLSFAllocator = rclcpp::allocator::AllocatorWithMemoryStrategy<
    std::allocator<uint8_t>,
    rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<
        std::allocator<uint8_t>>>;
特性 说明
TLSF (Two-Level Segregated Fit) 确定性时间复杂度的内存分配算法
有界分配时间 最坏情况分配时间可预测
低碎片化 适合长时间运行的实时系统

5.3 实时最佳实践

  • 避免在实时路径中使用 new/delete
  • 使用内存池预分配消息
  • 锁定内存防止页面错误
  • 使用实时调度策略(SCHED_FIFO/SCHED_RR)
  • 避免动态加载库

6. 内存优化 (分配器/内存池)

6.1 MessagePoolMemoryStrategy

原理: 编译时预分配固定大小的消息池,使用循环数组实现 O(1) 借还。

cpp 复制代码
#include <rclcpp/strategies/message_pool_memory_strategy.hpp>

using MyPoolStrategy = rclcpp::message_pool_memory_strategy::MessagePoolMemoryStrategy<
    std_msgs::msg::Float64,  // 消息类型
    50                        // 池大小
>;

auto subscription = node->create_subscription<std_msgs::msg::Float64>(
    "topic", 10, callback,
    rclcpp::SubscriptionOptions(),
    std::make_shared<MyPoolStrategy>());
特性 说明
静态内存分配 编译时确定内存需求
O(1) 借还操作 循环数组实现
线程安全 互斥锁保护
消除动态分配 消息处理期间无堆分配

6.2 自定义分配器

cpp 复制代码
// 使用自定义 STL 兼容分配器
rclcpp::PublisherOptionsWithAllocator<MyCustomAllocator> options;
options.allocator = std::make_shared<MyCustomAllocator>();

auto publisher = node->create_publisher<std_msgs::msg::String>(
    "topic", 10, options);

6.3 PMR 分配器支持

cpp 复制代码
// Polymorphic Memory Resources
#include <memory_resource>

std::pmr::monotonic_buffer_resource pool{large_buffer};
// 使用 PMR 分配器减少频繁的小内存分配

7. 序列化优化

7.1 序列化消息 API

cpp 复制代码
// 发布预序列化消息
rclcpp::SerializedMessage serialized_msg;
// ... 填充序列化数据 ...
publisher->publish(serialized_msg);  // 无需重新序列化

// 直接接收序列化数据
rclcpp::SerializedMessage msg;
subscription->take(serialized_msg, msg);  // 无需反序列化

7.2 CDR 序列化优化

RMW 实现 优化特性
FastCDR (Fast DDS) 高性能 CDR 序列化
Cyclone DDS dds_stream_check_optimize() - 可优化为 memcpy 的固定类型检测
通用 XCDR1/XCDR2 编码版本支持

7.3 使用场景

  • 消息转发/桥接: 直接转发序列化数据,避免反序列化再序列化
  • 录包/回放: 直接存储序列化数据
  • 自定义传输: 通过非 DDS 通道传输序列化数据

8. DDS 厂商特定优化

8.1 Fast DDS

环境变量/配置 说明 推荐值
RMW_FASTRTPS_PUBLICATION_MODE 发布模式 SYNCHRONOUS(低延迟)/ ASYNCHRONOUS(非阻塞)
RMW_FASTRTPS_USE_QOS_FROM_XML 从 XML 加载 QoS 设为 1 启用完整 QoS 控制
共享内存传输 同主机通信优化 默认启用
Data Sharing Delivery 主机内数据共享机制 适用于高频大数据

XML QoS 配置示例:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
    <participant profile_name="my_participant">
        <rtps>
            <useBuiltinTransports>true</useBuiltinTransports>
        </rtps>
    </participant>
</profiles>

8.2 Cyclone DDS

配置项 说明 推荐值
Iceoryx 共享内存 通过 Eclipse Iceoryx 实现零拷贝共享内存 启用
TCP_NODELAY Socket 延迟优化 启用
ENABLE_LTO 链接时优化 编译时启用
SPDP 禁用 禁用简单参与者发现 固定拓扑场景
多播回环 节点内性能优化 按需配置

Cyclone DDS XML 配置示例:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<CycloneDDS xmlns="https://cdds.io/config">
    <Domain Id="any">
        <General>
            <Interfaces>
                <NetworkInterface autodetermine="true"/>
            </Interfaces>
        </General>
        <Tracing>
            <Verbosity>config</Verbosity>
        </Tracing>
    </Domain>
</CycloneDDS>

9. 发现机制优化

9.1 ROS_DOMAIN_ID

bash 复制代码
# 网络隔离,减少发现流量
export ROS_DOMAIN_ID=42
场景 建议
多机器人系统 每个机器人使用不同的 DOMAIN_ID
测试环境隔离 使用 domain coordinator 自动分配
生产环境 固定 DOMAIN_ID 避免冲突

9.2 Fast DDS Discovery Server

适用于大规模部署,减少发现流量:

bash 复制代码
# 启动 Discovery Server
fast-discovery-server -c config.xml

# 客户端连接到 Discovery Server
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export FASTRTPS_DEFAULT_PROFILES_FILE=discovery_client.xml

9.3 减少发现开销

方法 说明
禁用 SPDP 固定拓扑场景可禁用简单参与者发现
静态发现配置 预配置已知端点,减少动态发现
减少参与者数量 合并节点,减少 DDS 参与者

10. 其他优化技巧

10.1 回调组优化

cpp 复制代码
// 互斥回调组:组内回调不会并发执行
auto mutex_group = node->create_callback_group(
    rclcpp::CallbackGroupType::MutuallyExclusive);

// 可重入回调组:组内回调可以并发执行
auto reentrant_group = node->create_callback_group(
    rclcpp::CallbackGroupType::Reentrant);
场景 推荐
共享资源保护 MutuallyExclusive
独立高频回调 Reentrant
多线程执行器 合理分配回调组以提高并行度

10.2 隐藏节点/主题

cpp 复制代码
// 使用隐藏节点减少命名空间污染
auto node = std::make_shared<rclcpp::Node>("_my_hidden_node");

// 隐藏主题
auto pub = node->create_publisher<std_msgs::msg::String>(
    "_internal_topic", 10);

10.3 参数服务优化

cpp 复制代码
rclcpp::NodeOptions options;
options.start_parameter_services(false);       // 禁用参数服务
options.start_parameter_event_publisher(false); // 禁用参数事件发布
options.allow_undeclared_parameters(true);     // 允许未声明参数(减少声明开销)

10.4 日志优化

cpp 复制代码
rclcpp::NodeOptions options;
options.enable_rosout(false);  // 禁用 rosout 日志(减少发布开销)

10.5 选择合适的 RMW 实现

bash 复制代码
# 切换 RMW 实现
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp    # Fast DDS(默认)
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp  # Cyclone DDS
export RMW_IMPLEMENTATION=rmw_connextdds      # Connext DDS
RMW 实现 特点
Fast DDS 功能丰富,共享内存支持好,适合大多数场景
Cyclone DDS 轻量级,延迟低,Iceoryx 零拷贝集成
Connext DDS 企业级,安全性好,适合认证场景

10.6 编译优化

bash 复制代码
# 发布模式编译
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release

# 启用 LTO(链接时优化)
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release -DENABLE_LTO=ON

# 优化标志
colcon build --cmake-args -DCMAKE_CXX_FLAGS="-O3 -march=native"

10.7 性能测试工具

工具 用途
ros2 topic hz 测量主题发布频率
ros2 topic bw 测量主题带宽
ros2 topic delay 测量消息延迟
ddsperf (Cyclone DDS) DDS 性能测试
Fast DDS latency/throughput test Fast DDS 性能测试
performance_test_fixture ROS2 性能测试框架

性能优化检查清单

优化项 优先级 预期收益
启用进程内通信 ⭐⭐⭐ 延迟降低 50-90%
使用零拷贝借用消息 ⭐⭐⭐ 消除消息拷贝
选择合适的执行器 ⭐⭐⭐ CPU 使用率降低 30-50%
优化 QoS 配置 ⭐⭐ 延迟降低 20-40%
使用内存池 ⭐⭐ 消除动态分配,提高确定性
启用共享内存传输 ⭐⭐ 同主机通信延迟降低
使用实时分配器 ⭐⭐ 最坏情况延迟可预测
优化发现机制 大规模系统启动时间缩短
编译优化 整体性能提升 10-20%
禁用不需要的服务 减少资源占用
相关推荐
山峰哥13 小时前
SQL慢查询调优实战:从全表扫描到索引覆盖的完整复盘
前端·数据库·sql·性能优化
码云之上15 小时前
万星入坞·其二:子应用如何优雅地"入坞"
性能优化·架构·前端框架
噗噗1221 小时前
大型私域系统的 Webhook 回调高并发架构设计与性能优化
性能优化
波特率1152001 天前
在ROS2当中两种rmw比较(CycloneDDS和FastDDS)
ros·ros2·dds
.NET修仙日记1 天前
.NET EFCore批量插入性能优化实战:30秒 → 0.5秒
性能优化·c#·.net·.netcore·微软技术·efcore·踩坑实录
Swift社区1 天前
鸿蒙 PC 性能优化实战:从卡顿到丝滑
华为·性能优化·harmonyos
斜阳日落1 天前
Qt 框架深度解析与性能优化
qt·性能优化·系统架构
lzhdim1 天前
C#性能优化技巧
开发语言·性能优化·c#
王飞飞不会飞2 天前
iOS卡顿查找和定位-ProFile
ios·性能优化