news 2026/8/20 12:40:33

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ROS2分层详解及足式机器人QoS配置

一、ROS2完整分层

ROS2的客户端库统称RCL (ROS Client Library),是 ROS2 最核心底层 C 语言库,所有高级语言接口(rclcpp、rclpy、rcljava…)全部基于 RCL 封装。RCL自底向上的分层顺序 为:DDS 层 →RMW 层 → RCL 核心层 → rcl 工具层 → 语言绑定层(rclcpp/rclpy) → 上层功能组件层→ 用户应用层。下面分别讲解各层的定义、职责与作用。

1. DDS 层

(1)DDS定义

DDS,即数据分发服务(DDS,Data Distribution Service)是由对象管理组织(OMG)制定的一项面向机器对机器(M2M)的分布式实时通信中间件标准规范。它采用以数据为中心的发布/订阅(Pub-Sub)体系架构,提供高性能、可靠性、可扩展性及丰富的服务质量(QoS)策略。
如果看完上面的官方定义,还是不理解DDS。请看下面人话版DDS解释。
想象一个聊天室场景:

  • ROS1:是中心服务器(roscore),所有消息都发给 master,master 再转发给其他人。一旦 master 挂掉,整个系统直接瘫痪。
  • ROS2 DDS:无中心,点对点广播。每个节点自己维护和其他节点的连接,没有总管。节点 A 发话题,感兴趣的节点 B、C 直接接收,不需要中间人。

(2)DDS职责

RCL并不实现通信,通信完全依赖 DDS,DDS 负责干这些脏活:

  • 网络收发数据包(UDP 为主,也支持 TCP)
  • 序列化 / 反序列化(CDR 标准二进制序列化)
  • 节点互相发现对方(发现谁在线、有什么话题)
  • 数据包重传、丢包处理、消息缓存、时序

(3)常见DDS类型

  • Fast‑DDS
  • Cyclone DDS
  • OpenDDS
  • Zenoh‑DDS
    其中,Fast‑DDS是ROS2 默认使用的DDS类型。Fast‑DDS功能多、生态完善、适合 PC、原型、复杂机器人。而Cyclone‑DDS则是一款极简轻量化的DDS。它具有低占用、抖动小的特点。适合嵌入式、硬实时项目使用。

2. RMW层

(1)RMW定义

RMW,即ROS Middle‑Ware,是 ROS2 最重要的DDS 硬件抽象层。

(2)RMW层的作用

RMW层用于屏蔽不同DDS厂商 API差异,向上提供一套统一C风格接口。不同DDS原生API完全不一样,rmw 做一层适配器,把 Fast‑DDS / Cyclone‑DDS 等DDS的API统一包装成 rmw_* 函数。更换 DDS只需要切换rmw实现,上层 RCL 代码完全不用改动。

