C#+Halcon上下双相机对位贴合:标定、MARK点校正与补偿算法实战
发布时间:2026/10/2 14:43:15来源:尧图网络
简介这份资源面向自动化视觉与SMT贴片机方向的C#开发者及机器视觉工程师聚焦模板识别、相机标定与对位贴合等核心环节提供一套可参考的工程级源程序。包内共101个文件以cs源码、exe可执行程序、dll动态库、config配置、manifest与application清单、pdb调试符号及cache缓存等为主另有sln解决方案、csproj工程文件与少量txt说明压缩包约10.45MB结构贴近实际Visual Studio项目。内容覆盖Halcon模板匹配、MARK点4点校正与2点补偿、贴合补偿算法以及上下双相机对位贴合流程可帮助读者理解相机标定、定位校正与偏差修正的代码实现思路。目前已有876人学习下载适合需要参考完整视觉对位方案、排查标定与贴合精度问题的中高级开发者。1. 从一台 SMT 贴片机的上下双相机说起这套 C# Halcon 方案到底解决什么问题SMT 贴片机在贴装 QFN、BGA、连接器这类元件时最怕的不是贴不上而是贴偏。元件从吸嘴取料那一刻起就带着来料偏差、吸嘴同心度误差、飞行过程中的微小位移到了 PCB 上方板子本身还有 MARK 点定位误差、涨缩变形。如果只靠机械定位0402 电阻或许还能凑合0.4mm pitch 的 IC 基本就是批量翻车。这套「C# Halcon 模板识别 上下双相机对位贴合」的方案核心就是用视觉把偏差量测出来再用贴合补偿算法把偏差补回去。它适合谁做 SMT 设备上位机、半导体固晶机、贴合类自动化设备的 C# 工程师尤其是已经会用 Halcon 做单相机定位、但没系统做过「上相机看元件 下相机看 PCB 双相机坐标统一 补偿下发」这条完整链路的人。整套逻辑拆开看是四块相机标定把像素换算成毫米MARK 点 4 点校正建立 PCB 坐标系2 点补偿修正旋转与平移残差贴合补偿算法算出最终贴装坐标。下面按落地顺序一层层讲透。2. 相机标定与上下双相机坐标系统一像素怎么变成毫米2.1 为什么标定是整套方案的地基Halcon 里做模板识别find_shape_model返回的是像素坐标和角度但贴片机执行机构要的是毫米和度。这中间隔着一个标定矩阵。标定不准后面 MARK 点校正、补偿算法全是白算——这就是典型的「地基歪了楼越高越危险」。常见做法是先用标定板圆点阵列或棋盘格标出相机的内参和畸变再标出像素当量mm/pixel。对于贴片机这种工作距离固定、视野固定的场景其实不需要每次都做完整内参标定用九点标定求一个仿射矩阵就够了速度快、现场可复现。九点标定的本质是求一个 2x3 的仿射变换矩阵把像素坐标 (u,v) 映射到机械坐标 (x,y)。Halcon 提供vector_to_hom_mat2d输入九组像素点和对应的机械点直接解出矩阵。这里有个血泪经验九点一定要覆盖整个视野别图省事只在中心打九个密集点边缘的畸变残差会全部漏掉贴到板边就偏。// C# 调用 Halcon 做九点标定求像素到机械的仿射矩阵 HTuple pixelRows new HTuple(new double[] { 100, 300, 500, 100, 300, 500, 100, 300, 500 }); HTuple pixelCols new HTuple(new double[] { 100, 100, 100, 300, 300, 300, 500, 500, 500 }); // 对应的机械坐标单位 mm由运动平台实际走到每个点后记录 HTuple mechX new HTuple(new double[] { 10.0, 30.0, 50.0, 10.0, 30.0, 50.0, 10.0, 30.0, 50.0 }); HTuple mechY new HTuple(new double[] { 10.0, 10.0, 10.0, 30.0, 30.0, 30.0, 50.0, 50.0, 50.0 }); HTuple homMat2D; HOperatorSet.VectorToHomMat2d(pixelRows, pixelCols, mechX, mechY, out homMat2D); // 后续把任意像素点转机械坐标 HTuple mechPointX, mechPointY; HOperatorSet.AffineTransPoint2d(homMat2D, new HTuple(320.0), new HTuple(240.0), out mechPointX, out mechPointY);这段代码的关键在VectorToHomMat2d的参数顺序前两组是像素的行列后两组是机械的 XY顺序不能错错了矩阵就是转置的贴出来会镜像。AffineTransPoint2d是单点转换批量转换用AffineTransPoint2d循环或AffineTransContourXld。参数上九点的机械坐标必须用激光干涉仪或光栅尺读出的真实值不能用手轮刻度估估出来的当量误差会直接放大到贴装精度上。2.2 上下双相机怎么统一到一个坐标系上相机朝下看吸嘴上的元件下相机朝上看 PCB 上的 MARK 点两个相机物理位置不同必须统一到同一个机械坐标系。常见做法是先各自做九点标定得到两个仿射矩阵再找一个公共基准——通常用吸嘴吸一个标准件让上相机和下相机分别看同一个特征点记录两次机械坐标求出两个相机坐标系之间的平移和旋转关系。这里有个容易忽略的点上下相机的光轴不平行会引入角度偏差。如果两个相机安装时没有严格平行公共基准求出来的旋转量会随高度变化贴不同厚度元件时补偿量就不一样。我一般会在标定时用两个不同高度的标准件各测一次如果两次求出的旋转角差超过 0.05 度就说明相机安装有倾斜得先调机械再标。// 求上下相机坐标系之间的变换上相机坐标 - 下相机坐标 // 用同一物理点在两个相机下的机械坐标各测 3 组以上 HTuple upCamX new HTuple(new double[] { 20.1, 40.2, 60.3 }); HTuple upCamY new HTuple(new double[] { 15.0, 15.1, 15.2 }); HTuple downCamX new HTuple(new double[] { 20.0, 40.0, 60.0 }); HTuple downCamY new HTuple(new double[] { 15.0, 15.0, 15.0 }); HTuple camToCamMat; HOperatorSet.VectorToHomMat2d(upCamX, upCamY, downCamX, downCamY, out camToCamMat); // camToCamMat 即上相机机械坐标到下相机机械坐标的映射参数说明VectorToHomMat2d至少需要 3 组不共线的点实际建议 5 组以上取平均减少单点噪声。求出的camToCamMat包含旋转和平移后续上相机测到的元件偏差要先经这个矩阵转到下相机坐标系再和 PCB 的 MARK 点坐标做运算。这一步顺序搞反补偿方向就全反了。3. MARK 点 4 点校正与 2 点补偿PCB 坐标系怎么建才稳3.1 4 点校正为什么比 2 点更抗涨缩PCB 在回流焊前后会有涨缩尤其是大板。用 2 个 MARK 点只能求出平移和旋转遇到板子 X 方向和 Y 方向涨缩不一致时贴到板子另一头就偏了。4 点校正的做法是在板子四角各取一个 MARK 点用 Halcon 的find_shape_model或find_marks_and_pose找到四个点然后拟合出一个带缩放因子的仿射变换甚至可以用透视变换vector_to_proj_hom_mat2d来吸收梯形变形。Halcon 里做 MARK 点识别模板要选对比度高、形状独特的区域别选圆形焊盘——圆形在find_shape_model里旋转不变性差容易和过孔混淆。常见做法是用十字标记或 L 形标记find_shape_model的AngleStart和AngleExtent设成 ±5 度就够贴片机 PCB 定位不会有大角度旋转设太大反而增加误匹配。// Halcon 识别 4 个 MARK 点并建立 PCB 坐标系 HObject ho_Image, ho_Region; HTuple hv_ModelID; // 读取预先训练好的 MARK 点模板 HOperatorSet.ReadShapeModel(mark_model.shm, out hv_ModelID); // 在整幅图中找所有 MARK 点Score 阈值设 0.7 过滤误匹配 HTuple hv_Row, hv_Column, hv_Angle, hv_Score; HOperatorSet.FindShapeModel(ho_Image, hv_ModelID, -0.09, 0.18, 0.7, 1, 0.5, least_squares, 0, 0.9, out hv_Row, out hv_Column, out hv_Angle, out hv_Score); // 假设已按顺序取到 4 个点用它们建立到标准 PCB 坐标的投影变换 HTuple pcbRow new HTuple(new double[] { 100, 100, 900, 900 }); HTuple pcbCol new HTuple(new double[] { 100, 900, 100, 900 }); HTuple stdX new HTuple(new double[] { 0, 50, 0, 50 }); HTuple stdY new HTuple(new double[] { 0, 0, 40, 40 }); HTuple projMat; HOperatorSet.VectorToProjHomMat2d(pcbRow, pcbCol, stdX, stdY, out projMat);参数说明FindShapeModel的MinScore设 0.7 是经验值光照稳定可以降到 0.6光照波动大就提到 0.8。NumMatches设 1 表示每个模板只找一个但 4 个 MARK 点如果模板相同需要设成 4 或者用不同模板分别找。Greediness设 0.9 是速度优先精度要求高可以降到 0.7。VectorToProjHomMat2d求出的投影矩阵能吸收梯形变形比仿射矩阵更适合大板。3.2 2 点补偿补的是什么4 点校正建立了 PCB 的整体坐标系但实际贴装时还有残差吸嘴取料时的同心度偏差、元件在吸嘴上的微小转动、飞行过程中的偏移。这些残差用 2 点补偿来修。做法是在 PCB 上选两个已知坐标的基准点贴装头带着元件分别移到这两个点上方用上相机看元件、下相机看基准点算出实际偏差量然后把这个偏差量按线性关系补偿到所有贴装点。2 点补偿的数学本质是求一个平移加旋转的修正量。假设两个基准点的理论坐标是 P1、P2实测偏差是 d1、d2那么任意贴装点 P 的补偿量可以用线性插值算出来。这里的关键是两点要尽量拉开距离距离越远旋转修正的灵敏度越高。如果两点只隔 10mm旋转 0.1 度产生的偏差只有 0.017mm根本测不出来。// 2 点补偿根据两个基准点的实测偏差计算任意点的补偿量 // 理论坐标 double x1 0, y1 0, x2 50, y2 0; // 实测偏差实测坐标 - 理论坐标 double dx1 0.02, dy1 -0.01, dx2 0.05, dy2 0.03; // 求补偿的平移和旋转 double dxAvg (dx1 dx2) / 2.0; double dyAvg (dy1 dy2) / 2.0; double dTheta Math.Atan2(dy2 - dy1, (x2 dx2) - (x1 dx1)) - Math.Atan2(y2 - y1, x2 - x1); // 对任意贴装点 (px, py) 计算补偿后坐标 double px 25, py 20; double cx px dxAvg - dTheta * (py - y1); double cy py dyAvg dTheta * (px - x1);参数说明dTheta是旋转修正量单位弧度计算时要用实测坐标而不是理论坐标否则旋转量会算偏。补偿公式里-dTheta * (py - y1)和dTheta * (px - x1)是旋转矩阵的小角度近似角度小于 1 度时误差可忽略大于 1 度建议用完整旋转矩阵。实际工程中2 点补偿通常只补平移和旋转缩放交给 4 点校正两者分工明确。4. 贴合补偿算法与上下双相机对位流程从识别到下发4.1 贴合补偿算法的完整计算链贴合补偿算法不是单一公式而是一条计算链上相机测元件偏差 → 转到下相机坐标系 → 叠加 PCB MARK 点偏差 → 叠加 2 点补偿残差 → 输出最终贴装坐标。这条链上每一步的坐标系必须统一否则补偿量会互相抵消或叠加错误。我一般会在代码里把每一步的中间结果都打日志出问题时能快速定位是哪一步算错了。上相机测元件偏差用find_shape_model找到元件上的特征和标准位置比得到 dx、dy、dtheta。这里要注意元件的旋转中心如果元件不是绕几何中心旋转dtheta 引起的偏移量要用旋转中心到特征点的距离来算不能直接用元件中心。常见做法是在模板训练时就把旋转中心定义在吸嘴中心这样 dtheta 直接就是元件相对吸嘴的转角。// 贴合补偿完整计算上相机元件偏差 PCB MARK 偏差 2点补偿 // 上相机测得的元件偏差已转到下相机坐标系 double compDx 0.03, compDy -0.02, compDtheta 0.001; // PCB MARK 点算出的板子偏差 double pcbDx 0.01, pcbDy 0.015, pcbDtheta 0.0005; // 2 点补偿残差 double residualDx 0.005, residualDy -0.003; // 最终补偿量 元件偏差 板子偏差 残差 double finalDx compDx pcbDx residualDx; double finalDy compDy pcbDy residualDy; double finalDtheta compDtheta pcbDtheta; // 贴装点理论坐标 double targetX 100.0, targetY 80.0; // 最终下发坐标 double pasteX targetX - finalDx; double pasteY targetY - finalDy; double pasteTheta finalDtheta;参数说明补偿量的符号很关键。如果定义偏差是「实测 - 理论」那么下发坐标应该是「理论 - 偏差」因为要把元件往偏差的反方向推。这个符号搞反是新手最常见的翻车点贴出来会偏两倍偏差量。finalDtheta直接作为贴装角度增量下发但要注意旋转方向Halcon 的角度是逆时针为正机械旋转如果是顺时针为正要取反。4.2 上下双相机对位的执行时序上下双相机对位不是同时拍照而是有严格时序吸嘴取料 → 上相机拍照测元件 → 运动到 PCB 上方 → 下相机拍照测 MARK 点 → 计算补偿 → 贴装。这个时序里上相机拍照时吸嘴要旋转到贴装角度这样测出的元件角度才是最终贴装姿态下的角度。如果上相机拍照时吸嘴没转到位测出的 dtheta 还要叠加吸嘴旋转量容易出错。下相机拍照测 MARK 点时要注意相机的曝光和光源。PCB 上的 MARK 点通常是铜箔或镀金反光特性差异大。常见做法是用同轴光或环形光曝光时间设到 MARK 点清晰、背景不过曝。如果 MARK 点识别不稳定先调光源和曝光别急着改算法参数。我见过太多人一上来就调MinScore结果把误匹配调进来了。// 上下双相机对位执行时序伪代码 // 1. 吸嘴取料后旋转到贴装角度 nozzle.RotateTo(pasteTheta); // 2. 上相机拍照测元件偏差 HObject upImage upCamera.Capture(); double dx, dy, dtheta; MeasureComponent(upImage, out dx, out dy, out dtheta); // 3. 运动到 PCB 上方下相机拍照测 MARK 点 HObject downImage downCamera.Capture(); double pcbDx, pcbDy, pcbDtheta; MeasureMarkPoints(downImage, out pcbDx, out pcbDy, out pcbDtheta); // 4. 计算最终补偿并下发 double finalX targetX - (dx pcbDx residualDx); double finalY targetY - (dy pcbDy residualDy); motion.Paste(finalX, finalY, pasteTheta dtheta pcbDtheta);参数说明nozzle.RotateTo要在上相机拍照前完成且要等旋转稳定后再拍照否则会有运动模糊。MeasureComponent里用find_shape_model时AngleStart和AngleExtent要覆盖吸嘴旋转后的角度范围。motion.Paste的坐标是机械坐标不是像素坐标中间所有转换都要用标定矩阵。实际工程中这一步的耗时主要在运动和数据传输视觉算法本身在 Halcon 里通常只要几毫秒。5. 避坑与排查这套方案最容易翻车的 5 个地方5.1 标定矩阵用错坐标系导致补偿方向反现象贴装后元件偏差不但没减小反而变成两倍方向还反了。原因标定矩阵的输入输出坐标系定义混乱比如把像素行列当成 XY或者把机械坐标的 XY 和图像坐标的 XY 搞反。解决在标定代码里加断言用已知点验证矩阵比如把标定用的九点重新代入矩阵看输出是否和机械坐标一致误差超过 0.01mm 就说明矩阵有问题。5.2 MARK 点识别不稳定导致偶发贴偏现象大部分板子贴得准偶尔一两块偏且偏移量随机。原因MARK 点识别受光照、板面污染影响MinScore设太低导致误匹配到过孔或焊盘。解决先固定光源和曝光把MinScore提到 0.8 以上同时用find_shape_model的NumMatches限制匹配数量再对匹配结果做几何校验——4 个 MARK 点的相对距离应该和标准值一致偏差超过 0.1mm 就判为识别失败报警而不是硬贴。5.3 上下相机坐标系转换漏了旋转量现象元件在视野中心时贴得准在视野边缘就偏且偏移方向随位置变化。原因上下相机坐标系转换只做了平移没做旋转或者旋转量算错。解决用至少 5 组公共基准点求VectorToHomMat2d检查求出的矩阵旋转部分是否接近单位矩阵如果旋转量超过 0.1 度说明相机安装有倾斜先调机械。转换时用完整矩阵别只取平移量。5.4 2 点补偿的两点距离太近现象旋转偏差补不回来元件角度总是差一点。原因两个补偿基准点距离太近旋转修正的灵敏度不够测量噪声淹没了真实旋转量。解决两点距离至少拉到视野的 1/2 以上最好在板子对角。如果板子太小改用 4 点补偿用最小二乘拟合旋转量比 2 点更稳。5.5 Halcon 模板训练时旋转中心设错现象元件旋转后贴装位置绕着一个错误的中心偏。原因模板训练时旋转中心默认在模板区域中心但实际元件旋转中心在吸嘴中心两者不重合。解决训练模板时用set_shape_model_origin把原点设到吸嘴中心对应的图像位置。如果不知道吸嘴中心在图像里的位置先用一个标准件吸在吸嘴上拍照后手动标定原点。6. 把补偿量做成可追溯的日志一个让我少加很多班的小技巧这套方案跑通之后真正让我省心的不是算法多精妙而是把每一步的补偿量都写进了日志。贴片机是 24 小时跑的出问题往往在半夜现场操作员说不清现象我第二天到现场只能靠日志复盘。我的做法是每贴一块板就把上相机偏差、MARK 点偏差、2 点残差、最终下发坐标全部写进 CSV字段包括时间戳、板序号、元件位号、各项偏差量、补偿后坐标。// 补偿量追溯日志每贴一个元件写一行 string logPath D:\smt_log\compensation.csv; using (StreamWriter sw new StreamWriter(logPath, true, Encoding.UTF8)) { // 表头只在文件新建时写一次实际工程中先判断文件是否存在 sw.WriteLine(${DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}, ${boardIndex},{componentRef}, ${compDx:F4},{compDy:F4},{compDtheta:F5}, ${pcbDx:F4},{pcbDy:F4},{pcbDtheta:F5}, ${residualDx:F4},{residualDy:F4}, ${pasteX:F4},{pasteY:F4},{pasteTheta:F5}); }这段代码本身很简单价值在字段设计。compDx和pcbDx分开记出问题时能一眼看出是元件识别偏了还是板子定位偏了。residualDx单独记如果它持续变大说明 2 点补偿的基准点该重新标了。pasteX和pasteY是最终下发值和实际贴装结果对比能算出这套补偿算法的真实残差。有了日志之后我还加了一个简单的统计每 100 块板算一次各项偏差的均值和标准差。均值反映系统性偏差比如标定不准标准差反映随机误差比如识别不稳定。如果某个元件的compDtheta标准差突然变大多半是吸嘴磨损或者元件来料一致性变差提前换吸嘴比批量贴废再返工划算得多。这个习惯说起来不起眼但它把「玄学调机」变成了「看数据说话」。贴片机这行经验很重要但经验如果不落成数据换个人、换台机器就得重新踩一遍坑。我现在做新项目第一版代码可以糙但日志字段一定先设计好后面省下的排查时间远超写日志的那点功夫。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网