rclcpp 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rclcpp
版本:16.0.19(Humble),子包 4 个,构建类型 ament_cmake,语言 C++17,许可证 Apache 2.0。
rclcpp(ROS Client Library for C++)是 ROS 2 C++ 客户端库,几乎所有 C++ 节点、rviz2、Nav2、MoveIt 2 等均构建于此。它在 rcl(C API)之上提供类型安全的 C++ 抽象,并引入 Executor、callback_group、intra-process、参数服务 等 ROS 2 特有机制。理解 rclcpp 是掌握 ROS 2 C++ 应用并发模型与通信路径的关键。
1. 总体认识
1.1 核心职责
| 能力 |
说明 |
| 生命周期 |
rclcpp::init() / rclcpp::shutdown(),Context 封装 rcl_init/rcl_shutdown |
| Node |
创建 pub/sub/service/client/timer,namespace 与 remap |
| Executor |
调度 ready 回调(subscription/timer/service/client/waitable) |
| CallbackGroup |
互斥/可重入分组,控制并发语义 |
| Intra-process |
同进程零拷贝/共享指针消息传递 |
| 参数 |
declare/get/set + 标准参数服务(rcl_interfaces) |
| 时间 |
Clock / TimeSource,订阅 /clock 实现 sim time |
| QoS |
C++ 封装 rmw_qos_profile_t,支持 qos_overrides |
| Action / Lifecycle / Components |
独立子包扩展 |
1.2 在 ROS 2 栈中的位置
| 下层依赖 |
rclcpp 如何使用 |
| rcl |
所有 pub/sub/service/client/timer/wait 均最终调用 rcl_* |
| rcl_action |
rclcpp_action 封装 action client/server |
| rcl_lifecycle |
rclcpp_lifecycle 封装 lifecycle 状态机 |
| rcl_interfaces |
参数服务、parameter_events 消息类型 |
| rosgraph_msgs |
/clock 话题类型 |
| libstatistics_collector |
可选 topic 统计 |
与 rcl 的分工:rcl 提供语言无关 C API 与 wait set;参数服务的实现主体在 rclcpp(ParameterService),rcl 只负责 CLI 参数/YAML 解析。Executor、callback_group、intra-process 均为 rclcpp 独有,不在 rcl 层。
2. 子包结构
1 2 3 4 5 6 7 8 9
| rclcpp/ ├── rclcpp/ # 核心库 ★ │ ├── include/rclcpp/ # ~85 个顶层头文件 + node_interfaces/ 等 │ ├── include/rclcpp/node_interfaces/ # 11 组 interface │ ├── src/rclcpp/ # 73 个 .cpp(~11.8K 行) │ └── src/rclcpp/executors/ # 4 种 Executor 实现 ├── rclcpp_action/ # Action C++ API ├── rclcpp_components/ # Composable Node + ComponentManager └── rclcpp_lifecycle/ # LifecycleNode
|
| 包 |
版本 |
职责 |
rclcpp |
16.0.19 |
Node、Executor、pub/sub/service/timer、参数、QoS、intra-process |
rclcpp_action |
16.0.19 |
Client/Server/ClientGoalHandle,封装 rcl_action |
rclcpp_components |
16.0.19 |
动态加载 .so 组件,ComponentManager 提供 load/unload/list |
rclcpp_lifecycle |
16.0.19 |
LifecycleNode、状态/转移、LifecyclePublisher |
2.1 源码规模(核心包 rclcpp/rclcpp)
| 指标 |
数量 |
公开头文件(.hpp) |
~148 |
实现文件(.cpp) |
~73 |
核心 .cpp 总行数 |
~11,794 |
| 最大单文件 |
executor.cpp(947 行)、node.cpp(607 行)、time_source.cpp(554 行) |
3. 依赖关系
3.1 rclcpp/package.xml
1 2 3 4 5 6 7 8 9 10 11
| rclcpp ├── rcl # C 客户端库 ├── rcl_yaml_param_parser # YAML 参数文件(经 rcl 间接使用) ├── rmw # QoS 类型、GID 等 ├── rcutils / rcpputils # 日志、ScopeExit、文件系统 ├── rosidl_runtime_cpp # 消息 C++ 运行时 ├── rosidl_typesupport_cpp # typesupport 查找 ├── rcl_interfaces # 参数 srv/msg ├── rosgraph_msgs # Clock 消息 ├── libstatistics_collector # topic 统计 └── tracetools # LTTng 追踪点
|
3.2 子包额外依赖
| 子包 |
关键依赖 |
rclcpp_action |
rcl_action, action_msgs, rosidl_runtime_c |
rclcpp_components |
class_loader, composition_interfaces, ament_index_cpp |
rclcpp_lifecycle |
rcl_lifecycle, lifecycle_msgs |
4. 核心设计:Node + node_interfaces
4.1 Node 是用户 API 入口
1 2
| /// Node is the single point of entry for creating publishers and subscribers. class Node : public std::enable_shared_from_this<Node>
|
用户通过 Node::create_publisher()、create_subscription() 等创建通信实体;内部不直接持有 rcl_node_t,而是通过 组合式 interface 拆分职责。
4.2 Interface 组合(构造顺序)
Node 构造函数按固定顺序实例化各 interface,并相互注入依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Node::Node( const std::string & node_name, const std::string & namespace_, const NodeOptions & options) : node_base_(new rclcpp::node_interfaces::NodeBase(...)), node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())), node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())), node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())), node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get(), node_timers_.get())), node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())), node_clock_(new rclcpp::node_interfaces::NodeClock(...)), node_parameters_(new rclcpp::node_interfaces::NodeParameters(...)), node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(...)), node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())), ...
|
| Interface |
实现类 |
职责 |
NodeBaseInterface |
NodeBase |
rcl_node_t 句柄、FQN、context、intra-process 开关 |
NodeGraphInterface |
NodeGraph |
topic/service 图 introspection |
NodeLoggingInterface |
NodeLogging |
节点 logger |
NodeTimersInterface |
NodeTimers |
创建/管理 timer |
NodeTopicsInterface |
NodeTopics |
create_publisher / create_subscription |
NodeServicesInterface |
NodeServices |
create_service / create_client |
NodeClockInterface |
NodeClock |
节点 clock |
NodeParametersInterface |
NodeParameters |
declare/get/set 参数、回调 |
NodeTimeSourceInterface |
NodeTimeSource |
订阅 /clock、驱动 sim time |
NodeWaitablesInterface |
NodeWaitables |
注册 waitable(含 action client 等) |
设计动机:
- Component 友好:
ComponentManager 只需 NodeBaseInterface 等指针即可把节点挂到 Executor
- 可测试:mock 单个 interface 而不构造完整 Node
- LifecycleNode 复用:
rclcpp_lifecycle::LifecycleNode 同样组合这些 interface
4.3 NodeOptions
NodeOptions 集中配置:
context()、use_intra_process_comms()、enable_topic_statistics()
start_parameter_services()、parameter_overrides()、allow_undeclared_parameters()
get_rcl_node_options() → 底层 rcl_node_options_t(含 remap、use_global_arguments)
- QoS 预设:
parameter_event_qos()、clock_qos() 等
5. Context 与 init
5.1 全局 init 流程
1 2 3 4 5 6 7 8 9 10 11
| void init( int argc, char const * const * argv, const InitOptions & init_options, SignalHandlerOptions signal_handler_options) { using rclcpp::contexts::get_global_default_context; get_global_default_context()->init(argc, argv, init_options); install_signal_handlers(signal_handler_options); }
|
rclcpp::init() → 默认 Context::init() → rcl_init(),并安装 SIGINT/SIGTERM 处理器(触发 shutdown)。
5.2 Context 封装 rcl_context
1 2 3 4 5 6 7 8 9 10 11 12 13
| Context::init( int argc, char const * const * argv, const rclcpp::InitOptions & init_options) { ... rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), context); ... rcl_context_.reset(context, __delete_context); if (init_options.auto_initialize_logging()) { rcl_logging_configure_with_output_handler(...); } }
|
| 特性 |
说明 |
| 多 Context |
WeakContextsWrapper 跟踪所有已创建 context,支持多 init 场景 |
| shutdown 回调 |
add_on_shutdown_callback() 注册清理逻辑 |
| 有效性 |
context->is_valid() 对应 rcl_context_is_valid() |
| 默认 context |
contexts/default_context.hpp 提供进程级单例 |
5.3 辅助 API
| API |
作用 |
rclcpp::ok() |
检查默认 context 是否仍有效 |
rclcpp::shutdown() |
关闭默认 context |
rclcpp::remove_ros_arguments() |
剥离 ROS 特有 CLI 参数 |
rclcpp::spin(node) |
便捷函数:SingleThreadedExecutor + add_node + spin |
6. Executor 架构
Executor 是 rclcpp 并发与调度核心:将「通信图」与「执行模型」解耦——节点创建实体,Executor 决定何时执行回调。
6.1 类层次
| 类 |
文件 |
特点 |
Executor |
executor.hpp / executor.cpp |
基类:wait set、实体收集、execute |
SingleThreadedExecutor |
executors/single_threaded_executor.cpp |
单线程 spin 循环 |
MultiThreadedExecutor |
executors/multi_threaded_executor.cpp |
N 线程并行 execute |
StaticSingleThreadedExecutor |
executors/static_single_threaded_executor.hpp |
静态实体列表,减少每轮重建开销 |
6.2 spin 主循环(SingleThreadedExecutor)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void SingleThreadedExecutor::spin() { if (spinning.exchange(true)) { throw std::runtime_error("spin() called while already spinning"); } RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); ); while (rclcpp::ok(this->context_) && spinning.load()) { rclcpp::AnyExecutable any_executable; if (get_next_executable(any_executable)) { execute_any_executable(any_executable); } } }
|
6.3 get_next_executable 两阶段
1 2 3 4 5 6 7 8 9 10 11 12 13
| bool Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanoseconds timeout) { bool success = get_next_ready_executable(any_executable); if (!success) { wait_for_work(timeout); if (!spinning.load()) { return false; } success = get_next_ready_executable(any_executable); } return success; }
|
get_next_ready_executable:按优先级扫描已 ready 实体(timer → subscription → service → client → waitable)
wait_for_work:若无 ready 实体,则 collect_entities → rcl_wait_set_resize → rcl_wait()
6.4 wait_for_work 与 rcl wait set
核心步骤(executor.cpp):
memory_strategy_->collect_entities(weak_groups_to_nodes_) — 收集本 Executor 管理的 callback group 内实体
rcl_wait_set_clear / rcl_wait_set_resize — 按实体数量调整 wait set
memory_strategy_->add_handles_to_wait_set — 填入 subscription/timer/service/client/guard_condition 句柄
rcl_wait(&wait_set_, timeout) — 阻塞直至有事件或超时
remove_null_handles — 清理 middleware 标记为 invalid 的句柄
这与 rcl 源码分析 中的 wait set 机制直接对应,rclcpp 在其上增加了 callback group 过滤 与 memory strategy 抽象。
6.5 execute_any_executable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void Executor::execute_any_executable(AnyExecutable & any_exec) { if (!spinning.load()) { return; } if (any_exec.timer) { execute_timer(any_exec.timer); } if (any_exec.subscription) { execute_subscription(any_exec.subscription); } if (any_exec.service) { execute_service(any_exec.service); } if (any_exec.client) { execute_client(any_exec.client); } if (any_exec.waitable) { any_exec.waitable->execute(any_exec.data); } any_exec.callback_group->can_be_taken_from().store(true); interrupt_guard_condition_.trigger(); }
|
执行完毕后:
- 重置
can_be_taken_from(MutuallyExclusive group 在执行前会被置 false)
- 触发
interrupt_guard_condition_,唤醒可能在 rcl_wait 中阻塞的其他线程
6.6 MultiThreadedExecutor
- 默认线程数 =
hardware_concurrency()(至少 1)
- 各线程在
wait_mutex_ 保护下调用 get_next_executable
- 可选
yield_before_execute 减少锁竞争
any_exec.callback_group.reset() 避免析构时错误重置 group 状态
6.7 StaticSingleThreadedExecutor
实体列表在 spin() 前通过 StaticExecutorEntitiesCollector 一次性收集,仅在 add/remove node 时更新。适合实体集合固定的生产节点,降低 collect_entities 开销。
6.8 MemoryStrategy
默认 AllocatorMemoryStrategy(memory_strategies/allocator_memory_strategy.hpp)负责:
- 从 callback group → node 映射中收集 handles
- 在 wait 返回后查找 next ready 实体
- 可替换为自定义 strategy(测试或特殊调度)
7. CallbackGroup 并发模型
7.1 两种类型
1 2 3 4 5
| enum class CallbackGroupType { MutuallyExclusive, Reentrant };
|
| 类型 |
行为 |
| MutuallyExclusive |
同 group 内同一时刻最多一个回调执行;can_be_taken_from_ 原子标志 gate |
| Reentrant |
同 group 内回调可并行(MultiThreadedExecutor 下) |
7.2 与 Executor 的关系
- 每个 subscription/timer/service/client 创建时绑定一个
CallbackGroup
- 默认 group 由
automatically_add_to_executor_with_node 控制是否随 add_node() 自动注册
- Executor 维护
weak_groups_to_nodes_ 映射,只调度已 add_callback_group() 的 group
- MutuallyExclusive:
get_next_ready_executable_from_map 选中实体后将 can_be_taken_from 置 false,直到 execute 完成
7.3 典型用法
1 2 3 4 5 6 7
| auto group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); rclcpp::SubscriptionOptions opts; opts.callback_group = group;
auto reentrant = node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
|
8. Publisher / Subscription
8.1 模板层次
| 类 |
说明 |
PublisherBase |
非模板基类,持有 rcl_publisher_t,topic 名、QoS、intra-process id |
Publisher<MessageT, AllocatorT> |
模板发布者,publish() 多重重载 |
SubscriptionBase |
非模板基类,type-erased take/handle |
Subscription<MessageT, AllocatorT> |
模板订阅者,用户回调 |
还支持 TypeAdapter(自定义类型适配 ROS 消息)、GenericPublisher/Subscription(运行时类型)。
8.2 发布路径(inter-process)
1 2 3 4 5
| Publisher::publish(msg) → do_inter_process_publish() → rcl_publish() → rmw_publish() → DDS
|
8.3 发布路径(intra-process 开启)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| publish(std::unique_ptr<T, ROSMessageTypeDeleter> msg) { if (!intra_process_is_enabled_) { this->do_inter_process_publish(*msg); return; } bool inter_process_publish_needed = get_subscription_count() > get_intra_process_subscription_count();
if (inter_process_publish_needed) { auto shared_msg = this->do_intra_process_ros_message_publish_and_return_shared(std::move(msg)); this->do_inter_process_publish(*shared_msg); } else { this->do_intra_process_ros_message_publish(std::move(msg)); } }
|
策略要点:
- 仅 Volatile durability 允许 intra-process
- 若存在跨进程订阅者,先 intra 再 inter(降低端到端延迟)
unique_ptr 路径优先零拷贝移交所有权
8.4 订阅执行路径(Executor)
execute_subscription() 三条分支(executor.cpp):
| 模式 |
方法 |
| Serialized |
take_serialized → handle_serialized_message |
| Loaned |
rcl_take_loaned_message → callback → rcl_return_loaned_message_from_subscription |
| 默认 copy |
take_type_erased → handle_message → return_message |
Intra-process 消息在 handle_message 内通过 MessageInfo.from_intra_process 区分,不经过 DDS take。
9. Intra-process 通信
9.1 IntraProcessManager
位于 rclcpp/experimental/intra_process_manager.hpp,由 Context 持有单例(每 context 一个)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| uint64_t IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher) { uint64_t pub_id = IntraProcessManager::get_next_unique_id(); publishers_[pub_id] = publisher; pub_to_subs_[pub_id] = SplittedSubscriptions(); for (auto & pair : subscriptions_) { ... if (can_communicate(publisher, subscription)) { insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method()); } } return pub_id; }
|
9.2 匹配规则 can_communicate
1 2 3 4 5 6 7 8 9 10 11 12
| bool IntraProcessManager::can_communicate(...) const { if (strcmp(pub->get_topic_name(), sub->get_topic_name()) != 0) { return false; } auto check_result = rclcpp::qos_check_compatible(pub->get_actual_qos(), sub->get_actual_qos()); if (check_result.compatibility == rclcpp::QoSCompatibility::Error) { return false; } return true; }
|
9.3 数据结构
| 成员 |
含义 |
publishers_ |
pub_id → weak_ptr PublisherBase |
subscriptions_ |
sub_id → weak_ptr SubscriptionIntraProcessBase |
pub_to_subs_ |
pub_id → {take_shared_subscriptions, take_ownership_subscriptions} |
两种 intra subscription 策略:共享指针(多订阅者共享同一份数据)与 所有权转移(unique_ptr 零拷贝)。
10. Service / Client / Timer / Waitable
| 实体 |
创建入口 |
Executor 执行 |
| Service |
Node::create_service() |
execute_service → take request → 用户回调 → send response |
| Client |
Node::create_client() |
execute_client → 处理 pending response |
| Timer |
Node::create_wall_timer() / create_timer() |
execute_timer → timer->execute_callback() |
| Waitable |
Node::create_waitable() 或 action client 内部 |
waitable->execute(data) |
Timer 基于 rcl timer + guard condition:到期时在 wait set 中触发,优先级高于 subscription(get_next_ready_executable_from_map 先查 timer)。
Waitable 是可扩展钩子:rclcpp_action::Client 实现 Waitable 接口,将 action 相关 waitable 实体纳入 Executor 调度,而无需独立 spin 线程。
11. 参数系统
11.1 NodeParameters
NodeParameters 负责:
declare_parameter() / get_parameter() / set_parameter()
- 参数覆盖(CLI、
--params-file、节点级 overrides)
on_parameters_set 回调链
- 可选启动
ParameterService 与 /parameter_events 发布者
11.2 ParameterService(标准服务)
1 2 3 4 5 6 7 8 9 10
| get_parameters_service_ = create_service<rcl_interfaces::srv::GetParameters>( node_base, node_services, node_name + "/" + parameter_service_names::get_parameters, [node_params](..., Request request, Response response) { auto parameters = node_params->get_parameters(request->names); for (const auto & param : parameters) { response->values.push_back(param.get_value_message()); } }, ...);
|
注册的服务(与 rcl_interfaces 分析 对应):
| 服务名 |
类型 |
get_parameters |
rcl_interfaces/srv/GetParameters |
get_parameter_types |
GetParameterTypes |
set_parameters |
SetParameters |
set_parameters_atomically |
SetParametersAtomically |
describe_parameters |
DescribeParameters |
list_parameters |
ListParameters |
11.3 ParameterClient
远程节点参数访问的客户端封装,内部创建对应 service client,供 ros2 param 等工具链使用。
12. 时间与 Clock
12.1 TimeSource
TimeSource(time_source.cpp)订阅全局 /clock(rosgraph_msgs/msg/Clock):
- 收到 sim time 时启用
ros_time_active_,更新所有关联 Clock
- 与
use_sim_time 参数联动(经 NodeTimeSource wiring)
- 支持独立 clock 线程(
use_clock_thread NodeOption)
12.2 Clock 类型
| 类型 |
说明 |
RCL_SYSTEM_TIME |
系统 wall clock |
RCL_ROS_TIME |
仿真时间(由 /clock 驱动) |
RCL_STEADY_TIME |
单调时钟 |
Timer 可选择 clock:create_timer(clock, period, callback) vs create_wall_timer()。
13. QoS
rclcpp::QoS 封装 rmw_qos_profile_t,提供流式 API:
1 2 3
| rclcpp::QoS(10).reliable().transient_local(); rclcpp::SensorDataQoS(); rclcpp::ParametersQoS();
|
13.1 qos_overrides
节点构造时通过 declare_qos_parameters() 暴露 qos_overrides.<topic>.<policy> 参数,允许运行时覆盖 depth/reliability/durability/history(见 node.cpp 中 get_parameter_events_qos)。
13.2 兼容性检查
qos_check_compatible() 在 intra-process 匹配与 rclcpp::QoS 警告中使用,对应 RMW 的 rmw_qos_profile_check_compatible。
14. rclcpp_action
依赖 rcl_action C 库,提供类型安全的 C++ Action API。
14.1 主要类型
| 类型 |
职责 |
rclcpp_action::Client<ActionT> |
发送 goal、cancel、接收 feedback/result |
rclcpp_action::Server<ActionT> |
接受 goal、执行、发布 feedback/result |
ClientGoalHandle / ServerGoalHandle |
单个 goal 的生命周期与状态 |
create_client() / create_server() |
工厂函数 |
14.2 与 Executor 集成
Action client/server 内部创建多个 pub/sub/service 及 Waitable 实体,注册到 callback group 后由 Executor 统一调度,无需用户手动 spin action 专用线程。
14.3 依赖链
1 2 3
| rclcpp_action → rclcpp → rcl → rcl_action → rcl → action_msgs(UUID 等)
|
15. rclcpp_components
15.1 动机
Composable Node 允许在 单进程 内加载多个节点组件,配合 intra-process 减少序列化与 DDS hop。
15.2 ComponentManager
1 2 3 4 5 6 7 8
| ComponentManager::ComponentManager(...) : Node(std::move(node_name), node_options), executor_(executor) { loadNode_srv_ = create_service<LoadNode>("~/_container/load_node", ...); unloadNode_srv_ = create_service<UnloadNode>("~/_container/unload_node", ...); listNodes_srv_ = create_service<ListNodes>("~/_container/list_nodes", ...); }
|
| 服务 |
接口包 |
~/_container/load_node |
composition_interfaces/srv/LoadNode |
~/_container/unload_node |
UnloadNode |
~/_container/list_nodes |
ListNodes |
15.3 加载流程
ament_index 查找包内注册的组件资源(rclcpp_components_register_nodes CMake 宏)
class_loader::ClassLoader 动态加载 .so
NodeFactory 实例化组件(RCLCPP_COMPONENTS_REGISTER_NODE 宏)
executor->add_node() 将新节点纳入调度
15.4 容器可执行文件
| 可执行文件 |
Executor |
component_container |
SingleThreadedExecutor |
component_container_mt |
MultiThreadedExecutor |
component_container_isolated |
每组件独立 Executor |
16. rclcpp_lifecycle
16.1 LifecycleNode
继承/组合与 Node 相同的 interface,额外实现 LifecycleNodeInterface:
| 主状态 |
说明 |
| Unconfigured |
初始 |
| Inactive |
已 configure,未 activate |
| Active |
正常运行 |
| Finalized |
已 cleanup/shutdown |
转移:configure → activate → deactivate → cleanup → shutdown 等,底层调用 rcl_lifecycle。
16.2 LifecyclePublisher
仅在 Active 状态下真正 publish;Inactive 时 publish 被忽略或缓存(取决于配置),便于安全切换。
16.3 与 Nav2 / 工业场景
生命周期节点是托管节点(managed node)模式的基础,配合 lifecycle_manager 统一拉起/关闭。
17. 关键数据路径
17.1 订阅回调端到端
17.2 发布端到端(含 intra-process)
17.3 进程启动典型顺序
1 2 3 4 5 6 7 8 9 10 11
| main() → rclcpp::init(argc, argv) → Context::init → rcl_init → auto node = std::make_shared<Node>(...) → NodeBase → rcl_node_init → NodeParameters → 声明参数 / 启动 ParameterService → NodeTimeSource → 订阅 /clock → rclcpp::spin(node) → SingleThreadedExecutor::add_node → spin loop (wait + execute) → rclcpp::shutdown()
|
18. 目录与模块索引
18.1 include/rclcpp/ 主要头文件
| 模块 |
头文件 |
| 节点 |
node.hpp, node_options.hpp |
| 执行器 |
executor.hpp, executors/*.hpp, any_executable.hpp |
| 通信 |
publisher.hpp, subscription.hpp, service.hpp, client.hpp, timer.hpp |
| 并发 |
callback_group.hpp, waitable.hpp |
| 实验特性 |
experimental/intra_process_manager.hpp |
| 参数 |
parameter.hpp, parameter_service.hpp, parameter_client.hpp |
| 时间 |
clock.hpp, time_source.hpp, duration.hpp |
| QoS |
qos.hpp, qos_event.hpp |
| 上下文 |
context.hpp, utilities.hpp |
| 内存 |
memory_strategy.hpp, message_memory_strategy.hpp |
18.2 src/rclcpp/ 核心实现
| 文件 |
行数 |
职责 |
executor.cpp |
947 |
wait/execute/spin 核心 |
node.cpp |
607 |
Node 构造、create_* 委托 |
time_source.cpp |
554 |
/clock 与 sim time |
parameter_client.cpp |
545 |
远程参数 |
context.cpp |
527 |
Context 生命周期 |
subscription_base.cpp |
459 |
take/handle 基础设施 |
intra_process_manager.cpp |
230 |
进程内路由 |
parameter_service.cpp |
158 |
标准参数服务 |
19. 与 rcl / rcl_interfaces 对照
| 功能 |
rcl |
rclcpp |
| init/shutdown |
rcl_init |
Context::init, rclcpp::init |
| wait |
rcl_wait, rcl_wait_set_t |
Executor::wait_for_work |
| publish |
rcl_publish |
PublisherBase::do_inter_process_publish |
| take |
rcl_take |
SubscriptionBase::take_type_erased |
| 参数服务 |
无 |
ParameterService + rcl_interfaces srv |
| Executor |
无 |
完整调度栈 |
| intra-process |
无 |
IntraProcessManager |
20. 调试与追踪
- 日志:
RCLCPP_* 宏 → rcutils logging;节点 logger 名来自 NodeLogging
- 追踪:
TRACEPOINT(rclcpp_executor_*) 等,依赖 tracetools
- Topic 统计:
libstatistics_collector 可选编译,经 subscription 回调统计
- 常见问题:
- 回调不触发 → 检查 Executor 是否
add_node、callback group 是否注册
- 死锁 → MutuallyExclusive group 内回调再次 spin 或阻塞同 group 实体
- intra-process 不生效 → QoS durability 非 Volatile、topic 名/QoS 不匹配
21. 推荐阅读顺序
utilities.cpp + context.cpp — 理解 init/shutdown 与默认 context
node.cpp + node_interfaces/node_topics.cpp — Node 如何创建 pub/sub
executors/single_threaded_executor.cpp + executor.cpp — spin / wait / execute 全流程
callback_group.hpp + executor.cpp(get_next_ready_executable_from_map) — 并发语义
publisher.hpp + intra_process_manager.cpp — 发布与进程内优化
parameter_service.cpp + node_interfaces/node_parameters.cpp — 参数栈
time_source.cpp — sim time
rclcpp_action/client.hpp — action 如何挂到 Executor
rclcpp_components/component_manager.cpp — 动态组件
rclcpp_lifecycle/lifecycle_node.hpp — 生命周期
- 对照
examples/rclcpp_* 与 rcl 源码分析 下层行为
22. 小结
rclcpp 是 ROS 2 C++ 应用的主 API 层,核心模式为:
- Node + node_interfaces 创建与管理通信实体
- Executor 通过
rcl_wait 驱动 callback_group 内的回调
- IntraProcessManager 在同进程内绕过 DDS 实现低延迟数据路径
- ParameterService / TimeSource 等将 ROS 2 系统服务集成进节点生命周期
掌握 Executor + callback_group 的交互,是理解 ROS 2 C++ 并发模型的关键;排查通信问题时,应沿 publish/take → rcl → rmw 链向下追踪,并区分 inter-process 与 intra-process 路径。
正在加载留言…