rosidl_typesupport 源码详细分析

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_crosidl_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}.sodlsym 取得具体实现。

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_generate_interfaces每接口包 install/lib运行时"dlopen libmy_msgs__rosidl_typesupport_fastrtps_cpp".idlrosidl_generator_c/cpprosidl_typesupport_c 生成器rosidl_typesupport_cpp 生成器rosidl_typesupport_fastrtps_* 生成器rosidl_typesupport_introspection_* 生成器libmy_msgs__rosidl_typesupport_cpp.so\n(dispatch + map)libmy_msgs__rosidl_typesupport_fastrtps_cpp.so\n(CDR 序列化)libmy_msgs__rosidl_typesupport_introspection_cpp.so\n(反射)rclcpp::Publisher\nget_message_type_support_handle<T>()rosidl_typesupport_cpp\nget_message_typesupport_handle_functionrmw / Fast-DDS\nidentifier=fastrtps_cpp
对比项 分发层 (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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
rosidl_typesupport/
├── README.md
├── LICENSE
├── rosidl_typesupport_c/ # ★ C 分发层
│ ├── include/rosidl_typesupport_c/
│ │ ├── type_support_map.h # 核心 map 结构
│ │ ├── message_type_support_dispatch.h
│ │ ├── service_type_support_dispatch.h
│ │ └── identifier.h
│ ├── src/
│ │ ├── identifier.c
│ │ ├── message_type_support_dispatch.cpp
│ │ ├── service_type_support_dispatch.cpp
│ │ └── type_support_dispatch.hpp # dlopen 逻辑
│ ├── cmake/
│ │ ├── get_used_typesupports.cmake # 发现可用插件
│ │ └── rosidl_typesupport_c_generate_interfaces.cmake
│ ├── resource/*.em # 生成模板
│ └── rosidl_typesupport_c/__init__.py
└── rosidl_typesupport_cpp/ # ★ C++ 分发层
├── include/rosidl_typesupport_cpp/
│ ├── message_type_support_dispatch.hpp
│ ├── service_type_support_dispatch.hpp
│ └── identifier.hpp
├── src/ # 结构与 C 包对称
├── cmake/rosidl_typesupport_cpp_generate_interfaces.cmake
└── resource/*.em

两包结构 高度对称:C 包提供 type_support_map_t 定义;C++ 包依赖 C 包并复用同一 map 结构。


3. 核心数据结构

3.1 rosidl_message_type_support_t

定义于 rosidl_runtime_c/message_type_support_struct.hrosidl 仓库):

1
2
3
4
5
struct rosidl_message_type_support_t {
const char * typesupport_identifier;
const void * data;
rosidl_message_typesupport_handle_function func;
};
  • 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
2
3
4
5
6
7
typedef struct type_support_map_t {
const size_t size;
const char * package_name;
const char * const * typesupport_identifier; // 插件名数组
const char * const * symbol_name; // dlsym 符号名数组
void ** data; // 缓存已加载的 SharedLibrary*
} 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template<typename TypeSupport>
const TypeSupport * get_typesupport_handle_function(
const TypeSupport * handle, const char * identifier)
{
// 1. 精确匹配:请求的 identifier 就是本 handle 的 identifier
if (strcmp(handle->typesupport_identifier, identifier) == 0) {
return handle;
}

// 2. 本 handle 是分发层(rosidl_typesupport_cpp/c)
if (handle->typesupport_identifier == rosidl_typesupport_cpp::typesupport_identifier) {
const type_support_map_t * map = static_cast<const type_support_map_t *>(handle->data);
for (size_t i = 0; i < map->size; ++i) {
if (strcmp(map->typesupport_identifier[i], identifier) != 0) continue;

// 3. 懒加载共享库
if (!map->data[i]) {
// library_basename = "{package_name}__{identifier}"
// 例: std_msgs__rosidl_typesupport_fastrtps_cpp
lib = new rcpputils::SharedLibrary(
rcpputils::get_platform_library_name(library_basename));
map->data[i] = lib;
}

// 4. dlsym 取得具体 typesupport 入口函数
sym = lib->get_symbol(map->symbol_name[i]);
func = reinterpret_cast<const TypeSupport *(*)(void)>(sym);
return func(); // 返回 concrete handle
}
}
return nullptr;
}

库命名规则{package_name}__{typesupport_identifier}
例如 std_msgs__rosidl_typesupport_fastrtps_cpplibstd_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 序列图

libpkg__fastrtps_cpp.sotype_support_maprosidl_typesupport_cpprmw_fastrtpslibpkg__fastrtps_cpp.sotype_support_maprosidl_typesupport_cpprmw_fastrtpsalt[首次加载]使用 concrete handle 做 CDR serializefunc(handle, "rosidl_typesupport_fastrtps_cpp")查找 map 中 matching identifierdlopen("libpkg__rosidl_typesupport_fastrtps_cpp.so")dlsym(get_message_type_support_handle__...)concrete rosidl_message_type_support_t*

5. 编译期代码生成

5.1 CMake 扩展注册

rosidl_typesupport_cpp-extras.cmake.in

1
2
3
4
5
get_used_typesupports(_typesupports "rosidl_typesupport_cpp")
ament_register_extension(
"rosidl_generate_idl_interfaces"
"rosidl_typesupport_cpp"
"rosidl_typesupport_cpp_generate_interfaces.cmake")

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

  1. ament_index_get_resources(available_typesupports "rosidl_typesupport_cpp")
    读取所有向 ament index 注册了 rosidl_typesupport_cpp 资源的包(如 fastrtps、introspection)
  2. 可选过滤:CMake 变量或环境变量 STATIC_ROSIDL_TYPESUPPORT_CPP(C 侧为 STATIC_ROSIDL_TYPESUPPORT_C),分号分隔插件名
  3. 输出 typesupports 列表传给 generator

构建日志示例:

1
Using all available rosidl_typesupport_cpp: rosidl_typesupport_fastrtps_cpp;rosidl_typesupport_introspection_cpp

仅一个插件时标记为 single,生成器走 静态直连 优化路径(见 §5.4)。

5.3 生成命令

1
2
3
4
5
6
7
8
add_custom_command(
OUTPUT ${_generated_sources}
COMMAND Python3::Interpreter
ARGS ${rosidl_typesupport_cpp_BIN}
--generator-arguments-file "${generator_arguments_file}"
--typesupports ${typesupports}
...
)

Python 入口 rosidl_typesupport_cpp/__init__.py

1
2
3
def generate_cpp(generator_arguments_file, type_supports):
mapping = {'idl__type_support.cpp.em': '%s__type_support.cpp'}
return generate_files(..., additional_context={'type_supports': type_supports})

每个 IDL 文件生成一个 {msg_name}__type_support.cpp,内含该文件中所有 message/service/action 的分发代码。

5.4 两种生成模式

模式 A:多 typesupport(默认,动态 dispatch)

Empy 模板 msg__type_support.cpp.em 生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
static const type_support_map_t String_message_typesupport_map = {
2, // size
"std_msgs",
&String_message_typesupport_ids.typesupport_identifier[0],
&String_message_typesupport_symbol_names.symbol_name[0],
&String_message_typesupport_data.data[0],
};

static const rosidl_message_type_support_t String_message_type_support_handle = {
::rosidl_typesupport_cpp::typesupport_identifier,
&String_message_typesupport_map,
::rosidl_typesupport_cpp::get_message_typesupport_handle_function,
};

map 中 typesupport_identifier 数组在编译期填入,例如:

1
2
"rosidl_typesupport_fastrtps_cpp",
"rosidl_typesupport_introspection_cpp",

模式 B:单一 typesupport(静态优化)

get_used_typesupports 只返回一个插件时,生成器 跳过 map,直接转发:

1
2
3
4
5
6
template<>
const rosidl_message_type_support_t *
get_message_type_support_handle<std_msgs::msg::String>() {
return ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(
rosidl_typesupport_fastrtps_cpp, std_msgs, msg, String)();
}

CMake 同时 静态链接 该 typesupport target:

1
2
3
if(NOT typesupports MATCHES ";")
target_link_libraries(... PRIVATE ${target}__${typesupports})
endif()

限制:多 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
2
template<typename T>
const rosidl_message_type_support_t * get_message_type_support_handle();

每个消息在生成的 __type_support.cpp显式特化


7. 与 rosidl 工具链的衔接

7.1 在 rosidl_generate_interfaces 中的顺序

1
2
3
4
5
6
7
rosidl_generator_c
→ rosidl_generator_cpp
→ rosidl_typesupport_c
→ rosidl_typesupport_cpp
→ rosidl_typesupport_introspection_c/cpp
→ rosidl_typesupport_fastrtps_c/cpp
→ rosidl_generator_py

introspection / fastrtps 的 CMake 同样注册 rosidl_generate_idl_interfaces,且通常依赖 generator 已生成的 struct 头文件。

7.2 ament index 注册链

具体 typesupport 插件在 自身 CMakeLists 中注册 index 资源:

1
2
# rosidl_typesupport_fastrtps_cpp/CMakeLists.txt
ament_index_register_resource("rosidl_typesupport_cpp")

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
2
3
4
5
6
7
8
9
10
rclcpp::Publisher<std_msgs::msg::String>::publish(msg)
→ rosidl_typesupport_cpp::get_message_type_support_handle<std_msgs::msg::String>()
返回 pkg 生成的 dispatch handle(identifier=rosidl_typesupport_cpp)
→ rcl_publish(..., ros_message, type_support)
→ rmw 内部调用 type_support->func(handle, rmw_typesupport_identifier)
rmw_typesupport_identifier = "rosidl_typesupport_fastrtps_cpp"
→ get_message_typesupport_handle_function(...)
→ dlopen libstd_msgs__rosidl_typesupport_fastrtps_cpp.so
→ 返回 fastrtps concrete handle
→ Fast-DDS CDR 序列化 → 网络发送

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
2
3
# 仅保留 fastrtps(示例)
export STATIC_ROSIDL_TYPESUPPORT_CPP=rosidl_typesupport_fastrtps_cpp
colcon build --packages-select my_msgs

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
2
3
4
5
6
7
8
libstd_msgs__rosidl_generator_c.so          # C struct + functions
libstd_msgs__rosidl_generator_cpp.so # 头文件(常为 INTERFACE)
libstd_msgs__rosidl_typesupport_c.so # ★ C dispatch
libstd_msgs__rosidl_typesupport_cpp.so # ★ C++ dispatch
libstd_msgs__rosidl_typesupport_fastrtps_c.so
libstd_msgs__rosidl_typesupport_fastrtps_cpp.so
libstd_msgs__rosidl_typesupport_introspection_c.so
libstd_msgs__rosidl_typesupport_introspection_cpp.so

运行时 rclcpp 主要链接/加载 __rosidl_typesupport_cpp;RMW 再经 dispatch 加载 __rosidl_typesupport_fastrtps_cpp


13. 源码阅读顺序

  1. 数据结构rosidl_typesupport_c/type_support_map.hrosidl_runtime_c/message_type_support_struct.h
  2. dispatch 核心rosidl_typesupport_cpp/src/type_support_dispatch.hpp
  3. 对外 APImessage_type_support_dispatch.hpp / identifier.hpp
  4. 生成模板resource/msg__type_support.cpp.em(多/单 typesupport 分支)
  5. CMakeget_used_typesupports.cmakerosidl_typesupport_cpp_generate_interfaces.cmake
  6. 扩展注册rosidl_typesupport_cpp-extras.cmake.in
  7. 测试test/test_message_type_support_dispatch.cpp
  8. 下游rclcppget_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 dlopen lib{pkg}__{plugin}.so,实现 RMW、Python、工具链对同一消息类型的 插件化 访问

理解 publish 失败或 typesupport 符号缺失时,应同时检查 dispatch 库具体插件库 是否都已正确生成并存在于 LD_LIBRARY_PATH / install lib/ 中。

文章互动

阅读 --

留言

0 条留言

正在加载留言…