(3)RMW 对外核心接口模块

  • rmw_node_t:DDS 参与者 (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内部可再次细分成几个内部模块

(1)rcl_node 节点模块,主要负责

  • 初始化节点、节点名称、命名空间
  • 创建底层 rmw_node_t
  • 管理节点内所有发布者、订阅者、服务、定时器
  • 节点默认参数服务、节点日志名称

(2)rcl_publisher 发布者

  • 校验话题名称、话题命名空间合法性
  • 封装 rmw_publisher
  • 消息类型支持、QoS 参数校验
  • rcl_publish()消息发送接口

(3)rcl_subscription 订阅者

  • 话题过滤、回调函数注册
  • 接收消息、内存管理
  • 消息回调调度

(4)rcl_timer 定时器

rcl_timer 定时器是ROS2的软定时器。它基于单调时钟(Steady Clock),依靠wait‑set等待定时事件,触发回调。它独立于DDS,纯 RCL 实现。

(5)rcl_service / rcl_client 服务端 / 客户端

请求‑应答通信封装,封装 rmw service,管理请求 ID、响应匹配。

(6)rcl_action 动作

rcl_action主要用于长时间任务,例如,导航、机械臂运动等具备目标、反馈、取消、返回执行结果的任务。rcl_action内部封装5个内置话题:goal、cancel、result、feedback、status。

(7)rcl_clock 时钟系统

ROS2有三种时钟:系统时钟(System Clock)、单调时钟(Steady Clock)、仿真时钟 (Simulation Time)。所有定时器、时间戳、超时全部由 rcl_clock 统一管理。

(8)rcl_logging 日志层

将日志等级 (Debug/Info/Warn/Error/Fatal)输出至控制台、文件、或rosout话题。

(9)rcl_parameters 参数系统

负责管理节点参数、参数变更回调、参数服务。

(10)rcl_wait 等待集 wait‑set

ROS2事件多路复用器,可以同时等待:订阅消息、定时器、服务请求、事件。相当于ROS2的事件循环内核,它是spin自旋的底层实现。

4. RCL工具层(rcl‑utils 家族)

rcl‑utils属于底层工具依赖,为RCL提供以下基础能力 。

  • rcl_yaml_param_parser:yaml 参数文件解析
  • rcl_logging日志后端
  • rcl_time时间、Duration、Time结构体。
  • rcl_allocator内存分配器,可以自定义内存管理(嵌入式零拷贝)。
  • rcl_error_handling:ROS2 异常错误码机制
  • rcutils ROS2 C 工具库:字符串、哈希、文件、环境变量、原子变量。它是是所有底层库中最基础的C工具库。

5. 语言绑定层(Client Library)

RCL是纯C接口,对开发者不友好;于是针对每种语言封装上层 API。

  • rclcpp:C++ 封装,面向对象:Node、Publisher、Subscription、Spin
  • rclpy:Python 绑定,通过 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_subscription

(2)rclcpp 调用 rcl_publisher_init / rcl_subscription_init

(3)RCL解析并 remap 话题名

(4)RCL调用 rmw_create_publisher / rmw_create_subscription此时把 QoS 交给 rmw,并记下 actual_qos

(5)RMW 创建 DDS DataWriter / DataReader

(6)DDS 发现: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 message

(2)rclcpp::Publisher::publish()

  • 若开启 intra-process:先把消息交给 IntraProcessManager(同进程订阅走旁路,见下文第11点)
  • 仍有跨进程订阅时,继续走下面的 rcl

(3)rcl_publish()

只做:publisher 句柄有效?msg != NULL? 不做:话题名检查、header.stamp 填写、QoS 校验 然后调用 rmw_publish()

(4)rmw_publish()

rmw 适配器(如 rmw_fastrtps_cpp / rmw_cyclonedds_cpp)

(5)序列化(常见发生在 rmw + ROSIDL type support,不是“rcl 序列化”)

ROS 消息 → CDR 字节流 loaned / 零拷贝时可跳过这一步

(6)调用 DDS 原生 write(如 DataWriter::write / dds_write)

DDS 在这里执行 History / Depth / Lifespan / Reliability:

  • KEEP_LAST(n):旧样本被挤掉
  • Lifespan:过期样本丢弃
  • RELIABLE:记录需重传的样本
  • source_timestamp 通常在此时打上(不是 msg.header.stamp)

(7)传输

  • 本机:共享内存 / localhost,不一定走网卡
  • 跨机:多为 UDP 单播(不是默认组播发业务数据)
  • RELIABLE:ACK / 重传由 DDS 完成

11. 一次跨进程接收(到用户回调为止)

ROS 2 是 wait + take,不是中间件把消息推进回调。

(1)远端 DDS DataReader 收到数据,写入 Reader History

(BEST_EFFORT 丢了就没了;RELIABLE 会等重传)

(2)rmw 感知“有数据”

常见实现:DDS listener / 内部 waitset → 把就绪状态打到 guard condition 或 fd 上。 消息此时仍在中间件队列里,尚未交给用户。

(3)rclcpp Executor 正在 rcl_wait(wait_set) 上阻塞

wait-set 里有:subscription、timer、service、guard condition、intra-process waitable…

(4)wait-set 就绪,rcl_wait() 返回

RCL不执行任何用户回调

(5)Executor 发现该 subscription 就绪

调用 rcl_take() → rmw_take() / rmw_take_with_info()

(6)rmw_take:从 DDS History 取出样本并反序列化

CDR → ROS 消息 同时填 MessageInfo:source_timestamp、received_timestamp、publisher GID 等

(7)rclcpp 把消息交给 Subscription 的回调包装器

(类型擦除 → AnySubscriptionCallback)

(8)用户注册的回调被调用

例如 lambda / std::bind / 成员函数

11. 三条旁路

(1) Intra-process(同进程)

publish()
→ IntraProcessManager 把消息放入同进程订阅的 ring buffer
→ 触发该订阅的 Waitable / guard condition
→ Executor 被唤醒
→ take_data() 从 buffer 取消息(可零拷贝 unique_ptr/shared_ptr)
→ 用户回调
对同进程订阅而言,不走 CDR、不走 DDS。

(2)Loaned / 零拷贝

borrow_loaned_message() → 用户填数据 → publish_loaned_message()
→ rmw / DDS 共享内存直接写
→ 订阅端 loan 取出
中间有无序列化,取决于 RMW 是否 can_loan_messages。

(3)非 DDS 的 rmw

rmw_publish 之后不一定是 DDS。例如 rmw_zenoh 走 Zenoh;传输、发现、QoS 语义随实现变化。上述第10点中的(5)~(7)、第11点中的(1)只对 DDS RMW 成立。

二、QoS详解

ROS2底层通信不靠自己写 UDP/TCP,DDS 是底层通信底座,QoS 是 DDS 的配置开关。QoS,即服务质量(Quality of Service),它是指网络满足给定业务合约的几率;或在许多情况下,非正式地指分组在网络中两点间通过的几率。QoS是一种控制机制,它提供了针对不同用户或者不同数据流采用相应不同的优先级,或者是根据应用程序的要求,保证数据流的性能达到一定的水准。
上述是Qos的官方定义,如果没接触过QoS,不是很好理解。QoS简单理解就是,给你的话题通信设置一套 “通信规则”,告诉 DDS 遇到各种网络情况该怎么干活。
同样一个话题,发布者和订阅者可以设置不一样 QoS。必须两边 QoS 配置互相兼容,才能连上,收得到消息。不匹配就会出现:节点都启动了,但是收不到任何数据。

1. 五个最常用 QoS 策略

  • Default(默认)
    适合普通业务数据;丢包可以接受,不保存历史消息。
  • SensorData(传感器专用)
    摄像头、激光雷达点云。只关心最新一帧,旧消息直接扔掉,不重传丢包。传感器数据过时就没用了,老数据传过来也没有意义。
  • Services(服务调用)
    可靠传输,必须保证消息送达。
  • Parameters(参数通信)
  • SystemDefault

2. QoS中的六个参数

(1)reliability

  • RELIABLE:可靠。消息必须送达,丢包就自动重传。适合指令、状态,不能丢。代价:网络差的时候会有延迟。
  • BEST_EFFORT:尽力而为。不重传,来了就收,丢了就算。适合高频传感器,数据更新飞快,老帧没用。
    必须注意兼容性规则:发布 BEST_EFFORT,订阅 RELIABLE → 不兼容,收不到消息! 两边必须匹配。

(2)history

  • KEEP_LAST:只保存最近 N 条消息(depth 设置 N)。队列满了,旧消息直接丢掉。绝大多数场景用这个。
    depth:队列深度,缓存多少条消息。比如 depth=10,缓冲区最多存 10 条。发布太快订阅处理不过来,超过 depth 就丢旧消息。
  • KEEP_ALL:保存全部历史消息,内存会暴涨,慎用。

(3)durability

持久性的意思是,控制新上线的订阅者,能不能收到发布者之前已经发过的老消息。

  • VOLATILE:默认。后来的订阅者只能收到订阅之后新发的消息,不给历史消息。
  • TRANSIENT_LOCAL:发布节点缓存消息,后面才启动的订阅者,上线立刻收到之前发布的数据。
    durability的典型用途:ROS2的latch话题(对应 ROS1 latched=true),比如地图话题,地图只发一次,后面启动的导航节点也要拿到地图。

(4)deadline

期望多久来一条消息,比如 500ms。如果超过时间没收到,会触发回调通知,用来检测节点卡死。

(5)Lifespan

从发布时间开始计时,超过 Lifespan,这条样本作废:不投递给订阅者,历史缓存里也会清掉。它回答的是:“现在拿到的还该不该用?”

(6)Lease

Lease是允许多久听不到活性声明,就把这个发布者判死。它回答的是:“控制程序是不是挂了 / 卡死了?”

3. rclcpp C++ 配置 QoS例子

#include<chrono>#include<memory>#include<string>#include<rclcpp/rclcpp.hpp>#include<std_msgs/msg/string.hpp>usingnamespacestd::chrono_literals;classDemoNode:publicrclcpp::Node{public:DemoNode():Node("qos_demo_node"){// 1. 默认 QoS:RELIABLE + KEEP_LAST depth=10autoqos_default=rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default),rmw_qos_profile_default);pub_default_=this->create_publisher<std_msgs::msg::String>("topic_default",qos_default);sub_default_=this->create_subscription<std_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. 传感器 QoS:BEST_EFFORT + KEEP_LAST depth=5// from_rmw() 只提供 history/depth;完整策略来自第二个参数autoqos_sensor=rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_sensor_data),rmw_qos_profile_sensor_data);pub_sensor_=this->create_publisher<std_msgs::msg::String>("topic_sensor",qos_sensor);sub_sensor_=this->create_subscription<std_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_default=std_msgs::msg::String();msg_default.data="default #"+std::to_string(count_);pub_default_->publish(msg_default);automsg_sensor=std_msgs::msg::String();msg_sensor.data="sensor #"+std::to_string(count_);pub_sensor_->publish(msg_sensor);});}private:size_t count_{0};rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_default_;rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_sensor_;rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_default_;rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_sensor_;};intmain(intargc,char**argv){rclcpp::init(argc,argv);rclcpp::spin(std::make_shared<DemoNode>());rclcpp::shutdown();return0;}

三、足式机器人专属QoS配置

足式机器人核心痛点:1)运动控制硬实时;2)点云大数据带宽压力;3)状态反馈低延迟可靠上报。三类业务流量时延、数据包大小、丢失容忍度、发送频率完全不一样。因此需做差异化 QoS 策略,下文给出参数配置方案。

