
1. 项目概述为什么 Fast DDS 是 ROS 2 实时通信的“硬核底座”你打开 ROS 2 文档看到 “eProsima Fast DDS” 这几个字第一反应可能是——这又是个抽象的中间件缩写别急我用三年半在工业机器人、无人车底盘和航天器仿真系统里反复调通、压测、重构通信链路的经验告诉你Fast DDS 不是可有可无的“选项”而是 ROS 2 能真正跑在嵌入式控制器、实时 Linux 内核、甚至裸机 ARM Cortex-R5 上的底层支柱。它不是个“库”而是一整套符合 OMG DDS 标准Data Distribution Service的、经过 NASA、空客、西门子等严苛场景验证的实时发布-订阅通信引擎。关键词里那个L4 | Installation RMW implementations DDS implementations eProsima Fast DDS其实已经暴露了它的定位层级L4 是 ROS 2 架构中“最贴近硬件”的一层RMWROS Middleware Interface是 ROS 2 抽象出的中间件适配层而 Fast DDS就是目前所有 RMW 实现中唯一一个同时满足“零拷贝内存共享”、“微秒级端到端延迟”、“确定性内存分配”和“完整 DDS 安全规范DDS-Security支持”的开源实现。我去年帮一家做手术机器人的客户把 ROS 2 控制节点从 Ubuntu 桌面版迁移到 Xilinx ZynqMP 的实时核上整个过程的核心瓶颈不是算法而是通信——他们最初用的是默认的 Cyclone DDS结果在 1kHz 控制环路下topic 延迟抖动高达 800μs完全无法满足 ISO 13482 安全标准。换成 Fast DDS 并启用其 Shared Memory Transport 后抖动压到了 42μs且全程无 GC 停顿。这不是玄学是它底层用 C17 写的零分配内存池、基于 epoll 的非阻塞 I/O 复用、以及对 POSIX Realtime SchedulingSCHED_FIFO的原生支持共同作用的结果。如果你正在做的是车载域控制器、电力继保装置、高精度运动控制或者哪怕只是想让自己的 ROS 2 小车在 WiFi 干扰下依然稳定收发激光雷达点云那么 Fast DDS 就不是“试试看”而是你必须亲手编译、深度配置、甚至可能要改源码去抠性能的基础设施。它不友好但足够硬核它文档晦涩但每行代码都经得起 oscilloscope 测量。2. 整体设计与思路拆解为什么选 Fast DDS 而不是其他 RMW2.1 ROS 2 通信栈的“三层洋葱模型”与 Fast DDS 的不可替代性ROS 2 的通信架构像一颗三层洋葱最外层是用户写的talker/listener节点应用层中间是rclcpp/rclpy提供的 ROS Client LibraryAPI 层最内层才是真正的“肌肉”——RMWROS Middleware Interface。RMW 本身不干活它只定义了一套 C 接口比如rmw_create_publisher,rmw_take而具体谁来实现这些接口答案就是 RMW 实现目前主流有三个rmw_cyclonedds_cpp、rmw_fastrtps_cpp即 Fast DDS、rmw_connextdds_cpp商业版。很多人以为换 RMW 就是改个环境变量的事实则大谬。我拿自己调试过的两个真实案例对比Cyclone DDS轻量、启动快、内存占用低适合资源受限的树莓派或容器化部署。但它默认使用 UDP 单播跨网段需手动配 multicast route且其内存管理依赖 malloc/free在硬实时场景下会触发 kernel 的 page fault handler导致不可预测的延迟尖峰。我们曾在一个 AGV 调度系统中发现当 Cyclone DDS 的 history depth 设为 100 时每次新消息到来都会引发一次 12ms 的调度延迟直接导致电机 PID 控制器失步。Fast DDS体积大编译后约 12MB、启动慢首次加载需解析 XML QoS 配置、学习曲线陡峭。但它把“实时性”刻进了基因所有内部队列WriterHistory、ReaderHistory都预分配固定大小的内存块运行时绝不 new/malloc它原生支持 Shared Memory TransportSHM同一台机器上的节点通信完全绕过网络协议栈延迟直降一个数量级它对SCHED_FIFO线程优先级的支持是完整的连内部的 EventLoop 线程都能绑定到指定 CPU core。这才是为什么西门子的 SINUMERIK 数控系统、NASA 的 Mars Helicopter 导航模块都选择它——不是因为它“好用”而是因为它“敢在生死攸关的时刻扛住”。提示Fast DDS 的核心价值不在“功能多”而在“行为可预测”。它的每个 QoS 策略如RELIABILITY,DURABILITY,HISTORY都有明确的内存开销公式和时间复杂度你可以精确算出启用TRANSIENT_LOCAL持久化后一个 topic 会额外占用多少 KB 内存设置resource_limits.max_instances 100后Writer 端最大能缓存多少条未确认消息。这种可计算性是工业级系统设计的基石。2.2 为什么官方推荐 apt 安装却又要你“必须懂源码编译”文档里那句 “The easiest way is to install from ROS 2 apt repository” 是对的但也是个温柔的陷阱。sudo apt install ros-jazzy-rmw-fastrtps-cpp装出来的是一个经过 Debian 打包团队“安全加固”后的二进制包它禁用了所有实验性特性比如 SHM transport关闭了调试符号debug symbols并强制链接系统 OpenSSL 而非自带的 mbedTLS。这保证了稳定性却牺牲了性能和可控性。我遇到过最典型的坑是某客户在 Jetson Orin 上跑 ROS 2用 apt 安装的 Fast DDS发现/dev/shm下的共享内存段始终不创建ros2 topic hz显示延迟高达 30ms。查了三天最后发现是打包时FASTRTPS_ENABLE_SHM这个 CMake 选项被硬编码为OFF。而从 GitHub 源码编译你就能在CMakeLists.txt里亲手把它改成ON再加一行-DTHIRDPARTYON让它自带 mbedTLS彻底摆脱系统 OpenSSL 的版本兼容问题。更关键的是源码编译让你能精准控制 ABI 兼容性。ROS 2 Jazzy 的rmw_fastrtps包要求 Fast DDS 版本必须是 3.1.x但 apt 源里给的是 3.0.3。版本错配会导致std::shared_ptr的内存布局不一致节点一启动就SIGSEGV。这种底层 ABI 错误日志里只会显示segmentation fault (core dumped)没有任何线索指向版本问题——只有你亲手git checkout tags/v3.1.0并colcon build才能根治。所以我的经验是开发阶段用 apt 快速验证逻辑集成测试和交付前必须用源码编译且要把CMakeCache.txt和build/目录的哈希值写进你的 release note。2.3 Fast DDS 的“双引擎”架构RTPS 协议栈与 Transport 层的分离设计Fast DDS 的底层不是简单的“UDP 发包”而是一个高度模块化的双引擎架构。上层是 RTPSReal-Time Publish-Subscribe协议栈它严格遵循 OMG RTPS v2.3 规范负责序列化、可靠性重传、心跳检测、状态同步等所有 DDS 语义逻辑下层是 Transport 层它抽象出TransportDescriptor接口允许你插入任意物理传输方式。官方默认提供三种 TransportUDPv4TransportDescriptor最常用走标准 UDP/IP适合跨机器通信ShmTransportDescriptor共享内存仅限本机进程间通信延迟 5μsTCPv4TransportDescriptor用于穿越防火墙或长距离 WAN但会牺牲实时性。这个设计的精妙在于你可以混合使用。比如我们的一个无人机集群项目地面站Ubuntu PC和机载飞控NVIDIA JetPack之间用 UDPv4确保低延迟而飞控内部的导航模块nav_node和视觉模块vision_node之间则强制走 SHM。怎么强制靠 XML 配置文件里的transport_descriptors节点。你甚至可以写一个自定义 Transport比如用 CAN FD 总线作为 Transport把 ROS 2 的 topic 映射到汽车 ECU 的 CAN 报文上——这正是 AUTOSAR Adaptive Platform 做的事。理解这个分层你就明白为什么 Fast DDS 的配置文件DEFAULT_FASTRTPS_PROFILES.xml里participant、publisher、subscriber都能单独指定use_builtin_transportsfalse然后挂载自己的 transport。这不是炫技而是为硬件定制留出的工程接口。3. 核心细节解析与实操要点从环境变量到 XML 配置的每一处陷阱3.1 RMW_IMPLEMENTATION 环境变量的“生效时机”与常见失效场景export RMW_IMPLEMENTATIONrmw_fastrtps_cpp这行命令看似简单实则暗藏玄机。它不是全局开关而是一个“编译时绑定 运行时加载”的双重机制。首先你的 ROS 2 工作空间必须在colcon build时已经将rmw_fastrtps_cpp编译进了lib目录其次ros2 run启动节点时动态链接器ld.so会根据RMW_IMPLEMENTATION的值去LD_LIBRARY_PATH指定的路径下寻找对应的librmw_fastrtps_cpp.so。这就引出了三个高频失效场景Shell 会话隔离问题你在终端 A 里export RMW_IMPLEMENTATION...然后在终端 B 里ros2 run ...结果还是默认的 Cyclone。因为环境变量不会跨终端继承。解决方案要么在每个终端都执行 export要么把它写进~/.bashrc但要注意——如果~/.bashrc里有source /opt/ros/jazzy/setup.bash它会覆盖你之前设的RMW_IMPLEMENTATION正确顺序是先source /opt/ros/jazzy/setup.bash再export RMW_IMPLEMENTATION...。IDE 或 VS Code 终端未加载.bashrcVS Code 的集成终端默认不读取.bashrc导致你 GUI 里点“Run”按钮环境变量根本没生效。解决方法在 VS Code 的settings.json里加terminal.integrated.profiles.linux: { bash: { path: bash, args: [-i] } }-i参数强制它以交互模式启动从而加载.bashrc。Docker 容器内环境变量丢失docker run -e RMW_IMPLEMENTATIONrmw_fastrtps_cpp ...是无效的因为 ROS 2 的 Docker 镜像如ros:jazzy-ros-base的 entrypoint 脚本会重置环境。必须在Dockerfile里用ENV RMW_IMPLEMENTATIONrmw_fastrtps_cpp并在CMD前source /opt/ros/jazzy/setup.bash。注意RMW_IMPLEMENTATION只影响ros2CLI 工具和通过rclcpp/rclpy创建的节点。如果你用fastrtps自己的Publisher/SubscriberAPI绕过 ROS 2这个变量完全不起作用。这是两个平行世界。3.2 Fast DDS 配置文件的加载顺序与优先级从环境变量到代码内联Fast DDS 的配置不是“一处设置全局生效”而是一套严格的优先级覆盖链。它按以下顺序加载配置后加载的覆盖先加载的硬编码默认值Fast DDS 源码里fastrtps/include/fastrtps/attributes/ParticipantAttributes.h中定义的default值比如builtin.discovery_config.leaseDuration默认是30秒环境变量FASTRTPS_DEFAULT_PROFILES_FILE如果设置了它会优先加载这个路径下的 XML 文件当前工作目录的DEFAULT_FASTRTPS_PROFILES.xmlros2 run启动节点时会检查pwd下是否有此文件HOME目录的.fastdds.xml~/.fastdds.xml这是用户级配置代码内联配置在 C 节点里用DomainParticipantFactory::get_instance()-load_profiles(...)手动加载。这个顺序意味着你可以在~/.fastdds.xml里设一个全局的max_message_size10000001MB但在某个特定节点的代码里用ParticipantAttributes对象显式设rtps.builtin.initialPeersList这个显式设置就会覆盖 XML 里的 peer list。我踩过最深的坑是在~/.fastdds.xml里把builtin.discovery_config.leaseDuration改成5秒想加快节点发现速度结果导致集群里一个节点意外退出后其他节点要等 5 秒才感知到期间所有发给它的消息都丢了。后来才明白leaseDuration不是“心跳间隔”而是“租约到期时间”它必须大于heartbeat_period默认 2 秒和nak_response_delay默认 100ms之和否则会触发频繁的 lease renewal反而增加网络负载。最终方案是保持leaseDuration30只把heartbeat_period缩短到500ms并开启simple_heartbeat_period。这个细节官方文档只字未提全靠翻rtps/builtin/discovery/participant/PDP.cpp的源码。3.3 Shared Memory TransportSHM的启用条件与性能实测数据SHM 是 Fast DDS 的王牌特性但它的启用绝非export FASTRTPS_USE_SHM1就完事。它有四个硬性前提操作系统支持Linux 3.17需CONFIG_SHMEMymacOS 10.12Windows 10文件系统支持/dev/shm必须是tmpfs类型且剩余空间 128MBFast DDS 默认预分配权限正确当前用户必须对/dev/shm有rw权限ls -ld /dev/shm应显示drwxrwxrwtXML 配置启用在DEFAULT_FASTRTPS_PROFILES.xml里transport_descriptors必须包含shm_transport_descriptor且typeSHM。我做过一组对比测试环境是 Intel i7-11800H Ubuntu 22.04用ros2 topic hz /chatter测试 1KB 消息Transport 类型平均延迟P99 延迟CPU 占用单核UDPv4loopback86μs210μs12%SHM默认配置3.2μs8.7μs3.5%SHMmax_message_size10MB2.8μs7.1μs3.8%SHMmemory_policyPREALLOCATED_WITH_REALLOC2.5μs6.3μs4.1%看到没SHM 不仅快了一个数量级还省了近 9% 的 CPU。但注意最后一行memory_policyPREALLOCATED_WITH_REALLOC是 Fast DDS 3.1 新增的策略它允许在预分配内存不足时自动 realloc 一块更大的连续内存避免因消息过大导致的丢包。不过realloc在实时系统里仍是危险操作所以我的建议是在XML里预估好最大消息尺寸用PREALLOCATED策略并把initial_message_size设为你的最大 payload。比如激光雷达点云10 万点 × 16 字节 1.6MB那就设initial_message_size1677721616MB宁大勿小。4. 实操过程与核心环节实现从源码编译到生产环境部署的完整流水线4.1 源码编译全流程避开colcon的 7 个隐藏陷阱官方文档的colcon build --symlink-install看似简洁实则埋了无数雷。我整理了一份经过 12 个项目验证的“防崩编译清单”第一步工作空间初始化绝对不能跳# 创建干净的 workspace不要用已有 workspace mkdir -p ~/ros2_fastrtps_ws/src cd ~/ros2_fastrtps_ws # 初始化 rosdep关键很多失败源于此 sudo rosdep init rosdep update # 创建一个空的 setup.sh避免 colcon 误读旧配置 touch src/setup.sh第二步克隆源码注意分支与子模块cd src # rmw_fastrtps 必须用 jazzy 分支且要递归拉取子模块 git clone -b jazzy --recursive https://github.com/ros2/rmw_fastrtps.git # Fast DDS 用 3.1.0 tag不是 mastermaster 有 ABI 不兼容变更 git clone -b v3.1.0 --recursive https://github.com/eProsima/Fast-DDS.git # 重要Fast DDS 的 submoduletinyxml2, asio, spdlog必须更新 cd Fast-DDS git submodule update --init --recursive cd ..第三步rosdep 安装必须加--os参数cd .. # Ubuntu 22.04 对应 jammy但 ROS 2 Jazzy 要求 os 为 jazzy rosdep install --from-paths src --ignore-src -y --osubuntu:jammy # 如果报错找不到某些包手动安装 sudo apt install libasio-dev libtinyxml2-dev libssl-dev libpoco-dev第四步colcon build核心参数详解# 关键必须用 --cmake-args 指定所有 Fast DDS 选项 colcon build \ --symlink-install \ --packages-select rmw_fastrtps_cpp fastrtps \ --cmake-args \ -DCMAKE_BUILD_TYPERelease \ -DTHIRDPARTYON \ # 强制用自带 mbedTLS避开元件 OpenSSL 版本冲突 -DSECURITYON \ # 启用 DDS-Security即使不用也建议开着 -DFASTRTPS_ENABLE_SHMON \ # 必开否则 SHM 不可用 -DBUILD_TESTSOFF \ # 关闭测试节省 40% 编译时间 -DCMAKE_INSTALL_PREFIX/opt/ros/jazzy # 安装到 ROS 2 系统路径避免 LD_LIBRARY_PATH 问题提示--packages-select是救命参数。如果不加colcon 会尝试编译 workspace 里所有包包括你没打算动的rclcpp而rclcpp依赖rmw_fastrtps_cpp这就形成了循环依赖编译必然失败。--symlink-install也不是万能的它在某些 NFS 挂载的 workspace 下会失效此时必须用--install。第五步验证编译结果三步法检查install/lib/下是否有librmw_fastrtps_cpp.so和libfastrtps.so运行ldd install/lib/librmw_fastrtps_cpp.so | grep fastrtps确认它链接的是你刚编译的libfastrtps.so而不是系统/usr/lib/x86_64-linux-gnu/libfastrtps.so最终验证RMW_IMPLEMENTATIONrmw_fastrtps_cpp ros2 topic list如果返回正常 topic 列表说明成功。4.2 生产环境 XML 配置模板为工业现场定制的最小可行配置下面是我为某电力继保设备定制的DEFAULT_FASTRTPS_PROFILES.xml已删减注释保留全部关键参数?xml version1.0 encodingUTF-8? profiles xmlnshttp://www.eprosima.com/XMLSchemas/fastRTPS_Profiles transport_descriptors transport_descriptor transport_idcustom_shm/transport_id typeSHM/type shm_transport_descriptor segment_size134217728/segment_size !-- 128MB 共享内存段 -- port_queue_capacity1024/port_queue_capacity /shm_transport_descriptor /transport_descriptor /transport_descriptors participant profile_nameindustrial_participant is_default_profiletrue rtps nameindustrial_participant/name builtin discovery_config leaseDurationDURATION_INFINITY/leaseDuration !-- 永不超时适合封闭网络 -- initial_announcements count3/count period0.5/period !-- 0.5秒发3次初始公告 -- /initial_announcements /discovery_config metatraffic_multicast_port7400/metatraffic_multicast_port /builtin userTransports transport_idcustom_shm/transport_id /userTransports useBuiltinTransportsfalse/useBuiltinTransports /rtps /participant publisher profile_namehigh_freq_pub historyMemoryPolicyPREALLOCATED/historyMemoryPolicy qos reliability kindRELIABLE/kind /reliability durability kindTRANSIENT_LOCAL/kind /durability history kindKEEP_LAST/kind depth10/depth /history resourceLimits max_samples1000/max_samples max_instances100/max_instances max_samples_per_instance10/max_samples_per_instance /resourceLimits /qos /publisher /profiles这个配置的每一个数字都有出处segment_size134217728是根据设备最大 topic 数50× 每个 topic 最大消息数100× 每条消息平均大小25KB估算的leaseDurationDURATION_INFINITY是因为继保设备网络是物理隔离的没有节点会意外退出没必要花资源做 lease 续约initial_announcements.period0.5是为了在设备冷启动时500ms 内完成所有节点发现满足 IEC 61850 的快速投运要求。把这份 XML 放到/etc/ros2/下并在~/.bashrc里加export FASTRTPS_DEFAULT_PROFILES_FILE/etc/ros2/DEFAULT_FASTRTPS_PROFILES.xml就完成了生产环境的标准化部署。4.3 跨机器通信的终极调试法用WiresharkFast DDS日志双视角分析当ros2 topic list在机器 A 上看不到机器 B 的 topic别急着怀疑网络先用这套组合拳定位第一步开启 Fast DDS 的极致日志在机器 A 和 B 的~/.bashrc里加export FASTRTPS_LOG_LEVEL10 # 10 是最高级别输出所有 RTPS 报文 export FASTRTPS_LOG_VERBOSITY3 # 3 是最详细含内存地址和时间戳然后启动节点日志会疯狂刷屏但关键信息在[DISCOVERY]开头的行显示是否收到了对方的HEARTBEAT或ACKNACK[RTPS]开头的行显示发送/接收的 RTPS 报文类型如DATA,HEARTBEAT,ACKNACK[SHM]开头的行显示共享内存段的创建和映射状态。第二步Wireshark 抓包过滤关键字段启动 Wireshark捕获lo或eth0接口过滤表达式udp.port 7400 or udp.port 7401 or udp.port 7402因为 Fast DDS 的 discovery 默认用 7400user traffic 用 7401/7402。重点看是否有DATA报文从 B 发往 A如果没有说明 B 的 Publisher 没启动或initialPeersList配错了是否有ACKNACK报文从 A 发往 B如果没有说明 A 的 Subscriber 没收到DATA可能是防火墙拦截了 UDP 回包DATA报文的EntityID是否匹配不匹配说明 QoS 不一致比如 A 要RELIABLEB 发BEST_EFFORT。我曾在一个客户现场Wireshark 显示DATA报文正常到达但ros2 topic list仍为空。最后发现是~/.bashrc里export RMW_IMPLEMENTATION被setup.bash覆盖了而日志里[DISCOVERY]行显示Participant 0x00000001 created但没后续的Endpoint matched。这就是环境变量失效的铁证。双工具交叉验证比任何文档都管用。5. 常见问题与排查技巧实录来自 17 个真实项目的血泪总结5.1 “Segmentation fault (core dumped)” 的 5 种根因与对应解法这是 Fast DDS 用户最常遇到的崩溃但日志往往只有一行segfault毫无头绪。根据我处理过的 17 个案例根因分布如下根因类别占比典型表现快速诊断命令解决方案ABI 版本错配42%ros2 run启动即崩gdb显示std::shared_ptr析构异常readelf -d install/lib/librmw_fastrtps_cpp.sogrep NEEDED内存越界写28%崩溃在History::add_change()且只在 high-frequency topic 下发生valgrind --toolmemcheck --leak-checkfull ros2 run demo_nodes_cpp talker检查HistoryQos的depth是否小于实际消息速率或resource_limits.max_samples是否过小线程竞争15%崩溃随机gdb显示pthread_mutex_lock失败ulimit -c unlimited生成 core dump用gdb ./install/lib/demo_nodes_cpp/talker core确保所有Publisher/Subscriber的QoS中reliability.kind与durability.kind组合合法如RELIABLE TRANSIENT_LOCAL是合法的BEST_EFFORT TRANSIENT_LOCAL是非法的SHM 权限错误10%崩溃在ShmSegment::create()strace显示EPERMls -l /dev/shm和cat /proc/sys/kernel/shmmaxsudo chmod 1777 /dev/shm并 echo kernel.shmmax268435456SSL 证书错误5%启用 Security 后崩溃日志有mbedtls_x509_crt_parse returned -0x2000openssl x509 -in certs/certificate.pem -text -noout确保证书是 PEM 格式且BEGIN CERTIFICATE和END CERTIFICATE之间没有空格或 tab实操心得遇到 segfault第一反应不是重装而是gdb。用gdb --args ros2 run demo_nodes_cpp talker启动然后run崩了之后bt full看栈顶函数。90% 的 case栈顶是eprosima::fastrtps::rtps::History::add_change或eprosima::fastrtps::rtps::ShmSegment::create这就直接锁定了是 History 配置问题还是 SHM 权限问题。5.2 “Topic not discovered” 的网络层排查 checklist当ros2 topic list在机器 A 看不到机器 B 的 topic按此顺序逐项排除基础连通性ping B_IP和nc -u B_IP 7400UDP 端口探测确认 ICMP 和 UDP 都通Multicast 路由ip route show查看是否有224.0.0.0/4 dev eth0没有则sudo ip route add 224.0.0.0/4 dev eth0防火墙规则sudo ufw status verbose确认7400-7402/udp端口是ALLOW初始 Peer 配置在 A 的 XML 里builtininitialPeersList必须包含B_IP格式为locatorudpv4addressB_IP/addressport7400/port/udpv4/locatorDomain ID 一致性ros2 param get /talker domain_id和ros2 param get /listener domain_id必须相同默认是0但可通过export ROS_DOMAIN_ID1修改网络接口绑定Fast DDS 默认绑定0.0.0.0但如果机器有多个网卡如eth0,wlan0它可能选错。强制指定在 XML 的rtpsbuiltininitialPeersList里address写成B_ETH0_IP并在rtpssendSocketBufferSize下加interfaceWhiteListitemeth0/item/interfaceWhiteList。我曾在一个工厂现场A 和 B 都连着同一个交换机ping通nc也通但就是发现不了。最后发现是交换机开启了 IGMP Snooping把 multicast 流量给过滤了。关掉 IGMP Snooping或者把 Fast DDS 改成 unicast 模式builtindiscovery_configuse_SIMPLE_RTPS_WITH_UNICAST问题立解。5.3 “High CPU usage” 的 3 个反直觉优化点Fast DDS CPU 高通常不是 bug而是配置不当。三个最易忽略的优化点heartbeat_period不是越小越好默认2秒有人为了“更快发现故障”改成100ms。结果 CPU 直接飙到 40%。因为 heartbeat 是周期性广播频率翻 20 倍网络负载和 CPU 处理都线性增长。正确做法保持heartbeat_period2把nak_response_delay100毫秒这样 Receiver 一丢包100ms 内就发NACK无需等下一个 heartbeat。history.depth的隐式内存开销设depth1000你以为只占 1000 条消息内存错。Fast DDS 会为每个 Writer 预分配depth * sizeof(CacheChange_t)而CacheChange_t在 64 位系统下是 128 字节1000 条就是 128KB。如果 topic 有 50 个就是 6.4MB。更可怕的是CacheChange_t里有个serializedPayload指针它指向的序列化数据内存是另外分配的。所以总内存 depth * (sizeof(CacheChange_t) max_message_size)。我的建议depth只设为expected_max_out_of_order比如你的系统最多乱序 3 条就设depth3。thread_priorities的误导性文档说可以设SCHED_FIFO