2. HALCON 标定算子概览:常用标定算子介绍与调用顺序
做标定,说白了就是跟几个核心算子打交道。很多新手一上来就对着 find_calib_object 猛调参数,结果报错一堆,根本不知道问题出在哪。我刚开始接触 HALCON 标定时也踩过这个坑——那时候我连算子之间的数据流都没搞清楚,就急着跑代码。
这一章,我就带你把这些核心算子捋一遍。你搞懂了它们各自干什么、谁先谁后,后面排查报错才能有的放矢。
2.1 标定流程中的三大核心算子
HALCON 的标定流程,可以浓缩成三个关键步骤。每个步骤对应一个核心算子:
| 步骤 | 核心算子 | 一句话说明 |
|---|---|---|
| 1. 提取标定板角点 | find_calib_object |
从图像中找到标定板,并提取出所有标记点的亚像素坐标 |
| 2. 执行标定计算 | calibrate_cameras |
利用提取到的点坐标,解算出相机内外参数 |
| 3. 查询标定结果 | get_calib_data |
从标定数据模型中读取你想要的任何参数 |
这三个算子,构成了标定流程的主干。我习惯把它们叫做「标定三兄弟」——老大负责找点,老二负责算参数,老三负责把结果告诉你。
2.2 算子详解与调用示例
2.2.1 find_calib_object:找点,是标定的第一步
这个算子,说白了就是在一张图像里找到标定板,然后精确提取出每个圆点或棋盘格角点的坐标。它的精度直接决定了标定结果的成败。
我曾经遇到一个项目,现场工人随手拍了几张标定板图像,结果 find_calib_object 一直报错。我排查了半天,最后发现是标定板上有指纹印,导致反光不均匀。擦干净重拍,问题立刻解决。
* 典型调用示例
find_calib_object (Image, CalibDataID, CalibObjIdx, CalibObjIdx, \
'calib_object_model', 'caltab_30mm.descr', \
'all', 'all', 'all')
这里要注意几个关键参数:
- CalibDataID:标定数据模型的句柄,你得先创建好它
- 标定板描述文件:比如
caltab_30mm.descr,这个文件必须跟你的实物标定板完全一致 - 'all':表示处理所有找到的标记点,你也可以指定只处理部分
2.2.2 calibrate_cameras:算参数,核心计算环节
当所有图像都处理完后,就该 calibrate_cameras 上场了。它会利用你之前提取到的所有点坐标,通过最小二乘法解算出相机内参(焦距、主点、畸变系数)和外参(每张图像对应的位姿)。
* 执行标定计算
calibrate_cameras (CalibDataID, 'all', 'all', 'all')
嗯,这里要注意——这个算子本身不返回任何可见的结果。它只是把计算出的参数写进了 CalibDataID 这个数据模型里。你想看结果?得用下一个算子去取。
2.2.3 get_calib_data:查结果,把参数读出来
这个算子是个「万能查询器」。你可以用它拿到相机内参、外参、标定板的位姿、甚至每张图像的误差信息。
* 获取相机内参
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
* 获取某张图像的外参(相机位姿)
get_calib_data (CalibDataID, 'calib_obj_pose', [0, 0], 'pose', Pose)
我个人习惯在标定完成后,第一时间用 get_calib_data 把内参和外参都读出来,然后打印到控制台看一眼。如果内参里的焦距值跟镜头标称值差太多,那多半是标定过程有问题。
2.3 算子调用顺序与数据流
这三个算子不是随便调用的。它们之间有严格的数据依赖关系。我画了一张图,帮你理清这个流程:
你看,整个流程是线性的:先创建模型 → 设置标定板 → 循环找点 → 执行计算 → 查询结果。其中 find_calib_object 这一步是循环执行的——你有多少张标定板图像,就要调用多少次。
find_calib_object 之前就去执行 calibrate_cameras,那样算子会因为找不到输入数据而直接报错。
2.4 其他辅助算子
除了上面三个核心算子,还有几个辅助算子你也会经常用到:
create_calib_data:创建标定数据模型。这是所有操作的前提,必须先调用它。set_calib_data_calib_object:设置标定板描述文件。告诉 HALCON 你用的是哪种标定板。set_calib_data_cam_param:设置相机初始内参。比如你已知镜头焦距是 12mm,可以先给个初始值,能加快收敛速度。disp_caltab:显示标定板在图像中的位置。调试时很有用,可以直观看到标定板是否被正确识别。
这些辅助算子,说白了就是给核心算子「打下手」的。它们负责配置环境、传递参数,让核心算子能顺利工作。
2.5 一个完整的标定代码骨架
最后,我给你一个完整的标定代码骨架。你以后写标定程序,直接套这个模板就行:
* 1. 创建标定数据模型
create_calib_data ('calibration_object', 'camera', 1, CalibDataID)
* 2. 设置标定板描述文件
set_calib_data_calib_object (CalibDataID, 0, 'caltab_30mm.descr')
* 3. 循环处理每张图像
for i := 1 to NumImages by 1
read_image (Image, 'calib_image_' + i$'02d')
find_calib_object (Image, CalibDataID, 0, 0, i-1, \
'calib_object_model', 'caltab_30mm.descr', \
'all', 'all', 'all')
endfor
* 4. 执行标定计算
calibrate_cameras (CalibDataID, 'all', 'all', 'all')
* 5. 获取标定结果
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
get_calib_data (CalibDataID, 'calib_obj_pose', [0, 0], 'pose', Pose)
* 6. 清理资源
clear_calib_data (CalibDataID)
这个骨架,我用了好几年。每次做新项目,都是复制粘贴然后改改参数。你把它记熟了,标定这块就稳了八成。
find_calib_object 后面加一行 disp_caltab,把识别到的标定板轮廓画出来。这样每处理一张图像,我都能肉眼确认标定板是否被正确识别。发现问题,当场重拍,不用等到最后标定失败才回头排查。