第一章:相机模型与坐标系

做视觉SLAM的第一步是什么?

我个人觉得,不是跑算法,不是调参数,而是先把相机怎么成像这件事搞明白。你想想看,我们所有的输入都来自相机,如果连相机模型都理解不透,后面做特征匹配、位姿估计,那都是空中楼阁。

这一章,我们就来聊聊针孔相机模型、内参矩阵、畸变模型,还有那几个绕来绕去的坐标系。嗯,内容不算多,但都是基本功。

1.1 针孔相机模型

针孔相机模型,说白了就是初中物理里的小孔成像。光线穿过一个小孔,在后面的成像平面上形成倒立的像。这个模型虽然简单,但它是所有现代相机模型的基础。

实际相机当然不是真的用针孔,而是用透镜。但数学上,我们仍然用针孔模型来近似。为什么呢?因为它的几何关系特别清晰——所有光线都通过一个点,这个点叫光心。

我在项目中遇到过一件事:有同事直接用针孔模型标定鱼眼相机,结果误差大得离谱。后来才发现,鱼眼相机的视场角太大,针孔模型根本hold不住。所以啊,选模型之前,先搞清楚你的相机是什么类型。

核心公式:

假设空间点 P = [X, Y, Z] 在相机坐标系下,投影到成像平面上的点 p = [x, y]:

x = f * X / Z
y = f * Y / Z

其中 f 是焦距。注意,这里成像平面在光心后方,所以像是倒立的。实际处理时,我们通常把成像平面虚拟到光心前方,这样像就是正立的,公式不变。

1.2 内参矩阵

刚才的公式是理想情况。但实际相机里,像素坐标和物理坐标之间还有一层转换。这层转换,就是内参矩阵 K。

内参矩阵长这样:

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

其中:

  • fx, fy:分别是x和y方向的焦距(单位是像素)
  • cx, cy:主点坐标,也就是光轴与成像平面的交点

为什么会有fx和fy?因为像素通常不是正方形,而且传感器可能还有倾斜。不过现在大多数相机的fx和fy很接近,倾斜因子也近似为0。

内参矩阵的作用,就是把相机坐标系下的三维点,投影到像素坐标系。完整的投影公式:

u = fx * X / Z + cx
v = fy * Y / Z + cy

写成矩阵形式:

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

这里的 s 是尺度因子,其实就是 Z 值。

我的经验:标定内参时,一定要用棋盘格拍至少20张不同角度的照片。我见过有人只拍5张就标定,结果重投影误差大得没法用。另外,标定板的精度也很重要,淘宝上几块钱的打印棋盘格,精度真的不行。

1.3 畸变模型

实际相机用的不是针孔,而是透镜。透镜会带来畸变。最常见的两种:

  • 径向畸变:光线经过透镜边缘时弯曲得更厉害。表现为图像边缘的直线变弯。分为桶形畸变和枕形畸变。
  • 切向畸变:透镜和成像平面不平行导致的。表现为图像看起来像被拉伸了。

畸变模型通常用多项式来拟合。径向畸变用 k1, k2, k3 三个参数,切向畸变用 p1, p2 两个参数。

矫正公式:

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,x, y 是归一化平面上的坐标。

注意:畸变参数是跟内参一起标定出来的。标定完成后,一定要做畸变矫正,否则后续的特征匹配和三角化都会出问题。我曾经在一个项目中忘了做畸变矫正,结果特征点匹配的误差直接导致位姿估计发散,排查了整整两天才找到原因。

1.4 坐标系转换

做视觉SLAM,你会在四个坐标系之间来回切换。搞不清楚它们的关系,代码写出来就是一团浆糊。

四个坐标系:

  1. 世界坐标系:我们定义的一个固定坐标系。通常以第一帧相机位置为原点,或者以某个已知物体为参考。
  2. 相机坐标系:以相机光心为原点,Z轴指向相机前方,X轴向右,Y轴向下(注意,这个跟OpenCV的惯例一致)。
  3. 图像坐标系:在成像平面上,以物理单位(毫米)度量。原点通常在图像中心。
  4. 像素坐标系:以像素为单位,原点在图像左上角,u轴向右,v轴向下。

它们之间的转换关系:

转换 公式 说明
世界 → 相机 P_cam = R * P_world + t R是旋转矩阵,t是平移向量
相机 → 图像 x = X/Z, y = Y/Z 归一化平面坐标
图像 → 像素 u = fx*x + cx, v = fy*y + cy 内参矩阵作用

你想想看,整个流程其实就是:世界坐标系下的一个点,先通过外参(R, t)转到相机坐标系,再通过内参投影到像素坐标系。反过来,从像素坐标恢复三维信息,就需要深度值了。

1.5 知识体系总览

下面这张图,把这一章的核心逻辑串起来了。我建议你多看几遍,把每个环节的输入输出搞清楚。

世界坐标系 外参(R,t) 相机坐标系 投影 图像坐标系 内参K 像素坐标系 坐标系转换流程 畸变矫正环节 注:畸变矫正通常在投影之前或之后进行

嗯,这张图把整个流程串起来了。从世界坐标系到像素坐标系,中间经过外参、投影、内参三个环节。畸变矫正可以放在投影之后,也可以放在投影之前,取决于你的实现方式。

1.6 实战中的注意事项

最后,分享几个我在实际项目中踩过的坑:

  • 坐标系方向:OpenCV和OpenGL的坐标系方向不一样。OpenCV是右手系,Z轴向前;OpenGL是左手系,Z轴向里。混用的时候一定要小心。
  • 像素坐标原点:图像坐标系原点在中心,像素坐标系原点在左上角。转换时别忘了加偏移。
  • 畸变参数顺序:不同库的畸变参数顺序可能不同。OpenCV是[k1, k2, p1, p2, k3],有些库是[k1, k2, k3, p1, p2]。标定完一定要确认。
  • 浮点精度:内参矩阵里的fx, fy通常很大(几百到几千),计算时尽量用double,避免精度损失。

我的建议:刚开始做视觉SLAM的同学,可以先用OpenCV的calibrateCamera函数走一遍标定流程。把内参和畸变参数打印出来,手动算几个点的投影,看看重投影误差。这样能帮你建立直观理解。

好了,这一章的内容就到这里。相机模型和坐标系转换是视觉SLAM的地基,一定要打牢。下一章我们会聊特征点提取与匹配,到时候这些坐标系知识会反复用到。

专注资料整理