4、相机模型与投影几何:针孔相机模型、畸变模型、双目视觉模型、单应矩阵与本质矩阵

各位做VIO的同行,咱们今天聊聊相机模型和投影几何。说实话,这部分内容看着像数学课本,但你要是搞不懂它,后面做标定、做初始化、做三角化,全都会踩坑。我当年刚入行时,就被畸变模型坑过一次——跑出来的轨迹像喝醉了酒,后来才发现是畸变参数给反了。

好,咱们一个一个来。

4.1 针孔相机模型:最朴素的成像原理

针孔相机模型,说白了就是小孔成像。你想想看,一个暗箱,前面戳个小孔,光线穿过小孔在后面的感光元件上成像。这就是最原始的相机模型。

数学上怎么描述?我们通常用四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。从3D世界点到2D像素点,经历三步变换:

  1. 世界坐标系 → 相机坐标系:刚体变换(旋转+平移)
  2. 相机坐标系 → 图像坐标系:透视投影(除以Z)
  3. 图像坐标系 → 像素坐标系:缩放+平移(内参)

写成矩阵形式就是:

s * [u, v, 1]^T = K * [R|t] * [X, Y, Z, 1]^T

其中K是内参矩阵:

K = [fx,  0, cx;
      0, fy, cy;
      0,  0,  1]

fx、fy是焦距(以像素为单位),cx、cy是主点坐标。嗯,这里要注意,fx和fy不一定相等,因为传感器像素可能不是正方形。

我的经验: 实际项目中,我习惯先做去畸变,再用针孔模型。这样处理起来逻辑清晰,不容易出错。有些库支持带畸变的投影,但我个人觉得分开处理更可控。

4.2 畸变模型:镜头不是完美的

现实中的镜头不是理想针孔。为了收集更多光线,镜头是凸透镜,这就带来了畸变。主要分两类:

  • 径向畸变:光线在透镜边缘弯曲更厉害,产生「桶形」或「枕形」畸变
  • 切向畸变:镜头和传感器不平行,产生「倾斜」效果

数学上,我们用多项式来建模:

x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2)
y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y

其中r^2 = x^2 + y^2,k1、k2、k3是径向畸变系数,p1、p2是切向畸变系数。

避坑指南: 我曾经在标定时用了错误的畸变模型阶数。对于广角镜头,只用k1、k2不够,必须加上k3。否则图像边缘的特征点匹配会出大问题,VIO初始化直接失败。

实际工程中,我们通常用OpenCV的cv::fisheyecv::calibrateCamera来做标定。标定板建议用棋盘格或AprilGrid,我个人更推荐AprilGrid——角点检测更鲁棒,尤其在有运动模糊的场景下。

4.3 双目视觉模型:用两个眼睛看世界

单目相机丢掉了深度信息。双目相机通过两个视角的视差,恢复出深度。说白了,就是模仿人的两只眼睛。

双目模型的核心是对极几何。左右两个相机观察同一个3D点,在左右图像上形成两个投影点。这两个点满足:

x2^T * F * x1 = 0

其中F是基础矩阵。如果两个相机已经标定好(内参已知),我们可以用本质矩阵E:

x2^T * E * x1 = 0

深度计算公式(理想平行双目):

Z = f * b / d

其中f是焦距,b是基线长度,d是视差(左右图像对应点的横坐标差)。

关键点: 视差d越大,深度Z越小(物体越近)。视差d越小,深度Z越大(物体越远)。当d=0时,深度无穷大。所以双目对近处物体精度高,远处物体精度差。

我在项目中遇到过一个问题:双目相机的基线太短(比如手机上的双目),导致远距离的深度估计噪声极大。后来我们换了个基线更长的相机模组,效果立竿见影。

4.4 单应矩阵与本质矩阵:两视图几何的核心

这两个矩阵是VIO初始化阶段的关键工具。它们描述了不同视角下同一场景的几何关系。

4.4.1 本质矩阵 E

本质矩阵描述了两个已标定相机之间的对极几何。它包含了旋转R和平移t的信息:

E = [t]× * R

其中[t]×是t的反对称矩阵。E有5个自由度(3个旋转 + 3个平移,但尺度不可知)。

求解E通常用五点法或八点法。我习惯用五点法,因为它对噪声更鲁棒,需要的匹配点更少。

从E中恢复R和t,需要对E做SVD分解:

E = U * Σ * V^T
R = U * R_z(±π/2) * V^T
t = U * (0, 0, 1)^T

这里有四种可能的解,只有一种能使得3D点在两个相机前方。嗯,这个「选对解」的步骤,我当年写代码时忘了做,结果初始化出来的位姿全是反的。

4.4.2 单应矩阵 H

单应矩阵描述了两个平面之间的投影变换。当场景近似在一个平面上时(比如地面、墙面),用单应矩阵更合适。

x2 = H * x1

H有8个自由度(尺度不可知)。求解H至少需要4对匹配点。

单应矩阵在VIO中有什么用?

  • 纯旋转检测:如果相机只有旋转没有平移,本质矩阵退化,但单应矩阵仍然有效
  • 平面场景初始化:比如无人机起飞时对着地面,用单应矩阵做初始化更稳定
  • 视觉里程计中的运动估计:当特征点共面时,用H比用E更准
我的建议: 在实际VIO系统中,我通常会同时计算E和H,然后根据场景选择。如果匹配点的视差分布均匀,用E;如果视差小或者场景近似平面,用H。这叫「混合初始化」,很多开源系统(比如VINS-Mono)就是这么干的。

4.5 本章知识体系

下面这张图总结了相机模型与投影几何的核心逻辑:

相机模型与投影几何知识体系 相机模型 针孔相机模型 畸变模型 双目视觉模型 世界→相机→图像→像素 内参矩阵 K 外参 [R|t] 径向畸变 k1,k2,k3 切向畸变 p1,p2 去畸变 → 校正图像 对极几何 视差 → 深度 Z = f*b/d 基础矩阵 F / 本质矩阵 E 两视图几何核心 本质矩阵 E(一般场景) vs 单应矩阵 H(平面场景) VIO初始化 → 运动恢复结构 → 位姿估计

4.6 工程实践中的注意事项

知识点 常见坑 我的建议
针孔模型 内参标定不准,尤其是主点偏移 用多张不同角度的标定图像,覆盖整个视场
畸变模型 广角镜头只用k1,k2不够 至少用k1,k2,k3,必要时用fisheye模型
双目模型 基线太短,深度噪声大 根据应用场景选择基线,室内10cm以上,室外20cm以上
本质矩阵 纯旋转时退化 检测视差是否足够,不够时用单应矩阵
单应矩阵 非平面场景下精度差 先做平面检测,再决定用E还是H

好了,相机模型和投影几何就聊到这儿。这些内容看起来基础,但每一个细节都直接影响VIO系统的精度和稳定性。我建议你动手写代码实现一遍标定和去畸变,踩过坑才能真正理解。

一句话总结: 针孔模型是基础,畸变模型是修正,双目模型是深度,本质矩阵和单应矩阵是两视图几何的「翻译官」——把2D匹配翻译成3D运动。

公众号:蓝海资料掘金营,微信deep3321