
简介这是一份面向机械零件识别与目标检测任务的螺丝螺母检测数据集包含423张已标注的真实场景图片适合训练螺丝、螺母等小目标识别模型可用于工业质检、自动化分拣等项目的算法验证与落地实践。压缩包内共428个文件其中jpg图片423张、txt标注文件3个、Python数据增强脚本1个及label_list类别清单1份整体体积82.69MB目录结构便于直接接入常见检测框架使用。标注文件已按目标检测格式整理读者可快速划分训练集与验证集附带的增强脚本支持旋转、缩放、亮度调节等操作可批量扩充样本以提升模型泛化能力对入门或进阶目标检测开发均具有实用价值。该资源目前已有606人学习下载适合需要真实工业场景数据开展模型训练与效果调试的开发者参考使用。1. 螺丝螺母检测数据集423张先别纠结张数看标注和增强脚本能不能直接复用在质检场景里机械零件识别检测数据集-螺丝螺母目标检测数据集423张带标注文件(附数据增强脚本程序).zip这类压缩包听起来不多但真正值不值得留不看423这个数字而是看三件事标注文件规不规整、图像有没有覆盖真实工况、数据增强脚本能不能直接跑起来。目标检测项目最怕的不是数据少而是标注格式混乱、坐标错位、类别定义随意最后一大半时间耗在清洗数据上。这篇按我处理同类数据集的习惯来写从解压目录讲到训练参数再讲透增强脚本的边界和典型坑让这423张料真正变成能出结果的模型。适合做毕设选题、小批量产线视觉预研或者刚入手目标检测想找一份短平快练手数据的人。2. 拆解数据集的zip包目录结构、标注格式与目标尺寸预检拿到这类zip我一般不会双击解压就开训练。先做三件事看目录组织、确认标注格式、抽查图像分布。这三件事加起来只要十分钟却决定了后面是直接进训练还是先修数据。对这一类机械零件识别检测数据集常见的打包结构大概是images目录放原图labels或Annotations目录放标注顶层附一个classes.txt和一个数据增强脚本。下面这张表是典型的文件清单。顶层目录/文件常见内容说明images/423张jpg或png原图文件名最好和标注文件同名labels/每张图对应一个txtYOLO格式标签一行一个目标Annotations/每张图对应一个xmlVOC格式和labels目录二选一classes.txt每行一个类别名决定模型输出多少个类别data_augment.py数据增强脚本注意脚本依赖的库版本README.txt数据集说明与版权信息有它时先读它能省很多事如果你解压后发现目录不是这个结构别慌大部分是作者按自己的习惯组织。先找classes.txt它写着类别名再找后缀是xml、json或txt的标注目录两者都找到了这包就能用。要特别注意图片和标注是否同名这一步决定转换脚本要不要额外做映射。zip在Windows下解压遇到中文目录名乱码的问题很常见我习惯用Python的zipfile模块解压编码按zip内元数据走具体写法放在第5章避坑部分。2.1 从标注文件后缀判断能不能直接训练VOC、COCO、YOLO三选一标注文件决定你能不能把图直接扔进训练。常见有三种格式VOC的xml里bndbox节点存的是绝对像素坐标左上有xmin、ymin右下有xmax、ymaxCOCO的json里annotations数组每个元素带bbox格式是[x, y, width, height]YOLO的txt里每行是类别id和归一化中心坐标、归一化宽高。螺丝螺母这种小物体YOLO原生txt最省事VOC和COCO都要转一步。我的习惯是先写个扫描脚本只解析前几个文件判断包的真实格式。# 检查数据集格式扫描标注目录并解析前3个样本 from pathlib import Path import xml.etree.ElementTree as ET import json root Path(./机械零件_dataset) labels root / labels if (root / labels).exists() else root / Annotations for f in sorted(labels.glob(*))[:3]: if f.suffix .xml: tree ET.parse(f) obj tree.find(object) name obj.find(name).text 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) print(fVOC格式: {f.name} class{name} box({xmin:.0f},{ymin:.0f},{xmax:.0f},{ymax:.0f})) elif f.suffix .json: data json.loads(f.read_text(encodingutf-8)) ann data[annotations][0] print(fCOCO格式: {f.name} category_id{ann[category_id]} bbox{ann[bbox]}) elif f.suffix .txt: lines f.read_text(encodingutf-8).strip().splitlines() cid, cx, cy, w, h map(float, lines[0].split()) print(fYOLO格式: {f.name} class_id{int(cid)} cx{cx:.4f} cy{cy:.4f} w{w:.4f} h{h:.4f})这个脚本只取前3个文件是为了避免你在超大目录上浪费时间。看到输出后你立刻能判断如果是VOC或COCO下一步就要做格式转换如果是YOLO txt还要看坐标是否在0到1之间cx和cy不在这个范围就说明标签已经损坏。参数说明labels路径要根据实际解压目录调整如果压缩包里同时存在labels和Annotations两个目录先看哪个目录的文件数量和images一致以那个为准。2.2 423张图的视觉预检分辨率、目标尺寸与分布盲区数据下载下来数量从来不是第一问题分布才是。CCPD、HRSC2016这类公开数据集都是很好的例子数据量大但场景单一换到真实环境照样要重新采样。螺丝螺母是结构稳定的工业零件423张可能大量集中在同一光照、同一角度甚至同一个料盘背景。我的做法是跑一个小脚本统计图像分辨率和每张图的目标数量确认目标在图中占多大面积。# 统计图像分辨率与单张目标数分布判断小目标比例 from pathlib import Path from PIL import Image import numpy as np img_dir Path(./机械零件_dataset/images) obj_counts [] for img_path in sorted(img_dir.glob(*.jpg)): w, h Image.open(img_path).size # 只读头信息不加载整图 label_path None for cand in [labels, Annotations]: p img_path.parent.parent / cand / (img_path.stem .txt) if p.exists(): label_path p break if label_path is not None: n sum(1 for _ in open(label_path)) obj_counts.append(n) else: obj_counts.append(0) arr np.array(obj_counts) print(f图像数量: {len(arr)}) print(f单张目标数: min{arr.min()}, 中位数{np.median(arr):.0f}, max{arr.max()})这段脚本的信息量比想象中大。如果单张目标数的中位数只有1说明绝大多数图里只有一个零件训练出来的模型很难处理密集摆放场景如果max是30以上又说明存在高度拥挤的极端样本。再看图像分辨率如果原图都是640x640而螺丝只有20x20那它就是典型小目标后面训练imgsz不能开小。参数说明图片后缀要按实际改成png或bmplabels和Annotations的路径判断逻辑假设它们和images平级如果嵌套更深先把目录结构打平再跑脚本。3. 用ultralytics练螺丝螺母环境配置、格式转换与第一个可跑的模型第2章确认完数据可用接下来就是把它跑成模型。螺丝螺母这类小目标检测我几乎固定用YOLO系不是因为它最先进而是因为它最适合这个数据量级和部署场景。你不需要在YOLOv8、YOLOv11之间纠结ultralytics仓库的接口基本一致下面的流程两个版本都吃。3.1 为什么螺丝螺母这个小目标场景首选YOLO系而不是Transformer423张原图加增强后大约几千张这个规模对检测Transformer来说偏小。YOLO是anchor-based单阶段模型先验框对螺丝螺母这种固定长宽比的目标很友好训练收敛快显存占用低导出ONNX后随便一个工业相机盒都能跑。检测Transformer当然可以做但同样的样本量下很难超过同参数的YOLO推理速度还要再打折。如果是带旋转角度的零件装配场景你已经能看到像MMRotate训练DOTA数据集那种旋转框方案但那属于进阶方向后面第6章再展开。现阶段先把水平框做到位。3.2 零基础配置ultralyticsconda建环境验证CUDA可用目标检测初学者最大的障碍往往不是模型而是环境装完发现torch版本和CUDA对不上。我一般这样装# 用conda创建独立环境避免把系统Python搞乱 conda create -n bolt_yolo python3.10 -y conda activate bolt_yolo pip install ultralytics # 验证CUDA是否可用这一步决定训练是分钟级还是小时级 python -c import torch; print(CUDA available:, torch.cuda.is_available())这个流程的核心是让torch拿到GPU。如果最后一行打印的是CUDA available: False不要怀疑模型代码先查显卡驱动和CUDA版本驱动版本用nvidia-smi看torch对应的CUDA版本由pip安装时自动匹配。电脑没独显也能跑训练速度会慢很多建议先把epochs调小流程跑通再上完整训练。参数说明python3.10是当前最稳的组合3.12有时会遇到onnx相关的依赖坑如果你机器上已经有torch的CUDA环境可以跳过pip install ultralytics里的torch依赖先装已有torch再装ultralytics。3.3 把VOC格式转成YOLO格式转换脚本里的四个关键参数如果压缩包里的标注是VOC或COCO直接拿去做YOLO训练会报格式错或者不报错但模型乱学。我在第2章已经确认过格式这一步就写转换脚本。核心不是代码本身而是四个参数图片真实宽高、类别映射表、归一化边界检查、输出目录。下面脚本按这几个点写# VOC转YOLO核心是从同名图片读取宽高并按真实尺寸归一化 import xml.etree.ElementTree as ET from pathlib import Path voc_dir Path(./机械零件_dataset/Annotations) img_dir Path(./机械零件_dataset/images) out_dir Path(./机械零件_dataset/labels_yolo) out_dir.mkdir(exist_okTrue) # 注意这个映射必须和classes.txt逐行对应 class_map {screw: 0, nut: 1} def convert_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() # 从同名图片读宽高绝不自己猜 img_path img_dir / (xml_path.stem .jpg) if not img_path.exists(): print(f找不到图片: {img_path}) return w_img, h_img 0, 0 # 这里用PIL读尺寸避免cv2在灰度图上读错通道 from PIL import Image w_img, h_img Image.open(img_path).size lines [] for obj in root.iter(object): name obj.find(name).text.strip() cls_id class_map.get(name, 0) 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) # 严格按YOLO归一化公式计算中心坐标和宽高 x_center (xmin xmax) / 2 / w_img y_center (ymin ymax) / 2 / h_img bw (xmax - xmin) / w_img bh (ymax - ymin) / h_img # 越界框直接报警不要带病训练 if not (0 x_center 1 and 0 y_center 1 and bw 1 and bh 1): print(f警告: 越界框 {xml_path.name}: cx{x_center:.4f}, cy{y_center:.4f}, w{bw:.4f}, h{bh:.4f}) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines) \n, encodingutf-8) for xml_file in voc_dir.glob(*.xml): convert_xml(xml_file) print(转换完成请检查labels_yolo目录)这段脚本里最容易出问题的两行是w_img和h_img的来源。VOC的xml不存图片尺寸必须从同名图片读如果图片实际是1920x1080你从某个yaml里猜一个640所有框都会偏移。class_map要和前面classes.txt逐行对一遍名字对不上类别id就会错位模型会把螺丝当螺母学。print里的越界警告不是摆设出现一条就要找原因不然训练时模型会学到错误坐标。参数说明图片后缀按实际改成png如果图片和标注不同名先在第2章脚本里把对应关系打出来建立映射再做批量转换。3.4 第一次训练的启动命令epochs、imgsz、batch、patience怎么定数据准备好后第一次训练我建议用yolov8s.pt作为预训练权重而不是从零开始。命令如下yolo detect train \ databolt.yaml \ modelyolov8s.pt \ epochs200 \ imgsz640 \ batch16 \ patience30 \ project./runs_bolt \ nameexp1这组参数是我处理小样本数据集的默认值。其中bolt.yaml是数据集描述文件内容至少包含train路径、val路径和nc、names两个字段如果你把423张按8:2拆成训练和验证train指向训练集图片目录val指向验证集图片目录就行。imgsz要结合第2章的统计结果来定如果螺丝在图中最大不到40x40建议直接把imgsz加到960甚至1280代价是显存占用上升。batch16在8GB显存下是安全线如果显存只有6GB先降成8。patience30表示30个epoch验证指标不涨就自动停小样本训练最怕死等300个epoch早停能省很多时间。4. 增强脚本的参数边界423张翻到4000张也要翻对方向这个压缩包的价值点之一就是带数据增强脚本。增强本身是把423张底图扩充成几千张但对螺丝螺母这类金属小零件不是所有增强都安全。高强度的仿射变换会让长螺丝出现离谱透视颜色增强太狠会让不锈钢螺母亮成白色。增强不只是调参它是在给模型编造训练分布编得偏离真实工况模型就学歪。4.1 螺丝螺母适用的增强组合与参数建议下面这张表是我处理同类零件时的参数参考可以直接抄进脚本里。增强操作建议参数为什么这样设水平翻转p0.5螺丝左右对称翻转不改变语义亮度扰动brightness0.7~1.3模拟车间不同角度的光照波动小角度旋转degrees±10模拟零件摆放倾斜超过30度会产生无意义姿态高斯噪声sigma5~15增加传感器噪声鲁棒性随机裁剪裁剪后resize回原尺寸只裁中间区域避免切掉零件主体色彩抖动hsv_v0.2~0.4只动亮度别动色相金属零件颜色信息很敏感表格里最容易被忽略的是旋转角度的上限。装配线上的螺丝在成像里最多倾斜十几度你如果给它做90度旋转等于告诉模型倒着放的螺丝也是正样本实际产线根本不会出现这个姿态模型反而会把正常姿态的特征学偏。亮度扰动也要克制螺丝在暗场和高光下的纹理差异很大调太强会让模型去认背景反光而不是认零件轮廓。4.2 增强脚本的正确写法图像和标注框要做同一套变换离线增强脚本最隐蔽的坑是只处理了图片、没有同步处理标注。图转了30度标注框还停在原来的位置训练时模型看到目标位置和标签对不上loss降不下来不说还可能直接学出一个偏移修正网络。正确写法是让图像和标注框共用同一个变换矩阵下面这段是旋转增强的示例# 旋转增强图像旋转theta度标注框由同一矩阵同步变换 import cv2 import numpy as np def rotate_image_and_boxes(img, boxes, angle_deg): h, w img.shape[:2] center (w / 2, h / 2) rot cv2.getRotationMatrix2D(center, angle_deg, 1.0) cos_a, sin_a abs(rot[0, 0]), abs(rot[0, 1]) # 计算旋转后画布的尺寸避免边缘被裁剪 new_w int(h * sin_a w * cos_a) new_h int(h * cos_a w * sin_a) rot[0, 2] new_w / 2 - center[0] rot[1, 2] new_h / 2 - center[1] img2 cv2.warpAffine(img, rot, (new_w, new_h)) new_boxes [] for box in boxes: # box格式: [xmin, ymin, xmax, ymax] 像素坐标 pts np.array([[box[0], box[1]], [box[2], box[1]], [box[0], box[3]], [box[2], box[3]]], dtypenp.float32) pts2 cv2.transform(pts.reshape(-1, 1, 2), rot).reshape(-1, 2) nx_min, ny_min pts2.min(axis0) nx_max, ny_max pts2.max(axis0) # 旋转后框会外扩裁剪回画布边界内 nx_min max(0, nx_min); ny_min max(0, ny_min) nx_max min(new_w, nx_max); ny_max min(new_h, ny_max) new_boxes.append([nx_min, ny_min, nx_max, ny_max]) return img2, new_boxes这段代码的关键在于图像的旋转矩阵和标注框角点的变换用的是同一个rot矩阵而不是对图像转一次、对坐标再算一次。旋转后取四个角点的外接矩形是为了在保持标注语义的前提下不引入多余的空背景。参数说明angle_deg建议按4.1表格控制在±10度如果你的增强脚本还要做缩放和裁剪记住一个原则——所有几何变换先拼成一个矩阵再一次性作用于图和框拆分操作越多越容易错位。4.3 离线增强和在线增强怎么配合mosaic与hsv参数离线增强脚本负责把数据集做大ultralytics训练时的在线增强负责在训练过程中继续加扰动两者互补你不需要二选一。在线增强不需要改数据在训练命令里追加参数即可yolo detect train \ databolt.yaml modelyolov8s.pt \ epochs200 imgsz640 batch16 \ hsv_v0.4 hsv_s0.7 \ degrees10 fliplr0.5 mosaic0.5hsv_v是亮度扰动幅度0.4配合离线脚本的亮度增强会让样本变化更丰富degrees10和离线旋转保持一致避免两个阶段的分布打架mosaic0.5表示一半的batch用马赛克拼接这会显著增加小目标的数量。 提示当数据集只有423张时mosaic不要开到1.0拼接图太抽象容易让模型学到拼接缝开0.5是安全值。在线增强的参数在ultralytics不同小版本里名称基本一致如果你的版本报参数不识别先yolo detect train --help看当前版本支持的参数名。5. 螺丝螺母目标检测避坑记录5个把训练搞翻车的高频问题数据量小意味着容错率低一个坑就能让整个训练白跑。下面这5条是我在这些年处理零件检测数据集时踩过的真实问题按现象、原因、解决的顺序写希望你不用重走这些弯路。5.1 解压后文件名乱码图片和标注对不上现象Windows下解压zip后目录名变成一堆乱码图片能打开但labels目录里的文件名和图片对不上构建数据集时报找不到标注。原因压缩包在非Windows环境打包中文文件名按UTF-8存储Windows自带解压用本地代码页GBK去解释名字就坏了。解决用Python的zipfile模块解压Python按zip的flag位正确读取UTF-8元数据文件名不会再乱。参考写法# 用Python解压zip按包内UTF-8元数据还原文件名 import zipfile from pathlib import Path zip_path 机械零件识别检测数据集-螺丝螺母目标检测数据集423张带标注文件(附数据增强脚本程序).zip out_dir Path(./dataset) out_dir.mkdir(exist_okTrue) with zipfile.ZipFile(zip_path) as zf: for name in zf.namelist(): # 统一路径分隔符避免Windows下混用/和\ target out_dir / name.replace(\\, /) if name.endswith(/): continue zf.extract(name, out_dir) print(解压完成请检查dataset目录下的文件名是否正常)逻辑说明zipfile在extract时按zip条目记录的编码处理文件名比Windows资源管理器直接调系统API更可靠。参数说明out_dir改成你自己的路径如果解压后还是乱码用zf.namelist()把所有条目先打印出来人工核对后再全局解压。5.2 转换后坐标全是0训练loss纹丝不动现象VOC转YOLO跑完labels目录里出现cx0.000000、w0.000000训练时loss不降验证集mAP一直是0。原因多数是xml的bndbox解析成了空字符串或带单位字符串float()转换失败被异常吞掉少数情况是归一化时把图片宽高写反了用宽除以高。解决先写个小脚本统计非法标注逐行打印前10个坏样本# 快速排查统计YOLO标注文件里的非法坐标 from pathlib import Path bad [] for f in Path(./机械零件_dataset/labels_yolo).glob(*.txt): for line in f.read_text(encodingutf-8).strip().splitlines(): v list(map(float, line.split())) if len(v) ! 5 or v[2] 0 or v[3] 0 or v[2] 1 or v[3] 1: bad.append((f.name, line)) print(f非法标注样本数量: {len(bad)}) for name, line in bad[:10]: print(name, line)这段代码把长度不为5、宽高为负、归一化后大于1的行都挑出来。你看到具体行之后回到对应xml检查bndbox节点是不是有小数点以外的字符。参数说明判定条件是v[2]和v[3]即归一化宽高如果打印出来全是0大概率是xml路径没对上解析了空文件。5.3 增强脚本跑完标注框和目标错位现象增强后拿标注可视化工具打开新图发现框还在原位置但目标已经被旋到了另一个角训练时模型学不到正确位置。原因脚本只调用了仿射变换处理图像没有把标注框做变换或者用了先裁剪后旋转的拆分步骤框的原始坐标已经失效。解决按第4.2节的方式把图像和框交给同一个变换矩阵。增强完之后别直接训练先抽10张图做可视化拼图检查# 增强后抽查把预测框画回图上肉眼确认对位 import cv2 from pathlib import Path for img_path in list(Path(augmented_images).glob(*.jpg))[:10]: img cv2.imread(str(img_path)) label_path Path(augmented_labels) / (img_path.stem .txt) if label_path.exists(): for line in label_path.read_text().strip().splitlines(): cid, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw/2) * img.shape[1]) y1 int((yc - bh/2) * img.shape[0]) x2 int((xc bw/2) * img.shape[1]) y2 int((yc bh/2) * img.shape[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite(fcheck_{img_path.stem}.jpg, img)这段脚本把归一化坐标换算回像素坐标并画框。参数说明如果框整体偏移但大小不变是平移没同步如果框大小和形状不符是缩放或旋转矩阵没有作用到框上。可视化是增强脚本的后悔药千万别省。5.4 验证集mAP很高换相机拍的照片漏检一半现象验证集mAP到了0.9看着很漂亮拿到产线用工业相机或手机新拍的照片漏检率超过一半。原因数据集本身场景单一增强脚本又只做了画风扰动模型学到的是训练集里特定背景和光照分布不是螺丝本身的几何特征。更隐蔽的是验证集和训练集来自同一组图的简单拆分模型过拟合了训练集验证集也判断不出来。解决训练集和验证集来自不同拍摄批次至少也要按不同料盘分。增强强度按真实工况校准把产线照片的亮度直方图跑出来和训练集的亮度分布对比差太多就降低hsv_v。看指标不要只看mAP50打开PR曲线看confidence在0.25以下时recall的下滑速度下滑越快说明模型越虚。5.5 螺丝目标太小imgsz640训练完根本看不见现象训练时loss正常测试时大螺母能找回小螺丝全漏mAP50-95比mAP50低一大截。原因螺丝在原始图里只有20x20像素以imgsz640、stride32计算下采样后的特征图上只剩不到一个像素特征全被卷积吃掉了。解决imgsz加到960甚至1280让小目标在特征图上多占几个点或者把原始大图裁成几个不重叠的patch再检测相当于把小目标放大检测完再把框映射回原图坐标。如果装配角度是个主要干扰源再考虑上旋转目标检测框架比如MMRotate那套DOTA训练管线但先确认是尺寸问题还是角度问题别一上来就换框架。6. 最后一步用没进过训练集的实拍图验收模型效果训练结束不代表能上线模型是自己人但实拍图是裁判。每次拿到这类数据集的模型我先不看测试集指标而是找一批没参与过训练、甚至没参与过验证的实拍照片来验收。6.1 用一批没进过训练的照片做批量推理验收把实拍图放进一个干净目录然后跑推理yolo detect predict \ modelruns_bolt/exp1/weights/best.pt \ source./real_capture \ imgsz960 conf0.25 iou0.5output目录里会生成带框的标注图打开看三件事漏检的目标有多少、框贴得紧不紧、有没有把背景零件误报成螺丝。如果漏检集中在某个角度回到增强脚本补那个角度的离线增强如果框普遍偏大或偏小检查标注转换时是否读错了图片尺寸。这个环节比mAP数字可靠得多因为测试集和训练集同源时mAP天然虚高。6.2 效果不够时的两个进阶方向如果水平框已经做到极限再往上走有两个方向。一是从目标检测切到旋转目标检测像MMRotate训练DOTA数据集那样给螺丝加旋转角标签适用于装配线上零件朝向混乱的场景二是从固定类别走向开放词汇目标检测让模型能处理没见过的零件但423张这个体量支撑不起大规模开放词汇训练多半还要借力预训练模型。如果目标本身是设备健康管理而不是视觉定位那该去研究的是PHM2012这类带退化标签的数据集而不是检测包。我现在拿到任何一种zip数据集第一件事永远是打开看几十张原图和标注框第二件事才是跑训练。这个习惯让我少翻了很多次车模型是个黑匣子但数据不是。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。