ARTICLE DETAIL

资讯详情

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

C#与YOLOv8在汽车零部件装配视觉检测中的应用实践

C#与YOLOv8在汽车零部件装配视觉检测中的应用实践 1. 项目需求分析与整体方案设计1.1 汽车产线上的真实痛点为什么非得做这套检测先说背景。我当时接到这个需求是在一条汽车发动机周边零部件的装配线上。这条线主要负责把螺栓、齿轮、垫片这些小件装配到壳体上节拍大概在30秒一件。表面上看产线已经用了好几年工人操作也熟练但客诉率里总有那么几条是“螺栓漏装”“齿轮错位”导致的异响、松动甚至退货。人工检测到底哪里不靠谱一是疲劳产线两班倒夜班质检员盯着几十颗螺栓一颗一颗看时间长了注意力必然下降二是节拍矛盾你想让工人仔细看产线节拍就扛不住你想保住节拍漏检率就上来了。更麻烦的是这类错装问题一旦流入整车厂装配环节返工成本直接翻好几倍还会影响整条供应链的交期。所以这套系统的目标很明确在每件产品装配完、流出工位之前自动判断螺栓是否全部拧到位、齿轮啮合是否错位。检出来是NG的直接让机械臂把工件抓去返修区。这里面的核心技术就是两个一是视觉算法能不能准确、稳定地识别出螺栓漏装和齿轮错位二是C#上位机能不能跟PLC、机械臂高效协同把检测结果转化成实时动作。整套系统的关键指标就三个单件检测时间不超过15秒要和产线节拍匹配、漏检率极低目标万分之一以下、误杀率尽量低不然产线天天停线也是灾难。1.2 技术选型的思考过程为什么是C#YOLOv8PLC机械臂选技术的逻辑说到底是围绕“谁来做、怎么配合、出了问题好不好维护”这三个问题展开的。视觉算法这一块我对比过传统机器视觉方案比如OpenCV做模板匹配、边缘检测和深度学习方案。传统方案在理想光照下能跑但汽车零部件表面通常有油污、反光、铸件纹理干扰螺栓头部和壳体的灰度对比不稳定模板匹配的鲁棒性很差。只要换个批次的产品或者光源稍微衰减误判率就猛涨。深度学习目标检测则不需要手工设计特征只要数据打够模型自己会学那些微妙的视觉差异。YOLOv8又是目前工程落地最舒服的那一档——训练和部署链路成熟模型体积小CPU或低端GPU都能实时跑不像一些两阶段检测器那么重。所以视觉这边定下来YOLOv8。上位机用C#主要看中它开发Windows桌面程序的效率、与工业相机SDK和PLC通信库的生态成熟度。C#做UI界面、做多线程管理、做数据记录都省事。虽然Python在算法侧更方便但上位机要长期运行在工控机上异常要能自动恢复、日志要清晰、维护工人要能看懂这些恰恰是C#的长处。PLC和机械臂就更不用说了——产线上本来就是西门子S7-1200做主站机械臂是主流六轴这套组合是工厂里最常见的配置选它意味着后续维护和扩展都方便不会跟现场老系统脱节。整套方案的架构可以这么看相机采图发给上位机里的YOLOv8推理模块做检测检测结果写入PLC的DB块PLC根据状态机和工位信号决定要不要启动机械臂抓料机械臂收到指令后执行放行或返修分流。C#相当于大脑PLC相当于神经中枢机械臂是手各司其职。2. 视觉检测模块YOLOv8模型训练与推理集成2.1 数据集采集与标注这里最容易被低估很多做项目的朋友一上来就急着训练模型结果发现效果不行回过头来补数据浪费了大量时间。我在这个项目里把数据采集和标注当成整个视觉模块最重要的环节来抓。先说采集环境。相机装在一个封闭检测暗箱里上下各一组条形白光光源角度大概是45度斜照。为什么要做暗箱和固定光源因为后期的推理环境必须和训练数据分布一致否则模型换了个光照条件就垮。用工业面阵相机500万像素像元尺寸合适配8mm定焦镜头拍工件俯视图像现场调好焦距和光圈以后全部锁死不能让人随便碰。采集的类目需要仔细设计。螺栓这一块我分了两个类别螺栓状态正常算一个类螺栓漏装或者明显未拧紧头部明显高出或者歪斜算另一个类。齿轮这一块正常啮合算一个类错位包括错半齿和错一齿以上算另一个类。实际拍的时候不是简单对着正常产品拍几张就完事而是要把各种情况都覆盖到正常装配件拍3000张左右故意去掉一颗螺栓的拍1000张左右而且要覆盖漏装位置不同的情况——因为螺栓分布在不同位置模型要学到的是“某个位置没有螺栓”这个模式而不是“某一颗特定的螺栓消失”漏装同时还有油污、光照变化的情况额外加500张齿轮错位需要手动把齿轮旋转一定角度再装配错半齿和错一整齿的比例大概3:1共拍1000张再补一些特殊情况的负样本比如产品表面本来就有污渍、料道内有异物等。标注用的是常见的数据标注工具打了四个类别bolt_ok、bolt_missing、gear_ok、gear_misalign。这里有一个关键细节标注框不要卡得太紧边缘留出10到20像素的余量因为后续推理时工件位置可能有轻微偏移框太紧会降低定位稳定性。数据增强也值得花心思。原始采集的图像直接去训练模型容易过拟合现场光照稍微变一点就废。我用了YOLOv8自带的增强参数亮度扰动从±25%开始对比度±25%饱和度和色相扰动轻微一点因为颜色本身是有效特征还有随机平移、缩放、旋转。旋转角度控制在±10度以内因为工件实际摆放方向本来就受料道限位约束不会出现完全倒置的情况。增强做完参与训练的图总量在8000到10000张。2.2 训练细节与损失曲线经验训练环境其实不用太豪华我用了一张GTX 1660 Ti的工控机6G显存同时跑推理和日常实验够了。模型选的是yolov8n。有人会问为什么不用更大的s或者m因为检测场景相对固定不是那种小目标密集的复杂场景n的精度已经能达到要求而推理速度能快不少。我把推理的置信度阈值定在0.55到0.65之间类别IoU阈值默认即可。训练参数上输入分辨率640x640batch size 8因为显存只有6Gbatch再大就爆显存了训练150个epoch。优化器用SGD初始学习率0.01weight decay 0.0005cosine学习率衰减。这里有个经验如果你发现训练集损失一直在降、验证集损失却开始反弹那就是过拟合了通常发生在100个epoch附近这时候不要盲目继续训练要看模型在验证集上的mAP选择mAP最高的那个权重文件而不是最后一个epoch的输出。损失曲线一定要画出来看。我习惯把train_loss、val_loss和mAP三个指标放在一起观察。如果刚开始几个epoch loss降得很快这是正常的如果val_loss在很长一段时间内波动很大说明增强过猛或数据有问题。在这个项目里总体训练了120多个epoch收敛mAP0.5在验证集上达到了0.988其中螺栓两类都能到0.99以上齿轮错位稍低0.96左右。说实话这个精度的代价是数据里专门拍了不少错位件如果你只有正常件和漏装件齿轮错位这类别就根本学不出来。训练完以后导出的权重是.pt格式但C#端不能直接用PyTorch来推理虽然可以用Python做成一个独立服务走HTTP或gRPC但这会让系统复杂化现场多一个进程就多一个故障点。我的方案是导出ONNX格式用ONNX Runtime在C#进程内直接推理。导出时要注意opset版本选择适中比如15或17版本太旧有时会丢算子的支持。另外输出端需要包含检测头和NMS后处理但导出时不需要把NMS加进模型图里后处理在C#里自己写这样反而更好调试和更新。2.3 C#调用YOLOv8的封装与性能调优C#里推理的封装核心就三步加载模型、前处理、后处理。代码不复杂但细节决定成败。前处理要做的是读图、缩放至640x640、BGR转RGB、归一化到0到1之间、再转为NCHW排布的float数组。C#里用System.Drawing读图性能有点慢建议用OpenCvSharp或者ImageSharp其中OpenCvSharp在批处理性能和格式转换上更顺手。推理用ONNX Runtime的C# API创建Session的时候可以指定CPU还是GPU。我现场用的是CPU推理一张图大概在15到25毫秒之间加上前后处理总共30到40毫秒对于我的节拍完全够。如果你的节拍要求更高可以试ONNX Runtime的GPU EPExecution Provider需要装CUDA和cuDNN并且模型文件要用GPU版本去做但GPU会带来额外的驱动管理负担万一现场显卡驱动又被谁更新了导致不兼容排查起来很头疼。所以我的建议是CPU能跑就不要为了几十毫秒上GPU。后处理这部分YOLOv8输出的结构是(batch, 84, 8400)8400是不同尺度特征图展平后的候选框总数84是4个坐标加80个类别如果你是80类模型但因为我们自己训练的是4类所以输出维度是(1, 8, 8400)。C#端需要把每个候选框的置信度算出来按阈值过滤再做NMS去重。这部分网上代码不少但有两个容易踩的坑第一个坑是坐标变换。YOLOv8输出的是相对640x640图的坐标要还原到原始图像尺寸需要记录前处理时的缩放比例和letterbox填充偏移。如果忘了做letterbox而是直接resize到矩形检测框位置会偏移。第二个坑是类别索引顺序。训练时定义的类别顺序bolt_ok、bolt_missing、gear_ok、gear_misalign会固化在onnx模型里C#端类别标签数组的顺序必须和训练时完全一致否则你读到的“类别0”其实是bolt_ok不是你以为的某个类。推理线程和相机采集线程之间要做解耦。相机SDK回调把图像放进一个ConcurrentQueue推理线程从这个队列里取图处理。这样即使某次推理偶然慢了也不会阻塞相机采图导致丢帧。检测结果封装成一个自定义类包含时间戳、类别、置信度、坐标再传给逻辑层做后续判定。3. C#上位机程序架构设计与实现3.1 整体程序框架WPF 多线程 日志上位机我选了WPF而不是WinForms。理由是老生常谈但很现实WPF的界面样式灵活可以做出现场工人愿意看的大字号、高对比度界面数据绑定和MVVM模式也让逻辑和界面分离后续改需求不用整个推翻。整个程序按模块划分了五个部分相机采集模块、视觉推理模块、PLC通信模块、业务逻辑模块、用户界面模块。相机采集和视觉推理各跑一个独立线程PLC通信在后台用定时器轮询或事件驱动UI线程只负责显示不让任何耗时操作卡住界面。多线程一定要处理好竞态。比如PLC那边要求检测完成后1秒内给出结果如果UI线程卡了、或者推理线程还在忙上一张图就会出现超时。我的设计是全部走消息队列相机采集线程把图像发给推理线程推理线程把结果发给业务逻辑线程业务逻辑线程再通过PLC通信模块写DB块。层与层之间不共享可变状态队列做成线程安全的ConcurrentQueue出问题好排查得多。日志这块现场调试的命根子。我用了开源日志库Serilog输出到文件按天滚动保留30天。日志格式统一是时间戳、线程ID、级别、消息。每次检测都打一条“检测结果[OK/NG]类别[类别名]耗时[毫秒]”每次PLC读写都打一条“写入DB100.DBW10 1”。不要心疼日志多等你半夜接到现场电话说设备又停机了日志就是唯一的破案线索。3.2 相机SDK集成与采图控制的两种方式工业相机品牌很多海康、大华、Basler都有C#的SDK。集成方式其实大同小异打开设备、设置分辨率/曝光/增益、注册取流回调、启动采集。这里要额外注意曝光和增益的设置。汽车零部件表面反光强曝光时间太长会过曝把螺栓和背景的对比拉没了太短则整体偏暗模型检测率下降。我的经验是先把曝光设成固定值比如5000微秒增益尽可能低通过调整光源亮度来获得理想图像。光圈也不要调到最大一般收两档让景深大一点避免工件高度方向有微小差异时失焦。采图控制模式我建议做成“软件触发”而不是“连续采集”。怎么理解连续采集就是相机一直在出图系统只取最新一帧简单但费CPU而且可能拍到工件还没到位时的图像。软件触发是PLC给一个到位信号上位机收到信号后再去触发相机采一张图。顺序上一定要防止竞态工件到位信号传上来不要立刻触发拍照先做个几十毫秒延时让工件完全停止震动后再拍否则图像是糊的。3.3 检测判定逻辑什么叫“合格”不能只看单帧在实际项目中视觉检测的判定逻辑绝不是“模型输出OK就是合格品”必须结合产线上下文。场景是这样的每个工件上有8颗螺栓模型检测时如果识别到3颗bolt_ok和5颗bolt_missing该怎么判定严格来说只要有任一螺栓漏装就算NG所以判定条件应该是“bolt_ok的置信度都超过阈值并且bolt_missing类的任何框置信度不能超过阈值”。同理齿轮如果有gear_misalign置信度超过阈值就直接判NG。我还加了一个保守策略第一帧检测出NG时不要立刻写PLC而是等工件在工位内再触发一次复检两次都为NG才真判NG。这个策略能把偶发抖动、反光闪变造成的误杀降下来代价是节拍多花几秒但在大多数产线是可接受的。这里顺便说一个容易被忽略的问题置信度阈值该设多少。设高了漏检风险大设低了误杀一堆。我的做法是先统计验证集上每张图的错误检测情况画出置信度-误杀率曲线找一个平衡点。在这个项目里bolt相关的阈值我设在0.6gear相关设在0.55。为什么齿轮要低一点因为齿轮错位本身就是比较细微的视觉差异置信度普遍比螺栓类低一些阈值卡高了会漏。4. PLC通信与机械臂联动控制4.1 PLC侧数据区规划提前划分好变量缓存很重要PLC和上位机的数据交换不用搞得太玄乎核心就是把一块内存区域规划清楚双方都按约定好的地址来读写。我用的是西门子S7-1200通信库选择了西门子的S7协议。C#这边有两个常用的库Sharp7和S7.net。对比下来我更习惯Sharp7因为它稳定、体积小、不依赖COM组件。S7.net用起来更“C#风格”但早期版本有个别的内存管理问题。两者都可以关键是选一个就在项目里一用到底不要中途换。PLC侧我单独建了一个DB块名字叫DB100_CMD。里面规划的数据区大概是这样的偏移变量名类型读写方向含义0.0SystemReadyBool上位机-PLC上位机程序启动完成0.1DetectStartBoolPLC-上位机工件到位请求检测0.2DetectDoneBool上位机-PLC检测完成结果有效0.3DetResult_TotalOKBool上位机-PLC判定合格0.4DetResult_BoltNGBool上位机-PLC螺栓问题0.5DetResult_GearNGBool上位机-PLC齿轮问题2.0DetResult_CodeInt上位机-PLC检测结果代码4.0DetTotalCountDInt上位机-PLC总检测数8.0NGTotslCountDInt上位机-PLCNG总数规划好后C#定时器每50毫秒读一次PLC的输入信号检测到DetectStart上升沿时触发一次拍照和检测流程。检测完成后往PLC写DetectDone和结果。为什么用上升沿而不是电平因为如果PLC那边信号保持为True上位机如果用“值等于True就触发”就会每50毫秒触发一次检测。上升沿检测在C#里可以用上一次读值和当前读值做“!prev curr”判断这也是现场最常见的小坑。4.2 机械臂控制方案选择直连还是PLC中转机械臂控制有两种做法上位机直接通过TCP/IP或者现场总线协议控制机械臂或者PLC先接收结果再由PLC通过硬接点/Profinet控制机械臂。我这次选择的是PLC中转理由很实际产线现有的安全逻辑、急停回路、互锁信号都集中在PLC里如果让上位机绕过PLC直接控制机械臂等于把安全逻辑从PLC中抽走了一旦上位机死机机械臂可能处于不可控状态。PLC中转的流程是这样的上位机把检测结果写到DB100PLC的OB1里写了一段逻辑读完结果后根据OK/NG状态去置位机械臂控制指令。机械臂那边有一个机器人控制箱支持Profinet通信PLC作为IO控制器机器人侧映射了几个字节的控制字——启动信号、工件抓取完成、返修流道放行等。机器人程序里有一个主循环等待PLC的抓取指令执行抓放动作后给PLC回一个完成信号。通信连不上的情况必须要考虑。C#这边我做了重连机制每500毫秒检测一次与PLC的Socket连接如果断开界面立刻大红色报警同时影响UI线程去显示“通信中断”并且暂停所有检测任务。PLC侧也做了看门狗如果上位机超过3秒没有刷新一个心跳位PLC就认为上位机异常自动把产线停下来。注意千万不要在通信中断时继续放行工件宁可停车也不放行这是安全底线。4.3 时序设计与异常保护上电、复位、急停都别忘这套系统的时序设计我用的是有限状态机。状态有空闲、等待到位、检测中、结果上报、等待机械臂完成、异常停机。每一个状态都有超时时间一旦超时系统进入异常状态并报警而不是卡在某个状态里不动。比如等待到位信号超时5秒说明可能工件没来或者到位传感器坏了系统要给提示。上电顺序也很重要先启动C#上位机上位机建立与PLC的通信连接然后通过界面按钮让系统“使能”。使能后系统才接受PLC的检测请求。这个顺序能防止意外情况——比如PLC已经发送了检测请求但相机还没有初始化完成直接去拍只能拍出一张黑图或者报错了一张空图。机械臂的动作延时也要考虑进去。检测结果写入PLC后到机械臂真正开始动作中间有几百毫秒的通信延迟和PLC扫描周期延迟S7-1200默认扫描周期一般在10毫秒以内但如果程序写得复杂也会被拖长。在PLC里我加了一个延时定时器检测结果写入后延时300毫秒再触发机械臂启动信号目的是确保数据已经稳定、不会因为上位机还在更新结果而产生竞态。5. 现场调试与常见问题排查实录5.1 误漏检排查先分数据再看算法现场调试里漏检和误杀是永恒的主题。出现误漏检时我建议先做数据回溯不要一上来就调模型参数。具体做法是把所有检测图像按时间存档再按检测结果分类。NG误杀实际是OK被判NG的图像要回看是光照变了、还是工件没到位、还是模型本身的失误。如果图像上看着很清楚模型却判错才是模型的问题如果图像本身就暗、糊、反光那是采集环境的问题。这个项目里我碰到过一次批量误杀排查后发现是因为现场检修时工人把光源亮度调低了。光源不是“调到某个值就一劳永逸”光源会衰减镜头会有灰尘建议做一个定期标定每天换班时拍一张标准工件计算一下平均灰度如果偏离基准值超过15%提示维护人员做清洁和光源调节。这个标定逻辑可以写进C#程序里一键执行不复杂但非常实用。5.2 PLC通信偶发断连与数据错乱S7协议通信偶发断连常见原因有几个一是网络中有IP冲突或交换机端口不稳定二是工控机网卡休眠功能把连接断开了三是PLC侧CPU负载过高响应超时。排查手段就是抓日志看断连的时间点和PLC侧诊断缓冲区做对照。我遇到最隐蔽的一个问题是Windows自带“节能以太网”功能长时间没有大流量数据时网卡会挂起导致通信出现几秒的假死。解决办法是在网卡高级设置里关闭节能以太网和绿色以太网选项。数据错乱的问题往往是地址规划撞了。比如PLC里原有的其他程序也在写同一块DB上位机写进去的值被冲掉。解决方式是跟电气工程师核对DB块分配表确保这段区域是专门留给视觉系统的。S7-1200访问DB块时也存在着数据类型对齐问题Bool按位访问Int按字访问DInt按双字访问。如果你用Sharp7读DB100.DBX0.0和DB100.DBW2但PLC里DBW2实际占用了第2和第3字节要注意边界不要重叠。5.3 节拍不达标怎么优化项目上线后最容易被老板问到的就是“检测会不会拖慢产线”如果节拍跟不上再准也没用。影响节拍的主要瓶颈有三个相机曝光时间、图像传输时间、推理耗时。相机曝光和图像传输这部分一个办法是缩小采集区域只拍工件所在的区域而不是整幅画面分辨率从500万降到200万出来效果差不多但速度提升明显。传输走网线的话确认相机SDK里是否启用了jpeg压缩模式某些千兆网相机在压缩模式下传输带宽能节省一半。推理耗时这块前面说过700毫秒以内通常没问题但如果你的CPU很老可以尝试把输入分辨率从640x640降到480x480代价是精度略降。我的建议是先量化评估在验证集上分别用640和480跑一遍看mAP下降多少如果下降在0.5%以内就可以接受。另外异步预加载也能省时间。工件还在传送带上前移时就可以先把相机SDK缓冲池准备好把模型推理温度跑起来等到位信号一到立即拍照进入推理。这里注意“温度”不是玄学CPU推理第一次会做算子初始化耗时比后续推理大不少预热可以在程序启动时做一次。5.4 视觉漏判现场回放一个易忽略的信号抖动调试期间遇到过一种很头疼的情况某个螺栓位置明明装上了但模型经常报漏装回放图像也看不出问题。后来抓了好几张错判图像对比才发现是工件在到位后仍然有微小的抖动螺栓头部正好处于一个“边缘似有似无”的姿态。尤其螺栓头是内六角的某种角度下反光会形成一个暗斑模型就把它当成背景了。解决办法是双管齐下一是工位增加一个聚氨酯限位块让工件定位更稳从源头减小抖动二是数据增强里增加“随机擦除”和“高斯模糊”的比例让模型对这种极端情况更鲁棒。经过这两步这个位置的误报基本消失了。所以遇到算法搞不定的问题先想机械和电气上能不能改善再回来调模型很多时候效果更好。6. 项目落地后的心得体会与可扩展方向这套系统从入场到稳定运行前后大概花了两个月。第一个月主要在做数据采集、标注和模型训练、验证第二个月做上位机开发、联调、试运行。说实话最难的不是某一个单一技术点而是把视觉、通信、机械臂这三个子系统在时间维度和逻辑维度上捏合起来。单独跑YOLOv8模型精度高不难单独写C#和PLC通信代码也不复杂但要让它们在产线节拍下稳定跑通还会遇到各种意想不到的小问题。如果让我重新做一次有几个事我会在项目启动时就更早做一是提前和机械、电气工程师确认好工位空间和PLC地址分配不要等程序写完了再改二是数据采集阶段就把现场可能的光照异常情况尽量拍全省得后期补拍再重新训练三是上位机的日志从第一天开发就完整打起来不要等到联调时才加。这三个都是“晚了就要多花几倍时间”的教训。再说说可扩展方向。现在这个系统的检测逻辑还比较“专用”——两个类别组合判定。如果后续产线要增加其他部件的检测比如垫片漏装、卡簧缺失、表面划痕最省事的路线是采数据、加类别、重新训练模型框架和软件架构基本不用动。推理这块如果想进一步提速可以研究一下TensorRT部署模型转到engine格式后在NVIDIA显卡上能明显快一截但要忍受更复杂的部署链和驱动依赖。PLC通信端当前用S7协议如果你想换成ModbusTCP或者Profinet IO直连C#这边的代码结构已经跟通信细节做了解耦改动量也不会太大。最后分享一个我坚持很久的习惯上线之后不要急着把技术文档写完就撤一定要在系统里留一个“自检模式”让现场维护人员每天换班时可以一键检查相机、光源、通信、模型推理是否正常。产线设备最怕的不是坏而是坏得无声无息。这个自检模式帮我挡掉了至少三次潜在的大故障——有一次光源驱动坏了图像全黑模型本该无法检测但自检提前发现了。做工业项目稳定比花哨重要这是一个重复过无数次但依然值得反复强调的结论。
返回列表