news 2026/9/17 5:05:33

使用 Rerun 可视化 nuScenes 自动驾驶数据集:多传感器同步回放与 3D 标注渲染实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Rerun 可视化 nuScenes 自动驾驶数据集:多传感器同步回放与 3D 标注渲染实战

使用 Rerun 可视化 nuScenes 自动驾驶数据集:多传感器同步回放与 3D 标注渲染实战

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

导读

本文基于 Rerun 仓库中的官方示例 examples/python/nuscenes_dataset/README.md,完整讲解如何用 Rerun SDK 读取并可视化 nuScenes 自动驾驶数据集:在同一时间轴内同步呈现 6 路相机图像、LiDAR 与 RADAR 点云、GPS/IMU 轨迹以及 3D 目标检测框。读完本文,你将掌握Transform3DPinholeEncodedImagePoints3DBoxes3DGeoPoints等 Rerun 核心 archetype 的组合用法,学会用 Blueprint API 按传感器数量动态生成视图布局,并了解数据自动下载与 GPS 坐标换算的实现细节。

nuScenes 数据集与示例背景

nuScenes 是一个面向自动驾驶研究的大型公开数据集。示例读取的场景数据来自自动驾驶车辆上一整套传感器组合,包括:

  • 6 个相机CAM_FRONTCAM_FRONT_LEFTCAM_FRONT_RIGHTCAM_BACKCAM_BACK_LEFTCAM_BACK_RIGHT);
  • 1 个 LiDAR(激光雷达);
  • 5 个 RADAR(毫米波雷达);
  • GPS 与 IMU传感器。

因此,数据集在任意时刻都同时提供车辆位姿(ego pose)、相机图像、原始传感器数据和目标检测结果。该示例在 examples/manifest.toml 中被归类在 “robotics, autonomous systems, and interfacing with sensor hardware”(机器人、自动驾驶系统与传感器硬件交互)类别下,是理解多模态传感器数据在 Rerun 中如何对齐到统一时间轴的最佳入门示例。

示例的核心入口为 examples/python/nuscenes_dataset/nuscenes_dataset/main.py,辅助模块还包括自动下载 minisplit 数据集的 download_dataset.py 和将全局坐标换算为经纬度的 export_gps.py。

环境准备与运行

安装依赖

示例依赖定义在 examples/python/nuscenes_dataset/pyproject.toml,包含matplotlib(用于点云按距离着色)、numpynuscenes-devkit(nuScenes 官方 Python SDK)、requests(下载数据)以及rerun-sdk

确保满足 Rerun 要求的 Python 版本并安装最新 SDK,然后克隆仓库并安装示例依赖:

pip install --upgrade rerun-sdk # 安装最新 Rerun SDK git clone git@github.com:rerun-io/rerun.git # 克隆 Rerun 仓库 cd rerun git checkout latest # 检出与最新 SDK 版本匹配的提交

安装示例所需的库:

pip install -e examples/python/nuscenes_dataset

运行示例

直接执行主脚本即可运行:

python -m nuscenes_dataset # 运行示例

如果想自定义行为、探索更多特性或保存结果,可通过--help查看完整的命令行参数说明:

python -m nuscenes_dataset --help

pip install -e安装出的nuscenes_dataset命令行入口(见 pyproject.toml 的[project.scripts]段)外,直接以模块方式运行的效果相同。

核心命令行参数

结合main.py 中的argparse定义,示例支持的参数如下:

参数类型默认值说明
--root-dir路径examples/python/nuscenes_dataset/datasetnuScenes 数据集的根目录
--scene-name字符串scene-0061要可视化的场景名,形如scene-xxxx
--dataset-version字符串v1.0-mini数据集版本,如v1.0-miniv1.0-trainval
--seconds浮点数inf限制只记录前 N 秒数据,CI 中常使用--seconds=5

此外,示例通过rr.script_add_args(parser)注入了 Rerun 脚本通用参数(定义于 rerun_py/rerun_sdk/rerun/_script_helpers.py),包括:

  • --headless:不弹出 GUI 窗口;
  • --connect:连接外部 Rerun Viewer;
  • --serve:启动 gRPC 与 Web 服务并在浏览器中打开 Web Viewer;
  • --url:指定要连接的 Rerun URL;
  • --save <path>:把数据保存为.rrd文件;
  • --stdout:把数据输出到标准输出,便于管道送入 Rerun Viewer。

数据集自动下载机制

