一、点胶质量检测任务需求:

点胶检查检测以下缺陷
1.缺少粘合胶的部分(断胶)
2.粘合剂过多或过少的部分(溢胶、缺胶)
3.粘合胶离其预定位置太远(点胶偏移)

它主要用于检查工件上的胶路(如密封胶、导电胶等)是否符合标准。具体来说,它会检测胶路的宽度是否均匀(有没有过宽或变窄断胶)以及位置是否偏移(有没有涂偏)。

二、检测思路:

1.使用halcon的可变形模板匹配,将检测物品转正,方便检测
这里我们用的较少,因为我们平时在做点胶检测的时候,用普通的模板匹配,还有普通的2D仿射变换就可以保证图像的一致性。

2.定义粘合胶条的参考路径,以及胶条的宽度,还有误差容忍值
注意,这里halcon的官方例程是给了一个轨迹的点集,我们在实际的检测中,可以使用CAD图纸导入或者手绘路径,完成此步骤。

3.利用算子create_bead_inspection_model 创建点胶轨迹模型
创建点胶轨迹的模型,相当于我们使用模板匹配时候用到的穿件模板算子,也不难的。到第三步,我们已经完成了创建模板与点胶轨迹模型的步骤,准备工作都进行完毕,接下来就要进行检测了。

4.校正胎圈的位置,并生成四条平行轮廓,进行显示
显示一下前面准备工作的结果,包括胶轨迹的样子,可容许胶轨迹范围等。

5.读入待检测图像,并进行校正,最后利用算子apply_bead_inspection_model进行点胶轨迹检测
从这一步开始,就使用前面几部的模板,进行点胶轨迹的检测了。首先我们先把图片进行一个仿射变换,转正图片,让我们处理的点胶区域每次都保持一致。这样极大地减小了处理难度。再调用apply_bead_inspection_model进行点胶轨迹检测,就检测完成啦。是不是并不难。

6.根据不同的检测类型在窗口上进行相关显示
最后搞个交互界面显示一下结果就OK了啦。

三、手动绘制胶路轨迹:

第一阶段:建立检测标准(标定与建模)

  1. 图像仿射变换(视角校正): 读取标准参考图像后,利用 vector_to_proj_hom_mat2dprojective_trans_image 将原本可能带有倾斜视角的图像,通过4个点映射到一个标准的矩形平面上(消除透视畸变,保证后续测量的物理尺寸准确)。

  2. 提取定位模板区域: 通过二值化 (binary_threshold)、形态学开运算 (opening_circle) 去噪、连通域分析和特征筛选(提取面积最大的区域),把工件的有效区域完整抠出来,并做适当膨胀 (dilation_circle) 包含边缘。

  3. 创建形变匹配模板: 使用 create_planar_uncalib_deformable_model 创建一个基于轮廓的微变形匹配模板。这个模板的作用是:在实际生产中,工件放到相机下每次的位置都会有微小偏差,利用这个模板可以先把每次拍到的新图像**对齐(Align)**到标准位置。

  4. 生成标准胶路轨迹: 代码中间有一段被注释掉的 while 循环,那是用来支持工程师用鼠标在图像上“手动打点”描绘胶路轨迹的。现用代码直接给定了打好的点阵坐标(RowsCols),并通过 gen_contour_nurbs_xld 将这些离散的点拟合成一条平滑的 NURBS 曲线,作为标准胶路的中心线

  5. 创建胶路检测模型: 利用 create_bead_inspection_model 定义标准:胶路标准宽度设为 15,允许误差 ±7(即 8~22 是合格的),位置允许偏移 30。以此生成检测模型,并在四周生成平行的轮廓线用于后续的视觉辅助显示。

第二阶段:批量自动检测

6. 循环检测实拍图像: 通过 for 循环连续读取 7 张测试图(adhesive_bead_0107)。

7. 对齐与卡尺检测: 先调用 align_model 将当前测试图与第一步做的模板进行位置对齐。然后核心算子 apply_bead_inspection_model 上场,它会沿着设定的轨迹去寻找实际胶路的边缘。

