资讯动态

PCB电路板元件检测数据集:5000张图VOC/COCO/YOLO标签全解析

发布时间:2026/10/1 23:28:09 来源:尧图企业网站定制
简介面向目标检测入门及PCB质检项目这套数据集包含五千张真实电路板元件图片由LabelImg标注标注框质量可靠并同时提供VOC、COCO、YOLO三种格式标签分别存放于不同文件夹可直接喂给YOLO系列模型训练省去自行转换标注格式的环节。压缩包共2000个文件其中xml格式标签约1985个其余为txt标签、Python划分脚本与html教程文档整体大小约748.6MBxml对应VOC格式txt对应YOLO格式py脚本用于按比例划分训练集、验证集和测试集html则承载完整操作指引。除数据本身外还附带Linux与Windows两个版本的YOLO环境搭建教程、训练案例教程以及常用数据划分脚本覆盖从环境准备到训练自己的数据集全流程对刚接触目标检测的读者尤为友好。当前已有967人学习下载适合需要可直接落地的PCB元件检测数据及配套训练方案的开发者、学生和竞赛参赛者。1. PCB元件检测为什么数据集比模型更值钱做目标检测的人迟早会遇到一个尴尬时刻模型结构抄好了、训练环境配好了打开标注工具一看手里没有能用的数据。PCB电路板元件检测尤其如此它不是一个“随便找点图就能训”的任务——板子上的电阻、电容、芯片密集排列丝印被遮挡元件尺寸极小背景反光严重通用数据集训出来的模型在真实板卡上基本是“睁眼瞎”。这套YOLO目标检测PCB电路板元件数据集带5000张图片和VOC、COCO、YOLO三种格式标签解决的正是这个卡脖子问题不必再从零标注拿到就能跑训练而且自带脚本和教程算是入局PCB检测最顺的一条路径。5000张图在目标检测领域不算海量但对PCB这类小目标检测来说是够用的起步盘它覆盖了常见元件类别标签已经做过格式转换省掉了最脏最累的预处理阶段。适合谁做硬件质检、工业视觉、PCB反向工程、学术实验的工程师和研究生都适用。下文按“格式理解 → 目录结构 → 转换脚本 → 划分脚本 → 踩坑 → 训练自验”的顺序把这条链路从头到尾走一遍。2. 理解三种标注格式VOC、COCO、YOLO到底差在哪2.1 VOCXML里的坐标直观但冗余VOC格式的核心是每张图片对应一个XML文件标签集中在object节点里。坐标用bndbox里的xmin、ymin、xmax、ymax表示是像素绝对值。这种格式的好处是肉眼可读打开XML就能人工核对有没有标错坏处是数据冗余一张图有几十个元件就有几十个object块文件体量明显偏大而且XML解析比纯文本慢。对于PCB检测VOC格式主要充当中间态。我从标注工具导出时默认得到的就是VOC接下来再转给COCO或YOLO。如果你在标注时发现某个元件类别特别容易漏检可以直接打开XML人工追加框这个操作在YOLO格式下会把人逼疯但在VOC里只需要照着既有节点复制改坐标即可。这也是很多工业项目坚持“标注用VOC、训练用YOLO”的原因。2.2 COCOJSON字典类别ID 分割信息COCO格式把所有标注聚合成一个annotations数组每个元素包含image_id、category_id、bbox格式是[x, y, width, height]和area字段。关键区别在于COCO的定位框坐标是左上角加宽高而VOC是左上角加右下角转换时需要先做一次减法。另一个隐藏陷阱是category_id在COCO里必须从1开始连续排列不能跳号否则评估脚本直接报错。对PCB元件检测来说COCO格式是“能不能用上现成模型”的分水岭。因为大量预训练模型和框架比如Detectron2、MMDetection、部分YOLO生态的评估代码都以COCO为原生输入。如果你的目标是跑通一个SOTA模型做性能对比COCO格式的标签能让你免去写适配器的过程。数据集里同时给了COCO格式标签意味着你在MMDetection这类框架里可以直接注册数据集路径开训。2.3 YOLOTXT里的归一化坐标训练最省心YOLO格式把每个标注框写成一行文本class_id x_center y_center width height全部相对于图片宽高做归一化取值在0到1之间。这种设计有两个直接后果第一不管训练时输入分辨率怎么变416、640还是1024标注不需要跟着改第二文件极小几千张图的标签加起来不到几兆。用YOLO格式训练时的坑在于坐标边界。比如某个元件的框正好顶在图像边缘归一化后坐标可能出现0.000000或1.000000这种极限值少数版本在增强时溢出报错。常见处理办法是给坐标加一个极小偏移像0.000001或者在预处理阶段就夹到[0, 1]区间。数据集里给的YOLO标签如果碰到“边界框无效”的报错优先排查这一类情况。下面给出三种格式的最小概念对比方便查表理解维度VOC (XML)COCO (JSON)YOLO (TXT)坐标定义xmin,ymin,xmax,ymaxx,y,width,heightx_center,y_center,width,height归一化类别ID任意名称从1开始连续编号从0开始的整数每张图文件数1个XML对应JSON中的一条记录1个TXT可读性高人眼友好中低不直观训练场景中间态/人工校验MMDetection、Detectron2YOLO系列、大多数检测生态3. 把VOC转成COCO和YOLO转换脚本与四个边界坑3.1 目录结构先对齐脚本才不会跑飞拿到数据集后第一步不是打开Python写转换脚本而是先把目录结构固定下来。常见做法是像下面这样组织便于脚本按路径批量操作dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── voc/ │ ├── coco/ │ └── yolo/ └── scripts/ ├── convert_annotations.py ├── split_dataset.py └── train.py有些数据集原目录用的是中文路径或者带空格的目录名我的建议是拿到手先统一改名为纯英文小写路径。因为Windows和Linux的解析差异带空格路径在批量脚本里经常成为翻车源头这种错不体现在早期运行而在某个os.path.join拼接后悄悄失效。目录对齐之后再跑转换脚本遇到问题可以直接用路径名定位省掉大量排错时间。3.2 VOC转YOLO把四个角点算成中心点VOC转YOLO的核心就一个公式把xmin, ymin, xmax, ymax换算成归一化中心点和宽高。代码实现简单但要注意图片尺寸必须读真实图像而非XML中记录的宽高因为标注工具可能记录的是原始分辨率而实际图片被缩放处理过。读真实尺寸用cv2.imread或者PIL.Image.open都行。下面给一个可直接改用的转换函数。import os import cv2 import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_width, img_height, class_dict, out_txt_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_dict: print(f跳过未知类别: {name}, 文件: {xml_path}) continue class_id class_dict[name] bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化计算, 并处理越界情况 x_center ((xmin xmax) / 2) / img_width y_center ((ymin ymax) / 2) / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height # 边缘保护: 防止坐标为0或1导致增强时报错 eps 1e-6 x_center min(max(x_center, eps), 1 - eps) y_center min(max(y_center, eps), 1 - eps) w min(max(w, eps), 1 - eps) h min(max(h, eps), 1 - eps) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))这段代码有几个值得注意的工程设计。第一未知类别用continue跳过而不是直接抛异常这样在转换大批量数据时不会因为一个坏标注中断全部任务但会把跳过的文件打印出来方便后续人工核查。第二epsilon保护是给边缘框上保险防止归一化坐标正好等于0或者1这个值取1e-6足够不会影响交并比计算。第三输出保留6位小数过高精度没有实际意义反而让文件变大。3.3 VOC转COCOcategory_id必须连续area不能只写零VOC转COCO相对繁琐因为YOLO只需要把坐标换算出来写进文本而COCO需要构建完整的JSON结构。这里给出最小实现注意两个最容易出错的字段category_id从1开始连续编号bbox的格式是[x, y, width, height]。此外segmentation字段如果暂时用不上可以填框的四个角点作为多边形避免部分框架读取时报NoneType错误。import json import glob from PIL import Image def voc_to_coco(xml_path_list, img_dir, class_list, output_json): categories [] for idx, name in enumerate(class_list, start1): categories.append({id: idx, name: name, supercategory: component}) images [] annotations [] ann_id 1 for img_id, xml_path in enumerate(xml_path_list, start0): # 提取对应图片, 获取真实宽高 img_name os.path.basename(xml_path).replace(.xml, .jpg) img_path os.path.join(img_dir, img_name) with Image.open(img_path) as img: w, h img.size images.append({ id: img_id, file_name: img_name, width: w, height: h }) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text cat_id class_list.index(name) 1 bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) width xmax - xmin height ymax - ymin annotations.append({ id: ann_id, image_id: img_id, category_id: cat_id, bbox: [xmin, ymin, width, height], area: width * height, iscrowd: 0, segmentation: [[xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax]] }) ann_id 1 json.dump({ images: images, annotations: annotations, categories: categories }, open(output_json, w), indent4)这里的class_list.index(name)是按名称查找索引再1虽然直观但时间复杂度O(n)类别少没问题如果你需要更高的转换吞吐先建立name→id的字典再查。另一个容易被忽略的是图片打开方式这里用PIL.Image.open读真实宽高如果换成cv2.imread要记得注意通道顺序不影响尺寸读取但文件路径含中文时可能返回None需要cv2.imdecode配合np.fromfile处理。3.4 四个边界坑漏检空图、类别名大小写、转码损坏、坐标翻转第一个坑是空XML文件。PCB板元件多偶尔某张图因为光照被标注工具跳过生成0KB的空XML。转换脚本解析时会抛ParseError实战中不应让整个流程崩溃正确做法是先遍历XML并统计文件大小小于1KB的统一单独放一个empty/目录后续人工决定是否补充标注。第二个坑是类别名不一致。同一份数据集里可能出现Resistor和resistor混写XML文本标签里肉眼很难察觉。解决方法是在转换脚本里做一次类别名的归一化映射统一转小写或者统一首字母大写。第三个坑是图片与XML文件不配对。脚本按XML找同名的JPG如果某张图的扩展名是.png或者.jpeg找不到图就能静默跳过训练时就会“有标签没图”或“有图没标签”YOLO会在启动时报找不到对应txt的错误。建议转换前跑一次文件名配对校验。第四个坑是坐标值异常。PCB图像存在旋转标注部分XML里的xmax可能小于xmin或者ymax小于ymin。转换时如果直接减出负宽高后续mAP计算会直接出错。在转换函数开头加一行坐标顺序交换或者直接丢弃该标注是最稳妥的做法。4. 划分脚本为什么不能直接random split按board_id分层更科学4.1 简单随机划分的隐患很多初学者拿到数据集后习惯直接用random.shuffle把图片列表打乱按比例切训练集、验证集、测试集。这种做法对“元素多样”的数据集可以对PCB板卡则可能埋雷。原因在于同一块板子的多张照片之间高度相似甚至只是旋转了角度如果这些相似图一部分进了训练集、一部分进了验证集评估指标就会被严重高估——因为模型见过几乎一样的板子验证集不再代表真实的新板。解决策略是按board_id分层划分而不是按图片划分。board_id是PCB板子编号同一块板的所有图片共享一个编号划分时先对板子编号进行分组然后以组为单位划分。这样验证集里的板子在训练集中完全没有见过指标才反映真实泛化能力。如果你的标注文件里有board_id字段直接用没有该字段就用文件名前缀中的板号部分因为常见的采集流程会命名成board01_cap1.jpg、board01_cap7.jpg这类规则。4.2 一个带stratify的划分脚本import os import random import shutil from collections import defaultdict def split_dataset(image_dir, label_dir, output_dir, train_ratio0.7, val_ratio0.2, seed42): random.seed(seed) images [f for f in os.listdir(image_dir) if f.endswith((.jpg, .png))] # 用文件名下划线前部分作为board_id, 如 board01_cap1.jpg - board01 board_groups defaultdict(list) for img in images: board_id img.split(_)[0] board_groups[board_id].append(img) boards list(board_groups.keys()) random.shuffle(boards) n_train int(len(boards) * train_ratio) n_val int(len(boards) * val_ratio) train_boards set(boards[:n_train]) val_boards set(boards[n_train:n_train n_val]) test_boards set(boards[n_train n_val:]) for split_name, board_set in [(train, train_boards), (val, val_boards), (test, test_boards)]: os.makedirs(os.path.join(output_dir, images, split_name), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, split_name), exist_okTrue) for b in board_set: for img in board_groups[b]: base os.path.splitext(img)[0] # 复制图片和对应的txt标签 src_img os.path.join(image_dir, img) dst_img os.path.join(output_dir, images, split_name, img) shutil.copy(src_img, dst_img) # 注意: 如果原目录是yolo格式, 直接复制txt; 若是voc/coco则需先转换 src_label os.path.join(label_dir, base .txt) if os.path.exists(src_label): dst_label os.path.join(output_dir, labels, split_name, base .txt) shutil.copy(src_label, dst_label) else: print(f警告: 标签缺失 {base}.txt) print(f划分完成: train{len(train_boards)}块板, val{len(val_boards)}块板, test{len(test_boards)}块板)这个脚本的核心是board_groups按board_id聚合图片列表然后对板子列表打乱而不是对图片打乱。比例上train 0.7、val 0.2、test 0.1是PCB检测的常用起点如果数据显示类别不均衡可以再用类别分布的频率进行加权划分但数据集本身类别较为均衡的话这个比例足够。seed42固定随机种子后多次运行结果一致方便你修改参数后对照实验不用每次担心偶然性导致结论漂移。4.3 类别分布检查与显存匹配PCB元件检测的类别往往分布不均。一块板上电阻电容可能有几十个而连接器只有两三个。划分完成后建议先做一个类别分布统计计算每个子集各类别的数量与全集的占比偏差。偏差超过20%的类别需要警惕——一个极端情形是某个稀有小类只出现在train里验证集里一个样本都没有导致验证mAP无法反映真实检测性能。出现这种偏差时需要手动把小类别样本所在board整体调整到验证集让验证集至少包含每类5个以上的实例。显存匹配是容易被忽略的伴随问题。5000张图、每张6000×4000分辨率的PCB图像你不可能全尺寸训练。常规做法是开启rectTrue和batch_size16配合让YOLO对同批次图片做矩形推理节约显存。划分脚本里可以增加一个输出统计信息的功能打印每个子集中图片的长宽比分布。这能为后续设置训练输入分辨率提供直接依据。5. 训练避坑PCB小目标检测的五个常见问题与排查方法5.1 现象loss正常下降但mAP卡在0.2以下不上涨这是PCB检测最常见的问题具体表现是训练日志里cls_loss平滑下降、box_loss也有收敛但验证集mAP始终在0.2附近徘徊。原因几乎总是小目标漏检PCB上的电阻电容目标可能只有8×8像素在416分辨率下经过下采样后特征图里可能只剩一个点。解决方法是更换模型输入分辨率和锚框。从imgsz640提高到imgsz1024或imgsz1280对小目标的收益立刻可见。锚框需要根据数据集重新聚类YOLOv8会自动做自适应锚框计算但旧版YOLOv5需要手动指定anchors参数建议跑一遍k-means脚本生成新锚框再填入配置文件。调整后如果显存不足优先减小batch_size而不是降低imgsz因为分辨率对检测效果的影响远比批量大小显著。5.2 现象训练到一半变成NaNBN层崩溃PCB数据集的标注中某些类别可能出现大量背景区域占比极大的框比如一个元件占整张图面积的30%以上。这种极端长宽比的框在损失计算中梯度极不稳定训练几十轮后loss直接变NaN后续验证指标也变成“全军覆没”状态。原因在于BatchNorm统计量被异常样本冲破尤其是batch_size较小时更容易发生。解决方案分三层第一过滤掉标注框中宽高比超过20:1的异常框这类框大概率是标注失误第二在训练配置中加上label_smoothing0.1对极端预测概率做平滑第三把batch_size从8提高到16或32让BN统计量更稳定。如果改用YOLOv5风格的结构尝试用--nosave配合--noval排查确认是训练还是验证阶段产生的NaN。5.3 现象混淆矩阵总和不唯一看起来是“脏”的不少人在验证阶段调用YOLO的自带指标脚本时发现混淆矩阵每行数值加起来不等于该类的真实样本数。这个现象让很多人误以为评估流程出错实际上这与标签中少量负样本框有关。具体原因混淆矩阵计算过程中部分gt框参与匹配时被ignore忽略区域这些标注既不算TP也不算FP因此矩阵行和列出现“缺失”的数字。这在PCB小目标数据集中尤其常见因为有些元件标得模糊模型预测结果置信度低阈值过滤后就被归到ignore。解决办法是检查数据集中是否有difficult属性或者在评估时把iou_thres从0.45略微调整到0.3观察矩阵形态变化。如果0.3下矩阵明显规整说明评估阈值设置过高不用改数据。5.4 现象验证集很好新板子上一片混乱这里说的“新板子”指完全未出现过的PCB布局。验证集mAP达到0.85以上但换一块新板子检测框全乱跳、置信度跌到0.1以下。这是过拟合到特定板卡背景的信号模型记住了板子的颜色、丝印纹理和元件相对布局而不是学习“电阻是这个小矩形”的通用概念。解决方向有两个一是增加Mosaic和MixUp增强强度让模型见过更多背景组合二是用cutout随机遮挡模仿元件被焊锡遮挡的情况。如果条件允许用不同光照角度重新拍一批新板照片加进训练集效果远好于任何正则化技巧。遇到这种情况不要调模型结构先怀疑数据多样性的缺失。5.5 现象标注目录里有大量空txtYOLO直接报错打开YOLO标签目录os.listdir后发现一批0KB的txt文件。YOLO训练启动时扫描标签文件夹读取0KB文件后会报“Label file X is empty”并直接跳过对应图片导致实际参与训练的有效图片数量远小于预期。原因通常是转换脚本对没有检测到目标对象的XML文件生成了空输出。PCB板上如果拍摄了空板区域这一现象很自然。解决方法是统一保留空文件但用yolo train的--empty参数显式允许空标签参与训练——空样本对判断“这个区域没有元件”是有信息量的。另一种选择是把空文件统一移出标签目录并同步从图片列表里删掉对应图片避免数据集内静默出现不配对的情况。6. 用YOLOv8系列跑通PCB检测训练命令与三个必调参数6.1 最小训练命令YOLO系列里当前最推荐跑PCB检测的是YOLOv8无论是官方Ultralytics版本还是社区改进版对新数据集的适配性都最友好。下面给出一个极简训练入口yolo train modelyolov8n.pt datapcb.yaml epochs100 imgsz1024 batch16 device0其中datapcb.yaml内容大致如下path: D:/dataset/pcb train: images/train val: images/val test: images/test nc: 6 names: [resistor, capacitor, diode, connector, chip, inductor]如果你更习惯YOLOv5的代码库命令风格略有差异但同理python train.py --data pcb.yaml --weights yolov5s.pt --img 1024 --batch 16 --epochs 1006.2 三个必调参数imgsz、anchor、label_smoothingimgsz是PCB检测中最容易产生收益的参数。从默认的640升到1024对小目标AP的改善通常在5-10个点升到1280时收益递减但显存翻倍。建议用1024起步显存够用再试1280。如果显存只有8-12GB固定imgsz1024并调低batch到8即可。第二个关键参数是锚框尺寸。YOLOv8默认启用了自适应锚框但它的自适应是基于COCO的分布先验PCB元件的锚框普遍偏小且比例集中。跑一次针对训练集标签的聚类统计得到常见宽高集中在8×8、12×20、20×8等尺寸然后手动写进模型配置里。这一步最容易被新手跳过但对PCB小目标的收益显著。第三个参数是label_smoothing。PCB元件框密集排列存在大量重叠和遮挡标准的one-hot标签会过度惩罚预测模糊的边界。打开label_smoothing0.1后模型预测概率更平滑验证时mAP不一定立刻涨但训练会更稳定尤其在批次统计量计算上更耐噪。6.3 损失函数选了有讲究改错会翻车网络热词里的“yolo损失函数”在PCB场景下常见误区是无脑替换成最新的损失函数变体。不需要YOLOv8内置的CIoU损失配合DFL已经能覆盖绝大多数PCB场景。改损失函数需要先跑基线确认当前瓶颈确实是边界框回归精度如果mAP卡住的原因是召回率不足优先增大imgsz和换锚框而不是动损失函数。6.4 三个自验方法防止训练完还在黑匣子里第一是算预测框与真实框的IoU分布直方图。训练完成后抽100张验证集图逐个计算TP框的IoU值如果峰值集中在0.7以下说明框偏大或偏小需要回去查锚框设置峰值在0.9以上说明回归收敛良好系统瓶颈在分类混淆。第二是看混淆矩阵的“误检方向”。PCB元件如果经常把电感误检成电容问题往往不是视觉特征差异而是线路上两种元件并排排列、密集接触导致NMS把相邻框合并。这时最有用的改进是调低NMS的IoU阈值从0.45降到0.35或者把anchor数量翻倍。第三是跑一次CAM热力图。用类别激活映射检查模型关注的是元件本身还是丝印背景。如果热力图集中在文字标注区域而非元件本体说明训练时类别混淆较强需要回到数据层面检查标签是否张冠李戴。这一步不用在代码里做——Ultralytics的推理结果本身就带每个框的置信度分布可以按类别打印平均置信度对照排查。6.5 我常用的进阶做法与最后建议5000张图的量级做工业级部署还偏小但作为基线数据集足够验证方案可行性。我常用的路径是先用yolov8n.pt跑通全流程确认mAP能到0.6以上再换大模型做精度优化。如果测试中发现某些元件类别的AP偏低就针对该类做数据增强或补充拍摄而不是盲目增加全部图片数量。切换成yolov6或yolov26版本时需要注意训练参数迁移不是零成本的尤其锚框匹配和loss权重差异较大。我在用到新版本时习惯把验证集预测结果与真值人工对比20张图起步跑完看错在哪里才决定是否继续用。不要迷信新版本一定更强PCB场景的特殊性决定了很多改进点根本触发不到。还要提醒一句PCB电路板元件检测最容易出现“AI实际检测性能与论文数字不符”的落差原因不在模型而在数据分布不一致。如果数据集里的图片拍摄角度全是垂直俯瞰部署时换成侧视角度就会崩溃。上板测试前一定要做多角度、多光照样本的验证这一步老工程师的“玄学”背后其实全是数据域偏移的坑。我自己在这个方向上的习惯是把训练日志里的每轮mAP曲线保存下来每次改参数都对比曲线形状而不仅仅是最终数值。曲线平滑持续上升说明参数方向正确曲线震荡剧烈就要早点停下来排查数据问题而不是硬跑到底。希望帮到你也祝你一次跑通、换板不慌。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