普通 RGB 相机拍出来的照片能告诉你"这东西长什么样",但它说不出"这东西离我多远、有多大、在不在移动"。深度摄像头解决的恰恰是后面这件事——它给画面里的每一个像素都附带一个距离值,让机器第一次具备了"空间感"。这张深度图,向上可以拼出三维点云做重建、测量、抓取,向下可以喂给避障、SLAM、手势交互这类算法。如果你在做机器人、三维扫描、体积测量、工业检测或者 AR/VR 相关的东西,深度摄像头大概率会在你的物料清单里出现;而如果你只是想搞清楚它到底怎么工作、为什么同一场景下不同模组表现差那么多,这篇内容也够用。我前后折腾过双目、结构光、ToF 这几条路线的模组,从几十块的玩具级到工业级都摸过,踩的坑不比写过的代码少,下面就把这些东西摊开讲。
1. 深度摄像头和普通相机差在哪:从像素到距离的那一步跨越
1.1 一张深度图里到底存了什么
先把最容易被误解的地方说清楚:深度摄像头输出的不是"更立体的一张照片",而是一张每个像素值代表距离的标量图。它通常还带一路 RGB,但那路 RGB 只是配菜,主菜是那张看起来灰蒙蒙、只有轮廓没有细节的深度图。深度图的分辨率往往比 RGB 低(常见 640×480、848×480,好一点的 1024×768),因为每算一个深度值都要付出额外的光学或计算代价,不像感光那样可以暴力堆像素。
一张典型的深度图长这样:靠近相机的物体偏亮(数值小),远处偏暗(数值大),完全没有回波的地方是 0 或者某个约定的无效值。你会发现桌子边缘有一圈毛刺,玻璃窗位置是个洞或者一片混乱的数值,黑色显示器背板上是一片空洞。这些不是设备坏了,而是测距物理本身在这些条件下的必然结果,后面第 6 章会逐条解释为什么。
深度图的价值在于它是"可计算的几何"。有了它,你就能从二维图像反推三维坐标,进而做体积、做定位、做抓取。RGB 图像给不了这个能力,因为同一张二维图像可以对应无穷多种三维世界——这就是经典的透视歧义。
1.2 深度图的三种常见数据语义,别搞混
拿到一台新设备,第一件事不是写算法,而是搞清楚它吐出来的数据到底是什么单位、什么语义。我见过太多次因为单位搞错,导致点云尺度差了 1000 倍,整个标定链条全废。
| 数据格式 | 典型取值含义 | 转换方式 | 常见来源 |
|---|---|---|---|
| uint16 原始值 | 距离 / depth_scale | 乘以 scale(常为 0.001 m) | 多数 ToF、结构光模组 |
| float32 米 | 直接就是米 | 直接用 | 部分工业相机、SDK 转换后 |
| 视差图 disp | 像素级视差 | Z = fx·B / disp | 双目、立体匹配输出 |
uint16 是一个很讨巧的设计:65535 × 0.001 m = 65.5 m,绝大多数室内场景够用,而且比 float32 省一半带宽。但不同厂商的 scale 不一样,有的用 0.001,有的用 0.00025,有的干脆在 SDK 里给一个get_depth_scale()接口让你查。我的习惯是初始化后立刻把这个值打印出来,记在配置文件里,而不是靠记忆。
视差图那一类要特别注意:视差和深度是反比关系,视差为 0 的区域意味着无穷远或者匹配失败,做除法时会直接产生 inf。做双目的时候如果不做无效值掩膜,后面一定会被 inf 和 nan 咬一口。
1.3 为什么"深度"比"颜色"难获取这么多
颜色是"被动接收"的,光子打到感光芯片上产生电荷,事情就结束了。深度是"主动推断"的,无论走哪条技术路线,都要在硬件或算法层面额外做一件事:建立像素与真实距离之间的映射关系。
被动双目靠的是两个视角之间的三角关系,代价是需要纹理,没有纹理就没法匹配;结构光靠的是投射已知图案再解算形变,代价是对环境光和被测材质敏感;ToF 靠的是光的往返时间,代价是需要高速调制和大量光子积累,功耗和成本都上去了。每一条路线都是拿一种"麻烦"去换另一种"麻烦",不存在通吃的方案。
这也解释了为什么工业界经常同时挂两三种传感器:一台深度相机用于大范围粗定位,一把线激光轮廓仪用于局部高精度测量,各自干自己擅长的那一段。
2. 四条主流测距路线的取舍:双目、结构光、iToF/dToF、激光轮廓
2.1 双目立体视觉:最便宜,也最挑场景
双目是原理最朴素的一条路:两个相机平行摆放,基线距离 B 已知,同一个物理点在左右图上的横坐标差就是视差 d,深度由三角关系给出:
Z = (fx · B) / d其中 fx 是焦距换算到像素单位后的值,B 是基线长度(米),d 是视差(像素),Z 就是距离(米)。举个具体数:fx = 1000 px,B = 0.06 m,视差算到 10 px,那 Z = 1000 × 0.06 / 10 = 6 米。反过来,视差越小对应距离越远,当视差缩到亚像素级别(比如 0.1 px)时,距离已经到 600 米——当然实际根本到不了,因为此时的误差已经大得离谱。
双目最大的优点是成本低、被动、无红外发射、不会互相干扰,多台同开也没问题。缺点同样明显:纯色墙面、白纸、玻璃这些没有纹理的表面,匹配算法找不到对应点,直接出空洞;在低照度下两个相机都看不清,深度也就无从谈起。所以双目方案通常要配一个"纹理投射器"(不可见光散斑投射),这就变成了主动双目,本质上已经和结构光沾边了。
2.2 结构光:近距离高精度的代价
结构光的思路是"既然没纹理,那我就投一个纹理上去"。投影器发出已知的图案(点阵、条纹、伪随机散斑),相机拍下图案在被测表面上的形变,通过投影器与相机之间的基线做三角测量,解出每个点的深度。
这条路线在 0.3 米到 1.5 米这个区间里精度非常能打,早期的人脸识别、手势交互、近距离三维扫描大量采用。它的软肋也很清晰:投射功率受限于人眼安全,在强阳光下图案直接被淹没;多台设备同开会互相看到对方的散斑,产生伪匹配;对黑色吸光材质和镜面反射材质表现很差,因为回波太弱或者被反射到别的方向去了。
结构光的精度和基线、投射图案的周期密切相关,也是一个随距离平方衰减的规律。放在扫描仪、人脸支付、近距离抓取这种"工作距离固定且短"的场景里最合适。
2.3 飞行时间:从相位法到单光子计数
ToF 是直接测光的往返时间或者相位差。它有两个分支,别混为一谈。
iToF(间接飞行时间)发射的是连续正弦调制光,接收端测量回波相对于发射信号的相位延迟 φ,距离由下式给出:
d = (c · φ) / (4π · f_mod)c 是光速,f_mod 是调制频率。注意这里有个致命约束:相位只能测到 2π,超过就产生模糊。最大无模糊距离是
d_max = c / (2 · f_mod)f_mod = 100 MHz 时,d_max = 3×10⁸ / (2×10⁸) = 1.5 米。想测更远,就得降低调制频率,但精度又跟着下降。工程上的标准做法是用多个频率同时调制,用低频解模糊、用高频保精度,这就是所谓双频或三频解调。iToF 的多径干扰比较讨厌:光打到墙角再反射回来,会算出一个"折中"的距离,导致墙角出现错误的圆角。
dToF(直接飞行时间)走的是另一条路:发一个极短的脉冲,用 SPAD(单光子雪崩二极管)阵列统计回波光子到达的时间,形成一个时间直方图,直方图的峰值位置就是飞行时间。它天然支持多回波(能同时区分玻璃前表面和后面的物体),量程也更容易做远,代价是需要大量累积帧数来提高信噪比,而且 SPAD 阵列的像素做不大,分辨率通常偏低。
一句话总结:近距离高分辨率用 iToF,远距离、强环境光、多回波需求用 dToF。
2.4 线激光与激光雷达:被忽略的工业主力
严格说线激光轮廓仪不算"摄像头",但在产线上它是绝对主力。它投射一条激光线,用相机拍下线的形状,逐条扫描出剖面,再由运动机构拼出完整三维。精度可以做到微米级,重复性好到离谱,缺点是必须配合运动或者需要扫描机构,单帧拿不到整个面。
而扫描式激光雷达(机械旋转或半固态)本质上是"用一条测距光束去扫面",输出的是稀疏点云。它不用来做稠密重建,用来做定位、避障、大范围测绘很合适。把激光雷达和相机做外参标定融合,是很常见的组合拳,视觉给语义、激光给几何。
3. 选型时必须盯死的硬指标,以及它们背后的数学
3.1 量程、基线与焦距:一个公式决定一切
很多人选型只看"最大量程多少米",这是最容易上当的指标。量程标 10 米,不代表 10 米处能用——它往往只代表 10 米处还能返回一个非零值,精度可能已经差到 30 厘米。
回到双目的公式 Z = fx·B/d,把它对 d 求导,得到深度误差的传递关系:
ΔZ = (Z² · Δd) / (fx · B)这个式子信息量极大。深度误差与距离的平方成正比,与焦距和基线成反比。代入一组数:fx·B = 50(fx=1000 px,B=0.05 m),视差测量误差 Δd 取 0.1 px(亚像素匹配的典型水平),那么在 Z = 1 米处 ΔZ = (1 × 0.1)/50 = 2 毫米;到了 Z = 5 米处,ΔZ = (25 × 0.1)/50 = 50 毫米。同一个相机,距离拉远 5 倍,误差涨了 25 倍。
这就是为什么所有深度相机的参数表都会给你一条"精度-距离"曲线,而不是一个固定的精度数字。看到只给一个数字的,基本可以判断是营销材料。
iToF 和结构光的误差传播规律类似,也大致是平方关系。所以选型第一个问题永远是:我的工作距离是多少?如果只要 0.3~1 米,选个近距离小基线的高精度模组就够;如果要 5 米开外,就得接受厘米级误差,或者换 dToF。
3.2 深度分辨率随距离平方衰减,这才是真正的瓶颈
承接上面的结论,实战中最容易被忽视的一个坑是:明明量程够,但远处的物体边缘糊成一团,分割算法做不了。
原因是深度分辨率(能区分的最小距离差)同样随距离平方衰减。上面算出来 5 米处 ΔZ = 5 厘米,意味着 5 米外一个 3 厘米厚的纸板,深度图里根本分不出前后。如果你的应用是做远处的目标检测加测距,光有深度不够,还得配高分辨率 RGB 做识别,深度只负责测距。
第二个相关概念是"横向分辨率":在距离 Z 处,一个像素对应的实际物理尺寸约为
w = Z / fxfx = 1000 px 时,1 米处一个像素约 1 毫米,5 米处就是 5 毫米。这个值决定了你能分辨多细的结构。做小零件检测的时候,如果像素尺寸比特征还大,再好的算法也无能为力。
3.3 视场角、分辨率与盲区的三角约束
FOV、分辨率、盲区这三者之间存在硬性的几何约束,无法同时优化。
- 视场角决定了你能看多宽。FOV 大,边缘畸变大,标定和去畸变难度上升。
- 分辨率决定了角分辨率。同样的 FOV 下,分辨率越高,每个像素张角越小,能分辨的细节越多,但数据量和带宽也越大。
- **盲区(最小工作距离)**由基线和 FOV 共同决定。基线和投影器之间存在视差,太近的地方投影图案和相机视野不重叠,就没法测。双目相机贴到 10 厘米以内基本就废了。
一个典型的取舍:机器人避障需要广角看四周,通常选 90° 以上的 FOV,代价是分辨率被摊薄,远处基本没数据;体积测量需要精确贴近货物,选中等 FOV 加短盲区。这两个需求不能用同一台设备满足,硬凑只会两头不讨好。
3.4 抗环境光、材质适应性与多机干扰
这三个指标在参数表里经常是缺失的,但在真实场景里往往是决定成败的关键。
抗环境光:室内普通照明(几百 lux)所有设备都能扛,但靠窗有阳光直射(几万到十几万 lux)时,结构光和 iToF 基本都会崩,因为有用的调制信号被背景光淹没了。判断方法很简单,看厂商给的"推荐照度范围",或者直接问有没有阳光下的实测数据。dToF 在这方面的表现通常好一些,因为它靠的是时间统计而不是幅度对比。
材质适应性:拿一块黑色哑光板、一块镜面不锈钢、一块透明亚克力,在同一个距离下测三遍,对比有效像素比例和噪声,这是我最常用的一套"土办法评估"。对深度相机来说,黑色、镜面、透明是三道坎,能全过的方案几乎没有,只能看哪个对你的场景影响最小。
多机干扰:结构光和 iToF 同频同开时会互相干扰。多台结构光相机会看到彼此的散斑,产生虚假匹配;iToF 之间会因为相位调制频率接近而互相污染。解决办法有几种:错开曝光时序(需要硬件触发同步)、错开调制频率、或者在空间上拉开角度。选购前如果有多机需求,一定要问清楚厂商支持不支持外部触发和频率配置。
4. 从插上线到跑出第一帧点云:一条能复现的实操链路
4.1 环境与驱动:先把权限和内核这两关过了
Linux 下第一次插设备跑不起来,九成是权限问题。普通用户默认没有 USB 设备的读写权限,SDK 会报"找不到设备"或者"permission denied"。加一条 udev 规则就能解决:
# /etc/udev/rules.d/99-depth-camera.rules SUBSYSTEM=="usb", ATTRS{idVendor}=="8086", MODE="0666"idVendor 换成你设备的实际厂商 ID,用lsusb查。写完规则执行sudo udevadm control --reload-rules && sudo udevadm trigger,然后重新插拔。
另一类问题是 UVC 元数据。很多深度相机在彩色流之外还走 UVC 的元数据通道,内核默认可能没启用,需要加载模块时加参数:
sudo modprobe uvcvideo quirks=128这个 quirk 值让驱动把元数据当普通数据流处理。有些发行版还得更新内核版本,因为旧内核的 uvcvideo 不支持一定宽度以上的元数据。别问我怎么知道的,为了一个"设备识别到了但取不到流"的问题,我查过整整一个下午的内核日志。
4.2 取流、对齐与深度尺度换算
跑起来之后的第一件事是确认数据是对的。下面这段是最小可用示例,思路是取深度流加彩色流、做对齐、换算成米:
import numpy as np import pyrealsense2 as rs pipeline = rs.pipeline() config = rs.config() config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) profile = pipeline.start(config) # 深度尺度:把 uint16 原始值换算成米 depth_sensor = profile.get_device().first_depth_sensor() depth_scale = depth_sensor.get_depth_scale() print("depth_scale =", depth_scale) # 常见 0.001 # 把深度图对齐到彩色相机坐标系 align = rs.align(rs.stream.color) try: while True: frames = pipeline.wait_for_frames() aligned = align.process(frames) depth_frame = aligned.get_depth_frame() color_frame = aligned.get_color_frame() if not depth_frame or not color_frame: continue depth = np.asanyarray(depth_frame.get_data()).astype(np.float32) * depth_scale color = np.asanyarray(color_frame.get_data()) print("有效像素比例 = %.3f" % (np.count_nonzero(depth) / depth.size)) finally: pipeline.stop()这段代码里有两个点值得强调。一是深度尺度必须先读再乘,不要假设是 0.001。二是对齐很重要:深度相机和彩色相机有物理间距,两组像素不对应,直接拿同一个 (u, v) 去取深度和颜色会错位几厘米。对齐操作会做一次重投影,把深度图变换到彩色相机的视角下,代价是边缘区域会出现无效值。
4.3 深度图转点云:内参、外参与坐标系
深度图转点云靠的是相机内参。每个像素 (u, v) 和它的深度 Z 决定一个三维点:
X = (u - cx) · Z / fx Y = (v - cy) · Z / fy Z = Zfx、fy、cx、cy 就是内参,SDK 一般都能直接给:
intr = depth_frame.get_intrinsics() print(intr.fx, intr.fy, intr.ppx, intr.ppy, intr.model, intr.coeffs)注意在已对齐的情况下,必须用彩色相机的内参来反投影,而不是深度相机原来的内参——这一步搞错,点云会整体缩放或者偏移,而且不容易一眼看出来。
用 Open3D 可以直接从对齐后的彩色图和深度图生成点云:
import open3d as o3d color_raw = o3d.io.read_image("color.png") depth_raw = o3d.io.read_image("depth.png") rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth( color_raw, depth_raw, depth_scale=1000.0, # 和上面 depth_scale 对应 depth_trunc=3.0, # 超过 3 米直接截断,减少噪声 convert_rgb_to_intensity=False) intrinsic = o3d.camera.PinholeCameraIntrinsic( 640, 480, fx, fy, cx, cy) pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intrinsic) # 相机默认朝 +Z,可视化时把它转到更直观的朝向 pcd.transform([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) o3d.visualization.draw_geometries([pcd])depth_trunc这个参数我强烈建议设,别省。远处的深度噪声会被映射成一大片飘在空中的散点,把近处的结构全遮住。
4.4 我的标定流程:预热、平面板与重复性验证
出厂标定只能保证"能用",不能保证"够准"。如果你的应用涉及毫米级测量,必须自己验证一遍。
我的流程是这样的:设备开机预热 20 到 30 分钟,让传感器温度稳定下来,温漂在这个阶段最明显;然后把一块平整的白板(或标定板)放在工作距离处,正对相机,测一遍平面度——如果深度图上的平面起伏标准差超过你精度要求的一半,就要留意了;接着把板子沿深度方向移动几个位置,每个位置记录中心区域的平均深度,和卷尺量出来的真值对比,画一条误差曲线。
平面度验证还有个小技巧:用红外摄像头(手机前摄改装的或者专门的 IR 相机)去看结构光投射的图案,图案是否清晰均匀、有没有暗斑或者畸变,能直观反映投影器的状态。这个动作还能帮你判断环境光干扰的程度——在 IR 图里,阳光是很强的背景噪声。
5. 拿到点云之后:滤波、配准与常见后处理
5.1 无效值、飞点与边缘混合像素的处理
原始深度图有三个常客:无效值(0)、飞点(flying pixels)、以及边缘处的混合像素。
无效值就是没测到,直接掩膜掉即可。飞点出现在物体边缘,因为一个像素的视场内同时包含了前景和背景,算法算出了一个介于两者之间的距离,结果就是边缘处飘着一些"空中点"。这类点在点云里非常显眼,做分割的时候会造成误判。
处理方法有几种。最简单的是基于邻域一致性滤波:如果一个点和它 8 邻域的点深度差超过阈值,就判定为飞点。Open3D 里有现成的统计离群点移除:
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) pcd_clean = pcd.select_by_index(ind)nb_neighbors是邻域点数,std_ratio是标准差倍数阈值,值越小过滤越狠。实测下来 20 和 2.0 是个不错的起点,具体按噪声水平调。
还有一种是边缘膨胀:直接把深度图的边缘区域标记为无效。代价是边缘精度下降,但如果你的应用只关心物体内部,这个做法最省事。
5.2 时域滤波与多帧融合
单帧深度图的噪声是随机的,而静态场景下多帧的均值能显著降噪。很多 SDK 内置了时域滤波(temporal filter),原理是用当前帧和历史帧做加权平均,权重和深度差异挂钩——差异小的加大权重,差异大的减小权重,这样既降噪又不会在运动时拖影。
如果 SDK 没有,自己实现也不难:维护一个环形缓冲,对每个像素做中值或者指数加权平均。要注意的是在有运动的场景下,时域滤波会引入"鬼影",需要在运动和静止之间切换策略。我的做法是先用深度差分判断像素是否静止,静止的走时域滤波,运动中的走单帧双边滤波。
双边滤波(spatial filter)是空间域的,能在保边的同时平滑噪声。它在平滑的同时保留边缘,非常适合深度图,但计算量比均值滤波大。实时性要求高的场景下,可以考虑降采样之后处理再上采样。
5.3 点云配准与坐标变换链
如果你要做多视角拼接或者把深度数据放到机器人基坐标系里,就绕不开配准和外参标定。
粗配准常用 RANSAC + FPFH 特征,精配准用 ICP。ICP 对初值敏感,初值差太多会收敛到局部最优,所以通常先用特征做粗对齐。
坐标变换链要理清楚:像素 -> 相机坐标系 -> 相机外参 -> 机器人基坐标系。每一段都是一次刚体变换,写成矩阵乘法的形式。这条链上任何一环标错,最后的结果就是"看起来差不多但就是抓不准"。我的经验是每一步都单独验证:相机内参用平面板验证,手眼标定用已知位置的标记点验证,别指望一步到位。
手眼标定的误差会随距离放大。如果工作距离是 1 米,标定误差 2 毫米,那么到 2 米处就可能变成 4 毫米以上。所以标定的时候,尽量在真实工作距离处采集数据。
6. 踩坑实录:那些让深度图直接废掉的场景
6.1 反光、透明与纯黑:三种"物理级"失效
这三种失效不是算法问题,是物理问题,任何后处理都救不回来。
镜面反光:光打在镜面上被镜面反射到别的方向去了,相机收不到回波,深度是空的。更麻烦的是,如果镜面反射恰好把别处的光反射进相机,还会测出一个完全错误但看起来合理的距离。我遇到过一面不锈钢板,深度图显示它比实际位置近 40 厘米,因为相机"看到"了反射的另一个物体。
透明材质:玻璃、亚克力这类材料,一部分光反射回来、一部分透过去。iToF 因为多径效应,会算出一个前后之间的中间值;dToF 运气好的话能通过多回波区分出前表面。实际处理中,玻璃区域通常直接标记为不可信。
纯黑吸光:黑色哑光表面吸收大部分入射光,回波极弱。结构光和 iToF 在黑色物体上有效像素比例可能掉到一半以下。解决方案一般是提高曝光时间(代价是帧率)或者叠加多帧,物理上没办法。
实用建议:如果你的场景里这三种材质占比超过 20%,提前准备好"无效区域"的兜底策略,比如用 RGB 做语义分割补充,或者干脆换用对材质不敏感的 dToF。
6.2 多机同开与红外互扰
这个坑我在一个多机器人项目里被狠狠教育过。三台同型号结构光相机在一个房间里同时工作,结果每台的深度图里都出现了规律的条纹噪声,某些区域测距直接偏了几十厘米。
原因是每台设备的散斑图案本质上是一样的,A 相机看到了 B 相机投的散斑,以为是自己的图案,就算出了错误的深度。改法有几种:硬件上做触发同步,让它们分时曝光(需要支持外触发);软件上错开投射图案的编码(需要厂商开放配置);实在不行就在物理上拉开角度,让每台设备的视野尽量不重叠。
iToF 之间也有类似问题,特别是调制频率接近的时候。选购多机方案前,务必确认设备支持的同步方式和可配置的频率档位,这个信息通常在技术手册的深处,要主动问。
6.3 带宽、线缆与供电引发的不稳定
"跑一会儿就掉帧"、"偶尔丢设备"、"换台电脑就好了"这三个症状,几乎都指向物理层。
USB 带宽是硬约束。以 640×480@30fps 为例,深度 uint16 是 640×480×2×30 ≈ 18.4 MB/s,加上彩色 640×480×3×30 ≈ 27.6 MB/s,加起来约 46 MB/s。USB 3.0 理论 5 Gbps 够用,但如果同一个控制器上挂了多个设备,或者线材质量不达标,就会开始丢包。解决办法是把设备分散到不同的 USB 控制器上(用lsusb -t看拓扑),或者降低分辨率。
线缆长度也是问题。USB 3.0 无源线超过 3 米信号质量就开始下降,超过 5 米基本不可靠。长距离要用有源延长线或者光纤延长。这个坑很阴,因为线短的时候一切正常,换成长线才出问题,很容易误判成软件 bug。
供电不足会导致设备反复重启。有些模组功耗不小,尤其是带主动投射的,用 USB Hub 供电可能不够。直接插主板后置 USB 口是最稳的。
6.4 温漂与"昨天还好好的"
深度相机对温度敏感,尤其是 ToF 和结构光,因为光源波长和传感器响应都会随温度变化。开机初期温度在爬升,测距值会缓慢漂移,通常 15 到 30 分钟后才趋于稳定。如果冷启动就标定,等设备热了之后误差可能到几毫米甚至更多。
我现在的做法是:任何精度相关的测试,都先让设备运行半小时;长期部署的场景,要么做温度补偿(周期性用已知距离的参考物修正),要么在标定时记录温度并留出余量。
另一个"昨天还好好的"的典型原因是机械松动。相机被撞了一下、支架螺丝松了、镜片上有指纹,都会导致精度突变。我一般会固定一个"健康检查"流程:每次开机后测一次固定位置的平面板,数值偏离超过阈值就报警。这个习惯帮我省了很多排查时间。
7. 典型落地场景与我见过的选型结论
7.1 机器人避障与 SLAM
机器人避障对深度的要求是"广度优先":视野要宽、帧率要高、近距离盲区要小,精度反而是次要的,厘米级就够用。这类场景我一般推荐广角 dToF 或者主动双目,理由是抗环境光好、功耗低、多机不打架。
SLAM 用深度相机的话,要注意深度图的噪声会直接影响位姿估计。稠密 SLAM 通常会把深度图降采样、滤波之后再用,或者干脆只取稀疏特征点再加深度做尺度恢复。纯用深度做里程计的话,边缘飞点和无效区域是主要误差源,前端的剔除外点策略要做足。
要注意的一点是,机器人在运动时对时域滤波很敏感。前面说过时域滤波会拖影,运动状态下反而会引入错误。有些方案会在检测到运动时自动降低滤波强度,这个逻辑要自己实现的话,用帧间深度差分做判断即可。
7.2 体积测量与物流
物流场景测包裹体积,核心诉求是精度和盲区。包裹尺寸从 10 厘米到 1 米不等,测量误差通常要求在 1% 到 2% 以内。这类应用我倾向选中等 FOV、短盲区、近距离高精度的方案,工作距离控制在 0.5 到 1.5 米。
实际做的时候有个细节:包裹表面往往是纸箱(漫反射,好测)和胶带(有反光,偶尔出洞)。孔洞处理不好会直接影响体积计算。我的做法是先做形态学闭运算填补小孔,再用凸包或者包围盒算法算体积,而不是直接对点云积分。直接积分的话,一个洞就能让体积偏小好几个百分点。
还要注意姿态。倾斜的包裹如果直接算轴对齐包围盒,体积会偏大,需要先做平面拟合把顶面找出来再算。
7.3 人机交互与手势
手势识别、人脸支付的场景通常工作距离短(0.3 到 1 米)、精度要求高、但对帧率要求没那么极致。结构光在这个区间性价比最高,近距离精度能到毫米级。
这里最大的坑是"手在动"。手部运动速度可以达到每秒一米以上,如果时域滤波开得太强,手部轮廓会糊成一团。另外手是多关节、非刚性的,深度图的遮挡区域(手指互相遮挡)会比较多,算法层面要能容忍这些空洞。
肤色对红外反射率有一定影响,深色皮肤的回波会弱一些,如果产品要面向多肤色用户,测试集一定要覆盖到,别等到上线才发现某些用户识别率明显下降。
7.4 工业检测
工业检测是深度相机最"卷"的场景,精度要求从毫米级到微米级都有。微米级的场合一般不用面阵深度相机,而是用线激光轮廓仪;毫米级的面检测可以用高精度结构光。
工业现场的两个额外挑战是环境光(车间照明、焊接弧光)和振动。照明方面,尽量用窄带滤光片把环境光滤掉,只让投射器的波长通过;振动方面,相机和被测物都要刚性固定,并且要考虑补偿。
温度这块在工业场景影响更明显,因为精度要求高。长时间运行的产线上,最好做周期性的在线校准,用一个已知尺寸的标准件做参考,自动修正漂移。
关于选型,我的整体建议是先明确工作距离和精度要求这两个硬约束,再在满足约束的方案里比抗环境光和多机能力,最后才看价格。顺序反了,很容易买了一台参数漂亮但用不了的设备。我个人在实际操作中的体会是,深度相机这个东西,参数表只能告诉你它能做什么,真正的边界要靠自己在目标场景里实测出来——带上黑色板、镜面片、透明片和卷尺,去现场跑一遍,比在办公室里看十份规格书都有用。