8. 结果判定与可视化: 如果检测算子返回的 ErrorType 长度为 0,说明没有错误,屏幕打出绿色的“胶路质量正常”;如果检测到缺陷(过宽、过窄、断胶、偏离等),则打出红色的“胶路质量异常”,并用红色线条高亮标出出错的那一段胶路(ErrorSegment)。

** 获取检测区域
dev_get_window (WindowHandle)
** 读取图像
read_image (Image, '../素材/adhesive_bead_ref.png')
****** 使用投影变换 ***********
**1、设置原始变换的4个点
Row := [658, 296, 314, 691]
Col := [332, 320, 972, 952]
**2、设置投影变换的4个点
W := 6.1
H := 3.4
Row1 := 400+[H, 0, 0, H]*100
Col1 := 400+[0, 0, W, W]*100
**3、获取投影变换矩形
vector_to_proj_hom_mat2d (Row, Col, Row1, Col1, 'normalized_dlt', [], [], [], [], [], [], HomMat2D, Covariance)
**4、使用矩阵检测结果
projective_trans_image (Image, TransImage, HomMat2D, 'bilinear', 'false', 'false')
*********** 分割出模板区域 ***********
** 二值化操作
binary_threshold (TransImage, Region, 'smooth_histo', 'light', UsedThreshold)
** 开运算去毛刺
opening_circle (Region, RegionOpening, 7.5)
** 连通区域断开
connection (RegionOpening, ConnectedRegions)
** 计算所有区域中心
area_center (ConnectedRegions, Area, CRow, CColumn)
* tuple_sort_index (-Area, Indices)
** 筛选最大区域
select_obj (ConnectedRegions, ObjectSelected, sort_index(-Area)[0]+1)
** 填充孔洞
fill_up (ObjectSelected, RegionFillUp)
** 区域膨胀
dilation_circle (RegionFillUp, RegionDilation, 7.5)
******************** 创建筛选区域对应图像的模板********************
** 获取检测参照中心位置
area_center (RegionDilation, _, RowT, ColumnT)
** 获取检测的模板图像
reduce_domain (TransImage, RegionDilation, ImageReduced)
** 把锁定图像作为微变形模板图像
create_planar_uncalib_deformable_model (ImageReduced, 'auto', [], [], 'auto', 1, [], \
   'auto', 1, [], 'auto', 'none', 'use_polarity', 'auto', 'auto', [], [], ModelID)
*************** 让加载图像与其模板位置对齐******************
read_image (Image1, '../素材/adhesive_bead_01.png')
align_model (Image1, TransImage1, ModelID, RowT, ColumnT)
**************** 根据参考图像绘制点胶轨迹轮廓*******************
stop ()
** 定义存储点的行和列数组
* Rows := []
* Cols := []
* while (1)
    ** 获取鼠标点击位置
    ** 左键:1, 右键:4
*     get_mbutton (WindowHandle, Row, Column, Button)
*     if (Button == 1)
*         Rows :=[Rows, Row]
*         Cols :=[Cols, Column]
        ** 定义对象用来存储箭头区域
*         gen_empty_obj (ArrowObj)
*         if (|Rows| > 1 )
*             for i := 0 to |Rows|-2 by 1
                ** 生成箭头轮廓
*                 gen_arrow_contour_xld (Arrow, Rows[i], Cols[i], Rows[i + 1], Cols[i + 1], 10, 10)
*                 concat_obj(ArrowObj, Arrow, ArrowObj)
*             endfor
*         endif
*     else
*         if (Button == 4)
*             break
*         endif
*     endif
* endwhile
* stop ()
Rows:=[733, 718, 705, 694, 682, 669, 655, 642, 627, 614, 601, 586, 577, 567, 558, 548, 541, 532, 522, 511, 503, 494, 485, 479, 474, 469, 466, 460, 455, 451, 450, 448, 447, 446, 447, 447, 448, 450, 450, 451, 452, 455, 456, 460, 464, 470, 479, 487, 494, 504, 511, 517, 527, 538, 549, 558, 569, 577, 589, 597, 607, 618, 628, 638, 652, 660, 671, 683, 694, 702, 709, 719, 729]
Cols:=[417, 421, 424, 427, 431, 436, 439, 444, 452, 457, 463, 471, 479, 486, 493, 501, 507, 516, 526, 536, 546, 556, 567, 576, 586, 592, 599, 614, 629, 641, 650, 661, 674, 685, 698, 709, 724, 739, 751, 765, 776, 785, 794, 805, 815, 826, 838, 848, 855, 864, 872, 879, 888, 900, 909, 918, 927, 935, 941, 949, 955, 961, 967, 971, 976, 981, 987, 988, 991, 990, 988, 987, 986]

