Skip to content

Generic Publishers 的实现核心思想是:绕过编译时类型检查,通过运行时动态加载和序列化来实现类型无关的发布。具体机制如下:

核心架构

1. 非模板类设计

GenericPublisher 不是模板类,直接继承自 PublisherBase。因为编译时不知道类型,所以无法使用模板参数化。

关键成员:

cpp
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;  // 持有类型支持库,确保不被卸载

2. 运行时类型支持加载

通过 typesupport_helpers 实现:

步骤1:解析类型字符串

  • extract_type_identifier()"std_msgs/msg/String" 中提取出 (package_name="std_msgs", middle_module="msg", type_name="String")

步骤2:定位并加载类型支持库

步骤3:获取类型支持句柄

  • get_message_typesupport_handle() 通过符号查找获取类型支持句柄
  • 符号命名规则:{typesupport_identifier}__get_message_type_support_handle__{package_name}__{middle_module}__{type_name}

3. 序列化消息传递

GenericPublisher::publish() 直接发布序列化消息:

cpp
rcl_publish_serialized_message(get_publisher_handle().get(), &message.get_rcl_serialized_message(), NULL);

SerializedMessage 封装了 rcl_serialized_message_t,提供 RAII 语义管理二进制数据。

4. 零拷贝优化(可选)

publish_as_loaned_msg() 提供了借出消息机制:

  1. borrow_loaned_message():从中间件借用消息内存
  2. deserialize_message():将序列化数据反序列化到借出的内存
  3. publish_loaned_message():直接发布借出的消息,避免拷贝

工作流程

用户指定类型字符串 (如 "std_msgs/msg/String")

extract_type_identifier() 解析出 package/msg/type

get_typesupport_library_path() 找到 .so 文件路径

SharedLibrary 动态加载库

get_message_typesupport_handle() 通过符号查找获取类型句柄

GenericPublisher 构造时使用类型句柄初始化 PublisherBase

publish() 时直接发送 SerializedMessage 二进制数据

限制

  • 不支持进程内通信:文档明确说明不支持 intra-process handling,因为类型在编译时未知
  • 性能开销:动态加载库和序列化/反序列化都有额外成本
  • 类型安全缺失:编译器无法检查类型匹配,错误只能在运行时发现

这个设计巧妙地利用了 ROS 2 的类型支持系统,通过动态链接库和序列化机制,实现了编译时类型未知的情况下的消息发布。

基于 VitePress 构建