第一次接触Halcon的视觉项目时,我被各种坐标系搞得晕头转向。直到在产线上调试一个零件尺寸检测系统时,因为坐标系理解错误导致测量偏差2毫米,差点造成批量报废事故,才真正明白坐标系转换的重要性。让我们从最基础的世界坐标系开始,逐步拆解这个三维到二维的映射过程。
世界坐标系就像我们生活中的GPS定位系统。在Halcon中,我们通常使用标定板来建立这个世界坐标系 - 标定板上的圆点阵列就是我们的"地图标记"。实际操作中,我会用find_calib_object算子自动识别标定板,这时Halcon会自动以标定板中心为原点建立坐标系。关键是要注意那个小小的三角标记,它决定了X轴的正方向,Y轴则垂直于X轴向右,Z轴通过右手定则确定(拇指X,食指Y,中指Z)。
相机坐标系则是以镜头光心为原点的私人坐标系。这里有个容易混淆的点:相机坐标系与世界坐标系的转换需要外参矩阵[R|t],其中R是3x3旋转矩阵,t是3x1平移向量。我常用calibrate_cameras算子来获取这些参数。记得有次项目,因为把旋转矩阵的顺序搞错,导致坐标系完全错乱,调试了整整两天才发现问题。
图像物理坐标系可能最抽象,它存在于相机的成像平面上。假设有个虚拟的二维网格贴在传感器前面,这个网格的中心就是原点。焦距f在这里起关键作用 - 它决定了三维点投影到二维平面的缩放比例。这个转换过程可以用小孔成像模型来理解:就像用针孔相机拍照,三维世界的点通过针孔(光心)投影到后面的成像面上。
像素坐标系是我们最熟悉的,在Halcon中用鼠标就能查看坐标值。但要注意三个易错点:
python复制# 典型的内参矩阵示例
CameraMatrix = [f/dx, 0, u0,
0, f/dy, v0,
0, 0, 1]
位姿(Pose)这个概念曾让我栽过不少跟头。最惨痛的一次是在汽车零部件检测项目中,因为没注意位姿的单位问题,导致机器人抓取位置偏移了1000倍 - 直接把目标物撞飞了。位姿本质上描述的是两个坐标系之间的相对关系,包含位置(x,y,z)和姿态(rx,ry,rz)共6个自由度。
Halcon中的位姿表示有个特别之处:旋转采用的是绕固定轴的旋转顺序。具体来说是先绕Z轴旋转rz,再绕Y轴旋转ry,最后绕X轴旋转rx。这种顺序在连续旋转时会产生"万向节锁"问题,我在做机械臂控制时就遇到过旋转异常的情况。这时改用四元数表示旋转会更稳定。
位姿与齐次矩阵的转换是工程中的常备技能。pose_to_hom_mat3d算子是我们的好帮手,它会生成一个4x4的变换矩阵。这个矩阵的精妙之处在于:左上角3x3是旋转矩阵,右上角3x1是平移向量,最后一行固定为[0,0,0,1]。我习惯用这个矩阵来做连续变换,因为矩阵乘法比直接操作位姿参数更不容易出错。
python复制# 位姿转齐次矩阵的Halcon示例
pose := [0.1, 0.2, 0.3, 0.1, 0.2, 0.3] # x,y,z,rx,ry,rz
hom_mat := pose_to_hom_mat3d(pose)
单位问题是个隐藏的大坑。Halcon默认使用米制单位,而工业机器人常用毫米。我的经验法则是:在数据进入Halcon前统一转为米,处理完再转回毫米。建立一套单位转换的封装函数能避免很多低级错误。另外要注意角度单位,Halcon使用弧度制,而有些设备使用角度制。
去年给一家电子厂做SMT元件贴装系统时,手眼标定环节花了我们三周时间。最终发现是标定板摆放角度超过了相机视野的容忍度。这个惨痛教训让我总结出一套可靠的标定流程,现在分享给大家。
准备工作阶段有三个关键点:
采集标定图像时,我通常采用"九宫格"法:让标定板占据视野的不同位置,至少15张不同角度。有个实用技巧是在标定板边缘贴几个明显标记,这样即使部分超出视野也能识别。采集时要确保标定板在每张图像中都能被完整识别,否则会影响参数估计精度。
标定过程的核心是calibrate_hand_eye算子。这里有个重要概念叫"手眼关系",分为眼在手上(Eye-in-Hand)和眼在手外(Eye-to-Hand)两种。我们的案例是眼在手外,需要特别注意机械臂的基坐标系与相机坐标系的对应关系。标定结果是一个位姿,表示工具坐标系到相机坐标系的变换。
python复制# 典型的手眼标定代码框架
* 读取标定图像
for i := 1 to 15 by 1
read_image(Image, 'calib_'+i)
find_calib_object(Image, CalibDataID, i, 1, [], [])
endfor
* 获取机械臂位姿数据
get_calib_data(CalibDataID, 'model', 'general', 'optimization_method', DataValue)
* 执行手眼标定
calibrate_hand_eye(CalibDataID, 'method', 'default', HandEyeCalibDataID)
验证环节经常被忽视,但至关重要。我会用标定结果反算几个已知点,检查重投影误差。通常要求误差小于0.1像素才算合格。如果误差过大,可能需要检查标定板摆放是否合理,或者机械臂位姿数据是否准确。
在半导体行业做晶圆检测时,我积累了一些坐标系转换的实用技巧。比如当需要测量多个相同零件的尺寸时,可以建立一个"零件坐标系"作为中间媒介,这样能大大简化计算过程。
image_points_to_world_plane算子是视觉测量的瑞士军刀。它能把图像点转换到世界平面,但要注意三个参数:
有次做皮带轮直径测量,就是因为没设置正确的Scale参数,导致测量结果比实际小了10倍。现在我都会先用标准量块验证测量系统,确保所有参数设置正确。
对于三维测量,project_3d_point和reconstruct_3d_from_focus是常用组合。前者用于已知三维坐标投影到图像,后者用于从多焦点图像重建三维形状。在汽车零件检测中,我们通过这两个算子的配合,实现了0.02mm的重复测量精度。
python复制# 典型的二维测量代码示例
* 获取边缘点
edges_sub_pix(Image, Edges, 'canny', 1.5, 15, 40)
* 拟合圆
fit_circle_contour_xld(Edges, 'algebraic', -1, 0, 0, 3, 2, Row, Column, Radius)
* 转换到世界坐标
image_points_to_world_plane(CameraParam, WorldPose, Row, Column, 1, X, Y)
Diameter := 2*Radius*Scale
调试阶段我必用的三个工具:
disp_caltab:可视化标定板坐标系pose_to_hom_mat3d+affine_trans_point_3d:验证坐标变换链dev_display+disp_message:实时显示坐标值最后提醒一个常见陷阱:Halcon的某些算子会内部缓存坐标系信息,比如create_shape_model会记住模板创建时的坐标系。当更换相机或调整位置后,一定要重新生成模板,否则会导致匹配位置偏差。