ARTICLE DETAIL

资讯详情

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

PCB表面缺陷检测数据集:1000张图六类缺陷与YOLO11训练实战

PCB表面缺陷检测数据集:1000张图六类缺陷与YOLO11训练实战 简介本资源面向工业视觉与目标检测方向的开发者、算法工程师及高校研究者提供一套真实采集的PCB板表面缺陷检测数据集可用于缺陷检测项目落地也可作为通用工业场景数据的补充。数据集包含1000张高清PCB板表面图片覆盖俯拍正拍与旋转拍摄姿态标注missing_hole、mouse_bite、open_circuit、short、spur、spurious_copper六个缺陷类别采用labelimg标注质量较高。资源包共1个PDF文件大小约6.92MB内附数据集基本情况介绍与获取方式并同步提供VOC、COCO、YOLO三种常见格式标签可直接用于YOLO等算法训练。此外还附赠YOLO11一键训练脚本支持GPU、CPU及Mac多平台方案并给出训练结果日志供参考。目前已有974人学习适合需要快速开展PCB缺陷检测实验与模型验证的读者。1. 从一张漏检的 PCB 说起这份 1000 张图的数据集到底能干什么去年帮一个做 SMT 产线的朋友排查误检问题他们用开源数据训的模型在自家板子上把「spur」和「spurious_copper」几乎全判反了。根因不是模型是训练集里这两类的样本分布和拍摄角度跟产线差太远。这件事让我意识到工业缺陷检测里数据集的质量和标注一致性比换什么网络结构都关键。这份 PCB 板表面缺陷检测数据集就是冲着这个痛点来的1000 张真实采集的高清 PCB 表面图覆盖俯拍正拍和旋转拍摄姿态标注了 missing_hole、mouse_bite、open_circuit、short、spur、spurious_copper 六个缺陷类别同时给出 VOC、COCO、YOLO 三种格式标签还附了一套 YOLO11 一键训练脚本GPU、CPU、Mac 三平台都能跑。适合正在做 PCB 缺陷检测项目、或者想拿工业场景数据补充自己训练集的从业者。2. 六个缺陷类别怎么认标注体系与三种格式的对应关系2.1 六类缺陷的视觉特征与标注边界拿到数据集第一件事不是急着训而是把六个类别的定义和视觉特征过一遍否则后面调参全是玄学。这六类在 PCB 行业里都是常见缺陷但落到标注框上边界容易混。missing_hole 是定位孔或过孔缺失通常表现为本该有孔的位置是完整铜箔或基材标注框一般贴着孔位区域。mouse_bite 是板边被咬掉一块形状像老鼠咬的缺口多出现在板子边缘标注时框住缺口轮廓。open_circuit 是走线断开视觉上是一条铜线中间出现断裂标注框要覆盖断点两侧一小段。short 是短路两条不该连的走线被多余铜连接框住搭接区域。spur 是走线旁边多出来的毛刺状铜细长且方向随机。spurious_copper 是多余的孤立铜块面积比 spur 大、形状更不规则。这六类里最容易混的是 spur 和 spurious_copper前者是「线状毛刺」后者是「块状余铜」。标注时如果框画大了两类会互相污染。我一般会先抽 50 张图把这两类的框逐个看一遍确认标注者的判定尺度再决定要不要在训练时做类别合并或加权。2.2 VOC、COCO、YOLO 三种格式的字段差异数据集同时给了三种格式这不是凑数而是对应不同的训练框架和评估工具链。理解字段差异能帮你在转换和校验时少走弯路。格式文件形态坐标表示类别字段典型用途VOC每图一个 xmlxmin/ymin/xmax/ymax 绝对像素name 标签传统检测框架、部分评估脚本COCO单个 json[x, y, w, h] 绝对像素category_id 数字COCO 评估、多模态工具链YOLO每图一个 txt归一化 cx/cy/w/hclass_index 数字YOLO 系列训练VOC 的 xml 里 size 字段记录了图像宽高坐标是绝对像素读的时候要跟 size 对齐。COCO 的 json 里 images、annotations、categories 三个数组通过 id 关联bbox 是左上角加宽高。YOLO 的 txt 每行是class cx cy w h全部归一化到 0 到 1class 从 0 开始编号。提示三种格式的类别顺序不一定一致。VOC 里是字符串COCO 和 YOLO 是数字转换前务必先导出各自的类别映射表否则会出现「标签对了但类别全错位」的翻车。2.3 用脚本校验三种格式的一致性在训练前我习惯写一个小脚本把三种格式交叉校验一遍确认同一张图的框在三种表示下指向同一个区域。这一步能提前暴露标注文件缺失、坐标越界、类别映射错位等问题。import os import json import xml.etree.ElementTree as ET # 配置三个格式的根目录 VOC_DIR dataset/voc/Annotations YOLO_DIR dataset/yolo/labels COCO_JSON dataset/coco/annotations.json # 读取 COCO建立 image_id - file_name 映射 with open(COCO_JSON, r, encodingutf-8) as f: coco json.load(f) img_id_to_name {img[id]: img[file_name] for img in coco[images]} name_to_anns {} for ann in coco[annotations]: fname img_id_to_name[ann[image_id]] name_to_anns.setdefault(fname, []).append(ann) def check_one(stem): # VOC 框数量 voc_path os.path.join(VOC_DIR, stem .xml) if not os.path.exists(voc_path): return f{stem}: VOC 缺失 tree ET.parse(voc_path) voc_objs tree.findall(object) # YOLO 框数量 yolo_path os.path.join(YOLO_DIR, stem .txt) if not os.path.exists(yolo_path): return f{stem}: YOLO 缺失 with open(yolo_path) as f: yolo_lines [l for l in f.read().splitlines() if l.strip()] # COCO 框数量 coco_anns name_to_anns.get(stem .jpg, []) if len(voc_objs) ! len(yolo_lines) or len(voc_objs) ! len(coco_anns): return f{stem}: 数量不一致 VOC{len(voc_objs)} YOLO{len(yolo_lines)} COCO{len(coco_anns)} return None stems [os.path.splitext(f)[0] for f in os.listdir(VOC_DIR) if f.endswith(.xml)] problems [p for p in (check_one(s) for s in stems) if p] print(f共检查 {len(stems)} 张异常 {len(problems)} 张) for p in problems[:20]: print(p)这段脚本做三件事从 COCO json 反查每张图的标注数量分别统计 VOC 的 object 节点数和 YOLO 的 txt 行数三者不一致就报出来。参数上VOC_DIR、YOLO_DIR、COCO_JSON 按你解压后的实际路径改图片扩展名默认按 .jpg 拼如果你的图是 .png把stem .jpg改成对应后缀。跑完如果异常数为 0说明三种格式在数量层面自洽可以进入下一步如果有异常优先看是不是某张图的某个格式漏标而不是急着改坐标。3. YOLO11 一键训练脚本拆解三平台参数怎么设3.1 脚本结构与 data.yaml 的关键字段一键训练脚本的核心其实是一个 data.yaml 加一条 ultralytics 的训练命令。data.yaml 决定了模型去哪找图、去哪找标签、类别叫什么写错一个字段就是训练能跑但结果全废。# data.yaml path: ./dataset/yolo # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 6 # 类别数六类缺陷 names: 0: missing_hole 1: mouse_bite 2: open_circuit 3: short 4: spur 5: spurious_copperpath 是根目录train 和 val 是相对 path 的图片目录YOLO 会自动把 images 替换成 labels 去找同名 txt。nc 必须等于 names 的长度names 的索引必须和 YOLO txt 里的 class 编号严格对应。我见过最常见的翻车是 names 顺序和标注时的类别顺序不一致导致模型学出来的类别整体错位mAP 看着还行但混淆矩阵一塌糊涂。注意如果你的 YOLO 标签是别人转的一定先用第 2 章的校验脚本确认 class 编号和 names 对得上再开始训练。3.2 GPU、CPU、Mac 三平台的启动命令脚本对三平台的支持本质是 device 参数和依赖安装的差异。ultralytics 会自动探测可用设备但显式指定更稳。GPU 单卡yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns/pcb \ namegpu_runGPU 多卡yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs100 \ imgsz640 \ batch32 \ device0,1 \ projectruns/pcb \ namemulti_gpuCPUyolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs50 \ imgsz640 \ batch8 \ devicecpu \ projectruns/pcb \ namecpu_runMacM 芯片yolo detect train \ datadata.yaml \ modelyolo11n.pt \ epochs50 \ imgsz640 \ batch8 \ devicemps \ projectruns/pcb \ namemac_rundevice0 是第一块 GPUdevice0,1 是前两块devicecpu 强制走 CPUdevicemps 走 Apple 芯片的 Metal 加速。batch 在 CPU 和 Mac 上要调小否则内存吃满直接崩。imgsz640 是 YOLO11 的常用输入尺寸PCB 缺陷里小目标多如果显存够可以试 960但训练时间会明显拉长。3.3 训练日志里该盯哪几个指标脚本附了博主的训练结果日志但别人的日志只能参考趋势不能照搬数值。真正要盯的是三个box_loss、cls_loss、mAP50。box_loss 是边界框回归损失正常应该在前 10 到 20 个 epoch 快速下降然后趋缓。如果它一直高位震荡多半是学习率太大或者标注框质量差。cls_loss 是分类损失六类缺陷如果某类样本特别少cls_loss 会降得慢这时候要考虑类别加权或补样本。mAP50 是 IoU 阈值 0.5 下的平均精度是最终效果的直观指标但别只看它要配合混淆矩阵看哪两类在互相误判。我一般会在训练跑完后立刻导出混淆矩阵重点看 spur 和 spurious_copper 这一对。如果它俩互相误判超过 20%说明标注边界本身就有歧义光靠调参救不回来得回到数据层面重新界定。4. 从 VOC 转 YOLO 的坐标换算四个容易翻车的边界坑4.1 归一化与坐标原点VOC 是绝对像素的 xmin/ymin/xmax/ymaxYOLO 要的是归一化的中心点加宽高。换算公式是cx (xmin xmax) / 2 / Wcy (ymin ymax) / 2 / Hw (xmax - xmin) / Wh (ymax - ymin) / H。W 和 H 来自 VOC xml 里的 size 字段不是图片实际尺寸——如果标注时 size 写错了换算全错。import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) W int(size.find(width).text) H int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像范围内防止越界 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(W, xmax), min(H, ymax) cx (xmin xmax) / 2 / W cy (ymin ymax) / 2 / H w (xmax - xmin) / W h (ymax - ymin) / H lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return linesclass_map 是类别名到编号的字典必须和 data.yaml 的 names 一致。裁剪那两行是后悔药防止个别标注框超出图像边界导致归一化后出现负数或大于 1 的值。4.2 四个边界坑第一个坑是坐标越界。有些标注框的 xmax 等于图像宽度归一化后 cx 加 w/2 刚好等于 1YOLO 能接受但如果 xmax 超过宽度就会出问题。上面代码里的裁剪就是防这个。第二个坑是零宽零高框。如果 xmin 等于 xmaxw 就是 0YOLO 训练时会报错或直接忽略。转换后要过滤掉 w 或 h 小于 0.001 的框。第三个坑是类别名大小写和空格。VOC 里可能是missing_hole也可能是missing hole或Missing_Holeclass_map 的 key 必须和 xml 里的 name 完全一致否则该框会被静默跳过。第四个坑是图片和标签不同名。YOLO 靠文件名匹配图片和标签001.jpg必须对应001.txt。如果 VOC 的 xml 名和图片名不一致转换后要统一重命名。提示转换完先跑一遍第 2 章的校验脚本确认 VOC 和 YOLO 的框数量一致再开始训练。5. 小目标漏检与类别混淆的排查三个高频问题5.1 现象spur 几乎全漏检原因spur 是细长毛刺在 640 输入下可能只占几个像素YOLO11 的 P3 特征图下采样 8 倍后小目标特征几乎消失。另外如果训练集里 spur 样本占比低模型会偏向多数类。解决把 imgsz 提到 960 或 1280让细长目标保留更多像素在 data.yaml 里对 spur 做类别加权或者用 copy-paste 增强把 spur 样本复制到更多图上。我一般先提 imgsz如果显存不够再考虑切图训练。5.2 现象short 和 spurious_copper 互相误判原因这两类在局部视觉上都是「多余的铜」short 是连接两条线的铜spurious_copper 是孤立铜块。如果标注时框的范围偏大short 的框里会包含周围走线模型学到的特征就混了。解决回到标注层面把 short 的框收紧到搭接点spurious_copper 的框只包孤立铜块。如果重标成本高可以在训练时对这两类做更强的颜色和形态增强逼模型学更本质的特征。但说实话标注歧义靠增强只能缓解根治还得重标。5.3 现象验证集 mAP 高但产线误检多原因验证集和训练集同分布但产线图片的拍摄角度、光照、板子颜色和数据集不一样。数据集虽然覆盖了俯拍和旋转姿态但不可能覆盖所有产线条件。解决从产线采一批图人工标 50 到 100 张做测试集先看模型在真实分布上的表现。如果掉得厉害用这批图做微调学习率调小到 1e-4 量级只训 20 到 30 个 epoch。这一步是血泪经验别等上线了才发现。6. 用训练结果日志反推数据质量一个验证习惯博主附的训练结果日志最大的价值不是让你抄超参而是让你拿自己的训练曲线跟它对比反推数据质量。我一般会做三件事。第一把日志里的 box_loss 曲线和自己的画在同一张图上。如果自己的 box_loss 收敛得明显慢先怀疑标注框质量而不是模型。PCB 缺陷里 missing_hole 和 open_circuit 的框如果画得松回归损失会一直下不去。第二看日志里每个 epoch 的 mAP50 波动。正常训练 mAP50 是震荡上升的如果出现大幅跳水再回升多半是某个 batch 里有脏数据比如零宽框或类别错标。这时候用第 4 章的转换脚本重新过一遍标签把异常框清掉。第三拿日志里的最终 mAP50 当基线但别当目标。1000 张图六类缺陷YOLO11n 在 640 下 mAP50 能到 0.7 以上就算正常具体数值取决于标注质量和类别平衡。如果只有 0.4 左右先别调模型回去查数据。import pandas as pd import matplotlib.pyplot as plt # results.csv 是 ultralytics 训练后自动生成的日志 df pd.read_csv(runs/pcb/gpu_run/results.csv) df.columns [c.strip() for c in df.columns] fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[0].plot(df[epoch], df[train/cls_loss], labelcls_loss) axes[0].set_xlabel(epoch) axes[0].legend() axes[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[1].set_xlabel(epoch) axes[1].legend() plt.tight_layout() plt.savefig(train_curve.png, dpi150)这段代码读 ultralytics 自动生成的 results.csv把损失和 mAP 画出来。列名里带斜杠是 ultralytics 的格式读进来后 strip 一下防止空格问题。跑完看图如果 box_loss 在 30 epoch 后还在明显下降说明还没收敛可以加 epoch如果 mAP50 早早平台期加 epoch 也没用该回去看数据了。从那以后我每次拿到新数据集都强制先跑一遍格式校验、再画一遍训练曲线、最后拿产线图做一次真实分布测试三步走完才敢说这个数据集能用。这份 PCB 数据集的价值在于它把三种格式和训练脚本都备齐了省掉了最耗时的格式转换和环境适配但数据质量到底适不适合你的产线还得靠上面这套流程自己验一遍。希望帮到你。本文还有配套的精品资源点击获取
返回列表