简介:这份资源是Halcon官方参考手册第六章Calibration的英文直译中文版,内容无删减,面向从事计算机视觉、视觉检测算法与相机标定的工程师及学习者,用于系统理解相机模型、畸变补偿与标定流程。全章围绕标定对象描述与查找、3D点投影与反投影、角度与径向畸变校正、相机内外参数处理、自校准,以及单目、双目、多视图和手眼标定等设置展开,并给出从准备标定板、采集输入数据、调整标定数据模型到执行calibrate_cameras、用RMSE评估结果的完整步骤,还涉及针孔与鱼眼等相机模型的限制。资源包共1个PDF文件,约813KB,篇幅紧凑便于随查随用。已有363人学习,适合希望对照原版术语深入掌握Halcon标定细节的读者。
1. 标定不是"调参玄学":为什么 0.1 像素的 RMSE 能决定整套视觉系统的成败
很多人第一次接触相机标定,会把它当成一个"跑一遍算子、拿到参数就完事"的流程。但真正做过视觉检测的人都知道,标定的本质是给整个成像链路建立一套可逆的数学模型——它决定了 3D 世界里的一个点,究竟会落在图像的哪个亚像素位置上。这个模型建得准不准,直接决定了后续测量、定位、手眼抓取的上限。Halcon 第六章 Calibration 给出的核心结论很朴素:标定成功与否,看的是反投影均方根误差(RMSE),单相机场景下 0.1 像素左右说明优化与观测数据拟合良好,偏离这个量级就说明图像质量、图像数量或标定板精度出了问题。这个数字不是玄学,它是"模型预测的标记点位置"与"实际提取到的标记点位置"之间的平均距离。搞懂它为什么能作为判据、背后那套从世界坐标到像素坐标的变换链是怎么搭起来的,比记住几个算子名有用得多。下面从标定对象、数据准备、参数体系一路拆到 3D 相机模型和源码级的变换步骤,把这条链路讲透。
2. 标定对象与数据模型:为什么标定板的方向比清晰度更致命
标定的起点不是相机,而是一个度量属性精确已知的标定对象。Halcon 支持两类标准标定板,它们的差异不只是"六边形"和"矩形"这么表面,而是直接决定了你能怎么拍、能拍多少张、以及原点落在哪里。
2.1 六边形标记板与矩形标记板的结构差异
六边形排列标记板(由create_caltab生成)的查找器模式是特殊的六边形标记组,其中部分标记带点状孔。它的关键特性是:只要至少一个查找器图案可见就能定位标定板,看到两个以上还能检测标定板是否被镜像。坐标系原点位于第一个查找器图案中心标记的中心,z 轴指向标定板,x 轴向右,y 轴向下,视线沿 z 轴方向。这意味着标定板不必在图像中完整可见,拍摄自由度更高。
矩形排列标记板(由gen_caltab生成)的查找器模式是外围框架加三角形角标记,因此标定板必须在图像中完全可见。原点位于标定板表面正中间,坐标轴方向定义与六边形板一致。这里有个容易翻车的细节:使用camera_calibration而非calibrate_cameras时,六边形板不适用。选错板子和算子组合,后面所有步骤都是白费。
| 特性 | 六边形标记板 | 矩形标记板 |
|---|---|---|
| 生成算子 | create_caltab | gen_caltab |
| 查找器模式 | 六边形标记组,部分带孔 | 外围框架 + 三角形角标记 |
| 可见性要求 | 至少一个查找器图案可见 | 必须完全可见 |
| 镜像检测 | 两个以上查找器图案可检测 | 依赖完整框架 |
| 最少图像数 | 6 张(至少 4 张倾斜) | 15 张 |
| 原点位置 | 第一个查找器中心标记中心 | 标定板表面正中间 |
这张表不是让你背,而是让你理解设计动机:六边形板牺牲了"完整可见"的约束,换来了更灵活的拍摄姿态;矩形板用完整框架换来了更直接的原点定义。两者在原点定义上的差异,会直接影响后续外参的物理含义。
2.2 校准数据模型的六步构建
在调用calibrate_cameras之前,必须先搭好校准数据模型。Halcon 给出的步骤是一条严格的依赖链,顺序错了就会在后续查询结果时拿到空值或报错。
# 伪代码:校准数据模型的构建顺序(对应 Halcon 算子语义) calib_data = create_calib_data( 'calibration_object', # 标定对象类型 num_cameras=1, # 安装的相机数量 num_calib_objects=1 # 使用的标定对象数量 ) # 第二步:指定相机类型与初始内参 set_calib_data_cam_param( calib_data, 0, 'area_scan_division', [0.008, 0.0, 3.45e-6, 3.45e-6, 640, 512, 1280, 1024] ) # 第三步:描述标定对象(标定板的 3D 点坐标与度量信息) set_calib_data_calib_object(calib_data, 0, calib_obj_handle) # 第四步:收集观测数据——提取标记图像坐标 + 粗略姿态 find_calib_object(image, calib_data, 0, 0, 0, [], []) # 或手动设置:set_calib_data_observ_points(...) # 第五步:配置优化,排除已知精确的参数 set_calib_data(calib_data, 'camera', 0, 'excluded_settings', ['sx', 'sy']) # 第六步:执行实际校准 calibrate_cameras(calib_data)第一步create_calib_data决定了模型的"容器形状"——相机数和标定对象数一旦定下,后续所有索引都受它约束。第二步的初始内参不是随便填的,Focus用镜头标称焦距(如 0.008 米),Sx/Sy用相机芯片规格里的像元尺寸,Cx/Cy用半图像宽高。第三步把标定板的 3D 点坐标灌进模型,这是"世界坐标"一侧的基准。第四步的find_calib_object同时完成两件事:提取标记的亚像素图像坐标,以及估计标定板相对相机的粗略姿态——后者是外参优化的起点,起点太差会导致优化陷入局部极小。第五步的excluded_settings是很多人忽略的精度杠杆:如果你已经精确知道传感器像元尺寸,把sx、sy排除出优化,能减少待估参数、提升其余参数的稳定性。第六步才是真正的优化。
提示:第四步的姿态估计质量直接决定校准能否收敛。如果
find_calib_object返回的姿态明显偏离物理直觉,先检查标定板是否被镜像、标记直径是否够 20 像素,而不是急着调优化参数。
3. 内参体系:为什么 Sy 永远被固定,焦距和放大率不能同时估
相机参数分内外两组。内参描述相机自身特性——传感器尺寸、镜头投影特性、畸变;外参描述世界坐标系相对相机坐标系的 3D 姿态。理解内参的关键,不是记住每个参数的含义,而是理解"哪些参数被算法固定、为什么固定"。
3.1 面扫相机的参数族与固定项
面扫相机按镜头类型分成投影、远心、倾斜、超中心几大类,每类的参数元组长度从 9 到 16 不等。以最常用的area_scan_division为例:
['area_scan_division', Focus, Kappa, Sx, Sy*, Cx, Cy, ImageWidth, ImageHeight]带星号的Sy是固定项。原因藏在针孔模型的数学结构里:投影相机用针孔模型描述时,焦距、Sx、Sy三者存在尺度耦合——同时放大焦距和缩小像元尺寸,投影结果不变。所以算法必须固定一个,Halcon 选择固定Sy,因为它由传感器物理尺寸决定,最可靠。远心镜头同理:放大率、Sx、Sy无法同时确定,固定Sy。
倾斜镜头的情况更复杂。像侧远心的倾斜镜头,焦距、Sx、Sy、Tilt、Rot无法同时确定,所以除了Sy,Sx也被固定。双侧远心的倾斜镜头,放大率、Sx、Sy、Tilt、Rot无法同时确定,同样固定Sx和Sy。这不是实现偷懒,而是模型本身的可辨识性问题——参数之间存在精确的代数依赖,强行全估只会得到病态的优化结果。
畸变模型分两种。分割模型(division)只用一个Kappa模拟径向畸变,初值 0.0 m⁻²;多项式模型(polynomial)用K1, K2, K3模拟径向、P1, P2模拟离心畸变,五个系数初值全为 0.0。分割模型的方程可以解析求逆,多项式模型不能解析反转,逆畸变必须迭代计算——这个差异在 3.3 节会展开。
3.2 线扫相机:运动矢量才是真正的难点
线扫相机的内参元组多了Vx, Vy, Vz三个运动矢量分量,单位是米/行。这是线扫与面扫最本质的区别:线扫相机每条图像线都是从不同位置成像的,相机与物体之间的相对运动必须建模。Halcon 的假设是相机沿直线匀速移动、方向恒定、所有图像的运动相等。
运动矢量的初值质量对整个校准成败至关重要。标准做法是把相机摆成传感器线垂直于传送带、相机 y 轴平行于传送带,此时Vx = Vz = 0,Vy可以从已知尺寸物体的线扫图像(标定板、标尺)里估出来。如果相机绕光轴转了 30 度,初值要按旋转矩阵重新算;绕 x 轴转 -20 度同理。这些初值不够准,校准可能直接失败。
远心线扫相机还有个特殊约束:Vz对 3D 点的图像位置没有影响,因此无法确定,算法保留其初始值,应设为 0。另外线扫相机的Sx和Sy都是固定项——Sy固定是因为标定中只出现pv = -Sy·Cy的组合形式,Sy和Cy不能同时确定;Sx固定是因为焦距/放大率与Sx不能同时确定。
3.3 畸变模型的选择:速度与精度的取舍
分割模型和多项式模型的选择,是一个典型的工程取舍。分割模型只有一个参数,逆畸变可解析计算,速度快,且在标定图像少、视场覆盖不足时结果更稳定。多项式模型用高阶项模拟径向畸变、额外模拟离心畸变,精度更高,但逆畸变必须迭代求解,慢;而且它要求标定序列提供更好的区域覆盖,在标定板覆盖区域之外(图像边界、视场未覆盖区)可能模拟不准。
Halcon 的建议很明确:通常用分割模型;标定精度不够高时再上多项式模型,但要保证后续测量区域被标定板充分覆盖。这个建议背后的逻辑是:多项式模型的高阶项在数据不足时容易过拟合,把噪声当成畸变来拟合,反而在未覆盖区域产生更大的外推误差。
注意:如果对图像做了次采样,
Sx、Sy、Cx、Cy、ImageWidth、ImageHeight都要相应调整——次采样会让像元间距变大、主点坐标和图像尺寸变小。忘了这一步,初始值就全错了。
4. 从世界坐标到像素:一条变换链上的四个步骤
标定模型的核心,是把世界坐标pw = (xw, yw, zw)ᵀ映射到像素坐标qi = (r, c)ᵀ。这条链分四步:世界→相机坐标、相机坐标→图像平面、镜头畸变(含倾斜透镜的附加变换)、图像平面→像素坐标。每一步都有明确的数学形式和实现约束。
4.1 第一步:世界坐标到相机坐标
用旋转矩阵R和平移矩阵T把pw变换到相机坐标系pc,点作为齐次向量处理,对应affine_trans_point_3d的语义。相机坐标系的 x、y 轴平行于图像的列轴和行轴,z 轴垂直于图像平面。对线扫相机,世界坐标系的位姿指的是第一图像线的相机坐标系——所有世界↔相机的转换都只基于第一图像线的姿态,这是 Halcon 简化线扫模型的关键设计。
4.2 第二步:投影到图像平面
面扫针孔相机的投影是透视投影,pc = (xc, yc, zc)ᵀ在成像平面上的投影由焦距f = Focus决定;超中心镜头也执行透视投影,公式略有不同。面扫远心相机则是平行投影,用放大率m = Magnification代替焦距。这一步的输出还是米制坐标,尚未进入像素域。
4.3 第三步:镜头畸变与倾斜透镜的附加变换
分割模型的畸变公式把畸变像平面坐标变换为未畸变坐标,且可解析求逆——从未畸变坐标反算畸变坐标有闭式解。多项式模型的公式不能解析反转,畸变坐标必须由未畸变坐标数值迭代计算。这就是 3.3 节说的速度差异的数学根源。
倾斜透镜在这一步插入一个附加变换,且要区分透镜类型。投影倾斜透镜和物侧远心倾斜透镜(在镜头图像侧执行透视投影),投影到倾斜像面的点用一个射影二维变换描述,即齐次 3×3 矩阵H,对应projective_trans_point_2d,其中d = ImagePlaneDist,旋转角p = Rot、倾斜角T = Tilt。像侧远心倾斜透镜和双侧远心倾斜透镜(在像侧执行平行投影),倾斜像面上的投影用 2×2 线性矩阵描述。这个区分不是形式主义:透视投影和 parallel 投影在倾斜面上的几何行为本质不同,用错矩阵,倾斜补偿就完全错了。
4.4 第四步:图像平面到像素坐标
最后把点(有倾斜透镜时是qt)从图像平面坐标系转换到像素坐标系。对线扫相机,这一步还要把运动矢量V纳入投影计算——线扫针孔相机需要联立求解包含t的方程组,线扫远心相机则对u和t联立求解,且z和Vz都不影响远心相机的投影。公式里已经包含了图像畸变的补偿。
这条四步链的设计动机很清楚:把"几何投影"和"畸变补偿"解耦,让每一层可以独立替换。换镜头类型只改第二步和第三步的公式,换传感器只改第四步的尺度,换畸变模型只改第三步。这种分层是 Halcon 能同时支持面扫/线扫、投影/远心、常规/倾斜这么多组合的根本原因。
5. 避坑与排查:标定翻车的五条血泪经验
标定流程看起来线性,实际踩坑点密集。下面五条按"现象→原因→解决"整理,都是能直接对照排查的。
现象一:RMSE 远大于 0.1 像素,优化不收敛。原因通常是标定图像数量不足或标定板姿态太单一——所有图像里标定板都平行放置,六个外参无法唯一确定,焦距和标定板到相机的距离耦合在一起。解决:确保图像集包含向不同方向倾斜约 30–45° 的标定板,六边形板至少 6 张(至少 4 张倾斜),矩形板至少 15 张。
现象二:标记提取失败或姿态估计明显错误。原因往往是标记直径太小或对比度不够。标记在每个图像中直径至少 20 像素,这是成功校准的关键阈值;明暗区域对比度至少 100 灰度值(字节图像)。解决:调整拍摄距离或镜头,让标记足够大;改善照明,避免标定板浅色部分灰度超过 240(过曝)或灰度范围超过 45(照明不均)。
现象三:标定板被镜像却浑然不觉。用玻璃标定板从背面拍摄时,标记会被反向获取。六边形板看到两个以上查找器图案时能检测镜像并报错,矩形板依赖完整框架。解决:确认拍摄面正确;六边形板尽量让两个以上查找器图案可见,把镜像检测打开。
现象四:远心镜头标定后 z 方向测量完全不对。远心透镜下,标定板与相机之间的距离无法确定,标定板姿态的 z 分量在结果中被强制设为 1 米。这不是 bug,是远心投影的固有不可辨识性。解决:接受这个约定,在测量时用同一约定解释 z;不要试图从远心标定里提取绝对距离。
现象五:倾斜镜头标定后 Tilt/Rot 和标称值差很多。透镜畸变越大,Tilt/Rot 越能准确确定;畸变小的透镜,Tilt/Rot 本身不可辨识,优化结果可能大幅偏离标称值。解决:先看误差——如果 RMSE 很小,说明得到的参数在校准体积内能一致描述成像几何,可用于精确测量,不必强求 Tilt/Rot 等于标称值。
提示:光圈、相机位置、焦点在采集过程中都不能变。标定后若改了光圈,必须重新标定——这条没有后悔药。
6. 设计取舍:为什么 Halcon 宁可固定参数也不让你全估
把整章读下来,会发现 Halcon 标定设计里贯穿一条主线:可辨识性优先于参数完备性。Sy被固定、远心 z 被设为 1 米、远心线扫Vz保留初值、小畸变透镜的 Tilt/Rot 不可辨识——这些都不是实现缺陷,而是模型数学结构的必然结果。Halcon 的选择是显式承认这些不可辨识性,用固定或约定值处理,而不是让优化器在病态空间里乱跑给出看似精确实则无意义的数字。
这个取舍有代价。固定Sy意味着你必须在初始值里填对传感器像元尺寸,填错了标定结果会系统性偏移;远心 z 设为 1 米意味着绝对距离信息丢失,跨设备、跨标定的 z 数据不能直接比较。但换来的好处是:在参数可辨识的子空间里,优化是良态的,RMSE 这个判据才有意义。如果强行全估,RMSE 可能看起来很小,但参数已经漂到物理上荒谬的值——这种"假收敛"比明显失败更难排查。
我一般会这么理解这套设计:标定不是"求出所有参数",而是"在可辨识的参数子空间里求最优,对不可辨识的部分用物理约定兜底"。所以每次标定前,我会先问三个问题——我的镜头类型对应哪个参数元组、哪些参数带星号、我的拍摄姿态是否让剩余参数可辨识。这三个问题答不上来,跑再多遍calibrate_cameras也是碰运气。从那以后我每次拿到一组标定图像,都强制先核对参数元组的固定项和拍摄姿态覆盖,再去看 RMSE。希望帮到你。
本文还有配套的精品资源,点击获取