张正友相机标定:内参、畸变与重投影误差
同一块标尺放在画面中心和边缘,换算出来的长度不同;再把标尺倾斜,误差继续增大。单一 mm_per_px 只能在受限拍摄条件下换算距离,不能解释镜头畸变和透视。相机标定要先建立从三维点到像素的模型。
本文介绍张正友平面标定法及 OpenCV 实践,属于 ScrewMetrology 系列的进阶知识。项目当前的两点尺度模块没有实现相机标定或去畸变。返回系列导读。
API 说明以 OpenCV 4.13 为参考;配套 C++17 示例已在 AppleClang 21 / OpenCV 5.0.0 下验证。OpenCV 5 使用 calib 和 geometry,OpenCV 4 使用 calib3d;4.x 分支在本文环境中未实测。
1. 内参、外参和尺度分别是什么
忽略畸变时,针孔模型写为:
1 | λ [u v 1]ᵀ = K [R | t] [X Y Z 1]ᵀ |
K 把相机归一化坐标映射到像素,fx、fy 的单位是像素,cx、cy 是主点。R、t 把标定板坐标转换为相机坐标;t 的单位由标定板三维点的单位决定。λ 是投影尺度,不是全图共用的毫米/像素比例。
张正友方法的一般内参模型可以包含 skew;本文与 OpenCV 常用模型按零 skew 解释。内参描述固定镜头和成像设置,每张标定图却有自己的外参。solvePnP 返回的也是物体到相机的变换,不是相机在物体坐标系中的位姿;后者需要求逆。
2. 平面为什么仍能提供内参约束
把棋盘格放在 Z=0 的平面,模型变为:
1 | λ [u v 1]ᵀ = K [r₁ r₂ t] [X Y 1]ᵀ |
每张图提供一个平面单应矩阵 H。H 只有比例意义,但旋转矩阵的前两列单位长度且正交,消除了部分自由度。令 B=K⁻ᵀK⁻¹,有:
1 | h₁ᵀ B h₂ = 0 |
B 对称,六个独立元素组成向量 b。把上述两式展开为关于 b 的线性方程,每个视角贡献两条约束;堆叠得到 Vb=0,通过 SVD 求取零空间,再恢复 K。
一般内参模型需要至少三个非退化视角才能提供足够约束,实际应采集更多覆盖视野、倾斜和距离的图像。重复拍摄相同正视姿态不会因为图片数量多就自动增加可观测性。
得到 K 后,可以由 K⁻¹h₁、K⁻¹h₂ 恢复旋转列、从第三列恢复平移,构造第三个旋转列并投影到合法旋转矩阵。最后结合畸变参数,最小化所有角点的重投影误差。线性解负责初始化,非线性优化负责进一步拟合。
3. 畸变模型与去畸变的作用
常用模型包含径向 k₁、k₂、k₃ 和切向 p₁、p₂。对归一化坐标 (x,y),r²=x²+y²,径向项随距光轴的距离变化,切向项描述另一类偏移。
1 | x_d = x(1+k₁r²+k₂r⁴+k₃r⁶) + 2p₁xy + p₂(r²+2x²) |
undistort 根据模型重采样图像;undistortPoints 校正点坐标,默认输出归一化坐标,若需要像素坐标需明确指定投影矩阵。处理后的图像可能使用不同的新内参和 ROI,后续测量必须使用一致的坐标定义。
去畸变不会自动把任意像素变成毫米。若工件处在已知测量平面,还需要平面姿态或单应映射;存在高度变化时,单个平面映射也不成立。
4. 实际棋盘格采集流程
棋盘参数使用内角点数量,不是格子数量。例如 9×6 内角点,每格边长 25 mm,则对象点可按行写为 (j*25,i*25,0)。图像点与对象点顺序必须一致。
1 | // 嵌入实际采集程序的核心片段;gray 已检查为非空灰度图。 |
所有图像需要一致的分辨率和固定焦距、对焦设置;避免大量模糊、过曝、几乎同姿态的图片。检查角点覆盖画面边缘和中心,观察不同倾角,保证棋盘格本身平整且尺寸可信。
检测失败的图像不能只跳过图像点却仍然添加对象点。可先使用 findChessboardCornersSB 等替代检测器比较角点质量,但仍要检查其角点排序和接口约定。
5. 可运行的合成标定实验
为了在没有相机照片时验证数据流,配套程序用已知 K、畸变和 15 个不同姿态,投影 9×6 平面角点,再通过 calibrateCamera 恢复参数。下载以下三个文件到同一目录:
1 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug |
若 CMake 无法找到安装,追加 -DOpenCV_DIR=你的OpenCV配置目录。配套 CMake 根据主版本选择模块。标定的核心调用如下,完整参数和数据生成见下载文件:
1 | // 核心片段:objectPoints/imagePoints 已按视角配对。 |
程序另用 projectPoints 计算恢复参数的 RMS,并检查 K 的相对误差。通过条件为重投影 RMS 小于 0.001 px、K 相对误差小于 0.001;这是无人工角点噪声的合成测试阈值,不是实物相机精度要求。
camera_calibration --degenerate 使用重复正视姿态,程序依据生成时已知的姿态多样性提前拒绝并返回 1。CTest 将这个非零退出视作预期。该检查用于证明合成退化路径,不能直接作为真实图像的通用退化检测器。
6. 重投影误差小,为什么仍要检查模型
定义每个角点误差为观测像素与投影像素的欧氏距离,整体 RMS 为:
1 | RMS = sqrt( sum ||observedᵢ − projectedᵢ||² / N_points ) |
同时查看逐图 RMS、误差分布、边缘覆盖以及内参是否合理。少量视角或复杂畸变模型可能过拟合;标定板翘曲、尺寸填写错误、角点排序错误也可能给出貌似可用的结果。
验证时保留独立拍摄图像,进一步检查去畸变后直线和平面尺寸的一致性。若改变分辨率,需要按图像变换调整内参;如果改变焦距或对焦导致模型变化,应重新标定。
7. 与 AX=XB 的衔接
相机标定建立三维标定板与图像的关系,给 solvePnP 提供 K 和畸变;每个机器人姿态下,PnP 再提供标定板到相机的变换。下一篇会把这些变换与机器人末端位姿配对,求相机与末端之间的固定关系。
张正友方法和 AX=XB 分别解决不同未知量。先明确坐标变换方向,才能把两条链正确连接。