ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

机械零件目标检测数据集:VOC与YOLO双格式解析及训练实践

机械零件目标检测数据集:VOC与YOLO双格式解析及训练实践 简介面向机械零件目标检测任务提供一份包含5913张真实工业零件图像的数据集覆盖轴承、螺栓、法兰、齿轮、螺母、弹簧六类常见零件累计24049个矩形标注框。数据同时提供Pascal VOC与YOLO两种标注格式XML文件适合Faster R-CNN、SSD等框架直接训练TXT文件可无缝接入YOLO系列流程省去格式转换耗时。压缩包约224.71MB共2000个文件以1999个XML标注文件为主体另附1个说明TXT所有标注均由labelImg工具按统一规则绘制类别样本较为均衡边界框质量可靠可直接用于模型训练、迁移学习或数据增强研究。XML与TXT并存的目录结构也便于同时开展不同框架的对照实验。已有923人学习下载适合高校实验教学、工业视觉项目预研以及目标检测算法调参练习。1. 这份目标检测数据集5900张5类机械零件VOC与YOLO双格式能帮你省下什么做工业视觉检测的人都有过这种体验找数据集比调模型更折磨。COCO那80类里没有螺栓、螺母这类零件的近景样本网上下到的图格式又五花八门自己标6000张得熬两周。这份常见机械零件目标检测数据集正好卡在这个缺口上——5900张图、5类零件、VOCPascal VOC的XML标注和YOLOTXT标注两套格式都备齐。它的核心价值不在图片多而在于你解压后就能直接跑通YOLOv8或YOLOv11的完整训练管线把时间花在模型调优和业务改造上而不是耗在格式转换和数据清洗上。新手用来入门老手拿来当迁移学习的起点都合适。2. 拆开zip看门道VOC的XML标注和YOLO的txt标注到底差在哪下载解压后你会看到一个结构清晰的数据集目录。虽然具体子目录名可能因制作习惯不同略有变化但机械零件数据集最常见的组织方式是VOC和YOLO两套标注同时提供目录大致长这样。2.1 目录结构一套图片两套标注三个清单文件common_mechanical_parts/ ├── JPEGImages/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── Annotations/ │ ├── 000001.xml │ ├── 000002.xml │ └── ... ├── labels/ │ ├── 000001.txt │ ├── 000002.txt │ └── ... ├── ImageSets/ │ └── Main/ │ ├── train.txt │ ├── val.txt │ └── trainval.txt └── data.yamlJPEGImages放原图Annotations放Pascal VOC格式的XMLlabels放YOLO格式的TXT。注意这套结构的关键点同一批图片对应两套标注内容等价但格式不同便于你选择任一套训练框架。ImageSets/Main下的三个txt是VOC规范里的划分清单train.txt是训练集图片名、val.txt是验证集图片名、trainval.txt是两者合集。data.yaml是给ultralytics这类框架用的可选配置文件。有的zip还会在根目录放README或标签说明讲清楚五个类别的命名和ID映射这部分信息比图片本身更值钱训练前一定要先看。2.2 VOC XML树状结构中藏着每个目标的绝对坐标随便打开一个XML你会看到类似这样的内容annotation folderJPEGImages/folder filename000001.jpg/filename size width1280/width height720/height depth3/depth /size object namebolt/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin356/xmin ymin210/ymin xmax412/xmax ymax268/ymax /bndbox /object object namenut/name bndbox xmin430/xmin ymin300/ymin xmax475/xmax ymax345/ymax /bndbox /object /annotation重点关注object节点里的name和bndboxname是类别名bndbox的四个值是目标框在原始图片上的像素坐标左上角xmin/ymin右下角xmax/ymax。VOC格式最大的优点是直观可读性好用LabelImg标注的默认输出就是这个方便你手工核对缺点是文件体积大解析慢每多一个目标就多一层XML嵌套。2.3 YOLO TXT归一化坐标直接喂给训练框架YOLO格式的txt文件每行对应一个目标0 0.300781 0.331944 0.043750 0.080556 1 0.353516 0.447917 0.035156 0.062500五个数字分别是类别ID、归一化中心x、归一化中心y、归一化宽w、归一化高h。坐标除以了图片宽高值域在0到1之间。换算回去中心点是385×239框宽56、高58和前面XML的bndbox其实框同一个位置。这份数据集比较好的地方是两套标注由同一次标注生成框的位置对得上。但如果你以后要自己扩展数据集千万别手工维护两套标注用脚本从一侧生成另一侧。我见过一个人手工改了一百张图的txt结果有7张框偏移了十几个像素训练精度怎么都上不去最后用脚本重转才解决。2.4 VOC转YOLO必须先做归一化的换算如果你想在自建数据上复制这套格式或者需要把VOC的XML转成YOLO训练用的txt直接看这段脚本import os import xml.etree.ElementTree as ET voc_dir Annotations yolo_dir labels class_map {bolt: 0, nut: 1, washer: 2, bearing: 3, gear: 4} os.makedirs(yolo_dir, exist_okTrue) for xml_file in os.listdir(voc_dir): tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) out_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h out_lines.append(f{class_map[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(os.path.join(yolo_dir, os.path.splitext(xml_file)[0] .txt), w) as f: f.write(\n.join(out_lines))逻辑说明先读取XML里的图片宽高再遍历每个object的bndbox把像素坐标转换成归一化中心点坐标和归一化宽高最后按YOLO五列格式写入同名txt。class_map是你自己定义的类别映射必须和最终训练时的data.yaml完全一致。参数说明w和h来自XML的size节点换算时除以宽和高是分开的不要搞反坐标统一用浮点防止整数除法丢精度。转换完成后用第3节的校验脚本跑一遍确认没有越界和空文件。2.5 类别ID映射一份data.yaml就够了不管用VOC还是YOLO喂给ultralytics的标注最终是数字ID。5类零件常见是螺栓、螺母、垫圈、轴承、齿轮这类通用件下面以这套映射为例演示data.yaml是怎么回事path: common_mechanical_parts train: ImageSets/Main/train.txt val: ImageSets/Main/val.txt names: 0: bolt 1: nut 2: washer 3: bearing 4: gearpath是数据集根目录相对路径train和val指向图片名清单names列表定义了ID到类名的映射顺序不能乱。如果你直接使用labels目录则train和val应指向images、labels这两个子目录共同存在的路径例如train: images/train配合labels/train。两套标注选一套使用即可不要在同一份data.yaml里混用XML和TXT数据加载器会迷路。3. 动手跑通YOLO训练从环境配置到第一个epoch3.1 环境准备ultralytics装好先自检再干重活现在跑YOLO训练最省心的就是ultralytics这套API支持YOLOv8/YOLOv11。环境部分其实并不复杂pip install ultralytics装完后用两条命令自检依赖和GPU状态python -c import ultralytics; print(ultralytics.__version__) python -c import torch; print(torch.cuda.is_available())逻辑说明第一条确认ultralytics装上了第二条确认PyTorch能不能拿到CUDA。对于机械零件这种小数据集CPU也能跑训练但一个epoch要等十几分钟有NVIDIA显卡会顺滑很多。参数说明如果你的显卡是RTX 3060或更高型号但torch.cuda.is_available()返回False八成是当初装的PyTorch是CPU版本或者CUDA版本对不上。去PyTorch官网按你机器的CUDA版本重装最省心别在旧环境上死磕。3.2 训练前的数据校验三个隐藏问题一次查完训练报错有八成发生在数据读取阶段集中在图片标注对不上、坐标越界、类别ID超范围这三类。我每次拿到新数据集都会先跑一段校验脚本import os from xml.dom import minidom img_dir JPEGImages ann_dir Annotations label_dir labels missing_label [] out_of_range [] for img_name in os.listdir(img_dir): stem os.path.splitext(img_name)[0] xml_path os.path.join(ann_dir, stem .xml) txt_path os.path.join(label_dir, stem .txt) if not os.path.exists(xml_path) or not os.path.exists(txt_path): missing_label.append(img_name) continue doc minidom.parse(xml_path) w int(doc.getElementsByTagName(width)[0].firstChild.data) h int(doc.getElementsByTagName(height)[0].firstChild.data) for obj in doc.getElementsByTagName(object): xmin int(obj.getElementsByTagName(xmin)[0].firstChild.data) ymin int(obj.getElementsByTagName(ymin)[0].firstChild.data) xmax int(obj.getElementsByTagName(xmax)[0].firstChild.data) ymax int(obj.getElementsByTagName(ymax)[0].firstChild.data) if xmin 0 or ymin 0 or xmax w or ymax h: out_of_range.append((img_name, xmin, ymin, xmax, ymax)) print(f缺失标注: {len(missing_label)}) print(f越界框: {len(out_of_range)}) for item in out_of_range[:5]: print(item)逻辑说明脚本遍历JPEGImages里的每张图检查同名XML和TXT是否都存在避免训练时某些图片被静默跳过然后解析XML里的width/height和每个bndbox把坐标超过图片边界的框全部挑出来。越界框一旦转YOLO格式会变成负数或大于1的值ultralytics读取时必报错提前查掉能省一次训练翻车。参数说明missing_label不为0时要区分是标注缺失还是背景图。机械零件数据集里通常没有背景图如果出现大量缺失多半是解压不完整或路径写错不是正常情况。3.3 写data.yaml并启动训练参数怎么调才稳数据校验通过后准备data.yaml并启动训练path: /your/absolute/path/common_mechanical_parts train: ImageSets/Main/train.txt val: ImageSets/Main/val.txt names: 0: bolt 1: nut 2: washer 3: bearing 4: gear训练命令yolo train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ workers4 \ device0 \ projectmech_parts_exp \ namerun1逐项说明modelyolov8s.pt是用s版本预训练权重起跑机械零件和COCO自然图像差距不小但预训练权重仍能加速收敛这就是迁移学习的收益imgsz640是默认输入分辨率机械零件如果偏小、占画面比例低后面可改成960代价是显存和推理变慢batch16在8GB显存下比较稳显存只有6GB就改成8epochs100对5900张的小数据集足够通常60轮后就到平台期patience20可以让mAP连续20轮不涨时自动停下。参数推荐值使用场景modelyolov8s.pt8G显存起步的默认选择modelyolov8m.pt精度优先显存12G以上imgsz640常规检测训练推理速度均衡imgsz960-1280小零件占画面比例低时提升召回batch8/16/32按显存调整8G用16稳妥epochs100小数据集通用值可配patience早停3.4 训练输出里最先要看的三张图训练结束后ultralytics会在实验目录下生成results.png、confusion_matrix.png和val_batch0_pred.jpg。results.png里包含box_loss、cls_loss、dfl_loss、precision、recall、mAP50、mAP50-95七条曲线。注意一个容易误判的点loss还在往下降不代表模型一定在变好要同时看验证集mAP。如果loss降但mAP不动要么是过拟合开始要么是训练集验证集分布不一致。机械零件类别之间视觉差异小特别是不同规格的螺栓和螺母容易混淆mAP50做到0.85以上就已经是能用的基线了。第一次训练不追求最高分重点是把pipeline跑通确认除了数据问题之外没有别的坑。4. 机械零件检测的5个典型坑从标注错位到小目标漏检4.1 坑1VOC和YOLO路径双写训练时所有标注读不到现象训练一开始dataloader正常loss一直为0进度条不动。 原因data.yaml里train指向ImageSets/Main/train.txt但ultralytics拿到图片名清单后默认在相同父目录下找标签文件如果该清单里只有图片名而没有相对路径标签目录又用的是labels路径对不上就全部读不到。 解决要么在data.yaml里显式配置labels_dir参数要么改用labels目录结构。我一般会直接改成ultralytics推荐的目录组织方式把images和labels按train/val分开放data.yaml里train和val指到image子目录名标签自动从同级labels子目录读取省心。4.2 坑2类别ID和类名映射顺序对不上现象训练能跑但预测时washer被标成bearing错得很有规律。 原因txt里的类别ID是数字如果转换脚本里class_map定义和训练用的data.yaml里names顺序不一致就会出现静默错乱。由于模型是在拟合“固定的错误映射”loss照样收敛肉眼很难察觉。 解决抽一张图对比txt的第一个数字和XML里object的name然后全量扫描。更彻底的做法是把XML里的name当作唯一标准先定义好class_map再统一生成txt和data.yaml保证两个文件引用同一份映射。4.3 坑3同一零件身份的图片同时出现在训练集和验证集现象mAP50跑到0.95一到现场准确率掉到0.6。 原因机械零件数据常见的是把零件放在工作台上多角度拍摄同一零件的连续帧几乎一模一样。如果随机划分train/val等于把同义副本塞进了两边验证时模型是在“背答案”不是泛化。 解决按零件个体ID划分数据同一个零件只能出现在训练集或验证集中。拿到这套zip时先检查train.txt和val.txt有没有文件名前缀相同或像素高度相似的图片。自己补数据时一定按对象分桶再做分割。4.4 坑4小零件在640输入下漏检现象大头螺栓能检到小号垫圈时有时无。 原因原始图片如果是1920×1080一枚垫圈可能只占30×30像素缩放到640后只剩10×10像素左右特征几乎被压缩没了。 解决首选把imgsz调到960或1280代价是训练速度和显存。更贴近产线实际的方案是裁patch训练把原图按区域切成小块让垫圈在patch里占比变大。这个方法对机械零件尤其有效因为产线相机拍到的本来就是近景小目标。4.5 坑5loss降到0.01漏检反而增多现象train loss一直降但val集的漏检数量往上走。 原因loss只代表训练集拟合程度不代表泛化能力。机械零件数据集类别不算多但背景、光照、摆放角度变化明显模型很容易在后期记住训练集细节进入过拟合。 解决用patience参数早停直接取best.pt而不是last.pt。同时可以开一点数据增强hsv_h0.015、degrees5、flipud0.5让模型对光照和角度变化更鲁棒。注意别把增强开太大金属零件反光严重时过强的hsv扰动反而会让模型学不到真实颜色特征。5. 别急着上产线先用三招验证模型再决定怎么迁移5.1 验证三件套混淆矩阵、置信度阈值、可视化抽检训练完先打开confusion_matrix.png看哪两个类互相混淆。机械零件里washer和bearing都是圆形是误检重灾区。这时调模型结构不如先调置信度阈值把conf_thres从0.25提到0.35到0.45误检会少一大截代价是召回略降。然后用val_batch0_pred.jpg和val_batch0_labels.jpg对比找出误检最集中的样本看是标注本身有误还是确实难以分辨。阈值调节有点玄学但我是先取0.35试一轮验证集再往上下各调0.05取mAP和F1的折中值。5.2 迁移到自有零件换最后一层类别重训50轮如果最终要检测的是自家产线的特殊零件拿这份预训练权重做迁移是最省时间的路。改data.yaml的names为你自己的零件清单然后用同样的命令把epochs降到30到50。底层特征提取已经学会了金属边缘、纹理和反光等通用表征新任务只需要学“你的零件长什么样”。我上次做轴承内圈检测时用这种方式只标了200张图就拿到0.82的mAP50比从零训练省了整整两天。导出部署的话常见做法是转成onnxyolo export modelbest.pt formatonnx imgsz640 dynamicTrueTensorRT是对性能要求高时的后续优化先用这份数据集把基准mAP打出来再说。我的经验是数据集格式越乱后续填坑时间越多早点校验、早点可视化、早点让数据与配置文件完全对齐训练本身反而比想象中顺。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进