Skip to content

核心结论

rmw_cyclonedds 的序列化不是直接使用 Cyclone DDS 的标准序列化,而是采用了一种"桥接式"的设计方案:

  • 序列化实现:完全自定义,基于 ROS 类型自译
  • 接口规范:严格遵循 Cyclone DDS 的底层接口标准
  • 数据格式:生成的 CDR 数据符合标准,可与其他 DDS 实现互通

架构分层

1. 序列化层(自定义实现)

rmw_cyclonedds 实现了一套完整的 CDR 序列化基础设施:

核心组件位于 Serialization.cpp

  • CDRCursor 抽象接口(第31-61行)

    • 定义了游标的通用操作:offset()、advance()、put_bytes()
    • 提供对齐功能 align()
    • 虚基类,供具体实现继承
  • SizeCursor(第63-83行)

    • 只计算偏移量,不实际写入数据
    • 用于预先计算序列化后的总大小
    • ignores_data() 返回 true
  • DataCursor(第85行起)

    • 实际写入数据到内存缓冲区
    • 从 SizeCursor 或直接初始化
    • ignores_data() 返回 false

这种设计允许先计算大小再分配内存,然后一次性完成序列化,避免了多次内存分配。

2. 集成层(标准接口)

虽然序列化逻辑是自定义的,但数据包装严格遵循 Cyclone DDS 的接口规范:

核心类位于 serdata.hpp

  • serdata_rmw 类(第58行)
    • 继承自 ddsi_serdata(Cyclone DDS 的标准接口)
    • 包含序列化数据缓冲区 m_data
    • 包含数据大小 m_size

核心操作位于 serdata.cpp

  • serialize_into_serdata_rmw()(第67行)

    • 将 ROS 消息样本序列化到 serdata 缓冲区
    • 支持普通消息和服务请求(带请求头)
    • 处理异常并设置错误信息
  • serdata_rmw_from_ser()(第145行)

    • 从网络接收的 fragment chain 创建 serdata 对象
    • 处理多片段重组
    • 确保数据完整性
  • serdata_rmw_from_ser_iov()(第176行)

    • 从 iovec 数组创建 serdata 对象
    • 支持零拷贝接收路径

为什么需要自定义序列化?

直接使用 Cyclone DDS 的标准序列化无法满足 ROS 的特殊需求,自定义实现解决了以下问题:

1. 支持动态类型系统

  • 通过 ROS 类型自译获取消息结构
  • 无需预编译类型支持代码
  • 支持运行时类型发现

2. 处理复杂消息类型

  • 嵌套消息结构
  • 数组和序列类型
  • 字符串(包括 UTF-16)特殊处理

3. 服务通信扩展

  • 请求/响应关联
  • 服务调用头信息注入
  • 序列管理

4. 性能优化

  • 基于类型自译的直接访问
  • 避免中间转换层
  • 支持零拷贝优化路径

数据流示意

发送路径(序列化)

ROS 消息样本

类型自译信息(TypeSupport)

CDR 序列化器(CDRCursor)

serdata_rmw 缓冲区

Cyclone DDS 发送

接收路径(反序列化)

Cyclone DDS 接收

serdata_rmw 缓冲区

CDR 反序列化器

类型自译信息

ROS 消息样本

关键代码位置速查

功能 文件 行号
CDR 游标接口 Serialization.cpp 31-61
大小计算游标 Serialization.cpp 63-83
数据写入游标 Serialization.cpp 85-
serdata 类定义 serdata.hpp 58
序列化入口 serdata.cpp 67
网络数据接收 serdata.cpp 145
共享内存支持 serdata.cpp 100-119

扩展阅读

如果想深入了解序列化的更多细节,建议按以下顺序学习:

  1. ROS 类型自省与类型支持生成 - 理解如何从 ROS 类型生成类型支持
  2. CDR 序列化实现 - 深入了解 SizeCursor 和 DataCursor 的实现
  3. Serdata 操作 - 学习样本到序列化形式的转换过程
  4. 基于 Iceoryx 的共享内存支持 - 了解零拷贝通信的序列化优化

基于 VitePress 构建