1. 三类业务完整 QoS 参数总表

业务ReliabilityDurabilityHistory / DepthDeadlineLifespanLease
1. 运动控制硬实时(关节指令 / IMU,200–1000 Hz)BEST_EFFORTVOLATILEKEEP_LAST11 个控制周期,如 2 ms @ 500 Hz1–2 个周期3–5 个周期
2. 点云大数据(LiDAR / 深度,10–20 Hz)BEST_EFFORTVOLATILEKEEP_LAST11 个扫描周期,如 100 ms1 个扫描周期2–3 个周期
3. 状态反馈可靠上报(模式 / 电池 / 故障,20–50 Hz)RELIABLETRANSIENT_LOCALKEEP_LAST1(状态)或10(事件)1 个上报周期,如 50 ms可关,或 3–5 周期200–500 ms

三类业务的配法可以压成一句话:控制丢旧保新、点云宁丢不堵、状态可靠锁存。

  • 运动:过期指令比丢包更危险,所以不重传、不排队、迟到包用 Lifespan 作废。500–1000 Hz 电机闭环应走共享内存 / EtherCAT,不要指望 DDS。
  • 点云:单帧数 MB,RELIABLE 会占满总线;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 的雷达,会完全收不到点云。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 12:33:20

NVIDIA DriveOS的Suspend to RAM/SC7完整流程