** 根据绘制数据生成点胶参考轮廓
gen_contour_nurbs_xld (ContourRef, Rows, Cols, 'auto', 'auto', 3, 1, 5)
******************* 创建基于胶路轮廓检测模板**********************
** 定义胶路标准宽度
TargetWidth := 15
** 定义可以容忍宽度范围[8~22]
WidthTolerance := 7 
** 定义胶路偏移范围
PositionTolernace := 30
** 定义筛选极性
Polarity := 'dark'
** 创建胶路模板
create_bead_inspection_model (ContourRef, TargetWidth, WidthTolerance, 30, \
                              Polarity, [], [], BeadInspectionModel)
tuple_type (BeadInspectionModel, Type)
stop ()
write_tuple (BeadInspectionModel, 'E:/gerry/1V1伴学/胶路质量检测/源码/11.tup')
read_tuple ('E:/gerry/1V1伴学/胶路质量检测/源码/11.tup',BeadInspectionModel)
****************** 创建平行轮廓来显示胶路宽度的范围******************
gen_parallel_contour_xld (ContourRef, WidthContour1, 'regression_normal', TargetWidth*0.5)
gen_parallel_contour_xld (ContourRef, WidthContour2, 'regression_normal', -TargetWidth*0.5)
concat_obj (WidthContour1, WidthContour2, WidthContours)

****************** 创建平行轮廓来显示胶路位置偏移的范围******************
gen_parallel_contour_xld (ContourRef, PositionContour1, 'regression_normal', PositionTolernace*0.5)
gen_parallel_contour_xld (ContourRef, PositionContour2, 'regression_normal', -PositionTolernace*0.5)
concat_obj (PositionContour1, PositionContour2, PositionContours)
******************* 开始检测胶路质量********************
set_display_font (WindowHandle, 20, 'mono', 'true', 'false')
for Index := 1 to 7 by 1
    ** 读取检测图像
    read_image (Image, '../素材/adhesive_bead_' + Index$'02')
    ** 把图像变换到参照位置
    align_model (Image, AlignImage, ModelID, RowT, ColumnT)
    ** 使用胶路模板检测质量
    apply_bead_inspection_model (AlignImage, LeftContour, RightContour, \
                                 ErrorSegment, BeadInspectionModel, ErrorType)
    ** 显示参照宽度平行线及偏移平行线
    dev_clear_window ()
    dev_display (AlignImage)
    dev_set_line_width (1)
    dev_set_color ('yellow')
    dev_display (ContourRef)
    dev_display (WidthContours)
    dev_display (PositionContours)
    ** 显示检测的胶路轮廓
    dev_set_color ('green')
    dev_set_line_width (2)
    dev_display (LeftContour)
    dev_display (RightContour)
    ** 显示错误轮廓
    dev_set_color ('red')
    dev_display (ErrorSegment)
    ** 判断并输出检测结果
    if (|ErrorType| == 0)
        dev_disp_text ('胶路质量正常', 'window', 20, 20, 'white', 'box_color', 'forest green')
    else
        dev_disp_text ('胶路质量异常', 'window', 20, 20, 'white', 'box_color', 'red')
    endif
endfor

