尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLOv8的PCB缺陷检测实战:从数据标注到边缘端部署

基于YOLOv8的PCB缺陷检测实战:从数据标注到边缘端部署 去年接了个PCBA代工厂的预研项目要用视觉方案检测PCB裸板上的缺陷。设备预算卡得紧每个工位都上工业级AOI不太现实于是想到了基于YOLOv8做一套轻量级的PCB缺陷检测方案。折腾了一个多月从环境配置到数据标注、从模型调优到边缘端部署踩了不少坑也积累了不少一手经验。今天把这段完整过程整理出来如果你正准备做相关方向的毕业设计或者在评估产线质检的智能化改造这份实操记录应该能帮你少走很多弯路。1. PCB缺陷检测为何选择YOLOv8方案对比与项目边界1.1 传统视觉方案在PCB缺陷检测上的局限先聊聊为什么没有走传统机器视觉的老路。PCB裸板缺陷检测最常见的需求是检测短路、断路、缺口、毛刺、余铜这些问题看起来都是形状异常似乎用模板匹配、差影法就能搞定但实际落地时坑非常多。第一个问题是公差。PCB制程本身有工艺公差同一批次板子的走线宽度、焊盘位置都存在细微差异。模板匹配要求待测图像与标准模板严格对齐稍有偏差就会在差值图像上产生大量假阳性把正常的工艺波动当成缺陷报出来。我当时用OpenCV做过一轮差影法测试无论如何调形态学参数误报率都压不下去根本原因是铜箔边缘的亚像素偏移在差值图上形成了大量伪边缘。第二个问题是缺陷形态的多样性。短路不一定是两根线连在一起可能是细小的铜渣搭桥断路也不一定是线完全断开可能是线宽局部收窄到临界值。这些缺陷用固定规则描述非常困难往往是看到了能认出来但写不出公式。Halcon那套算子虽然强大本质还是靠工程师手工设计特征和逻辑规则换一种板卡、换一种光源规则就要重新调一遍。目标检测网络恰好绕开了这个问题。它不显式定义什么是缺陷而是从大量标注样本中隐式学习缺陷的视觉特征面对工艺波动和形态多样时鲁棒性更好。这也是为什么我会把方案重心放到深度学习方法上。1.2 为什么是YOLOv8而不是其他网络现在工业缺陷检测可选的网络不少Faster R-CNN、YOLOv5、YOLOv8、还有更新的YOLOv9、v10。我最终选YOLOv8核心是三点。第一是推理速度与精度的平衡。Faster R-CNN在精度上确实扎实尤其小目标上因为有RPN和ROI Align表现优秀但在1660Ti这类工控机上跑不到实时。PCB检测虽然不像自动驾驶那样要求毫秒级响应但产线节拍通常只有几秒甚至一秒多Faster R-CNN在CPU或低端显卡上容易成为瓶颈。YOLOv8的anchor-free设计、C2f结构和Decoupled Head在同等算力下能拿到明显更好的速度和精度组合。第二是生态成熟度。YOLOv8依托ultralytics这个框架训练、验证、导出、推理全链路通吃命令行和Python API都齐全。数据格式、预训练权重、调参入口全部标准化没有乱七八糟的历史包袱。YOLOv9和v10我也关注过个别指标确实更好看但工业项目求的不是榜单分数是稳定性——背靠活跃社区和成熟文档的YOLOv8遇到问题能搜到大量解决方案。第三是对小目标的先天支持。PCB缺陷是典型的小目标很多缺陷只有几十个像素。YOLOv8在neck部分和anchor-free分支上做了针对性设计配合高分辨率输入小目标召回率比我预想的好。1.3 检测目标的定义哪些缺陷值得做做工业项目第一件事不是急着跑模型而是把检测目标定义清楚。我这边检测的是PCB裸板外观缺陷选取了工业界最常见的六类缺陷类别英文标识典型表现漏孔missing_hole焊盘或过孔位置没有打孔鼠咬mouse_bite铜箔边缘出现半圆弧状缺损断路open_circuit走线不完全断开或线宽严重收窄短路short两根不该相连的走线被铜连接毛刺spur铜箔边缘延伸出尖刺状突起余铜false_copper非走线区域残留的铜箔碎片需要强调的是这个范围刻意排除了焊点检测和贴片元件的锡膏检测——那是另一套光学方案和检测逻辑混在一起做会让数据集和目标定义都变得模糊。做检测项目先画边界边界不清后面全乱。2. 环境配置与数据集准备从零到可训练状态2.1 开发环境搭建1660Ti这种入门显卡也能跑先说环境。我用的是GTX 1660Ti6GB显存属于入门级显卡。很多人担心这个配置跑不了YOLOv8实际上完全能跑只要别盲目贪大模型就行。我的环境组合供参考系统Ubuntu 20.04Windows也能跑但后续部署到Linux工控机时会有环境迁移成本Python3.9CUDA11.8PyTorch2.xpip安装时会自动匹配CUDA版本ultralytics8.1.0安装命令很简单顺序很重要conda create -n yolo python3.9 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics装完验证一下能正常打印版本号就说明环境OKyolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg这里有个容易踩的坑先装ultralytics再装torch或者直接pip install ultralytics让它自动装torch大概率会装上CPU版torch。训练时进度条半天不动才发现GPU根本没用上。装完之后用python -c import torch; print(torch.cuda.is_available())检查返回True再往下走。2.2 数据从哪来公开数据集与自采数据的配合数据集是最费时间的环节。PCB裸板缺陷这边有一个公开的PCB_DATASET数据集包含一千多张真实PCB图像覆盖上面说的六类缺陷标注格式是VOC格式的XML需要转成YOLO格式。这个数据集做算法验证和毕设完全够用但如果要落地到特定产线必须补充自己采集的实际板卡图像。为什么必须自采因为公开数据集和你产线上的板卡在板材颜色、铜厚、走线密度、光照环境上都有差异。算法是所见即所学训练时没见过你产线上的场景部署时就会水土不服。自采数据有几个细节值得注意。首先是相机选型面阵相机加远心镜头是首选远心镜头能消除透视畸变保证成像尺寸一致性其次是光源PCB铜面是高反光材质推荐低角度环形光或同轴光能有效抑制铜面反光带来的过曝。我用的是低角度红光加蓝光组合铜面在红光下呈亮色、基材在蓝光下呈深色对比度非常舒服。2.3 标注规范与格式转换细节决定模型上限公开数据集已经有标注但格式是VOC。转换时注意坐标系的坑VOC是左上角x1y1、右下角x2y2YOLO是归一化的中心点x_center、y_center和宽高w、h而且YOLO要求归一化到0~1之间。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) with open(out_path, w) as f: for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: continue cls_id class_map[cls_name] box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)自采数据的标注我用的X-AnyLabeling。这个工具支持加载YOLOv8模型做自动标注先用已有模型在图片上跑一遍预标注再把框拖拽修正效率比从头画框高很多。标注规则上有一条关键经验PCB缺陷普遍小且边缘锐利框必须紧贴缺陷边缘宁紧勿松。框太松会让模型学到大量背景特征推理时把正常铜箔也框进来。标注完成后按8:2划分训练集和验证集。注意划分前先按板卡批次分组避免同一块板子的不同图像同时出现在训练集和验证集里那会导致验证集虚高上线后原形毕露。2.4 数据增强工业场景别撒欢ultralytics自带数据增强Mosaic、随机透视、色彩抖动、翻转都有。但对PCB这种高精度检测场景增强策略要克制。Mosaic默认开启前几个epoch会明显提升模型对不同光照和背景的适应能力。但有个细节训练最后10个epochultralytics默认会关闭Mosaicclose_mosaic参数让模型从高噪声增强回归到真实数据分布上微调收敛。这个默认行为很合理不用改。翻转增强我建议关闭或在数据划分时特别小心。PCB板有丝印文字和元器件方向性垂直翻转后文字倒置、引脚方向相反虽然缺陷本体还在但语义发生了变化。模型学到的特征可能和真实场景不符反而降低泛化能力。我实测下来对PCB检测最有用的增强是HSV色彩抖动和轻微的光照变化。把hsv_h设为0.02hsv_s设为0.6模拟产线不同时段光线的轻微波动能明显提高模型的抗光照干扰能力。3. 模型训练从默认参数到自定义数据集的调优过程3.1 模型规格选择n、s、m怎么选YOLOv8有n/s/m/l/x五个版本由浅到深。PCB缺陷是典型小目标理论上模型越大特征提取能力越强但数据量只有一千多张模型过大容易过拟合而且1660Ti跑不动大模型。我的建议是s起步n做对照组。几个版本在COCO上的基础数据供参考模型参数量计算量COCO mAP50-956GB显存能否训练YOLOv8n3.2M8.7 GFLOPs37.3轻松YOLOv8s11.2M28.6 GFLOPs44.9可以batch需控制YOLOv8m25.9M78.9 GFLOPs50.2勉强需小batch实测下来YOLOv8n在PCB数据集上mAP50能到0.88左右v8s能到0.93以上m的提升幅度有限但训练时间和显存占用明显上升。工业项目我推荐直接用s精度和速度均衡最理想。如果你算力紧张n也能用只是对极小缺陷的召回率会差一些。3.2 训练超参数基于6GB显存的实际配置这是我的训练配置文件直接能跑# pcb.yaml path: ./datasets/PCB train: images/train val: images/val nc: 6 names: [missing_hole, mouse_bite, open_circuit, short, spur, false_copper]训练命令yolo detect train \ datapcb.yaml \ modelyolov8s.pt \ epochs200 \ imgsz640 \ batch16 \ optimizerAdamW \ lr00.001 \ patience30 \ ampTrue \ projectruns/pcb \ nameexp1参数说明几个关键点imgsz640还是1280PCB_DATASET原始图像大约1600x1600直接缩到640会让很多小缺陷缩到十几个像素甚至几个像素丢失特征。我的经验是先640跑通流程然后训练一个1280版本对比。1280版本的mAP50通常能提升2到3个点但当显存只有6GB时1280分辨率加s模型batch只能开到4左右训练速度很慢。实操中更推荐先用640训练快速迭代最终模型确定了再用1280重新训练一版作为上线模型。batch怎么定6GB显存跑s模型640输入batch16配合自动混合精度amp刚好能跑显存占用约5.5GB。如果爆显存优先降batch到8不要轻易降分辨率。epochs和patience。1000多张图的数据量150到200个epoch足够设一个patience30提前停止。如果训练到50个epoch还没有任何改善说明数据或参数有问题不是时间不够。优化器。工业落地我推荐AdamW收敛稳定对学习率的敏感度比SGD低不容易跑飞。用SGD调得好的话mAP可能略高但调参成本高不是最优性价比。3.3 训练曲线怎么读三个损失函数各有脾气训练过程中ultralytics会在runs/pcb/exp1/下输出三张损失曲线图box_loss、cls_loss、dfl_loss。刚开始接触YOLOv8的人容易盯着loss绝对值看这是个误区——不同损失函数的量纲不同绝对值大小没有可比性要看的是下降趋势和收敛情况。box_loss是边界框回归损失YOLOv8用的是CIoU加DFL的组合。它前期下降明显后期会呈现小幅震荡尤其在最后一个epoch Mosaic被关闭后可能会有轻微回升这正常。cls_loss是分类损失收敛趋势比box_loss更平缓。如果cls_loss出现先降后升的反弹同时验证集精度在下降基本可以断定过拟合了。这时候应该回调patience、增加数据增强力度或者直接用验证效果最好的那个epoch的权重也就是best.pt不要用last.pt。dfl_loss是distribution focal loss负责框边缘的精细回归。这个loss对PCB这种需要精确定位的场景很重要。如果dfl_loss迟迟不降往往和标注框不准确有关——标注的框边缘质量不高模型无论如何都拟合不了。3.4 经典问题数据不均衡导致的单类AP偏低PCB_DATASET的六类缺陷样本量并不均匀short和spur的样本多missing_hole的样本量明显偏少。训练完看每个类别的PR曲线时missing_hole的AP大概率垫底这是典型的样本不足表现。处理数据不均衡有几种思路按见效速度排序第一优先是补数据。去产线多拍各种孔径、位置的漏孔板扩充样本量。如果实在补不了对漏孔区域做裁剪复制粘贴但要注意粘贴位置不要和原图重叠语义更自然。第二是调整损失权重。ultralytics没有直接暴露类权重参数但可以在数据加载层面动手脚——使用过采样让少量类别在训练时被抽样到的概率更高。简单实现可以复制数量少的类别样本到额外文件但注意要把复制样本放在同一个划分集里避免验证集和训练集重复。第三是调整置信度阈值。在推理阶段对个别类别降低conf-thres来提高召回率虽然会增加一些误报但在产线场景中误报可以靠复检工位兜底漏检才是真正致命的。4. 实测中的误检与漏检根因排错链路与针对性优化4.1 最大的误检来源铜面纹理与反光模型训练完验证集mAP50有0.93当时觉得稳了。拿到产线测试板一跑发现问题远比预想的多——最多的误报出现在大面积的铜皮区域正常走线之间的夹角、铜面的划痕、甚至丝印字符的边缘都被模型当成毛刺和余铜报了出来。回到图像上分析根因是训练样本的背景多样性严重不足。公开数据集的图像里背景以深色基材为主铜面纹理干净但实际产线的板子有铜箔织纹、有划痕、有字符丝印这些在模型看来都是没见过的怪东西干脆一梭子全报为缺陷。这也是我前面强调自采数据的重要原因。模型对背景的鲁棒性完全取决于训练时看了多少种背景。没有足够的真实背景样本再好的网络结构也扛不住场景迁移。4.2 小目标漏检输入分辨率与切片推理另一个典型问题是极小缺陷的漏检。鼠咬和缺口这类缺陷在640分辨率下往往只有20x20像素左右特征极少模型容易漏。提高输入分辨率是最直接的解法。我对比过同一批数据在640和1280分辨率下的测试结果1280版本的small类缺陷召回率提升了近10个百分点。代价是推理速度大幅下降——1660Ti上yolov8s在640输入下推理约40ms1280下直接翻倍到接近100ms。如果产线节拍紧张还可以考虑切片推理SAHI方案把大图切成若干小图分别推理再合并结果。PCB板图像尺寸大、缺陷尺寸小切片推理非常契合而且可以把输入分辨率相对降低整体速度和精度都比直接高分辨率推理划算。4.3 一个典型的误检排查过程完整链路复现拿个真实案例带你走一遍排查流程。当时的情况是验证集mAP50高达0.92但小批量产线测试时余铜false_copper一类疯狂误报达到了每块板15次以上完全不可用。排查第一步我看的不是网络而是数据分布。把所有误报检测框在原图上叠加输出发现误报集中在两个区域大面积铺铜的角落、字符丝印的边缘。这些位置在铜面反光下局部纹理与真实余铜缺陷高度相似。第二步回看训练集。余铜这个类别的标注样本主要来自公开数据集形态单一样本里几乎没有复杂字符丝印边缘这种困难负样本。模型学到的余铜特征偏理想化——孤立的、形状明显的铜渣自然会把真实场景中模样相近的纹理全纳入进来。第三步针对性修数据。我从产线拍的大量正常板子中截取包含字符丝印、铺铜角落、正常走线夹角的区域作为负样本加入训练。这里有一个重要概念负样本不是标注为无缺陷就行而是让模型看到这些复杂但正常的区域不产生检测框。在YOLO体系里背景区域本身就是隐式的负样本提供越多的困难背景模型学到的背景特征就越准确。第四步调整推理阈值。训练集补充后重训一轮误报降到每块板一两次。随后把conf-thres从0.25提到0.4误报归零同时观察漏检率没有明显上升。最终余铜这一类的可用性完全达标。4.4 不同缺陷的阈值策略后处理的工程手段YOLOv8原生的推理命令只支持统一阈值但实际产线中不同缺陷的容忍度是不一样的。例如短路是致命缺陷必须高召回宁可容忍一些误报而毛刺如果很微小可能不影响电气性能误报多了反而干扰生产。ultralytics没有提供per-class阈值参数但自己写后处理很简单推理时把conf-thres设得低一些比如0.05拿到所有候选框然后在后处理代码里按类别分别过滤from ultralytics import YOLO model YOLO(best.pt) results model.predict(sourcetest.jpg, conf0.05, iou0.5, verboseFalse) class_threshold { 0: 0.2, # missing_hole 漏孔要求高召回 1: 0.3, # mouse_bite 鼠咬 2: 0.4, # open_circuit 断路 3: 0.3, # short 短路高召回 4: 0.5, # spur 毛刺压误报 5: 0.5, # false_copper 余铜压误报 } for r in results: boxes r.boxes keep [] for i in range(len(boxes.cls)): cls_id int(boxes.cls[i]) if boxes.conf[i] class_threshold[cls_id]: keep.append(i) # 按 keep 序号过滤检测框输出给下游这个技巧帮我解决了大量类别间置信度分布不一致的问题。核心原因是不同缺陷的形态复杂度和背景干扰程度不同统一阈值永远只能委屈一头。5. 模型导出与产线部署从验证到落地的最后一公里5.1 导出ONNX验证一致性模型在PyTorch环境里跑得好不等于部署环境里一样好。第一步是导出ONNX并在导出后做一致性校验。yolo export modelbest.pt formatonnx opset12导出后在ONNX Runtime下跑同一张图拿结果和PyTorch推理对比。实际使用中FP32 ONNX与PyTorch的结果几乎一致可以放心。如果发现检测框偏移或置信度明显变化优先怀疑是预处理差异——ultralytics在推理时做了letterbox缩放ONNX部署时要用完全相同的预处理逻辑。导出的几个注意事项固定batch size能降低算子复杂度但产线推理通常是动态batch建议导出时加dynamicTrueopset版本选择12到17都可以太老的新算子不支持太新的在部分推理框架里可能报错。5.2 TensorRT加速精度与速度的平衡在1660Ti上PyTorch直接推理yolov8s单帧640大约40msONNX Runtime下也差不多。但部署设备往往不止跑一个模型还要做图像预处理、结果后处理时间预算必须留足。TensorRT是N卡上绕不开的加速方案。TensorRT FP16量化后的模型实测推理时间可以压到10ms以内速度提升4倍以上而mAP50掉点通常在0.5个百分点以内对于PCB缺陷检测这种任务完全可接受。INT8量化我一直不建议直接用。PCB图像的缺陷和背景对比度不高INT8量化后小目标的响应值容易被量化噪声淹没出现莫名其妙的漏检。FP16的精度和速度已经足够好不必冒INT8的风险。5.3 边缘设备部署RK3588的NPU路线产线工控机如果不想配独显边缘设备是更经济的选择。目前国内用得比较多的是RK35888核CPU加6 TOPS NPU部署YOLOv8s这种量级的模型很合适。RK3588的完整部署流程大致是PyTorch模型导出ONNX然后在PC上用rknn-toolkit2把ONNX转换成RKNN格式支持INT8和FP16混合量化转换过程中需要准备一些校准图片用于量化。转换完成后在板端用RKNN Runtime的Python或C API调用。实际踩坑最多的是算子兼容性。YOLOv8中某些上采样算子和split操作在RKNN转换时会报不支持或警告处理办法一般是修改模型结构用板端支持更好的算子替代比如用最邻近上采样替代某些特定实现。这一步需要查RKNN的算子支持列表不同版本的rknn-toolkit2支持情况也有差异。部署后性能预期yolov8s在RK3588上INT8量化后640输入的单帧推理时间大约在30ms到50ms之间可以满足慢速产线的节拍。如果节拍更快就得考虑yolov8n版本或者做切片并行推理。5.4 从模型到完整检测系统最后一步是把模型嵌进检测链路这可能是工期占比最被低估的环节。一个完整的PCB检测工位需要处理几件事相机触发与图像采集必须和产线PLC联动。通常用接近开关或光电传感器触发相机采图后立刻送入检测程序检测结果再回传给PLC由PLC控制NG/OK分选机构或报警灯。这个闭环整个走下来单板检测时间预算要在产线节拍内所以模型的推理时延只是其中一环图像传输、预处理、I/O通信的时间都要算进去。误检率和漏检率在生产中的权衡策略也是和工艺一起定的。电性能相关的缺陷如短路、断路漏检会造成客诉必须高召回外观类的缺陷如毛刺、余铜不直接影响电气性能可以适度压误报。产线上的常规配置是检测程序标记NG的板子不直接判废进入人工复检工位。操作员每天面对大量误报会疲劳过度压误报又会漏真缺陷这个平衡点必须根据实际NG率动态调整。上线前还有一个常规但非常有效的动作把历史积累的NG图片全部跑一遍统计badcase分布。这一步能在正式投产前暴露大部分场景迁移问题提前规避生产事故。个人体会与扩展建议整个项目走下来我最大的体会是YOLOv8只是工具真正决定缺陷检测上线效果的是数据和图像采集质量。模型选型半小时就能定训练一轮也就一两个小时但数据标注和实地测试花掉了整个项目百分之六十以上的时间。如果你刚起步别急着追求新模型新算法先把数据质量做扎实YOLOv8s就已经能顶住绝大多数PCB缺陷检测场景。最后分享一个小技巧上线后不要只盯着mAP在推理代码中加一个保存badcase的逻辑把误报和漏检的样本自动存下来定期回灌到训练集里重新微调。这个是工业部署里最实用也最容易被忽略的迭代闭环——模型的精度不是训练一次定终身的而是随着生产数据积累不断成长的。
返回列表