恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

C#实战YOLOv9螺栓漏装检测:零中间层部署与PLC联动方案

  • 首页
  • 资讯中心
  • /
  • C#实战YOLOv9螺栓漏装检测:零中间层部署与PLC联动方案

相关资讯

校园管理系统源码从部署到二次开发:环境配置与排错全流程 2026/10/22 8:40:31
三层交换机实现不同VLAN间通信:SVI原理、配置与排错指南 2026/10/22 8:35:30
从低功耗MCU到MQTT上云:Nucleo-STM32L073智能家居方案全解析 2026/10/22 8:35:30

最新资讯

基于Node.js的小程序点餐系统设计与实现:从架构到部署全解析
基于Python Django的反诈管理系统开发实战:从数据库到可视化大屏
创业赛项目标签拆解:全球首创、行业唯一与自研AI的真实含金量
K8S从入门到实践:容器编排架构、集群部署与避坑指南
鲁棒故障估计:解耦执行器故障与外部扰动
容器化分布式集群搭建实战:从镜像构建到多节点编排

今日推荐

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南
压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解
若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

C#实战YOLOv9螺栓漏装检测:零中间层部署与PLC联动方案

发布时间:2026/10/22 8:40:31
C#实战YOLOv9螺栓漏装检测:零中间层部署与PLC联动方案 1. 项目背景与方案选型1.1 螺栓漏装检测为什么难在汽车装配产线上螺栓漏装属于那种“看着简单、做起来要命”的工位。一个轮毂上可能有5颗、8颗甚至更多的螺栓装配工人在节拍压力下少拧一颗靠人眼复检很难百分之百拦住。产线停下来返工是小事要是不合格品流出去了那就是质量事故。我之前接过一个类似的工位改造需求轮毂螺栓装配检测要求实时性高、误报低、能直接对接产线PLC。最初方案想得挺简单——用普通工业相机拍一张图算一下高亮区域数量就行。但实际上螺栓头是金属反光面光照角度一变亮斑就乱七八糟再加上轮毂本身也有圆形孔洞和纹理传统图像处理的分割阈值完全扛不住复杂背景。于是视觉部分基本锁定深度学习目标检测能够直接定位每一个螺栓的位置、判断有没有装同时还能检测螺栓是否安装到位漏装、未拧紧、错装。模型选型上当时对比了几个目标检测框架最终用了YOLOv9。原因也比较实际它比YOLOv5/v8在特征提取上更强对小目标和密集目标比如一排螺栓挨得很近的召回率更好而且部署资料多、导出ONNX顺畅踩坑成本低。1.2 为什么能省掉Python/C中间层很多团队做这类项目习惯的架构是“Python跑模型C/C#做界面串口/以太网跟PLC通信”。这套组合成熟但是有个麻烦Python推理服务进程一旦崩了上位机还傻等两个进程之间的通信调度、异常同步都要单独写甚至要再加一层Redis或者HTTP服务。对工厂环境来说多一个进程就多一个故障点而且每次现场调试你都得在两个进程的日志里来回翻效率很低。我这里说的“零中间层”意思是整个视觉处理和上位机控制逻辑全部跑在C#进程里。用ONNX Runtime的C# API直接加载YOLOv9导出的模型做推理推理结果留在进程内存里直接用不需要序列化、不需要走Socket、不需要写JSON。PLC通信、相机采集、UI刷新、检测逻辑都在同一个进程内协作写起来像在写单机程序定位问题也很直接。当时不少人劝我用C做推理层理由是C性能更快。但那都是老观念了。ONNX Runtime在C#和C下用的是同一个底层推理引擎实际跑640x640输入的推理单张耗时差距通常在2-5毫秒以内对一个节拍3-5秒的检测工位来说完全无感。而C#在写界面、写通信、调试效率上的优势是实打实的。1.3 整体架构与工作流程这套系统从物理上分为三大部分工业相机采集轮毂工位照片通过GigE或者USB3连接到工控机。工控机Windows运行C#上位机程序内置YOLOv9 ONNX推理引擎负责图像采集、螺栓定位识别、结果判定。可编程控制器PLC控制产线运动、电磁阀、传感器。相机触发信号由PLC输出给工控机检测完成后工控机把OK/NG结果写回PLC指定数据块PLC根据结果决定放行还是拦截。整个流程看起来像一条流水线轮毂到达检测工位传感器到位PLC发送“拍照触发”信号比如把数据块里某个位写1。C#上位机轮询到这个信号立即从相机采集一张当前工位的图像。图像缩放填充到640x640转为Tensor输入ONNX模型推理得到每个螺栓的边界框和类别。上位机完成规则判断该工位应当安装的螺栓是否全部存在且类别正确是否存在漏装/错装区域。把OK或NG标志写回PLC的一个数据位。PLC根据结果控制下一个动作OK则放行NG则让轮毂进入返修通道同时上位机保存当前NG图像方便后续追溯。这套架构最大的好处现场调试时一台笔记本电脑加一根网线就能把整条检测逻辑跑通。相机、PLC、模型推理全在上位机进程里日志也是同一个文件排查问题很快。2. 部署模型从YOLOv9训练到ONNX导出2.1 数据集的采集与标注经验数据是整个检测项目的地基。螺栓检测的样本和普通公开数据集不太一样特别注意三点光照多样性白天、晚上、车间灯光、侧面补光、无补光每个场景都要拍。我们要求现场采集至少覆盖两种光照模式最好是把生产线上不同班次、不同天气的图都拿上。最终数据集中大约有2万张真实产线图像加上各类增强凑到3万张左右。负样本和难例很多团队只看“有螺栓”的数据结果上线后发现大量的误检因为没有让模型见过“没有螺栓时的样子”。轮毂上明明没有螺栓模型却因为孔的纹理误判为螺栓。所以负样本必须单独标注一个“missing”类别还是让模型不输出我这里采用的是显式类别除了螺栓bolt还标了一个类别“missing_screw”专门表示“这个位置应该有螺栓但没装”的空洞。这样模型能学会区分“正常孔”和“漏装孔”后期判断逻辑也会简单很多。标注框一定要贴紧螺栓目标小如果标注框上下左右留太多空白训练出来的框偏移会很严重直接影响后面的坐标规则判断。我们用标注工具统一调整为贴合螺栓边缘。对于部分遮挡的螺栓只要还能分辨就标上完全被遮挡的不标避免给模型发送错误信号。2.2 模型训练与权重选择YOLOv9主要有两种结构Ttiny和Ccompact再往上还有Eextended。工位检测的硬件大多是工控机CPU可能一般GPU也没有独立的显卡所以我一开始就没考虑YOLOv9-C这种大模型直接选了YOLOv9-T在识别精度下降可控的前提下把推理速度放在第一位。训练时输入尺寸640x640类别设为2类bolt和missing_hole。批量大小在单卡上设为16初始学习率0.01使用余弦退火调度。简单说下训练过程的直观感受大约100轮以内验证集mAP就能爬到0.9以上到200轮基本收敛稳定。主要卡时间的是数据读取和增强如果训练机器磁盘慢可以先把数据全部读进内存。这个项目用的标签格式是YOLO的txt格式归一化坐标没有特殊处理。训练完成后权重保存在best.pt中接下来就是导出。2.3 模型导出ONNX的操作与验证导出这一步是整个项目的关键。导出前先确认一版环境pip install ultralytics onnx onnxruntime-gpu onnxsim python export.py --weights best.pt --include onnx --opset 12 --simplify如果你用的是官方YOLOv9仓库导出完成后会在同一目录生成best.onnx。这里有几个非常容易踩的坑导出前关闭模型训练模式确保权重已经推理模式。官方脚本一般会自动处理但如果改了源码注意eval。Opset版本建议不低于12太低的话某些算子不支持。我实测opset 12和opset 17差别不大但别用opset 11个别结构在ONNX Runtime新版上会报错。导出后一定要用onnxsim做简化能消掉一部分多余的恒等算子减少模型大小。螺栓检测模型从40MB降到35MB左右推理时间差不了太多但简化掉一些微量开销总是好的。导出完成后用Python快速验证一下ONNX的输出和PyTorch原始权重输出是否一致import onnxruntime as ort import numpy as np import cv2 img cv2.imread(test_bolt.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1)) img np.expand_dims(img, axis0) sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) outputs sess.run(None, {images: img}) print(outputs[0].shape)这一步很重要如果输出张量的shape和预期一致比如1, 6, 8400其中6是xywh、conf、label的组合说明导出成功。如果模型有多个输出节点建议写个小的归一化脚本统一后处理逻辑。提示如果导出时加了nms或end2end选项输出格式会变成1x300x6这种固定框数量格式。这个项目里我选择不经NMS的原始输出把后处理完全交给C#实现灵活度更高调参也不用重新导出模型。3. C#端推理引擎搭建3.1 项目结构与本方案的技术栈C#上位机项目使用的核心依赖ONNX RuntimeMicrosoft.ML.OnnxRuntime.Gpu——加载和执行YOLOv9 ONNX模型。如果工控机没有NVIDIA显卡就用CPU版本实际检测速度也够。OpenCvSharp4.Windows或Emgu CV——负责图像采集之后的封装、颜色转换、尺寸缩放、画框。这类库在NuGet上直接安装集成非常方便不需要额外配置C编译环境。开源S7通信库——用于和PLC建立以太网通信直接读写数据块。整个上位机主体是一个WPF或者WinForms应用。我们这版用了WPF因为界面需要实时显示相机画面和检测框WPF的绑定和渲染比WinForms更顺手。当然WinForms也可以逻辑上没有区别。3.2 C#端图像预处理实现先定义检测结构体。我们最终YOLOv9模型输出是[1, 6, 8400]这种格式取决于导出的decoupled输出这里以常见的输出格式做示例。预处理的核心是LetterBox缩放——把原始图像等比缩放到640x640周围用灰边填充避免截断目标。很多新手直接Resize结果螺栓变形、长宽比失真模型精度掉得厉害。这一步必须保留原始比例。public class YoloV9Detector { private readonly InferenceSession _session; private readonly int _inputSize; private readonly float _confTh; private readonly float _nmsTh; private readonly float _scale; private readonly int _padW; private readonly int _padH; public YoloV9Detector(string modelPath, int inputSize 640, float confTh 0.25f, float nmsTh 0.45f) { _session new InferenceSession(modelPath); _inputSize inputSize; _confTh confTh; _nmsTh nmsTh; } public ListDetection Detect(Mat originalImage) { var (letterBoxed, _scale, _padW, _padH) LetterBoxResize(originalImage, _inputSize); var tensor MatToTensor(letterBoxed); var inputs new ListNamedOnnxValue { NamedOnnxValue.CreateFromTensor(images, tensor) }; using var results _session.Run(inputs); var outputTensor results.First().AsTensorfloat(); var rawDetections DecodeOutput(outputTensor); return NonMaxSuppression(rawDetections); } }LetterBoxResize的实现private (Mat resized, float scale, int padW, int padH) LetterBoxResize(Mat src, int size) { int h src.Rows; int w src.Cols; float scale Math.Min((float)size / w, (float)size / h); int newW (int)(w * scale); int newH (int)(h * scale); var resized new Mat(); Cv2.Resize(src, resized, new Size(newW, newH), 0, 0, InterpolationFlags.Linear); int padW (size - newW) / 2; int padH (size - newH) / 2; var canvas new Mat(size, size, MatType.CV_8UC3, new Scalar(114, 114, 114)); resized.CopyTo(canvas[new Rect(padW, padH, newW, newH)]); return (canvas, scale, padW, padH); }把图像转成Tensorprivate DenseTensorfloat MatToTensor(Mat img) { // YOLOv9输入是 CHW 顺序BGR通道翻转成RGB再归一化到0-1 var tensor new DenseTensorfloat(new[] { 1, 3, _inputSize, _inputSize }); for (int y 0; y _inputSize; y) { for (int x 0; x _inputSize; x) { var pixel img.AtVec3b(y, x); tensor[0, 0, y, x] pixel[2] / 255f; tensor[0, 1, y, x] pixel[1] / 255f; tensor[0, 2, y, x] pixel[0] / 255f; } } return tensor; }这段代码虽然看起来像素级操作很慢但实际上在Release模式下JIT会做很多优化640x640输入单张预处理大约10毫秒以内完全不影响整体节拍。如果你追求极限性能可以用OpenCvSharp的Split把BGR通道拆开再分通道赋值或者直接使用Tensor的缓冲区指针用unsafe方式操作但那些都属于进阶优化前期不必折腾。3.3 后处理与NMS实现YOLOv9的原始输出是若干特征图合并之后的结果每一行对应一个预测框。假设输出shape是(1, 6, 8400)其中6 4个坐标 1个置信度 1个类别编号这里我按这个格式解码。private ListDetection DecodeOutput(Tensorfloat output) { int rows output.Dimensions[2]; // 8400 int cols output.Dimensions[1]; // 6 或 numClass5取决于导出配置 var list new ListDetection(); for (int i 0; i rows; i) { float xc output[0, 0, i]; float yc output[0, 1, i]; float w output[0, 2, i]; float h output[0, 3, i]; float objConf output[0, 4, i]; // 如果模型已经提前解码类别第5个元素是类别ID // 如果是旧式84输出还要遍历后面的80类取最大值 if (objConf _confTh) continue; int classId (int)output[0, 5, i]; float score objConf; // 映射回原图坐标 float x1 (xc - w / 2 - _padW) / _scale; float y1 (yc - h / 2 - _padH) / _scale; float x2 (xc w / 2 - _padW) / _scale; float y2 (yc h / 2 - _padH) / _scale; list.Add(new Detection( new Rect((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1)), score, classId )); } return list; }注意一个细节如果你导出的是带类别分支那种格式即每个anchor有多个类别分数那需要在循环里遍历类别维取最大值作为最终置信度。我导出的模型开启了类别分支所以上述代码直接取第六个通道。NMS实现private ListDetection NonMaxSuppression(ListDetection detections) { var candidates detections .Where(d d.Score _confTh) .OrderByDescending(d d.Score) .ToList(); var result new ListDetection(); while (candidates.Count 0) { var best candidates[0]; result.Add(best); candidates.RemoveAt(0); candidates candidates .Where(d IoU(best.Box, d.Box) _nmsTh) .ToList(); } return result; } private float IoU(Rect a, Rect b) { int interX1 Math.Max(a.X, b.X); int interY1 Math.Max(a.Y, b.Y); int interX2 Math.Min(a.X a.Width, b.X b.Width); int interY2 Math.Min(a.Y a.Height, b.Y b.Height); int interW Math.Max(0, interX2 - interX1); int interH Math.Max(0, interY2 - interY1); float interArea interW * interH; float unionArea a.Width * a.Height b.Width * b.Height - interArea; return unionArea 0 ? interArea / unionArea : 0; }这个NMS是比较朴素的实现因为螺栓数量少、目标密度高但不至于重叠严重排序情况下性能足够。如果后续要扩展到几百个目标的场景可以考虑使用集合式NMS或者直接调用OpenCvSharp的Dnn.NMSBoxes。4. PLC联动与产线控制逻辑4.1 与PLC的以太网通讯模块工业环境中PLC品牌五花八门我这次用的是S7协议因为设备端用的就是S7-1200系列支持以太网直连。C#里用开源的S7通讯库封装连接与管理即可。连接代码public class PlcGateway : IDisposable { private Plc _plc; private readonly object _locker new object(); public bool Connect(string ip, int rack 0, int slot 1) { try { _plc new Plc(CpuType.S71200, ip, rack, slot); _plc.Open(); return _plc.IsConnected; } catch (Exception ex) { Log.Error($PLC连接失败: {ex.Message}); return false; } } public void Reconnect() { if (_plc ! null _plc.IsConnected) return; // 自动重连逻辑带5次退避 for (int i 1; i 5; i) { if (Connect(_plcIp)) return; Thread.Sleep(i * 1000); } throw new PlcException(多次重连失败); } public void WriteResult(bool ok) { lock (_locker) { // 这里定义DB10.DBX0.1为OK结果标志DB10.DBX0.2为NG标志 if (!_plc.IsConnected) Reconnect(); _plc.Write(DB10.DBX0.1, ok); _plc.Write(DB10.DBX0.2, !ok); } } public bool ReadTrigger() { lock (_locker) { if (!_plc.IsConnected) Reconnect(); return _plc.Read(DB10.DBX0.0); } } public void ClearResultFlags() { lock (_locker) { _plc.Write(DB10.DBX0.1, false); _plc.Write(DB10.DBX0.2, false); } } public void Dispose() { _plc?.Close(); } }关于PLC通信有两个容易出事的地方。多线程访问同一个PLC连接对象很容易产生“管道已断开”之类的异常。务必给读写操作加锁。PLC的DB号和偏移地址必须和电气工程师核对清楚。我曾经就吃过亏——电气那边说的是DB10.DBX0.1实际他手里还有一份旧图纸是DB12.DBX0.1结果检测结果一直写不进去。所以现场施工之前一定要拿着拓扑图逐点确认地址表。4.2 触发-检测-反馈的完整闭环整个检测状态机不复杂但要注意不要把视觉处理和PLC通信混在一个线程里。推荐用三个线程协作主线程UI线程显示图像、显示当前状态、处理人工操作。PLC轮询线程每10毫秒读一次触发信号一旦检测到信号调用相机采集并启动推理。相机采集线程独立处理相机帧防止因为相机SDK的回调阻塞导致画面卡死。我简化后的主循环长这样public void ProductionLoop(CancellationToken ct) { while (!ct.IsCancellationRequested) { try { if (!_plcGateway.ReadTrigger()) { Thread.Sleep(10); continue; } // 清掉触发信号防止重复处理同一轮 _plcGateway.ClearTriggerFlag(); var sw Stopwatch.StartNew(); // 1.采集相机图像 using var frame _camera.CaptureFrame(); // 2.推理 var detections _detector.Detect(frame); // 3.业务规则判断 bool resultOk EvaluateRule(detections, _expectBoltCount); // 4.显示和保存 var annotated DrawDetections(frame, detections); _uiDispatcher.Invoke(() UpdateScreen(annotated, resultOk)); if (!resultOk) { SaveNgImage(frame, detections); } // 5.写回PLC _plcGateway.WriteResult(resultOk); sw.Stop(); Log.Info($本轮检测耗时: {sw.ElapsedMilliseconds} ms); } catch (Exception ex) { Log.Error(ex.ToString()); // 某些异常需要将工位置为NG等待人工处理 _plcGateway.WriteResult(false); } } }注意ClearTriggerFlag这一步很关键否则PLC如果忘记自动清位上位机会同一帧图像重复检测很多次产线动作乱套。4.3 业务规则判定螺栓漏装检测不能只看“模型输出了几个目标”还要结合工位结构做规则标准数量校验这个工位应该装8颗螺栓检测出来的“bolt”类别数量必须大于等于8。少于8说明漏装。漏装区域校验如果模型在某个应装位置输出了“missing_hole”不管螺栓数量是否够数都视为NG。这个设计是为了防“一颗螺栓没装但旁边多装了一颗”这种情况。类别校验如果检测结果里出现“missing_hole”直接NG。private bool EvaluateRule(ListDetection detections, int expectedCount) { var bolts detections.Where(d d.ClassId (int)YoloClasses.Bolt).ToList(); var missing detections .Where(d d.ClassId (int)YoloClasses.MissingHole) .ToList(); if (missing.Count 0) return false; return bolts.Count expectedCount; }如果某个工位有多排螺栓比如前排4个、后排4个可以额外加一个“按区域计数”的规则把检测框按x坐标或者y坐标分区分别统计数量。这里可以根据工位自由扩展不必把所有逻辑塞进模型里——能用规则做的事就不要用模型做模型只负责“看见目标”规则负责“判断对错”。5. 实测效果与调优之路5.1 从97%到99.99%的关键调整首版系统在实验室测试时准确率只有97%左右主要体现在两类问题一类是漏检螺栓表面反光太严重导致没有检出另一类是误检把轮毂上的定位孔当成了螺栓。经过几轮调整准确率才逐步爬到99.99%这个量级。这几个调整是至关重要的加入现场真实光照数据实验室图片打光均匀但现场车间顶部荧光灯、侧窗自然光、检测工位补光灯混合在一起反差极大。采集来的真实数据重新标注并加入训练集后召回率立刻上去不少。调整置信度阈值首版用0.45的置信度漏检很多。调到0.25后召回率上来了但误检也增加了一些。这里不能在训练参数里死磕而是在后处理规则上区分螺栓这种目标需要高精度定位我把它单独设置0.35阈值missing_hole这种需要高召回单独设0.2。模型输出阶段用一个统一阈值过滤低质量框之后按类别再做一次阈值筛选。增加目标框最小尺寸过滤有些误检框特别小大约是正常螺栓框的三分之一这类框大概率是孔洞边缘误识别。在后处理时补充一个最小宽高限制直接把太小的框丢掉。5.2 误检和漏检的平衡99.99%这个数字听起来吓人但实际说的是“在固定测试集上的表现”而且业务上我们更看重“漏检率”而不是“误检率”。漏检意味着螺栓真的没装但系统没报警这是生产事故必须压到最低。误检意味着明明装好了但系统报警这会额外增加人工复查工作量影响节拍但在可控范围。所以我的阈值选择策略是宁误勿漏把missing_hole类的置信度阈值拉低尽量让疑似漏装的目标都报出来bolt类阈值略微拉高避免乱七八糟的误报。正常情况下10000次检测中会出现几次误报但螺栓漏装漏报的次数是零。这种取舍在工厂里是合理的因为误报让工人看一眼就好漏报会出大问题。5.3 性能实测数据在实际工控机上无独显i7-8700级别CPU单张640x640图像的模型推理时间大约100-130毫秒加上预处理、后处理、PLC读写和相机采集整体流水线耗时在300毫秒左右。按3秒一个节拍算余量非常大。后来给工控机加了一张入门的NVIDIA显卡类似GTX 1650级别推理时间降到20-30毫秒整条流水线耗时不到80毫秒。其实对这类项目来说CPU跑100毫秒已经够用不一定非要上显卡。要不要上GPU取决于产线节拍有多紧以及工控机里还有没有其他任务在抢CPU。在项目落地两个多月后统计结果是累计检测超过3万件产品漏装漏报0起误报警出现过几次全部是光照突变导致比如有人站在相机前短暂遮挡或者一个螺栓表面带着油污经人工确认后放行。6. 现场踩坑与排错实录6.1 C#推理结果和Python完全不一样这个坑遇到的人绝对不少。模型在Python里跑得好好的导出的ONNX在C#里跑框的位置偏移很大或者干脆结果全是空。我排查下来最常见的原因有两个输入Tensor的通道顺序不是模型期望的顺序。YOLOv9训练时用CHW而OpenCvSharp读图得到的是HWC必须在赋值前转成CHW并把BGR转成RGB。上面代码里已经做了处理。归一化方式不一致。如果训练时使用了均值和方差归一化导出模型后C#端不记得这件事直接把像素除以255喂进去结果自然错。YOLOv9本身用的就是0-1归一化但要确认自己的训练配置有没有覆盖默认设置。排查方法也比较简单在C#预处理完成后把Tensor里第一个通道的前几行数据打印出来和Python代码里处理后的数据对比。如果数值对不上就一条条追。6.2 相机采集偶尔超时导致卡死相机SDK采集图像有时候会因为网络冲突或者相机掉线而阻塞。刚开始我用单线程直接采集一旦相机卡住整个程序全部卡死连界面都没反应。后来改成独立相机线程并给采集操作加了超时控制var task Task.Run(() _camera.CaptureFrame()); if (!task.Wait(TimeSpan.FromMilliseconds(1500))) { Log.Warning(相机采集超时); _camera.Reconnect(); // 本轮不检测直接认为系统故障置NG _plcGateway.WriteResult(false); return; }虽然每次采集外包一个Task有点土但胜在简单、稳定。工程现场最重要的是程序不会因为一个超时把整个工位锁死。6.3 PLC通讯偶发异常与断线恢复S7协议通信在稳定网络下没什么问题但工厂车间电磁干扰大偶发连接超时或者“目标机器积极拒绝”也很常见。我在PLC封装里加了重连机制检测到连接失败后先尝试关闭再重新打开退避时间为1秒、2秒、3秒、5秒最多重试5次。如果重连也不成功就上位机直接弹窗报警并保持输出NG信号让产线无法放行这是最安全的状态。另外PLC侧也要配合设置上位机写回DB区数据后PLC应该在一个扫描周期内读取并复位并且最好增加握手信号——上位机写结果后PLC把结果取走再回写一个“已读取”标志。这样就不会出现上位机和PLC因为时序差而对同一个标志位反复读写的情况。6.4 模型更新时需要停机吗之前每次更新模型权重都需要重启上位机进程。后来我做了一个小优化在上位机里加载模型时把模型路径放到配置文件里增加一个“热更新”按钮。点击后程序重新创建InferenceSession并替换当前引用不需要退出程序。虽然ONNX Runtime初始化新模型对象需要几秒钟但放在检测间隙执行完全可行。这个功能对现场调试特别有用因为你可以现场标注新数据、重新训练、导出新ONNX然后直接让产线程序热加载不用反复开关机。最后的实操体会这套C#直接集成YOLOv9的方案我在两个类似项目里用过整体评价是省掉了中间层现场调试效率至少提升一倍。不需要在两个进程的日志里找问题不需要担心Python解释器崩溃导致整个视觉服务挂掉所有状态、缓存、业务规则都放在一个进程里逻辑非常清晰。如果你也想做类似项目我建议先别急着买昂贵的GPU工控机。先把相机、PLC地址表、模型精度这三件事坐下来和机械、电气工程师对齐尤其是PLC的数据块地址。很多项目延误都是出在这种“鸡毛蒜皮”的对接上而不是算法跑不动。另外一个经验是不要过分追求模型指标好看把漏装检测做成100%。生产线上真正要的是稳定、可解释、可追溯。只要每一轮检测都有日志、有图像保存、有PLC反馈记录即使偶尔误报一次也能顺着记录快速定位原因这才是工业项目该有的样子。模型推理只是其中一环整个系统稳定闭环了产线才会真正信任这套视觉检测。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号