简介:这份资源面向计算机视觉学习者与道路安全检测方向的开发者,提供一套基于YOLOv8实现路面坑洼识别的完整Python项目,涵盖从数据准备、模型训练到推理评估的全流程,适合具备一定深度学习基础、希望动手实践目标检测的读者参考。压缩包共10个文件,约170.66MB,包含4个py脚本(训练、测试、验证与预测)、2个pt权重文件、1个requirements依赖清单、1个mp4演示视频、1个md项目说明及1张png效果图,结构紧凑、开箱即用。目前已有423人学习下载。项目说明文档梳理了数据集标注、参数配置与结果分析思路,配套权重可直接用于坑洼检测推理,演示视频直观呈现检测效果,依赖清单便于快速复现环境。读者可借此理解YOLOv8在真实道路场景中的落地方式,并迁移至交通标志识别、路面破损评估等相似任务。
1. 路面坑洼检测为什么值得单独做一个 YOLOv8 项目
城市道路巡检、自动驾驶感知、市政养护评估,这三类场景里「路面坑洼检测」几乎是绕不开的一环。传统做法靠人工巡检或传统图像处理,前者效率低,后者对光照、阴影、水渍极其敏感,换个路段就翻车。YOLOv8 把目标检测做成了单阶段回归问题,一次前向就能同时输出类别和边界框,在坑洼这种「形状不规则、尺度跨度大、背景干扰强」的任务上,比两阶段方案更适合落地到边缘设备。
这份资源是一套完整的 Python 工程:train.py、val.py、predict.py、test.py四个脚本覆盖训练到推理全链路,附带best.pt权重、y8best.pt备份权重、tested.mp4实测视频、requirements.txt依赖清单和README.md项目说明。拿到手不用从零搭环境,直接能跑通「加载模型 → 读图/读视频 → 输出带框结果」这条主线。适合想快速验证坑洼检测可行性的人,也适合拿它当模板改造成裂缝、井盖、标线等其他路面病害检测。下面按「资源结构 → 训练与推理 → 避坑 → 进阶」的顺序拆开讲。
2. 工程结构与 YOLOv8 训练链路拆解
2.1 目录里每个文件到底干什么
先把压缩包解开,按功能把文件分成四类,这样后面改代码时不会找错地方。
| 文件/目录 | 类型 | 作用 |
|---|---|---|
train.py | 脚本 | 训练入口,读取数据集配置并启动 YOLOv8 训练 |
val.py | 脚本 | 在验证集上评估 mAP、precision、recall |
predict.py | 脚本 | 单图/视频推理,输出带检测框的结果 |
test.py | 脚本 | 快速自测,通常用于确认环境和权重可用 |
best.pt | 权重 | 训练收敛后的最优模型权重 |
y8best.pt | 权重 | 备份或另一轮训练的权重,命名区分 |
tested.mp4 | 视频 | 实测推理输出,用来直观判断效果 |
requirements.txt | 依赖 | Python 库及版本约束 |
README.md | 文档 | 数据集说明、训练参数、使用步骤 |
best.pt和y8best.pt同时存在,说明作者至少跑过两轮训练或做过权重对比。实际使用时先确认哪个权重对应的 mAP 更高,别默认best.pt一定最好——命名是人为的,不是框架保证的。
2.2 环境安装与依赖确认
YOLOv8 依赖 Ultralytics 框架,底层是 PyTorch。环境装不对,后面全是玄学报错。建议用 Python 3.8~3.10,太新的版本某些 CUDA 轮子还没跟上。
# 创建独立环境,避免和系统 Python 冲突 python -m venv pothole_env source pothole_env/bin/activate # Windows 用 pothole_env\Scripts\activate # 先装 PyTorch,按自己的 CUDA 版本选对应命令 # 以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再装项目依赖 pip install -r requirements.txt # 单独确认 ultralytics 版本,训练脚本对版本敏感 pip show ultralyticsrequirements.txt里通常包含ultralytics、opencv-python、numpy、pillow、matplotlib。如果pip install卡在opencv-python,多半是网络问题,换国内镜像源即可。装完后跑一句python -c "from ultralytics import YOLO; print('ok')",能打印ok说明框架通了。
提示:不要用
pip install ultralytics覆盖requirements.txt里锁定的版本,版本漂移会导致train.py里的参数名对不上。
2.3 训练脚本的关键参数怎么改
train.py是整条链路的起点。YOLOv8 的训练参数集中在model.train()里,下面这段是常见写法,我按坑洼检测场景补了注释。
from ultralytics import YOLO # 加载预训练权重,从 COCO 迁移比从零训练收敛快得多 model = YOLO("yolov8n.pt") # n/s/m/l/x 按算力选,边缘设备优先 n 或 s model.train( data="data/pothole.yaml", # 数据集配置文件,指向 train/val 路径和类别名 epochs=100, # 坑洼样本少时 100 起步,观察 mAP 是否还在涨 imgsz=640, # 输入分辨率,坑洼小目标多可提到 800 batch=16, # 显存不够就降到 8 或 4 lr0=0.01, # 初始学习率,迁移学习常用 0.01 patience=20, # 20 轮 mAP 不涨就早停,省时间 device=0, # 0 表示第一块 GPU,CPU 写 "cpu" project="runs/train", # 输出目录 name="pothole_v8n" # 本次实验名,方便对比 )data/pothole.yaml是数据集描述文件,格式如下,路径写绝对路径最稳,相对路径容易因为工作目录不同而找不到。
path: /home/user/pothole_dataset train: images/train val: images/val nc: 1 names: ["pothole"]nc: 1表示只有坑洼一个类别。如果后面要加裂缝、井盖,改nc和names即可,但标注文件里的类别索引也要同步改,否则训练时标签越界会直接报错。imgsz和batch是最容易翻车的两个参数:分辨率翻倍显存大约翻四倍,batch 调大要先看显存余量。
2.4 验证与推理:从 mAP 到可视化结果
训练完先跑val.py,别急着看视频。验证集上的 mAP50、mAP50-95 才是判断模型能不能用的硬指标。
# 在验证集上评估,指定权重和数据配置 python val.py --weights best.pt --data data/pothole.yaml --img 640 --batch 16输出里重点看三行:mAP50反映宽松匹配下的检出能力,mAP50-95反映框的贴合精度,precision/recall反映误检和漏检的平衡。坑洼检测里 recall 通常比 precision 更重要——漏掉一个坑可能意味着车辆受损,多检一个顶多人工复核。
推理用predict.py,单图和视频都支持:
# 单张图片推理,保存带框结果 python predict.py --weights best.pt --source test.jpg --conf 0.25 --save # 视频推理,输出到 runs/detect 目录 python predict.py --weights best.pt --source tested.mp4 --conf 0.25 --save--conf 0.25是置信度阈值,低于它的框会被丢弃。坑洼边缘模糊时,阈值设太高会漏检,设太低会满屏框。我的习惯是先跑 0.25 看整体,再针对具体路段微调到 0.3~0.4。--save会把可视化结果写到runs/detect/下,tested.mp4就是这类输出的样例。
3. 数据集准备与标注格式的实操细节
3.1 坑洼数据从哪来、怎么标
YOLOv8 要的是 YOLO 格式标注:每张图对应一个.txt,每行类别 中心x 中心y 宽 高,坐标全部归一化到 0~1。很多人拿 LabelImg 标完导出 VOC 的 XML,直接丢给 YOLO 训练,结果标签读不进去——格式不对,框架不会帮你转。
# VOC XML 转 YOLO txt 的核心逻辑 import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, img_w, img_h, out_path): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): cls_id = 0 # 单类别坑洼固定为 0 bbox = obj.find("bndbox") x1 = float(bbox.find("xmin").text) y1 = float(bbox.find("ymin").text) x2 = float(bbox.find("xmax").text) y2 = float(bbox.find("ymax").text) # 归一化并转成中心点+宽高 cx = (x1 + x2) / 2 / img_w cy = (y1 + y2) / 2 / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") Path(out_path).write_text("\n".join(lines))转换时最容易错的是img_w/img_h取错——必须用原图尺寸,不能用缩放后的。另外坐标要裁剪到 0~1,标注框超出图像边界时归一化会得到负数或大于 1 的值,训练时直接报invalid label。
3.2 数据划分与类别平衡
坑洼数据集普遍存在两个问题:负样本(无坑洼路面)太多,正样本里小坑洼占比高。划分时按 8:1:1 分 train/val/test,且要保证每个子集里都有不同光照、不同路面的样本,不能随机切完训练集全是晴天、验证集全是雨天。
# 按文件名列表划分,避免随机种子导致每次结果不同 python split_dataset.py --images data/all_images --ratio 0.8 0.1 0.1 --seed 42如果正负样本比例超过 1:10,训练时可以考虑在pothole.yaml里加fraction参数只取部分负样本,或者用 YOLOv8 自带的copy_paste、mosaic增强。但增强不是万能药,坑洼的形状被过度拼接后会失真,mAP 反而下降,这个后面避坑章节细说。
3.3 用预训练权重做迁移的取舍
yolov8n.pt是在 COCO 上训的,COCO 里没有坑洼类别,但底层卷积特征(边缘、纹理、明暗对比)是通用的。从预训练权重起步,通常 30~50 轮就能到可用水平;从零训练,100 轮可能还在震荡。
选 n 还是 s/m,取决于部署目标。如果只是本地验证,yolov8s.pt精度更稳;如果要往 RK3588 这类边缘板子上部署,yolov8n参数量小、推理快,但小坑洼召回会掉。我的做法是先用 n 跑通全流程,确认数据标注没问题,再换 s 对比 mAP,最后按部署算力定版本。
4. 训练与推理中的避坑排查清单
4.1 现象:训练 loss 正常下降但 mAP 一直是 0
原因:标注文件路径或类别索引不对。YOLOv8 读不到标签时不会报错,而是把该图当负样本训练,loss 照样降,但模型学不到任何目标。
解决:跑一次python -c "from ultralytics.data.utils import check_det_dataset; check_det_dataset('data/pothole.yaml')",它会打印数据集统计。如果train的 instances 为 0,就是标签没被读到。检查.txt是否和图片同名同目录,以及names里的类别数是否和标注里的最大类别索引匹配。
4.2 现象:显存溢出,报 CUDA out of memory
原因:imgsz或batch超过显卡容量。YOLOv8 训练时显存占用和imgsz² × batch近似成正比。
解决:先把batch减半,还不行再降imgsz。GTX1660Ti 6G 显存跑imgsz=640, batch=16基本到顶,建议batch=8。也可以用--amp混合精度,显存能省三成左右,但个别老卡上会出现 loss NaN,开了要盯着前几轮。
4.3 现象:推理时框大量重叠、同一个坑被检多次
原因:NMS(非极大值抑制)的 IoU 阈值设得过高,重叠框没被合并。
解决:推理时加--iou 0.5或更低。YOLOv8 默认 IoU 是 0.7,对坑洼这种大而模糊的目标偏松。但 IoU 调太低会把相邻的两个坑合并成一个,需要按实际场景试。密集坑洼路段建议 0.5~0.6。
4.4 现象:换一段路视频,检测效果断崖式下降
原因:训练集过拟合到特定路面材质或光照。模型学到的是「这段路的坑洼长这样」,不是「坑洼的通用特征」。
解决:补充不同路段、不同天气的样本重新训练,或者在推理前做简单的直方图均衡化。更彻底的做法是加mosaic=0关掉拼接增强,让模型看到更完整的单图上下文,减少对局部纹理的依赖。
4.5 现象:predict.py报找不到best.pt
原因:权重路径写的是相对路径,而脚本运行目录和权重目录不一致。
解决:统一用绝对路径,或者在脚本里用Path(__file__).parent / "best.pt"定位。这类问题在 Windows 和 Linux 之间来回切的时候特别常见,血泪经验是路径一律写绝对路径,别偷懒。
5. 从能跑到好用:阈值调优与边缘部署的进阶技巧
模型跑通只是起点,真正决定这套东西能不能上巡检车或边缘盒子,是后处理阈值和推理后端。先说置信度阈值:--conf 0.25是通用起点,但坑洼检测里我一般会做一次分路段标定。拿一段已知坑洼数量的视频,把conf从 0.1 到 0.6 每 0.05 跑一遍,画一条 recall-conf 曲线,选 recall 开始明显下降前的那个点。多数情况下这个点在 0.3 附近,比默认值高,能压掉一批阴影误检。
NMS 的 IoU 阈值同理,但它和conf是耦合的。两个参数一起调,别单独动一个。我习惯固定conf=0.3,只扫iou,因为conf对召回的影响更直观,先定它再定iou更省事。
如果要往 RK3588 这类板子部署,PyTorch 权重不能直接跑,得先导出 ONNX 再转 RKNN。导出这一步有个坑:YOLOv8 的动态输出在转 ONNX 时如果opset版本选低了,后处理节点会丢,转出来的模型输出维度对不上。
# 导出 ONNX,opset 至少 12,imgsz 和训练时保持一致 yolo export model=best.pt format=onnx opset=12 imgsz=640 simplify=Truesimplify=True会调用 onnx-simplifier 合并冗余节点,转 RKNN 时成功率更高。导出后先用onnxruntime在 PC 上验证一遍输出和 PyTorch 一致,再上板子转 RKNN,否则板子上报错你分不清是模型问题还是转换问题。
| 参数 | 通用起点 | 坑洼场景建议 | 影响 |
|---|---|---|---|
| conf | 0.25 | 0.30~0.35 | 越高误检越少,漏检越多 |
| iou | 0.70 | 0.50~0.60 | 越低重叠框合并越狠 |
| imgsz | 640 | 640~800 | 越大越小目标越友好,越吃显存 |
| batch | 16 | 8(6G 显存) | 越大训练越稳,越吃显存 |
最后说一个验证习惯:每次改完参数,别只看tested.mp4这种「作者挑过的样例」,自己拿手机拍一段新路面的视频跑一遍。我吃过亏——在样例视频上 mAP 漂亮得很,换到自己小区门口那段烂路,模型把井盖和坑洼混在一起检。从那以后我每次调完阈值,都强制用一段没参与训练的新视频走一遍,确认没有系统性误检才收工。希望这套流程能帮你少走点弯路。
本文还有配套的精品资源,点击获取