资讯详情

资讯详情

Halcon圆孔检测完整方案:亚像素边缘提取、标定测量与故障排查实例

干工业视觉这行圆孔检测是躲不开的基础场景。手机中框上的螺丝孔、汽车零部件上的安装孔、PCB板上的定位孔甚至包装药板上的泡罩孔本质上都在做同一件事把图像里的圆找出来然后把直径、圆心坐标、圆度、孔间距这些参数测准。我最早接触 Halcon 就是因为一个来料检项目工件上均匀分布了十几个圆孔客户要求既能测单孔直径又能查孔位偏移还得把破孔堵孔这种缺陷挑出来。当时查了不少资料也踩了不少坑后来逐步把这些需求整理成了一套相对完整的检测流程。这篇就把我的做法、算子用法和排错经验一起写出来给正在调试圆孔检测的朋友做个参考。1. 需求拆解与检测方案整体设计1.1 圆孔检测到底要测什么、治什么很多刚入行的朋友以为圆孔检测就是“找圆、量直径”真到现场就会发现需求远不止这些。我做过一个统计工业现场对圆孔的需求可以分成四类尺寸测量类单孔直径、圆弧半径、孔深配合景深或结构光、沉孔台阶尺寸。这类需求最看重绝对精度往往要换算到毫米级。位置关系类圆心坐标、孔间距、阵列孔的位置度、同心度内外圆圆心偏差。手机、3C和汽车零部件上这类需求最多。形状判定类圆度圆形度、椭圆度、边缘破损、缺口、崩边。这类是对“圆”本身质量的评价单纯拟合出直径还不够。完整性检查类堵孔、半孔、毛刺残留、内部异物遮挡。注塑件和压铸件上特别常见孔没打穿或者被飞边遮住全靠灰度特征判断。所以拿到需求先不要急着写算子把“检测项”表格列清楚每个检测项的量程、公差、单位是毫米还是像素、是绝对测量还是相对比较。这一步决定了后面选相机分辨率、选光源、选算子的方向。我见过不少项目做到一半才发现要测的东西根本不是圆而是倒角轮廓那种返工非常痛苦。1.2 为什么用Halcon而不是其他视觉库每次聊到圆孔检测总有人问为什么不用 OpenCV。两个库我都实际用过说实话OpenCV 也能做但 Halcon 在这个领域确实有几个明显优势亚像素边缘提取更稳定。Halcon 的edges_sub_pix和fit_circle_contour_xld这套组合是专门为工业测量设计的边缘提取和圆拟合的精度、稳定性在实测中优于 OpenCV 的普通 Canny 加最小二乘拟合。尤其是圆弧不完整、边缘有噪点时差距更明显。卡尺测量工具开箱即用。measure_pos、measure_pairs这类算子把“沿着某个方向扫灰度梯度”这件事封装好了你用 OpenCV 得自己写采样、插值、求极值用 Halcon 直接拖参数就行开发效率高一大截。HDevelop 交互式调试非常顺手。变量窗口实时看图、ROI 随手画、参数即时生效调视觉比写代码快得多。配合run按钮逐行跑处理哪一步出了问题一眼就能定位。当然 Halcon 授权贵按开发机和运行时分别收费项目量小的时候确实肉疼。但如果你做的是设备里的视觉模块一个项目开发周期省下来的时间成本往往超过授权费。再说直白点工业客户认这个软件后续维护和二次开发文档也齐全算是视觉工程师的“标准装备”。1.3 检测流程怎么搭才不容易乱我把圆孔检测拆成六个环节每个环节都有明确的目标图像采集保证圆的轮廓清晰、对比度高、亮度均匀这是所有后续步骤的基础。标定建立像素坐标和物理坐标毫米/微米的映射关系让测量结果可以直接对公差。ROI 定位在图像中固定圆孔可能出现的大致区域减少处理范围同时避免其他干扰。边缘提取与圆拟合用亚像素方法找到圆的轮廓点再拟合成圆得到圆心和半径。结果判定根据标定后的尺寸、位置、形状数据与公差上下限比较输出 OK/NG。数据输出与交互把结果传给 PLC、显示界面或数据库保存检测图片和日志。这六步不是每次都全要。比如纯做缺陷检测、不需要精确尺寸时可以略过标定直接用像素特征判断固定位置拍照、工件由治具卡死时ROI 可以不动态定位写死坐标就行。但原理上有个共同点前面的环节给后面提供输入前面差一点后面就差一大截。标定不准拟合再准也是白搭光照不稳定阈值稍微挪一点边缘就跑好几个像素。2. 图像采集与预处理环节2.1 光源和相机怎么选才不吃亏圆孔检测里光源选型的影响比很多人想象的大。我以前也吃过暗亏用环形光打一个有光泽的金属工件孔口周围出现一圈高光边缘严重过曝卡尺测量时梯度方向反了直径结果忽大忽小。后来换成低角度背光瞬间整个世界清净了。我的经验是分场景选光源测尺寸、测孔径、测位置优先用背光平行背光或面光源工件放在光源和相机之间孔的位置是亮的背景是暗的或反过来边缘对比度最强灰度梯度陡峭亚像素提取最准。测表面缺陷、看孔口毛刺用环形光或同轴光把表面纹理和孔口特征照出来这时候孔本身往往不是纯黑或纯白边缘会有丰富细节。透明或半透明材料玻璃、薄膜用红外光或特定波长的光源避免内部散射造成伪边缘。相机的选择跟精度直接挂钩。行业里有个粗略公式像素精度 视场宽度 / 相机横向分辨率要命的是测量精度至少是像素精度的 3 到 5 倍实际还要考虑边缘提取的重复性。举个例子检测直径 5 mm 的孔公差 ±0.02 mm视场覆盖 20 mm 宽的工件区域。像素精度至少要 0.005 mm/pixel 才算保险那么横向分辨率就是 20 / 0.005 4000 像素所以选 500 万像素级别的工业相机比较合适。如果公差只有 ±0.05 mm200 万像素也能凑合。核心原则是用分辨率换精度余量不要卡着下限选。2.2 预处理到底要做哪几步很多人拿到图像先是一顿滤波器乱打结果把边缘搞糊了亚像素精度反而下降。我的原则是“预处理最小化”只要对比度够、噪声不算离谱能不做就不做非做不可就做最轻的。灰度化彩色相机拍的图先转灰度Halcon 里rgb1_to_gray或image_to_channels取单通道。去噪高斯滤波gauss_filter或中值滤波median_image核选 3x3 或 5x5目的是去掉传感器噪声但不能把边缘梯度削平。我一般只在边缘太碎时轻度使用。对比度增强scale_image或equ_histo_image做灰度拉伸。注意别把灰度直方图拉过头否则有可能把中间调的灰度跳变变成伪边缘。阴影校正光照不均匀时用背景图做除法校正。拍一张没有工件的光场图作为背景然后background_seg或illuminate分离背景、再做校正。实际现场光源衰减、反光板脏了这一步能救回来很多画面。有个非常实用的排查技巧任何预处理做完之后把结果图和原图叠在一起看边缘确认边缘位置没有发生肉眼可见的偏移。预处理引入的“假边缘”或“边缘位移”往往是后续测量结果系统偏大或偏小的真凶。2.3 标定像素怎么变成毫米尺寸测量类圆孔检测标定这关绕不过去。最简单的办法是拍一个已知尺寸的标定物比如高精度的陶瓷棋盘格或圆点标定板算出一个“像素/毫米”的比例系数。但更推荐做完整的相机标定用calibrate_cameras或者 HDevelop 的 Calibration 助手一次性消除镜头畸变和透视误差。我遇到过的情况是把工件放在视野边缘时测出来的圆明显变形圆度指标怎么都调不好。后来发现是镜头畸变没有校正边缘位置的圆被拉成了椭圆。用标定助手加载标定板图片、设置好相机内参初始值、板子尺寸和标记点间距就能算出畸变系数。后面所有测量图先change_radial_distortion_image做校正再走检测流程圆度立刻恢复正常。标定有几个容易忽视的小细节标定板必须尽量与工件表面在同一焦平面否则标定结果对特定物距才成立。标定板要覆盖整个视野四种角度各拍几张别只拍正对一张。标定完成后用标定板本身验证一下实测标定板上已知间距误差应在 0.01 mm 量级。如果客户只要求相对检测比如“孔间距是否比标准件大”不需要绝对尺寸标定可以简化成只算一个比例系数甚至直接在像素空间比较。但只要是出检测报告、可追溯尺寸的就必须走完整标定。3. 圆孔定位与测量的核心实现3.1 阈值分割与Blob分析先找到圆的大概位置圆孔检测里第一步通常是找到每个孔的大致位置然后才做精细测量。这一步我习惯用“阈值分割 Blob 分析”快速搞定。以背光场景为例孔是亮的、背景是暗的用threshold就能把亮区域分出来read_image (Image, shell_holes.png) threshold (Image, Regions, 128, 255) connection (Regions, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, area, and, 500, 99999)threshold的上下限不能拍脑袋定。我一般先打开灰度直方图看看孔区域和背景区域的灰度峰值分别在哪取两个峰谷之间作为阈值。早期项目图采得多了也可以直接让程序动态取阈值min_max_gray (Regions, Image, 0, Min, Max, Range)connection把连在一起的区域拆成独立的连通域这是 Blob 分析的核心。select_shape再按面积、圆度、长宽比等特征筛掉非孔区域。圆孔一般面积在某个范围、圆度接近 1可以多写几个特征条件组合筛选。如果孔内部有高光或脏点导致空洞可以用fill_up或先opening_circle再closing_circle做形态学修补。这一步只是粗定位修过头一点没关系。Blob 分析虽然简单但特别好使一次遍历就能把几十个孔全找出来还能把面积、中心坐标这些特征顺手统计好。后面精细测量只需要对每个找到的 ROI 单独处理运算量小很多速度也快很多。3.2 亚像素边缘提取与卡尺测量粗定位之后圆的轮廓还不能直接用因为阈值分割得到的边缘是像素级的测量精度不够。工业检测要做到亚像素常用两条路亚像素轮廓提取或者卡尺灰度梯度测量。亚像素轮廓提取的标准打法是这样reduce_domain (Image, SelectedRegions, ImageReduced) edges_sub_pix (ImageReduced, Edges, canny, 1.5, 10, 40) select_contours_xld (Edges, SelectedContours, contour_length, 20, 200, -0.5, 0.5)edges_sub_pix的参数里1.5是高斯滤波的 Sigma控制边缘平滑度10和40是滞后阈值的低高阈值决定哪些边缘点会被保留。Sigma 太大边缘会钝太小噪声点多。铝合金、钢件表面一般 1.5 到 2 比较稳妥塑料件和暗色橡胶件可以降到 1 左右。卡尺测量是另一种方式也是我认为 Halcon 最值钱的功能。measure_pos、measure_pairs这些算子可以沿着一条直线或一个环形区域扫灰度变化找到灰度梯度的峰值位置再利用像素灰度做插值得到亚像素坐标。对圆孔来说可以创建等间距分布的多个卡尺每个卡尺从孔中心向外扫描找到内外边缘然后统一拟合圆gen_measure_arc (CenterRow, CenterCol, Radius, AngleStart, AngleEnd, Width, Height, Interpolation, MeasureHandle) measure_pairs (Image, MeasureHandle, 2, 1, negative, all, RowEdgeFirst, ColEdgeFirst, AmpFirst, RowEdgeSecond, ColEdgeSecond, AmpSecond) close_measure (MeasureHandle)用卡尺时最关键的参数是边缘阈值和极性。阈值设小了会把反光点、毛刺当成边缘阈值设大了会丢掉弱边缘尤其是孔口有倒角的地方。极性negative或positive决定了检测的是暗到亮还是亮到暗的变化选反了就是一条边都找不到。3.3 圆拟合算法对比与参数选择拿到亚像素轮廓点之后最后一步是拟合出圆。我用得最多的是fit_circle_contour_xld但这个算子有几个拟合算法选错会直接影响结果fit_circle_contour_xld (SelectedContours, algebraic, -1, 0, 0, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)algebraic代数最小二乘速度最快但对离群点和噪声敏感适合边缘干净、无缺陷的场合。geometric几何距离最小化抗噪比代数好精度高是我日常调试的首选。huber和tukey鲁棒拟合算法能自动降低离群点的权重。当孔口有毛刺、磕碰、油污造成的边缘异常时用这两种比单纯最小二乘稳定得多。代价是需要设置一个“剔除比例”参数调不好的话反而把好点也滤掉了。我的选择经验很简单标准件测量、边缘光洁用geometric来料检、工件表面状况参差不齐直接上huber参数从 0.3 开始调太激进就降到 0.1。另外fit_circle_contour_xld可以设置一个“圆弧覆盖范围”限制有些孔只有一小段弧可见比如被遮挡的过孔这时要限制最小覆盖角度否则拟合出的圆会在缺失区域乱飘。拟合完成后圆心和半径的单位仍然是像素。要换算成毫米直接用标定的比例系数物理直径 像素半径 * 2 * 标定系数。如果做了完整相机标定也可以把拟合出的轮廓点用contour_to_world_plane_xld映射到世界坐标再拟合精度更高但计算量也更大。3.4 进阶场景同心圆、阵列孔与孔内缺陷基础的单个圆量完不少项目会叠加其他需求挑三个常见的说一下。同心圆检测法兰盘或轴承座上常见要测内孔圆心和外圆圆心是否偏移。做法是对内外轮廓分别拟合得到两组圆心然后计算两点距离。注意内外圆图像对比度可能不同外圆要压低曝光才能看到边缘、内圆则相反这时候可以分别用不同 ROI 和不同灰度阈值处理不在一张图上硬调。阵列孔位置度十几个孔要求“所有孔之间相对位置误差小于某个值”。粗定位阶段用 Blob 把孔全找出来记录每个孔的中心坐标然后算两两距离。实际操作中要注意坐标系要统一到同一个基准通常用第一个孔或定位特征作为坐标原点如果工件在视野里有旋转需要先做模板匹配find_shape_model或find_ncc_model算出旋转角度把坐标旋转校正后再计算。孔内缺陷破孔、堵孔光拟合直径发现不了内部异常。我的做法是在孔区域内做二次 Blob 分析检查灰度异常区域大小和形状。比如堵孔的特征是孔中心灰度接近背景面积异常小破孔的特征是边缘出现长条形凹陷圆度严重下降毛刺是孔边缘有向外突起的尖角。这些都可以通过对比 Blob 特征和拟合残差来量化。比如把拟合圆和实际边缘叠在一起计算边缘点到拟合圆的最大偏移量超过阈值的点就是缺陷位置。4. 工程化落地、稳定性与常见问题排查4.1 程序结构怎么组织才方便维护和提速HDevelop 里调试好不等于完事工程落地还要把检测逻辑转成 C#、C 或 VB.NET 接口。我习惯把整个流程拆成三层初始化层读相机参数、标定数据、ROI 配置、公差配置程序启动时加载一次检测循环里不重复加载。单帧检测层一张图进来按“预处理 - 粗定位 - 精测量 - 判定”的顺序执行每个环节封装成独立函数。这样某个孔检测出问题时可以单独调这个函数排查。交互输出层结果传给 PLC一般走 TCP/IP 或 Modbus、显示到界面、保存图片和日志。性能方面圆孔检测的算子链并不长真正耗时的常常是图像预处理和标定校正。我实测过一个 500 万像素图、单张里面 12 个孔的场景把 ROI 都缩到孔周围小区域后一帧总耗时从约 180 ms 降到约 35 ms靠的就是reduce_domain和只处理感兴趣区域。另外如果相机本身的曝光时间就设得很大算法再怎么优化也提不上来这种时候先把曝光压下来、打强光是性价比最高的提速方式。4.2 稳定性评估与公差判级很多人只关心算法“能不能测出来”忽略了“能不能测稳”。我每次做圆孔检测项目都会在现场连续跑几百张图统计同一个孔直径的均值、标准差和极差。如果标准差超过公差范围的五分之一就说明系统余量不够早晚会误判。稳定性不足常见于三个地方工件抖动每次来料位置偏差大ROI 挡住了一部分边缘。解法是用模板匹配先定位工件再根据匹配结果把 ROI 平移旋转到对应位置。光照波动光源老化或环境光干扰灰度整体变化。解法是加光幕挡环境光、定期做光源亮度标定代码里用动态阈值做补偿。标定漂移相机松动或镜头调焦后标定参数过期。解法是定期拍标准件验证偏差超过阈值就触发重新标定。公差判级建议不要只在代码里写死 if/else而是把检测项做成可配置表检测项目、单位、上限、下限、优先级。现场换产品时改配置文件就行不用重新编译。输出 NG 时顺手截一张 ROI 图和测量数据一起保存客户追溯时不用翻半天日志。4.3 常见坑与避坑实录把我在调试中踩过、以及帮别人排查过的坑整理一下大概率能帮你省一天时间现象可能原因处理办法直径系统性偏大或偏小标定系数偏了或者预处理把边缘腐蚀/膨胀了先检查标定再检查预处理。形态学操作会改边缘位置测量前尽量别用结果忽大忽小波动很大光照不稳、孔口有毛刺、卡尺阈值太低导致边缘点乱跳查看缓存图确认边缘状态换更亮的背光提高边缘阈值考虑用鲁棒拟合边缘找不全圆拟合失败ROI 太小盖住了部分圆弧或对比度过低导致部分边缘漏检放大 ROI、降低 Canny 低阈值或改用卡尺方式分段扫圆度极差但圆拟合正常工件本身是斜放的或镜头有透视畸变先把工件放平或者做相机畸变校正孔的位置偏移测不准基准不统一模板匹配没做旋转补偿用模板匹配重投影 ROI把所有坐标旋转到工件基准坐标系下明显是圆孔但 Blob 筛掉了面积、圆度特征条件写太死或孔里有高光空洞先fill_up孔洞再筛特征条件放宽后用别的特征进一步区分还有一个特别容易被忽略的坑图像压缩格式。有些工业相机默认保存成 JPEG压缩率高了以后边缘会出现振铃效应亚像素测量直接废掉。做精确测量时务必用未压缩的 RAW、BMP 或 PNG或者从相机内存里直接拿图像。4.4 一个完整的Mini检测流程示例最后放一个浓缩的 Halcon 脚本骨架基本涵盖了我上面讲的整个过程。这个例子是“背光下检测单个圆孔的直径和圆心位置”标定部分用简单的像素转毫米系数代替方便立刻跑通* 读取图像并灰度化 read_image (Image, hole_sample.png) rgb1_to_gray (Image, GrayImage) * 粗定位阈值分割找孔区域 threshold (GrayImage, Region, 100, 255) connection (Region, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, [area, circularity], and, [800, 0.75], [5000, 1.0]) * 对每个孔区域进行亚像素圆拟合 count_obj (SelectedRegions, NumHoles) for i : 1 to NumHoles by 1 select_obj (SelectedRegions, HoleRegion, i) reduce_domain (GrayImage, HoleRegion, ImageReduced) edges_sub_pix (ImageReduced, Edges, canny, 1.5, 10, 40) select_contours_xld (Edges, SelectedContours, contour_length, 20, 500, -0.5, 0.5) fit_circle_contour_xld (SelectedContours, geometric, -1, 0, 0, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder) * 换算物理尺寸假设标定系数 0.02 mm/pixel DiameterMm : Radius * 2 * 0.02 CenterRowMm : Row * 0.02 CenterColMm : Column * 0.02 * 判定直径公差 5.00 /- 0.05 mm if (DiameterMm 5.05 or DiameterMm 4.95) disp_message (WindowHandle, NG: Diameter DiameterMm, window, Row, Column, red, false) else disp_message (WindowHandle, OK: Diameter DiameterMm, window, Row, Column, green, false) endif endfor这段代码直接复制到 HDevelop 跑通后再根据现场情况换阈值、换 ROI、加模板定位就成了一个能用的原型。注意那个0.02 mm/pixel是示意值实际项目里必须用标定结果替换。个人经验与最后几点建议圆孔检测做了几年我最大的感受是这个需求看着简单真正做好靠的是对每一步误差来源的控制。光源没打好后面算法再高级也补不回来标定没做准拟合结果再漂亮也只是像素游戏。建议新手朋友拿到项目后先用白纸把检测流程画一遍标出每个环节的输入输出和公差分配再动手写代码。另外一个小技巧调试圆孔检测时一定要保留故障现场的原始图像。现场偶发的一个 NG原理上最难复现但只要有当时的原图和数据基本能在一小时内定位到是光源问题、工件问题还是算法参数问题。我在每个项目里都会让程序自动保存 NG 图有些 NG 率异常的问题翻历史图一两眼就看穿了。这套流程既能应对单个孔的尺寸检测也能扩展到阵列孔、同心度、缺陷检测。你从最简单的threshold加fit_circle_contour_xld跑通开始一点一点往上加需求最后会形成一套自己顺手、也经得起现场验证的检测模板。希望能帮你少走几步弯路。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →