Halcon联合C#点胶质量检测
一、点胶质量检测任务需求:
点胶检查检测以下缺陷
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了啦。
三、手动绘制胶路轨迹:
第一阶段:建立检测标准(标定与建模)
-
图像仿射变换(视角校正): 读取标准参考图像后,利用
vector_to_proj_hom_mat2d和projective_trans_image将原本可能带有倾斜视角的图像,通过4个点映射到一个标准的矩形平面上(消除透视畸变,保证后续测量的物理尺寸准确)。 -
提取定位模板区域: 通过二值化 (
binary_threshold)、形态学开运算 (opening_circle) 去噪、连通域分析和特征筛选(提取面积最大的区域),把工件的有效区域完整抠出来,并做适当膨胀 (dilation_circle) 包含边缘。 -
创建形变匹配模板: 使用
create_planar_uncalib_deformable_model创建一个基于轮廓的微变形匹配模板。这个模板的作用是:在实际生产中,工件放到相机下每次的位置都会有微小偏差,利用这个模板可以先把每次拍到的新图像**对齐(Align)**到标准位置。 -
生成标准胶路轨迹: 代码中间有一段被注释掉的
while循环,那是用来支持工程师用鼠标在图像上“手动打点”描绘胶路轨迹的。现用代码直接给定了打好的点阵坐标(Rows和Cols),并通过gen_contour_nurbs_xld将这些离散的点拟合成一条平滑的 NURBS 曲线,作为标准胶路的中心线。 -
创建胶路检测模型: 利用
create_bead_inspection_model定义标准:胶路标准宽度设为 15,允许误差 ±7(即 8~22 是合格的),位置允许偏移 30。以此生成检测模型,并在四周生成平行的轮廓线用于后续的视觉辅助显示。
第二阶段:批量自动检测
6. 循环检测实拍图像: 通过 for 循环连续读取 7 张测试图(adhesive_bead_01 到 07)。
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
这个检测过程依赖于机器视觉中的三个核心算法原理:
-
透视变换(Projective Transformation)原理: 现实中相机镜头如果不完全垂直于工件,拍出来的画面会有“近大远小”的透视畸变。代码利用单应性矩阵(Homography Matrix),通过数学空间映射,强行把图像“拉平”。这样在图像上计算的像素宽度,才能等比例代表真实的物理宽度。
-
模板匹配(Template Matching / Alignment)原理: 图像对齐阶段使用的是基于边缘梯度的形状匹配算法。系统提取模板图像边缘的梯度方向和幅值信息。在新图像中,它会在旋转、平移的一定范围内搜索,找到与模板特征重合度最高的区域,从而计算出偏移矩阵,把新图像“摆正”。
-
法向卡尺边缘检测(Bead Inspection)原理:
apply_bead_inspection_model的底层逻辑是沿着路径打卡尺”。-
系统会沿着你定义的 NURBS 轨迹线,每隔极小的距离,向轨迹的垂直方向(法线方向)**发射一条搜索线(Profile line)。
-
在这条搜索线上,它寻找由暗到亮(或由亮到暗,取决于你设定的极性
Polarity := 'dark')的像素突变点,以此作为胶路的左边缘和右边缘。 -
算出左右边缘后,两点之间的距离就是实际宽度,两点中心与标准轨迹的距离就是位置偏移。最后将这些实时数据与你设定的公差(WidthTolerance / PositionTolernace)进行比对,得出OK或NG的结论。
-
align_model 是如何实现对齐的?
这个封装函数内部主要经历了以下三个核心步骤(这也是机器视觉中经典的“定位+纠偏”流程):
-
特征搜索(找靶标):
函数内部会调用
find_planar_uncalib_deformable_model算子。它会在当前的“测试图片”中,寻找与“模板图片”相似的边缘轮廓和梯度特征。无论你的工件是平移了、旋转了,还是发生了轻微的形变,算法都会算出一个最高匹配度的位置坐标(Row, Column)以及变形矩阵。 -
计算空间偏差(算矩阵):
拿到测试图片中工件的实际坐标后,算法会将其与之前记录的标准参照中心位置
RowT, ColumnT进行比对。通过计算两者的差值,生成一个二维几何变换矩阵(Homogeneous Transformation Matrix)。这个矩阵记录了“需要把图像移动/旋转多少度,才能回到标准位置”。 -
图像仿射变换(空间纠偏):
最后,利用
projective_trans_image或affine_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 := 15、WidthTolerance := 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对话的问题
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)