纯 Rust 离线生成 ROS2 消息,支持零拷贝 CDR 编解码
背景描述
常言rust重写一切, 但真正用rust写过ros2的朋友们应该知道, 如果自己手写 ROS2 消息结构体 + CDR 解码,工作量巨大、容易出错、维护成本极高。我相信这是所有做 Rust + ROS2 离线数据处理的开发者都会遇到的共同痛点。
方案调研
于是我调研了现有开源方案,rosbag-rs / rosbag2-rs 确实能实现离线读取,但它们的消息解析逻辑与文件读取逻辑高度耦合,无法独立抽出来作为通用消息层使用。cdr又不够可控,因为我看到现成的解决方案基本上是自己实现了一套cdr的解析。而RustDDS又是另外一个完整的环境,这又违背了初衷。
而我的想法是——上述项目的真正价值不应该埋没在一个rosbag解析上。
架构探索
基于这个愿景以及个人学习需求,我决定不采用耦合方案,而是独立开发一套纯 Rust 的 ROS2 消息生成与编解码引擎。启发我的则是这个ros2_rust:issue#628。
为了验证这套架构的可行性,我在项目 v0.1.1 版本时,就基于它开发了一个 mini 版 的 ROS2 消息发布订阅器,忽略qos和xtype,只保留最核心的消息发布与订阅功能。测试结果是:基础功能正常跑通。证明这套消息生成 + CDR 编解码的设计是可用的。
但在进一步对接 CycloneDDS 时遇到了一个已知问题:部分消息因为 XTYPE 类型映射不完全匹配,无法直接从 CycloneDDS 读取,后来我是从发布者的hpp里面提取的xtype和blob注入进我生成的头文件里面,才成功读取;但同样的消息,在 ROS2(RMW 绑定 CycloneDDS)下可以正常收发。
这也让我更加明确:我要做的不是一个简单的 bag 解析工具,而是一套将 ROS 2 消息视为一种在线格式,而非运行时抽象。
应用场景
它的应用场景应该是:
- ros2 bag的离线消息的读取并解析,用户可以用简洁的代码直接进行读取ros2 bag,并得到rust的结构体。以便进行进一步的支持:比如使用离线数据进行slam地图构建。
- 数据集的转换:在构建数据集的时候,有时需要高性能的数据转换工具,这个工具的意义就在于此,将消息转换为rust结构体之后,可以轻松对接下游转换parquet、hdf5,转接arrow、zenoh协议等各种需求。
于是,ros2-message-gen 诞生了。它的定位非常清晰:纯 Rust、无 ROS2 依赖、自动生成消息、支持零拷贝 CDR 解码,可直接对接 DDS 实时流这方面则是未来可期的能力。
本项目核心模块 cdr-runtime、工程结构设计、优化方向设计由本人独立实现;rust代码生成等部分由 AI 辅助开发完成,部分代码使用了codegen作为依赖。
项目核心特性
- 无 ROS2 强依赖纯离线解析.msg、.srv文件,无需安装 ROS2。
- 工程化自动生成按 ROS 原生包结构自动拆分生成独立 Rust Crate,自带 cdr-runtime 运行时,结构清晰可直接集成业务项目。
- 双解码模式
- 常规所有权解码:适用于通用业务场景(如果不想下游代码借用和生命周期满天飞,也可以使用borrowdecode+to_own()的方式,小端性能相差不大,在大端数据解析的时候反而提升)。
- 零拷贝 Borrowed 解码:利用 Rust 生命周期直接引用原始缓冲区,减少内存分配、大幅提升海量日志解析性能,测试时每iter只需100ns不到。 - 全覆盖支持基础类型、定长 / 动态数组、常量定义、嵌套消息、跨包引用、msg/srv消息全部(我电脑上的)支持。没有支持action。
- Schema 动态分发支持通过消息类型名(如sensor_msgs/msg/Imu)动态解码,适合做通用消息网关、日志解析工具。
项目地址:ros2-message-gen
声明
本项目处于前期状态,目前仅支持了大小端序和xcdr1,没有对xcdr2做适配。测试无法全面覆盖,请见谅。
