RealSense 深度相机标定:3 分钟拿到内外参,避开 90% 的坑
2026/9/19 13:18:51 网站建设 项目流程

RealSense 深度相机标定:3 分钟拿到内外参,避开 90% 的坑

【免费下载链接】librealsenseRealSense SDK项目地址: https://gitcode.com/GitHub_Trending/li/librealsense

点云和彩色图对不齐、早上量的距离和晚上对不上、换台电脑数值就飘——多半不是算法的锅,是标定的事。用 librealsense(RealSense 深度相机的官方 SDK)配合 pyrealsense2,一个下午能走完 RealSense 深度相机标定的完整链路:取参、懂参、避坑、维护。

动手:用 pyrealsense2 3 分钟拿到内参矩阵和外参

参数来自流描述(stream profile,即一路视频流的完整配置与元数据),不是来自文件。流一起,内参外参就能直接从 profile 上拿。下面这段只干两件事:启动深度+彩色,然后从深度流取内参,并用一行get_extrinsics_to取深度到彩色的变换。注意它不需要任何标定板,拿到的是设备出厂已写入的基础参数。

import pyrealsense2 as rs pipe, cfg = rs.pipeline(), rs.config() cfg.enable_stream(rs.stream.depth, 848, 480, rs.format.z16, 30) cfg.enable_stream(rs.stream.color, 1280, 720, rs.format.rgb8, 30) prof = pipe.start(cfg) depth_prof = prof.get_stream(rs.stream.depth).as_video_stream_profile() color_prof = prof.get_stream(rs.stream.color).as_video_stream_profile() intri = depth_prof.get_intrinsics() # 内参:焦距/主点/畸变 extri = depth_prof.get_extrinsics_to(color_prof) # 外参:深度->彩色 4x4 变换 print("焦距 fx,fy:", intri.fx, intri.fy, " 主点:", intri.ppx, intri.ppy) print("外参平移(mm):", extri.translation) pipe.stop()

结果怎么读:fx/fy单位是像素,D435 深度流一般在几百到一千多;ppx/ppy贴近图像中心;外参的translation单位是毫米,两颗镜头物理间距只有几厘米,量级应在几十 mm。跑出来是 0 或异常大,往下看避坑部分。

字段单位含义
fx,fy像素焦距,决定像素到 3D 射线的张角
ppx,ppy像素主点,光轴与像面交点
coeffs无量纲畸变系数,描述边缘弯曲
translation毫米外参中两传感器原点的相对位置

内参修什么、外参修什么,为什么 Y16 必须是原始流

内参:修"单颗镜头内部"的变形。镜头是曲面,画面边缘的像素会向四周漂移,离主点越远漂得越厉害。内参就是用fx/fyppx/ppycoeffs把这个漂移建模出来:给定一个像素,你能反推出它对应空间里的哪条射线。没有它,棋盘格边缘会弯成桶形,测距随画面位置变化。

外参:把一颗镜头的坐标系"搬"到另一颗。深度相机外参(把红外坐标系搬彩色坐标系的 4×4 变换)描述的是传感器在板子上的相对位置和夹角。D400 系列的深度(红外)与彩色是两颗物理独立的镜头,外参不准,深度点云贴彩色纹理时怎么调都对不上。多传感器之间的这种坐标变换关系,示意图如下(以 T265 为例,原理相同):

为什么 Y16 原始流是标定前提。Y16 是红外传感器直接输出的 16 位原始数据,像素值未经 SDK 处理;而平时拿到的 z16 深度流,是时序滤波、空间滤波、空洞填充等后处理后的结果,像素已被"修过"。在修过的数据上拟合,等于拿失真的样本建模型;而且从已校正流上读到的畸变系数是全零——SDK 替你把畸变消化掉了。规则很硬:标定时用原始 Y16,运行时才切 z16

避坑:帧率、零畸变、温度漂移三个高频翻车场景

⚠️ 下面三个场景几乎覆盖了全部新手翻车现场,每个按"现象 → 原因 → 修复"过一遍。

"Couldn't resolve requests"启动即报错。现象:pipeline.start直接抛这个异常,流一个都起不来。原因:Y16 格式在 USB 3.0 连接下只开放 15fps 和 25fps 两档(Y16 是 16 位数据,带宽占用是 8 位格式的两倍),你配的 30fps 在设备侧不存在这个组合。修复:Y16 场景把帧率改成 15 或 25;非要 30fps 就改用 Y8,代价是红外动态范围减半。

畸变系数全零。现象:intri.coeffs输出一排 0。原因:你从后处理流或已校正流上取参,SDK 已经做过畸变校正,profile 里自然留了个空壳。修复:按上面"Y16 是前提"一节,从 Y16 原始流重新取一次;仍为零再检查设备固件里的标定数据是否完整。

深度值随温度漂移。现象:相机连续运行升温后,或者昼夜温差大,深度边缘整体跳变几个毫米。原因:红外发射器与镜头对温度敏感,出厂标定参数是特定温度下的快照,温度一变,模型就偏。修复:温度大幅变化后重跑片上标定,librealsense 提供on-chip-calib接口和rs-on-chip-calib示例程序,让设备自动完成内部自标定并把结果写回。

一次标定不够:怎么让它持续可用

多距离采集,别只贴一面墙按按钮。标定本质是拟合,你喂的数据决定拟合范围。只站在 1m 前采一组,保住的也只是 1m 附近。建议覆盖 0.5m、1.5m、3m 几个工作距离,每个距离摆 15~20 个不同角度位置,让标定模型横跨相机整个工作区间。

参数落盘:保存与重载。pyrealsense2 没有"保存标定参数"按钮,但参数本来就是普通数据。把intri(fx、fy、ppx、ppy、coeffs)和extri(rotation、translation)序列化成一个 JSON 存到本地,启动时读回来覆盖或校验即可。文件里记上设备序列号做绑定,换机器部署时参数可复现——这一步直接消掉"换台电脑数值就飘"的问题。

验证:用重投影误差当验收线。把标定板角点的 3D 坐标用新参数投回 2D,与实测像素坐标的偏差即重投影误差,控制在 0.5 像素内才算合格。深度侧再看 Z 向精度,深度误差与顶点坐标的变换关系如下图:

点云效果层面,直接在 Viewer 里看深度+彩色的融合:纹理和几何贴合说明外参没问题,整体偏移说明外参还有残差,如下图 D435 在 Jetson 平台上的效果:

交付前对照检查:

  • 标定取参用的是 Y16 原始流,而不是 z16 深度流
  • Y16 帧率只写 15 或 25,不越设备支持范围
  • fx/fy是像素量级、外参平移是毫米量级,眼检通过
  • 多距离(0.5/1.5/3m)× 多角度,共 15~20 组采集
  • 重投影误差验证通过,控制在 0.5 像素内
  • 内外参已序列化落盘,并与设备序列号绑定
  • 温度剧变或固件更新后,已重跑片上标定

【免费下载链接】librealsenseRealSense SDK项目地址: https://gitcode.com/GitHub_Trending/li/librealsense

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询