rcl 源码详细分析

rcl 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rcl
版本:5.3.13(Humble),子包 4 个,构建类型 ament_cmake,语言 C,许可证 Apache 2.0

rcl(ROS Client Library)是 ROS 2 客户端库的 C 实现层,封装 rmw(ROS Middleware Interface),为 rclcpprclpy 及其他语言绑定提供语言无关、稳定 ABI 边界的统一 C API。所有 DDS/中间件细节都在 rcl 以下;调试通信问题时常需追到 rcl → rmw 返回值链。


1. 总体认识

1.1 核心职责

能力 说明
生命周期 rcl_init() / rcl_shutdown() / rcl_context_t 管理
节点 创建/销毁 node,namespace 校验,logger 名生成
Pub/Sub Publisher/Subscription 封装,topic 展开与 remap
Service/Client 请求-响应 RPC 封装
Timer 基于 clock + guard_condition 的定时器
Wait Set 聚合 subscription/service/client/timer/event,调用 rmw_wait()
Graph 节点/topic/service introspection(ros2 topic list 底层)
参数解析 解析 --remap--params-file 等 CLI 参数
Action / Lifecycle 独立子包,基于 rcl 的 pub/sub/service 组合实现

1.2 在 ROS 2 栈中的位置

语言客户端rcl 仓库下层rclcpprclpy / _rclpyrcl 核心rcl_actionrcl_lifecyclercl_yaml_param_parserrmw_implementationrcutilsrosidl_runtime_cFast-DDS / CycloneDDS
上层消费者 使用的 rcl 能力
rclcpp 全部核心 API + rcl_action + rcl_lifecycle
rclpy 通过 C 扩展调用相同 API
ros2cli 间接通过 rclcpp/rclpy 使用 graph API
launch 通过节点进程 CLI 参数触发 rcl_parse_arguments()

注意参数服务get_parameters 等)的实现主体在 rclcpp/rclpy,不在 rcl 核心库内。rcl 只负责解析 --params-file YAML(通过 rcl_yaml_param_parser),供上层加载初始参数。


2. 子包结构

1
2
3
4
5
6
7
rcl/
├── rcl/ # 核心库 ★
│ ├── include/rcl/ # 37 个公开头文件
│ └── src/rcl/ # 32 个 .c 实现(~11.5K 行)
├── rcl_action/ # Action C API
├── rcl_lifecycle/ # Lifecycle 状态机
└── rcl_yaml_param_parser/ # YAML 参数文件解析
版本 职责
rcl 5.3.13 init、node、pub/sub、service/client、timer、wait、graph、arguments
rcl_action 5.3.13 Action client/server、goal handle、goal 状态机
rcl_lifecycle 5.3.13 Lifecycle 状态/转移/默认状态机
rcl_yaml_param_parser 5.3.13 解析 --params-file YAML → rcl_params_t

