rosidl_typesupport_fastrtps 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rosidl_typesupport_fastrtps
版本:2.2.4(Humble),子包 3 个,许可证 Apache 2.0。
rosidl_typesupport_fastrtps 是 ROS 2 在 Fast-DDS / Fast-RTPS RMW 下的 CDR 序列化 typesupport 插件:为每条消息/服务生成 fastcdr::Cdr 读写代码,并通过 message_type_support_callbacks_t 回调表暴露给 rmw_fastrtps_cpp。本仓库 不实现 DDS 传输层——那是 rmw_fastrtps 的职责;此处只负责 ROS 消息 ↔ CDR 字节流。
范围说明:分发层(
rosidl_typesupport_c/cpp)在独立仓库rosidl_typesupport;本仓库是 RMW 实际调用的 concrete typesupport 插件 之一。Humble 默认 RMW 为rmw_fastrtps_cpp,因此本插件为数据面关键路径。
1. 总体认识
1.1 核心职责
| 能力 | 实现位置 | 说明 |
|---|---|---|
| Fast-CDR 序列化生成 | rosidl_typesupport_fastrtps_c/cpp |
Empy 模板生成 per-message CDR 代码 |
| 回调结构定义 | message_type_support.h |
cdr_serialize / cdr_deserialize 等函数指针 |
| typesupport handle | 生成源文件 | rosidl_message_type_support_t + identifier |
| wstring 转换 | wstring_conversion.cpp |
ROS u16string ↔ FastCDR wstring |
| CMake 发现 FastRTPS | fastrtps_cmake_module |
FindFastRTPS.cmake 包装 fastcdr/fastrtps |
| ament index 注册 | 两 generator 包 | 注册为 rosidl_typesupport_c/cpp 插件 |
1.2 在 ROS 2 栈中的位置
| 对比项 | rosidl_typesupport(分发层) |
rosidl_typesupport_fastrtps(本仓库) |
|---|---|---|
| identifier | rosidl_typesupport_cpp |
rosidl_typesupport_fastrtps_cpp |
| data 字段 | type_support_map_t |
message_type_support_callbacks_t |
| 序列化 | 无 | Fast-CDR 实现 |
| 消费者 | rcl/rclcpp 入口 | rmw_fastrtps 直接读 callbacks |
| 库名 | libpkg__rosidl_typesupport_cpp.so |
libpkg__rosidl_typesupport_fastrtps_cpp.so |
1.3 端到端 publish 数据路径(简化)
1 | rclcpp::Publisher::publish(msg) |
2. 仓库结构
1 | rosidl_typesupport_fastrtps/ |
2.1 子包一览
| 包名 | 版本 | 职责 |
|---|---|---|
fastrtps_cmake_module |
2.2.4 | 提供 FindFastRTPS.cmake,查找 fastcdr/fastrtps |
rosidl_typesupport_fastrtps_cpp |
2.2.4 | C++ typesupport 生成器 + 运行时辅助库 |
rosidl_typesupport_fastrtps_c |
2.2.4 | C typesupport 生成器(序列化 rosidl_runtime_c__* 类型) |
3. fastrtps_cmake_module
3.1 作用
Historically ROS 2 用统一 CMake 模块查找 eProsima 组件。FindFastRTPS.cmake 封装:
1 | find_package(fastcdr REQUIRED CONFIG) |
注意:本仓库生成代码 直接使用 fastcdr(#include <fastcdr/Cdr.h>),不直接在模板里调用 FastRTPS API。FindFastRTPS 主要供 下游 RMW 与依赖链使用;generator 的 find_package(fastcdr REQUIRED CONFIG) 才是编译关键。
3.2 质量等级
QL 4(辅助模块),与两个 typesupport 包(QL 1)不同。
4. message_type_support_callbacks_t — 核心契约
定义于 rosidl_typesupport_fastrtps_cpp/message_type_support.h,C 与 C++ 生成代码共用:
1 | typedef struct message_type_support_callbacks_t { |
4.1 四个回调的含义
| 回调 | 用途 |
|---|---|
cdr_serialize |
将 ROS 消息写入 FastCDR(已去 encapsulation) |
cdr_deserialize |
从 FastCDR 读出 ROS 消息 |
get_serialized_size |
运行时计算 实际 序列化大小(含变长 string/sequence) |
max_serialized_size |
编译期/初始化期计算 最大 大小,并返回 bounds 分类 |
4.2 bounds_info 与零拷贝优化
1 |
rmw_fastrtps_cpp::TypeSupport::set_members() 读取 max_serialized_size(bounds_info):
- PLAIN:POD 型消息,内存布局与 CDR 一致 → 可 loan/sample 零拷贝 优化
- BOUNDED:有界(无有界 string/sequence 外的 unbounded)
- UNBOUNDED:含 unbounded string/sequence
1 | // rmw_fastrtps_cpp/src/type_support_common.cpp |
空消息特殊处理:plain 且 size=0 时加 dummy byte。
4.3 service_type_support_callbacks_t
1 | typedef struct service_type_support_callbacks_t { |
Service handle 的 data 指向此结构;RMW 分别对 Request/Response 消息 callbacks 创建 RequestTypeSupport / ResponseTypeSupport。
5. 代码生成(Empy 模板)
5.1 生成入口
C++(rosidl_typesupport_fastrtps_cpp/__init__.py):
1 | mapping = { |
C(rosidl_typesupport_fastrtps_c/__init__.py):
1 | mapping = { |
5.2 CMake 扩展注册
rosidl_typesupport_fastrtps_cpp-extras.cmake.in:
1 | if(NOT fastcdr_FOUND) |
前置条件:
- C++:
__rosidl_generator_cpp必须先存在 - C:
__rosidl_generator_c+rosidl_typesupport_fastrtps_cpp包
ament index(构建时注册为可用插件):
1 | # rosidl_typesupport_fastrtps_cpp/CMakeLists.txt |
5.3 生成的 CMake Target
| Target 后缀 | 产物 |
|---|---|
__rosidl_typesupport_fastrtps_cpp |
lib{pkg}__rosidl_typesupport_fastrtps_cpp.so |
__rosidl_typesupport_fastrtps_c |
lib{pkg}__rosidl_typesupport_fastrtps_c.so |
链接依赖:fastcdr、rmw::rmw、rosidl_runtime_*、librosidl_typesupport_fastrtps_cpp.so(C 包)。
6. C++ 生成模板详解(msg__type_support.cpp.em)
6.1 生成结构概览
对每条消息 MyMsg,生成:
- 公开 CDR 函数(
typesupport_fastrtps_cpp命名空间)cdr_serialize/cdr_deserialize/get_serialized_size/max_serialized_size_MyMsg - 静态包装函数(type-erased,
void*) message_type_support_callbacks_t静态实例rosidl_message_type_support_thandle- C++ 模板特化 + extern “C” 导出符号
6.2 handle 组装
1 | static message_type_support_callbacks_t MyMsg__callbacks = { |
- identifier:
"rosidl_typesupport_fastrtps_cpp" - func:
rosidl_runtime_c提供的链式查找;对 concrete handle,identifier 匹配时直接返回自身
导出符号(供分发层 dlsym):
1 | ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME( |
6.3 字段序列化规则(模板逻辑摘要)
| IDL 类型 | C++ 序列化方式 |
|---|---|
| 基本数值 | cdr << ros_message.field |
boolean |
cdr << (value ? true : false) |
| 固定数组(基本类型) | cdr << ros_message.array |
| 固定数组(嵌套/msg) | 循环 cdr_serialize(element) |
| unbounded/bounded sequence(基本类型) | cdr << size + serializeArray 或逐元素 |
string |
cdr << ros_message.str |
wstring |
u16string_to_wstring → cdr << wstr |
| 嵌套消息 | 调用依赖类型的 typesupport_fastrtps_cpp::cdr_serialize |
反序列化对称实现;bounded sequence 检查 size > maximum_size 抛 std::runtime_error。
6.4 max_serialized_size 与 plain 判定
模板递归计算 CDR alignment,累加 current_alignment:
- 基本类型按
sizeof+Cdr::alignment - 嵌套类型调用
max_serialized_size_DependentType - string 按 bounded 与否估算 upper bound
- 最后用
offsetof比较 in-memory 布局与 CDR 大小,确认 is_plain
7. C 生成模板差异(msg__type_support_c.cpp.em)
C 侧操作 rosidl_generator_c 生成的 C struct:
| 概念 | C++ 生成 | C 生成 |
|---|---|---|
| 字符串 | std::string |
rosidl_runtime_c__String + string_functions.h |
| 序列 | std::vector |
rosidl_runtime_c__*__Sequence |
| 嵌套类型 | pkg::msg::Type |
pkg__msg__Type + __functions.h |
| callbacks 结构 | 同 message_type_support.h |
同左(复用 C++ 包头) |
| identifier | — | rosidl_typesupport_fastrtps_c__identifier |
C handle 示例:
1 | static rosidl_message_type_support_t MyMsg__type_support = { |
依赖关系:C 包 <depend>rosidl_typesupport_fastrtps_cpp</depend>,因 callbacks 结构体定义在 C++ 包头中。
8. Service 与 Action
8.1 Service
srv__type_support.cpp.em:
- 对 Request/Response 各调用一次
msg__type_support.cpp.em - 组装
service_type_support_callbacks_t,指向两个 message 的 fastrtps symbol:
1 | static service_type_support_callbacks_t MySrv__callbacks = { |
RMW 侧 RequestTypeSupport / ResponseTypeSupport 从 service callbacks 取出 request/response 的 message callbacks。
8.2 Action
无独立 action 模板;Action 的 Goal/Result/Feedback/SendGoal/GetResult/FeedbackMessage 作为 普通 message/service 在 IDL 展开后走同一套 msg/srv 模板(与 rosidl 工具链 Action 衍生类型一致)。
9. 运行时辅助库
9.1 librosidl_typesupport_fastrtps_cpp.so
编译产物(非 per-package):
identifier.cpp—typesupport_identifier = "rosidl_typesupport_fastrtps_cpp"wstring_conversion.cpp— u16 ↔ wstring
wstring 转换(FastCDR 使用 std::wstring,ROS 使用 char16_t/u16string):
1 | void u16string_to_wstring(const std::u16string & u16str, std::wstring & wstr) { |
rmw_fastrtps_dynamic_cpp 在动态类型路径中也复用这些转换函数。
9.2 C 包运行时
C 包主要提供 generator + rosidl_typesupport_fastrtps_c__identifier;wstring 实现在 C++ 包中,C 侧通过 rosidl_typesupport_fastrtps_c/wstring_conversion.hpp 包装调用。
10. 与 rmw_fastrtps 的衔接
10.1 TypeSupport 包装
rmw_fastrtps_cpp/src/type_support_common.cpp 是 本仓库 callbacks 的直接消费者:
1 | bool TypeSupport::serializeROSmessage( |
创建 publisher 时(subscription.cpp 等):
1 | auto callbacks = static_cast<const message_type_support_callbacks_t *>(type_support->data); |
type_support 来自 RMW,已通过 rosidl_typesupport_cpp 分发层解析到 fastrtps concrete handle。
10.2 rmw_fastrtps_cpp vs rmw_fastrtps_dynamic_cpp
| RMW | typesupport 使用 |
|---|---|
rmw_fastrtps_cpp |
编译期类型,直接用生成的 callbacks |
rmw_fastrtps_dynamic_cpp |
运行时动态类型,仍依赖 fastrtps CDR 规则 + wstring 工具 |
两包均 build_depend 本仓库的 rosidl_typesupport_fastrtps_c/cpp。
11. 编译开关与环境变量
| 变量 | 效果 |
|---|---|
FASTRTPS_STATIC_DISABLE=TRUE |
跳过两个 typesupport 包构建(early return()) |
未安装 fastcdr |
rosidl_typesupport_fastrtps_cpp 扩展不注册,无法生成 fastrtps typesupport |
Humble 通过 rosidl_default_generators 默认拉入本插件;缺失时 ros2 bag/DDS 通信会在运行时 dlopen 失败。
12. 安装布局示例
以 std_msgs 为例:
1 | install/std_msgs/lib/ |
publish 热路径:per-package 的 __rosidl_typesupport_fastrtps_cpp 库。
13. 与 rosidl_typesupport 分发层的关系
回顾分发层加载逻辑(见 rosidl_typesupport源码详细分析.md):
rosidl_typesupport_cpphandle 的 map 含"rosidl_typesupport_fastrtps_cpp"dlopen("libstd_msgs__rosidl_typesupport_fastrtps_cpp.so")dlsym("rosidl_typesupport_fastrtps_cpp__get_message_type_support_handle__std_msgs__msg__String")- 返回 §6.2 中的 concrete handle(
data→ callbacks)
本仓库不负责 dispatch;只实现 map 中 被指向的目标库。
14. 序列图:CDR 序列化
15. 调试与常见问题
| 现象 | 排查 |
|---|---|
Could not load library pkg__rosidl_typesupport_fastrtps_cpp |
接口包未用 default generators 构建;或 fastcdr 缺失导致未生成 |
| CDR deserialize 异常 | 两端 RMW/消息定义不一致;检查 .msg 变更后是否全量 rebuild |
| wstring 相关崩溃 | wchar_t 宽度平台差异;查 wstring_conversion 测试 |
| plain 类型未零拷贝 | max_serialized_size 判定非 PLAIN(含 string/sequence) |
| 仅 C 节点失败 | 缺 __rosidl_typesupport_fastrtps_c 或未 link |
实用命令:
1 | # 确认插件已注册 |
16. 与 introspection typesupport 对比
| 维度 | fastrtps(本仓库) | introspection |
|---|---|---|
| 主要消费者 | rmw_fastrtps(DDS 线格式) | rclpy、ros2 topic echo |
| data 内容 | CDR 函数指针 | 字段 offset/类型表 |
| 依赖 | fastcdr | 无 fastcdr |
| 性能 | 数据面热路径 | 反射/转换 |
同一消息 同时 生成两种插件库,由分发层按 identifier 选择。
17. 源码阅读顺序
- 契约:
message_type_support.h→service_type_support.h - RMW 消费方:
rmw_fastrtps_cpp/src/type_support_common.cpp - C++ 生成模板:
resource/msg__type_support.cpp.em(serialize 与 max_size 两段) - C 生成模板:
resource/msg__type_support_c.cpp.em - Service:
resource/srv__type_support.cpp.em - CMake 生成:
rosidl_typesupport_fastrtps_cpp_generate_interfaces.cmake - 扩展注册:
rosidl_typesupport_fastrtps_cpp-extras.cmake.in - 分发层衔接:
rosidl_typesupport仓库type_support_dispatch.hpp - 辅助:
wstring_conversion.cpp、FindFastRTPS.cmake
18. 小结
rosidl_typesupport_fastrtps 仓库实现 ROS 2 消息在 Fast-DDS RMW 下的 CDR 序列化 typesupport:
rosidl_typesupport_fastrtps_cpp/c:Empy 生成 per-message 的fastcdr读写与message_type_support_callbacks_tfastrtps_cmake_module:CMake 查找 eProsima 依赖rmw_fastrtps_cpp:读取 callbacks,封装 encapsulation,交给 Fast-DDS
理解 DDS 通信问题需串联 本仓库生成的 CDR 代码 与 rmw_fastrtps 的 TypeSupport 包装;理解 typesupport 加载失败则需同时查看 rosidl_typesupport 分发层 的 dlopen 逻辑。
正在加载留言…