JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

恢复深度:立体视觉、点云与三维重建

两只眼睛(或一台移动的相机)如何找回平面照片丢掉的第三个维度,把图像变成可测量的三维形状。

照片丢掉的那个维度

相机把丰富的三维世界压扁成平面的二维图像。镜头前的每一个点都落在一个像素上,而 针孔相机模型 精确地告诉我们这是怎么发生的:一个三维点沿着穿过镜头的光线,投影到像平面上。但这种投影是一条单行道。一旦一个点变成了像素,相机就忘记了它有多远。你桌上的一只咖啡杯,和房间另一头一只一模一样的杯子,可能产生完全相同的像素——深度,正是平面照片悄悄丢弃的信息。

对机器人来说,这个缺失的数字就是一切。要抓住杯子、绕过桌子或避开墙壁,机器人需要的是真实单位的距离——米,而不是像素。深度感知的全部工作,就是*把第三个维度找回来*。让这一切成为可能的诀窍优雅而简单:从两个略有差异的视角观察同一个场景,再比较各自看到了什么。

立体视觉:两只眼睛,一个三角形

立体视觉 用两台相隔固定距离的相机来模仿那个眨眼的把戏,这段固定距离叫作*基线*。每台相机都从自己的视角拍下场景。由于视角不同,同一个真实世界中的点,在左图和右图中会落在不同的水平位置上。这种以像素为单位测量的左右位移,叫作视差——它正是 深度估计 的核心。

这种关系非常直接:视差大意味着近,视差小意味着远。正前方的一个点,在两个视角间位移很大;地平线上的一座山,则几乎不动。如果你知道基线和相机的焦距(来自 相机标定),一条简短的公式就能把视差直接换算成距离。两台相机和场景中的点构成一个三角形,我们解出它的深度——这就是为什么这叫作*三角测量*。

depth Z = (focal_length f  ×  baseline B) / disparity d

   big disparity d   ->  small Z  (object is near)
   small disparity d  ->  large Z  (object is far)
立体深度公式:一旦求出某个像素的视差 d,深度便随之而来。

难的部分是*匹配*:对于左图中的一个像素,右图中哪个像素才是同一个真实点?这和你用 特征描述子 匹配 图像关键点 时遇到的对应问题是同一个,但立体视觉有一个很大的捷径。标定之后,匹配像素总是位于另一幅图像中的同一条水平线上,于是搜索范围从整幅二维图像缩小到了一行。纯色的墙面和光亮的表面仍然会带来麻烦——没有纹理可供匹配,视差就只能靠猜。

从深度到点云

一旦每个像素都有了深度,我们就能把平面图像从屏幕上抬起来,送回到空间中去。取每个像素在图像中的位置,附上它测得的距离,再用标定好的相机模型,算出这个点在三维空间中真正所处的位置。对整幅图像都这样做,你就得到了一片 点云:成千上万乃至数百万个微小的 (x, y, z) 点,漂浮在机器人的坐标系里,每个点往往还带着颜色。它就是感知输出的原始、无结构的三维数据。

点云很强大,但也很杂乱。它没有物体或表面的概念——只有一堆点。于是机器人要做进一步的处理:把邻近的点聚成物体,拟合平面以找出桌面和地板,或者把从不同角度采集的多片点云缝合成一片。对齐两片有重叠的点云,正是 迭代最近点(ICP) 的工作,它不断微调一片点云,直到它的点尽可能贴合到另一片之上。

当你把许多视角融合成一个干净的单一表面时,就从原始点跨入了 三维重建 ——把点云变成一个不漏水的网格,或一个整洁的模型,可供你测量、打印,或放进仿真器。点云还为下游任务提供输入,例如 六自由度物体位姿估计:机器人把一个已知物体的形状拟合到这些点上,从而在抓取之前精确弄清它的位置和朝向。

一台移动的相机:运动恢复结构

其实你并不需要两台相机。一台*移动*的相机,随着时间从许多视角看到了场景——而第 1 帧和第 10 帧之间的间隔,作用就像一条立体基线。运动恢复结构(SfM)正是利用了这一点。它在一连串图像中追踪同样的特征,并根据这些特征如何在帧间滑动,一举解开一个了不起的双重谜题:场景的三维形状,*以及*相机为拍下它而走过的路径

  1. 在每一帧中检测特征,并跨帧匹配它们,追踪每个特征如何移动——这与 光流(图像间逐像素的运动)密切相关。
  2. 根据匹配到的运动,估计相机在各视角之间是如何移动的(即它不断变化的位姿)。
  3. 在两个或更多已知相机位姿之间,对每个被追踪的特征做三角测量,把它定位到三维空间,从而构建出一片稀疏点云。
  4. 把每一个相机位姿和每一个三维点放在一起联合优化(光束法平差),使整个重建结果保持一致。

如果逐帧实时地运行它,SfM 就成了机器人在一边给世界建图、一边推断自身位置时的视觉骨干。把相机和惯性传感器配在一起,你就得到了 视觉惯性里程计:来自视觉和来自惯性单元的运动估计互相印证,对机器人身在何处、周围一切位于何方,给出更稳定的估计。

深度会在哪里出错

恢复出来的深度从来都不是完美的,有两个陷阱反复出现。第一个是尺度不确定性。一台单独移动的相机,可以恢复出场景的*形状*和自身路径的*形状*——但恢复不出它们真实的大小。近距离拍摄的一列小火车模型,看起来和远距离拍摄的一列真火车一模一样。SfM 重建出的世界差着一个未知的尺度因子;没有外部帮助,它无法告诉你一道门是一米宽,还是一百米宽。

解决办法是喂进一个真实的测量值:来自立体装置的已知基线、能感知真实加速度的惯性传感器、轮式里程计,或者一个已知尺寸的标定物。这其中任何一个,都能把那个悬浮的重建钉到真实的米上。这正是为什么第二台相机或一个 IMU 物有所值——它锁定了单凭视觉无法确定的那个尺度。

第二个陷阱是深度噪声。在纯色墙面、玻璃、镜子和阴影处——这些没有东西可供匹配的地方——视差变得不可靠。远处的点尤其脆弱:在远距离上,一个极小的视差误差会变成一个巨大的距离误差,所以你看得越远,深度就越模糊。好的机器人把深度当作一个带有不确定性的测量值来对待,而不是金科玉律——它们对深度做滤波、与其他传感器融合,并且对近处的读数远比对远处的读数更信任。