rosidl 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rosidl
版本:3.1.8(Humble),子包 12 个,许可证 Apache 2.0。
rosidl 是 ROS 2 接口定义语言(IDL)工具链的核心仓库:将 .msg / .srv / .action(或原生 .idl)解析为 AST,经 Empy 模板 生成 C/C++ 结构体与函数,并注册 typesupport 扩展点;运行时通过 rosidl_message_type_support_t 与 RMW/DDS 衔接。
范围说明:本仓库含 编译期 核心(adapter / parser / generator / runtime / introspection typesupport)。Python 生成器(
rosidl_generator_py)、通用 typesupport 分发(rosidl_typesupport_cpp)、Fast-DDS typesupport(rosidl_typesupport_fastrtps_*)、默认生成器聚合(rosidl_default_generators)位于 独立仓库,见 §12。
1. 总体认识
1.1 核心职责
| 能力 | 实现位置 | 说明 |
|---|---|---|
| Legacy 格式适配 | rosidl_adapter |
.msg/.srv/.action → .idl |
| IDL 解析 | rosidl_parser |
Lark 语法 → definition.py AST |
| CMake 编排 | rosidl_cmake |
rosidl_generate_interfaces() 宏 |
| C 代码生成 | rosidl_generator_c |
struct + init/fini + type_support 声明 |
| C++ 代码生成 | rosidl_generator_cpp |
struct + builder + traits |
| C 运行时 | rosidl_runtime_c |
String、序列、message_type_support_t |
| C++ 运行时 | rosidl_runtime_cpp |
traits、bounded_vector、type_support 声明 |
| Typesupport 接口 | rosidl_typesupport_interface |
符号命名宏 |
| Introspection TS | rosidl_typesupport_introspection_* |
字段反射(Python/rqt/ros2 topic echo) |
| CLI 工具 | rosidl_cli |
rosidl generate / translate |
| 集成测试 | rosidl_typesupport_introspection_tests |
introspection 正确性验证 |
1.2 在 ROS 2 栈中的位置
| 阶段 | 输入 | 输出 |
|---|---|---|
| 适配 | Chatter.msg |
Chatter.idl(OMG IDL 模块语法) |
| 解析 | .idl 文本 |
IdlFile / Message / Service / Action AST |
| 生成 | AST + Empy 模板 | *.h / *.hpp / __functions.c / typesupport 源 |
| 链接 | 多个 __rosidl_* 库 |
可链接的 interface package |
| 运行 | rosidl_message_type_support_t |
RMW publish/take、Python 绑定、introspection |
1.3 端到端流水线
1 | package.xml + CMakeLists.txt |
2. 仓库结构
1 | rosidl/ |
2.1 子包一览
| 包名 | 语言 | 职责 |
|---|---|---|
rosidl_adapter |
Python | Legacy 接口 → IDL |
rosidl_parser |
Python | IDL → AST |
rosidl_cmake |
CMake+Python | 构建编排、generate_files |
rosidl_generator_c |
Python+Empy | 生成 C 代码 |
rosidl_generator_cpp |
Python+Empy | 生成 C++ 代码 |
rosidl_runtime_c |
C | 字符串、序列、type_support 结构体 |
rosidl_runtime_cpp |
C++ | traits、初始化策略 |
rosidl_typesupport_interface |
C 头文件 | 符号命名约定 |
rosidl_typesupport_introspection_c |
Python+Empy | C introspection typesupport |
rosidl_typesupport_introspection_cpp |
Python+Empy | C++ introspection typesupport |
rosidl_typesupport_introspection_tests |
C++ | introspection 测试框架 |
rosidl_cli |
Python | 独立 CLI(非 colcon 主路径) |
3. rosidl_adapter:Legacy 格式 → IDL
3.1 入口
CMake 调用 rosidl_adapt_interfaces(),最终执行:
1 | # rosidl_adapter/main.py |
按后缀分发:
| 后缀 | 函数 | 输出目录 |
|---|---|---|
.msg |
convert_msg_to_idl |
output_dir/msg/ |
.srv |
convert_srv_to_idl |
output_dir/srv/ |
.action |
convert_action_to_idl |
output_dir/action/ |
3.2 Legacy 解析器(rosidl_adapter/parser.py)
与 rosidl_parser 独立:专门解析 ROS 1 风格 .msg 文本。
支持的 primitive 类型:
1 | bool, byte, char, float32, float64, |
语法元素:
- 注释:
# - 常量:
NAME=123 - 数组:
type[N]固定长度,type[]/type[<=N]动态/有界序列 - Service:以
---分隔 request/response - Action:三段
---分隔 goal/result/feedback
命名校验:package/field 名小写+下划线;message 名 PascalCase(兼容 ROS 1 的宽松模式可选)。
3.3 类型映射(MSG → IDL)
1 | # rosidl_adapter/msg/__init__.py |
3.4 IDL 模板(Empy)
msg.idl.em 将解析结果渲染为 OMG IDL 模块:
1 | module test_msgs { |
注释通过 @verbatim 注解保留。输出编码为 iso-8859-1(兼容非 ASCII 注释)。
Service / Action 模板(srv.idl.em、action.idl.em)生成对应 module 结构;Action 在 adapter 阶段仅转换用户定义的三段,衍生类型(SendGoal/GetResult 等)由 rosidl_parser 的 Action 类在解析 IDL 后构建。
4. rosidl_parser:IDL → AST
4.1 解析器实现
- 语法引擎:Lark(
grammar.lark) - 入口:
parse_idl_file(IdlLocator)→IdlFile(locator, content) - 调试:
idl2png可将语法树导出为 PNG
1 | # parser.py |
4.2 AST 类型体系(definition.py)
| 类型 | 说明 |
|---|---|
BasicType |
IDL 基本类型(int32、boolean、octet…) |
NamespacedType |
pkg::msg::Message 命名空间类型 |
Array / BoundedSequence / UnboundedSequence |
数组与序列 |
BoundedString / UnboundedString |
字符串(含 wstring) |
Member |
结构体字段 |
Constant |
模块级常量 |
Structure |
struct 定义 |
Message |
包装 Structure |
Service |
request/response 两个 Message |
Action |
goal/result/feedback + 衍生 service/message |
Include |
#include 依赖 |
Annotation |
@verbatim 等 |
IdlContent |
单文件全部内容 |
IdlLocator |
(basepath, relative_path) 定位器 |
命名后缀常量(全栈统一):
1 | SERVICE_REQUEST_MESSAGE_SUFFIX = '_Request' |
4.3 Action 衍生类型
用户 .action 文件只定义 goal/result/feedback 三段;Action 类自动构造:
| 衍生类型 | 成员 |
|---|---|
{Action}_SendGoal 服务 |
Request: goal_id (UUID) + goal;Response: accepted + stamp |
{Action}_GetResult 服务 |
Request: goal_id;Response: status + result |
{Action}_FeedbackMessage |
goal_id + feedback |
隐式依赖:builtin_interfaces/msg/Time.idl、unique_identifier_msgs/msg/UUID.idl。
因此 action 包必须在 package.xml 中依赖 action_msgs(CMake 会检查)。
5. rosidl_cmake:构建编排
5.1 rosidl_generate_interfaces 宏
核心步骤(rosidl_generate_interfaces.cmake):
- 校验接口文件存在;支持
basepath:relative_path元组 - 分离
.idl与 legacy 文件 - 对 legacy 文件调用
rosidl_adapt_interfaces生成.idl - 对
.srv额外在 build 目录生成_Request.msg/_Response.msg(供 linter/索引) - 收集
DEPENDENCIES中的 IDL 文件 - 注册 ament index
rosidl_interfaces ament_execute_extensions("rosidl_generate_idl_interfaces")— 触发所有 generator/typesupport- 安装
.idl/.msg到share/${PROJECT_NAME}/
package.xml 要求:安装接口的包须声明:
1 | <member_of_group>rosidl_interface_packages</member_of_group> |
5.2 扩展点机制
各 generator 通过 ament_register_extension 注册:
1 | # rosidl_generator_c/cmake/register_c.cmake |
拓扑顺序:后注册的 extension 可声明对先生成 target 的依赖。例如 introspection 要求:
1 | if(NOT TARGET ${target}__rosidl_generator_cpp) |
典型顺序:generator_c → generator_cpp → typesupport_*(introspection、fastrtps、cpp、py)。
5.3 generate_files() — 共享生成框架
rosidl_cmake/__init__.py 中:
- 读取 JSON generator arguments(
idl_tuples、template_dir、output_dir) - 对每个 IDL:
parse_idl_file(locator)得 AST - 用 Empy 渲染模板映射表
- 增量构建:比较 template/idl 与已有输出 mtime
C 与 C++ generator 仅 mapping 表 不同:
1 | # rosidl_generator_c |
5.4 rosidl_get_typesupport_target
供同包内其他 target 依赖生成的 typesupport 库:
1 | rosidl_get_typesupport_target(cpp_typesupport_target |
6. rosidl_generator_c / rosidl_generator_cpp
6.1 生成产物(以 std_msgs/msg/String 为例)
C 侧(rosidl_generator_c/):
| 文件 | 内容 |
|---|---|
string.h |
对外 include |
detail/string__struct.h |
C struct 定义 |
detail/string__functions.h/.c |
init/fini/copy/resize |
detail/string__type_support.h |
type_support 声明 |
C++ 侧(rosidl_generator_cpp/):
| 文件 | 内容 |
|---|---|
string.hpp |
对外 include |
detail/string__struct.hpp |
String_<ContainerAllocator> 模板 struct |
detail/string__builder.hpp |
fluent builder API |
detail/string__traits.hpp |
data_type()、name()、has_fixed_size 等特化 |
detail/string__type_support.hpp |
C++ type_support 声明 |
6.2 类型映射
IDL → C(BASIC_IDL_TYPES_TO_C):
boolean→booloctet→uint8_tchar→signed charwchar→uint16_t
IDL → C++(MSG_TYPE_TO_CPP):
- 基本类型 →
uint32_t等 string→std::basic_string<char, ...>(allocator 模板参数)- 嵌套类型 →
pkg::msg::Type_<ContainerAllocator> - 固定数组 →
std::array<T, N> - 动态序列 →
std::vector<T, Allocator>
6.3 生成的 CMake Target
每个 interface package 产生多个库 target,后缀模式:
| Target 后缀 | 内容 |
|---|---|
__rosidl_generator_c |
C struct + functions 源文件 |
__rosidl_generator_cpp |
仅头文件(INTERFACE 库) |
__rosidl_typesupport_introspection_c |
introspection C 实现 |
__rosidl_typesupport_introspection_cpp |
introspection C++ 实现 |
[外部] __rosidl_typesupport_cpp |
C++ typesupport 分发 |
[外部] __rosidl_typesupport_fastrtps_* |
Fast-DDS 序列化 |
7. rosidl_runtime_c / rosidl_runtime_cpp
7.1 C 运行时
字符串(string.h):
1 | typedef struct rosidl_runtime_c__String { |
宽字符串:u16string.h
动态数组:primitives_sequence.h + *_functions.h
有界序列:sequence_bound.h、string_bound.h
Type support 结构(message_type_support_struct.h):
1 | struct rosidl_message_type_support_t { |
typesupport_identifier:如"rosidl_typesupport_introspection_cpp"func:按 identifier 链式查找其他 typesupport 的 handleROSIDL_GET_MSG_TYPE_SUPPORT(Pkg, msg, MsgName):获取默认 C typesupport 符号
类似结构存在于 service_type_support_struct.h、action_type_support_struct.h。
消息初始化(message_initialization.h):ROSIDL_RUNTIME_C_MSG_INIT_ALL / ZERO / DEFAULTS_ONLY 等策略。
7.2 C++ 运行时
traits(traits.hpp)提供通用模板与工具函数:
rosidl_generator_traits::value_to_yaml()— 各类型 YAML 序列化has_fixed_size<T>/has_bounded_size<T>— 类型属性 traitis_message<T>/is_service_request<T>/is_action_goal<T>— 类型分类(由 generator 特化)
bounded_vector.hpp:有界动态数组的 C++ 容器实现。
message_initialization.hpp:C++ 侧对应初始化枚举。
8. Typesupport 体系
8.1 接口层(rosidl_typesupport_interface)
统一 符号命名,避免各 typesupport 插件冲突:
1 |
示例符号:rosidl_typesupport_introspection_cpp__get_message_type_support_handle__std_msgs__msg__String
8.2 Introspection Typesupport
用途:运行时反射消息字段——ros2 topic echo、rclpy、参数转换、调试工具。
核心结构(message_introspection.hpp):
1 | typedef struct MessageMember_s { |
Identifier(identifier.cpp):
1 | const char * typesupport_identifier = "rosidl_typesupport_introspection_cpp"; |
Generator 为每个消息生成 MessageMembers 静态表 + get_message_type_support_handle() 导出函数。
8.3 Typesupport 链与 RMW
运行时 publish 路径(简化):
1 | rclcpp::Publisher::publish(msg) |
Introspection typesupport 不参与 DDS 线格式序列化,但 Python 层需要它把 C struct 转为 Python 对象。
9. rosidl_cli
独立于 colcon 的 命令行工具(rosidl_cli/cli.py):
1 | rosidl generate ... # 调用已注册 generator 扩展 |
通过 rosidl_cli/extensions.py 与 entry_points 可扩展子命令;主要用于工具开发/debug,普通接口包构建走 CMake 路径。
10. 完整示例:从 .msg 到可链接库
10.1 作者侧
my_msgs/msg/Velocity.msg:
1 | float64 linear |
CMakeLists.txt:
1 | find_package(rosidl_default_generators REQUIRED) |
package.xml:
1 | <buildtool_depend>rosidl_default_generators</buildtool_depend> |
10.2 构建时发生的事
10.3 安装布局
1 | install/my_msgs/ |
11. Service 与 Action 的处理差异
| 类型 | Adapter 输出 | Parser 产物 | 额外生成 |
|---|---|---|---|
| Message | 单 struct IDL | Message |
1 组 msg 文件 |
| Service | request/response struct | Service + 两个 Message |
Request/Response 消息 + srv typesupport |
| Action | goal/result/feedback struct | Action + 衍生 3 类 |
Goal/Result/Feedback + SendGoal/GetResult + FeedbackMessage |
CMake 对 .srv 还会在 build 目录拆分出 _Request.msg / _Response.msg,便于 rosidl_interfaces index 与 linter。
12. 关联仓库(非本目录)
| 包 | 仓库路径(Humble) | 职责 |
|---|---|---|
rosidl_default_generators |
ros2/rosidl_defaults |
聚合默认 generator + typesupport 依赖 |
rosidl_default_runtime |
ros2/rosidl_defaults |
运行时依赖聚合 |
rosidl_generator_py |
ros2/rosidl_python |
Python 模块生成 |
rosidl_typesupport_c/cpp |
ros2/rosidl_typesupport |
typesupport 分发层 |
rosidl_typesupport_fastrtps_* |
ros2/rosidl_typesupport_fastrtps |
Fast-DDS CDR 序列化 |
rosidl_typesupport_introspection_* |
本仓库 | 反射 typesupport |
find_package(rosidl_default_generators) 会拉入上述外部 generator,使 rosidl_generate_interfaces 一次触发完整工具链。
13. 与 ROS 1 genmsg 对比
| 维度 | ROS 1 genmsg | rosidl |
|---|---|---|
| 源格式 | .msg only |
.msg/.srv/.action + .idl |
| 中间表示 | 内部 AST | 标准 OMG IDL |
| 生成语言 | C++(roscpp) | C + C++ + Python |
| Typesupport | 无插件概念 | 多 typesupport 插件 |
| 字符串 | std::string |
rosidl_runtime_c__String / 可配置 allocator |
| 数组 | 固定/vector | 固定 array + bounded/unbounded sequence |
| Action | 无原生 | 原生 .action + 衍生类型 |
| 构建 | catkin msg 宏 | ament rosidl_generate_interfaces + extensions |
14. 调试与常见问题
| 现象 | 排查方向 |
|---|---|
rosidl_generate_interfaces 报错缺 action_msgs |
action 包未声明依赖 |
缺少 rosidl_interface_packages group |
package.xml 未加 member_of_group |
| 找不到 typesupport symbol | 未 link __rosidl_typesupport_cpp target |
修改 .msg 未生效 |
检查 build 目录缓存;clean rebuild |
| IDL 解析失败 | 用 rosidl translate 或 idl2png 看语法树 |
| Python 导入失败 | 确认 rosidl_generator_py 已执行(外部包) |
| introspection 字段偏移错误 | 查看 rosidl_typesupport_introspection_tests 对应用例 |
实用命令:
1 | # 查看某包安装了哪些接口 |
15. 源码阅读顺序
- 走通样例:手写最小 msg 包,
colcon build后对照build/与install/生成物 - 适配层:
rosidl_adapter/parser.py→msg/__init__.py→resource/msg.idl.em - IDL AST:
rosidl_parser/definition.py→parser.py→grammar.lark - CMake 编排:
rosidl_generate_interfaces.cmake→register_c.cmake - 生成框架:
rosidl_cmake/__init__.py的generate_files() - C 生成:
rosidl_generator_c/__init__.py+resource/idl__struct.h.em - C++ 生成:
rosidl_generator_cpp/__init__.py+__traits.hpp.em - 运行时:
message_type_support_struct.h→traits.hpp - Introspection:
message_introspection.hpp→rosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em - 外部衔接:
rosidl_typesupport_cpp(另一仓库)→rmw_fastrtps
16. 小结
rosidl 目录实现 ROS 2 强类型接口的编译期核心:rosidl_adapter 兼容 Legacy 格式,rosidl_parser 提供标准 IDL AST,rosidl_cmake 通过 ament 扩展点 编排多 generator/typesupport 并行生成,rosidl_runtime_* 定义运行时公共类型与 type_support 结构,rosidl_typesupport_introspection_* 提供字段反射能力。
理解「.msg 如何变成 DDS 线上的字节」需串联 本仓库生成链路 与 外部 typesupport_fastrtps + rmw 两段;调试接口问题时,从 rosidl_generate_interfaces 的 build log 与生成的 detail/*__struct.hpp 入手最为直接。
正在加载留言…