简介:YOLOv10汽车轮胎检测项目提供训练好的轮胎识别权重与配套数据集,主要面向自动驾驶、智能交通、车辆检测等场景下的轮胎目标检测需求,适合有一定PyTorch基础、需要快速获取完整训练流程或参考数据组织方式的开发者。项目类别定义为tire,使用PyTorch框架并以Python代码实现,标签文件分别按txt和xml两种格式保存在独立文件夹中,便于切换不同标注工具或复现训练。压缩包共1701个文件,核心内容包括450张JPG轮胎图像、450个txt标签、158个Python脚本、67个yaml配置以及训练好的pt权重文件;同时附带PR曲线、loss曲线、训练日志、说明文档和部分推理示例代码,整体大小148.39MB。资源还提供了作者在实际轮胎检测数据集上的训练结果与配套参考链接,可帮助读者快速复现检测效果;权重既可直接用于推理,也可基于数据集继续微调。当前已有933人浏览学习,适合YOLOv10入门学习或轮胎识别项目开发者直接参考使用。
1. YOLOv10 轮胎检测:为什么通用权重在部件级识别上不香
很多工程师拿到 COCO 预训练权重就直接跑车辆检测,结果发现车框得很准,轮胎单独框出来却总是一半、漏检、或者把轮毂和轮胎混在一起。原因很简单:COCO 的 80 类里没有“轮胎”,模型在训练时只见过“car”“truck”这种整体语义,没见过“tire”这种部件级目标。你真正需要的是一个在轮胎数据集上专门收敛过的权重,以及能复现这个收敛过程的一整套配置。这份 YOLOv10 汽车轮胎检测资源包里恰好同时给了训练好的权重和配套数据集。
资源包的核心内容是:一个训练好的轮胎识别权重(类别名tire,单类别)、包含txt(YOLO 格式)和xml(VOC 格式)两种标签的完整数据集,以及训练过程中产生的events.out.tfevents日志文件——里面有 PR 曲线和 loss 曲线,可以直接复盘模型的收敛过程。这对两类人最有用:一是正在做车辆部件识别、轮胎缺陷检测预研的 CV 工程师,二是准备用 YOLOv10 训练自己的单类别数据集、但不想从零开始做数据标注的人。下文从数据集解析、配置文件改造、推理验证和事件文件复盘四个维度,把这个项目完全拆开。
2. 数据集解剖:txt 标签与 xml 标签的对应关系与转换
2.1 目录结构里的隐藏信息
解压资源包后先看目录,通常会出现images、labels(包含 txt)、Annotations(包含 xml)三类目录,以及顶层散落的labels.cache、events.out.tfevents.1719203393.USER-20231125JB.7292.0等文件。labels.cache是 YOLO 训练过程中自动生成的标签缓存,作用是加速数据集校验和样本读取;如果改动过标签文件,它不会自动重建,而会在训练时报数据不匹配错误,需要手动删除后重新生成。
find . -maxdepth 2 -type d | sort find . -name "*.xml" | wc -l find . -name "*.txt" -path "*labels*" | wc -l第一条命令梳理目录层级,第二、三条用来快速核对两种格式的标签数量是否一致。数量不一致时优先检查是不是有纯背景图没有标注,这类图片需要保留但只留 txt 空标签文件,否则训练时会抛数据集不匹配异常。
2.2 YOLO 格式 txt 标签解析与归一化坐标
YOLO 格式的每一行代表一个目标,结构为class_id x_center y_center width height,后四个值都是相对于图片宽高的归一化数值。注意这里不是边界框的左上角坐标,很多新人在手工修改标签时会把x_center当成左上角x_min,导致训练出的权重在推理时框整体左移。
with open("labels/train/000001.txt", "r") as f: lines = f.readlines() img_w, img_h = 1280, 960 # 实际数据集中图片的宽高 for line in lines: cls_id, x_c, y_c, w, h = map(float, line.strip().split()) x_min = int((x_c - w / 2) * img_w) y_min = int((y_c - h / 2) * img_h) x_max = int((x_c + w / 2) * img_w) y_max = int((y_c + h / 2) * img_h) print(f"类别: {int(cls_id)}, 左上角: ({x_min}, {y_min}), 右下角: ({x_max}, {y_max})")这段代码把归一化坐标还原成像素坐标,方便和图片上的实际轮胎位置做肉眼对比。x_c - w / 2是从中心点反推左上角的核心逻辑,除以 2 这一半最容易漏掉。转换后发现框比轮胎大一圈或小一圈时,不要怀疑公式,先回看labelling标注时的框线贴边程度——标注习惯不同会导致同一种转换逻辑在视觉上有偏差。
2.3 VOC xml 标签到 YOLO txt 的完整转换脚本
Annotations里的 xml 是 Pascal VOC 风格,结构简单且标签类别名就是tire(摘要里已说明类别名为 tire,有些版本可能是car_tire,以你自己那份 xml 里的name节点为准)。训练或迁移时需要把 xml 转换成 YOLO txt,按同样的规律落到labels目录。
import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, out_dir, class_map=None): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) yolo_lines = [] for obj in root.iter("object"): cls_name = obj.find("name").text cls_id = 0 if class_map is None else class_map[cls_name] box = obj.find("bndbox") x_min = float(box.find("xmin").text) y_min = float(box.find("ymin").text) x_max = float(box.find("xmax").text) y_max = float(box.find("ymax").text) x_c = (x_min + x_max) / 2 / img_w y_c = (y_min + y_max) / 2 / img_h w = (x_max - x_min) / img_w h = (y_max - y_min) / img_h yolo_lines.append(f"{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}") txt_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(yolo_lines)) xml_to_yolo("Annotations/000001.xml", "labels/train")这个脚本中(x_min + x_max) / 2 / img_w分两步:先算两个端点之和的平均值得到中心点像素坐标,再除以图片宽度做归一化。面积计算时注意,VOC 的xmaxymax通常是从 1 开始计数,而 PASCAL VOC 坐标换算成 COCO 的像素坐标时会涉及减一还是不减一的问题,但 YOLO 在做归一化时差异在万分位上,可以忽略不计。实际转换完抽样三张图用mAP评估一次,比肉眼逐个检查更高效,也更能发现系统性偏移。
2.4 标签质量检查的一个关键指标
用脚本统计每一张图的标注框数量与面积分布,轮胎数据集很容易出现“轮毂内侧被标成背景”“两个轮胎被标成一个框”两种情况,这类样本会直接拉低 mAP50。一个通用做法是计算label_area_ratio(标注框面积占整图面积的比值),轮胎部件级的比值一般在 0.03 到 0.2 之间,若超过 0.4 大概率是误标成整车了。
import os def check_label_area(img_dir, label_dir): for img_name in os.listdir(img_dir): stem = os.path.splitext(img_name)[0] txt_path = os.path.join(label_dir, stem + ".txt") if not os.path.exists(txt_path): print(f"缺少标签: {stem}") continue total_ratio = 0.0 with open(txt_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: print(f"格式异常: {txt_path} -> {line}") continue _, _, _, w, h = map(float, parts) total_ratio += w * h print(f"{stem} 标注面积占比: {total_ratio:.3f}")补充一句,labels.cache文件在这套检查流程里只能说明 YOLO 曾经成功读过一次标签,不能把它当成标签正确的证据;真正要验证的是缓存重建的日志里没有WARNING: corrupted image这行字样。
3. 配置 YOLOv10 环境与创建 tire.yaml 训练文件
3.1 YOLOv10 官方仓库的安装与目录确认
实现训练流程前先把基础环境立住。YOLOv10 是 2024 年提出的方案,整体逻辑和 YOLOv8 高度一致,直接使用 Ultralytics 仓库代码可以跑通大部分训练任务,仓库内置了yolov10s.yaml、yolov10m.yaml、yolov10l.yaml等模型结构定义,以及对应的预训练权重。
git clone https://github.com/THU-MIG/yolov10.git cd yolov10 pip install -r requirements.txt pip install -e .pip install -e .把 yolov10 包以可编辑模式装进当前 Python 环境,这样你在任意目录下执行from ultralytics import YOLO都能导入。装完后先确认版本是否正常,import ultralytics; ultralytics.__version__能正常输出版本号即可开始下一步。若环境里本来就装过老版本ultralytics,建议先pip uninstall ultralytics再装,避免 YOLOv10 的检测头配置被旧版本覆盖——这个问题在带 torch 2.x 的既有环境里特别常见。
3.2 解析固有配置文件:yaml 的结构层次
网上搜 “yolov10 yaml 文件怎么创建” 的答案五花八门,本质上你要创建的是数据集配置文件,不是模型结构文件。模型结构文件在ultralytics/cfg/models/v10/下,里面的yolov10s.yaml描述了骨干网络、颈部结构和检测头,而数据集配置文件放在ultralytics/cfg/datasets/,两者职责不同。
# tire.yaml path: /path/to/your/tire-dataset # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 1 # 类别数量,只有轮胎一类 names: 0: tire # 类别名必须与训练数据中的 cls_id 对应这份配置里path可以写绝对路径,也可以写成相对根目录的路径,YOLOv10 会拼接path + train得到完整的图片目录。最常翻车的点在于names是从 0 开始的字典,如果第 0 类写成了car,训练出来的权重依然会输出轮胎框,但类别名在推理时显示为 car,混淆矩阵和 PR 曲线的类别语义也会跟着错位。写完配置后运行一次:
python -c "from ultralytics.data import build_yolo_dataset; build_yolo_dataset('tire.yaml', 'images/train', batch_size=8, mode='train')"这一步能在不启动训练的情况下提前暴露路径错误、标签缺失和图片损坏等问题,比直接训练到一半再报错要高效得多。构建失败时优先检查labels目录命名是否正确,YOLOv10 默认把标签放在labels/train和labels/val下,与images/train、images/val严格对应。
3.3 结构配置文件里与 tire 相关的三个关键参数
打开yolov10s.yaml,除了nc: 80改成nc: 1之外,还有几个参数直接影响轮胎这个小目标的召回。
scale: - 0.33 # depth_multiple,统一控制模型的深度倍数 - 0.50 # width_multiple,控制每层通道数 - 1.0 # max_channels,限制最大通道数不超过 1024 backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, Conv, [128, 3, 2]] ...depth_multiple值越大网络越深,对轮胎的纹理细节(如胎纹、轮毂边缘)提取越充分,但显存消耗同步上升。轮胎在车辆图像中往往只占 5% 到 15% 的像素面积,属于中小目标,如果数据集中远距离轮胎样本多,建议保持 depth=0.33 而把max_channels从 1024 调到 1280,相当于给高层特征图更多通道去表达小目标语义。另一个值得关注的是yolov10s.yaml里的max_det参数(推理阶段专用),默认 300 个检测框,轮胎场景一般不会超过 30 个目标,保持默认即可,不需要像工业零件密集检测那样调大到 600。
数据集配置文件与结构配置文件的完整关系是:tire.yaml指向数据源和类别语义,yolov10s.yaml定义网络容量与计算量,训练命令把两者传参给 Trainer。修改任何一处 yaml 后,删除labels.cache再重启训练才能让新配置完全生效。若你对 yaml 创建工具不熟,建议直接复制coco128.yaml改名后逐行替换路径与类别名,比从空白文件新建少踩很多格式坑。
4. 训练后的权重推理与评估指标解码
4.1 使用 CLI 快速推理测试图片
资源包里的权重是训练好的tire.pt(也可能命名为best.pt,优先找 val 指标最高的那个),拿到图和权重后先用命令行跑一遍,确认推理链路无损。
yolo detect predict model=tire.pt source=tire_test.jpg conf=0.25 iou=0.7 save=True project=runs/detect name=tire_infer这里conf=0.25表示置信度阈值低于 0.25 的预测框被过滤,iou=0.7是 NMS(非极大值抑制)的 IoU 阈值。YOLOv10 的核心改动之一是去掉了 NMS 依赖,模型直接输出一组稀疏的候选框,但在推理脚本里依然保留了 NMS 后处理以保证老代码兼容性,所以iou参数仍然可以调节——车轮相互遮挡的场景把 IoU 降到 0.5 能保留更多被抑制但实际存在的轮胎框。save=True会把标注了预测框的图片输出到runs/detect/tire_infer目录,便于直接观察错检和漏检。
4.2 脚本方式推理:拿到张量结果
CLI 适合快速验证,但如果你想在二次开发的检测系统里集成这个权重,必须理解模型输出张量的结构。
from ultralytics import YOLO model = YOLO("tire.pt") results = model.predict(source="tire_test.jpg", conf=0.25, verbose=False) for r in results: boxes = r.boxes for box in boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() conf = float(box.conf[0]) cls_id = int(box.cls[0]) print(f"轮胎框: ({x1:.1f}, {y1:.1f}) -> ({x2:.1f}, {y2:.1f}), 置信度: {conf:.3f}") # 可视化保存 r.save(filename="infer_result.jpg")r.boxes.xyxy返回的是像素坐标,不是归一化坐标,直接用于画矩形框。box.conf是每个框的置信度分数,多个轮胎时排序后取前 K 个即可以实现“只保留置信度最高的轮胎”的业务逻辑——比如停车场入口只关心最近的一个轮胎。cls_id在这种单类别模型里基本都是 0,但不要硬编码,逻辑代码里仍应以cls_id作为类别分支依据,方便以后升级为多类别(tire, wheel hub, brake disc)。
4.3 理解 PR 曲线和 loss 曲线对应的数据字段
权重包里带有events.out.tfevents.1719203393.USER-20231125JB.7292.0,这个文件是 TensorBoard 的日志格式,里面记录了训练过程的train/loss、val/box_loss、metrics/mAP50()等标量数据,以及验证阶段计算得到的 PR 曲线、F1 曲线和混淆矩阵。不要直接双击打开,要用解析脚本读。
from tensorboard.backend.event_processing.event_accumulator import EventAccumulator acc = EventAccumulator("events.out.tfevents.1719203393.USER-20231125JB.7292.0") acc.Reload() tags = acc.Tags()["scalars"] print("可用指标:", tags) mAP50 = acc.Scalars("metrics/mAP50()") for step_item in mAP50[-3:]: print(f"step={step_item.step}, mAP50={step_item.value:.4f}") train_loss = acc.Scalars("train/box_loss") print(f"最终训练损失: {train_loss[-1].value:.4f}")mAP50()是 IoU 阈值为 0.5 时的平均精度均值,对轮胎这种刚体目标来说,mAP50 在 0.9 以上说明模型整体可靠;而mAP50-95()在不同 IoU 阈值下的平均值更能反映定位精度,如果 mAP50 高但 mAP50-95 偏低,说明预测框虽然位置对但边缘贴合不紧,此时优先考虑数据集中增加遮挡样本,而不是加训练轮数。train/box_loss和val/box_loss两条曲线之间的差距如果越拉越大,就是过拟合信号,需要提前停止或在配置里增加weight_decay。
表格整理这几项的核心用途:
| 指标 | 数据来源 | 轮胎场景解读 | 优调手段 |
|---|---|---|---|
| mAP50 | TensorBoardmetrics/mAP50() | 目标检测的召回与精度综合表现 | 调整 conf 阈值、增加正样本 |
| mAP50-95 | TensorBoardmetrics/mAP50-95() | 定位框贴合度,越高越好 | 优化标注边缘、提高输入分辨率 |
| val/box_loss | TensorBoardval/box_loss | 验证集的边框回归误差 | 调节学习率、增加数据增强 |
| P(Precision) | PR 曲线顶点 | 误检少 | 提高 conf 阈值、增加难负样本 |
| R(Recall) | PR 曲线右端 | 漏检少 | 降低 conf 阈值、增加目标样本 |
CLI 推理时修改conf=0.5会在 Precision 和 Recall 之间来回摆,务必要配合 PR 曲线图选定业务阈值。PR 曲线里靠近右上角的点对应的置信度就是理论和实测比较匹配的平衡点,我一般先取这个值做落地部署,再根据线上误报率上下浮动 0.05。
5. 基于已有权重继续微调与 TensorBoard 可视化技巧
5.1 用预训练权重做初始化而非重新训练
很多人在新数据集上习惯weights=yolov10s.pt从头训,其实更好的选择是直接用本资源包里的tire.pt做初始化,特别是当你的新数据只有几百张轮胎图时。预训练权重已经学到了轮胎的边缘纹理和轮毂结构特征,它在新数据集上的收敛速度比 COCO 预训练权重要快得多,通常只需一半的 epoch 就能到达相同 mAP。
yolo train data=my_tire_v2.yaml model=tire.pt epochs=100 imgsz=640 batch=16这里model=tire.pt既是结构定义也是权重初始化来源;如果想让最后一层检测头重新初始化以适配新的类别数,可以在配置里加上freeze=10冻结前 10 层骨干网络,只训练特征融合和检测头部分。冻结层数的选择有一个快捷经验:先跑 10 个 epoch,记录每层权重更新量的梯度范数,把梯度范数接近零的层全部冻结,这样显存占用更低且训练稳定性更可控。
5.2 TensorBoard 实时观察训练过程
启动训练后如果需要远程查看训练曲线,可以在任意一台机器上指向日志目录启动 TensorBoard 服务。
tensorboard --logdir=runs/detect --port=6006 --host=0.0.0.0浏览器打开http://<服务器IP>:6006,在 SCALARS 面板里可以按步骤勾选metrics/mAP50()与metrics/mAP50-95()两条曲线,这个对比是评估模型是否有持续优化空间的性价比最高的方式。如果 mAP50 在提升而 mAP50-95 停滞甚至下降,说明模型过拟合于粗定位,此时在ultralytics/cfg/default.yaml中加大hsv_h、hsv_s等颜色增强幅度可以改善边缘回归的多样性,而对轮胎这种纹理相对单一的目标,degrees=15适度旋转也能帮助泛化。
5.3 推理提速的细节配置
最后一个实操技巧是推理阶段的批量预处理。轮胎检测在车辆入口、安检通道等场景中往往需要接入 RTSP 视频流,此时不要逐帧调用model.predict(),而是用批量推理:
from ultralytics import YOLO import cv2 model = YOLO("tire.pt") cap = cv2.VideoCapture("rtsp://your_camera_stream") batch_frames = [] while True: ret, frame = cap.read() if not ret or len(batch_frames) == 8: if batch_frames: results = model.predict(batch_frames, conf=0.3, imgsz=640) for r in results: print(len(r.boxes)) batch_frames = [] if not ret: break batch_frames.append(frame)model.predict(batch_frames)内部会自动做一批推理,相比 for 循环逐帧调用,省去了多次前处理的重复计算,吞吐量能提升 40% 以上。注意imgsz=640是推理输入尺寸,实际图片大于这个分辨率时 YOLOv10 会等比缩放后填充灰边,padding 部分不会产生误检。若视频流卡顿,优先把imgsz降到 480 而不是调低conf,分辨率下降导致的检测率损失小于阈值下降带来的误报增加。
另外一点我在多个轮胎项目里验证过:权重包里如果有yolov10tire.pt与yolov10tire-s.pt两个文件,优先用参数量更大的那个做离线检测,参数小的留给嵌入式设备;把权重用torch.jit.trace导出成 TorchScript 后,在 Jetson 这类边缘设备上的推理延迟一般能压到 20ms 以内,转换前在 Python 环境里复现一次 PR 曲线,确保转换过程没有破坏检测头的输出分布。
本文还有配套的精品资源,点击获取