nuScenes转ROS2 MCAP

把nuScenes自动驾驶数据集转成ROS2消息格式的MCAP文件

nuScenes转ROS2 MCAP
💡
这个工具是从Foxglove的项目Fork过来,然后VibeCoding实现的。鉴于这是一个很小众的工具,且AI实现了全部代码,那这篇介绍文章由作者本人来写最适合不过了。

做自动驾驶相关开发的人,大概率接触过nuScenes——nuTonomy开源的城市场景数据集。它提供了丰富的传感器数据(6路相机、5路雷达、LIDAR、GPS、IMU、CAN总线),外加语义标注和地图信息,在学术界和工业界都很常用。

但这个数据集有个使用门槛:它的标准格式是nuScenes自己的JSON结构,不能直接用常见的ROS2工具链打开。开发中你想回放一段数据、验证某个算法、或者用Foxglove看看效果,都得先自己写一把转换脚本。

所以AI写了个工具:nuscenes2mcap,把nuScenes mini数据集直接转成ROS2消息格式的MCAP文件

为什么用ROS2消息格式?

市面上已有其他nuScenes → MCAP转换工具,但它们通常使用Foxglove自家的Protobuf格式(Foxglove Schema)。这本身没问题,Foxglove解析得很好。

但如果你更习惯ROS2生态——比如你想用 ros2 bag play 回放数据、评估你的感知算法,或者把数据导入已有的ROS2 pipeline——那 ROS2消息(CDR编码) 就是最直接的桥接方式。

这个转换脚本做的事情很简单:把nuScenes的20Hz关键帧数据、非关键帧传感器数据、CAN总线消息,全部重新打包为标准ROS2消息,写入MCAP文件。

输出了哪些消息?

转换覆盖了nuScenes的主要数据源:

`sensor_msgs/PointCloud2`LIDAR点云(LIDAR_TOP)
`sensor_msgs/CompressedImage`六路相机图像
`sensor_msgs/CameraInfo`相机内参 / 畸变系数
`sensor_msgs/PointCloud2`5路雷达数据
`sensor_msgs/NavSatFix`GPS经纬度
`sensor_msgs/Imu`IMU(来自CAN总线)
`nav_msgs/Odometry`自车位姿 + 速度(来自CAN总线)
`nav_msgs/OccupancyGrid`地图底图 + 可驾驶区域
`geometry_msgs/PoseStamped`自车实时位姿
`tf2_msgs/TFMessage`所有传感器到车体的坐标变换
`visualization_msgs/MarkerArray`3D标注框 + 车道线
`diagnostic_msgs/DiagnosticArray`CAN总线状态(转向角、刹车、油门等)

每个 sample(~2Hz关键帧)生成一个完整的时间步,中间的非关键帧传感器数据按时间戳排序后插入,确保时间轴连续。

值得一提的是 语义地图 的处理:nuScenes的地图是矢量格式,转换脚本把底图渲染为OccupancyGrid,同时把车道中心线投影为MarkerArray,随数据一起输出。在Foxglove里打开,地图、自车、传感器数据都是对齐的。

使用方法

三步走:

# 1. 下载数据
# 从 nuScenes 官方 S3 下载 v1.0-mini,解压
# can_bus.zip → data/
# nuScenes-map-expansion-v1.3.zip → data/maps/
# v1.0-mini.tgz → data/

# 2. 安装
pip install -e .

# 3. 运行
python convert_to_mcap_ros2.py

默认扫描 data/ 目录,每个场景输出一个 .mcap 文件,保存在 output_ros2/。也支持指定场景ID:

python convert_to_mcap_ros2.py --scene scene-0061

技术要点

转换过程中有几个值得注意的点:

时间同步。 nuScenes里不同传感器的时间戳独立,脚本以LIDAR_TOP关键帧为基准,将同一sample内的所有传感器数据对齐到同一时间步。CAN总线消息有独立的时间戳,按其自身时间戳插入,确保CAN数据和传感器数据时间线一致。

CDR序列化。 使用 rosbags 库的typesys获取标准ROS2消息定义,通过 serialize_cdr 序列化,再写入MCAP。这样输出的文件 ros2 bag 可以直接读。

TF树。 构建了 map → base_link → sensor_id 的TF树,每个时间步发布完整的坐标变换。在Foxglove里打开,所有传感器的位姿都是正确的。

地图渲染。 nuScenes的MapExpansion提供了矢量地图,脚本用 get_map_mask 按自车位置动态裁剪可驾驶区域,以自车为中心的OccupancyGrid实时更新。

一些感想

写这个工具的过程挺有意思的。nuScenes的数据结构不算复杂,但因为它用自己那套schema组织(sample、sample_data、ego_pose、calibrated_sensor这些表互相token关联),想要完整映射到ROS2的消息体系,需要对两边的数据模型都熟悉。

最折腾的部分是CAN总线数据。nuScenes的CAN消息是单独通过 NuScenesCanBus 加载的,格式和主数据不同步。需要手动解析 ms_imuposesteeranglefeedback 等消息,映射到 sensor_msgs/Imunav_msgs/Odometrydiagnostic_msgs/DiagnosticArray

项目在GitHub上开源:

转发至

微信扫一扫分享

WeChat QR Code