用 Rerun 实时可视化 Intel RealSense 深度传感器:RGB 与深度流式管线实战
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
本篇技术指南基于 Rerun 仓库中的live_depth_sensor官方示例(examples/python/live_depth_sensor/README.md),讲解如何把 Intel RealSense 深度传感器实时流式采集的 RGB 彩色帧与深度帧接入 Rerun 3D 视图,并借助针孔相机(Pinhole)模型实现 RGB 相机、深度相机与三维空间的坐标对齐。读完本文,你将掌握Pinhole、Transform3D、Image、DepthImage等 Rerun 核心 archetype 的组合用法、meter深度单位换算的关键细节,以及如何运行、自定义这套实时传感器可视化方案。
示例概览:把 RealSense 实时流接入 Rerun 3D 视图
该示例的功能非常聚焦:从一台已连接的 Intel RealSense 深度传感器上持续读取彩色(RGB)与深度(Depth)两种数据流,并逐帧送入 Rerun 进行可视化。最终在 Rerun Viewer 中看到的不只是两张并排的图像,而是一个带针孔相机模型的 3D 场景——RGB 相机与深度相机都被标定在正确的位置,深度图在 3D 视图中被自动反投影为点云。
硬件要求:此示例需要一台真实连接(USB)的 Intel RealSense 深度传感器才能运行,例如 D400 系列。正因为依赖硬件,示例在 pyproject.toml 中通过
[tool.rerun-example] skip = true # requires hardware标记为跳过 CI 自动化执行。
示例使用到的 Rerun 核心类型(archetype)共 4 种:
| Archetype | 作用 |
|---|---|
Pinhole | 描述相机透视投影(内参),负责图像坐标与相机坐标的映射 |
Transform3D | 描述相机外参,用于把 RGB 相机摆放到相对深度相机的正确位置 |
Image | 记录 RGB 彩色图像 |
DepthImage | 记录深度图像,并携带深度单位(meter)信息 |
运行环境与依赖
按照示例文档,你需要先准备好最新版 Rerun SDK 并检出仓库代码:
pip install --upgrade rerun-sdk # 安装最新版 Rerun SDK git clone https://gitcode.com/GitHub_Trending/re/rerun.git # 检出仓库 cd rerun git checkout latest # 检出与最新 SDK 发布版本匹配的提交然后安装该示例所需的依赖库:
pip install -e examples/python/live_depth_sensor依赖声明见 examples/python/live_depth_sensor/pyproject.toml,核心依赖只有三个:
numpy:用于把 RealSense 原始帧数据转为 NumPy 数组;pyrealsense2(macOS 上为pyrealsense2-mac):Intel RealSense 官方 Python SDK,提供rs.pipeline()、get_intrinsics()、get_extrinsics_to()等接口;rerun-sdk:Rerun 日志 SDK。
示例同时声明了命令行入口脚本:live_depth_sensor = "live_depth_sensor:main"。
运行示例主脚本:
python -m live_depth_sensor # 运行示例如果想自定义行为、探索更多功能或保存数据,用--help查看全部 CLI 选项:
python -m live_depth_sensor --help代码结构总览
示例主体是一个约 100 行的单文件脚本 examples/python/live_depth_sensor/live_depth_sensor.py,流程可以概括为四个阶段:
- 声明坐标系:把整个
realsense空间声明为 RDF(右、下、前)坐标约定; - 标定相机(静态):分别记录深度相机与 RGB 相机的内参(
Pinhole)以及两者之间的外参(Transform3D),全部使用static=True只记录一次; - 循环取流:通过
rs.pipeline()阻塞等待帧,逐帧把深度图与彩色图 log 到对应实体路径; - 清理退出:
finally中调用pipe.stop()释放 RealSense 管线。
其中关键的相机标定与坐标约定逻辑如下。
相机模型与坐标约定:Pinhole + Transform3D + ViewCoordinates
坐标约定:RDF
RealSense 的坐标系约定为RDF(X 向右、Y 向下、Z 向前),这与 Rerun 深度相机的默认坐标约定一致。示例在第一行就把它声明为静态属性:
rr.log("realsense", rr.ViewCoordinates.RDF, static=True) # Visualize the data as RDF从 rerun_py/rerun_sdk/rerun/archetypes/view_coordinates.py 的实现说明看,[Right, Down, Forward]表示+X指向右、+Y指向下、+Z指向前。RDF 是图像类数据(所有图像都使用 RDF 像素坐标约定)最自然的空间表示。
深度相机内参
示例把深度相机的坐标系原点直接当作世界原点——realsense/depth保持为单位变换(Identity),因此无需为深度相机记录外参。只记录深度相机的针孔内参:
rr.log( "realsense/depth/image", rr.Pinhole( resolution=[depth_intr.width, depth_intr.height], focal_length=[depth_intr.fx, depth_intr.fy], principal_point=[depth_intr.ppx, depth_intr.ppy], ), static=True, )三个参数分别对应 RealSense 内参结构(depth_intr)中的分辨率、焦距(fx/fy)与主点(ppx/ppy),来源是profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()。
RGB 相机外参与内参
RGB 相机与深度相机通常不在同一物理位置,需要外参把 RGB 相机摆放到正确位姿。示例用get_extrinsics_to获取深度相机到 RGB 相机的变换,并记录为Transform3D:
rgb_from_depth = depth_profile.get_extrinsics_to(rgb_profile) rr.log( "realsense/rgb", rr.Transform3D( translation=rgb_from_depth.translation, mat3x3=np.reshape(rgb_from_depth.rotation, (3, 3)), relation=rr.TransformRelation.ChildFromParent, ), static=True, )这里的relation=rr.TransformRelation.ChildFromParent表示该变换描述子实体(RGB 相机)相对父实体(深度相机坐标系)的姿态。之后 RGB 相机同样记录自己的针孔内参:
rr.log( "realsense/rgb/image", rr.Pinhole( resolution=[rgb_intr.width, rgb_intr.height], focal_length=[rgb_intr.fx, rgb_intr.fy], principal_point=[rgb_intr.ppx, rgb_intr.ppy], ), static=True, )从 rerun_py/rerun_sdk/rerun/archetypes/pinhole.py 的源码注释可以进一步理解Pinhole的语义:它是一个相机透视投影 archetype("Camera perspective projection (a.k.a. intrinsics)"),resolution是子图像空间的像素分辨率(宽、高),image_from_camera投影到(0,0)到resolution - 1的范围内。当同一实体下记录了Transform3D时,它表示相机外参并优先于Pinhole被用于空间定位;而当DepthImage记录在Pinhole之下时,深度图会沿相机朝向被自动反投影成 3D 点云——这正是本示例在 3D 视图中看到点云的原理。
Pinhole还支持更多可选字段:camera_xyz(默认即RDF)、image_plane_distance(仅影响 3D 可视化中相机平截头体的显示距离)、color与line_width(相机线框的样式)等,均可按需扩展。
记录 RGB 图像:Image
相机标定完成后,进入主循环。每取到一帧,先递增帧号并用rr.set_time把它写入时间轴,再记录彩色图:
rr.set_time("frame_nr", sequence=frame_nr) rr.log("realsense/rgb/image", rr.Image(color_image))color_image由np.asanyarray(color_frame.get_data())转换而来,即 RealSense 原始彩色帧数据(BGR/RGB 排列的 H×W×C 数组)。由于实体路径与之前Pinhole记录的路径一致(realsense/rgb/image),图像会自动与相机内参关联,从而在 3D 视图中拥有正确的位置与投影。
记录深度图像:DepthImage 与 meter 单位换算
深度图与彩色图同样逐帧记录,但多了关键的meter参数:
rr.log( "realsense/depth/image", rr.Pinhole( resolution=[depth_intr.width, depth_intr.height], focal_length=[depth_intr.fx, depth_intr.fy], principal_point=[depth_intr.ppx, depth_intr.ppy], ), static=True, )rr.set_time("frame_nr", sequence=frame_nr) rr.log("realsense/depth/image", rr.DepthImage(depth_image, meter=1.0 / depth_units))depth_units来自depth_frame.get_units(),是 RealSense 深度值对应的物理长度(单位为米)。例如depth_units = 0.001表示每个深度单位是 1 毫米,此时meter = 1.0 / 0.001 = 1000,即 1 米对应 1000 个原生深度单位。
根据 rerun_py/rerun_sdk/rerun/archetypes/depth_image.py 的字段文档,meter的含义是"原生深度单位中一米有多长"。它对 2D 视图的影响仅是悬停时显示的物理深度值;在 3D 视图中则直接决定点云中每个点的空间位置,单位错误会导致点云被整体缩放或拉伸。若省略meter,Viewer 会默认浮点深度格式取1.0、整数格式取1000.0(毫米)。
DepthImage还提供其它可视化相关字段,可按需组合:
| 字段 | 说明 | 默认 |
|---|---|---|
colormap | 深度渲染使用的颜色映射 | Turbo 色图 |
depth_range | 期望的有效深度值范围,超出部分在映射色图时被截断(点云不受影响) | 自动从数据估计 |
point_fill_ratio | 缩放 3D 点云中点的半径 | 1.0 |
draw_order | 2D 视图下的绘制顺序 | -20.0 |
magnification_filter | 2D 视图放大时的过滤方式(仅 2D 有效) | — |
时间轴:用 frame_nr 串联连续帧
rr.set_time("frame_nr", sequence=frame_nr)每一帧都调用一次rr.set_time,把序列时间点frame_nr写入 Rerun 时间轴。这样所有帧(无论 RGB 还是深度)都会被标记上同一帧号,Viewer 可以按时间轴播放、拖动、对比不同帧的数据。这也是 Rerun 处理流式时序数据(multimodal robotics data)的标准方式。
完整的帧循环
综合上述各环节,主循环在源码中的实现如下(来自 examples/python/live_depth_sensor/live_depth_sensor.py):
frame_nr = 0 try: while True: if num_frames and frame_nr >= num_frames: break rr.set_time("frame_nr", sequence=frame_nr) frame_nr += 1 frames = pipe.wait_for_frames() for _f in frames: depth_frame = frames.get_depth_frame() depth_units = depth_frame.get_units() depth_image = np.asanyarray(depth_frame.get_data()) rr.log("realsense/depth/image", rr.DepthImage(depth_image, meter=1.0 / depth_units)) color_frame = frames.get_color_frame() color_image = np.asanyarray(color_frame.get_data()) rr.log("realsense/rgb/image", rr.Image(color_image)) finally: pipe.stop()num_frames由--num-frames命令行参数控制,不传则无限流式运行,直到按Ctrl-C中断,此时finally块确保 RealSense 管线被正确关闭。
CLI 选项:script_add_args 带来的标准能力
示例的main()除了自定义的--num-frames外,还调用了rr.script_add_args(parser)与rr.script_setup(args, "rerun_example_live_depth_sensor")。从 rerun_py/rerun_sdk/rerun/_script_helpers.py 的源码可以看到,这组辅助函数会为脚本注入一整套标准 Rerun 参数:
--headless:不弹出 GUI,只记录数据;--connect [URL]:连接到一个外部运行的 Rerun Viewer(不指定 URL 时连接默认地址);--serve:启动 gRPC 与 Web 服务器,并打开浏览器端的 Web Viewer;--url:指定要连接的 Rerun URL;--save <path>:把数据保存为.rrd文件;--stdout:把数据写到标准输出,供管道重定向到 Rerun Viewer。
script_setup会根据这些参数自动完成rr.init与连接建立,script_teardown负责收尾(如在--serve模式下阻塞等待)。因此本示例天然支持"离线保存为 rrd 文件"或"无头模式采集"等扩展用法,无需修改任何业务代码。
小结
live_depth_sensor示例展示了 Rerun 处理实时多模态传感器数据的标准范式:先以static=True一次性记录坐标系约定(ViewCoordinates.RDF)、相机内参(Pinhole)与外参(Transform3D),建立 3D 空间的标定骨架;再逐帧用rr.set_time标记时间戳,并通过Image与DepthImage(配合meter单位换算)持续记录数据流。这套模式可以直接迁移到任何深度相机、双目相机或 RGB-D 机器人感知管线上,是理解 Rerun 相机模型与流式日志机制的最小可运行范本。
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考