运行时会先调用ensure_scene_available()检查数据集是否就绪。当dataset-versionv1.0-mini且目标场景属于 minisplit 场景列表时,若本地无数据则自动从https://www.nuscenes.org/data/v1.0-mini.tgz下载并解压(见 download_dataset.py)。minisplit 共包含 10 个场景,覆盖多种驾驶工况,例如scene-0061(转弯)、scene-0103(城市道路直行)、scene-0796(匀速直行)、scene-0916(停车场绕圈)以及若干夜间场景。若请求的场景不在可自动下载范围内,脚本会提示并退出。

传感器标定:Pinhole 相机与传感器位姿

可视化前,需要为每个传感器初始化相机内参和空间位姿,以同时提供 3D 世界视角与相机透视视角。这通过PinholeTransform3D两个 archetype 实现。

先以静态(static=True)方式记录传感器相对 ego 车辆的位姿。示例将 nuScenes 的四元数从wxyz顺序转换为 Rerun 所需的xyzw顺序(np.roll(..., shift=-1)),并以ParentFromChild关系表达子坐标系到父坐标系的变换:

rr.log( f"world/ego_vehicle/{sensor_name}", rr.Transform3D( translation=calibrated_sensor["translation"], rotation=rr.Quaternion(xyzw=rotation_xyzw), relation=rr.TransformRelation.ParentFromChild, ), static=True, )

再记录针孔相机内参。image_from_camera接收 3x3 相机内参矩阵,widthheight对应图像分辨率(来自sample_data):

rr.log( f"world/ego_vehicle/{sensor_name}", rr.Pinhole( image_from_camera=calibrated_sensor["camera_intrinsic"], width=sample_data["width"], height=sample_data["height"], ), static=True, )

从源码看,log_sensor_calibration()main.py)仅在camera_intrinsic非空时才记录Pinhole,即该逻辑对相机与激光雷达/雷达传感器同样适用。另外在场景开头,示例还会记录rr.log("world", rr.ViewCoordinates.RIGHT_HAND_Z_UP, static=True),将世界坐标系约定为右手系、Z 轴朝上(对应自动驾驶常见的坐标约定)。

时间线:把多模态数据对齐到统一时间轴

所有后续数据在写入时都会绑定到具体时间点。Rerun 为每条日志数据分配时间戳,并挂载到timelines上。示例使用 nuScenes 的微秒时间戳,换算成秒后写入名为"timestamp"的时间线:

rr.set_time("timestamp", timestamp=sample_data["timestamp"] * 1e-6)

这一行代码是整条时间轴同步的关键:由于所有传感器(相机、LiDAR、RADAR、标注)都基于各自sample_data的同一时刻调用set_time,Rerun Viewer 才能在拖动时间滑块时让六路图像、点云与 3D 框保持帧级对齐。

车辆位姿、GPS 与轨迹

车辆位姿

车辆行驶过程中位姿持续变化,因此相机与传感器的位置也要随之更新,这同样通过Transform3D完成,记录在world/ego_vehicle实体上:

rr.log( "world/ego_vehicle", rr.Transform3D( translation=ego_pose["translation"], rotation=rr.Quaternion(xyzw=rotation_xyzw), relation=rr.TransformRelation.ParentFromChild, ), )

GPS 数据

GPS 坐标由场景的参考坐标加上变换(地图起始点 + 里程计)推导得出,以GeoPointsarchetype 记录:

rr.log( "world/ego_vehicle", rr.GeoPoints([[lat, long]]), )

在源码实现中(main.py),每帧还会把当前经纬度追加到ego_trajectory_lat_lon,并用rr.GeoLineStrings记录完整的行驶轨迹,同时用红色点标注当前车辆位置:

