简介面向机械零件识别与质检场景的螺丝螺母目标检测数据集包含423张真实拍摄的JPG图片及对应标注文件覆盖不同角度、光照与摆放姿态下的螺丝与螺母实例适合作为YOLO、SSD、Faster R-CNN等主流目标检测模型的训练与验证数据。压缩包内共428个文件包括423张jpg图像、3个txt标注文件、1个label_list类别清单以及1个Python数据增强脚本整体大小约82.69MB结构清晰便于直接导入项目使用。附带的数据增强脚本支持对原始图像进行旋转、翻转、亮度调节等操作可有效扩充样本规模提升模型在小样本条件下的泛化能力。适用于计算机视觉初学者、算法工程师及自动化质检项目开发者已有605人学习下载可直接应用于毕业设计、竞赛实验或工业检测原型验证。1. 机械零件识别检测数据集423张标注图凭什么能撑起一个目标检测项目机械零件识别检测数据集说白了就是一套螺丝、螺母的目标检测训练素材423张零件实拍图每张图都带标注文件还附带一份数据增强脚本程序。这套东西解决的痛点很具体——搞工业视觉、自动化分拣的工程师想用目标检测模型识别小零件第一关不是模型选型而是没有数据。公开的人脸、车辆数据集多得是螺丝螺母这种细碎零件却少有人整理。它适合两类人一是想快速跑通“目标检测模型训练自己数据集”完整流程的新手二是在做零件分拣方案、需要基准数据做算法预研的工程师。423张图规模不大但配合增强脚本足够把一个小型检测模型从零训到能用的状态。别一听四百来张图就觉得寒碜工业零件场景里类别固定、背景单一小数据集的利用率远比你想象的高。2. 数据集结构拆解423张图与标注文件的边界在哪2.1 先分清两类标注格式能直接喂YOLO的和要转换的拿到任何带标注的目标检测数据集第一件事不是解压就训练而是先确认标注格式。常见做法是两种一种是Pascal VOC风格的XML文件一个标注对象一个节点存的是绝对坐标另一种是YOLO风格的TXT文件一行一个目标存的是归一化后的类别、中心点、宽高。现在发行版里大量数据集直接用YOLO格式因为省去转换这一步。你手里这份如果标注文件是TXT每行五个数字那就属于这一类。YOLO格式每行的含义是第1个数类别编号0代表螺丝1代表螺母取决于数据集类别定义第2、3个数目标框中心点的x、y除以图片宽高后的归一化值范围0~1第4、5个数目标框的宽、高同样归一化如果你的标注文件是XML训练前要先转成YOLO格式因为YOLO系模型默认读TXT。转换脚本不难但有个坑XML里坐标是左上角和右下角xmin, ymin, xmax, ymax转归一化时要先换算成中心点再除宽高这一步很多人直接拿左上角坐标去归一化结果框全偏到右上角去了。import xml.etree.ElementTree as ET import os # VOC XML - YOLO TXT def convert_xml_to_yolo(xml_path, out_path, class_list): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_list: continue cls_id class_list.index(cls_name) 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) # 关键先算中心点再归一化 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) class_list [screw, nut] # 按实际类别定义调整 convert_xml_to_yolo(annotation.xml, annotation.txt, class_list)这段脚本的核心逻辑就是尺寸换算。XML坐标系的单位是像素YOLO要求的是比例所以一切除以图片宽高。特别注意cx和cy的公式是(min max) / 2不要写成(max - min) / 2那算出来是框宽高的一半不是中心点。文本落地后可以直接复制改一下class_list和路径就能跑。2.2 423张图为什么够用小数据集的训练逻辑很多人一看到423张图第一反应是“这能训出个啥”。但工业零件检测和通用目标检测有个本质区别类别少、场景单一、光照可控。螺丝就是螺丝螺母就是螺母形态变化有限不像COCO数据集里猫有几百个品种。这里423张图的价值在于“精”而不在于“多”每张图上目标数量可能不止一个实际标注框数可能上千这是训练有效样本的真正单位。我一般会这么做先数一下标注框总数如果单类别标注框超过800个训练一个小模型完全够用。框数比图数更能反映数据集的潜力。另一个原因是这份数据集带了增强脚本意味着你可以自己做数据闭环。423张原始图翻个倍、旋转一下、调调亮度很容易到2000张以上。目标检测的数据增强不是简单复制图片而是模拟现实中可能出现的视角变化这在工厂流水线上特别实用——零件在传送带上转个角度形态就变了。2.3 用最短路径验证标注质量画框脚本拿到数据集后如果不做质量检查直接训练很容易被标注错误带偏——框没对准、类别标反、坐标越界都是常见问题。训练前先写个脚本把标注框画回图片上肉眼扫一遍这是血泪教训换来的习惯。验证脚本思路很简单读图片、读TXT、用OpenCV画矩形。import cv2 import os def draw_yolo_boxes(img_dir, label_dir, class_names, save_dir): os.makedirs(save_dir, exist_okTrue) for img_name in os.listdir(img_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue img_path os.path.join(img_dir, img_name) label_path os.path.join(label_dir, img_name.rsplit(., 1)[0] .txt) img cv2.imread(img_path) h, w img.shape[:2] if not os.path.exists(label_path): print(f警告: {img_name} 缺少标注文件) continue with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) # 反归一化还原成像素坐标 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 0, 255) if cls_id 0 else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imwrite(os.path.join(save_dir, img_name), img) print(画框完成输出目录:, save_dir) class_names [screw, nut] draw_yolo_boxes(images, labels, class_names, verify_output)这段代码的关键在反归一化过程归一化坐标乘以宽高还原成像素值画框用的是左上角和右下角所以从中心点往两边扩展。跑完后按图片顺序翻一遍重点看两类问题一是框是否紧贴目标边缘二是小目标是否被遗漏。如果发现某个类别大量漏标训练出来的模型对那个类别会直接瞎掉。3. 让423张图“增值”的数据增强脚本坐标随图变是硬道理3.1 数据增强为什么是刚需小样本、小目标、类不均衡螺丝螺母检测有个天然难题目标小。一张1920x1080的图上一个M8螺母可能只占30x30像素这种小目标对模型的特征提取能力要求很高数据不够就更容易翻车。数据增强在这里起的作用不是“凑数”而是让模型在不同尺度、不同光照、不同角度下看到同一个物体逼它学出更本质的特征。常见的数据增强脚本程序一般至少包含三类操作几何变换、色彩扰动、混合增强。几何变换解决视角问题色彩扰动解决光照问题混合增强解决遮挡问题。对螺丝螺母这种金属件最有效的是几何变换因为金属反光已经够让模型头疼了光照扰动容易把视觉特征搞乱要小心控制强度。3.2 标注入门的增强操作翻转、旋转、亮度噪声翻转是最安全的增强方式之一水平翻转变换坐标时公式极其简单cx_new 1 - cx。垂直翻转变为cy_new 1 - cy。旋转稍微复杂90度、180度、270度这种直角旋转可以精确算坐标任意角度旋转会引入黑边和坐标插值误差反而干扰训练。亮度、对比度、噪声扰动用OpenCV就能做不需要给标注文件动刀。这里有个原则几何变换必须同步改坐标色彩扰动不需要动标注。写脚本时把两类操作分开处理逻辑清晰不容易出错。3.3 增强脚本核心坐标随图变别把框带偏如果把代码分成“生成脚本”和“应用脚本”核心就是后者。对每张图执行一次增强完成一次训练样本的扩展。下面这套是我常用的组合水平翻转 90度旋转 亮度变化增强倍数是3正好应对小目标检测数据不足的问题。import cv2 import numpy as np import os import random def flip_label_horizontal(cx, cy, w, h): return 1.0 - cx, cy, w, h def rotate90_label(cx, cy, w, h): # 顺时针旋转90度后原图宽高互换 return cy, 1.0 - cx, h, w def apply_brightness(img, factor): hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hsv[:, :, 2] np.clip(hsv[:, :, 2] * factor, 0, 255) return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) def save_yolo_label(label_path, boxes): with open(label_path, w) as f: for box in boxes: cls_id, cx, cy, w, h box f.write(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n) def augment(img_path, label_path, out_dir, aug_type): img cv2.imread(img_path) h, w img.shape[:2] boxes [] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) boxes.append([cls_id, cx, cy, bw, bh]) for i, box in enumerate(boxes): cls_id, cx, cy, bw, bh box if aug_type flip: boxes[i] [cls_id, *flip_label_horizontal(cx, cy, bw, bh)] elif aug_type rotate90: boxes[i] [cls_id, *rotate90_label(cx, cy, bw, bh)] if aug_type flip: img cv2.flip(img, 1) elif aug_type rotate90: img cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) elif aug_type brightness: img apply_brightness(img, random.uniform(0.7, 1.3)) base os.path.splitext(os.path.basename(img_path))[0] save_img os.path.join(out_dir, f{base}_{aug_type}.jpg) save_lbl os.path.join(out_dir, f{base}_{aug_type}.txt) cv2.imwrite(save_img, img) save_yolo_label(save_lbl, boxes) # 用法对每张原图生成两个增强版本 augment(data/images/001.jpg, data/labels/001.txt, data/aug_images, flip) augment(data/images/001.jpg, data/labels/001.txt, data/aug_images, rotate90)注意rotate90_label里宽高互换的处理图片顺时针旋转90度后原来的宽变成了新图的高归一化后的宽高值也要跟着交换否则框就画歪了。这段代码里没有做亮度增强时坐标的改动因为亮度操作不改变目标位置直接沿用原坐标即可。save_yolo_label保证输出标注的格式和原始YOLO格式完全一致这样增强后的数据可以无缝接进训练流程。3.4 增强倍数怎么定不是越大越好增强不是越多越好。我把423张图增强到5倍用随机裁剪、翻转、亮度调整组合结果训练loss下降是快了但在真实场景测试反而下降了。原因是过度增强让模型学到了“假模式”比如某类图片上固定的色块、固定的纹理这些是噪声放大后的产物不是螺钉的真实特征。一般建议增强倍数控制在2~4倍之间。对小目标检测优先做几何变换其次做亮度扰动的轻量组合。增强后的数据集要和原图分开存放训练时按原图和增强图1:1的比例混着喂给模型避免模型只看到增强后的“变形图”而丢了原始特征。我实际跑下来423张图增强到1500张左右训练效果最好再往上提升就有限了。4. 把增强后的数据喂给目标检测模型YOLOv8训练自建数据集全流程4.1 数据集目录结构一张图一条TXT拿到整理好的图片、标注文件和增强后样本后下一步是搭目录。YOLO系列模型对目录结构要求很明确images目录放训练图和验证图labels目录放对应的TXT标注。训练集和验证集按比例划分我一般用8:2423张图拆分后训练集338张、验证集85张。划完记得把增强数据也做同样拆分不要让同一张原图的增强版本同时出现在训练集和验证集里那是数据泄漏。data/ ├── images/ │ ├── train/ # 所有训练图片 │ └── val/ # 所有验证图片 └── labels/ ├── train/ # 训练图片对应的TXT └── val/ # 验证图片对应的TXT机械零件识别检测数据集中423张原始图加增强图按这个结构放好训练脚本直接指定data目录即可。如果原始数据里有XML标注先转成YOLO格式再放labels目录别混着放。4.2 data.yaml与训练命令拿到手就能跑的最小配置YOLOv8训练自建数据集核心在于一个YAML配置文件和一条命令。YAML文件指定类别名、类别数、训练集和验证集路径这是模型感知外部世界的唯一入口。如果类别定义和TXT里的类别编号对不上模型训练就会乱套。# data.yaml train: data/images/train val: data/images/val nc: 2 names: [screw, nut]这个YAML文件里nc是类别数量两个类别就是2names是类别名称列表按txt标注文件里的类别编号顺序排列。类别编号0对应screw类别编号1对应nut编号是从0开始的和TXT文件里每行第一个数字一一对应。一旦这里错了模型会把螺丝识别成螺母且难以排查。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0命令里的modelyolov8n.pt是预训练权重n是nano版本算力不够时的首选imgsz640是输入图片尺寸显存不够可以降到416batch16是批大小显存小就降到8device0指定用GPU没有GPU就把这个参数去掉用CPU训练会很慢但能跑。训练完成后模型权重保存在runs/detect/train/weights/best.pt后续推理和验证都用这个文件。4.3 训练参数选型batch、imgsz、epochs的搭配逻辑这三个参数是目标检测训练中性价比最高的调节项。imgsz决定了模型看到的目标大小——螺丝在原始图上可能只有30x30像素如果把图片缩到416目标就变成20像素以内小目标检测效果会很差。所以我建议用640甚至768代价是显存占用上升、训练速度变慢。用640以上尺寸时图片中目标占比不变模型能学到更多细节纹理。batch受显存限制一般6GB显存跑YOLOv8n用16没问题12GB可以到32。epochs不是越大越好工业零件这种简单场景100轮足够配合早停机制防止过拟合。我自己跑的时候会先来一轮10个epoch的快速验证确认数据路径、标注格式都没问题再启动完整训练这样可以避免等了一个小时才发现数据配错的黑匣子情况。4.4 看loss曲线定早停模型训没训好看这两条线训练时控制台会输出每一次迭代的loss一般包含box_loss框回归损失、cls_loss分类损失、dfl_loss分布聚焦损失三个指标。看曲线时记住两个关键点训练集loss持续下降是正常的验证集loss下降后开始反弹说明模型开始过拟合这时就该停了。YOLOv8提供了早停参数patience默认是50个epoch没提升就自动停止可以按需调成20。盯着验证集的mAP50这个指标它最直观——在零件检测里mAP50到0.9以上说明项目基本可行。如果训练到一半loss变成nan那就是学习率太高把lr0从默认的0.01降到0.001重跑一遍。5. 螺丝螺母数据集训练避坑实录5个必踩的坑与排查方法5.1 坑增强脚本把标注框算歪了现象训练时发现loss下降很快但验证集的mAP曲线一直在0.5附近打转。手动把预测结果画回图片上发现框全部偏在目标旁边有的甚至悬空。原因增强脚本只对图片做了翻转变换却忘了同步更新标注坐标。最常见的是旋转90度时宽高换了但标注框的cx、cy没跟着转。这类问题在翻转、旋转类增强操作中非常高发。解决增强操作后用2.3节画框脚本把增强数据重新画一遍框肉眼对比原图和增强图的框是否都在目标上。凡是坐标标注经不起画框验证的增强操作一律不要出现在训练数据里。5.2 坑小目标漏检——螺丝在图上只有20像素现象模型对螺母识别效果不错但对小螺丝几乎全部漏检验证集里小于30x30像素的目标mAP几乎为0。原因整体缩放到640尺寸时小目标的特征在特征金字塔中经过多次下采样后丢失了。YOLOv8的检测头对8x8特征图上的小目标感知能力有限目标太小、特征太弱模型学不到。解决一是把imgsz从640提到768或1024给小目标更多像素二是在数据增强时加入随机裁剪放大把图中局部小目标放大后再参与训练三是训练集里多放些近距离拍摄的螺丝特写图别全是流水线全景图。5.3 坑样本不均衡——螺母多、螺丝少模型偏科现象模型只输出cat_id等于螺母的检出框螺丝类别几乎被忽略。验证集上螺母类别的召回率90%螺丝类别的召回率只有20%。原因数据集里423张图螺母占了2000个框螺丝只有600个框模型倾向学“常见类别”的捷径对数量少的类别直接放弃。解决先统计TXT里每个类别的框总数对齐标注文件逐个累加。如果不均衡比例超过3:1就要做类别重加权。YOLOv8里没有直接class_weight参数简单做法是用增强脚本单独对螺丝类别多生成几份翻转、旋转样本把两类框数拉近。另外可以调整loss函数对少样本类别的权重但优先还是从数据层面解决。5.4 坑微调崩了——预训练权重与数据不匹配现象加载yolov8n.pt从头微调自己数据集时训练集loss正常下降验证集mAP前几轮是0后续一直不涨模型输出全为空。原因预训练权重是在COCO数据集上学的80类物体特征里面没有螺丝、螺母。头几轮模型在强行覆盖原有知识如果学习率太大或训练轮数太少新旧知识冲突导致权重震荡。严格来说这不是“崩了”是微调过程中的正常过渡期但很多人以为数据有问题就放弃了。解决微调时把初始学习率调低lr00.001warmup_epochs保持默认即可。另外把epochs设置成足够大让模型有充分时间适应新类别。COCO上学的底层纹理、边缘特征对零件识别没有任何负面影响这是迁移学习最大的好处。别动辄重新随机初始化或者白手起家训练那才是真的训练不出来。5.5 坑验证集“泄漏”——增强数据混进了验证集现象训练时mAP一路涨到0.95以上但只要把模型拿到现场部署到实际流水线上表现立刻崩回0.6。随手抽几张训练图片看预测效果看起来完美无缺却掩盖了真实泛化能力的不足。原因划分训练集和验证集时没有检查同一张原图的增强版本是否同时出现在两边。原图和它的水平翻转版在视觉上高度相似模型在训练中已经见过了验证集内容验证集失去评估意义。解决划分数据时先按“原始图片ID”分组以整组为单位划分训练集和验证集保证一张原图的任何增强版本只会落在一个集合里。实现上可以给每张原图一个唯一文件名前缀用前缀重命名所有增强版本再按前缀哈希划分集合。import os import random import shutil # 按原图ID分组划分避免数据泄漏 all_groups {} for img_name in os.listdir(data/images): group_id img_name.split(_)[0] # 假设原图命名如 001.jpg增强如 001_flip.jpg all_groups.setdefault(group_id, []).append(img_name) ids list(all_groups.keys()) random.seed(42) random.shuffle(ids) val_ids set(ids[:int(len(ids) * 0.2)]) # 20%的组做验证 data_dir data os.makedirs(f{data_dir}/images/val, exist_okTrue) os.makedirs(f{data_dir}/images/train, exist_okTrue) os.makedirs(f{data_dir}/labels/val, exist_okTrue) os.makedirs(f{data_dir}/labels/train, exist_okTrue) for group_id, imgs in all_groups.items(): target_set val if group_id in val_ids else train for img_name in imgs: lbl_name img_name.rsplit(., 1)[0] .txt shutil.move(f{data_dir}/images/{img_name}, f{data_dir}/images/{target_set}/{img_name}) shutil.move(f{data_dir}/labels/{lbl_name}, f{data_dir}/labels/{target_set}/{lbl_name})这段代码的操作逻辑是先把所有图片按组归类再把每个组整体划入验证集或训练集一个组的增强版本永远不会被拆散。random.seed(42)保证每次划分结果一致如果你想换个划分方式改这个种子就可以了。这样划出来的验证集才有真实评估价值模型的现场表现和验证指标才会接近。6. 423张图的数据集怎么做出能上线的模型验证与迭代技巧训练完不等于完事部署前还差最后一步严谨验证。我的习惯是拿增强后数据里约10%的样本做“模拟真实验证集”这些样本在训练全程不参与梯度更新。然后跑一遍测试重点看两类错误误检和漏检。对螺丝和螺母这类形状简单、特征明确的零件漏检比误检更致命漏检导致下游机械臂抓不到工件整条流水线卡住。自动验证完一定要手动看一批预测结果不要只看指标。从验证集里随机抽20张图把模型预测结果画回去人眼扫一遍。这个动作虽然原始但在小数据集场景下比任何指标统计都更能暴露问题。如果发现某个角度的螺丝总是漏检回到数据增强脚本单独为那个角度补充样本重新训练一轮。这就是“数据闭环”——增长的不是模型复杂度而是数据覆盖面。最后说一个让效果再提一档的小技巧测试时增强TTA。训练完成后推理时把原图、水平翻转图、垂直翻转图各跑一次预测再把结果合并。对于拧在不同角度、不同光线下的螺丝螺母这个技巧能提3~5个点。代价是推理时间变长如果实时性要求高这一步可以省略。做机械零件识别这类小目标检测数据量少是常态别指望等到上万张图才开始。423张图加一份靠得住的增强脚本把标注质量把好关、增强逻辑写对称、验证集划分干净一个能用的检测模型就在手里了。希望这些调试习惯和踩坑经验帮到你。本文还有配套的精品资源点击获取
