ROS2分层详解及足式机器人QoS配置

ROS2分层详解及足式机器人QoS配置 一、ROS2完整分层ROS2的客户端库统称RCL (ROS Client Library)是 ROS2 最核心底层 C 语言库所有高级语言接口rclcpp、rclpy、rcljava…全部基于 RCL 封装。RCL自底向上的分层顺序 为DDS 层 →RMW 层 → RCL 核心层 → rcl 工具层 → 语言绑定层(rclcpp/rclpy) → 上层功能组件层→ 用户应用层。下面分别讲解各层的定义、职责与作用。1. DDS 层1DDS定义DDS即数据分发服务DDSData Distribution Service是由对象管理组织OMG制定的一项面向机器对机器M2M的分布式实时通信中间件标准规范。它采用以数据为中心的发布/订阅Pub-Sub体系架构提供高性能、可靠性、可扩展性及丰富的服务质量QoS策略。如果看完上面的官方定义还是不理解DDS。请看下面人话版DDS解释。想象一个聊天室场景ROS1是中心服务器roscore所有消息都发给 mastermaster 再转发给其他人。一旦 master 挂掉整个系统直接瘫痪。ROS2 DDS无中心点对点广播。每个节点自己维护和其他节点的连接没有总管。节点 A 发话题感兴趣的节点 B、C 直接接收不需要中间人。2DDS职责RCL并不实现通信通信完全依赖 DDSDDS 负责干这些脏活网络收发数据包UDP 为主也支持 TCP序列化 / 反序列化CDR 标准二进制序列化节点互相发现对方发现谁在线、有什么话题数据包重传、丢包处理、消息缓存、时序3常见DDS类型Fast‑DDSCyclone DDSOpenDDSZenoh‑DDS其中Fast‑DDS是ROS2 默认使用的DDS类型。Fast‑DDS功能多、生态完善、适合 PC、原型、复杂机器人。而Cyclone‑DDS则是一款极简轻量化的DDS。它具有低占用、抖动小的特点。适合嵌入式、硬实时项目使用。2. RMW层1RMW定义RMW即ROS Middle‑Ware是 ROS2 最重要的DDS 硬件抽象层。2RMW层的作用R层用于屏蔽不同DDS厂商 API差异向上提供一套统一C风格接口。不同DDS原生API完全不一样rmw 做一层适配器把 Fast‑DDS / Cyclone‑DDS 等DDS的API统一包装成 rmw_* 函数。更换 DDS只需要切换rmw实现上层 RCL 代码完全不用改动。3RMW 对外核心接口模块rmw_node_tDDS 参与者 (Participant) 封装rmw_publisher_t发布者rmw_subscription_t订阅者rmw_service_t/rmw_client_t服务通信rmw_action_*动作通信底层等待集 wait‑set、事件回调、话题查询、网络配置、QoS 配置3. RCL核心层RCL核心层用纯C实现是ROS2 所有客户端库的根基。RCL核心层的总体职责是处理 ROS2 语义不关心底层 DDS它依靠调用RMW层来完成通信。RCL内部可再次细分成几个内部模块1rcl_node 节点模块主要负责初始化节点、节点名称、命名空间创建底层 rmw_node_t管理节点内所有发布者、订阅者、服务、定时器节点默认参数服务、节点日志名称2rcl_publisher 发布者校验话题名称、话题命名空间合法性封装 rmw_publisher消息类型支持、QoS 参数校验rcl_publish()消息发送接口3rcl_subscription 订阅者话题过滤、回调函数注册接收消息、内存管理消息回调调度4rcl_timer 定时器rcl_timer 定时器是ROS2的软定时器。它基于单调时钟Steady Clock依靠wait‑set等待定时事件触发回调。它独立于DDS纯 RCL 实现。5rcl_service / rcl_client 服务端 / 客户端请求‑应答通信封装封装 rmw service管理请求 ID、响应匹配。6rcl_action 动作rcl_action主要用于长时间任务例如导航、机械臂运动等具备目标、反馈、取消、返回执行结果的任务。rcl_action内部封装5个内置话题goal、cancel、result、feedback、status。7rcl_clock 时钟系统ROS2有三种时钟系统时钟System Clock、单调时钟Steady Clock、仿真时钟 (Simulation Time)。所有定时器、时间戳、超时全部由 rcl_clock 统一管理。8rcl_logging 日志层将日志等级 (Debug/Info/Warn/Error/Fatal)输出至控制台、文件、或rosout话题。9rcl_parameters 参数系统负责管理节点参数、参数变更回调、参数服务。10rcl_wait 等待集 wait‑setROS2事件多路复用器可以同时等待订阅消息、定时器、服务请求、事件。相当于ROS2的事件循环内核它是spin自旋的底层实现。4. RCL工具层rcl‑utils 家族rcl‑utils属于底层工具依赖为RCL提供以下基础能力 。rcl_yaml_param_parseryaml 参数文件解析rcl_logging日志后端rcl_time时间、Duration、Time结构体。rcl_allocator内存分配器可以自定义内存管理嵌入式零拷贝。rcl_error_handlingROS2 异常错误码机制rcutils ROS2 C 工具库字符串、哈希、文件、环境变量、原子变量。它是是所有底层库中最基础的C工具库。5. 语言绑定层Client LibraryRCL是纯C接口对开发者不友好于是针对每种语言封装上层 API。rclcppC 封装面向对象Node、Publisher、Subscription、SpinrclpyPython 绑定通过 ctypes 调用 C的RCL接口rcljava、rclgo、rclrust、rclc嵌入式 C 专用其中rclcpp还额外增加以下能力。智能指针、回调函数、Lambda 回调多线程自旋器Multi‑Threaded spinner组件、节点基类、生命周期节点封装6. 上层功能组件层ROS2 Stack上层功能组件主要包括基于 rclcpp 搭建出来的各种功能包rclcpp_lifecycle生命周期节点tf2、nav2、rqt、rviz2、rosbag2各种传感器驱动、控制器7. 用户应用层用户自己编写的业务节点程序。8. ROS2 完整层级图9. 创建与发现调用链路每个 Publisher / Subscription 只做一次1用户 create_publisher / create_subscription2rclcpp 调用 rcl_publisher_init / rcl_subscription_init3RCL解析并 remap 话题名4RCL调用 rmw_create_publisher / rmw_create_subscription此时把 QoS 交给 rmw并记下 actual_qos5RMW 创建 DDS DataWriter / DataReader6DDS 发现SPDP参与者 SEDP端点。用户数据常用单播组播 UDP 主要用于发现7匹配时做 QoS 兼容检查Reliability / Durability / Deadline / Liveliness 等。不兼容则永远收不到不会在 publish 时报错8 匹配成功后发布端 History 开始为该订阅者服务。TRANSIENT_LOCAL 还会给晚到订阅者补历史。10. 一次跨进程发布用户调用 publish 之后1用户代码publisher-publish(msg) 或 publish(std::unique_ptr)、publish 已序列化消息、loaned message2rclcpp::Publisher::publish()若开启 intra-process先把消息交给 IntraProcessManager同进程订阅走旁路见下文第11点仍有跨进程订阅时继续走下面的 rcl3rcl_publish()只做publisher 句柄有效msg ! NULL 不做话题名检查、header.stamp 填写、QoS 校验 然后调用 rmw_publish()4rmw_publish()rmw 适配器如 rmw_fastrtps_cpp / rmw_cyclonedds_cpp5序列化常见发生在 rmw ROSIDL type support不是“rcl 序列化”ROS 消息 → CDR 字节流 loaned / 零拷贝时可跳过这一步6调用 DDS 原生 write如 DataWriter::write / dds_writeDDS 在这里执行 History / Depth / Lifespan / ReliabilityKEEP_LAST(n)旧样本被挤掉Lifespan过期样本丢弃RELIABLE记录需重传的样本source_timestamp 通常在此时打上不是 msg.header.stamp7传输本机共享内存 / localhost不一定走网卡跨机多为 UDP 单播不是默认组播发业务数据RELIABLEACK / 重传由 DDS 完成11. 一次跨进程接收到用户回调为止ROS 2 是 wait take不是中间件把消息推进回调。1远端 DDS DataReader 收到数据写入 Reader HistoryBEST_EFFORT 丢了就没了RELIABLE 会等重传2rmw 感知“有数据”常见实现DDS listener / 内部 waitset → 把就绪状态打到 guard condition 或 fd 上。 消息此时仍在中间件队列里尚未交给用户。3rclcpp Executor 正在 rcl_wait(wait_set) 上阻塞wait-set 里有subscription、timer、service、guard condition、intra-process waitable…4wait-set 就绪rcl_wait() 返回RCL不执行任何用户回调5Executor 发现该 subscription 就绪调用 rcl_take() → rmw_take() / rmw_take_with_info()6rmw_take从 DDS History 取出样本并反序列化CDR → ROS 消息 同时填 MessageInfosource_timestamp、received_timestamp、publisher GID 等7rclcpp 把消息交给 Subscription 的回调包装器类型擦除 → AnySubscriptionCallback8用户注册的回调被调用例如 lambda / std::bind / 成员函数11. 三条旁路1 Intra-process同进程publish()→ IntraProcessManager 把消息放入同进程订阅的 ring buffer→ 触发该订阅的 Waitable / guard condition→ Executor 被唤醒→ take_data() 从 buffer 取消息可零拷贝 unique_ptr/shared_ptr→ 用户回调对同进程订阅而言不走 CDR、不走 DDS。2Loaned / 零拷贝borrow_loaned_message() → 用户填数据 → publish_loaned_message()→ rmw / DDS 共享内存直接写→ 订阅端 loan 取出中间有无序列化取决于 RMW 是否 can_loan_messages。3非 DDS 的 rmwrmw_publish 之后不一定是 DDS。例如 rmw_zenoh 走 Zenoh传输、发现、QoS 语义随实现变化。上述第10点中的57、第11点中的1只对 DDS RMW 成立。二、QoS详解ROS2底层通信不靠自己写 UDP/TCPDDS 是底层通信底座QoS 是 DDS 的配置开关。QoS即服务质量Quality of Service它是指网络满足给定业务合约的几率或在许多情况下非正式地指分组在网络中两点间通过的几率。QoS是一种控制机制它提供了针对不同用户或者不同数据流采用相应不同的优先级或者是根据应用程序的要求保证数据流的性能达到一定的水准。上述是Qos的官方定义如果没接触过QoS不是很好理解。QoS简单理解就是给你的话题通信设置一套 “通信规则”告诉 DDS 遇到各种网络情况该怎么干活。同样一个话题发布者和订阅者可以设置不一样 QoS。必须两边 QoS 配置互相兼容才能连上收得到消息。不匹配就会出现节点都启动了但是收不到任何数据。1. 五个最常用 QoS 策略Default默认适合普通业务数据丢包可以接受不保存历史消息。SensorData传感器专用摄像头、激光雷达点云。只关心最新一帧旧消息直接扔掉不重传丢包。传感器数据过时就没用了老数据传过来也没有意义。Services服务调用可靠传输必须保证消息送达。Parameters参数通信SystemDefault2. QoS中的六个参数1reliabilityRELIABLE可靠。消息必须送达丢包就自动重传。适合指令、状态不能丢。代价网络差的时候会有延迟。BEST_EFFORT尽力而为。不重传来了就收丢了就算。适合高频传感器数据更新飞快老帧没用。必须注意兼容性规则发布 BEST_EFFORT订阅 RELIABLE → 不兼容收不到消息 两边必须匹配。2historyKEEP_LAST只保存最近 N 条消息depth 设置 N。队列满了旧消息直接丢掉。绝大多数场景用这个。depth队列深度缓存多少条消息。比如 depth10缓冲区最多存 10 条。发布太快订阅处理不过来超过 depth 就丢旧消息。KEEP_ALL保存全部历史消息内存会暴涨慎用。3durability持久性的意思是控制新上线的订阅者能不能收到发布者之前已经发过的老消息。VOLATILE默认。后来的订阅者只能收到订阅之后新发的消息不给历史消息。TRANSIENT_LOCAL发布节点缓存消息后面才启动的订阅者上线立刻收到之前发布的数据。durability的典型用途ROS2的latch话题对应 ROS1 latchedtrue比如地图话题地图只发一次后面启动的导航节点也要拿到地图。4deadline期望多久来一条消息比如 500ms。如果超过时间没收到会触发回调通知用来检测节点卡死。5Lifespan从发布时间开始计时超过 Lifespan这条样本作废不投递给订阅者历史缓存里也会清掉。它回答的是“现在拿到的还该不该用”6LeaseLease是允许多久听不到活性声明就把这个发布者判死。它回答的是“控制程序是不是挂了 / 卡死了”3. rclcpp C 配置 QoS例子#includechrono#includememory#includestring#includerclcpp/rclcpp.hpp#includestd_msgs/msg/string.hppusingnamespacestd::chrono_literals;classDemoNode:publicrclcpp::Node{public:DemoNode():Node(qos_demo_node){// 1. 默认 QoSRELIABLE KEEP_LAST depth10autoqos_defaultrclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default),rmw_qos_profile_default);pub_default_this-create_publisherstd_msgs::msg::String(topic_default,qos_default);sub_default_this-create_subscriptionstd_msgs::msg::String(topic_default,qos_default,[this](conststd_msgs::msg::String::SharedPtr msg){RCLCPP_INFO(this-get_logger(),[default] recv: %s,msg-data.c_str());});// 2. 传感器 QoSBEST_EFFORT KEEP_LAST depth5// from_rmw() 只提供 history/depth完整策略来自第二个参数autoqos_sensorrclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_sensor_data),rmw_qos_profile_sensor_data);pub_sensor_this-create_publisherstd_msgs::msg::String(topic_sensor,qos_sensor);sub_sensor_this-create_subscriptionstd_msgs::msg::String(topic_sensor,qos_sensor,[this](conststd_msgs::msg::String::SharedPtr msg){RCLCPP_INFO(this-get_logger(),[sensor] recv: %s,msg-data.c_str());});timer_this-create_wall_timer(500ms,[this](){count_;automsg_defaultstd_msgs::msg::String();msg_default.datadefault #std::to_string(count_);pub_default_-publish(msg_default);automsg_sensorstd_msgs::msg::String();msg_sensor.datasensor #std::to_string(count_);pub_sensor_-publish(msg_sensor);});}private:size_t count_{0};rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisherstd_msgs::msg::String::SharedPtr pub_default_;rclcpp::Publisherstd_msgs::msg::String::SharedPtr pub_sensor_;rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr sub_default_;rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr sub_sensor_;};intmain(intargc,char**argv){rclcpp::init(argc,argv);rclcpp::spin(std::make_sharedDemoNode());rclcpp::shutdown();return0;}三、足式机器人专属QoS配置足式机器人核心痛点1运动控制硬实时2点云大数据带宽压力3状态反馈低延迟可靠上报。三类业务流量时延、数据包大小、丢失容忍度、发送频率完全不一样。因此需做差异化 QoS 策略下文给出参数配置方案。1. 三类业务完整 QoS 参数总表业务ReliabilityDurabilityHistory / DepthDeadlineLifespanLease1. 运动控制硬实时关节指令 / IMU200–1000 HzBEST_EFFORTVOLATILEKEEP_LAST11 个控制周期如 2 ms 500 Hz1–2 个周期3–5 个周期2. 点云大数据LiDAR / 深度10–20 HzBEST_EFFORTVOLATILEKEEP_LAST11 个扫描周期如 100 ms1 个扫描周期2–3 个周期3. 状态反馈可靠上报模式 / 电池 / 故障20–50 HzRELIABLETRANSIENT_LOCALKEEP_LAST1状态或10事件1 个上报周期如 50 ms可关或 3–5 周期200–500 ms三类业务的配法可以压成一句话控制丢旧保新、点云宁丢不堵、状态可靠锁存。运动过期指令比丢包更危险所以不重传、不排队、迟到包用 Lifespan 作废。500–1000 Hz 电机闭环应走共享内存 / EtherCAT不要指望 DDS。点云单帧数 MBRELIABLE 会占满总线Depth 大于 1 会把内存和延迟一起撑爆。只处理最新扫描。状态模式和急停不能丢晚启动的节点还要立刻拿到当前值所以 RELIABLE TRANSIENT_LOCAL。高频关节角给控制用时归第 1 类不要走 RELIABLE。2. rclcpp 写法// 1 / 2 周期数字按表改rclcpp::QoS(1).best_effort().durability_volatile().deadline(2ms).lifespan(4ms);// 3rclcpp::QoS(1).reliable().transient_local().deadline(50ms);// 4rclcpp::QoS(1).reliable().transient_local();订阅端 Reliability 不能高于发布端用 RELIABLE 去订 BEST_EFFORT 的雷达会完全收不到点云。