rr.log( "world/ego_vehicle", rr.Transform3D( translation=ego_pose["translation"], rotation=rr.Quaternion(xyzw=rotation_xyzw), relation=rr.TransformRelation.ParentFromChild, ), rr.TransformAxes3D(10.0), # 可视化坐标轴的长度 rr.GeoPoints(lat_lon=position_lat_lon, radii=rr.Radius.ui_points(8.0), colors=0xFF0000FF), )

GPS 坐标换算原理

经纬度换算逻辑位于 export_gps.py,其核心是derive_latlon()函数:

  • 基于 nuScenes dev-kit 中的export_poses.py改编;
  • 预先内置了 4 张地图的参考经纬度:boston-seaportsingapore-onenorthsingapore-hollandvillagesingapore-queenstown
  • 假设参考坐标与全局位姿原点均位于地图西南角;
  • 取位姿平移量前两维(x, y),计算相对参考点的方位角bearing = atan(x / y)与距离distance = sqrt(x² + y²)
  • 按地球半径6.378137e6米做球面坐标推算,返回目标点经纬度。

这一换算保证了 Rerun 内置MapView能在地图上正确渲染车辆位置与轨迹。

LiDAR 点云可视化

LiDAR 数据以Points3Darchetype 记录。示例读取.bin点云文件后取前三通道(x、y、z),并按点到传感器原点的距离用turbo_r色带着色,近处偏暖、远处偏冷:

rr.log(f"world/ego_vehicle/{sensor_name}", rr.Points3D(points, colors=point_colors))

对应的源码(main.py)为:

data_file_path = nusc.dataroot / sample_data["filename"] pointcloud = nuscenes.LidarPointCloud.from_file(str(data_file_path)) points = pointcloud.points[:3].T # 转置后形状为 (num_points, 3) point_distances = np.linalg.norm(points, axis=1) point_colors = cmap(norm(point_distances))

着色使用matplotlib.colormaps["turbo_r"],归一化范围vmin=3.0vmax=75.0(见文件头部定义),即 3 米以内与 75 米以外的距离都被截断到色带端点。由于历史原因(仓库 issue #4409),示例当前手动计算颜色而非直接使用 Rerun 内置的距离着色。

相机图像可视化

相机数据以编码图像形式记录,使用EncodedImagearchetype。直接传入磁盘上的图像文件路径即可,无需提前解码,Rerun 会按需加载:

rr.log(f"world/ego_vehicle/{sensor_name}", rr.EncodedImage(path=data_file_path))

在源码的log_cameras()main.py)中,对每路相机沿sample_data["next"]指针遍历该场景所有帧,逐帧设置时间戳并记录图像,直到超过max_timestamp_us或遍历结束。这保证了相机流与 LiDAR 流在时间上严格同步。

RADAR 点云可视化

RADAR 数据与 LiDAR 类似,同样以Points3D记录,并按距离着色:

rr.log(f"world/ego_vehicle/{sensor_name}", rr.Points3D(points, colors=point_colors))

对应源码使用nuscenes.RadarPointCloud.from_file()读取雷达点云(main.py),处理方式与 LiDAR 一致。5 路 RADAR 各自拥有独立的实体路径(world/ego_vehicle/<RADAR_通道名>),因此可在 3D 视图中单独开关。

3D 目标检测标注(Annotations)

标注以Boxes3Darchetype 记录,包含目标位置(centers)、尺寸(sizes)与旋转(quaternions),并通过class_ids关联类别:

rr.log( "world/anns", rr.Boxes3D( sizes=sizes, centers=centers, quaternions=quaternions, class_ids=class_ids, fill_mode=rr.components.FillMode.Solid, ), )

源码实现(main.py)还包含两个关键细节:

  1. 坐标轴约定转换:nuScenes 的尺寸为(width, length, height),示例转换为 Rerun 的(length, width, height)以对应 x、y、z 三个轴向;
  2. 类别管理与标注上下文:首次出现的类别名会注册进label2id字典并分配整数 id,随后用rr.AnnotationContext(annotation_context)static=True记录类别名到 id 的映射,Viewer 因此能按类别显示不同颜色与图例。

此外,标注的 GPS 坐标与车辆一样通过derive_latlon()换算,并作为rr.GeoPoints一并记录在world/anns实体上,使地图视图也能呈现目标位置。

用 Blueprint 动态编排默认视图布局

默认蓝图(blueprint)由 Blueprint API 程序化生成。核心思路是按传感器数量动态创建视图并排布成网格——这对相机数量随数据集变化的场景非常实用:

sensor_views = [ rrb.Spatial2DView( name=sensor_name, origin=f"world/ego_vehicle/{sensor_name}", # 将所有相机视图的图像平面距离设为 5m defaults=[rr.Pinhole.from_fields(image_plane_distance=5.0)], overrides={"world/anns": rr.Boxes3D(fill_mode="solid")}, ) for sensor_name in nuscene_sensor_names(nusc, args.scene_name) ] blueprint = rrb.Vertical( rrb.Horizontal( rrb.Spatial3DView(name="3D", origin="world"), rrb.Vertical( rrb.TextDocumentView(origin="description", name="Description"), rrb.MapView( origin="world", name="MapView", zoom=rrb.archetypes.MapZoom(18.0), background=rrb.archetypes.MapBackground(rrb.components.MapProvider.OpenStreetMap), ), row_shares=[1, 1], ), column_shares=[3, 1], ), rrb.Grid(*sensor_views), row_shares=[4, 2], )

这段布局体现了 Rerun Blueprint 的容器机制:

  • Spatial3DView:以world为原点展示 3D 世界,包含 LiDAR/RADAR 点云、车辆位姿与 3D 标注框,并通过defaults将图像平面距离(image plane distance)统一设为 5 米,使相机画面在 3D 空间中呈现在距中心 5m 的平面上;
  • Spatial2DView:每路相机一个 2D 视图,origin指向对应传感器实体,overrides强制world/anns使用实心填充显示 3D 框在图像上的投影;
  • MapView:以 OpenStreetMap 为底图、缩放级别 18 渲染world上的 GPS 轨迹与车辆位置;
  • TextDocumentView:在左上区域渲染 Markdown 格式的示例说明。

row_shares用于给垂直容器的每个子元素分配相对尺寸,水平容器对应column_shares,而Grid网格容器两者都支持。这里垂直方向 3D 视图与相机网格按 4:2 分配,水平方向 3D 视图与右侧面板按 3:1 分配。

值得注意,nuscene_sensor_names()main.py)会遍历首帧所有相机sample_data收集通道名,并按预设顺序(CAM_FRONT_LEFT → CAM_FRONT → CAM_FRONT_RIGHT → CAM_BACK_RIGHT → CAM_BACK → CAM_BACK_LEFT)排序,使网格中六路相机按车身环绕顺序排列,模拟“绕车一周”的观察视角。

实际运行时,蓝图通过rr.script_setup(args, "rerun_example_nuscenes", default_blueprint=blueprint)注入(main.py),同时会将description实体以静态 Markdown 文档形式记录。若应用已有激活的蓝图,新蓝图需用户点击 “reset blueprint” 才会生效;script_setup还会根据命令行参数自动决定数据去向——spawn 本地 Viewer、连接外部 Viewer、保存.rrd文件或启动 Web 服务(见 rerun_py/rerun_sdk/rerun/_script_helpers.py)。

运行效果与常见调参建议

运行python -m nuscenes_dataset后,Viewer 将呈现:

  • 3D 视图:ego 车辆坐标系、LiDAR 点云(按距离着色)、RADAR 点云、带类别颜色的 3D 标注框、车辆行驶轨迹;
  • 六路相机 2D 视图:每帧图像叠加 3D 框的 2D 投影;
  • 地图视图:基于 OSM 底图的车辆位置与 GPS 轨迹;
  • 描述面板:示例说明文档。

调参建议:

  • 只关注点云可加--headless --save output.rrd将数据保存为.rrd文件离线分析;
  • 想快速验证流程可加--seconds=10仅记录前 10 秒,减少内存占用;CI 中该示例即使用--seconds=5
  • 换场景可将--scene-name设为 minisplit 中其他场景(如scene-0916停车场绕圈),对比不同驾驶工况下的传感器数据形态;
  • 使用完整数据集时,将--dataset-version改为v1.0-trainval并指定包含完整数据的--root-dir(完整数据需在 nuScenes 官网申请下载,脚本不会自动下载)。

小结

nuScenes 示例是 Rerun 处理多传感器、多模态自动驾驶数据的一个完整范式:以set_time统一时间线、以Transform3DPinhole建立空间标定、以Points3D/EncodedImage/Boxes3D承载异构数据、以 Blueprint API 动态编排视图,最终在单个 Viewer 中实现“一条时间轴、多视角联动”的沉浸式回放。对希望用 Rerun 可视化自有机器人或自动驾驶数据的开发者,可直接复用本文涉及的 archetype 组合与 Blueprint 编排模式,将数据接入替换为自身传感器流即可。

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 5:05:33

STM32CubeIDE Attach调试:不复位不烧录,直接接管运行中目标

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 5:03:56

Python零基础入门:条件循环与数据结构实战

1. 项目概述&#xff1a;零基础Python入门第三课"0基础Python-003"这个标题背后&#xff0c;是一个面向编程新手的Python入门系列课程。作为该系列的第三课&#xff0c;它通常承担着承前启后的关键作用——在学员掌握了基础语法和简单逻辑后&#xff0c;开始接触更贴…

作者头像 李华
网站建设 2026/9/17 5:03:54

WorkBuddy技术拆解:AI工作台的产品化壁垒与工程实践

WorkBuddy这段时间讨论度确实高。我从它刚火的时候开始折腾&#xff0c;装客户端、配本地模型、挂SkillHub里的各种技能&#xff0c;也把网上那些“从入门到精通”的实操手册翻了个遍。一个很直接的感受是&#xff1a;大家把它想得太神秘了。拆到技术层&#xff0c;它就是LLM调…

作者头像 李华
网站建设 2026/9/17 5:02:44

Gazebo与CoppeliaSim深度对比:仿真器选型与实操避坑指南

1. 两大仿真器&#xff0c;不止是二选一这可能是系列里最容易被催更的一篇。从第1篇聊仿真器选型开始&#xff0c;到今天第10篇收官&#xff0c;后台私信里反复出现的两个名字就是 Gazebo 和 CoppeliaSim。说实话&#xff0c;这对冤家几乎占据了具身智能仿真生态的半壁江山&…

作者头像 李华