2.1 依赖关系(rcl/package.xml

1
2
3
4
5
6
7
8
rcl
├── rmw_implementation # 运行时加载的 RMW 实现
├── rcutils # 日志、错误、分配器、时间
├── rosidl_runtime_c # 消息 typesupport 接口
├── rcl_interfaces # 内置服务/msg 定义
├── rcl_yaml_param_parser # YAML 参数解析
├── rcl_logging_spdlog # 默认日志后端
└── tracetools # LTTng 追踪点

3. 模块划分(官方文档)

rcl.h 是总入口,按 ROS 概念组织 API:

1
2
3
4
5
6
7
8
9
* - Nodes          → rcl/node.h
* - Publisher → rcl/publisher.h
* - Subscription → rcl/subscription.h
* - Service Client → rcl/client.h
* - Service Server → rcl/service.h
* - Timer → rcl/timer.h
* - Wait sets → rcl/wait.h
* - Graph → rcl/graph.h
* - Init/Shutdown → rcl/init.h

辅助模块:

头文件 职责
context.h rcl_context_t 生命周期
init_options.h init 选项(domain_id、allocator 等)
arguments.h / remap.h CLI 参数、remap 规则
guard_condition.h 异步唤醒 wait set
event.h QoS 事件(deadline、liveliness 等)
time.h ROS Time / Steady Time / System Time
logging.h / logging_rosout.h 日志与 /rosout 发布
security.h 从环境变量加载安全选项
domain_id.h / localhost.h ROS_DOMAIN_IDROS_LOCALHOST_ONLY
validate_topic_name.h / expand_topic_name.h topic 名校验与展开
error_handling.h / types.h 错误码与返回值
allocator.h 可注入的内存分配器

4. 核心数据结构

4.1 rcl_context_t — 进程级上下文

1
2
3
4
5
6
7
typedef struct rcl_context_s
{
/// Global arguments for all nodes which share this context.
rcl_arguments_t global_arguments;

/// Implementation specific pointer.
rcl_context_impl_t * impl;

rcl_context_impl_t 内部持有:

1
2
3
4
5
6
7
8
struct rcl_context_impl_s
{
rcl_allocator_t allocator;
rcl_init_options_t init_options;
int64_t argc;
char ** argv;
rmw_context_t rmw_context;
};

生命周期:

1
zero-init → rcl_init() → [valid] → rcl_shutdown() → [invalid] → 销毁所有 entity → rcl_context_fini()
  • 一个 context 可创建多个 node(共享同一 rmw_context
  • rcl_shutdown() 后 context 仍”已初始化但无效”,entity 可继续 cleanup
  • 支持多 context(多 init 场景,如测试)

4.2 rcl_node_t — 节点

1
2
3
4
5
6
7
8
struct rcl_node_impl_s
{
rcl_node_options_t options;
rmw_node_t * rmw_node_handle;
rcl_guard_condition_t * graph_guard_condition;
const char * logger_name;
const char * fq_name;
};
  • rmw_node_handle:RMW 层节点句柄
  • graph_guard_condition:图变化时唤醒 wait set(新 publisher 出现等)
  • logger_name:如 /a/b 命名空间下节点 c → logger 名 a.b.c
  • fq_name:完全限定名

4.3 rcl_wait_set_t — 等待集合

1
2
3
4
5
6
7
8
typedef struct rcl_wait_set_s
{
const rcl_subscription_t ** subscriptions;
size_t size_of_subscriptions;
const rcl_guard_condition_t ** guard_conditions;
// ... timers, clients, services, events
rcl_wait_set_impl_t * impl;
} rcl_wait_set_t;

内部 rcl_wait_set_impl_t 聚合对应的 rmw_subscriptions_trmw_guard_conditions_t 等,最终调用 rmw_wait()


5. 关键流程

5.1 初始化 — rcl_init()

1
2
3
4
5
6
7
8
9
10
11
12
13
rcl_ret_t rcl_init(int argc, char const * const * argv,
const rcl_init_options_t * options, rcl_context_t * context)
{
// 1. 分配 context->impl
// 2. 复制 argc/argv
// 3. rcl_parse_arguments() — 解析 --remap, --params-file, --enclave 等
// 4. 设置 instance_id(全局唯一)
// 5. 解析 ROS_DOMAIN_ID(若未指定)
// 6. 解析 ROS_LOCALHOST_ONLY
// 7. 设置 enclave 名(默认 "/")
// 8. rcl_get_security_options_from_environment()
// 9. rmw_init() — 初始化中间件
}

要点:

步骤 函数 说明
参数解析 rcl_parse_arguments() 最大文件(2079 行),处理所有 ROS CLI 参数
Domain ID rcl_get_default_domain_id() ROS_DOMAIN_ID 环境变量
安全 rcl_get_security_options_from_environment() SROS2 证书路径
中间件 rmw_init() 进入 Fast-DDS/CycloneDDS

5.2 创建节点 — rcl_node_init()

1
2
3
4
5
6
7
8
9
10
11
12
rcl_ret_t rcl_node_init(rcl_node_t * node, const char * name,
const char * namespace_, rcl_context_t * context,
const rcl_node_options_t * options)
{
// 1. 校验 node name / namespace(rmw_validate_*)
// 2. namespace 规范化(空 → "/",无前缀 → 加 "/")
// 3. remap node name / namespace
// 4. rmw_create_node()
// 5. 创建 graph_guard_condition
// 6. 生成 logger_name, fq_name
// 7. rcl_logging_rosout_init() — 可选 /rosout 发布
}

5.3 发布 — rcl_publisher_init() + rcl_publish()

初始化

1
2
3
4
5
6
rcl_ret_t rcl_publisher_init(...)
{
// 1. rcl_node_resolve_name() — 展开相对名 + remap
// 2. rmw_create_publisher(node, type_support, remapped_topic, qos)
// 3. rmw_publisher_get_actual_qos() — 存储实际 QoS
}

发布

1
2
3
4
5
6
7
8
9
10
11
rcl_ret_t rcl_publish(const rcl_publisher_t * publisher,
const void * ros_message,
rmw_publisher_allocation_t * allocation)
{
TRACEPOINT(rcl_publish, ...);
if (rmw_publish(publisher->impl->rmw_handle, ros_message, allocation) != RMW_RET_OK) {
RCL_SET_ERROR_MSG(rmw_get_error_string().str);
return RCL_RET_ERROR;
}
return RCL_RET_OK;
}

其他 publish 变体:

函数 用途
rcl_publish_serialized_message() 直接发已序列化字节
rcl_publish_loaned_message() 零拷贝 loaned buffer
rcl_borrow_loaned_message() 从 RMW 借出写入 buffer

5.4 订阅 — rcl_subscription_init() + rcl_take()

取消息

1
2
3
4
5
6
7
8
9
rcl_ret_t rcl_take(const rcl_subscription_t * subscription,
void * ros_message, rmw_message_info_t * message_info,
rmw_subscription_allocation_t * allocation)
{
bool taken = false;
rmw_ret_t ret = rmw_take_with_info(
subscription->impl->rmw_handle, ros_message, &taken, message_info_local, allocation);
// taken == false → RCL_RET_SUBSCRIPTION_TAKE_FAILED
}
函数 说明
rcl_take() 取走后从队列移除
rcl_take_sequence() 批量取
rcl_take_serialized_message() 取原始字节
rcl_take_loaned_message() 零拷贝取
rcl_subscription_set_content_filter() DDS ContentFilteredTopic

5.5 等待与调度 — rcl_wait()

rclcpp::Executor::spin() 底层即循环调用 rcl_wait()

1
2
3
4
5
6
7
8
rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
{
// 1. 校验 wait_set 非空
// 2. 遍历 timers → 计算最近到期时间 → 合并到 timeout
// 3. 将 timer 的 guard_condition 加入 rmw_guard_conditions
// 4. rmw_wait(subscriptions, guard_conditions, services, clients, events, timeout)
// 5. 返回后,未 ready 的 entity 指针置 NULL
}

Wait set 使用模式:

1
2
3
4
5
6
7
8
9
10
rcl_wait_set_t ws = rcl_get_zero_initialized_wait_set();
rcl_wait_set_init(&ws, n_subs, n_gc, n_timers, n_clients, n_services, n_events, context, allocator);

// 循环:
rcl_wait_set_clear(&ws);
rcl_wait_set_add_subscription(&ws, sub);
rcl_wait_set_add_timer(&ws, timer);
rcl_wait(&ws, timeout); // 阻塞
// 检查 ws.subscriptions[i] 是否仍为 non-NULL → ready
rcl_take(sub, msg, ...);

5.6 Timer

1
2
3
4
5
6
7
8
9
10
11
struct rcl_timer_impl_s
{
rcl_clock_t * clock;
rcl_context_t * context;
rcl_guard_condition_t guard_condition; // 到期时触发
atomic_uintptr_t callback;
atomic_uint_least64_t period;
atomic_int_least64_t next_call_time;
atomic_bool canceled;
rcl_allocator_t allocator;
};
  • Timer 不独立线程,依赖 rcl_wait() 检测 guard_condition 或超时
  • 支持 RCL_ROS_TIME 跳变(sim time 切换时的 credit 机制)
  • rcl_timer_call() 手动触发回调

5.7 Service / Client

与 pub/sub 对称:

操作 Service 端 Client 端
创建 rcl_service_init()rmw_create_service() rcl_client_init()rmw_create_client()
收发 rmw_take_request() / rmw_send_response() rmw_send_request() / rmw_take_response()
Wait 加入 rcl_wait_set 加入 rcl_wait_set

5.8 Graph API

路径:src/rcl/graph.c(744 行)

函数 用途
rcl_get_node_names() 列出所有节点名
rcl_get_topic_names_and_types() 所有 topic 及类型
rcl_get_service_names_and_types() 所有 service
rcl_count_publishers() / rcl_count_subscribers() 某 topic 的 pub/sub 数量
rcl_get_publisher_names_and_types_by_node() 某节点的 publisher 列表
rcl_get_subscriber_names_and_types_by_node() 某节点的 subscription 列表

底层全部转发到 rmw_get_* 系列函数。ROS 2 CLI 的 ros2 topic listros2 node list 最终依赖这些 API。


6. 参数与 Remap

6.1 arguments.c — CLI 解析核心

最大源文件(2079 行),处理:

CLI 参数 解析结果
--remap __ns:=/foo remap 规则
--remap __node:=bar 节点名 remap
--remap from:=to topic/service remap
--params-file file.yaml 参数文件路径
-p name:=value 参数覆盖
--enclave /my_enclave 安全 enclave

Remap 规则解析使用 lexer.c(676 行)+ lexer_lookahead.c 做 token 化。

6.2 rcl_yaml_param_parser

路径:rcl_yaml_param_parser/src/

文件 行数 职责
parse.c 1037 YAML 词法/语法解析
parser.c 449 公开 API
yaml_variant.c 177 参数值类型(bool/int/double/string/array)
namespace.c 命名空间处理
node_params.c 145 按节点组织参数

YAML 格式:

1
2
3
4
5
6
/my_node:
ros__parameters:
param1: 42
param2: "hello"
nested:
sub_param: true

解析结果存入 rcl_params_t,由 rcl_arguments 持有,上层(rclcpp)在 node 创建时读取。


7. rcl_action — Action 子包

路径:rcl_action/src/rcl_action/

Action 在 rcl 层不引入新中间件概念,而是将 Action 协议映射为 topics + services 组合:

Action 概念 底层实现
SendGoal service ~/action_name/_action/send_goal
CancelGoal service ~/action_name/_action/cancel_goal
GetResult service ~/action_name/_action/get_result
Feedback topic ~/action_name/_action/feedback
Status topic ~/action_name/_action/status

核心 API:

头文件 职责
action_server.h / action_client.h 创建/销毁 action server/client
goal_handle.h 单个 goal 的生命周期管理
goal_state_machine.h goal 状态转移(PENDING→ACTIVE→SUCCEEDED 等)
wait.h action 专用 wait set 扩展
names.h 生成 action 相关 topic/service 名
graph.h action 图 introspection

action_server.c 中通过宏 SERVICE_INIT(Type) 批量创建 send_goal/cancel_goal/get_result 三个 service。


8. rcl_lifecycle — Lifecycle 子包

路径:rcl_lifecycle/src/

提供纯 C 状态机,不含网络通信——上层(rclcpp_lifecycle)负责将状态变化发布到 ~/transition_event topic 和 ~/change_state service。

文件 职责
default_state_machine.c 预置状态(Unconfigured/Inactive/Active/Finalized)和转移
transition_map.c 自定义状态机映射
rcl_lifecycle.c 状态 init/trigger/get 等 API
com_interface.c lifecycle_msgs 的转换

核心类型:rcl_lifecycle_state_trcl_lifecycle_transition_trcl_lifecycle_state_machine_t


9. 错误处理

C 风格,无异常

机制 说明
返回值 rcl_ret_tRCL_RET_OK = 0,其余为错误码)
错误消息 RCL_SET_ERROR_MSG() → 线程局部字符串
读取 rcl_get_error_string()
RMW 转换 rcl_convert_rmw_ret_to_rcl_ret()

常见错误码(types.h):

范围 示例
通用 RCL_RET_OKRCL_RET_ERRORRCL_RET_BAD_ALLOC
Context RCL_RET_ALREADY_INITRCL_RET_NOT_INITRCL_RET_ALREADY_SHUTDOWN
Node RCL_RET_NODE_INVALID_NAMERCL_RET_NODE_INVALID_NAMESPACE
Pub/Sub RCL_RET_PUBLISHER_INVALIDRCL_RET_SUBSCRIPTION_TAKE_FAILED
Wait RCL_RET_WAIT_SET_EMPTYRCL_RET_TIMEOUT
Timer RCL_RET_TIMER_CANCELED

10. 源文件布局与规模

10.1 rcl 核心(按行数排序)

文件 行数 职责
arguments.c 2079 CLI 参数/remap/params 解析
subscription.c 783 订阅 init/take/loan/filter
graph.c 744 图 introspection
lexer.c 676 remap 规则词法分析
wait.c 672 wait set + rcl_wait()
node.c 541 节点 init/fini/resolve_name
time.c 475 时钟类型与时间跳变
timer.c 473 定时器
publisher.c 455 发布 init/publish/loan
init.c 260 init/shutdown
service.c / client.c ~350 服务端/客户端
remap.c ~300 remap 规则应用
guard_condition.c ~200 guard condition
event.c ~200 QoS 事件
logging*.c ~400 日志与 rosout

10.2 内部头文件(src/rcl/ 私有)

文件 用途
context_impl.h context 内部结构
init_options_impl.h init 选项内部结构
arguments_impl.h 参数解析内部结构
publisher_impl.h / subscription_impl.h pub/sub 内部结构
remap_impl.h remap 规则存储
common.h 公共辅助函数

11. 完整数据路径

11.1 发布一条消息

1
2
3
4
5
6
rclcpp::Publisher::publish(msg)
→ rcl_publish(publisher, &msg, allocation)
→ rmw_publish(rmw_handle, &msg, allocation) [rmw_fastrtps]
→ DataWriter::write(msg)
→ TypeSupport::serialize() [Fast-CDR]
→ RTPS 发送

11.2 Executor spin 一次迭代

1
2
3
4
5
6
7
8
9
10
11
12
rclcpp::Executor::spin_some()
→ rcl_wait_set_clear/add_*
→ rcl_wait(wait_set, timeout)
→ rmw_wait(subs, gcs, services, clients, events, timeout)
→ DDS WaitSet 阻塞
→ 对每个 ready subscription:
→ rcl_take(sub, msg, &info, allocation)
→ rmw_take_with_info(...)
→ 对每个 ready timer:
→ rcl_timer_call(timer)
→ 对每个 ready service:
→ rmw_take_request(...) → 用户回调 → rmw_send_response(...)

12. 设计特点小结

特点 说明
纯 C 无 C++ 依赖,稳定 ABI,多语言绑定基础
薄封装 大部分函数是参数校验 + rmw_* 转发
零初始化模式 所有 entity 提供 rcl_get_zero_initialized_*()
可注入分配器 rcl_allocator_t 贯穿所有 init 函数
Context 隔离 多 context 支持测试和多 init 场景
Remap 在 rcl 层 topic/node namespace 重映射对上层透明
Timer 无独立线程 依赖 wait set + guard_condition
追踪集成 TRACEPOINT(rcl_init/rcl_publish/...) via tracetools

13. 与相邻层对比

语言 职责 关键抽象
rclcpp C++ 类型安全、Executor、参数服务 Node、Publisher<T>、Executor
rcl C ROS 概念封装、CLI 解析 rcl_node_t、rcl_wait_set_t
rmw C 中间件抽象接口 rmw_publisher_t、rmw_wait
Fast-DDS C++ DDS/RTPS 实现 DomainParticipant、DataWriter

14. 推荐阅读顺序

  1. 总览include/rcl/rcl.h — 模块地图
  2. 生命周期init.ccontext.cinit_options.c
  3. 节点node.c — 理解 namespace/remap/logger
  4. 通信publisher.c + subscription.c — init/publish/take 链路
  5. 调度wait.c + timer.c + guard_condition.c — 理解 spin 底层
  6. CLIarguments.c(可按需跳读)— remap/params 解析
  7. 图 APIgraph.c
  8. 扩展rcl_action/action_server.crcl_lifecycle/rcl_lifecycle.c
  9. 上层集成rclcpp/executor.cpp — 看 rcl_wait 如何被使用

15. API 速查

15.1 最小 C 节点示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <rcl/rcl.h>

int main(int argc, char ** argv)
{
rcl_ret_t ret;
rcl_init_options_t init_options = rcl_get_zero_initialized_init_options();
rcl_init_options_init(&init_options, rcl_get_default_allocator());

rcl_context_t context = rcl_get_zero_initialized_context();
ret = rcl_init(argc, argv, &init_options, &context);

rcl_node_t node = rcl_get_zero_initialized_node();
rcl_node_options_t node_options = rcl_node_get_default_options();
rcl_node_init(&node, "my_node", "", &context, &node_options);

// ... create publisher/subscription, rcl_wait loop ...

rcl_node_fini(&node);
rcl_shutdown(&context);
rcl_context_fini(&context);
rcl_init_options_fini(&init_options);
return 0;
}

15.2 关键函数一览

类别 函数
Init rcl_init(), rcl_shutdown(), rcl_context_fini()
Node rcl_node_init(), rcl_node_fini(), rcl_node_get_name()
Pub rcl_publisher_init(), rcl_publish(), rcl_publisher_fini()
Sub rcl_subscription_init(), rcl_take(), rcl_subscription_fini()
Wait rcl_wait_set_init(), rcl_wait_set_add_*(), rcl_wait()
Timer rcl_timer_init(), rcl_timer_call(), rcl_timer_is_ready()
Graph rcl_get_node_names(), rcl_get_topic_names_and_types()
Args rcl_parse_arguments(), rcl_get_remap_rules()

文档基于 ROS 2 Humble 工作区中的 rcl 5.3.13 源码分析生成。

文章互动

阅读 --

留言

0 条留言

正在加载留言…