拓冰建站拓冰建站
首页 / 资讯中心 / 正文

C# WinForm工业相机显示屏缺陷检测系统开发实践

简介本资源是一个基于C# WinForm开发的显示屏质量检测工具源码包面向电子制造质检工程师、工业自动化软件开发者及.NET桌面应用学习者用于快速构建产线级显示屏性能评估系统。压缩包共33个文件含18个核心C#源码文件如Form1.cs、SerialPortBase.cs、ReadCommand.cs等、7个依赖DLL、3个本地化资源文件.resx、1个Visual Studio解决方案.sln及配套配置文件整体仅516KB结构紧凑、模块清晰便于二次开发与嵌入式集成。已有100人学习下载可直接编译运行完整覆盖显示屏信息读取、亮度/对比度测试、坏点诊断、检测数据记录与报告生成五大功能模块代码中包含串口通信SerialPortBase、INI配置管理IniFiles.cs、命令解析ReadCommandModel.cs及C DLL调用CallCPlusPlusDLL.cs等实用技术点是理解工业检测类WinForm应用架构的优质实践样本。1. 项目背景从一份“显示屏检测”工程包说起前阵子我拿到一个客户交付过来的半成品项目压缩包名字就叫做“C# winform-HK-显示屏检测.zip”。打开之后里面是常规的WinForm工程结构配套着HK相机的SDK、一堆检测算法的DLL和几份测试图片。这种“半成品工程包”在工控行业里很常见经常是上一家公司做了一半撤了或者设备厂家把软件外包出来拿一个贴着品牌名的源码包丢给接手的人。当时我的任务很简单把它跑起来调好检测效果交付到产线。这个项目解决的场景很明确显示屏出厂前需要用工业相机抓拍屏幕画面通过软件判断屏幕上有没有亮点、暗点、坏点、亮度不均匀等缺陷。过去很多产线靠人工目检人员盯久了眼睛疲劳漏检率忽高忽低而且记录不规范客户要追溯数据时拿不出有效报告。用WinForm写一个上位机把相机采集、图像分析、结果判定、数据记录串起来属于工控领域非常典型的一套组合。在聊具体实现之前先说说为什么这类项目选了C#和WinForm而不是别的。我见过不少团队尝试用WPF做新一代工控软件界面确实漂亮但落到现场维护阶段问题就出来了工业现场的老师傅习惯的是WinForm时代的控件逻辑相机的SDK、Halcon的例子、PLC通信库十份例程里有八份是WinForm写的直接搬来就能用。再加上WinForm部署简单目标框架选好之后一台工控机装上.NET Framework就能跑不需要额外运行时折腾。所以我给客户的技术方案里依然坚持WinForm作为主框架这不是技术保守而是现场可靠性优先。整套系统的核心链路并不复杂HK相机实时采集屏幕画面图像进入检测引擎在设定好的ROI区域内完成坏点、均匀性、Mura等项目的判定结果在界面上实时刷新同时把OK/NG信号通过串口或Modbus发给PLC。下面我把这个链路里最容易踩坑的环节逐段拆开讲。1.1 产线检测场景的三个典型痛点先分析需求而不是急着写代码。显示屏检测这个场景和普通视觉检测有明显差异主要体现在三个方面。第一屏幕是主动发光器件拍到的图像质量受相机参数影响极大。同一个屏幕曝光时间差一毫秒检测结果可能就天差地别。相机如果用自动曝光系统会自动匹配环境亮度导致一批屏幕测出来的灰度基线忽高忽低坏点判定阈值就没法统一。所以这类项目的第一个硬性要求是相机的曝光、增益、白平衡必须固定下来不能依赖自动模式。第二被测对象本身是整片发光区域检测密度高。一个27寸的显示器按1920x1080分辨率去测等效像素点数量非常大。虽然工业相机的分辨率不需要跟屏幕物理像素一一对应但一个面板上需要关注的区域仍然是海量的逐点遍历如果代码写得不讲究一帧图像处理可能就要几百毫秒产线节拍根本扛不住。这里涉及的不仅是算法本身还有图像数据的访问方式后面会细说。第三检测结果需要跟产线设备联动。单纯在电脑屏幕上显示“NG”没有意义产线机械手或分拣机构需要拿到一个电平信号或者通信指令然后自动把不良品踢出去。所以软件层面必须预留对外通信接口串口、TCP、Modbus至少要支持一种而且通信逻辑要独立于检测流程不能因为界面卡顿导致信号丢发。1.2 HK相机的选型逻辑与SDK取舍“HK”在这个项目里不是神秘代号就是常见的工业相机品牌海康、大华这一类或者某些国产机器视觉相机。设备选型时我一般按接口类型来分USB3 Vision和GigE Vision两种最常用。USB3 Vision相机的优势是连接简单一条USB线同时供电和数据传输带宽也足够跑500万像素级别的帧率适合检测工位离电脑比较近的场景。GigE Vision的优势是传输距离长布线灵活几十米内没问题适合产线跨区域部署但需要配置网卡巨帧、预留带宽网络环境复杂的现场要额外处理丢包问题。不管选哪种接口一个原则是优先使用厂商SDK而不是通用的DirectShow或AForge接口。很多同事拿到HK相机之后第一反应是装一个AForge.NET的库然后写VideoCaptureDevice去枚举设备这样确实能出画面但也有个隐患AForge走的是UVC免驱协议工业相机厂商自己SDK里的硬触发、曝光设置、增益控制、像素格式转换这些高级接口全部用不了。显示屏检测对曝光和增益是强需求所以我从来不用AForge做工业相机主采集它更适合做USB摄像头、视频文件的读取或者项目原型验证。当时我接手的工程包里HK相机的采集用的就是厂商SDK的C#接口底层封装在MvCameraControl这种类库里。第一次跑起来会遇到一个典型的启动问题DLL没有复制到输出目录。厂商SDK的DLL往往有好几个MvCameraControl.dll、MvGigECamera.dll、MvUsb3Camera.dll这种默认引用之后Visual Studio不一定把它拷到bin\Debug下程序一启动就报“无法加载DLL”处理办法其实不复杂右键引用设置为“复制本地”同时把SDK的运行时路径加到项目输出目录。这个虽然基础但就是很多项目第一次编译不通过的根源。2. 图像采集链路从HK相机到WinForm实时预览采集链路的稳定性决定了整个检测系统的底线。在显示屏检测项目里我习惯把采集链路拆成三个环节设备初始化、参数固定、帧回调分发。任何一个环节做不好后面算法写得再好也白搭。2.1 相机初始化流程与设备掉线重连相机的初始化代码看起来机械但里面的顺序有讲究。以海康MVS SDK的C#接口为例标准流程是枚举设备、创建句柄、打开设备、设置传输模式、开始采集。刚开始做这个项目时我犯过一个低级错误把所有初始化代码堆在窗体Load事件里一次执行完。结果现场某台工控机上相机打开失败或者SDK初始化超时软件直接卡死在启动阶段操作工只能强制重启。后来我把相机初始化和检测主流程完全解耦窗体启动后用一个后台线程去执行相机连接界面上先显示“正在连接相机”连接成功再切到预览状态。如果连接失败弹窗提示同时保留一个“重新连接”按钮。这在产线环境里属于保命设计因为相机掉线不是会不会的问题而是什么时候的问题。还有一个很容易忽略的细节相机SDK里打开设备后最好设置一下MV_CC_SetEnumValue把像素格式转成PixelType_Gvsp_BGR8_Packed或者Mono8。显示屏检测一般用黑白相机或者彩色相机转灰度因为算法核心是灰阶分析。如果直接拿彩色原始数据跑通道处理会拖慢速度而信息量并没有增加。2.2 参数固定检测类项目的“铁律”固定相机参数这件事我在项目初期被坑过一次。当时为了调试方便开了相机的自动增益和自动曝光结果下午车间灯光一变同一块屏幕测出来的亮度值整体漂移误检率直接翻倍。产线老师傅过来问软件是不是坏了我一看日志图像平均灰阶从120变成160坏点检测阈值完全是乱的。从那以后我把参数固定写进了初始化代码里并加了一行日志输出方便回溯相机状态。核心参数设置逻辑可以参考下面这段C#代码这里调用的是海康SDK的封装其他品牌SDK大同小异// 关闭自动曝光固定曝光时间 MVCC_FLOATVALUE exposure new MVCC_FLOATVALUE(); exposure.fValue 5000f; // 单位微秒具体值以现场标定为准 mvCamera.MV_CC_SetFloatValue(ExposureTime, ref exposure); // 关闭自动增益固定增益值 MVCC_FLOATVALUE gain new MVCC_FLOATVALUE(); gain.fValue 0f; mvCamera.MV_CC_SetFloatValue(Gain, ref gain); // 关闭自动白平衡固定白平衡通道比例 mvCamera.MV_CC_SetEnumValue(BalanceWhiteAuto, 0); mvCamera.MV_CC_SetEnumValue(BalanceRatioRed, 150); mvCamera.MV_CC_SetEnumValue(BalanceRatioGreen, 100); mvCamera.MV_CC_SetEnumValue(BalanceRatioBlue, 160);参数标定不能靠拍脑袋。我通常在正式检测前让屏幕上显示标准纯白画面读取中心区域的灰度均值通过调整曝光时间把灰度控制在200到220左右留出余量给亮度波动。灰度太低坏点对比度不够检测灵敏度下降灰度太高接近饱和区稍有波动就会误报。这个标定过程最好做成一个独立的“参数调试模式”界面上能实时看到直方图和平均灰度现场调机效率会高很多。2.3 帧回调与生产者消费者模型相机SDK的采集方式一般两种主动拉流MV_CC_GetImageBuffer和回调方式MV_CC_RegisterImageCallBackEx。WinForm项目里我强烈建议用回调方式但千万不要在回调函数里直接更新UI控件。回调线程是SDK内部的采集线程帧率可能30帧甚至更高。如果你在回调里直接写pictureBox.Image bitmap等于让UI线程跟采集线程抢资源界面迟早卡死而且Bitmap对象频繁创建不释放内存会以肉眼可见的速度往上涨。正确的姿势是做一个阻塞队列采集回调只入队检测线程消费队列。这里用 C# 的BlockingCollectionBitmap最顺手因为内部封装了线程同步不会出现多线程环境下队列被并发写坏的问题。private BlockingCollectionBitmap frameQueue new BlockingCollectionBitmap(20); // 相机回调 private void OnCameraFrame(IntPtr pData, MV_FRAME_OUT_INFOEx stFrameInfo, IntPtr pUser) { // 将相机原始数据转成Bitmap后入队 Bitmap bmp ConvertRawToBitmap(pData, stFrameInfo); if (bmp ! null) { // 如果队列满了丢弃最旧的一帧保证实时性 if (frameQueue.Count 15) { Bitmap drop; if (frameQueue.TryTake(out drop)) drop.Dispose(); } frameQueue.Add(bmp); } } // 检测线程 private void DetectionLoop() { foreach (Bitmap frame in frameQueue.GetConsumingEnumerable()) { var result Detect(frame); // 通过事件或委托通知UI线程 OnDetectionResult?.Invoke(result); // 处理完之后手动释放避免内存泄漏 frame.Dispose(); } }这段代码里有一个细节值得注意我把队列容量限制在20帧。如果队列长了检测速度跟不上采集速度画面延迟会越来越大产线语义上这是不可接受的。队列满时优先丢弃旧帧保证软件处理的是“最近的一帧”这比死等检测完成要合理。3. 检测算法核心显示屏缺陷判定的实现细节显示屏检测的算法说穿了就是图像处理和阈值判定的组合。这个项目里最核心的三个检测项是坏点检测、均匀性检测和Mura检测。前两个靠传统图像处理就能实现得很好Mura类缺陷亮度不均的云斑状瑕疵视情况决定要不要上深度学习。3.1 坏点检测区域灰阶突变是唯一线索坏点检测的原理不复杂。亮点也叫亮斑是屏幕上某些像素始终发白光暗点则始终不发光。在图像上表现出来坏点区域的灰度值和周围正常区域明显不一致而且这个差异不会因为显示内容变化而消失。算法实现上我倾向于对整帧灰度图做“局部对比度计算”而不是设置一个绝对灰度阈值。因为屏幕亮度、电缆衰减、相机响应都会导致灰度整体偏移绝对阈值一遇到批次差异就失效。局部对比度的思路是对每个像素计算它和周围邻域均值的差差超过一定倍数的局部标准差就标记为候选坏点。用C#实现时要注意效率不能用GetPixel逐点去读那会让一帧图像处理耗时好几秒。正确做法是Bitmap.LockBits锁定位图数据然后用unsafe指针直接访问内存速度能提升几十倍。下面给出一个简化版核心逻辑private void DetectDeadPixels(Bitmap grayBmp, int threshold, out ListPoint defects) { defects new ListPoint(); int width grayBmp.Width; int height grayBmp.Height; Rectangle rect new Rectangle(0, 0, width, height); BitmapData bmpData grayBmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); unsafe { byte* ptr (byte*)bmpData.Scan0; int stride bmpData.Stride; // 跳过边界边界像素邻域不完整不做判定 for (int y 3; y height - 3; y) { for (int x 3; x width - 3; x) { byte center ptr[y * stride x]; // 计算3x3邻域均值实际项目可以扩大到5x5或7x7 int sum 0; for (int dy -1; dy 1; dy) { for (int dx -1; dx 1; dx) { if (dx 0 dy 0) continue; sum ptr[(y dy) * stride (x dx)]; } } byte avg (byte)(sum / 8); int diff center avg ? center - avg : avg - center; if (diff threshold) { defects.Add(new Point(x, y)); } } } } grayBmp.UnlockBits(bmpData); }这里的threshold就是灵敏度参数一般通过标定得到。我在项目里把阈值做成界面上可调的参数现场调机时先找一块良品屏调整阈值让软件零误报再找一块有缺陷的屏确认软件能准确报出缺陷。这个调参过程的截图会成为验收资料的一部分。算法跑完之后还有个后处理步骤形态学去噪。单个像素的孤立跳变可能是坏点但相机传感器本身也有噪声会出现零星的伪影。我通常用“连通域面积过滤”只有面积在3到50个像素之间的团块才被判定为坏点。太小的忽略太大的可能是脏污或者划痕需要单独分类不要笼统地算成坏点。3.2 均匀性检测区域统计而不是盯住某一点均匀性检测是显示屏行业比较看重的一个指标反映的是屏幕亮度分布是否一致。业内常用的方法有中心五点法、九点法、五百分区法等。基本原理是一样的把屏的有效显示区域划分成若干网格计算每个网格的灰度均值然后比较最大值和最小值之间的差异。五百分区法相对更贴近产线实际因为屏幕局部亮度波动往往出现在某个区域而不是单独一个点。实现时把ROI划分为横纵各若干份的网格统计每个网格的平均灰度再计算整张灰度图的均值、最大值、最小值和标准差。private void CalcUniformity(byte[] imageData, int width, int height, int gridRows, int gridCols, out double maxGray, out double minGray, out double cv) { double[] cellAverages new double[gridRows * gridCols]; int cellW width / gridCols; int cellH height / gridRows; for (int r 0; r gridRows; r) { for (int c 0; c gridCols; c) { long sum 0; int count 0; for (int y r * cellH; y (r 1) * cellH; y) { for (int x c * cellW; x (c 1) * cellW; x) { sum imageData[y * width x]; count; } } cellAverages[r * gridCols c] count 0 ? (double)sum / count : 0; } } maxGray cellAverages.Max(); minGray cellAverages.Min(); double avg cellAverages.Average(); cv (maxGray - minGray) / (avg 0.0001); // 变异系数越小越均匀 }判定逻辑上我用两个指标配合一是最大灰度与最小灰度的绝对差值差值超过设定值就判负二是所有网格灰度的标准差标准差反映整体离散程度。实际生产时不同型号显示屏的亮度基线不同所以均匀性阈值也要做成配方化。每新上一个机型维护人员先跑一遍良品标定软件自动保存这组阈值之后切换机型时加载对应配方。3.3 与Halcon混合编程的边界划分这个工程里还涉及Halcon相关的内容热搜词里有一条“HOperatorSet.QueryAvailableDLDevices(Runtime, GPU, out hv_DLD) 失败”很多人在这个接口上栽过跟头。我的建议是能用传统算法解决的问题不要一开始就上深度学习必须上深度学习的场景C#和Halcon之间的边界要划清楚。Halcon在这类项目里通常扮演“算法计算器”的角色。C#端负责界面、相机采集、结果展示、通信Halcon端负责图像处理算子。混合编程的典型写法是C#调用Halcon的DLL把HObject、HTuple作为参数传进去调用HOperatorSet完成处理再取回结果。这种模式下最容易出的问题是Halcon运行时路径和许可证没配对。程序启动时如果报HOperatorSet加载异常基本就是Halcon的DLL没有找到或者许可证文件不在默认目录。至于GPU相关接口如果检测需求没到非用深度学习不可的程度我建议先跳过。调用QueryAvailableDLDevices失败原因可能是驱动、CUDA版本、Halcon运行时版本三者不匹配。在工控机上折腾这个时间成本很高。传统算法在稳定性和可控性上远好于模型推理而且现场维护工程师容易理解阈值、面积这些参数。深度学习更适合Mura这类人眼能看出来、但传统规则很难描述的复杂缺陷并且需要前期收集大量标注样本。4. WinForm界面架构与交互细节界面部分决定了操作工愿不愿意用你的软件。在显示屏检测这种每天要开八小时以上的产线工具里界面设计没必要追求花哨但必须信息清晰、交互顺畅、长时间运行不卡顿。4.1 功能分区左边看画面右边调参数下边看结果我做这套系统时的界面布局如下窗体左侧是相机实时预览区和检测结果叠加层右侧是参数配置面板底部是当前工件的检测结果列表和状态栏。这种布局来源于操作工的习惯——他们要看画面判断产品状态同时要能快速确认“这单是OK还是NG”。WinForm原生控件做右侧参数区其实够用我用一组GroupBox把相机参数、检测阈值、通信参数分组再用PropertyGrid作为高级参数的展示面板。不过这里有个细节PropertyGrid默认是只读的如果想让它支持修改需要把选中对象的属性加上[Browsable(true)]和 setter并且注意PropertyGrid.SelectedObject的类型必须是公开类否则修改值不会生效。热搜词里有人问“WinForm的PropertyGrid只能查看不能修改怎么实现”多半就是setter没写或者类不是public导致的。预览区的图像缩放也值得一说。相机分辨率动辄几百万像素直接塞进PictureBox会失真而且缩放算法选择不对会把坏点给“平均”掉影响人工确认。我建议用PictureBox.SizeMode Zoom并在绘制检测结果时把缺陷点坐标按缩放比例映射到控件坐标系上用红色矩形框标出来。这样操作工一眼就能看到缺陷在屏幕上的位置。4.2 多线程模型UI线程不能卡是底线WinForm有个老规矩UI线程不能被耗时操作阻塞否则窗口会进入“未响应”状态。检测算法通常要跑几十毫秒甚至上百毫秒必须放后台线程。上文提到的BlockingCollection消费者线程就是一种方案。但是当检测线程要向界面推送结果显示时C#里要用Invoke或BeginInvoke回到UI线程。很多初学者容易掉进Invoke的坑线程里频繁调用Invoke会导致UI线程忙于处理上下文切换出现卡顿。解决方法是降低刷新频率检测结果不要求每帧都更新到界面。我一般让检测线程把结果写进一个共享对象UI线程用System.Windows.Forms.Timer每隔100毫秒去取一次然后再重绘。这样实时性够用又避免了跨线程地狱。private System.Windows.Forms.Timer uiTimer; private void MainForm_Load(object sender, EventArgs e) { uiTimer new System.Windows.Forms.Timer(); uiTimer.Interval 100; uiTimer.Tick UiTimer_Tick; uiTimer.Start(); } private void UiTimer_Tick(object sender, EventArgs e) { // 检测线程只更新共享字段UI线程在这里读取 labelAvgGray.Text _latestResult.AverageGray.ToString(0.0); labelDefectCount.Text _latestResult.DefectCount.ToString(); pictureBoxPreview.Invalidate(); // 触发重绘在Paint里画检测框 }使用Timer还有一个附加好处如果检测线程挂掉了UI还能正常响应。现场维护时看到“图像不更新”和“程序卡死”是两个不同量级的故障前者容易恢复后者只能重启进程。4.3 界面美化与产线专用习惯WinForm原生的Button、GroupBox样式比较老旧虽然不影响功能但在客户演示时容易被挑刺。现在的WinForm项目里美化方案通常两条路一是引入第三方UI库像AntdUI、SunnyUI这些二是自己做自绘控件。第三方库见效快但我建议不要为了UI库把项目依赖搞得过于复杂尤其是客户后续要自己维护代码时越少的黑盒依赖越安全。我在这套系统里用的是一种折中方案不引入完整UI框架只做了必要的视觉优化。窗体设置DoubleBuffered true消除控件闪烁自定义一个标题栏按钮用扁平化配色检测状态通过一个大的圆形指示灯控件显示——绿色代表OK红色代表NG。这个指示灯是自绘的核心代码其实不长用GDI的FillEllipse画同心圆就实现了。它传递信息的效率比一堆文字要高得多工位上的操作工扫一眼就知道状态。产线上还有个细节字体要大按钮要高亮。工控机的显示分辨率通常不高操作系统缩放也千奇百怪控件尺寸如果写死换个机器就可能挤成一团。我用TableLayoutPanel做布局字体用微软雅黑10号以上同时开启AutoScaleMode.Dpi这样在不同缩放下界面不会崩。5. 部署排障实录那些必须写下来的坑软件打包交付到产线之后真正的考验才开始。我在这个项目上遇到过几个印象特别深的坑每个都有代表性。5.1 程序启动报“无法加载一个或多个请求的类型”这个报错在热搜词里出现过很多次它本质上是程序集加载失败。具体到我们这个项目最可能的原因是引用了某个依赖DLL但运行时没有找到或者是编译目标平台不对x86的DLL被塞进了x64进程反过来也一样。我排这种问题有一套固定流程先看事件查看器里的.NET Runtime错误日志确认是哪个程序集加载失败再看解决方案里的“平台目标”工控机是64位系统项目就编译成x64如果检测SDK只提供32位版本那就统一装32位运行环境不要混合。还有一个不起眼但常见的原因某个DLL依赖了VC运行库而工控机上没装表现也是“加载类型失败”。遇到这种情况装一遍VC Redistributable就解决了。5.2 相机用着用着掉线显示屏检测产线通常不是单台设备相机线缆可能要走桥架或者跟动力线平行布线。USB3相机最常见的掉线原因是供电不足尤其是通过USB集线器转接时。GigE相机掉线则多半是网络丢包或者IP冲突。排查时我会分三步走第一步看相机端指示灯状态判断是供电问题还是通信问题第二步看SDK日志确认掉线瞬间是设备断开还是网络超时第三步看现场的线缆铺设。最终这个项目的解决方案是给相机换独立供电的USB线缆并且加装了重连逻辑——SDK检测到设备掉线后先释放句柄再重新枚举和打开设备整个过程自动完成操作工不会感知到。这个重连逻辑在代码里是一个状态机正常采集中、掉线重连中、重连成功、重连失败。5.3 误检率为什么降不下来项目调试过程中误检率是客户最关注的数据。有一段时间一块明明很好的屏幕软件总是报边缘区域有缺陷。我抓下那帧图放大一看屏幕边缘有轻微的圆弧反光灰阶值从220降到190变化幅度确实达到了阈值。问题不在算法而在ROI没画好——我把检测区域取到了屏幕边缘以内不足10个像素边框的过渡区也被纳入计算了。解决方式是把ROI往内缩避开屏幕边框。另外还在预处理里加了一个3x3高斯滤波先压制随机噪点再做局部对比度计算。滤波本身会损失一点细节但对坏点这种大梯度突变影响很小整体效果是误报明显下降。在这个基础上我更推荐“二次复核”机制第一次检测用较低阈值筛出所有可疑点第二次对可疑点区域用更高分辨率的局部阈值复查。可疑点如果连续多帧稳定存在才判为缺陷。因为坏点是物理固定的而灰尘、反光在连续帧里不会每次都在同一个位置。这个策略在产线上把误报率压到了可接受范围内。5.4 检测结果联动PLC串口与Modbus的简单实现显示屏检测设备的自动分拣一般靠PLC接收OK/NG信号。我在WinForm里用System.IO.Ports.SerialPort做串口通信协议非常简单检测完成时发一个2字节指令比如0xAA 0x01表示OK0xAA 0x02表示NG。PLC侧收到指令后动作。串口通信要注意波特率、数据位、校验位跟PLC配置一致另外建议使用serialPort.WriteTimeout设置超时避免发送异常时线程卡住。如果客户现场用的是Modbus协议可以用开源库NModbus。我在一个项目里用Modbus TCP把检测结果寄存器地址写成一个整数比如0代表无缺陷1代表亮点2代表暗点3代表Mura。PLC轮询读取这个寄存器即可。这种做法的好处是扩展性强不光能传OK/NG还能传缺陷代码产线上的MES系统也能通过同一路网关采集数据。6. 这类项目做完之后我自己的感受显示屏检测这套系统的核心难点从来不是某个单一技术而是把相机SDK、图像算法、界面交互、设备通信串成一个在产线上能稳定运行的整体。C#和WinForm在这类工控项目里生命力依然强不是因为它们新而是因为它们可靠、生态成熟、现场维护成本低。真正把项目做好的关键是把相机参数标定、算法阈值、通信协议这些细节扎扎实实搞清楚每一步都有依据不靠试运气。最后分享一个小技巧给这类检测程序写一个“自检模式”开机时先让相机拍摄一张标准白板和标准黑板自动计算图像均匀性和背景灰度如果环境光变化太大直接预警提示。这样能让操作工在开始检测前就意识到环境异常而不是等一批产品测完才发现结果不可信。我在多个项目里都加了这个功能反馈都很好。产线环境里的软件多一点防御性设计后面就能少很多半夜被叫起来改参数的事故。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门