这个检测过程依赖于机器视觉中的三个核心算法原理:

  1. 透视变换(Projective Transformation)原理: 现实中相机镜头如果不完全垂直于工件,拍出来的画面会有“近大远小”的透视畸变。代码利用单应性矩阵(Homography Matrix),通过数学空间映射,强行把图像“拉平”。这样在图像上计算的像素宽度,才能等比例代表真实的物理宽度。

  2. 模板匹配(Template Matching / Alignment)原理: 图像对齐阶段使用的是基于边缘梯度的形状匹配算法。系统提取模板图像边缘的梯度方向和幅值信息。在新图像中,它会在旋转、平移的一定范围内搜索,找到与模板特征重合度最高的区域,从而计算出偏移矩阵,把新图像“摆正”。

  3. 法向卡尺边缘检测(Bead Inspection)原理: apply_bead_inspection_model 的底层逻辑是沿着路径打卡尺”。

    • 系统会沿着你定义的 NURBS 轨迹线,每隔极小的距离,向轨迹的垂直方向(法线方向)**发射一条搜索线(Profile line)。

    • 在这条搜索线上,它寻找由暗到亮(或由亮到暗,取决于你设定的极性 Polarity := 'dark')的像素突变点,以此作为胶路的左边缘和右边缘。

    • 算出左右边缘后,两点之间的距离就是实际宽度,两点中心与标准轨迹的距离就是位置偏移。最后将这些实时数据与你设定的公差(WidthTolerance / PositionTolernace)进行比对,得出OK或NG的结论。

align_model 是如何实现对齐的?

这个封装函数内部主要经历了以下三个核心步骤(这也是机器视觉中经典的“定位+纠偏”流程):

  1. 特征搜索(找靶标):

    函数内部会调用 find_planar_uncalib_deformable_model 算子。它会在当前的“测试图片”中,寻找与“模板图片”相似的边缘轮廓和梯度特征。无论你的工件是平移了、旋转了,还是发生了轻微的形变,算法都会算出一个最高匹配度的位置坐标(Row, Column)以及变形矩阵。

  2. 计算空间偏差(算矩阵):

    拿到测试图片中工件的实际坐标后,算法会将其与之前记录的标准参照中心位置 RowT, ColumnT 进行比对。通过计算两者的差值,生成一个二维几何变换矩阵(Homogeneous Transformation Matrix)。这个矩阵记录了“需要把图像移动/旋转多少度,才能回到标准位置”。

  3. 图像仿射变换(空间纠偏):

    最后,利用 projective_trans_imageaffine_trans_image 等算子,将这个变换矩阵应用到整张测试图片上。这一步等于在内存中把拍歪的图片硬生生地“拽”回到了基准位置。输出的 AlignImage 就是对齐后的完美图像,此时之前画的那条参考轨迹就能严丝合缝地套在新图像上了。


对模板图片和测试图片的要求

要想让这种基于轮廓特征的对齐(模板匹配)百发百中,对图像有非常严格的物理和光学要求:

1. 对“模板图片”(建基准)的要求
  • 必须有稳定且锐利的边缘(Anchor Features): 模板不能是一块纯色或者模糊的区域。它必须包含工件上刚性、不变形、对比度高的结构(例如 PCB 板的边缘、定位孔、金属倒角等)。

  • 避开胶路本身作为模板:绝对不能用“胶水”本身去建立定位模板。因为胶水是流体,每次打出来的形状、粗细都不一样。如果你用胶水做模板,测试图里的胶水一旦变形,定位就直接失败了。必须用胶水周围坚硬的物理结构来定位

  • 光照均匀,无过度曝光: 模板图的特征轮廓越清晰,提取的边缘线质量越高,后续匹配的精度(通常能达到亚像素级)就越高。

2. 对“测试图片”(实际运行)的要求
  • 特征不可被严重遮挡: 机器在流水线上拍到的新图片,其用于定位的关键特征(比如定位孔)不能被异物、污渍或溢出的胶水完全遮挡。如果特征丢失过多,匹配分数会低于设定阈值,导致“找不到模板”。

  • 光照环境的一致性: 虽然 HALCON 的形状匹配对光照变异有一定的鲁棒性(尤其是开启了 'use_polarity' 参数后),但如果打光发生剧变(比如原本是黑底白边,突然变成了白底黑边,或者出现了强烈的局部反光),匹配依然会失效。

  • 在预设的容差范围内: 工件在视野里的偏移、旋转角度,必须在你创建模板时设定的范围之内。比如你限定了只能找旋转的工件,如果流水线上传来一个歪了的工件,系统就会报错。

