4、张正友标定法(上):棋盘格角点检测、单应性矩阵求解内参的闭环推导

张正友标定法,说白了就是「用一张棋盘格照片,把相机内参和外参一起算出来」。这个方法在工业界和学术界都极其常用,我当年刚入行时,第一个上手的标定工具就是基于这个原理。今天我们先讲上半部分:棋盘格角点怎么检测,以及单应性矩阵如何帮我们反推出内参。

4.1 棋盘格角点检测:从图像到亚像素坐标

角点检测是整个标定流程的第一步,也是最容易出坑的一步。你想想看,如果角点坐标都歪了,后面算出来的内参肯定不准。

4.1.1 为什么用棋盘格?

棋盘格有几个天然优势:

  • 角点特征明显:黑白交替,梯度变化剧烈,容易定位
  • 结构规则:角点间距已知,方便建立世界坐标系
  • 抗干扰能力强:比圆点标定板更鲁棒

我个人习惯用 9×6 或 7×5 的内角点棋盘格,太大太小都不好。太大容易拍不全,太小角点数量不够,解算不稳定。

4.1.2 检测流程

  1. 图像预处理:灰度化、直方图均衡化,增强对比度
  2. 粗定位:用 Harris 或 Shi-Tomasi 角点检测器找到候选角点
  3. 亚像素精化:在粗定位周围做二次曲面拟合,得到亚像素级坐标
  4. 排序与编号:按棋盘格结构给每个角点分配 (u, v) 索引
我的经验:亚像素精化这一步,OpenCV 的 cornerSubPix() 函数默认迭代次数可能不够。我一般设成 30 次,终止精度 0.001 像素,效果会好很多。

4.1.3 亚像素精化原理

粗定位得到的角点坐标是整数像素,精度不够。亚像素精化的核心思想是:在角点附近,梯度方向与角点到邻域点的向量垂直。用数学表达就是:

对于角点 q 的邻域点 p_i:
  ∇I(p_i) · (q - p_i) = 0

其中 ∇I(p_i) 是 p_i 处的图像梯度
q 是待求的亚像素角点坐标

通过最小二乘法求解这个方程组,就能得到亚像素级的角点坐标。我做过实验,亚像素精化后,角点重复性可以从 0.5 像素提升到 0.05 像素左右。

4.2 单应性矩阵:连接世界与图像的桥梁

有了角点坐标,下一步就是建立世界坐标系和图像坐标系之间的映射关系。这个映射就是单应性矩阵 H。

4.2.1 单应性矩阵的物理意义

单应性矩阵描述的是两个平面之间的投影变换。在张正友标定法中,棋盘格平面(世界坐标系 Z=0)到图像平面的映射就是一个单应性变换:

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

其中:
  (u, v) 是图像坐标
  (X, Y) 是世界坐标(Z=0)
  s 是尺度因子
  H 是 3×3 的单应性矩阵
关键点:单应性矩阵有 8 个自由度(去掉一个尺度因子),所以理论上只需要 4 对匹配点就能求解。但实际中我建议用 10 对以上,用 RANSAC 剔除误匹配,结果更稳定。

4.2.2 单应性矩阵与内参的关系

单应性矩阵 H 可以分解为内参矩阵 K 和外参(旋转 R、平移 t)的乘积:

H = K * [r1, r2, t]

其中:
  r1, r2 是旋转矩阵的前两列
  t 是平移向量
  K 是内参矩阵

这个分解关系是张正友标定法的核心。通过多张不同角度的棋盘格图像,我们可以利用旋转矩阵的正交约束来求解内参。

4.3 内参求解的闭环推导

这部分是数学推导的核心,我尽量讲得直观一些。

4.3.1 约束条件

旋转矩阵 R 的列向量 r1 和 r2 是正交的,且模长为 1。所以:

r1^T * r2 = 0
||r1|| = ||r2|| = 1

把 H = [h1, h2, h3] 代入,得到:

h1^T * K^{-T} * K^{-1} * h2 = 0
h1^T * K^{-T} * K^{-1} * h1 = h2^T * K^{-T} * K^{-1} * h2

4.3.2 构建线性方程组

令 B = K^{-T} * K^{-1},B 是一个对称矩阵,有 6 个独立元素。把 B 写成向量形式:

b = [B11, B12, B22, B13, B23, B33]^T

对于每一张棋盘格图像,我们可以得到两个约束方程:

v12^T * b = 0
(v11 - v22)^T * b = 0

其中 vij 是由单应性矩阵元素构成的向量。

注意:至少需要 3 张图像才能解出 b 的 6 个未知数。但实际中我建议拍 15-20 张不同角度的图像,覆盖视野的各个区域,这样解算结果更鲁棒。

4.3.3 从 B 矩阵反解内参

解出 b 后,通过 Cholesky 分解可以从 B 矩阵中恢复出内参矩阵 K:

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

其中:
  fx = sqrt(1 / B11)
  fy = sqrt(1 / B22)
  cx = -B13 * fx^2
  cy = -B23 * fy^2

这里有个细节:B 矩阵的符号问题。我刚开始做的时候,解出来的 fx 是负数,查了半天才发现是 B 矩阵的符号取反了。嗯,遇到这种情况,把 K 矩阵整体取反就行。

4.4 完整流程总结

我把整个流程整理成一张图,方便你理解:

张正友标定法(上)核心流程 采集棋盘格图像 角点检测与亚像素精化 计算单应性矩阵 H 构建约束方程组 求解 B 矩阵 Cholesky 分解求内参 K 输出内参矩阵 ⚠️ 关键约束: 旋转矩阵正交性 至少 3 张图像 💡 建议: 15-20 张不同角度 覆盖视野各区域

4.5 避坑指南

这部分是我这些年踩过的坑,分享给你:

常见问题 原因 解决方案
角点检测失败 光照不均、棋盘格反光 使用漫反射标定板,避免强光直射
内参结果不稳定 图像数量太少或角度变化太小 至少 15 张,角度覆盖 30°-60°
fx 和 fy 差异过大 图像分辨率或镜头畸变严重 先做畸变校正,再重新标定
重投影误差偏大 角点坐标精度不够 检查亚像素精化参数,增加迭代次数
我的习惯:每次标定完,我都会用重投影误差来评估结果。如果平均误差超过 0.3 像素,我会重新采集数据。0.1 像素以内算优秀,0.2 像素以内可接受。