4. 相机标定原理:张正友标定法
相机标定,说白了就是给相机做一次「视力矫正」。
你想想看,相机镜头不是完美的。光线穿过透镜会发生折射,传感器也不一定完全平行。这些偏差会导致图像变形——直线变弯、矩形变鼓。做手眼标定之前,必须先把相机本身的「脾气」摸清楚。
我个人习惯把标定分成两步:先算内参(焦距、主点),再算畸变系数(径向、切向)。张正友标定法是目前最主流的做法,用棋盘格就能搞定。
4.1 棋盘格标定板
棋盘格长什么样?就是黑白相间的方格阵列。我刚开始做项目时,直接打印一张A4纸贴在硬纸板上。后来发现,打印精度和平面度都会影响结果。
棋盘格的关键参数:
- 内角点数:比如 9×7 的棋盘,内角点是 8×6。注意是「内角」,不是格子数。
- 方格尺寸:必须精确测量,单位毫米。我一般用游标卡尺量三次取平均。
- 拍摄姿态:至少 10-20 张不同角度、不同距离的图片。倾斜、旋转、平移都要有。
4.2 单应性矩阵
张正友标定法的核心,就是利用棋盘格平面到成像平面的单应性关系。
什么是单应性矩阵?简单说,它描述了两个平面之间的投影变换。在标定场景里,一个是棋盘格所在的物理平面(世界坐标系 Z=0),另一个是相机成像平面。
数学上,单应性矩阵 H 是一个 3×3 的矩阵:
s * [u, v, 1]^T = H * [X, Y, 1]^T
其中:
- (u, v) 是图像坐标
- (X, Y) 是棋盘格上的物理坐标
- s 是尺度因子
- H 包含内参和外参的混合信息
每张棋盘格图片,都能算出一个 H。我一般用 OpenCV 的 findHomography 函数,输入至少 4 个对应点就能解。但实际项目中,我会用所有内角点做最小二乘,精度更高。
4.3 内参求解
内参矩阵 K 长这样:
K = [fx, 0, cx;
0, fy, cy;
0, 0, 1]
其中 fx、fy 是焦距(像素单位),cx、cy 是主点坐标(光轴与成像平面的交点)。
张正友的解法很巧妙。他从单应性矩阵 H 中提取约束方程,利用旋转矩阵的正交性,构建线性方程组。具体步骤:
- 对每张图片计算 H
- 从 H 中提取关于内参的约束
- 构建超定方程组,用最小二乘求解
- 得到内参的闭式解
我记得第一次手动推导这个流程时,被矩阵运算绕晕了。后来直接用 OpenCV 的 calibrateCamera,一行代码搞定。但理解原理很重要——遇到标定失败时,你知道该查哪里。
| 参数 | 含义 | 典型值(640×480) |
|---|---|---|
| fx | x 方向焦距 | ~500-600 |
| fy | y 方向焦距 | ~500-600 |
| cx | 主点 x 坐标 | ~320 |
| cy | 主点 y 坐标 | ~240 |
4.4 畸变系数求解
畸变主要分两类:
- 径向畸变:光线通过透镜边缘时弯曲更严重,导致图像边缘向内(桶形)或向外(枕形)扭曲
- 切向畸变:透镜和传感器不平行,导致图像倾斜
张正友标定法用多项式模型来拟合畸变:
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 是切向畸变系数
- r^2 = x^2 + y^2
求解过程是这样的:先用线性方法算出内参,然后固定内参,用非线性优化(比如 Levenberg-Marquardt)迭代求解畸变系数。
标定完成后,可以用 undistort 函数矫正图像。我习惯先看一眼矫正前后的对比图:棋盘格边缘的直线应该变直,否则标定参数有问题。
4.5 标定质量评估
标定完了,怎么知道结果好不好?
我最常用的指标是重投影误差。就是把棋盘格上的物理点,用标定出的内参和畸变系数重新投影到图像上,算算和实际检测到的角点差多少像素。
影响标定精度的常见因素:
- 图片数量太少(< 10 张)
- 棋盘格姿态变化不够(全是正面)
- 棋盘格平面度差(纸板弯曲)
- 光照不均匀导致角点检测偏移
嗯,标定这件事,看起来简单,做起来全是细节。我刚开始做的时候,标定了十几次才拿到满意的结果。后来总结出一套流程:先拍 20 张,跑一次标定,看重投影误差,剔除误差最大的几张,再跑一次。反复两三轮,基本就能稳定在 0.3 像素以内。