YOLOv10汽车轮胎缺陷检测实战:从数据标注到模型微调全流程

发布时间:2026/9/16 5:26:39
YOLOv10汽车轮胎缺陷检测实战:从数据标注到模型微调全流程 简介面向汽车轮胎检测场景的 YOLOv10 识别方案包含训练好的轮胎类别tire权重与配套数据集标签同时提供 txt 和 xml 两种格式并分别保存在独立文件夹中PR 曲线、loss 曲线、训练事件日志等过程文件也一并打包方便复现实验与评估模型。资源共 1701 个文件、约 148.39MB主要文件类型覆盖 jpg 图像、txt/xml 标注、py/pyc 代码、yaml 配置、pt 权重和 CSV 指标数据另有 C 推理、Dockerfile、notebook 示例、TensorBoard 日志等扩展内容从数据准备、模型训练到部署推理均有对应模块。上手者可直接加载 .pt 权重对轮胎图片做推理也能基于这两套标注格式自行调整数据集并继续微调压缩包内还包括若干环境配置与推理入口可减少复现时的依赖排查成本。适合有 Python/PyTorch 基础、从事车辆质检或驾驶辅助相关工作的开发者学习使用。目前已有 933 人学习下载。1. 轮胎检测用 YOLOv10不是赶时髦轮胎检测这个任务看着简单真做起来坑不少。胎面细裂纹、侧壁鼓包、花纹磨损这类缺陷跟轮胎表面的纹理、灰尘混在一起通用目标检测模型直接搬过去用漏检率能到三成以上。YOLOv10 发布后被车载视觉和工业质检团队频繁选用核心不是去掉了 NMS 这个噱头而是它在保持实时推理的同时对中小目标的召回率比同代模型更可靠正好对应轮胎缺陷尺寸小、分布密的特点。接下来按一线工程的做法把 YOLOv10 做汽车轮胎检测的完整链路讲清楚数据集怎么摆、权重怎么推理、自带数据集怎么微调出权重最后是验证和难例回灌。新手照步骤能跑通熟手重点看参数边界和踩坑点。2. YOLOv10 的架构变化与轮胎检测适配2.1 NMS-free 检测头对轮胎这类目标意味着什么YOLOv10 相对 v8 最大的改动是把训练时的 one-to-many 标签分配和推理时的 NMS 合并成了 one-to-one 匹配。传统 YOLO 推理时必须做非极大值抑制把同一个目标上的多个候选框合并成一个这个步骤在轮胎场景里很吃亏胎面一段磨损带里同时出现十几条细裂纹时NMS 会把它们压成一个框缺陷数量统计直接失真。YOLOv10 训练阶段就给每个真实目标分配唯一正样本推理输出的框天然稀疏不需要后处理合并漏检和误检的表现都更一致。另一个对轮胎检测实用的改动是轻量级分类头。YOLOv10 在分类分支上用了深度可分离卷积整体计算量下降这对部署在 Jetson、RK3588 这类边缘设备上的轮胎识别系统很友好。胎侧和胎面缺陷在图像里的长宽比差异很大YOLOv10 的 anchor-free 设计让回归分支不需要预设 anchor 尺寸4:1 以上的细长裂纹也能直接回归省掉了 anchor 聚类这一步。有些团队在轮胎朝向变化大的场景里试过旋转目标检测mmrotate 训练 DOTA 那套思路但产线相机位置固定、轮胎角度可控时正框检测已经够用旋转框的标注成本和推理开销都不划算。2.2 选 yolov10n/s/m 哪个规格跑轮胎识别权重选型直接决定后面的调参基线。官方 YOLOv10 有 n/s/m/b/l/x 六个规格但轮胎检测真正常用的就是前三档规格参数量量级单帧耗时量级GPU典型场景yolov10n约 2.3M13ms嵌入式巡检、移动端yolov10s约 7.2M35ms产线在线检测首选yolov10m约 15.4M58ms离线复检、缺陷分级我一般建议产线项目从 yolov10s 起步这个规格在精度和速度上最平衡。如果业务只要「轮胎有无」这种粗判断yolov10n 足够如果后面要接缺陷等级分类直接上 yolov10m省得来回试错。注意这个选择有个前提用 COCO 预训练权重初始化。轮胎类别在 COCO 里没有需要预训练权重提供通用视觉特征做迁移微调细节在第 4 章展开。b 和 l 规格在轮胎这种单类别任务上收益很小参数翻倍换来的 mAP 提升不到 1 个点部署和推理成本却成倍增加一般不推荐。2.3 轮胎数据集的标注规范与目录组织拿到带标注的轮胎数据集后第一步不是训练而是核对目录结构。YOLO 格式的标准组织方式是这样tire_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── tire.yaml每一张images/train/xxx.jpg对应一个labels/train/xxx.txttxt 每行是class x_center y_center width height坐标是相对图片宽高的归一化值取值 0 到 1。收到数据集后先写几行脚本核对图片和标签是否一一对应缺标签的图片要单独挑出来。否则训练时 YOLO 会把缺标签的图当成负样本参与计算background 类别被污染后期误检率会异常高。轮胎目标有一个高频标注错误把整个轮胎当检测框而不是把缺陷区域当检测框。如果你的任务定位是缺陷检测标注框应该框住裂纹、鼓包的具体区域而不是胎体轮廓。框太大模型学到的是「轮胎在哪」而不是「缺陷在哪」这和自动驾驶数据集里标注车辆障碍物的思路不一样做轮胎缺陷时特别容易踩。3. 用训练好的轮胎识别权重跑通推理3.1 环境安装与权重文件组织YOLOv10 的训练和推理建议直接用 ultralytics 框架这是目前维护最活跃、API 最完善的实现YOLOv5/v8 的用法几乎可以无缝迁移。安装只需要一行命令pip install ultralytics装完把训练好的权重统一放进weights/目录比如weights/tire_best.pt。很多人拿到权重后随手放桌面就开始跑等到要换机器部署时找不到对应的训练配置很被动。我习惯在权重旁边同时保存训练时的tire.yaml和args.yaml这两个文件记录了类别名、数据集路径和超参数。三个文件放同一个目录再打包分发换设备时结果可复现。3.2 图片、视频、摄像头三种推理方式ultralytics 的推理核心就一个predict接口。假设手头的训练权重是tire_best.pt先跑一张图验证输出from ultralytics import YOLO # 加载训练好的轮胎检测权重 model YOLO(weights/tire_best.pt) # 单张图片推理saveTrue 把标注结果图保存到 runs/detect/ results model.predict( sourcedemo_tire.jpg, conf0.25, iou0.7, imgsz640, saveTrue, ) # 打印每个目标的类别与置信度 for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别 {cls_id} 置信度 {conf:.3f} 坐标 {xyxy})参数逻辑conf是置信度阈值低于它的预测框被丢弃轮胎检测一般从 0.25 起步iou在 YOLOv10 推理时不是必需路径ultralytics 保留它用于多模型结果合并时的去重imgsz是输入分辨率裂纹这类小目标建议至少 640算力允许直接上 1280。第一次推理前框架会自动下载对应的预训练配置网络正常时不需要手动准备 yaml。视频和摄像头推理不用改代码逻辑只换source。视频文件直接给路径摄像头给设备序号比如source0代表第一个 USB 摄像头。批量化验证时给一个目录路径框架会自动递归读取里面的所有图片yolo predict modelweights/tire_best.pt source./test_images/ conf0.25 imgsz640 saveTrue命令行参数和 Python 接口完全一致适合快速过一批图。跑完重点看runs/detect/predict/下的标注图第一轮不着急看指标先看坏样本漏检的目标是遮挡严重、光照暗还是本身模糊这些观察结论直接决定第 4 章的调参方向。3.3 推理参数怎么调imgsz 优先conf 次之在轮胎检测上imgsz 对结果的影响通常大于 conf。把 imgsz 从 640 提到 1280小裂纹的召回率能提升 5 到 10 个点代价是推理耗时增加两到三倍。调参顺序我固定为先定 imgsz再扫 conf最后动 iou。三个参数的典型取值和影响如下参数常见取值调大后的效果调小后的效果imgsz640 / 1280召回率升、速度降速度升、小目标漏检变多conf0.150.5误检变少、漏检变多漏检变少、误检变多iou0.50.7相邻框合并变少密集目标框被合并选择 conf 可以做个快速扫描在验证集上记录不同阈值下的精确率和召回率取两条曲线的交叉点附近。业务上要是后面有人工复核conf 放到 0.15 也行无人值守的自动分拣线conf 建议拉到 0.4 以上减少误触发导致的停机。4. 用带的数据集微调训练自己的轮胎识别权重4.1 数据集配置 yaml 的写法拿到数据集后先写tire.yaml这是训练入口。格式和 YOLOv5/v8 完全兼容所以「yolov10 yaml 文件怎么创建」这个问题答案往往不是从零创建而是复制现成配置改路径# tire.yaml path: /data/tire_dataset # 数据集根目录 train: images/train # 训练图片目录相对 path val: images/val # 验证图片目录 test: images/test # 测试集可省略 nc: 3 # 类别数 names: # 类别名与 class id 一一对应 0: tread_crack # 胎面裂纹 1: sidewall_bulge # 侧壁鼓包 2: tread_wear # 胎面磨损names的顺序最容易出错。标注文件第一列是数字 class id如果标注工具导出时的类别顺序和你写的顺序不一致模型会学到完全错位的映射推理表现为「框的位置对、标签对不上」。写 yaml 前先抽一个 labels 下的 txt 文件确认 class id 分布再回填。提示yaml 文件用记事本编辑容易存成带 BOM 的 UTF-8框架解析时直接报错。用命令行创建最保险还能避免缩进被自动纠正成空格的问题。cat /data/tire_dataset/tire.yaml EOF path: /data/tire_dataset train: images/train val: images/val nc: 3 names: 0: tread_crack 1: sidewall_bulge 2: tread_wear EOF写完检查解析yolo cfg/data/tire_dataset/tire.yaml能正常打印配置说明没问题。训练自己的数据集这个流程在 v5、v8 时代就定型了YOLOv10 只是换了model的前缀不用重学一套体系。4.2 训练命令与关键超参数用 COCO 预训练权重初始化训练自己的轮胎数据集yolo train \ modelyolov10s.pt \ data/data/tire_dataset/tire.yaml \ epochs100 \ batch16 \ imgsz640 \ lr00.01 \ optimizerSGD \ projecttire_train \ nameexp1 \ patience20参数要点modelyolov10s.pt保留yolov10s前缀框架才能识别 backbone 结构首次运行会自动下载对应 COCO 预训练权重patience20表示验证集指标连续 20 个 epoch 不提升就提前停止轮胎数据量不大时通常 40 到 60 个 epoch 就能收敛lr0是初始学习率数据量小于 5000 张时建议降到 0.005防止早期 mAP 震荡optimizerSGD是数据量小时更保险的选择数据量上万可以换AdamW加速收敛。显存不够时优先减batch而不是imgsz。batch 减半照样收敛只是梯度噪声大一点imgsz 从 640 降到 416小目标特征基本丢失。batch 最小可以到 4配合accumulate2等效于 batch8是省显存的常用组合。4.3 训练日志怎么看loss 曲线和指标训练过程盯三个量box_loss、cls_loss、mAP50。它们都在runs/detect/train/results.csv里每个 epoch 一行。前 10 个 epoch 快速下降是正常的20 个 epoch 后还在大幅波动先查学习率再查标注质量——标注框偏移超过目标尺寸三分之一时loss 会一直下不去。验证集指标重点看mAP50和mAP50-95的差别mAP50是 IoU 阈值 0.5 下的平均精度适合评估「轮胎目标有没有被框住」mAP50-95是 0.5 到 0.95 每 0.05 算一次再取平均对定位精度更敏感。裂纹缺陷对框的贴合度要求高mAP50-95至少要过 0.5 才敢上线。训练结束目录里会有best.pt和last.pt。best.pt是验证集 mAP50 最高的权重推理和部署都用它。如果best.pt和last.pt指标差距超过 15 个点说明训练后期过拟合这时该减 epochs 或加强数据增强而不是继续拖长训练。4.4 数据划分的陷阱轮胎数据集最常见的问题是同一个轮胎序列的图片被同时分进训练集和验证集。轮胎纹理高度相似模型容易记住特定花纹验证集指标虚高到真实环境直接掉点。正确做法是按场景划分同一条轮胎的所有图片只进训练或只进验证。类别不平衡也很常见比如裂纹 8000 个框、鼓包只有 200 个。常见做法有两条一是给少数类加 loss 权重ultralytics 里通过class_weight指定二是在数据增强里提高mosaic和copy_paste的概率让少数类在训练时多出现几次。两条路可以同时走先加增强再看指标决定要不要动 loss 权重。5. 轮胎检测权重的漏检回灌与注意力微调5.1 批量漏检扫描脚本模型上线前我会写一个脚本把验证集全部跑一遍自动挑出漏检图片。这是性价比最高的改进入口对比每张图的标注框数和预测框数预测少于标注的就是漏检样本。from ultralytics import YOLO from glob import glob model YOLO(weights/tire_best.pt) val_images glob(/data/tire_dataset/images/val/*.jpg) missed [] for img_path in val_images: results model.predict(img_path, conf0.25, imgsz640, verboseFalse) label_path img_path.replace(images/val, labels/val).replace(.jpg, .txt) with open(label_path) as f: gt_count len(f.readlines()) pred_count len(results[0].boxes) if pred_count gt_count: missed.append(f{img_path} (gt{gt_count}, pred{pred_count})) print(f漏检样本数: {len(missed)} / {len(val_images)}) for m in missed[:20]: print(m)逻辑不复杂关键在于对比逻辑预测框数少于真实框数说明至少一个目标没被检出。把missed里的图片单独建一个难例目录下一轮训练混入训练集真实场景的召回率会明显改善。这比盲目调 conf 有效得多。5.2 难例回灌与注意力微调难例回灌的做法把漏检图片和对应标注复制到images/train和labels/train重新跑第 4 章的训练命令。这是「续训」不是从头训权重继承上一轮的best.pt用modelweights/tire_best.pt指定epochs 减到 30 到 50学习率降到原来的十分之一。如果复杂背景下还是反复漏检可以尝试在 backbone 的 C2f 结构里插入轻量注意力模块比如 CBAM它用通道注意力和空间注意力两路权重分配让模型更关注轮胎纹理区域、抑制背景干扰。常见做法是复制一份 ultralytics 的nn/modules目录加一个CBAM类再改tire.yaml把指定层替换进去。这个改动对显存占用影响很小mAP50 提升一般在 1 到 3 个点属于值得一试的优化方向但不建议在数据还没洗干净时做先解决标注和数据划分问题最后调模型结构。本文还有配套的精品资源点击获取