ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C#+Halcon可视化抓边抓圆工具:交互式测量与参数实时调节实战

C#+Halcon可视化抓边抓圆工具:交互式测量与参数实时调节实战 前阵子给一条产线做视觉测量程序客户的需求很直接把产品放到相机下面工具上画个框就能自动测出边缘到边缘的距离、圆的直径和圆心位置最好还能把不合格的点和毛刺排除掉不用每次改代码。这类需求在3C、五金、医疗耗材行业里太常见了于是我基于C#和Halcon整理了一个可视化抓边、抓圆工具从交互画ROI到参数调节再到结果输出全部做成界面操作。这篇内容就是整个工具的实现复盘从原理到代码到填坑一次性讲透。做软件的人应该都有体会算法再强如果调试过程痛苦现场一样难落地。我最早用纯HDevelop脚本做测量每次客户说“阈值调一下”“ROI往右挪一点”都得回到脚本里改参数再导出来回折腾大半天。后来把C#做界面、Halcon做算法引擎这套组合跑通之后调试效率提升了一个量级。这篇博文适合下面这类人看正在用C#做上位机开发的工程师刚接触Halcon想快速做测量工具的视觉新人以及需要在产线现场给操作人员交付可调参工具的开发者。就算你完全没碰过Halcon跟着思路走也能搭出一个能用的测量工具。1. 为什么需要这样一个可视化工具1.1 工业测量场景里的真实痛点先聊一下需求背景。产线上的视觉测量往往不是“识别有没有缺陷”这么简单而是要求出具体数值比如这个零件的边到边距离是20.05毫米那个孔的圆心坐标偏移了多少圆半径是否在公差范围内。这类需求对精度的要求是亚像素级的普通像素级边缘检测根本扛不住。更麻烦的是现场环境不是实验室。同一个产品不同批次的光照可能有变化料件表面可能有油污、毛刺、反光点。如果工具不支持交互调节每遇到一次光照变化就得改代码项目周期会被拖得很长。所以第一步不是急着写算法而是想清楚这个工具必须满足三个条件操作人员能在界面上直接画ROI感兴趣区域不接触代码。抓边、抓圆的算法参数能实时调节界面上拖动滑块立刻出结果。干扰点可以手动排除支持绘制掩膜区域把不需要参与拟合的边缘点直接过滤掉。这三点都做到了工具才算真正“好用”。1.2 选型思路C# Halcon 组合为什么合适选型这事我纠结过一阵子。一开始考虑过用OpenCV毕竟开源免费边缘检测、霍夫变换这些功能都有。但实际做测量后发现OpenCV的亚像素边缘拟合能力比起商业算法库还是有差距尤其是圆弧拟合、直线拟合的鲁棒性在毛刺和噪点比较多的情况下需要自己写很多后处理逻辑过程控制比较麻烦。Halcon的优势恰好命中测量场景。它的亚像素边缘提取算子经过多年工业项目验证稳定性很好轮廓拟合函数自带鲁棒性算法比如tukey、huber权重能自动降低离群点的影响再加上HWindowControl控件可以直接嵌到WinForms或WPF里做可视化开发效率非常高。C#在这个组合里的角色是“骨架”。Halcon负责图像处理C#负责界面交互、参数管理、结果保存、外接扫码枪和PLC通信。两个配合起来视觉算法和业务逻辑解耦后续维护也方便。成本方面需要注意Halcon需要授权但正规项目里这笔投入通常能被节约的调试时间快速覆盖。如果用公司的正版授权开发阶段用官方申请试用授权也够用。授权激活和管理以官方渠道流程为准按正常开发流程走就行。1.3 工具要做到什么样的程度才算“好用”我给自己定的标准是“现场操作工不用看文档也能上手”。界面分成几个区域左边是图像显示区中间是ROI绘制工具栏右边是参数面板底部是测量结果列表。操作人员只需要三步打开图片或者连接相机采集一帧图。选择“画矩形”或“画圆”工具在目标区域拖一个框。点“执行测量”结果立刻在图像上叠加显示数值输出到底部表格。如果测量结果不理想操作人员拖动几个滑块调阈值图像上的边缘线和拟合圆会实时刷新。整个过程不需要重启程序不需要改代码。这个交互设计思路就是用界面把Halcon的调试过程包起来让不懂算法的人也能做算法参数调整。2. 抓边、抓圆的核心技术与方案选型2.1 抓边与抓圆的算法本质抓边本质上是在图像中找出物体与背景的灰度跳变位置然后把这些位置拟合成直线或圆弧。Halcon的edges_sub_pix算子可以从图像中提取亚像素精度的边缘轮廓输出结果是XLDeXtended Line Description轮廓也就是浮点坐标的轮廓线。XLD轮廓比像素级轮廓精度高得多后续拟合出的直线端点、圆心、半径都是亚像素坐标这是做精密测量的基础。抓圆比抓边多一个步骤。圆可以是完整的圆也可能是弧形缺口。Halcon的做法是先从边缘轮廓中筛选出类圆形的轮廓按圆度、长度等特征筛选然后用fit_circle_contour_xld做圆拟合输出圆心行坐标、列坐标、半径、起始角度、结束角度。算法层面的核心是拟合时的鲁棒性。Halcon的拟合函数支持最小二乘algebraic、几何距离geometric等算法还可以设置迭代次数和排除因子ClippingFactor。我实际测试下来测量场景优先选“algebraic”如果边缘点里有少量毛刺干扰配合合适的排除因子可以把影响降到最低。2.2 测量卡尺与全图边缘提取怎么选Halcon里做测量有两种主流方式新手经常搞混我在这里理一下。第一种是测量卡尺方式对应算子measure_pos、measure_pairs。它的原理是在一个矩形区域里沿矩形的长边方向等间距放置多个“卡尺”每个卡尺扫掠一条线段上的灰度变化找到边缘位置。这种方式适合测量已知位置附近的边缘位置速度快、稳定性高但需要预先知道边缘大致在哪个方向而且一次只能测一个矩形范围内的边缘。第二种是全图边缘提取加轮廓拟合也就是edges_sub_pix fit_line_contour_xld / fit_circle_contour_xld。这种方式先在整个感兴趣区域里提取所有边缘轮廓再按形状筛选和拟合。灵活性更高交互画ROI之后能自由处理不规则形状适合做抓圆、抓圆弧以及需要排除干扰点的场景。我这个工具主推第二种因为可视化交互场景下用户画的框大小、形状不一定规整全图提取后筛选的方式更通用。如果是高节拍的产线在线检测且产品位置固定测量卡尺方式会更合适。工具后来也预留了卡尺模式的接口给了参数切换选项。2.3 可视化交互的设计思路可视化交互是工具的灵魂主要分三块图像显示、ROI绘制、结果叠加。图像显示直接用Halcon自带的HWindowControl控件它内部封装了Halcon的窗口支持缩放、平移也能直接在窗口上显示图像和XLD轮廓。ROI绘制这块有两种实现方案一是调用Halcon内置的交互算子draw_rectangle2、draw_circle它们会自动阻塞等待鼠标操作返回绘制结果二是自己在C#里用鼠标事件画框。我建议工具项目里用第二种自己做鼠标框选逻辑这样能控制绘制样式比如画出来的矩形要能拖动、缩放、删除和操作系统的交互习惯保持一致。结果叠加显示比较简单用HOperatorSet.DispObj显示图像再用HOperatorSet.DispLine和DispCircle在窗口上绘制拟合结果颜色设为红色线宽设成3这样边缘结果一目了然。3. 从零搭建可视化抓边、抓圆工具的落地过程3.1 环境准备与工程搭建开发环境我用的Visual Studio 2019Net版本选.NET Framework 4.7.2因为HalconDotNet对这个版本的支持最稳定。安装了Halcon 21.05引用程序集时找到安装目录下的halcondotnet.dll加入项目引用。工程结构建议按模块分目录Forms主窗体、参数面板窗体。HalconHelper封装图像读取、灰度转换、边缘提取、拟合等操作。Model定义ROI数据结构、测量结果数据结构。Utils类型转换、日志、文件读写辅助。还有一个容易忽略的坑Halcon的C运行库和dotnet程序集必须匹配。如果系统是64位项目平台必须设置为x64否则运行时会报BadImageFormatException。别问我怎么知道的第一次搭项目时就被这个坑卡了半小时。3.2 抓边功能的实现抓边功能的完整流程是读取图像 → 转灰度 → 裁剪ROI → 亚像素边缘提取 → 筛选直线轮廓 → 拟合直线 → 输出端点坐标和角度。核心代码大致如下using HalconDotNet; // 1. 读取图像并转灰度 HImage image new HImage(imagePath); if (image.CountChannels() 3) { image image.Rgb1ToGray(); } // 2. 用ROI区域裁剪图像只处理感兴趣范围 HRegion roi new HRegion(roiRow, roiCol, roiHeight, roiWidth); HImage reduced image.ReduceDomain(roi); // 3. 亚像素边缘提取 HObject edges; HOperatorSet.EdgesSubPix(reduced, out edges, canny, 1.5, 20, 40); // 参数说明 // canny边缘提取算法工业场景优先选这个 // 1.5高斯滤波系数值越大边缘越平滑抗噪越强 // 20滞后阈值的高阈值灰度变化小于该值的点不认为是边缘 // 40低阈值用于边缘连接 // 4. 按长度筛选轮廓去掉太短的噪声边缘 HObject selectedEdges; HOperatorSet.SelectShapeXld(edges, out selectedEdges, contlength, and, 30, 99999); // 5. 拟合直线输出两端点坐标 HTuple row1, col1, row2, col2, nr, nc, dist; HOperatorSet.FitLineContourXld(selectedEdges, tukey, -1, 0, 5, 2, out row1, out col1, out row2, out col2, out nr, out nc, out dist); // 6. 在窗口上绘制拟合直线 HWindow window hWindowControl.HalconWindow; window.SetColor(red); window.SetLineWidth(3); window.DispLine(row1[0].D, col1[0].D, row2[0].D, col2[0].D);参数里的门道我单独说一下。edges_sub_pix的滞后阈值两个值间隔越小提取出的边缘点越多、越碎间隔越大边缘越干净但可能漏掉弱边缘。现场调节时一般先固定高阈值慢慢提高低阈值观察边缘线刚好连续完整即可。fit_line_contour_xld算法选“tukey”它比普通最小二乘更能抵抗离群点配合ClippingFactor2适合大多数五金件边缘。如果边缘本身比较规整可以用“algebraic”算法速度更快。3.3 抓圆功能的实现抓圆的流程和抓边类似多了一个“轮廓筛选”的环节需要按圆度筛掉非圆形边缘。// 1. 读取、裁剪和边缘提取的代码同上这里省略 // 2. 按圆度筛选轮廓 HObject circleContours; HOperatorSet.SelectShapeXld(edges, out circleContours, circularity, and, 0.5, 1.0); // circularity 是圆度1表示标准圆0.5以上基本可以认为是圆 // 3. 圆拟合 HTuple row, col, radius, startPhi, endPhi, pointOrder; HOperatorSet.FitCircleContourXld(circleContours, algebraic, -1, 0, 0, 3, 2, out row, out col, out radius, out startPhi, out endPhi, out pointOrder); // 4. 在窗口上绘制圆和圆心 window.SetColor(red); window.DispCircle(row[0].D, col[0].D, radius[0].D); window.SetColor(blue); window.DispCross(row[0].D, col[0].D, 20, 0);这里有一个需要注意的细节如果产品上的圆不是完整圆而是带缺口的圆弧fit_circle_contour_xld的MaxClosureDist参数要设成非0值否则它会把缺口当成误差处理拟合圆心会偏。缺口比较大时还需要把算法换成“geometric”它对圆弧部分拟合的精度更高。圆度公差判断也在这一层做拟合完成后把半径换算成直径和上下公差对比直接输出OK/NG。3.4 参数面板与实时刷新的实现参数面板我用了WinForms的TrackBar控件和NumericUpDown控件。界面上放几个滑块分别控制高斯滤波系数、边缘阈值、最小轮廓长度、圆度阈值。每个滑块绑定ValueChanged事件事件里重新执行检测流程并刷新窗口。这块最影响体验的是实时性。最初版本每动一下滑块图像就从头读一遍、灰度转一遍导致界面卡顿明显。后来做了两项优化效果显著图像加载和灰度转换只做一次执行检测时直接从内存里的HImage后续处理。把“读图”和“检测”分离拖动滑块时只重复边缘提取和拟合步骤不重新读图。优化后速度从每帧300多毫秒降到40毫秒左右拖动滑块已经有了“所见即所得”的顺滑感。这组调优思路在批量生产时也很有价值一张图做完一次预处理后可以把不同参数组合快速跑一遍找出最优参数组合。3.5 绘制掩膜与排除不需要的点项目里比较出彩的一个功能是“绘制掩膜排除干扰点”。现场常见的场景是这样的要测一个圆环的外径但圆环边缘上有一小段沾了胶水灰度跳变异常拟合出的圆会被这段干扰拉偏。传统的做法是缩小ROI避开那段但ROI缩得太多参与拟合的圆弧变短精度反而下降。我的做法是在ROI之外额外允许用户绘制一块或几块“排除区域”算法在拟合前会把落在排除区域内的边缘轮廓全部去掉。实现分三步用户在工具栏选择“绘制排除区域”在图像上用鼠标画一个多边形或多段圆弧。程序将用户绘制的区域转为HRegion调用difference算子从ROI区域中减掉。对排除区域覆盖范围内的边缘轮廓做筛选剔除再进入拟合步骤。排除区域的代码核心就是区域减法HRegion roi new HRegion(roiRow, roiCol, roiHeight, roiWidth); HRegion excludeRegion new HRegion(excludeRowPoints, excludeColPoints); HRegion validRegion roi.Difference(excludeRegion); HImage reduced image.ReduceDomain(validRegion);还有一种更精细的“排除点”模式不画区域而是把拟合过程中的离群点实时显示出来用户点击某个点程序把这个点在后续拟合中忽略。这个模式的底层实现是保存所有参与拟合的轮廓点坐标用户点击后计算最近邻点标记为禁用然后再拟合一次。实测下来用这种方式处理毛刺点比单纯调鲁棒性算法更可控因为它是显式地告诉算法“这个点是坏点”而不是让算法自己猜。4. 实测复盘那些绕不开的坑4.1 授权与运行环境问题开发阶段最容易出问题的是Halcon运行时授权。刚装好环境第一次运行窗口打开时偶尔会弹出找不到license的提示或者程序在启动时直接崩溃。多数情况是授权的版本和运行的主机环境不匹配。这里提醒一下Halcon的授权和机器绑定换电脑后需要重新激活。开发环境装好之后建议先跑一遍官方示例程序确认环境没问题再开始写自己的代码否则后面排查起来容易误认为是代码问题。还有一种情况是开发机运行正常部署到现场工控机上报错。原因基本是现场机器没装Halcon运行时或者缺少VC运行库。解决方法是把部署包做完整用Inno Setup或MSI打包向导把运行库、Halcon运行时DLL、授权文件一起打进去。这一步别偷懒否则每次去现场都要手动配环境。4.2 坐标与像素尺度标定工具刚做出来时测出来的结果客户不认因为输出的是像素值而不是毫米值。很多刚入行的人会忽略这一步图像坐标系和实际物理坐标系之间需要一个标定系数也就是每个像素对应多少毫米。最简单的标定方法是拍一个已知尺寸的标准件量出图像里对应尺寸的像素数用实际尺寸除以像素数得到像素当量。但如果相机和产品之间有角度倾斜或者镜头有畸变这种简单换算误差会比较大。正规做法是用Halcon的标定板助手做相机标定得到内参和外参再把像素坐标转换成世界坐标。现场设备如果已经固定安装、不涉及大范围运动用一个定值像素当量也能对付。但需要提醒的是每次调节相机焦距或位置后标定系数就变了重新标定。我这里把像素当量做成一个可配置参数保存到配置文件里现场调试工具时直接填数字不用重新编译程序。等后续需要更高精度时再集成标定板自动标定功能。4.3 byte转real与内存指针的坑热词里有人搜“halcon把byte转为real”这个场景一旦理解了就不难。Halcon里常见的灰度图是byte类型取值范围0到255。做某些图像处理算法时需要把图像转成real类型比如亚像素精度更高的滤波操作或者要自己用浮点数组构造图像。C#里做转换有几个思路。如果是整张图转换直接用HImage的ConvertImageType方法最省事HImage gray image.Rgb1ToGray(); HImage real gray.ConvertImageType(real);如果从相机SDK拿到的是byte数组要自己构造HImage需要注意内存布局。Halcon的HImage默认是按行连续存储的步长等于图像宽度。我们可以用下面的方式把byte数组转成real数组再构造HImageIntPtr ptr gray.GetImagePointer1(out string type, out int width, out int height); byte[] buffer new byte[width * height]; Marshal.Copy(ptr, buffer, 0, buffer.Length); float[] floatBuffer new float[width * height]; for (int i 0; i buffer.Length; i) { floatBuffer[i] buffer[i] / 255.0f; } HImage realFromArray new HImage(real, width, height, floatBuffer);另一个高频坑是内存泄漏。Halcon的HObject和HImage底层是C对象靠析构函数释放内存但C#的GC调用析构函数时机不可控。在高帧率采集场景下HObject没及时释放会导致内存持续增长。解决办法是每个HObject用完都调用Dispose()或者配合using语句。特别是循环里反复创建的HRegion、HImage不释放的话跑一晚上内存能涨到几个GB。4.4 常见问题速查表问题现象可能原因解决方法运行报BadImageFormatException项目平台目标不是x64将项目平台目标改为x64打开窗口提示找不到license授权文件与机器不匹配按官方流程重新激活授权拟合出的直线方向乱跳边缘轮廓筛选条件太宽拟合到了多段不同方向边缘提高最小轮廓长度或增加方向筛选条件圆拟合圆心偏移很大轮廓圆度阈值太低把非圆轮廓也纳入拟合提高circularity阈值到0.7以上界面拖动滑块卡顿每次都重新读图、转灰度将图像预处理移到初始化阶段检测只做边缘提取和拟合循环检测内存持续增长HObject未及时释放每个HObject调用Dispose()循环内注意临时变量4.5 UI线程卡顿的处理最后说说热词里反复出现的“C#循环数据采集和UI刷新卡顿”。这个在视觉工具里太常见了尤其是实时相机采集时UI线程里做采集、算法、绘制界面必然卡成PPT。处理思路很简单耗时操作全部放到后台线程UI线程只做结果展示。我用的是Task.Run加Control.Invoke的方式private void btnContinuousDetect_Click(object sender, EventArgs e) { running true; Task.Run(() { while (running) { HImage frame camera.GrabImage(); // 相机采集 var result measureEngine.Measure(frame); // 抓边抓圆 this.Invoke(new Action(() { UpdateResultView(result); // 只刷新UI })); } }); }这里有两个细节值得注意。一是UI刷新频率不是越高越好如果采集帧率是60fpsUI刷新也到60fps反而费CPU结果视图根本看不过来。通常控制到20~30fps就够了用一个时间戳判断上次刷新时间即可。二是结果的叠加绘制不要每次重新画全图只更新变化的图层实测能省下不少绘制时间。5. 工具的进一步扩展与实战建议5.1 对接扫码枪与产品配方管理工具做到能手动测、调参还远远不够产线真正用起来还需要和业务流打通。一个很快见效的扩展是扫码枪触发。具体实现是程序监听串口或USB扫码枪的输入扫码枪扫到条码后自动触发采集和检测检测结果与条码绑定后保存下来。C#里监听串口数据用SerialPort类的DataReceived事件收到数据后解析条码再触发一次采集检测流程。不同产品对应不同的检测配方A型号要测两条边的夹角B型号要测圆的直径和位置。配方管理就是把ROI坐标、边缘阈值、圆度阈值、公差范围这些参数按产品编号存储起来扫码后自动加载对应配方。这是工具从“调试版”走向“产线版”的关键一步。5.2 结果输出与数据追溯测量结果如果只在界面上显示就算白做了。我建议把结果输出分成三个层级屏幕显示实时值、OK/NG状态。本地保存每个测量结果的图片、数值、时间戳保存到本地文件夹方便事后追溯。数据对接通过TCP/IP或Modbus把结果发给PLC或MES系统实现自动分拣和闭环反馈。本地保存的格式我推荐每一条记录存一个JSON文件包含产品条码、测量项名称、测量值、公差、判定结果、ROI参数、原始图片路径。这样即使时间过去很久也能完整还原当时的检测过程。如果现场已经用了第三方MES直接调用MES提供的Web API上传数据即可。5.3 关于VisionMaster与其他视觉平台的对接很多项目现有系统是海康VisionMaster客户问我C#上位机用什么协议和它通讯比较好。我一般建议VisionMaster作为视觉处理端C#作为上位机调度端通讯协议用TCP/IP最简单直接VisionMaster有对应的通讯配置工具把结果打包成XML或JSON发送到指定端口即可。如果对实时性要求高可以走共享内存或者利用SDK回调。但如果你正在读这篇文章并且打算自己写视觉算法C#直接调Halcon做测量是更轻量的选择不需要在中间件协调上多一层复杂度。5.4 从WinForms迁移到WPF的一点建议WinForms写工具程序上手快但界面观感比较“复古”。如果客户对界面美观度有要求可以考虑迁到WPF。Halcon的HWindowControl在WPF里需要放在WindowsFormsHost中承载运行时仍然会创建WinForms的消息循环两者共存没有大问题只是注意Dpi自适应和样式覆盖。界面框架方面MVVM模式适合做参数面板和结果列表但ROI绘制的鼠标逻辑和WinForms差别不大可以直接平移过去。不过我的看法是工具的价值核心在于测量功能和调试体验界面风格属于锦上添花。如果项目周期紧张优先把算法、稳定性、部署方案做扎实界面简洁大方即可。最后分享一个我一直在用的习惯不管工具做得再怎么顺手给每个版本留一个带参数快照的调试日志。现场出了尺寸波动往回翻日志就能知道当时用的是哪组阈值、哪个ROI。工具好用是一方面能被稳定复现才是生产力。这条建议看起来不炫技但真到了项目交付和售后阶段能省下无数沟通成本。做视觉工具这几年我最大的体会是算法精度决定工具的上限而调试体验和可维护性决定工具能走多远。这个基于C#和Halcon的可视化抓边、抓圆工具就是在“把算法顺手地交到操作人员手里”这件事上下了功夫希望这次的复盘能给你的项目一些参考。
返回列表