4. 相机内参标定:HALCON相机标定流程、内参矩阵与畸变系数、标定结果评价
相机内参标定,说白了就是搞清楚你的相机到底长什么样。焦距多少?像素中心在哪?镜头畸变有多严重?这些参数如果不准,后面的手眼标定全是白搭。我见过太多项目,标定误差大得离谱,最后查来查去,根子就在内参没标好。
这一节,我们就把HALCON的标定流程掰开揉碎,讲清楚每一步该怎么做,以及怎么判断标定结果到底行不行。
4.1 HALCON标定流程:从拍照到出参
HALCON的标定流程其实不复杂,但细节很多。我个人习惯把流程分成四个阶段:准备、采集、计算、验证。
核心流程速览
- 准备标定板——生成描述文件,打印或购买高精度板
- 采集图像——不同位姿拍15-20张
- 提取角点——用find_caltab和find_marks_and_pose
- 计算内参——调用camera_calibration
- 验证结果——看重投影误差,做残差分析
嗯,这里要注意:标定板的质量直接决定标定上限。我曾经用打印的标定板做过实验,结果重投影误差死活下不去0.5像素。换成陶瓷基板的高精度标定板,同样的流程,误差直接降到0.1像素以内。
4.2 内参矩阵:相机内部的"坐标系"
内参矩阵,就是描述相机内部几何关系的3x3矩阵。HALCON里用campar这个数组来表示,包含以下参数:
| 参数 | 符号 | 含义 | 典型值范围 |
|---|---|---|---|
| 焦距 | f | 镜头焦距(像素单位) | 1000-5000 |
| 主点x | cx | 图像中心x坐标 | 图像宽度/2 |
| 主点y | cy | 图像中心y坐标 | 图像高度/2 |
| 像素宽高比 | sx | 像素的宽高比 | 1.0(方形像素) |
| 倾斜因子 | skew | 像素轴倾斜度 | 0.0(理想情况) |
你想想看,如果主点偏差了10个像素,那在图像边缘的测量误差会被放大多少倍?这就是为什么内参标定不能马虎。
HALCON里获取内参矩阵的代码很简单:
* 从标定结果中提取内参
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
* 查看内参
disp_message (WindowHandle, '焦距: ' + CamParam[0] + ' 像素', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle, '主点: (' + CamParam[1] + ', ' + CamParam[2] + ')', 'window', 36, 12, 'black', 'true')
4.3 畸变系数:镜头不是完美的
没有镜头是完美的。畸变主要分两类:径向畸变和切向畸变。HALCON支持多种畸变模型,最常用的是多项式模型和除法模型。
我的经验:对于普通工业镜头,用多项式模型(k1, k2, k3三个径向系数)就够了。鱼眼镜头才需要除法模型。别一上来就选高阶模型,参数多了反而容易过拟合。
畸变系数在HALCON的campar数组里排在后面:
- k1, k2, k3:径向畸变系数(k1主导桶形/枕形畸变)
- p1, p2:切向畸变系数(镜头与传感器不平行导致)
我曾经遇到过一个案例:标定出来的k1是负值,而且值很大。一开始以为是标定错了,后来检查发现——镜头装歪了。重新安装后,k1恢复正常。所以,畸变系数不只是数学参数,它还能帮你诊断硬件问题。
4.4 标定结果评价:别只看重投影误差
标定完了,怎么知道结果好不好?很多人只看重投影误差(RMS),觉得小于0.3像素就万事大吉。其实不然。
我个人习惯从三个维度评价标定结果:
- 重投影误差——每个角点的检测位置和投影位置的偏差
- 参数稳定性——换一组标定图像,参数变化大不大
- 物理合理性——焦距、主点、畸变系数是否在合理范围内
避坑指南:我曾经只盯着重投影误差看,标定结果RMS只有0.08像素,觉得完美了。结果换到实际场景,测量误差直接飙到0.5毫米。后来才发现,标定板只拍了中心区域,边缘畸变根本没标出来。所以,标定图像的位姿分布比RMS值更重要。
HALCON里查看重投影误差的方法:
* 获取所有标定图像的误差
get_calib_data_observ_contours (CalibDataID, 'caltab', 0, 'all', Contours)
get_calib_data_observ_points (CalibDataID, 'caltab', 0, 'all', Row, Column, Index, Pose)
* 计算每个角点的重投影误差
for I := 0 to |Row| - 1 by 1
project_3d_point (X[I], Y[I], Z[I], CamParam, Pose[I], RowProj, ColProj)
Error := sqrt((Row[I] - RowProj)^2 + (Column[I] - ColProj)^2)
* 记录误差
endfor
4.5 标定流程的SVG框架图
下面这张图把整个标定流程串起来了,你可以对照着看:
4.6 标定结果评价的量化指标
光说"好"或"不好"不够,得有数据说话。我整理了一个评价表格,你可以对标自查:
| 评价指标 | 优秀 | 合格 | 不合格 | 检查方法 |
|---|---|---|---|---|
| 重投影误差RMS | < 0.1 像素 | 0.1 - 0.3 像素 | > 0.3 像素 | get_calib_data |
| 最大单点误差 | < 0.3 像素 | 0.3 - 0.8 像素 | > 0.8 像素 | 逐点计算 |
| 焦距稳定性 | 变化 < 1% | 变化 1% - 3% | 变化 > 3% | 多次标定对比 |
| 主点偏移 | < 5 像素 | 5 - 15 像素 | > 15 像素 | 与图像中心对比 |
| 畸变系数k1 | -0.1 ~ 0.1 | -0.3 ~ 0.3 | 超出范围 | 查看campar[5] |
小技巧:标定完成后,我习惯把标定板放在视场中心拍一张,然后用rectify_image做畸变校正。如果校正后的标定板边缘还是弯的,说明畸变模型没选对,或者标定图像不够覆盖边缘区域。
最后说一句:内参标定不是一次性工作。镜头松动、温度变化、焦距调整,都会导致内参漂移。我建议每次做手眼标定之前,都重新标定一次内参。别嫌麻烦,这点时间省不得。