第二章 相机模型与标定:从针孔模型到畸变校正

做自动驾驶的朋友都知道,相机是感知系统的「眼睛」。但你真的了解这双眼睛的成像原理吗?说实话,我刚入行时也以为相机模型就是个简单的投影公式,直到在实际项目中吃了亏——标定参数差一点点,车道线检测就偏出去几十厘米。嗯,今天我们就来彻底搞懂相机模型与标定。

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

先说说最基础的针孔模型。你想想看,小孔成像的原理其实特别简单——光线通过一个小孔,在后面的平面上形成倒立的像。相机本质上就是把这个过程数字化了。

数学上怎么描述?我们用三个坐标系来搞定:世界坐标系、相机坐标系、图像坐标系。我个人习惯把世界坐标系想象成「真实世界的坐标」,相机坐标系是「以相机为中心看世界」,图像坐标系就是「最终拍到的照片」。它们之间的转换关系,说白了就是一系列矩阵乘法。

核心公式:

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

其中 K 是内参矩阵,[R|t] 是外参矩阵。这个公式贯穿了整个视觉SLAM,一定要刻在脑子里。

2.2 内参与外参:相机的「基因」和「姿态」

内参矩阵 K 长这样:

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

fx、fy 是焦距(以像素为单位),cx、cy 是主点坐标。我在项目中遇到过一个问题:用手机拍的视频做SLAM,结果发现主点偏移特别大。后来一查,原来是手机相机自带的防抖裁剪导致的。所以啊,标定前一定要确认原始图像没有被裁剪过。

外参就更好理解了——相机在世界坐标系中的位置和朝向。旋转矩阵 R 和平移向量 t 共同描述了「相机在哪、看哪」。做自动驾驶时,外参标定尤其重要,因为我们需要知道相机相对于车体的精确位置。

参数类型 含义 维度 标定方式
内参 焦距、主点、畸变系数 4~8个参数 离线标定(棋盘格)
外参 旋转、平移 6个自由度 离线/在线标定

2.3 畸变模型:为什么直线会变弯?

实际相机不是完美的针孔。镜头有曲率,光线经过时会发生折射。这就导致了畸变。最常见的两种:

  • 径向畸变:镜头形状引起的,越靠近边缘越明显。桶形畸变和枕形畸变就是典型的径向畸变。
  • 切向畸变:镜头和成像平面不平行导致的。这个在量产车上比较常见,因为装配误差很难完全消除。

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

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

k1、k2、k3 是径向畸变系数,p1、p2 是切向畸变系数。我曾经在标定一个广角相机时,发现只用 k1、k2 根本不够,边缘还是弯的。后来加了 k3 才搞定。所以建议你:如果镜头视角超过 120°,一定要用 3 个径向畸变参数。

2.4 标定流程:从棋盘格到参数求解

标定说白了就是「已知 3D 点,找到 2D 投影,反推相机参数」。最经典的方法就是张正友标定法。流程如下:

  1. 打印一张棋盘格,贴在平面上
  2. 从不同角度拍摄 15~20 张照片
  3. 检测每张照片中的角点
  4. 用最小二乘法求解内参和畸变系数
  5. 用非线性优化做精炼

我的经验:拍摄时注意三点——棋盘格要占画面 1/3 以上;角度变化要大(俯仰、偏航、滚转都要有);光照要均匀。我曾经因为偷懒只拍了 8 张,结果标定出来的焦距偏差 5%,导致后续建图直接飘了。

2.5 畸变校正:让图像「变直」

拿到畸变系数后,怎么校正图像?有两种思路:

  • 前向映射:遍历原图每个像素,计算它在校正图中的位置。缺点是校正图可能出现空洞。
  • 后向映射:遍历校正图每个像素,反查它在原图中的位置。这是实际工程中常用的方法。

代码实现其实不复杂:

// 伪代码:后向映射畸变校正
for each pixel (u, v) in corrected_image:
    // 1. 将像素坐标转为归一化坐标
    x = (u - cx) / fx
    y = (v - cy) / fy
    
    // 2. 计算畸变后的坐标
    r^2 = x^2 + y^2
    x_dist = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2)
    y_dist = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y
    
    // 3. 转回像素坐标
    u_dist = fx * x_dist + cx
    v_dist = fy * y_dist + cy
    
    // 4. 双线性插值取像素值
    corrected_image[u][v] = interpolate(original_image, u_dist, v_dist)

注意:畸变校正后的图像边缘会被裁剪掉一部分。如果你需要保留完整的视场角,可以考虑用鱼眼相机模型,或者在校正时适当放大输出图像尺寸。

2.6 知识体系总览

下面这张图总结了本章的核心逻辑。从针孔模型出发,到畸变建模,再到标定和校正,每一步都是环环相扣的。

相机模型与标定知识体系 针孔相机模型 畸变模型 相机标定 内参矩阵 K 外参 [R|t] 径向畸变 (k1,k2,k3) 切向畸变 (p1,p2) 张正友标定法 畸变校正 核心目标:建立 3D 世界点到 2D 像素点的精确映射 应用场景:视觉SLAM、3D重建、自动驾驶感知 关键工具:OpenCV、Kalibr、MATLAB Camera Calibrator

2.7 工程中的坑与建议

最后分享几个我踩过的坑:

  • 标定板不平:我曾经用亚克力板贴棋盘格,结果板子本身有微小弯曲,导致标定误差。后来改用陶瓷板,问题解决。
  • 温度影响:相机长时间工作会发热,镜头热胀冷缩会导致内参漂移。做量产项目时,建议在不同温度下标定多组参数。
  • 鱼眼 vs 针孔:视场角超过 120° 就别用针孔模型了,老老实实用鱼眼模型(如 Kannala-Brandt 模型)。

实用工具推荐:我个人最常用的是 OpenCV 的 cv::calibrateCamera() 和 Kalibr 工具箱。Kalibr 支持多相机联合标定,做自动驾驶的多目系统时特别好用。

好了,相机模型和标定就讲到这里。记住一句话:标定是视觉SLAM的基石,参数不准,后面全是白搭。下一章我们会聊聊特征点提取与匹配,到时候你会感谢今天认真标定的自己。

专注资料整理