rosidl_typesupport_fastrtps 源码详细分析

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 工具链rosidl_typesupport_fastrtpsrmw_fastrtps网络"dlopen libpkg__fastrtps_cpp""callbacks->cdr_serialize"rosidl_generator_c/cpp\nstruct 定义rosidl_typesupport_c/cpp\n分发层 maprosidl_typesupport_fastrtps_c\nC struct CDRrosidl_typesupport_fastrtps_cpp\nC++ struct CDRfastrtps_cmake_moduleMessageTypeSupport\nTypeSupport::serializeROSmessageFast-DDS DataWriter/ReaderCDR 字节 + encapsulation
对比项 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
2
3
4
5
6
7
8
9
rclcpp::Publisher::publish(msg)
→ rosidl_typesupport_cpp dispatch handle
→ dlopen libstd_msgs__rosidl_typesupport_fastrtps_cpp.so
→ get_message_type_support_handle<std_msgs::msg::String>()
→ rmw_fastrtps_cpp::MessageTypeSupport(callbacks)
→ TypeSupport::serializeROSmessage()
ser.serialize_encapsulation()
callbacks->cdr_serialize(ros_message, ser)
→ Fast-DDS 发送 CDR buffer

2. 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rosidl_typesupport_fastrtps/
├── fastrtps_cmake_module/ # FindFastRTPS.cmake
│ └── cmake/Modules/FindFastRTPS.cmake
├── rosidl_typesupport_fastrtps_cpp/ # ★ C++ 消息 CDR + 回调结构定义
│ ├── include/rosidl_typesupport_fastrtps_cpp/
│ │ ├── message_type_support.h # callbacks 结构体(C/C++ 共用)
│ │ ├── service_type_support.h
│ │ ├── message_type_support_decl.hpp
│ │ └── wstring_conversion.hpp
│ ├── src/identifier.cpp, wstring_conversion.cpp
│ ├── resource/*.em # Empy 生成模板
│ └── cmake/rosidl_typesupport_fastrtps_cpp_generate_interfaces.cmake
└── rosidl_typesupport_fastrtps_c/ # ★ C 消息 CDR(依赖 C++ 包头)
├── resource/*_c.cpp.em / *_c.h.em
└── cmake/rosidl_typesupport_fastrtps_c_generate_interfaces.cmake

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
2
3
find_package(fastcdr REQUIRED CONFIG)
find_package(fastrtps REQUIRED CONFIG)
# 设置 FastRTPS_INCLUDE_DIR, FastRTPS_LIBRARIES, FastCDR_LIBRARIES

注意:本仓库生成代码 直接使用 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.hC 与 C++ 生成代码共用

1
2
3
4
5
6
7
8
9
typedef struct message_type_support_callbacks_t {
const char * message_namespace_;
const char * message_name_;

bool (* cdr_serialize)(const void * untyped_ros_message, eprosima::fastcdr::Cdr & cdr);
bool (* cdr_deserialize)(eprosima::fastcdr::Cdr & cdr, void * untyped_ros_message);
uint32_t (* get_serialized_size)(const void *);
size_t (* max_serialized_size)(char & bounds_info);
} 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
2
3
#define ROSIDL_TYPESUPPORT_FASTRTPS_UNBOUNDED_TYPE 0x00
#define ROSIDL_TYPESUPPORT_FASTRTPS_BOUNDED_TYPE 0x01
#define ROSIDL_TYPESUPPORT_FASTRTPS_PLAIN_TYPE 0x03

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
2
3
4
5
// rmw_fastrtps_cpp/src/type_support_common.cpp
auto data_size = members->max_serialized_size(bounds_info);
max_size_bound_ = 0 != (bounds_info & ROSIDL_TYPESUPPORT_FASTRTPS_BOUNDED_TYPE);
is_plain_ = bounds_info == ROSIDL_TYPESUPPORT_FASTRTPS_PLAIN_TYPE;
m_typeSize = 4 + data_size; // + encapsulation,4 字节对齐

空消息特殊处理:plain 且 size=0 时加 dummy byte。

4.3 service_type_support_callbacks_t

1
2
3
4
5
6
typedef struct service_type_support_callbacks_t {
const char * service_namespace_;
const char * service_name_;
const rosidl_message_type_support_t * request_members_;
const rosidl_message_type_support_t * response_members_;
} 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
2
3
4
5
6
mapping = {
'idl__rosidl_typesupport_fastrtps_cpp.hpp.em':
'detail/%s__rosidl_typesupport_fastrtps_cpp.hpp',
'idl__type_support.cpp.em':
'detail/dds_fastrtps/%s__type_support.cpp',
}

Crosidl_typesupport_fastrtps_c/__init__.py):

1
2
3
4
5
6
mapping = {
'idl__rosidl_typesupport_fastrtps_c.h.em':
'detail/%s__rosidl_typesupport_fastrtps_c.h',
'idl__type_support_c.cpp.em':
'detail/%s__type_support_c.cpp',
}

5.2 CMake 扩展注册

rosidl_typesupport_fastrtps_cpp-extras.cmake.in

1
2
3
4
5
6
7
8
if(NOT fastcdr_FOUND)
message(STATUS "Could not find eProsima Fast CDR - skip rosidl_typesupport_fastrtps_cpp")
else()
ament_register_extension(
"rosidl_generate_idl_interfaces"
"rosidl_typesupport_fastrtps_cpp"
"rosidl_typesupport_fastrtps_cpp_generate_interfaces.cmake")
endif()

前置条件

  • C++:__rosidl_generator_cpp 必须先存在
  • C:__rosidl_generator_c + rosidl_typesupport_fastrtps_cpp

ament index(构建时注册为可用插件):

1
2
3
4
# rosidl_typesupport_fastrtps_cpp/CMakeLists.txt
ament_index_register_resource("rosidl_typesupport_cpp")
# rosidl_typesupport_fastrtps_c/CMakeLists.txt
ament_index_register_resource("rosidl_typesupport_c")

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

链接依赖:fastcdrrmw::rmwrosidl_runtime_*librosidl_typesupport_fastrtps_cpp.so(C 包)。


6. C++ 生成模板详解(msg__type_support.cpp.em

6.1 生成结构概览

对每条消息 MyMsg,生成:

  1. 公开 CDR 函数typesupport_fastrtps_cpp 命名空间)
    cdr_serialize / cdr_deserialize / get_serialized_size / max_serialized_size_MyMsg
  2. 静态包装函数(type-erased,void*
  3. message_type_support_callbacks_t 静态实例
  4. rosidl_message_type_support_t handle
  5. C++ 模板特化 + extern “C” 导出符号

6.2 handle 组装

1
2
3
4
5
6
7
8
9
10
11
12
13
static message_type_support_callbacks_t MyMsg__callbacks = {
"my_pkg::msg", "MyMsg",
_MyMsg__cdr_serialize,
_MyMsg__cdr_deserialize,
_MyMsg__get_serialized_size,
_MyMsg__max_serialized_size
};

static rosidl_message_type_support_t MyMsg__handle = {
rosidl_typesupport_fastrtps_cpp::typesupport_identifier,
&MyMsg__callbacks,
get_message_typesupport_handle_function,
};
  • identifier"rosidl_typesupport_fastrtps_cpp"
  • funcrosidl_runtime_c 提供的链式查找;对 concrete handle,identifier 匹配时直接返回自身

导出符号(供分发层 dlsym):

1
2
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(
rosidl_typesupport_fastrtps_cpp, my_pkg, msg, MyMsg)()

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_wstringcdr << wstr
嵌套消息 调用依赖类型的 typesupport_fastrtps_cpp::cdr_serialize

反序列化对称实现;bounded sequence 检查 size > maximum_sizestd::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
2
3
4
5
static rosidl_message_type_support_t MyMsg__type_support = {
rosidl_typesupport_fastrtps_c__identifier,
&__callbacks_MyMsg,
get_message_typesupport_handle_function,
};

依赖关系:C 包 <depend>rosidl_typesupport_fastrtps_cpp</depend>,因 callbacks 结构体定义在 C++ 包头中。


8. Service 与 Action

8.1 Service

srv__type_support.cpp.em

  1. 对 Request/Response 各调用一次 msg__type_support.cpp.em
  2. 组装 service_type_support_callbacks_t,指向两个 message 的 fastrtps symbol:
1
2
3
4
5
static service_type_support_callbacks_t MySrv__callbacks = {
"my_pkg::srv", "MySrv",
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(..., MySrv_Request)(),
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(..., MySrv_Response)(),
};

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.cpptypesupport_identifier = "rosidl_typesupport_fastrtps_cpp"
  • wstring_conversion.cpp — u16 ↔ wstring

wstring 转换(FastCDR 使用 std::wstring,ROS 使用 char16_t/u16string):

1
2
3
4
5
void u16string_to_wstring(const std::u16string & u16str, std::wstring & wstr) {
wstr.resize(u16str.size());
for (size_t i = 0; i < u16str.size(); ++i)
wstr[i] = static_cast<wchar_t>(u16str[i]);
}

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
2
3
4
5
6
7
8
9
10
11
bool TypeSupport::serializeROSmessage(
const void * ros_message, eprosima::fastcdr::Cdr & ser, const void * impl) const
{
ser.serialize_encapsulation();
if (has_data_) {
auto callbacks = static_cast<const message_type_support_callbacks_t *>(impl);
return callbacks->cdr_serialize(ros_message, ser);
}
ser << (uint8_t)0; // empty message dummy
return true;
}

创建 publisher 时(subscription.cpp 等):

1
2
auto callbacks = static_cast<const message_type_support_callbacks_t *>(type_support->data);
MessageTypeSupport ts(callbacks);

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
2
3
4
5
6
7
8
9
10
11
install/std_msgs/lib/
├── libstd_msgs__rosidl_typesupport_fastrtps_cpp.so
└── libstd_msgs__rosidl_typesupport_fastrtps_c.so

install/std_msgs/include/std_msgs/std_msgs/
├── msg/detail/dds_fastrtps/string__type_support.cpp # 仅 build 树
└── msg/detail/string__rosidl_typesupport_fastrtps_cpp.hpp

install/lib/
├── librosidl_typesupport_fastrtps_cpp.so # 公共辅助库
└── librosidl_typesupport_fastrtps_c.so # (若 C 包也建 runtime lib)

publish 热路径:per-package__rosidl_typesupport_fastrtps_cpp 库。


13. 与 rosidl_typesupport 分发层的关系

回顾分发层加载逻辑(见 rosidl_typesupport源码详细分析.md):

  1. rosidl_typesupport_cpp handle 的 map 含 "rosidl_typesupport_fastrtps_cpp"
  2. dlopen("libstd_msgs__rosidl_typesupport_fastrtps_cpp.so")
  3. dlsym("rosidl_typesupport_fastrtps_cpp__get_message_type_support_handle__std_msgs__msg__String")
  4. 返回 §6.2 中的 concrete handle(data → callbacks)

本仓库不负责 dispatch;只实现 map 中 被指向的目标库


14. 序列图:CDR 序列化

Fast-DDSfastcdr::Cdrmessage_type_support_callbacks_tMessageTypeSupportrmw_fastrtps_cpprclcpp NodeFast-DDSfastcdr::Cdrmessage_type_support_callbacks_tMessageTypeSupportrmw_fastrtps_cpprclcpp Nodetype_support 已通过 dispatch 解析为 fastrtps handlermw_publish(msg, type_support)serializeROSmessage(msg, ser, callbacks)serialize_encapsulation()cdr_serialize(msg, cdr)cdr << fields ...write(CDR buffer)

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
2
3
4
5
6
7
8
# 确认插件已注册
ls install/share/rosidl_typesupport_fastrtps_cpp/cmake

# 查看某消息 fastrtps 库
ls install/lib/lib*fastrtps*

# 查看生成 CDR 源(build 树)
ls build/std_msgs/rosidl_typesupport_fastrtps_cpp/std_msgs/msg/detail/dds_fastrtps/

16. 与 introspection typesupport 对比

维度 fastrtps(本仓库) introspection
主要消费者 rmw_fastrtps(DDS 线格式) rclpy、ros2 topic echo
data 内容 CDR 函数指针 字段 offset/类型表
依赖 fastcdr 无 fastcdr
性能 数据面热路径 反射/转换

同一消息 同时 生成两种插件库,由分发层按 identifier 选择。


17. 源码阅读顺序

  1. 契约message_type_support.hservice_type_support.h
  2. RMW 消费方rmw_fastrtps_cpp/src/type_support_common.cpp
  3. C++ 生成模板resource/msg__type_support.cpp.em(serialize 与 max_size 两段)
  4. C 生成模板resource/msg__type_support_c.cpp.em
  5. Serviceresource/srv__type_support.cpp.em
  6. CMake 生成rosidl_typesupport_fastrtps_cpp_generate_interfaces.cmake
  7. 扩展注册rosidl_typesupport_fastrtps_cpp-extras.cmake.in
  8. 分发层衔接rosidl_typesupport 仓库 type_support_dispatch.hpp
  9. 辅助wstring_conversion.cppFindFastRTPS.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_t
  • fastrtps_cmake_module:CMake 查找 eProsima 依赖
  • rmw_fastrtps_cpp:读取 callbacks,封装 encapsulation,交给 Fast-DDS

理解 DDS 通信问题需串联 本仓库生成的 CDR 代码rmw_fastrtps 的 TypeSupport 包装;理解 typesupport 加载失败则需同时查看 rosidl_typesupport 分发层 的 dlopen 逻辑。

文章互动

阅读 --

留言

0 条留言

正在加载留言…