恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLO实时物体检测实战:齿条、螺栓、螺母工业质检方案
首页
资讯中心
/
YOLO实时物体检测实战:齿条、螺栓、螺母工业质检方案
YOLO实时物体检测实战:齿条、螺栓、螺母工业质检方案
发布时间:2026/10/12 18:35:08
简介这份资源面向计算机视觉初学者与工业检测方向的开发者围绕YOLO实时物体检测展开重点覆盖齿条、螺栓、螺母及裂缝等目标的识别任务可用于制造业质量控制、紧固件缺失排查与结构裂缝预警等场景。压缩包共2000个文件约199.17MB以1903个txt数据与说明文件为主另含49个h头文件、46个c源文件及少量cpp与md文档构成一套可编译的YOLO工程代码涉及数据加载、图像处理、网络构建与各层实现等模块。已有67人学习下载。读者可从中获取完整的检测工程源码与配套数据理解网格划分、边界框预测、置信度与IOU计算等核心机制并借助多版本迭代思路与损失函数、训练策略的改进经验快速搭建面向工业零件的实时检测流程排查编译与训练中的常见问题。1. 产线质检的实时困局为什么齿条螺栓螺母的检测值得单独做一套 YOLO一条装配线上齿条、螺栓、螺母三种零件混在同一个振动盘里往下走相机每秒出 30 帧你要在零件进入下一道工序之前判断齿条有没有裂纹、螺栓头部有没有缺料、螺母有没有混入规格不符的次品。这个场景听起来简单但真正上手会发现两个绕不开的矛盾——零件小、背景金属反光强通用检测模型直接拿来用召回率经常掉到七成以下而产线节拍不等人单帧推理超过 30 毫秒后面的分拣机构就跟不上。这就是「yolo实时物体检测_齿条、螺栓、螺母」这个方向真正要解决的问题不是训练一个能跑通的模型而是训练一个在金属反光、小目标、多类别混流条件下还能稳定实时输出的模型。它适合两类人——一类是手上已经有产线相机和分拣执行机构、缺一个能落地的检测方案的自动化工程师另一类是刚接触 YOLO、想找一个比 COCO 更贴近工业场景的数据集和训练流程来练手的算法同学。接下来我会按数据准备、模型选型、训练调参、部署推理、踩坑排查的顺序把这套方案拆到能照着复现的程度。2. 数据先行齿条、螺栓、螺母的标注策略与增强方案工业检测项目翻车十次里有七次不是模型的问题是数据的问题。齿条、螺栓、螺母这三类零件在图像里的形态差异很大齿条是长条形、纹理重复螺栓是短粗带螺纹螺母是六边形小目标。如果标注规则不统一模型学到的边界会非常混乱。2.1 三类零件的标注边界怎么定标注的第一原则是框要框住「判断缺陷所需的最小完整区域」而不是框住整个零件。以齿条裂纹检测为例如果裂纹出现在齿根位置标注框应该覆盖裂纹所在的那一段齿而不是整根齿条。原因是整根齿条可能占据图像大半宽度框太大模型对小裂纹的特征响应会被稀释。具体规则我一般这样定齿条按「齿段」标注每 5 到 8 个齿为一个标注单元类别名用rack。如果做裂纹检测额外加一个rack_crack类别框只覆盖裂纹区域。螺栓按「头部 可见螺纹段」标注类别名bolt。缺料缺陷单独标bolt_missing。螺母按六边形外轮廓标注类别名nut。规格不符的次品标nut_defect。标注工具用 LabelImg 或 CVAT 都可以导出 YOLO 格式的 txt。这里有一个容易忽略的点YOLO 的 txt 格式是class_id x_center y_center width height全部归一化到 0 到 1。如果标注时图像有旋转归一化坐标会出错所以标注前先把图像统一转正。# 将 LabelImg 的 VOC xml 转成 YOLO txt 格式 import xml.etree.ElementTree as ET import os # 类别映射顺序必须和训练时的 data.yaml 一致 CLASS_MAP {rack: 0, bolt: 1, nut: 2, rack_crack: 3, bolt_missing: 4, nut_defect: 5} def voc_to_yolo(xml_path, img_w, img_h, out_dir): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in CLASS_MAP: continue 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) # 归一化并转成中心点 宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{CLASS_MAP[cls_name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))这段代码的关键参数是CLASS_MAP它决定了类别 id 的分配。注意rack_crack和rack是两个独立类别不要合并否则模型会把裂纹当成正常齿条的一部分。img_w和img_h必须和实际图像尺寸一致如果标注时用的是缩放后的图这里要填缩放前的原始尺寸。2.2 金属反光场景下的增强参数怎么设工业图像增强和自然图像增强的逻辑不一样。自然图像常用随机裁剪、颜色抖动但金属零件的颜色信息本身就不稳定颜色抖动反而会引入噪声。我一般用这几类增强随机亮度对比度调整brightness_limit0.2, contrast_limit0.2模拟产线光照波动。高斯模糊blur_limit(3, 7)模拟镜头轻微失焦。随机遮挡cutout或coarse_dropout模拟零件重叠。水平翻转p0.5但垂直翻转要慎用因为螺栓和螺母的上下方向有语义。用 Albumentations 配置增强管道import albumentations as A train_transform A.Compose([ A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.5), A.GaussianBlur(blur_limit(3, 7), p0.3), A.CoarseDropout(max_holes8, max_height16, max_width16, p0.3), A.HorizontalFlip(p0.5), # 注意不做垂直翻转保留零件方向语义 ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels]))CoarseDropout的max_holes不要设太大8 个以内比较合适否则小目标螺母容易被完全遮住模型学不到有效特征。GaussianBlur的核大小用奇数3 到 7 之间模拟不同对焦状态。提示增强后的图像要抽样人工检查一遍确认标注框没有因为增强而错位。Albumentations 会自动同步 bbox但如果用了自定义增强函数需要手动处理坐标变换。3. 模型选型与训练从 YOLOv8n 到可落地的工业检测器数据准备好之后下一步是选模型。工业实时检测的选型逻辑和学术 benchmark 不一样不是精度越高越好而是在满足节拍的前提下找精度上限。3.1 为什么我优先选 YOLOv8n 而不是更大的模型YOLOv8n 是 nano 版本参数量约 3.2M在 RTX 3060 上单帧推理可以压到 8 毫秒以内。齿条、螺栓、螺母这三类目标的特征相对固定不需要超大模型来学复杂语义。我做过对比YOLOv8s 的 mAP 比 v8n 高大约 2 到 3 个点但推理时间翻倍在 30 FPS 的产线上没有优势。如果产线用的是边缘设备比如 Jetson 系列那 v8n 几乎是唯一选择。更大的模型要么跑不动要么需要降分辨率降分辨率之后小目标螺母的召回率掉得比换模型还快。选型时还要看输入分辨率。默认 640×640 对螺母这种小目标可能不够我一般会试 640 和 960 两个档位。960 的推理时间大约是 640 的 2.2 倍但如果螺母在图像里只有 20 像素宽960 带来的召回提升是值得的。3.2 训练命令与关键超参设置YOLOv8 的训练入口很简洁但工业场景有几个参数必须改# 训练命令基于 ultralytics 框架 yolo detect train \ data./data/industrial.yaml \ modelyolov8n.pt \ imgsz960 \ epochs150 \ batch16 \ lr00.01 \ lrf0.01 \ warmup_epochs5 \ patience30 \ augmentTrue \ cacheTrue \ device0逐项说明data指向数据集配置文件里面写train、val路径和names类别名。imgsz960输入分辨率。如果显存不够降到 640但要做小目标召回对比。epochs150工业数据集通常比 COCO 小150 轮足够收敛。配合patience3030 轮没提升就早停。lr00.01初始学习率。如果 loss 在前 10 轮震荡降到 0.005。warmup_epochs5预热轮数防止初期梯度爆炸。cacheTrue把图像缓存到内存加快训练。数据集超过 10GB 时慎用。数据集配置文件industrial.yaml长这样path: ./data/industrial train: images/train val: images/val names: 0: rack 1: bolt 2: nut 3: rack_crack 4: bolt_missing 5: nut_defectnames的顺序必须和前面CLASS_MAP完全一致否则训练出来的模型类别会错位这种错误在推理时表现为「框对了但标签全错」排查起来很费时间。3.3 训练过程中的 loss 曲线怎么看训练启动后重点盯三个指标box_loss、cls_loss、mAP50。正常情况下box_loss在前 20 轮快速下降之后缓慢收敛cls_loss下降速度取决于类别平衡度如果某一类样本特别少cls_loss会居高不下。如果box_loss降到 0.5 以下但mAP50不涨通常是标注框质量问题回去检查标注。如果cls_loss震荡剧烈检查学习率是否过大或者类别样本是否严重不均衡。齿条裂纹这种缺陷样本通常很少可以用过采样或者copy_paste增强来补。注意不要只看最终 mAP要分类别看。螺母的 mAP 如果明显低于齿条和螺栓说明小目标检测是瓶颈优先考虑提高输入分辨率而不是换更大的模型。4. 推理部署把训练好的模型塞进产线节拍训练完模型只是第一步真正决定项目能不能落地的是推理环节。产线环境对延迟、稳定性、异常处理的要求比实验室高得多。4.1 用 ONNX 导出与推理加速PyTorch 模型直接推理会有框架开销我一般导出成 ONNX再用 ONNXRuntime 或 TensorRT 跑。导出命令yolo export model./runs/detect/train/weights/best.pt formatonnx imgsz960 opset12 simplifyTrueopset12是兼容性比较好的版本simplifyTrue会做图优化去掉冗余算子。导出后用 ONNXRuntime 推理import onnxruntime as ort import numpy as np import cv2 # 加载 ONNX 模型指定 CUDA 执行提供者 session ort.InferenceSession(./best.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider]) input_name session.get_inputs()[0].name def preprocess(img, size960): # letterbox 保持长宽比避免形变影响小目标 h, w img.shape[:2] scale min(size / h, size / w) nh, nw int(h * scale), int(w * scale) resized cv2.resize(img, (nw, nh)) canvas np.full((size, size, 3), 114, dtypenp.uint8) canvas[:nh, :nw] resized blob canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 return np.expand_dims(blob, axis0), scale def infer(img): blob, scale preprocess(img) outputs session.run(None, {input_name: blob}) # outputs[0] 形状为 [1, num_boxes, 4num_classes] return outputs[0], scaleletterbox是关键直接 resize 会拉伸图像齿条的长宽比被破坏后模型对裂纹的定位精度会下降。114是填充灰度值和 YOLO 训练时的填充值保持一致。4.2 后处理NMS 阈值与置信度阈值怎么调YOLO 输出的是大量候选框需要经过非极大值抑制NMS才能得到最终结果。两个核心参数conf_thres置信度阈值。工业场景我一般设 0.35 到 0.5。设太低会引入大量误检设太高会漏掉裂纹这种低对比度目标。iou_thresNMS 的 IoU 阈值。设 0.45 到 0.6。如果零件密集排列设低一点避免相邻零件被合并。def nms(boxes, scores, iou_thres0.5): # boxes: [N, 4] xywh, scores: [N] x1 boxes[:, 0] - boxes[:, 2] / 2 y1 boxes[:, 1] - boxes[:, 3] / 2 x2 boxes[:, 0] boxes[:, 2] / 2 y2 boxes[:, 1] boxes[:, 3] / 2 areas (x2 - x1) * (y2 - y1) order scores.argsort()[::-1] keep [] while order.size 0: i order[0] keep.append(i) xx1 np.maximum(x1[i], x1[order[1:]]) yy1 np.maximum(y1[i], y1[order[1:]]) xx2 np.minimum(x2[i], x2[order[1:]]) yy2 np.minimum(y2[i], y2[order[1:]]) inter np.maximum(0, xx2 - xx1) * np.maximum(0, yy2 - yy1) iou inter / (areas[i] areas[order[1:]] - inter) order order[1:][iou iou_thres] return keep这段 NMS 是标准实现实际部署时可以直接用 ONNXRuntime 的NonMaxSuppression算子速度更快。但理解原理有助于排查「框重叠」或「框丢失」的问题。4.3 产线节拍对齐从单帧推理到流水线单帧推理时间只是下限实际产线还要考虑图像采集、预处理、后处理、通信的时间。我一般会做一个端到端计时环节典型耗时ms优化手段图像采集2 到 5用硬件触发避免软触发抖动预处理3 到 8用 GPU 做 resize 和归一化模型推理8 到 15ONNX TensorRT半精度后处理2 到 5向量化 NMS避免 Python 循环通信输出1 到 3用共享内存或零拷贝总耗时控制在 25 毫秒以内才能稳定跑 30 FPS。如果超了优先优化预处理和后处理这两块用 Python 写很容易成为瓶颈。提示产线部署一定要加异常兜底。模型推理失败或超时不能直接卡死要输出一个「未知」状态让分拣机构跳过同时记录日志。5. 避坑与排查齿条螺栓螺母检测的 5 个血泪教训这一章是我在实际项目里踩过的坑每条都按「现象 → 原因 → 解决」写希望能帮你省点时间。5.1 螺母召回率突然掉一半现象训练时螺母的 mAP 有 0.85部署到产线后实际召回只有 0.4大量螺母漏检。原因产线相机换了镜头视场角变大螺母在图像里的像素尺寸从 35 降到 18。模型训练时用的是 640 分辨率小目标特征本身就弱尺寸再缩小就彻底学不到了。解决把推理分辨率从 640 提到 960同时用产线实际图像重新标注一批小尺寸螺母样本做微调训练。微调时lr0降到 0.001训练 30 轮即可。5.2 齿条裂纹被当成正常齿条现象裂纹检测的召回率始终上不去模型把有裂纹的齿条判成正常。原因裂纹样本太少和正常齿条的样本比例是 1:50。模型在训练时倾向于把所有齿条都判成正常因为这样 loss 更低。解决两个手段一起上。一是对裂纹样本过采样让比例降到 1:5 以内二是在 loss 里给裂纹类别加权重YOLOv8 可以在data.yaml里通过cls_pw参数调整或者用 focal loss 替换默认的 BCE loss。5.3 金属反光导致误检框满天飞现象图像里出现大量高置信度误检框位置都在反光斑点上。原因训练数据里的光照条件太单一模型没见过强反光样本把反光当成了目标特征。解决在增强管道里加RandomSunFlare或RandomShadow模拟反光。另外采集数据时要有意覆盖不同光照时段上午、中午、下午各采一批。如果已经训练完了可以用产线误检图像做 hard negative mining把这些反光区域标成背景重新训练。5.4 推理延迟忽高忽低现象平均推理时间 12 毫秒但偶尔跳到 50 毫秒以上导致丢帧。原因Python 的垃圾回收GC在推理过程中触发或者 ONNXRuntime 的线程池和图像采集线程抢 CPU。解决推理主循环里关掉 GC 自动触发改成手动定时回收ONNXRuntime 的intra_op_num_threads设成固定值不要用默认的自动分配。另外图像采集和推理放在不同进程用队列通信避免线程竞争。5.5 模型换批次后精度下降现象同一套模型换了一批新零件后误检率明显上升。原因新批次零件的表面处理工艺有变化比如发黑处理改成镀锌颜色和纹理都变了。模型对训练时的表面特征过拟合。解决这是工业检测的常态没有一劳永逸的模型。我的做法是保留一个「在线难例回传」机制产线上置信度在 0.3 到 0.5 之间的样本自动存下来每周人工审核一次把确认的难例加入训练集做增量训练。增量训练用小的学习率只跑 20 到 30 轮避免灾难性遗忘。6. 进阶技巧用切片推理和类别平衡把 mAP 再推 5 个点前面五章把一套能跑的方案讲完了这一章说两个进阶手段适合已经跑通 baseline、想再压榨精度的人。第一个手段是切片推理SAHI。齿条是长条形目标在 960 分辨率下可能占据图像大半宽度但螺母又很小。单一分辨率很难同时照顾两者。切片推理的思路是把图像切成重叠的小块每块单独推理再合并结果。对于齿条这种大目标切片后反而能保留更多局部细节对于螺母切片相当于变相提高了分辨率。from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction # 加载模型切片推理 detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_path./best.onnx, confidence_threshold0.35, devicecuda ) result get_sliced_prediction( line_frame.jpg, detection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2 )slice_height和slice_width设成 640overlap设 0.2意思是相邻切片有 20% 重叠避免目标被切断。切片推理的代价是推理时间增加因为一张图要跑多次。如果产线节拍允许比如从 30 FPS 降到 15 FPS 也能接受那这个手段值得上。如果节拍卡死就不要用。第二个手段是类别平衡采样。齿条、螺栓、螺母的样本量通常不均衡螺母因为小、数量多样本可能占 60% 以上裂纹样本可能不到 2%。训练时按类别频率做加权采样让每个 batch 里各类别比例接近。YOLOv8 本身不直接支持类别平衡采样但可以在数据集层面做把低频类别的图像复制多份或者用WeightedRandomSampler自定义 dataloader。我自己的习惯是每次训练完先看混淆矩阵找出被误判最多的类别对然后针对性地补样本。比如齿条裂纹经常被误判成正常齿条那就专门采集「有裂纹但光照不好」的样本而不是盲目加数据量。这个习惯帮我省了很多无效训练时间。最后说一个验证方法不要只用 mAP 判断模型好坏。产线关心的是漏检率和误检率这两个指标和 mAP 不是线性关系。我一般会在验证集上画一条「置信度阈值 vs 漏检率/误检率」的曲线找到满足产线要求的阈值点再把这个阈值固化到部署配置里。这样模型换版本时只要重新跑一遍曲线就能快速判断新模型能不能用。希望帮到你。本文还有配套的精品资源点击获取