4. 相机内参标定
在Camera-LiDAR-IMU联合标定工程中,相机内参标定是所有外参标定的基础。如果内参不准确,即使外参求解算法再精细,最终融合结果也会出现系统性偏差。本节围绕针孔成像模型、畸变模型、张正友标定法、标定板选择以及实操流程展开,帮助读者建立从理论到落地的完整认知。
一、针孔模型:把三维世界映射到二维像素
针孔模型是相机成像的经典近似。它假设光线通过一个理想小孔,在像平面上形成倒立实像。在工程中,我们通常用四个内参描述这一过程:焦距 fx、fy,以及主点坐标 cx、cy。三维点 (X, Y, Z) 投影到像素坐标 (u, v) 的关系可写为:
u = fx * X / Z + cx
v = fy * Y / Z + cy
其中 fx = f / dx,fy = f / dy,dx、dy 分别为单个像素在 x、y 方向的物理尺寸。主点是光轴与像平面的交点,通常接近图像中心,但受装配误差影响会有偏移。针孔模型不考虑镜头畸变,因此只适用于理想小孔或畸变已校正后的图像。
二、畸变模型:径向畸变与切向畸变
真实镜头存在制造和装配误差,导致图像产生畸变。最常见的是径向畸变和切向畸变。径向畸变使直线变弯,表现为桶形或枕形;切向畸变由镜头与成像平面不平行引起,使图像产生切向偏移。OpenCV 常用五参数模型 (k1, k2, p1, p2, k3) 描述:
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 为归一化平面坐标。对于广角或鱼眼镜头,还需引入 k4、k5、k6 或使用鱼眼模型。畸变系数不准确会直接导致角点重投影误差增大,进而影响外参精度。
三、张正友标定法:从多视角棋盘格到内参求解
张正友标定法(Zhang's Method)是当前最广泛使用的相机内参标定方法。其核心思想是:使用一个已知尺寸的平面标定板,从多个不同姿态拍摄图像,利用平面单应性矩阵和旋转矩阵的正交约束,线性求解内参初值,再通过最大似然估计进行非线性优化。
具体步骤包括:
- 打印或制作标定板,固定在刚性平面上;
- 从不同角度、不同距离拍摄 15~30 张图像,覆盖画面各个区域;
- 检测每张图像中的角点坐标;
- 计算每张图像的单应性矩阵 H;
- 利用 H 的列向量正交约束求解内参矩阵 K;
- 以重投影误差最小为目标,优化 K 和畸变系数。
张正友标定法的优势在于:不需要高精度三维标定物,只需平面标定板;操作简单,精度可满足大多数工程需求。在联合标定中,通常先单独标定相机内参,再固定内参进行外参求解。
四、标定板选择:棋盘格、圆点与 ChArUco
标定板的选择直接影响角点检测的稳定性和标定精度。常见类型包括:
| 标定板类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 棋盘格 | 角点锐利、检测成熟、成本低 | 对模糊和遮挡敏感 | 常规镜头、实验室标定 |
| 圆点阵列 | 圆心定位抗噪、对模糊不敏感 | 圆心提取存在偏心误差 | 工业视觉、远距离标定 |
| ChArUco | 结合棋盘格与ArUco,部分遮挡仍可检测 | 制作稍复杂 | 多相机、遮挡场景、联合标定 |
| 鱼眼专用板 | 覆盖大视场,适配鱼眼模型 | 需专用检测算法 | 鱼眼镜头、广角相机 |
在Camera-LiDAR-IMU联合标定中,若相机为普通针孔镜头,棋盘格已足够;若存在部分遮挡或多相机同步采集,ChArUco 更具鲁棒性。标定板尺寸应占画面 1/3 以上,方格边长建议 20~50 mm,根据工作距离选择。
五、实操流程与常见误区
以下给出基于 OpenCV 的典型标定流程:
# 1. 准备标定板参数
pattern_size = (9, 6) # 内角点数量
square_size = 0.025 # 方格边长,单位:米
# 2. 采集图像并检测角点
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
corners = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
# 3. 标定
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
object_points, image_points, gray.shape[::-1], None, None)
# 4. 评价重投影误差
mean_error = 0
for i in range(len(object_points)):
imgpoints2, _ = cv2.projectPoints(object_points[i], rvecs[i], tvecs[i], K, dist)
error = cv2.norm(image_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
mean_error += error
print("重投影误差:", mean_error / len(object_points))
复盘时建议:固定光圈与焦距,使用全局快门相机;采集时让标定板覆盖画面四角与中心;标定完成后用独立图像验证重投影误差;若误差偏大,优先检查角点检测质量,再考虑增加图像数量或更换标定板类型。只有内参稳定可靠,后续 Camera-LiDAR-IMU 联合标定才能获得可信的外参结果。