rmw 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rmw
版本:6.1.2(Humble),子包 2 个,构建类型 ament_cmake,语言 C,许可证 Apache 2.0。
rmw(ROS Middleware Interface)是 ROS 2 中间件抽象层:在 RCL 与具体 DDS 实现之间定义统一的 C API 契约。rcl / rclcpp / rclpy 只依赖 rmw.h 中的符号名,运行时通过 rmw_implementation 代理库链接到 rmw_cyclonedds_cpp、rmw_fastrtps_cpp 等后端。
本仓库的关键定位:
rmw包提供 头文件 API 声明 + 与实现无关的公共工具实现(校验、QoS 字符串、init 零初始化、graph 辅助结构体等)。rmw_create_publisher、rmw_wait、rmw_init等 核心中间件操作在本仓库仅声明,实现在各rmw_*_cpp包中。
设计文档参考:ROS Middleware Interface
1. 总体认识
1.1 核心职责
| 能力 | 说明 |
|---|---|
| API 契约 | rmw.h 及 50+ 头文件定义 init/node/pub/sub/service/client/wait/graph 等全部 C 接口 |
| 公共类型 | rmw_*_t 句柄、QoS 枚举/结构、GID、message_info、wait 容器 |
| 共享实现 | 20 个 .c 源文件:命名校验、QoS 字符串、init_options 默认值、topic_endpoint_info 等 |
| 构建辅助 | rmw_implementation_cmake 提供 CMake 宏,选择/枚举 RMW 实现 |
| 实现注册 | register_rmw_implementation() 向 ament index 注册 typesupport 资源 |
1.2 在 ROS 2 栈中的位置
| 层级 | 职责边界 |
|---|---|
| rcl | ROS 语义:namespace 展开、remap、timer、wait_set 组装、参数 CLI |
| rmw(本仓库) | 中间件无关的类型与 API 签名;部分校验/工具函数 |
| rmw_implementation | 编译期链接所选后端;运行时 dlopen 或直接链接 |
| rmw_*_cpp | 将 rmw_* 映射到 DDS Participant/DataWriter/DataReader 等 |
1.3 API 声明 vs 实现分离
这是阅读 rmw 源码时最容易混淆的一点:
| 类别 | 位置 | 示例 |
|---|---|---|
| 仅声明(实现在后端) | include/rmw/rmw.h、init.h、init_options.h、event.h |
rmw_init、rmw_create_publisher、rmw_wait、rmw_take |
| 本仓库实现 | src/*.c(20 文件) |
rmw_validate_full_topic_name、rmw_qos_reliability_policy_to_str、rmw_get_zero_initialized_init_options |
| 头文件 + 零/辅助实现 | types.h + src/types.c |
rmw_get_zero_initialized_message_info |
CMakeLists.txt 中 add_library(rmw ...) 只编译 20 个 .c 文件,不包含任何 DDS 调用。链接 librmw.so 的应用若直接调用 rmw_init 会得到 undefined symbol,必须通过 rmw_implementation 或具体后端包提供实现。
2. 子包结构
1 | rmw/ |
| 包 | 版本 | 职责 |
|---|---|---|
rmw |
6.1.2 | API 头文件 + 公共 C 工具库 |
rmw_implementation_cmake |
— | 构建时枚举/选择 RMW 实现 |
2.1 依赖关系(rmw/package.xml)
1 | rmw |
运行时 不 依赖任何 DDS 库;DDS 依赖在各 rmw_*_cpp 包中。
3. 句柄设计模式
几乎所有 RMW 实体采用相同的 type-erased handle 模式:
1 | typedef struct rmw_publisher_s { |
| 字段 | 作用 |
|---|---|
implementation_identifier |
防止混用不同后端的句柄;不匹配时返回 RMW_RET_INCORRECT_RMW_IMPLEMENTATION |
data |
指向后端内部对象(如 CycloneDDS 的 dds_entity_t 封装) |
| 其余字段 | ROS/RMW 层可见的元数据,由创建函数填充 |
同类结构:rmw_node_t、rmw_subscription_t、rmw_service_t、rmw_client_t、rmw_guard_condition_t、rmw_wait_set_t、rmw_event_t。
3.1 分配器辅助(src/allocators.c)
后端实现创建实体时可复用本仓库提供的 句柄内存分配:
rmw_node_allocate()/rmw_node_free()rmw_publisher_allocate()/rmw_publisher_free()- 以及 subscription、service、client、guard_condition、wait_set 的对称函数
底层使用 rcutils 默认分配器,保证各实现一致的句柄布局。
4. 生命周期:init / context / shutdown
4.1 核心类型
1 | // init_options.h — 传入 rmw_init() 的配置 |
4.2 本仓库提供的零初始化
1 | // src/init.c |
4.3 实现侧必须提供的函数(仅声明)
| 函数 | 说明 |
|---|---|
rmw_init_options_init / fini / copy / set_domain_id / set_enclave 等 |
配置 init 选项 |
rmw_init |
初始化 DDS participant 工厂等 |
rmw_shutdown |
关闭 context,释放 impl |
rmw_context_fini |
销毁 context 结构 |
rcl_init() 调用链:rcl_init → rmw_init_options_init → 设置 enclave/domain → rmw_init → 保存 rmw_context_t 到 rcl_context_t。
5. rmw.h 主 API 分组
rmw/include/rmw/rmw.h 是单一入口,按功能可划分为:
| 分组 | 主要 API | 说明 |
|---|---|---|
| 标识 | rmw_get_implementation_identifier、rmw_get_serialization_format |
返回后端名字与序列化格式(如 "cdr") |
| Node | rmw_create_node、rmw_destroy_node、rmw_node_get_graph_guard_condition |
DDS participant 上的逻辑节点 |
| Publisher | create/destroy、rmw_publish、rmw_publish_loaned_message、rmw_publish_serialized_message |
发布路径 |
| Subscription | create/destroy、rmw_take* 系列、rmw_take_sequence |
订阅与批量 take |
| Loaned message | rmw_borrow_loaned_message、rmw_return_loaned_message_from_* |
零拷贝(实现可选) |
| Serialize | rmw_serialize、rmw_deserialize、rmw_get_serialized_message_size |
与 rosidl typesupport 配合 |
| Service/Client | create/destroy、rmw_take_request、rmw_send_response、rmw_send_request、rmw_take_response |
DDS RPC 映射 |
| Wait | rmw_create_wait_set、rmw_wait、rmw_destroy_wait_set |
多路复用等待 |
| Guard condition | rmw_create_guard_condition、rmw_trigger_guard_condition |
唤醒 wait(timer、graph 等) |
| Graph | rmw_get_node_names、rmw_count_publishers/subscribers、rmw_get_gid_for_publisher |
发现与 introspection |
| QoS 查询 | rmw_publisher_get_actual_qos、rmw_subscription_get_actual_qos |
协商后的实际 QoS |
| Liveliness | rmw_node_assert_liveliness、rmw_publisher_assert_liveliness |
手动断言存活 |
| Callback | rmw_subscription_set_on_new_message_callback 等 |
替代 wait 的异步通知(可选) |
| Event | rmw_event_set_callback |
QoS 不兼容、deadline 等事件 |
| 其他 | rmw_compare_gids_equal、rmw_service_server_is_available、rmw_set_log_severity |
工具与日志 |
Graph 相关 API 还分散在独立头文件中(见第 8 节)。
6. QoS 体系
6.1 rmw_qos_profile_t
1 | typedef struct rmw_qos_profile_s { |
时间字段使用 RMW_DURATION_UNSPECIFIED / RMW_DURATION_INFINITE 表示“用实现默认”或“无限”。
6.2 预设 Profile(qos_profiles.h)
| 常量 | 典型用途 |
|---|---|
rmw_qos_profile_sensor_data |
传感器:BEST_EFFORT,depth=5 |
rmw_qos_profile_default |
普通 topic:RELIABLE,depth=10 |
rmw_qos_profile_parameters |
参数服务:RELIABLE,depth=1000 |
rmw_qos_profile_services_default |
Service:RELIABLE |
rmw_qos_profile_parameter_events |
参数事件 |
rmw_qos_profile_system_default |
全部 SYSTEM_DEFAULT |
rmw_qos_profile_unknown |
未知/未设置 |
rcl 层将这些 profile 映射到 rcl_qos_profile_t,再传给 rmw_create_publisher。
6.3 QoS 字符串转换(src/qos_string_conversions.c)
本仓库 完整实现 policy 枚举 ↔ 字符串互转,供 CLI、日志、测试使用:
rmw_qos_reliability_policy_to_str/_from_strrmw_qos_durability_policy_to_str/_from_strrmw_qos_history_policy_to_str/_from_strrmw_qos_liveliness_policy_to_str/_from_strrmw_qos_policy_kind_to_str/rmw_qos_policy_kind_from_str
7. Wait Set 与 rmw_wait 契约
7.1 相关类型
1 | typedef struct rmw_subscriptions_s { |
还有对称的 rmw_services_t、rmw_clients_t、rmw_events_t、rmw_guard_conditions_t。
7.2 rmw_wait 语义(实现在后端)
1 | rmw_ret_t rmw_wait( |
契约要点(摘自 rmw.h 文档):
- 调用前:数组中非 NULL 条目表示要等待的实体;
NULL条目无效。 - 调用后:未触发的实体对应数组项被设为
NULL;触发的保留原指针。 - 返回值:
RMW_RET_OK(有就绪)、RMW_RET_TIMEOUT(超时)、RMW_RET_INVALID_ARGUMENT。 - 线程安全:同一 wait_set 不可并发 wait;同一实体不可被多个 wait_set 并发 wait。
- 所有实体须属于与 wait_set 相同的
rmw_context。
7.3 rcl 层如何使用
1 | rcl_wait_set_t |
8. Graph Introspection API
除 rmw.h 中的 rmw_get_node_names、rmw_count_publishers 外,专用头文件定义更丰富的图 API:
| 头文件 | API | 用途 |
|---|---|---|
get_topic_names_and_types.h |
rmw_get_topic_names_and_types |
全局 topic 列表 |
get_service_names_and_types.h |
rmw_get_service_names_and_types |
全局 service 列表 |
get_node_info_and_types.h |
rmw_get_*_names_and_types_by_node |
按节点查 pub/sub/service/client |
get_topic_endpoint_info.h |
rmw_get_publishers_info_by_topic、rmw_get_subscriptions_info_by_topic |
endpoint 级 QoS/GID |
get_network_flow_endpoints.h |
rmw_publisher_get_network_flow_endpoints |
网络流信息(调试用) |
辅助数据结构在本仓库实现:
rmw_names_and_types_t—src/names_and_types.c(init/fini/check_zero)rmw_topic_endpoint_info_t—src/topic_endpoint_info.c(set topic_type、gid、qos 等)rmw_topic_endpoint_info_array_t—src/topic_endpoint_info_array.c
9. 命名校验(本仓库完整实现)
ROS 2 图命名规则在 RMW 层统一校验,供 rcl 与后端共用:
| 模块 | 文件 | 规则摘要 |
|---|---|---|
| Topic | validate_full_topic_name.c |
必须以 / 开头、不以 / 结尾、仅 [a-zA-Z0-9_/] |
| Namespace | validate_namespace.c |
类似 topic,允许空 namespace |
| Node name | validate_node_name.c |
不含 /,字母数字与 _ |
每个校验函数返回 RMW_RET_OK + validation_result 枚举(非错误码),并提供 *_validation_result_string() 人类可读描述。
示例(topic 校验入口):
1 | rmw_ret_t rmw_validate_full_topic_name( |
10. 事件(Events)
10.1 事件类型(event.h)
| 枚举 | 方向 | 含义 |
|---|---|---|
RMW_EVENT_REQUESTED_QOS_INCOMPATIBLE |
Subscription | 请求 QoS 与 offer 不兼容 |
RMW_EVENT_OFFERED_QOS_INCOMPATIBLE |
Publisher | 提供 QoS 与 request 不兼容 |
RMW_EVENT_LIVELINESS_CHANGED / LOST |
Sub / Pub | 存活策略变化 |
RMW_EVENT_*_DEADLINE_MISSED |
Sub / Pub | 错过 deadline |
RMW_EVENT_MESSAGE_LOST |
Subscription | 消息丢失(实现相关) |
10.2 状态结构(events_statuses/)
incompatible_qos.h、liveliness_changed.h 等定义 DDS 事件计数的 C 结构,与 rmw_take_event(后端实现)配合。
10.3 本仓库实现
rmw_get_zero_initialized_event()—src/event.crmw_event_fini()— 重置为零结构(不释放 impl,impl 由后端管理)
rmw_publisher_event_init、rmw_subscription_event_init、rmw_take_event 等 仅声明。
11. 安全与网络选项
11.1 Security(src/security_options.c)
1 | rmw_security_options_t rmw_get_default_security_options(void); |
与 SROS2 集成:enclave + security_root_path 指向密钥库。具体 DDS Security 插件加载在各后端。
11.2 Localhost / Domain
domain_id.h—RMW_DEFAULT_DOMAIN_ID(0),隔离不同 ROS 域localhost.h—RMW_LOCALHOST_ONLY_DEFAULT等,限制仅本机通信
12. 消息路径辅助类型
| 类型 | 文件 | 说明 |
|---|---|---|
rmw_serialized_message_t |
serialized_message.h |
字节缓冲区 + 分配器 |
rmw_message_info_t |
types.h |
source_timestamp、publication_handle、sequence_number |
rmw_message_sequence_t |
message_sequence.c |
批量 take 的消息数组 |
rmw_request_id_t |
types.h |
Service 关联 writer_guid + sequence_number |
rmw_get_zero_initialized_message_info() 在 src/types.c 实现。
12.1 Loaned Message
API 在 rmw.h 中声明;can_loan_messages 标志由 rmw_create_publisher/subscription 设置。是否支持取决于后端与 typesupport(Fast-DDS 支持较好)。
12.2 Serialize / Deserialize
1 | rmw_ret_t rmw_serialize( |
实现通常委托给 rosidl_typesupport_* 的 CDR 序列化,再经 DDS 发送。
13. 错误处理
error_handling.h 薄封装 rcutils 线程局部错误链:
1 |
|
13.1 返回码(ret_types.h)
| 宏 | 值 | 含义 |
|---|---|---|
RMW_RET_OK |
0 | 成功 |
RMW_RET_ERROR |
1 | 通用错误(查 error string) |
RMW_RET_TIMEOUT |
2 | wait 超时 |
RMW_RET_UNSUPPORTED |
3 | 不支持的操作 |
RMW_RET_BAD_ALLOC |
10 | 内存分配失败 |
RMW_RET_INVALID_ARGUMENT |
11 | 无效参数 |
RMW_RET_INCORRECT_RMW_IMPLEMENTATION |
12 | 句柄与当前后端不匹配 |
src/convert_rcutils_ret_to_rmw_ret.c 提供 rmw_convert_rcutils_ret_to_rmw_ret(),供后端统一转换 rcutils 返回值。
13.2 断言宏(sanity_checks.c / macros.h)
RMW_CHECK_ARGUMENT_FOR_NULL、RMW_CHECK_TYPE_IDENTIFIERS_MATCH 等在后端实现中广泛使用,保证跨实现一致的错误报告。
14. 特性查询(features.h)
1 | typedef enum rmw_feature_e { |
用于上层判断 rmw_message_info_t 中 sequence number 字段是否可靠填充。
15. src/ 模块一览
| 源文件 | 实现的公开 API |
|---|---|
allocators.c |
各 rmw_*_allocate/free、rmw_allocate/free |
convert_rcutils_ret_to_rmw_ret.c |
rcutils → rmw 返回码映射 |
event.c |
event 零初始化、fini |
init.c |
rmw_get_zero_initialized_context |
init_options.c |
rmw_get_zero_initialized_init_options |
message_sequence.c |
message / message_info sequence init/fini |
names_and_types.c |
rmw_names_and_types_* |
network_flow_endpoint.c |
网络 endpoint 零初始化、set address |
network_flow_endpoint_array.c |
endpoint 数组管理 |
publisher_options.c |
rmw_get_default_publisher_options |
subscription_options.c |
rmw_get_default_subscription_options |
subscription_content_filter_options.c |
Content Filter Topic 选项 |
qos_string_conversions.c |
QoS 枚举 ↔ 字符串 |
sanity_checks.c |
string array 零检查 |
security_options.c |
security options 生命周期 |
time.c |
rmw_time_equal、rmw_time_from_nsec 等 |
topic_endpoint_info.c |
endpoint info 字段设置 |
topic_endpoint_info_array.c |
endpoint 数组 |
types.c |
rmw_get_zero_initialized_message_info |
validate_*.c |
topic/namespace/node 名校验 |
16. rmw_implementation_cmake
16.1 枚举可用实现
get_available_rmw_implementations(var):
- 从 ament index 读取
rmw_typesupport资源(排除rmw_implementation代理包本身) - 可通过环境变量
RMW_IMPLEMENTATIONS或 CMake 变量过滤 - 结果写入
${var}列表
16.2 选择默认实现
get_default_rmw_implementation(var):
- 若未设置
RMW_IMPLEMENTATION(CMake 或环境变量):- 优先
rmw_fastrtps_cpp - 否则取字母序第一个
- 优先
- 若已设置:验证其在可用列表中,
find_package该包 - Humble 上默认编译链接 Fast-DDS,不是 CycloneDDS(除非显式设置
RMW_IMPLEMENTATION=rmw_cyclonedds_cpp)
16.3 注册实现(register_rmw_implementation.cmake)
各后端 CMakeLists.txt 调用:
1 | register_rmw_implementation( |
向 ament index 写入:
rmw_typesupport— 所有 typesupport 包名rmw_typesupport_c/rmw_typesupport_cpp— 分语言列表
17. 构建与导出
cmake/configure_rmw_library.cmake 设置:
RMW_BUILDING_DLL(Windows 导出)- 默认 symbol visibility hidden,仅
RMW_PUBLIC导出
rmw-extras.cmake + ament_export_* 导出 librmw 与 include 路径。
18. 数据路径(端到端)
18.1 发布
18.2 订阅
1 | rmw_take(sub, msg, &taken, alloc) |
变体:rmw_take_with_info、rmw_take_serialized_message、rmw_take_loaned_message、rmw_take_sequence。
18.3 等待
1 | rcl_wait(wait_set, timeout) |
18.4 Service
1 | Client: rmw_send_request → DDS request writer |
19. 与 rcl 的职责对比
| 主题 | rcl | rmw |
|---|---|---|
| Namespace / remap | ✅ 展开 ~/foo → 绝对名 |
接收最终字符串 |
| QoS 默认值 | rcl_qos_profile_* |
rmw_qos_profile_* |
| Wait set 组装 | ✅ timer → guard_condition | 执行底层 wait |
| 参数 / YAML | ✅ CLI 解析 | 无 |
| 日志 | ✅ rcl_logging | 仅 rmw_set_log_severity 转发 DDS 日志 |
| 命名校验 | 调用 rmw_validate_* | ✅ 实现校验逻辑 |
| DDS 实体 | 无 | ✅ 后端实现 |
20. 相关仓库
| 仓库/包 | 关系 |
|---|---|
rmw_implementation |
代理库;RMW_IMPLEMENTATION 环境变量在运行时切换(若构建支持) |
rmw_cyclonedds_cpp |
CycloneDDS 后端 |
rmw_fastrtps_cpp |
Fast-DDS 后端;Humble 默认 |
rmw_connextdds |
RTI Connext 后端 |
rmw_dds_common |
多后端共享:graph 缓存、GID、demangle |
rosidl_typesupport_* |
消息序列化;与 rmw_serialize 配合 |
rcl |
RMW 主要调用方 |
21. 推荐阅读顺序
rmw/types.h— 掌握所有句柄与 QoS 类型(~650 行)rmw/init.h+init_options.h— 理解 context 生命周期契约rmw/rmw.h— 按 pub/sub/wait/graph 分段阅读(不必一次读完 3200 行)rmw/src/validate_full_topic_name.c— 看本仓库“有实现”代码的风格rmw_implementation_cmake/get_default_rmw_implementation.cmake— 理解构建默认后端rmw_implementation仓库 — 符号如何转发到具体后端- 任选一后端 — 如
rmw_cyclonedds_cpp中rmw_create_publisher/rmw_wait实现 rmw_dds_common— graph 与 GID 共享逻辑
22. 小结
rmw 仓库是 ROS 2 可插拔中间件 的 契约层 + 公共工具库:
- 对上:为
rcl提供稳定 C ABI 与一致的类型定义; - 对下:不绑定任何 DDS,具体通信由
rmw_*_cpp实现; - 本仓库源码价值:理解 ROS 2 中间件 接口设计(句柄、wait 语义、QoS、graph API)以及 跨实现共享逻辑(命名校验、QoS 字符串、init 默认值);
- 调试通信问题:沿
rcl_ret_t→rmw_ret_t→rmw_get_error_string()向下追至具体后端。
换 DDS 只需安装并选择对应 rmw_*_cpp 实现,rcl / rclcpp / rclpy 无需修改。
正在加载留言…