
1. 球焊检验项目的整体设计思路球焊英文叫 Ball Bonding是半导体封装环节里极为关键的一道工序。它的原理说起来不复杂用一根极细的金线或铜线穿过毛细管劈刀在高温和超声波作用下先在芯片焊盘上熔出一个金球并压焊上去再把线的另一端以弧形拉到引脚框架上完成第二焊点。整个过程在毫秒级完成肉眼几乎看不清但焊球的质量直接决定了这颗芯片的电气连接是否可靠。球焊检验要做的就是在生产线上快速、稳定地判断每一个焊球是否合格——球有没有偏位、直径是否在公差内、有没有变形、有没有缺失。Halcon 作为机器视觉领域的老牌工具库自带大量工业级例程其中ball.hdev就是专门针对球焊检验的一个经典示例。这个例程的核心价值在于它用一套完整的图像处理链路把“找球—量球—判球”三件事串了起来而且代码量不大非常适合作为入门工业视觉检测的练手项目。我第一次接触这个例程的时候觉得它简单得有点过分但真正把它跑通、改到能适配自己手头的图像之后才发现里面藏着不少值得琢磨的细节。这个例程适合谁看如果你刚接触 Halcon想找一个有实际工业背景、又不至于太复杂的案例来练手ball.hdev是个很好的起点。如果你已经在做视觉检测但一直用模板匹配或者深度学习那一套想回头看看传统形态学方法能做到什么程度这个例程也能给你一些启发。它解决的核心问题是在灰度图像中如何稳定地提取出球焊点区域并对其几何参数进行量化评估。我打算把这个例程拆成几个层面来讲先讲整体思路和方案选型背后的考量再逐段拆解核心算子的用法和参数含义然后给出完整的实操流程和我自己改过的参数配置最后把我在实际调试中踩过的坑和排查经验整理出来。整个过程我会尽量用大白话解释让即使没怎么用过 Halcon 的人也能跟上。2. 核心算子与图像处理链路拆解2.1 为什么选择形态学区域分析而不是模板匹配拿到一张球焊图像最直觉的做法可能是模板匹配先做一个标准焊球的模板然后在图像里找相似区域。但ball.hdev没有走这条路它用的是阈值分割加形态学处理再加区域特征筛选。为什么这么选原因有几个。第一球焊点的灰度特征非常明显。在典型的背光或同轴光照明下焊球区域通常比背景亮得多或者比背景暗得多对比度足够高用简单的阈值就能把候选区域分出来。模板匹配反而显得笨重因为你要维护模板还要处理旋转和缩放。第二焊球的形状相对固定接近圆形或椭圆形用形态学算子可以很方便地做圆度筛选和面积筛选。模板匹配对形状变化的容忍度反而没那么好一旦焊球有轻微变形匹配分数就会掉得厉害。第三工业现场对速度要求高。阈值加形态学的计算量远小于模板匹配尤其是在高分辨率图像上这个差距会被放大。ball.hdev的处理链路基本是线性的没有迭代搜索单帧处理时间可以压得很低。当然这个方案也有前提光照要相对稳定背景不能太杂乱。如果现场光照波动大或者背景里有大量和焊球灰度接近的干扰物那单纯靠阈值就不够了需要加预处理或者换方案。这一点我在后面的排查部分会展开讲。2.2 图像预处理从灰度图到候选区域例程的第一步是读图。Halcon 里读图用read_image这个没什么好说的。读进来之后通常会先看一下图像的灰度分布决定阈值怎么取。ball.hdev里用的是threshold算子但阈值不是拍脑袋定的而是通过灰度直方图或者min_max_gray之类的算子先摸清楚图像的灰度范围。我自己的习惯是拿到一张新图先用gray_histo看一下直方图找到背景和前景之间的谷底位置把阈值定在谷底附近。如果谷底不明显说明对比度不够得回头调光源或者加滤镜而不是硬调阈值。阈值分割之后得到的是一个区域Region。这个区域里可能包含焊球也可能包含噪声、反光点、边缘毛刺。接下来就是形态学处理登场的时候。ball.hdev里用到的典型操作包括opening_circle或者closing_circle目的是把小的噪声点去掉同时把焊球区域内部的小孔洞填上。这里有个细节值得说结构元素的大小怎么选。opening_circle里的半径参数如果选得太小噪声去不干净选得太大小焊球可能被直接腐蚀掉。我的经验是先估算焊球在图像中的大致像素直径然后取这个直径的十分之一到八分之一作为结构元素半径。比如焊球直径大约 80 像素那半径取 8 到 10 比较合适。这个值不是绝对的要根据实际图像的分辨率和噪声水平微调。2.3 区域筛选把真正的焊球挑出来形态学处理之后图像里可能还剩下一堆区域。这时候需要用区域特征来筛选。ball.hdev里常用的特征包括面积area_center、圆度circularity、紧凑度compactness等。面积筛选最直接焊球的面积应该在一个合理的范围内。太小的是噪声太大的是多个焊球粘连或者背景干扰。圆度筛选则更进一步焊球的圆度应该接近 1。如果某个区域圆度很低说明它形状不规则很可能是干扰。这里有个实操技巧不要只用一个特征做筛选而是多个特征组合起来。比如先按面积筛一遍再按圆度筛一遍最后按位置筛一遍焊球应该在芯片焊盘的预期位置上。多级筛选的好处是每一级都可以设得相对宽松避免因为某一个特征波动就漏检同时组合起来又能保证准确率。ball.hdev里还用到了select_shape算子这个算子可以一次性指定多个特征和对应的范围用起来很方便。但要注意特征的范围不是一成不变的换一批图像可能就要重新调。我的做法是先在一批样本图上统计各个特征的分布取均值的正负三倍标准差作为初始范围然后再根据误检和漏检情况微调。2.4 几何量测直径、偏位与变形评估筛选出焊球区域之后接下来就是量测。ball.hdev里主要关注几个参数焊球直径、焊球中心位置、以及焊球的形状偏差。直径的测量通常用smallest_circle或者elliptic_axis。smallest_circle给出的是能包住区域的最小圆它的半径乘以二就是直径。elliptic_axis则给出等效椭圆的半轴长度可以用来评估焊球是否被拉长或压扁。中心位置用area_center就能拿到返回区域的面积和重心坐标。重心坐标和最小圆的圆心有时候会有细微差别如果焊球形状不规则这个差别可能更大。实际检验中用哪个作为中心取决于你的评判标准。如果关注的是焊球整体位置用重心更合理如果关注的是焊球最外轮廓的圆心用最小圆圆心更合适。形状偏差的评估一个常用指标是圆度另一个是等效椭圆的长短轴比。圆度接近 1 说明形状接近正圆长短轴比接近 1 也是同样的意思。如果长短轴比超过某个阈值比如 1.2就说明焊球被明显拉长了可能是焊接参数有问题。这些量测结果最终会和一个预设的公差范围比较超出范围就判为不合格。公差范围的设定需要结合工艺能力和产品要求不能拍脑袋。通常的做法是先收集一批合格品的量测数据算出均值和标准差然后把公差定在均值正负三到四倍标准差的位置。这样既能保证合格品不被误判又能把明显异常的品挑出来。3. 完整实操流程与参数配置3.1 环境准备与图像采集要点在跑ball.hdev之前有几件事得先准备好。首先是 Halcon 环境版本倒没有特别严格的要求但建议用 18.11 之后的版本因为一些形态学算子和区域特征算子在后续版本里有优化。安装好之后把例程文件ball.hdev加载进来确保能正常打开。图像采集这块虽然例程里用的是静态图片但实际项目中图像质量决定了整个检测链路的上限。球焊检验对光照的要求比较高理想的情况是用同轴光或者环形光让焊球表面反光均匀背景尽量暗且干净。如果焊球是金球颜色偏黄可以考虑加蓝色滤镜增强对比度。如果是铜球表面容易氧化发暗光照要更均匀一些。相机的分辨率选择也有讲究。假设焊球实际直径是 80 微米你希望测量精度达到 2 微米那理论上需要至少 40 个像素来覆盖焊球直径也就是相机在焊球位置的像素分辨率要达到 2 微米每像素。实际中为了留余量通常会选更高分辨率的相机或者加显微镜头。这个计算过程在方案设计阶段就要做不能等图像采回来才发现分辨率不够。提示图像采集阶段多花的时间会在后面的算法调试阶段成倍地省回来。光照和分辨率一旦定死后面再怎么调算法都很难突破物理限制。3.2 逐步拆解 ball.hdev 的核心代码段下面我按自己的理解把ball.hdev的核心逻辑用伪代码加注释的方式过一遍。注意这不是逐行照抄而是提炼出关键步骤方便你理解每一步在干什么。* 读取图像 read_image (Image, ball_bonding_sample) * 转灰度如果原图是彩色 rgb1_to_gray (Image, GrayImage) * 阈值分割提取亮区域 threshold (GrayImage, Regions, 120, 255) * 连通域拆分把粘连的区域分开 connection (Regions, ConnectedRegions) * 形态学开运算去噪填孔 opening_circle (ConnectedRegions, RegionOpening, 8.5) * 按面积筛选 select_shape (RegionOpening, SelectedRegions, area, and, 2000, 50000) * 按圆度筛选 select_shape (SelectedRegions, BallRegions, circularity, and, 0.85, 1.0) * 量测直径和中心 smallest_circle (BallRegions, Row, Column, Radius) area_center (BallRegions, Area, RowCenter, ColumnCenter) * 计算圆度用于评估 circularity (BallRegions, Circularity) * 输出结果或做进一步判断 * ...这段代码里threshold的阈值范围 120 到 255 是示例值实际用的时候要根据图像直方图来定。opening_circle的半径 8.5 也是示例对应的是焊球直径大约 80 到 100 像素的情况。select_shape里的面积范围 2000 到 50000圆度范围 0.85 到 1.0同样需要根据实际图像调整。我特别想强调connection这一步。如果阈值分割后多个焊球连在一起connection能把它们拆成独立区域。但如果粘连太严重connection也拆不开这时候就需要用watersheds或者基于距离变换的分水岭算法来做分割。ball.hdev里默认的场景是焊球之间有一定间距所以connection够用。如果你的图像里焊球挨得很近这一步可能要换方案。3.3 参数计算与选择过程参数选择是这个例程里最需要经验的部分。我拿一个实际调过的案例来说明。假设图像分辨率是 2448 乘 2048焊球在图像中的直径大约是 90 像素。那么阈值范围先看直方图背景灰度集中在 30 到 60焊球灰度集中在 180 到 240谷底在 110 左右。所以阈值下限取 110上限取 255。开运算半径焊球直径 90 像素取十分之一到八分之一也就是 9 到 11 像素。我先试 9发现噪声去不干净调到 10 之后效果稳定。面积范围焊球理论面积是 π 乘以 45 的平方约等于 6362 平方像素。考虑形状偏差和分割误差下限取 4000上限取 9000。如果图像里有多个焊球面积范围要覆盖所有焊球的可能大小。圆度范围合格焊球的圆度通常在 0.9 以上。我把下限设到 0.85留一些余量给轻微变形的合格品。如果误检多可以提高到 0.88 或 0.9。这些参数不是一次就能调好的。我的做法是准备一批样本图包含合格品和各种典型缺陷品然后写一个简单的脚本批量跑统计误检率和漏检率根据结果反过来调参数。这个过程可能要迭代五六轮但一旦调好后面换产品只要微调就行。3.4 结果输出与判定逻辑量测完之后判定逻辑其实很简单把每个焊球的直径、圆度、偏位和预设公差比较全部在范围内就判 OK有任何一项超差就判 NG。但实际项目中判定逻辑往往比这复杂。比如有些产品要求焊球直径必须在某个范围内但圆度可以稍微放宽有些产品对偏位要求很严但直径公差可以宽一些。这些都要根据具体产品的检验标准来定。ball.hdev里默认的输出是把结果显示在图像上用不同颜色标注 OK 和 NG 的焊球。实际产线上通常还要把结果传给 PLC 或者上位机触发分拣动作。这部分 Halcon 本身不直接做需要配合通信接口比如 TCP/IP、串口或者共享内存。我在实际项目中一般用 Halcon 做视觉处理然后把结果通过 TCP 发给上位机上位机再控制执行机构。注意判定逻辑里一定要留“不确定”这个状态。有些焊球的量测值刚好在公差边缘这时候直接判 OK 或 NG 都有风险。我的做法是设一个灰色区间落在灰色区间的焊球标记为待定由人工复检或者用更高精度的设备二次确认。4. 常见问题与排查技巧实录4.1 阈值分割不稳定怎么办这是最常见的问题。表现是同一批产品有些图能分出来有些图分不出来或者分割结果时好时坏。原因通常是光照波动或者产品表面状态变化。排查思路分几步走。先看图像本身的灰度分布如果背景和前景的灰度差本身就很小那说明光照不够得先解决光源问题。如果灰度差够大但阈值位置不好那就用直方图找谷底把阈值定在谷底附近。如果谷底不明显可以考虑用自适应阈值Halcon 里有dyn_threshold算子可以根据局部灰度动态调整阈值。还有一个技巧是用binary_threshold代替threshold这个算子能自动计算阈值适合背景和前景灰度分布比较规律的情况。但它也不是万能的如果图像里有多个灰度峰自动阈值可能选错这时候还是得手动指定。我自己的经验是如果一批图里超过百分之五的图需要单独调阈值那说明光照系统有问题应该回头改硬件而不是在算法上死磕。4.2 焊球粘连导致漏检或误判焊球粘连是指两个或多个焊球在图像里连成一片connection拆不开结果被当成一个大区域面积超上限被筛掉导致漏检。解决这个问题的思路有几个。一是用分水岭算法先对区域做距离变换找到各个焊球的“山峰”然后从山峰开始向外扩展把粘连区域分开。Halcon 里有watersheds和watersheds_threshold算子可以用。二是用基于形状的分割比如用gen_circle生成一个和焊球大小相近的圆然后在粘连区域里做模板匹配找到各个焊球的位置。三是如果粘连不严重可以调小开运算的半径让粘连处断开但这样可能会引入更多噪声。我实际用下来分水岭算法效果最稳但参数比较敏感距离变换的阈值和分水岭的合并阈值都要调。模板匹配的方法更直观但速度慢一些。具体选哪个看你的精度要求和速度要求。4.3 圆度筛选误杀合格品圆度筛选的本意是去掉形状不规则的干扰但有时候合格焊球的圆度也会偏低尤其是当焊球表面有反光或者图像有噪声的时候。遇到这种情况先别急着降圆度下限而是回头看看图像质量。如果焊球边缘有毛刺圆度自然就低。这时候可以用smooth_contours_xld或者fit_circle_contour_xld先对轮廓做平滑或者拟合再算圆度。这样得到的圆度更能反映焊球的真实形状而不是被噪声干扰。另一个思路是换特征。圆度对边缘噪声敏感但等效椭圆的长短轴比对噪声就没那么敏感。如果圆度筛选老是误杀可以试试用长短轴比来代替或者两个特征一起用取交集。4.4 处理速度跟不上产线节拍ball.hdev默认的处理链路在单张图上跑速度通常没问题。但如果图像分辨率很高或者焊球数量很多处理时间可能会超过产线节拍。提速的思路有几个。一是缩小处理区域如果焊球只出现在图像的某个固定区域可以用reduce_domain把处理范围限制在这个区域减少计算量。二是降低图像分辨率如果精度允许可以先做降采样再处理。三是优化算子参数比如开运算的半径不要设得太大连通域分析时可以用connection的加速版本。还有一个容易被忽略的点是图像传输时间。如果相机采集和处理的帧率不匹配瓶颈可能在传输环节而不是算法本身。这时候要考虑用更快的接口或者优化采集触发逻辑。4.5 常见问题速查表问题现象可能原因排查方向解决建议阈值分割不稳定光照波动或对比度不足检查直方图灰度分布调整光源或改用自适应阈值焊球粘连漏检焊球间距太小或分割过度观察连通域形状用分水岭或模板匹配分割圆度筛选误杀边缘噪声或反光干扰检查轮廓平滑度先平滑轮廓再算圆度处理速度慢图像分辨率高或区域过大统计各算子耗时缩小处理区域或降采样量测值波动大分割边缘不稳定对比多次量测结果优化阈值或加形态学平滑中心位置偏移焊球形状不规则对比重心和最小圆圆心根据检验标准选择合适中心4.6 我踩过的几个坑第一个坑是结构元素大小选得太随意。刚开始用opening_circle的时候我随手填了个 5结果噪声去不干净后面筛选压力很大。后来老老实实按焊球直径的十分之一来算效果立刻稳定了。这个教训是形态学参数不是拍脑袋定的要有依据。第二个坑是忽略了图像边缘的焊球。如果焊球刚好在图像边缘connection之后区域可能被截断面积和圆度都会异常。解决办法是在采集时留足够的边距或者在算法里对边缘区域做特殊处理比如用clip_region把边缘区域裁掉再处理。第三个坑是判定逻辑写得太死。一开始我把公差设得很紧结果合格品误判率很高。后来改成先统计一批合格品的量测分布再定公差误判率就降下来了。这个经验是公差要从数据来不能从感觉来。第四个坑是忘了考虑图像旋转。如果产品在视野里不是固定方向焊球的等效椭圆长短轴方向会变但长短轴比不变所以用长短轴比做筛选是安全的。但如果用方向相关的特征就要先做旋转校正。这个坑我在另一个项目里踩过后来养成了习惯凡是和方向相关的特征先确认图像方向是否固定。5. 从 ball.hdev 延伸出去的几个方向ball.hdev本身是个入门级例程但它涉及的图像处理链路——阈值分割、形态学、区域筛选、几何量测——是工业视觉检测的通用套路。把这套东西吃透很多类似的检测项目都能套用。比如焊线弧高检测思路类似只是量测的对象从焊球变成了弧线特征从圆度变成了曲率或者高度。再比如芯片表面缺陷检测也是先分割出缺陷区域再用面积、形状、灰度等特征做筛选和分类。区别在于缺陷的形态更复杂可能需要引入纹理分析或者频域分析。如果要把ball.hdev用到实际产线还有几件事要做。一是封装成可调用的函数或者过程把参数暴露出来方便换产品时快速调整。二是加异常处理比如图像读取失败、区域为空、量测结果异常等情况要有兜底逻辑。三是和产线通信对接把结果实时传出去。四是做长期稳定性验证收集一段时间的数据看误检率和漏检率是否稳定。我个人在实际操作中的体会是ball.hdev最大的价值不在于它本身能解决多少问题而在于它提供了一个清晰的框架让你知道一个完整的视觉检测项目应该包含哪些环节。从这个框架出发你可以往里填各种算子、各种特征、各种判定逻辑慢慢就形成了自己的工具箱。后面再遇到新的检测需求无非是换特征、换参数、换组合方式底层逻辑是不变的。最后再分享一个小技巧调试这类例程的时候不要一上来就改代码先把图像读进来用 Halcon 的变量窗口一步步看中间结果。阈值分割后区域长什么样开运算后噪声有没有去掉筛选后还剩几个区域每一步都看清楚了再决定改哪里。这样比盲目调参快得多也更容易积累经验。