news 2026/10/6 11:50:21

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 到 1,class 从 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", encoding="utf-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_copper

path 是根目录,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 \ data=data.yaml \ model=yolo11n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/pcb \ name=gpu_run

GPU 多卡:

yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=100 \ imgsz=640 \ batch=32 \ device=0,1 \ project=runs/pcb \ name=multi_gpu

CPU:

yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=50 \ imgsz=640 \ batch=8 \ device=cpu \ project=runs/pcb \ name=cpu_run

Mac(M 芯片):

yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=50 \ imgsz=640 \ batch=8 \ device=mps \ project=runs/pcb \ name=mac_run

device=0 是第一块 GPU,device=0,1 是前两块,device=cpu 强制走 CPU,device=mps 走 Apple 芯片的 Metal 加速。batch 在 CPU 和 Mac 上要调小,否则内存吃满直接崩。imgsz=640 是 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/ymax,YOLO 要的是归一化的中心点加宽高。换算公式是:cx = (xmin + xmax) / 2 / W,cy = (ymin + ymax) / 2 / H,w = (xmax - xmin) / W,h = (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 lines

class_map 是类别名到编号的字典,必须和 data.yaml 的 names 一致。裁剪那两行是后悔药,防止个别标注框超出图像边界导致归一化后出现负数或大于 1 的值。

4.2 四个边界坑

第一个坑是坐标越界。有些标注框的 xmax 等于图像宽度,归一化后 cx 加 w/2 刚好等于 1,YOLO 能接受,但如果 xmax 超过宽度,就会出问题。上面代码里的裁剪就是防这个。

第二个坑是零宽零高框。如果 xmin 等于 xmax,w 就是 0,YOLO 训练时会报错或直接忽略。转换后要过滤掉 w 或 h 小于 0.001 的框。

第三个坑是类别名大小写和空格。VOC 里可能是missing_hole,也可能是missing hole或Missing_Hole,class_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"], label="box_loss") axes[0].plot(df["epoch"], df["train/cls_loss"], label="cls_loss") axes[0].set_xlabel("epoch") axes[0].legend() axes[1].plot(df["epoch"], df["metrics/mAP50(B)"], label="mAP50") axes[1].set_xlabel("epoch") axes[1].legend() plt.tight_layout() plt.savefig("train_curve.png", dpi=150)

这段代码读 ultralytics 自动生成的 results.csv,把损失和 mAP 画出来。列名里带斜杠是 ultralytics 的格式,读进来后 strip 一下防止空格问题。跑完看图,如果 box_loss 在 30 epoch 后还在明显下降,说明还没收敛,可以加 epoch;如果 mAP50 早早平台期,加 epoch 也没用,该回去看数据了。

从那以后我每次拿到新数据集,都强制先跑一遍格式校验、再画一遍训练曲线、最后拿产线图做一次真实分布测试,三步走完才敢说这个数据集能用。这份 PCB 数据集的价值在于它把三种格式和训练脚本都备齐了,省掉了最耗时的格式转换和环境适配,但数据质量到底适不适合你的产线,还得靠上面这套流程自己验一遍。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 11:50:20

激光雷达与RTK标定实战:从坐标系对齐到轨迹精度闭环

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 11:45:50

FPGA多级降采样实战:CIC+FIR从150MHz到1MHz的Vivado实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 11:45:40

射频探针选型与S参数测量:从校准方法到PA匹配电路的特殊处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 11:45:32

MOC3081与MOC3061光耦可控硅驱动芯片:过零触发与电路设计指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 11:44:30

GSM信令实战:LAPDm解析与主叫/切换流程排错

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 11:44:28

Windows下CLion与ESP-IDF环境配置全攻略:ESP32开发避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华