尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

深度相机与双目测距:视差原理、标定与角点剔除实战

发布时间:2026/9/30 1:24:15

资讯中心
01
ARTICLE

深度相机与双目测距:视差原理、标定与角点剔除实战

深度相机与双目测距:视差原理、标定与角点剔除实战
1. 从一台能测距的相机说起三条技术路线到底差在哪第一次接触三维感知的人几乎都会卡在同一个问题上市面上叫深度相机的东西那么多有的长得像个方块有的像个圆柱还有的干脆就是两个镜头加一条红外激光它们测量的物理量根本不是一回事。我最早做机器人抓取项目时采购单上写着深度相机一台结果买回来的设备在黑色塑料件上直接输出一片空洞换另一台又能测出来——那时候我才真正意识到深度相机其实是一个统称底下藏着的成像原理可以差到十万八千里。先把概念理清楚。深度相机指的是能够直接输出每个像素点到相机距离的设备它是一个功能定义不是原理定义。而双目相机指的是靠左右两个镜头从不同视角拍摄同一场景通过寻找同一点在两个画面中的位置差视差来反推距离的设备它是一种实现方式。也就是说双目相机可以做成深度相机但深度相机不一定是双目——这层包含关系是后面所有讨论的地基。目前在工程里真正被大量使用的主要是三条路线结构光、飞行时间ToF、双目立体视觉。结构光靠投射已知图案用图案在物体表面的形变来解码深度ToF直接测量光脉冲往返的时间双目则完全被动只靠两个镜头的几何关系算距离。这三者在精度、距离范围、成本、环境适应性上的表现截然不同选错了方案后面所有的算法优化都是白费力气。1.1 结构光、ToF、双目成像原理的直观拆解结构光的思路有点像投影仪打格子。它往场景里投一片红外散斑或者正弦条纹如果物体是平面接收到的图案应该和投射图案一致一旦表面有凹凸图案就会被拉扯变形通过分析这些变形量就能反算出深度。它的优势是近距离0.3 到 1.5 米精度非常漂亮毫米级甚至亚毫米级都能做到而且因为用了主动光源在纹理单一的白墙上也能出图。但它的软肋同样明显强光下红外图案会被阳光淹没室外基本歇菜两个设备对着同一片区域工作还会互相干扰。ToF 走的是另一条路。它发射调制过的光测量返回光与发射光之间的相位差直接把时间转换成距离。因为每个像素独立测距它不需要复杂的立体匹配算力开销小、帧率高而且测距范围比结构光远得多室内三五米没问题。代价是精度通常低于结构光尤其是多路径反射的场景比如墙角、玻璃容易测出错误的距离。早期的 ToF 分辨率普遍偏低现在虽然提升了不少但和普通 RGB 相比仍有差距。双目则是纯粹的几何派。它不主动发光只靠同一物点在左右图中的位置不同来计算距离。这个位置差叫视差视差越大说明物体越近。理论上只要两个镜头的相对位姿标定准确它就能在任意距离、任意光照只要有纹理下工作。它最迷人的地方是被动、成本可控、分辨率可以跟着 RGB 走而且能同时得到稠密的深度和彩色纹理。难点也很实在需要纹理白墙、纯色地板这类地方匹配不上计算量大做实时稠密匹配对算力有要求最重要的是标定精度直接决定最终精度标定跑偏一个像素深度误差可能就上天了。1.2 一张表看清三种方案的适用边界我把这几年踩过的项目和选型经验整理成一张对照表方便你快速判断该往哪个方向走。需要说明的是表里的数值是常见量产模块的大致范围具体型号差异很大选型时还是要以实测为准。维度结构光ToF双目典型有效距离0.3 - 1.5 m0.5 - 5 m0.3 - 20 m取决于基线近距精度高毫米级中中高近距可做到毫米级室外表现差一般到较好好不受红外干扰纹理依赖低低高算力开销中低高多机互相干扰严重轻微无成本中中高低到中深度图分辨率中中可与 RGB 同级从这张表能看出来没有一种方案通吃。室内近距离高精度、对纹理不敏感的场景比如人脸、精密装配、手部跟踪结构光是最优解中远距离、对帧率和功耗敏感的场景比如扫地机避障、手势交互ToF 更合适需要高分辨率稠密深度、要拿到彩色纹理、且成本敏感的场景比如双目测距、三维重建、农业采摘机器人双目很难被替代。顺带说一句现在市面上很流行把两种方案揉在一起做成一体式深度相机比如那种白色圆柱形、被网友起了各种外号的桌面设备。它们内部往往是结构光或者 ToF 加一个 RGB出厂就已经把深度和彩色对齐好了插上就能用对刚入门的人确实友好。但如果你想真正搞懂深度到底是怎么来的一台能自己标定、自己跑匹配的双目相机是最好的老师——因为它把所有的中间过程都暴露给你了。1.3 为什么双目在高精度场景依然不可替代有人会问既然结构光和 ToF 都能直接出深度为什么还要费劲做双目匹配我的答案是三个字可控性。第一双目没有主动光源意味着它不会干扰别的设备也不会被别的设备干扰。在多机器人协作的场景里这一点价值极其高——你很难想象十台结构光相机对着同一片工作区互相打红外那画面基本是废的。第二双目的精度是可以设计的。基线和焦距决定了它在某个距离上的理论精度你可以根据实际工作距离去选镜头和基线长度把精度堆在你最需要的那一段。结构光和 ToF 一旦出厂精度曲线就基本固定了。第三双目能同时提供稠密深度加彩色纹理而且两者的分辨率可以做到一致。这对三维重建、语义分割、SLAM 这类需要纹理和几何联合优化的任务来说非常关键。不过这里要泼一盆冷水双目的这些优势全部建立在一个前提上——标定必须准。我见过太多项目双目算法写得花里胡哨最后卡在标定上深度图糊成一团。所以后面我会花大量篇幅讲标定尤其是那个很多人忽略但极其关键的环节剔除不合格角点。2. 双目测距的数学骨架视差、基线与焦距怎么互相牵制在动手标定之前最好先把双目的数学关系吃透。很多人是照着教程跑通代码就完事结果一旦深度不对完全不知道从哪个参数下手排查。其实整个双目测距的核心就一个公式理解了这个公式你就知道所有误差是从哪来的。2.1 从相似三角形推导深度公式设想左右两个相机光心之间的距离是 B这个叫基线两个相机的焦距都是 f。空间中有一个点 P它在左相机成像平面上的横坐标是 x_L在右相机上是 x_R。因为两个相机平行放置且经过了极线校正后面会讲所以 P 在左右图中的纵坐标是一样的只有横坐标有差异。这个差异 d x_L - x_R 就是视差。根据相似三角形关系可以推出Z f * B / d其中 Z 就是点 P 到相机的深度。这个公式极其简洁但信息量很大。它告诉我们三件事深度和视差成反比。视差越小深度越大而且是非线性的。这意味着远处的物体视差变化一点点深度就变化很大——所以双目在远距离精度会急剧下降。基线越大同样视差对应的深度越大。或者说基线越大在同一个距离上产生的视差越大深度分辨能力越强。焦距越大视差越大。长焦镜头对远处的分辨能力更强。把这三点合起来看双目的精度本质上是用基线换的。但基线不能无限加大因为基线越大左右两个相机的公共视野越小近距离的盲区越大而且两个视角差异太大时匹配算法会因为透视变形而失效。2.2 基线选择的取舍精度与盲区的博弈我来给一个具体的估算这样你在选型时有数。假设你要做一个 0.5 米到 3 米工作范围的双目传感器是常见的 1/3 英寸分辨率 1280x720像素尺寸 3 微米选了 f 4 毫米的镜头。那焦距换算成像素单位就是 f_px 4 / 0.003 ≈ 1333 像素。现在算精度。深度误差和视差误差的关系对公式求导可以得到ΔZ Z² * Δd / (f_px * B)假设视差估计误差 Δd 是 0.25 像素这是做得比较好的亚像素匹配精度基线 B 60 毫米那么在 Z 1 米处ΔZ 1² * 0.25 / (1333 * 0.06) ≈ 0.0031 m ≈ 3.1 mm在 Z 3 米处ΔZ 9 * 0.25 / (1333 * 0.06) ≈ 0.028 m ≈ 28 mm看到了吧同样的硬件和算法1 米处精度 3 毫米3 米处就掉到 28 毫米差了将近十倍。这就是平方项在作祟。那如果把基线加到 120 毫米呢1 米处误差降到 1.5 毫米3 米处降到 14 毫米。代价是最近可测距离会明显变大——基线越大左右相机要看到同一个近处物体就越困难通常最小工作距离会增大到基线的 10 到 20 倍左右。所以选基线的时候我的一般做法是先确定最重要的那个工作距离然后用公式反推需要多大基线。如果你的核心场景是 1.2 米的抓取作业那就把 1.2 米处的精度调到最优别去纠结 5 米外有多准。2.3 极线校正与立体匹配的基本流程理论上两个相机不可能做到完全平行且完全同构所以实际使用中必须做一步极线校正Stereo Rectification。它的作用是把左右两个相机的成像平面旋到一个共同的虚拟平面上让两幅图的行完全对齐。校正之后同一个物点在左右图中的纵坐标相同匹配就只需要在水平方向的一行里搜复杂度从二维降到一维速度快了不止一个量级。校正依赖的就是标定得到的外参 R 和 T右相机相对左相机的旋转和平移以及各自的内参。OpenCV 里用stereoRectify计算校正映射再用initUndistortRectifyMap生成查找表然后remap应用即可。校正之后进入立体匹配。常见的思路分两条局部匹配SGBM、BM在每一行的某个视差范围内计算匹配代价比如 Census 变换、绝对差选代价最小的视差。速度快适合实时。全局匹配GC、图割、半全局加入平滑约束做全局优化效果好但慢。工程上 90% 的场景我会用SGBM半全局块匹配它在效果和速度之间找到了很好的平衡。它的核心参数有这几个每一个都会显著影响结果参数作用调参建议minDisparity最小视差通常为 0若校正有偏移可微调numDisparities视差搜索范围必须是 16 的倍数按最近工作距离算B*f/ZminblockSize匹配块大小奇数纹理弱用大需要细节用小P1/P2视差平滑惩罚P2 通常是 P1 的 4 倍左右uniquenessRatio唯一性检查5 到 15 之间越大越严格speckleWindowSize散斑过滤窗口50 到 200滤掉小块噪声numDisparities这个参数最容易设错。它是这么算的如果最近要测到 Z_min 0.5 米基线 60 毫米f_px 1333那最大视差是1333 * 60 / 500 ≈ 160向上取到 16 的倍数就是 160。设小了近处会有大片测不到设大了白白浪费算力还容易引入误匹配。3. 双目标定实操从采集到参数输出标定这件事说难不难说简单也真不简单。它本质上是一个优化问题找一组内外参使得所有标定板上角点重投影回图像后和实际检测到的位置尽可能一致。原理不复杂但坑全在细节里。3.1 标定板选择与采集姿态的讲究先说标定板。常见的有棋盘格和圆点阵列两种。棋盘格的优点是角点检测精度高、OpenCV 支持成熟缺点是角点方向容易发生歧义需要靠行列顺序去纠正圆点阵列抗模糊更好但圆心定位在透视变换下会有偏心误差需要用findCirclesGrid配合专门的标定板。我个人在双目上更偏向棋盘格因为角点的亚像素定位cornerSubPix在棋盘格上表现得非常稳定。标定板尺寸的选择有个实用规则让标定板在图像中占据大概 1/3 到 1/2 的面积。太小了角点定位误差会被放大太大了边缘畸变严重且容易出现部分角点出画。打印的时候一定要用哑光纸别用光面纸反光会让角点检测飘。采集姿态是最容易偷懒的地方但恰恰最影响结果。我的经验是准备25 到 40 组图像对双目需要同时采集左右图且必须是同步的并且满足这几个条件覆盖整个视野标定板要出现在图像的左上、右上、左下、右下、中心各个区域不能全堆在中间。因为畸变参数是靠边缘的数据约束出来的中间再多也没用。姿态多样要有正对相机的也要有大角度倾斜的俯仰、偏航、滚转都要有倾角最好能覆盖到 30 度以上。倾斜姿态对解算焦距和畸变特别重要。距离多样从近到远都要有覆盖你预期的整个工作距离。左右图同时清晰这是双目特有的要求。因为是同一时刻拍两张标定板必须落在两个相机的公共视野内且两边都清晰、不模糊。如果你的双目基线很大公共视野很窄那采集时要特别小心。这里有个血泪教训我第一次做双目标定时为了图快用了一个先拍左图再拍右图的方案因为只有一台相机。结果标定出来的外参 R、T 完全不对深度图整个是扭曲的。原因是标定板在两次拍摄之间动了左右图对应的是标定板的不同位姿几何约束根本不成立。双目标定必须同步采集左右图对应同一时刻的同一块板。如果手头没有双目同步采集的设备至少要保证标定板绝对静止且两块相机刚性固定。3.2 张正友标定法的调用与参数含义数据采好之后就可以上代码了。整个流程分三步单目标定得到两个相机的内参和畸变再用stereoCalibrate求外参最后stereoRectify求校正参数。import cv2 import numpy as np import glob # ---------- 1. 准备标定板的三维坐标 ---------- PATTERN (9, 6) # 内角点数列 x 行 SQUARE_SIZE 25.0 # 方格实际边长单位 mm criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6) objp np.zeros((PATTERN[0] * PATTERN[1], 3), np.float32) objp[:, :2] np.mgrid[0:PATTERN[0], 0:PATTERN[1]].T.reshape(-1, 2) objp * SQUARE_SIZE objpoints [] # 世界坐标系下的角点 imgpoints_l [] # 左图角点 imgpoints_r [] # 右图角点 # ---------- 2. 角点检测 ---------- left_files sorted(glob.glob(calib/left/*.png)) right_files sorted(glob.glob(calib/right/*.png)) assert len(left_files) len(right_files) for lf, rf in zip(left_files, right_files): img_l cv2.imread(lf) img_r cv2.imread(rf) gray_l cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY) gray_r cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY) ok_l, corners_l cv2.findChessboardCorners(gray_l, PATTERN, None) ok_r, corners_r cv2.findChessboardCorners(gray_r, PATTERN, None) if ok_l and ok_r: corners_l cv2.cornerSubPix(gray_l, corners_l, (11, 11), (-1, -1), criteria) corners_r cv2.cornerSubPix(gray_r, corners_r, (11, 11), (-1, -1), criteria) objpoints.append(objp) imgpoints_l.append(corners_l) imgpoints_r.append(corners_r) else: print(f跳过{lf} / {rf} 角点未找全)角点检测完先做单目标定把内参初值定下来再固定内参做双目外参标定# ---------- 3. 单目标定 ---------- flags cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_NORMALIZE_IMAGE ret_l, K1, D1, _, _ cv2.calibrateCamera( objpoints, imgpoints_l, gray_l.shape[::-1], None, None) ret_r, K2, D2, _, _ cv2.calibrateCamera( objpoints, imgpoints_r, gray_r.shape[::-1], None, None) # ---------- 4. 双目标定固定内参 ---------- ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( objpoints, imgpoints_l, imgpoints_r, K1, D1, K2, D2, gray_l.shape[::-1], criteriacriteria, flagscv2.CALIB_FIX_INTRINSIC) # ---------- 5. 极线校正 ---------- R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, gray_l.shape[::-1], R, T, alpha0, flagscv2.CALIB_ZERO_DISPARITY)这里有几个参数必须解释清楚不然出了问题你不知道去哪找。CALIB_FIX_INTRINSIC这个标志非常关键。它的意思是我信任单目标定出的内参双目标定只优化外参。另一种选择是CALIB_USE_INTRINSIC_GUESS让双目标定同时微调内参和外参。在实际操作中如果单目标定的重投影误差已经很小0.1 像素量级我倾向于固定内参因为这样更稳如果单目误差偏大可以放开让双目一起优化但要小心过拟合。alpha参数控制校正后图像的黑边有多少。alpha0表示裁掉所有黑边只保留有效像素代价是丢失一部分视野alpha1保留全部像素但黑边很多。我一般先用 0 看看如果视野损失太大再调。CALIB_ZERO_DISPARITY保证校正后主点在水平方向对齐这样无穷远处的视差为零SGBM 的minDisparity可以直接从 0 开始省很多事。3.3 标定结果怎么判断好坏重投影误差和极线误差标定跑完ret会返回一个总的重投影误差。很多人一看小于 0.3 像素就以为万事大吉了这个想法很危险。总误差是一个平均值它会把个别离谱的图片掩盖掉。我的判断标准是三看第一看整体。双目重投影误差低于 0.3 像素算合格低于 0.15 像素算优秀。如果超过 0.5 像素基本不用往下走先去查数据。第二看单张。要逐张算出每张图的重投影误差把误差最大的几张挑出来看。常见问题是某几张图角点检测有偏比如运动模糊、反光这几张图会拉高整体误差。经验上如果某张图的误差是平均值的 2 倍以上就该怀疑它。第三看极线误差。这是双目特有的检查项把左图检测到的角点按标定得到的基础矩阵 F 映射到右图算出它到右图对应极线的垂直距离这个距离就是极线误差。好的标定应该让这个值在 0.2 像素以内。如果极线误差大说明 R、T 不准那就是外参的问题。def reprojection_error(objp, imgp, K, D, rvec, tvec): proj, _ cv2.projectPoints(objp, rvec, tvec, K, D) err np.linalg.norm(proj.reshape(-1, 2) - imgp.reshape(-1, 2), axis1) return err.mean(), err # 平均值 每个点的误差4. 剔除不合格角点标定精度的真正分水岭现在进入这篇文章最想讲的部分。前面所有的步骤网上教程一抓一大把照着抄都能跑通。但从能跑通到深度图真的准中间隔着的就是角点质量这一关。这是我在几个量产项目里反复验证过的结论同样一批数据做不做角点剔除深度误差能差出一倍。4.1 什么样的角点算不合格先定义清楚什么叫不合格。角点不是非黑即白的它是一个连续的质量问题。但从工程角度可以分成几类第一类是明显错位。棋盘格在边缘有畸变或者图像有运动模糊导致角点检测器找到了错误的位置偏离真实角点一两个像素甚至更多。这类点对优化是纯拖累。第二类是半遮挡。标定板有一角被桌子、手或者设备挡住了findChessboardCorners有时还能猜出缺失角点的位置这些猜出来的点误差极大。第三类是亚像素定位失败。cornerSubPix在纹理对比度低、或者角点附近有噪声时会收敛到一个局部错误位置。这种情况很难从图像上肉眼看出来但它确实存在。第四类是透视畸变引起的系统性偏差。大角度倾斜时棋盘格的角点因为镜头畸变校正不完美会有系统性的偏移。这类偏差不是坏点但可以用鲁棒损失函数来降低它的权重。关键问题是这些不合格角点光靠看是看不出来的。一张检测正常的图像里可能藏着几个偏差 0.5 像素的点肉眼完全分辨不了但它们在优化里会实实在在拉偏结果。4.2 基于重投影误差的迭代剔除流程我的做法是迭代式的重投影误差剔除。逻辑很简单先做一次标定算出每个角点的重投影误差把误差超过阈值的点挖掉如果一张图里挖掉的点太多整张图都丢掉然后用剩下的数据重新标定。重复两到三轮直到没有点被剔除为止。def calibrate_with_rejection(objpoints, imgpoints_l, imgpoints_r, image_size, max_rounds3, thresh1.0): 迭代剔除重投影误差过大的角点 thresh: 单个角点的重投影误差阈值像素 cur_obj [o.copy() for o in objpoints] cur_l [p.copy() for p in imgpoints_l] cur_r [p.copy() for p in imgpoints_r] for rnd in range(max_rounds): ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( cur_obj, cur_l, cur_r, np.eye(3), np.zeros(5), np.eye(3), np.zeros(5), image_size, criteria(cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6), flagscv2.CALIB_FIX_INTRINSIC if rnd 0 else 0) new_obj, new_l, new_r [], [], [] dropped_pts, dropped_imgs 0, 0 for i in range(len(cur_obj)): # 左相机位姿由基础矩阵与内参分解这里用 solvePnP 逐帧求 ok_l, rv_l, tv_l cv2.solvePnP(cur_obj[i], cur_l[i], K1, D1) ok_r, rv_r, tv_r cv2.solvePnP(cur_obj[i], cur_r[i], K2, D2) if not (ok_l and ok_r): dropped_imgs 1 continue err_l np.linalg.norm( cv2.projectPoints(cur_obj[i], rv_l, tv_l, K1, D1)[0].reshape(-1, 2) - cur_l[i].reshape(-1, 2), axis1) err_r np.linalg.norm( cv2.projectPoints(cur_obj[i], rv_r, tv_r, K2, D2)[0].reshape(-1, 2) - cur_r[i].reshape(-1, 2), axis1) keep (err_l thresh) (err_r thresh) n_keep int(keep.sum()) n_total len(cur_obj[i]) # 保留角点太少整张图作废 if n_keep 0.75 * n_total: dropped_imgs 1 continue dropped_pts n_total - n_keep new_obj.append(cur_obj[i][keep]) new_l.append(cur_l[i][keep]) new_r.append(cur_r[i][keep]) print(f第 {rnd1} 轮剔除角点 {dropped_pts} 个 f丢弃图像 {dropped_imgs} 张重投影误差 {ret:.4f}) cur_obj, cur_l, cur_r new_obj, new_l, new_r if dropped_pts 0 and dropped_imgs 0: break return ret, K1, D1, K2, D2, R, T, E, F这段代码有几个设计点值得展开说。第一阈值怎么定。我一般从 1.0 像素开始第一轮剔掉最离谱的然后降到 0.5 像素做第二轮最后用 0.3 像素做第三轮收紧。不要一上来就用 0.2 像素因为第一轮标定本身就不准算出来的误差会虚高用严格阈值会把好点也误杀。第二为什么整张图要能丢。如果一张图超过 25% 的角点不合格那说明这张图的采集质量本身就有问题——可能是模糊了、曝光过了、或者标定板在动。这种图留着只会稀释有效约束。丢掉 5 张烂图比留着它们拉低整体精度要划算得多。第三单帧用solvePnP求位姿。stereoCalibrate内部虽然也做了这件事但它不把每帧的位姿返回给我们。所以在做剔除时需要手动对每一帧解一次 PnP这样才能算出该帧每个角点的独立误差。这一步会慢一点但只在标定阶段跑一次完全可以接受。第四CALIB_FIX_INTRINSIC的时机。第一轮我用的是全参数优化内参加外参一起放开因为此时内参初值还不够准从第二轮开始固定内参只优化外参收敛更稳定。4.3 剔除的度剔太多反而更差的反直觉现象这里有个非常重要的经验我几乎没在教程里见过有人提醒剔除过度标定会变差而不是变好。原因在于标定本质上是一个求解超定方程组的过程需要足够的约束。角点数量决定了方程的条数。如果你剔除得太狠每张图只剩下一半角点总约束数大幅下降此时一些本来被约束住的参数尤其是畸变系数就会变得不稳定解会出现漂移——误差看着是小了但那是过拟合出来的小用在新图像上就是错的。我遇到过最典型的一次一个项目标定误差怎么都下不去我一狠心把阈值调到 0.15 像素结果重投影误差降到了 0.08看着特别漂亮。但上机一测深度图在图像边缘整个翘起来了。回过头看那张最边上的标定图被剔得只剩三十几个角点畸变系数失去了有效约束。把阈值调回 0.3误差回升到 0.18 像素但深度图反而准了。所以我的建议是把握好三个底线每张图保留的角点不少于总数的 75%低于这个数就整张丢。最终参与标定的图像不少于 15 张低于这个数就得重新采集。重投影误差不必追求极致小0.1 到 0.2 像素之间是非常健康的区间小到 0.05 反而要警惕。还有一个细节剔除应该是双向的。左图的角点和右图的角点要一起判定——只有当某个物点在左右两边的误差都合格时才保留。如果只在左边合格、右边偏差大那说明这个点本身就不可靠必须一起扔。这也是我上面代码里用(err_l thresh) (err_r thresh)的原因。另外提一句剔除的收益有多大。以我手头一个 1.2 米工作距离的抓取项目为例同一批 32 张图像处理方式重投影误差1.2m 处深度 RMSE不剔除0.42 px7.8 mm迭代剔除0.5 px0.21 px4.1 mm过度剔除0.15 px0.09 px6.3 mm数据说得很清楚适度剔除把深度误差砍掉将近一半过度剔除反而比不剔除好不了多少。这个表格我现在还贴在工位上提醒自己别贪那个漂亮的误差数字。5. 从标定到可用深度图几个绕不过去的工程坑标定做完了深度图也能出来了但离能用还有一段距离。这一段往往是最耗时间的因为问题不是原理性的而是一个个具体的、零散的工程细节。5.1 视差空洞、边缘飞点和纹理缺失区的处理拿到原始视差图你大概率会看到三类问题。第一类是空洞。物体边缘、遮挡区、纹理缺失区SGBM 给不出可靠视差。解决办法是在 SGBM 里开disp12MaxDiff做左右一致性检查保证不过检然后对空洞做后处理。简单点的做法是用邻域中值填讲究一点用cv2.inpaint或者基于引导滤波的填充。但要注意填充只是视觉上好看它并没有真的恢复几何信息如果你下游要做精密测量空洞区域宁可标成无效也别填一个假值进去。第二类是边缘飞点。物体边界处视差会出现明显的拖尾深度值突变。这是匹配窗口跨越了深度不连续面导致的是所有块匹配算法的通病。缓解手段有用speckleWindowSize做散斑过滤用 WLS加权最小二乘滤波器做视差细化OpenCV 里有ximgproc.createDisparityWLSFilter效果非常明显我用它几乎是标配。第三类是弱纹理区。白墙、纯色地板、金属反光面这些地方匹配代价图是一个平原没有明确最小值视差是随机的。这个问题算法层面基本无解只能靠主动纹理。我见过有人给场景打了一个散斑投影器效果立竿见影。但这就把双目变回了主动双目丢失了被动优势是否值得要看场景。这里分享一个小技巧在计算深度前先做一次深度范围的合理性裁剪。根据你算好的numDisparities把超过理论最近距离和超过理论最远距离的视差全部置无效。这一步能干掉大量远距离的离谱飞点成本几乎为零效果却很好。5.2 深度相机与双目相机如何组合使用实际项目里我的建议往往是不要二选一而是组合。举一个我做过的例子。一个室内拣选机器人需要在 0.4 到 1.5 米范围内稳定识别和抓取各种材质的物体其中有黑色橡胶件、透明塑料袋、还有金属件。纯结构光方案在黑色和金属上表现糟糕纯双目在透明塑料袋上直接失效。最后的方案是主传感器用双目负责提供高分辨率的稠密深度和彩色纹理用于整体场景理解和物体定位辅助传感器用一台近距离的主动深度模块专门在抓取前的最后阶段对目标区域做一次高精度补测把双目在弱纹理区测不到的地方补上。两者的点云通过标定好的外参融合在一起。组合使用的关键在于外参标定要准。这里就不能用棋盘格标定板了因为两个传感器的视场和原理不一样。我一般用一块带纹理的刚体标定物两个传感器都能在它上面找到特征然后做点云配准求外参。也可以用一个可以同时被两边识别的标定球。还有一点融合是有代价的。两套数据对齐误差、时间戳同步误差、两套坐标系下的噪声特性差异都会引入新的问题。我的原则是能用一套解决的绝不加第二套。组合方案只有在单一方案确实覆盖不了核心场景时才值得上。5.3 我在实际调试中总结的几条经验最后把这几年的零散经验整理一下都是些文档里不太会写的东西。关于标定频率。标定不是一次性的。相机装到设备上、经历了运输、温度变化、或者被磕碰之后外参就会漂。我的做法是每次开机做一次快速自检用一个已知位置的标定板拍一张算一下重投影误差超过阈值就提示重新标定。这个自检代码我封装成了一个小模块几十行省了很多现场排查的时间。关于同步。如果是动态场景左右相机的曝光必须严格同步。用软件触发是会产生几毫秒到几十毫秒的偏差的物体一动视差就全乱了。硬触发是刚需别在这上面省钱。关于曝光一致。左右相机的曝光时间、增益要尽量设在同一个值并且关掉自动曝光。因为自动曝光在左右两边会各自调节拍出来的图像亮度不一致匹配代价会明显变差。我通常的做法是先用自动曝光跑一圈确定一个合理值然后全部锁死。关于温度。镜头和传感器在温度变化时会发生微小的机械形变焦点和主点都会漂。做高精度测量时设备最好预热 20 到 30 分钟再标定标定完就别挪动。关于验证。标定完千万别只看重投影误差就上机。一定要拿一个已知尺寸的物体比如一块标准量块在几个不同的距离上测一下实际深度和真值比对。这个端到端验证是唯一能证明标定真的有效的步骤重投影误差只是间接指标。我印象最深的一次排查是深度图在某个特定角度下总是有个固定的偏差带。查了两天最后发现是标定板有一格被打印机的墨点污染了导致那一帧的一个角点检测偏了 1.5 个像素。自从加了角点剔除流程之后这类问题基本再没出现过——因为那个坏点会被自动挖掉。如果让我给刚入门的人一句话建议把 80% 的精力花在数据采集和标定质量上剩下 20% 再考虑算法。双目这套东西几何是确定的代码是现成的真正决定成败的永远是你手上那几十张标定图干不干净。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。