简介:这份Halcon及视觉学习资料面向机器视觉初学者与工业自动化工程师,系统梳理了从基础概念到行业落地的完整知识脉络。内容涵盖Halcon图像处理库的核心算法,包括形状匹配、模板匹配、1D/2D码识别、OCR、测量与定位,并延伸至机器视觉概述、Blob分析、三维定位方法、标定与精确测量、编译环境编程,以及半导体与电子行业的晶圆检测、芯片识别、电路板检测等典型应用。资源以zip压缩包形式提供,整体约121.79MB,文件总数与类型明细上游暂未提供,但可预期包含文档、示例代码与图像素材等学习材料。目前已有673人学习下载,适合希望深入理解Halcon算法原理、掌握视觉项目开发流程、提升实际工程能力的读者参考,无论是入门者还是有经验的工程师,都能从中获得可复用的技术思路与行业案例。
1. Halcon 及视觉学习资料:从算子手册到产线落地的距离
很多人第一次接触机器视觉,是从一份 Halcon 及视觉学习资料开始的。下载下来几十个 PDF、一堆 .hdev 示例,翻了两天,算子查了不少,threshold、connection、select_shape也能背下来,但真把相机架到产线上,面对反光、抖动、光照漂移,还是不知道从哪下手。问题不在资料不够,而在于资料是按“算子字典”组织的,产线问题却是按“成像—分割—测量—判定”这条链路组织的。这篇笔记就按后者的顺序,把 Halcon 从环境搭建、图像采集、预处理、特征提取到标定与测量串一遍,中间给出可直接抄的代码和参数,也把我在现场踩过的坑摊开讲。适合刚转视觉的电气/机械工程师,也适合已经会用 OpenCV、想切到 Halcon 做高精度测量的熟手。
2. 把 Halcon 环境跑起来:版本、授权与第一个 .hdev
2.1 选版本和授权方式,别一上来就追新
Halcon 的版本迭代比较快,每年一个大版本。我的建议是:产线项目锁版本,学习阶段用当前主流稳定版即可。原因很现实——你写的 .hdev 在旧版本里可能因为算子签名变化直接报错,而产线设备一旦定型,换版本意味着重新验证。常见做法是:新项目用较新的稳定版,老设备维护就沿用原版本,不要为了“用上新算子”去动已经跑通的产线。
授权方面,Halcon 分开发授权和运行授权。学习阶段用评估授权就够,注意评估授权有有效期,到期后dev_display之类的基础算子还能用,但部分高级算子会受限。判断授权是否正常,最直接的方式是打开 HDevelop,看菜单里有没有报授权错误,或者跑一句:
* 查询当前授权状态与可用模块 get_system ('license_info', LicenseInfo) * 打印出来看是否包含你需要的高精度测量、3D 等模块 disp_message (3600, LicenseInfo, 'window', 12, 12, 'black', 'true')逻辑说明:get_system是 Halcon 读取系统级信息的通用入口,'license_info'这个键返回当前授权包含的模块清单。参数说明:第一个参数是查询键,第二个是接收变量。如果你要做亚像素测量却发现结果只有整像素精度,先回来查这里,很可能是测量模块没授权,而不是算法写错了。
2.2 图像采集:从 open_framegrabber 到异步抓取
Halcon 采集图像的核心算子是open_framegrabber。它支持的接口很多,GigE、USB3、CameraLink 都有对应驱动。新手最容易卡在“相机能出图但 Halcon 抓不到”,八成是接口名或相机参数没对上。
* 打开 GigE 相机,接口名和参数需按实际相机调整 open_framegrabber ('GigEVision2', 0, 0, 0, 0, 0, 0, 'progressive', -1, 'default', -1, 'false', 'default', 'cam0', 0, -1, AcqHandle) * 设置曝光和增益,单位与相机固件有关,先小步试 set_framegrabber_param (AcqHandle, 'ExposureTime', 8000) set_framegrabber_param (AcqHandle, 'Gain', 2.0) * 抓一帧 grab_image (Image, AcqHandle)逻辑说明:open_framegrabber的参数顺序是固定的,接口名、水平/垂直偏移、宽高、起始场、位深、色彩空间、相机类型、设备名等。参数说明:'GigEVision2'是接口名,'cam0'是设备标识,具体值要用 Halcon 自带的采集助手去枚举,不要凭记忆填。ExposureTime单位通常是微秒,Gain是无量纲倍数,这两个值直接决定图像亮度和噪声,建议先用采集助手实时调,再写进代码。
如果要做连续检测,别用grab_image一帧一帧同步抓,改用异步抓取,否则采集和处理的耗时叠加,节拍会崩:
* 异步采集,先启动再取图,适合连续检测 grab_image_async (Image, AcqHandle, -1) * 处理完再取下一帧,-1 表示用上次的异步请求 grab_image_async (Image, AcqHandle, -1)逻辑说明:第一次调用发起异步请求,第二次调用取回结果。参数说明:最后一个参数是异步句柄,-1 表示沿用当前请求。这样采集和处理可以部分重叠,节拍能压下来。注意异步模式下如果处理太慢,会丢帧,需要配合相机缓存或降低分辨率。
2.3 第一个可复现的 .hdev 骨架
不管做什么项目,我都会先搭一个骨架:读图/采集、预处理、分割、特征、显示。这样换项目时只改中间几步。
* 1. 读取图像(学习阶段用文件,产线换成采集) read_image (Image, 'part_01.png') * 2. 转灰度,彩色相机才需要 rgb1_to_gray (Image, GrayImage) * 3. 预处理:抑制噪声,保留边缘 median_image (GrayImage, MedianImage, 'circle', 2, 'mirrored') * 4. 分割:阈值提取亮区 threshold (MedianImage, Region, 120, 255) * 5. 连通域拆分 connection (Region, ConnectedRegions) * 6. 按面积筛选 select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 500, 99999) * 7. 显示 dev_display (Image) dev_display (SelectedRegions)逻辑说明:median_image用圆形核做中值滤波,对椒盐噪声和细小毛刺很有效;threshold是全局阈值,适合光照均匀的场景;connection把连通区域拆成独立对象;select_shape按面积过滤掉噪点。参数说明:中值滤波的核大小 2 表示半径,越大越平滑但边缘越钝;阈值 120~255 需要按实际灰度直方图调,不要照抄。这套骨架跑通,你就有了一个能改的起点。
3. 预处理与分割:为什么你的阈值总在飘
3.1 光照不均时,全局阈值必然翻车
产线上最常见的问题就是光照不均——中间亮、边缘暗,或者工件表面有反光。这时候用全局threshold,阈值定高了边缘丢,定低了背景进来,怎么调都不对。血泪经验是:先解决成像,再谈算法。加个漫射板、换个环形光、调一下相机角度,往往比在代码里折腾半天管用。
如果硬件已经定型,那就上动态阈值。Halcon 里常用的是dyn_threshold,它用一个参考图(通常是原图大核滤波后的结果)做局部比较:
* 用大核均值滤波得到背景参考 mean_image (GrayImage, MeanImage, 31, 31) * 动态阈值:原图比背景亮 offset 以上才算目标 dyn_threshold (GrayImage, MeanImage, RegionDyn, 8, 'light')逻辑说明:mean_image的核越大,背景估计越平滑,越能代表光照趋势;dyn_threshold逐像素比较原图和参考图,差值超过 offset 的判为目标。参数说明:核大小 31 要大于目标尺寸,否则目标本身会被当成背景;offset 取 5~15 比较常见,太小会引入噪声,太大会漏掉弱边缘。这个算子的代价是慢,核越大越慢,产线节拍紧的话要权衡。
3.2 边缘提取:亚像素精度从哪来
做尺寸测量,整像素边缘是不够的,必须上亚像素。Halcon 的edges_sub_pix是主力算子,它基于 Canny 或 Deriche 等滤波器,输出的是亚像素精度的轮廓。
* 亚像素边缘提取,alpha 控制平滑,低阈值和高阈值控制边缘强度 edges_sub_pix (GrayImage, Edges, 'canny', 1.0, 20, 40) * 按长度筛选,去掉碎边 select_shape_xld (Edges, LongEdges, 'contlength', 'and', 30, 99999)逻辑说明:edges_sub_pix输出的是 XLD 轮廓,不是区域,后续要用 XLD 系列算子处理。参数说明:'canny'是滤波器类型,alpha是平滑系数,越大越平滑、定位越粗,一般 0.8~1.5;低阈值 20 和高阈值 40 是滞后阈值,低于低阈值的直接丢,高于高阈值的保留,中间的看是否连到强边缘。这两个阈值对结果影响极大,建议用 HDevelop 的变量检查窗口边调边看。
拿到亚像素轮廓后,拟合直线或圆:
* 把轮廓拟合成直线,得到亚像素级的直线参数 fit_line_contour_xld (LongEdges, 'tukey', -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)逻辑说明:fit_line_contour_xld用鲁棒算法拟合直线,输出起点、终点和法向量。参数说明:'tukey'是加权方式,对离群点不敏感;-1表示自动迭代;5是最大迭代次数;2是剔除阈值。拟合出来的Dist是点到直线的最大距离,可以用来判断拟合质量,太大说明轮廓不干净。
3.3 形态学与区域处理:别把好区域修没了
分割出来的区域往往有毛刺、孔洞、粘连。形态学能修,但用过头会把真实特征也修掉。常见做法是:先opening_circle去毛刺,再closing_circle补孔洞,核大小从 1.5 起步试。
* 开运算去小毛刺,核半径 1.5 opening_circle (RegionDyn, RegionOpen, 1.5) * 闭运算补小孔,核半径 2.5 closing_circle (RegionOpen, RegionClose, 2.5) * 填充孔洞 fill_up (RegionClose, RegionFilled)逻辑说明:开运算是先腐蚀后膨胀,去掉比核小的突出;闭运算是先膨胀后腐蚀,填掉比核小的凹陷。参数说明:核半径直接对应能处理的毛刺/孔洞尺寸,半径 1.5 能处理约 3 像素宽的毛刺。注意:如果目标本身有小于核尺寸的真实特征,会被一起修掉,所以核大小必须小于最小真实特征。
4. 标定与测量:把像素变成毫米的那一步
4.1 标定板选型和标定流程
像素测量要转成物理尺寸,必须标定。Halcon 支持多种标定板,常见的是圆点阵列和棋盘格。选型原则:视野越大,标定板要越大,且标定板要覆盖整个视野。如果标定板只占视野一角,边缘的畸变校正会不准。
标定流程分三步:采集多张不同姿态的标定板图像、提取标定点、计算相机参数。
* 创建标定数据模型,指定标定板描述文件 create_calib_data ('calibration_object', 1, 1, CalibDataID) * 设置相机类型和初始参数 set_calib_data_cam_param (CalibDataID, 0, 'area_scan_division', [0.016, 0, 0, 0, 0, 0, 0, 0]) * 设置标定板描述文件 set_calib_data_calib_object (CalibDataID, 0, 'calplate_20mm.descr') * 对每张图找标定点并加入模型 find_calib_object (Image, CalibDataID, 0, 0, 0, [], []) * 标定 calibrate_cameras (CalibDataID, Error)逻辑说明:create_calib_data建立标定模型;set_calib_data_cam_param设置相机内参初值,area_scan_division是带畸变的面阵相机模型;find_calib_object自动提取标定点;calibrate_cameras做优化。参数说明:[0.016, 0, 0, 0, 0, 0, 0, 0]里第一个是像元尺寸(米),后面是焦距和畸变初值,初值不准没关系,优化会调,但不能全填 0。Error是标定残差,一般要求小于 0.1 像素,大了说明标定板姿态不够丰富或图像模糊。
4.2 把像素测量转成毫米:image_points_to_world_plane
标定完成后,用image_points_to_world_plane把图像坐标映射到物理平面。前提是测量平面和标定平面一致,否则会有透视误差。
* 把图像点映射到世界平面,单位由标定板描述文件决定 image_points_to_world_plane (CamParam, Pose, Row, Col, 'mm', X, Y)逻辑说明:CamParam是标定得到的相机参数,Pose是标定板在世界坐标系中的位姿,Row、Col是图像坐标,X、Y是输出的物理坐标。参数说明:'mm'是输出单位,要和标定板描述文件一致。注意:如果测量对象不在标定平面上,需要先做平面校正或使用远心镜头减小透视误差。
4.3 测量结果的重复性验证
标定完不代表测出来就准。我一般会做重复性验证:同一工件测 30 次,看标准差。如果标准差大于公差的三分之一,说明系统不稳定,要回去查光源、相机固定、工件定位。
* 循环测量同一特征,记录结果 for i := 1 to 30 by 1 grab_image (Image, AcqHandle) * ... 分割、拟合、映射 ... * 把结果存入数组 Result[i] := Distance endfor * 算均值和标准差 tuple_mean (Result, Mean) tuple_deviation (Result, Deviation)逻辑说明:tuple_mean和tuple_deviation是 Halcon 的元组统计算子。参数说明:Result是测量结果数组,Deviation是标准差。如果标准差偏大,优先查机械固定和光源稳定性,而不是改算法。
5. 避坑与排查:那些让我返工的细节
5.1 现象:阈值调好了,换一批工件就失效
原因:光照随环境温度、灯管老化漂移,全局阈值对亮度变化敏感。解决:改用动态阈值,或者加一个自动曝光/自动增益的闭环,让图像平均灰度稳定在目标值附近。我一般会在采集后加一句intensity统计,超出范围就报警。
5.2 现象:亚像素边缘抖动大,重复性差
原因:图像本身噪声大,或者边缘对比度低。edges_sub_pix对噪声敏感,alpha 太小会把噪声当边缘。解决:先做适度平滑(gauss_filter或median_image),再提边缘;同时提高打光对比度,让边缘更陡。alpha 从 1.0 往上调,直到重复性达标。
5.3 现象:标定残差很小,但测量偏差大
原因:标定平面和测量平面不重合,存在透视误差;或者标定板描述文件里的圆点间距填错了。解决:确认测量对象和标定板在同一高度;核对描述文件里的间距和单位。远心镜头能显著减小这类误差,但成本高。
5.4 现象:程序在开发机上跑得好,到产线就卡
原因:开发机用了文件读图,产线是实时采集,采集和处理串行导致节拍不够;或者显示窗口刷新拖慢了主循环。解决:改用异步采集;把dev_display从主循环里拿掉,或者只在调试时开;用count_seconds测各段耗时,找出瓶颈。
5.5 现象:换相机后所有参数都要重调
原因:不同相机的像元尺寸、色彩响应、曝光曲线都不同。解决:把相机相关参数(像元尺寸、曝光、增益、白平衡)抽成配置文件,换相机只改配置,不动算法。标定必须重做,因为内参变了。
6. 进阶:把 Halcon 测量做成可复用的检测框架
学到这里,你已经能跑通单次测量。但产线要的是稳定、可维护、能换型的检测框架。我的做法是把整个流程拆成配置层、采集层、算法层、判定层四块,用 Halcon 的字典(dict)存配置,用过程(procedure)封装算法。
配置层用create_dict和set_dict_tuple存阈值、核大小、标定文件路径等:
* 创建配置字典 create_dict (Config) set_dict_tuple (Config, 'threshold_low', 120) set_dict_tuple (Config, 'threshold_high', 255) set_dict_tuple (Config, 'median_radius', 2) set_dict_tuple (Config, 'calib_file', 'calplate_20mm.descr') * 从字典读参数 get_dict_tuple (Config, 'threshold_low', ThreshLow)逻辑说明:字典是键值对容器,适合存异构配置。参数说明:set_dict_tuple的第三个参数可以是任意元组,读的时候用get_dict_tuple。这样换型时只改字典,算法过程不动。
算法层把分割、拟合、映射封装成过程,输入图像和配置,输出测量结果和判定:
* 过程签名示例(在 HDevelop 中定义为 procedure) * MeasurePart (Image, Config, Result, IsOK) * 内部按配置执行分割、拟合、映射 * 判定:结果在公差内则 IsOK 为 true逻辑说明:过程封装后,主程序只负责采集和调用,逻辑清晰,也方便多人协作。参数说明:Config是配置字典,Result是测量值,IsOK是布尔判定。判定阈值也放在配置里,不要硬编码。
验证方法上,我习惯做两件事:一是用标准件跑 100 次,统计均值和标准差,确认重复性;二是故意换一批有轻微差异的工件,看判定是否合理,避免过杀和漏杀。最后说个我自己的教训:早期我总想把算法调得“万能”,结果参数越加越多,维护成本爆炸。后来学乖了,先把成像和机械做稳,算法只做最后那 20% 的事,反而更省心。希望帮到你。
本文还有配套的精品资源,点击获取