将 Halcon 脚本导出为 C# 代码,仅仅是完成了“翻译”工作。要让它真正在实际设备上运行起来,还需要给它搭建一个完整的“舞台”——也就是上位机软件。

AdhesiveBeadDetection 这个标准的 C# 视觉项目中,导出的原生代码经历了彻底的工程化改造。整个整合过程通常分为以下四个核心环节:

1. 视觉窗口的替换与承载 (UI 层)

在 Halcon 脚本中,我们习惯使用 dev_display 来显示图像和轮廓。但在 C# 中,没有内置的 Halcon 窗口。

项目中引入了 MyControl/ZxHSmartWindowControl.cs 这样一个自定义控件。它的底层包装了 Halcon 提供的 HSmartWindowControl 组件。

  • 作用: 它被拖拽到 MainForm 主界面上,作为相机的“监视器”。所有原本通过 dev_display 显示的图像(AlignImage)、参考线(ContourRef)和缺陷标记(ErrorSegment),现在都会被发送到这个控件的句柄(Window Handle)上进行渲染,并且自带鼠标缩放、拖拽的功能。

2. 算法逻辑的面向对象封装 (逻辑层)

导出的 action() 函数往往是一长串意大利面条式的代码。在实际项目中,这些代码会被拆解并封装到独立的工具类中,比如项目中的 Tools/HalconTool.cs

这个类通常承担三个关键职责:

  • 初始化 (Init): 负责在软件启动时,读取提前保存好的模板文件(如 BeadInspectionModel.tup)和标准参考轮廓(refxld.dxf),而不是每次检测都重新建模。

  • 执行检测 (Inspect): 将原来的死循环或 for 循环拆掉,暴露出一个类似 public void InspectImage(HObject image) 的方法。每次相机传来一张新图片,或者用户点击“检测”按钮时,主程序就调用一次这个方法。

  • 内存释放 (Dispose): C# 虽然有垃圾回收,但 Halcon 的图像对象(HObject)占用的是非托管内存。HalconTool 必须严格管理 Dispose(),防止由于连续检测导致内存泄漏。

3. 参数的动态化与持久化 (数据层)

脚本里写死的参数(例如 TargetWidth := 15WidthTolerance := 7)在工业现场是行不通的,因为换线或微调时不可能重新编译代码。

  • 模型化: 项目里构建了 Model/Settings.cs 实体类,用来映射这些参数。

  • 用户配置: 配合 SettingsForm.cs(参数设置窗口),工程师可以在界面上直接修改标准宽度、容差等数值。

  • 存储与传递: 修改后的参数会被 Tools/JsonTool.cs 序列化保存到本地(如 settings.dat)。每次执行检测时,HalconTool 会读取这些最新的动态参数传入 ApplyBeadInspectionModel 算子中。

4. 主线程调度与状态反馈 (控制层)

MainForm.cs 扮演着“总指挥”的角色。它将用户的操作、视觉算法和界面反馈串联起来:

  • 当用户点击“加载图片”或“开始检测”时,MainForm 会触发事件。

  • 它负责从本地或相机获取图像,并将图像传递给 HalconTool 进行处理。

  • 拿到算法返回的结果后,MainForm 负责更新 UI 状态,比如在界面贴上绿色的“OK”或红色的“NG”标签,并可能通过 Tools/LogTool.cs 将检测记录(时间、结果、缺陷类型)写入日志文件。

通过这样的分层架构,原本孤立的一段数学计算脚本,就变成了一个具备人机交互、参数调节、日志追溯且运行稳定的工业级视觉软件。

四、一些和Gemini对话的问题

https://gemini.google.com/share/dba0733b6702

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