官方资料&#xff1a;Suspend to RAM/SC7 — NVIDIA DriveOS 7.0.3 Linux SDK Developer Guide 最新在项目中加入了sc7功能&#xff0c;用户态与内核态会通过netlink进行通信&#xff0c;下面简单介绍下进入和退出sc7模式的流程 SMGR (用户态) Kernel (te…

作者头像 李华
网站建设 2026/8/20 12:27:43

VectorAtlas:80KB免费SVG世界地图的快速集成与交互实现指南

1. 先搞清楚 VectorAtlas 到底解决了什么地图素材问题 如果你在做数据可视化、后台管理面板或者需要展示全球数据的项目&#xff0c;大概率会遇到一个头疼的问题&#xff1a;找一个合适的、免费的、能直接编程操作的世界地图素材。网上很多地图要么是图片格式没法交互&#xf…

作者头像 李华
网站建设 2026/8/20 12:26:56

深度背书:一所OSSD学校的硬核数据拆解

五年4486封offer、QS前百97%&#xff1a;这所加拿大学校凭什么&#xff1f; 在OSSD赛道里&#xff0c;“100%升学率”“人均X封名校offer"已经是标配话术了。但真正值得深挖的&#xff0c;是数据背后的含金量——是精挑细选几十个好学生堆出来的案例&#xff0c;还是大量样…

作者头像 李华