rosidl_typesupport 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rosidl_typesupport
版本:2.0.2(Humble),子包 2 个,许可证 Apache 2.0。
rosidl_typesupport 仓库实现 ROS 2 typesupport 分发层(dispatch layer):为每个消息/服务/动作生成「中间 handle」,在运行时按 RMW 所需的 具体 typesupport identifier(如 rosidl_typesupport_fastrtps_cpp)动态加载对应共享库并返回真正的序列化实现。本仓库 不实现 DDS 线格式序列化——那是 rosidl_typesupport_fastrtps_*、rosidl_typesupport_introspection_* 等插件的职责。
范围说明:本仓库仅含
rosidl_typesupport_c与rosidl_typesupport_cpp。具体 RMW/DDS typesupport 在rosidl_typesupport_fastrtps仓库;introspection 在rosidl仓库的rosidl_typesupport_introspection_*包。
1. 总体认识
1.1 为什么需要 typesupport 分发层?
ROS 2 允许同一消息类型存在 多种 typesupport 实现:
| Typesupport 插件 | identifier 示例 | 用途 |
|---|---|---|
rosidl_typesupport_fastrtps_cpp |
同上 | Fast-DDS CDR 序列化(RMW 数据面) |
rosidl_typesupport_introspection_cpp |
同上 | 字段反射(Python、ros2 topic echo) |
rosidl_typesupport_fastrtps_c |
同上 | C 侧 Fast-DDS |
rosidl_typesupport_introspection_c |
同上 | C 侧 introspection |
用户代码和 RCL 通常持有 分发层 handle(identifier = rosidl_typesupport_cpp),在 publish/take 时 RMW 传入 自身需要的 identifier,分发层通过 dlopen 加载 lib{pkg}__{identifier}.so 并 dlsym 取得具体实现。
1.2 核心职责
| 能力 | rosidl_typesupport_c |
rosidl_typesupport_cpp |
|---|---|---|
| 运行时 dispatch 库 | librosidl_typesupport_c.so |
librosidl_typesupport_cpp.so |
| 动态加载逻辑 | type_support_dispatch.hpp |
同左(C++ 版,逻辑一致) |
| type_support_map | type_support_map.h |
复用 C 版头文件 |
| 代码生成 | 每消息 *_type_support.cpp |
每消息 *_type_support.cpp + C++ 模板特化 |
| CMake 扩展 | rosidl_typesupport_c_generate_interfaces.cmake |
rosidl_typesupport_cpp_generate_interfaces.cmake |
| ament index | 消费 rosidl_typesupport_c 资源 |
消费 rosidl_typesupport_cpp 资源 |
1.3 在 ROS 2 栈中的位置
| 对比项 | 分发层 (rosidl_typesupport_*) |
具体插件 (fastrtps / introspection) |
|---|---|---|
| 库名 | 固定 librosidl_typesupport_cpp.so |
lib{pkg}__rosidl_typesupport_fastrtps_cpp.so |
| 每消息生成 | 是(map + 入口符号) | 是(序列化/反射实现) |
| identifier | rosidl_typesupport_cpp |
rosidl_typesupport_fastrtps_cpp 等 |
| 动态加载 | 发起方(加载其他库) | 被加载方 |
2. 仓库结构
1 | rosidl_typesupport/ |
两包结构 高度对称:C 包提供 type_support_map_t 定义;C++ 包依赖 C 包并复用同一 map 结构。
3. 核心数据结构
3.1 rosidl_message_type_support_t
定义于 rosidl_runtime_c/message_type_support_struct.h(rosidl 仓库):
1 | struct rosidl_message_type_support_t { |
- identifier:本 handle 所属 typesupport 层
- data:具体 typesupport 私有数据,或指向
type_support_map_t - func:按 identifier 查找/加载其他 typesupport 的回调
3.2 type_support_map_t
定义于 rosidl_typesupport_c/type_support_map.h:
1 | typedef struct type_support_map_t { |
运行时填充:data[i] 初始为 NULL;首次请求 identifier typesupport_identifier[i] 时,dlopen 加载库并缓存指针。
3.3 分发层 identifier
| 包 | 全局符号 | 字符串值 |
|---|---|---|
| C | rosidl_typesupport_c__typesupport_identifier |
"rosidl_typesupport_c" |
| C++ | rosidl_typesupport_cpp::typesupport_identifier |
"rosidl_typesupport_cpp" |
4. 运行时 dispatch 算法
实现于 src/type_support_dispatch.hpp(C/C++ 两包逻辑相同)。
4.1 get_typesupport_handle_function
1 | template<typename TypeSupport> |
库命名规则:{package_name}__{typesupport_identifier}
例如 std_msgs__rosidl_typesupport_fastrtps_cpp → libstd_msgs__rosidl_typesupport_fastrtps_cpp.so
符号命名规则(rosidl_typesupport_interface/macros.h):
1 | {typesupport_name}__get_message_type_support_handle__{package}__{subfolder}__{MessageName} |
例:rosidl_typesupport_fastrtps_cpp__get_message_type_support_handle__std_msgs__msg__String
4.2 序列图
5. 编译期代码生成
5.1 CMake 扩展注册
rosidl_typesupport_cpp-extras.cmake.in:
1 | get_used_typesupports(_typesupports "rosidl_typesupport_cpp") |
在 rosidl_generate_interfaces() 执行 ament_execute_extensions("rosidl_generate_idl_interfaces") 时被调用。
前置依赖:必须先有 __rosidl_generator_cpp target(C 包则要求 __rosidl_generator_c)。
5.2 get_used_typesupports() — 发现可用插件
rosidl_typesupport_c/cmake/get_used_typesupports.cmake:
ament_index_get_resources(available_typesupports "rosidl_typesupport_cpp")
读取所有向 ament index 注册了rosidl_typesupport_cpp资源的包(如 fastrtps、introspection)- 可选过滤:CMake 变量或环境变量
STATIC_ROSIDL_TYPESUPPORT_CPP(C 侧为STATIC_ROSIDL_TYPESUPPORT_C),分号分隔插件名 - 输出
typesupports列表传给 generator
构建日志示例:
1 | Using all available rosidl_typesupport_cpp: rosidl_typesupport_fastrtps_cpp;rosidl_typesupport_introspection_cpp |
仅一个插件时标记为 single,生成器走 静态直连 优化路径(见 §5.4)。
5.3 生成命令
1 | add_custom_command( |
Python 入口 rosidl_typesupport_cpp/__init__.py:
1 | def generate_cpp(generator_arguments_file, type_supports): |
每个 IDL 文件生成一个 {msg_name}__type_support.cpp,内含该文件中所有 message/service/action 的分发代码。
5.4 两种生成模式
模式 A:多 typesupport(默认,动态 dispatch)
Empy 模板 msg__type_support.cpp.em 生成:
1 | static const type_support_map_t String_message_typesupport_map = { |
map 中 typesupport_identifier 数组在编译期填入,例如:
1 | "rosidl_typesupport_fastrtps_cpp", |
模式 B:单一 typesupport(静态优化)
当 get_used_typesupports 只返回一个插件时,生成器 跳过 map,直接转发:
1 | template<> |
CMake 同时 静态链接 该 typesupport target:
1 | if(NOT typesupports MATCHES ";") |
限制:多 typesupport + BUILD_SHARED_LIBS=OFF 会 fatal error(无法静态链接多个插件)。
5.5 生成的 CMake Target
| Target | 产物 |
|---|---|
{pkg}__rosidl_typesupport_c |
lib{pkg}__rosidl_typesupport_c.so |
{pkg}__rosidl_typesupport_cpp |
lib{pkg}__rosidl_typesupport_cpp.so |
与 {pkg}__rosidl_typesupport_fastrtps_cpp 等 并列安装 于 lib/。
6. C 与 C++ 分发层差异
| 维度 | rosidl_typesupport_c |
rosidl_typesupport_cpp |
|---|---|---|
| dispatch 函数 | rosidl_typesupport_c__get_message_typesupport_handle_function |
rosidl_typesupport_cpp::get_message_typesupport_handle_function |
| C++ 入口 | 仅 extern "C" 符号 |
额外提供 template<> get_message_type_support_handle<T>() |
| 依赖 | rosidl_runtime_c |
rosidl_runtime_c + rosidl_runtime_cpp + 依赖 C 包 |
| 使用者 | RMW C API、Python C 扩展 | rclcpp 模板 publish、rosidl_typesupport_cpp::get_message_type_support_handle<T>() |
| 生成源文件扩展名 | .cpp(内含 extern "C") |
.cpp |
C++ 侧模板声明位于 rosidl_runtime_cpp/include/rosidl_typesupport_cpp/message_type_support.hpp:
1 | template<typename T> |
每个消息在生成的 __type_support.cpp 中 显式特化。
7. 与 rosidl 工具链的衔接
7.1 在 rosidl_generate_interfaces 中的顺序
1 | rosidl_generator_c |
introspection / fastrtps 的 CMake 同样注册 rosidl_generate_idl_interfaces,且通常依赖 generator 已生成的 struct 头文件。
7.2 ament index 注册链
具体 typesupport 插件在 自身 CMakeLists 中注册 index 资源:
1 | # rosidl_typesupport_fastrtps_cpp/CMakeLists.txt |
get_used_typesupports() 据此发现插件。若某插件未安装,不会进入 map,对应 RMW 在运行时会加载失败。
7.3 rosidl_typesupport_c_packages 组
package.xml 中:
1 | <group_depend>rosidl_typesupport_cpp_packages</group_depend> |
Bloom 打包时确保二进制发行版包含常见 RMW typesupport 依赖;源码构建时由 workspace 中已 clone 的 fastrtps 等包满足。
8. 运行时调用路径示例
8.1 rclcpp publish
1 | rclcpp::Publisher<std_msgs::msg::String>::publish(msg) |
8.2 Python / introspection
Python 绑定通常请求 rosidl_typesupport_introspection_c identifier,同一 dispatch 机制加载 libpkg__rosidl_typesupport_introspection_c.so,取得字段 layout 用于 C ↔ Python 转换。
9. 测试与质量
两包均为 Quality Level 1,含:
| 测试 | 内容 |
|---|---|
test_message_type_support_dispatch.cpp |
map 查找、dlopen、dlsym、错误路径 |
test_service_type_support_dispatch.cpp |
service handle 分发 |
benchmark_type_support_dispatch.cpp |
dispatch 性能基准 |
test_cli_extension.py |
generator CLI 参数 |
测试构造 mock type_support_map_t,用 rosidl_typesupport_cpp__test_type_support1/2 等假库验证加载逻辑;故意放置非共享库文件验证错误处理。
10. 配置与调优
10.1 限制编译期 typesupport 集合
减少 map 大小、缩短构建时间、缩小 install 体积:
1 | # 仅保留 fastrtps(示例) |
CMake 中等价:set(STATIC_ROSIDL_TYPESUPPORT_CPP "rosidl_typesupport_fastrtps_cpp")
10.2 静态链接注意
- 单一 typesupport:可静态链接,无 dlopen 开销
- 多 typesupport + 静态库模式:不支持(CMake fatal error)
- 默认
BUILD_SHARED_LIBS=ON使用动态 dispatch
10.3 常见问题
| 现象 | 原因 | 排查 |
|---|---|---|
Could not load library pkg__rosidl_typesupport_fastrtps_cpp |
未构建/未安装 fastrtps typesupport | ls install/lib/lib*fastrtps* |
Failed to find symbol ... |
插件与消息包版本不匹配 | 全部 rebuild |
| Python 导入消息失败 | 缺 introspection 库 | 安装 rosidl_typesupport_introspection_c |
| 自定义 RMW 找不到 typesupport | 新插件未注册 ament index | 插件 CMake 加 ament_index_register_resource |
11. 与关联仓库对比
| 仓库/包 | 角色 |
|---|---|
本仓库 rosidl_typesupport_c/cpp |
分发层 + 代码生成 |
rosidl_typesupport_interface(rosidl 仓库) |
符号命名宏 |
rosidl_typesupport_introspection_*(rosidl 仓库) |
反射 typesupport 插件 |
rosidl_typesupport_fastrtps_* |
Fast-DDS CDR 插件 |
rosidl_runtime_c |
rosidl_message_type_support_t 结构定义 |
rosidl_generator_c/cpp |
消息 struct 生成(非 typesupport) |
记忆口诀:generator 造 数据 struct;typesupport 插件造 序列化/反射;rosidl_typesupport_cpp 造 路由表 把 RMW 请求转到正确插件。
12. 端到端库依赖图(单接口包)
以 std_msgs 为例,install/lib/ 中典型产物:
1 | libstd_msgs__rosidl_generator_c.so # C struct + functions |
运行时 rclcpp 主要链接/加载 __rosidl_typesupport_cpp;RMW 再经 dispatch 加载 __rosidl_typesupport_fastrtps_cpp。
13. 源码阅读顺序
- 数据结构:
rosidl_typesupport_c/type_support_map.h→rosidl_runtime_c/message_type_support_struct.h - dispatch 核心:
rosidl_typesupport_cpp/src/type_support_dispatch.hpp - 对外 API:
message_type_support_dispatch.hpp/identifier.hpp - 生成模板:
resource/msg__type_support.cpp.em(多/单 typesupport 分支) - CMake:
get_used_typesupports.cmake→rosidl_typesupport_cpp_generate_interfaces.cmake - 扩展注册:
rosidl_typesupport_cpp-extras.cmake.in - 测试:
test/test_message_type_support_dispatch.cpp - 下游:
rclcpp中get_message_type_support_handle<T>()调用链;rosidl_typesupport_fastrtps_cpp中 concrete 实现
14. 小结
rosidl_typesupport 仓库的 2 个包是 ROS 2 类型系统中连接「接口包」与「多种 RMW/语言 typesupport 插件」的 枢纽:
- 编译期:扫描 ament index 中已注册插件,为每条消息生成
type_support_map或静态直连代码,产出lib{pkg}__rosidl_typesupport_{c,cpp}.so - 运行期:通过
get_message_typesupport_handle_function按 identifier dlopenlib{pkg}__{plugin}.so,实现 RMW、Python、工具链对同一消息类型的 插件化 访问
理解 publish 失败或 typesupport 符号缺失时,应同时检查 dispatch 库 与 具体插件库 是否都已正确生成并存在于 LD_LIBRARY_PATH / install lib/ 中。
正在加载留言…