基于YOLOv8的路面坑洼检测:从数据集标注到边缘部署全流程
发布时间:2026/10/11 6:30:35 锦皓数字建站

简介这份资源面向计算机视觉学习者与道路安全检测方向的开发者提供一套基于YOLOv8实现路面坑洼识别的完整Python项目适合具备一定深度学习基础、希望动手实践目标检测全流程的中高级用户。压缩包共10个文件约170.66MB包含4个py脚本、2个pt权重文件、1个txt依赖清单、1个mp4演示视频、1个md说明文档和1个png效果图覆盖数据预处理、模型训练、推理与后处理等环节。项目围绕坑洼特征标注、YOLOv8训练、特征提取、非极大值抑制及mAP评估展开配套说明文档梳理了数据集信息、参数设置与结果分析训练好的权重可直接用于检测。目前已有423人学习下载读者可借此理解从数据准备到模型部署的完整链路并迁移至交通标志识别、路面破损评估等相似任务。1. 路面坑洼检测为什么值得用 YOLOv8 重做一遍市政巡检车每天跑几十公里回传的影像动辄上万帧靠人眼在监控室里逐帧找坑洼漏检率高得离谱而且同一个坑在连续帧里被反复标记统计口径完全乱套。这正是「基于 YOLOv8 实现的路面坑洼检测方法系统」要解决的问题把坑洼当成目标检测任务用 YOLOv8 在自建路面数据集上训练输出带类别和置信度的边界框再叠加去重和面积估算形成可落地的巡检报告。它适合三类人做智慧交通/市政方向的学生和工程师、手里有行车记录仪或巡检影像想跑通检测的开发者、以及需要把模型部署到边缘盒子比如 RK3588 这类平台的落地团队。整套东西的核心不是模型多新而是数据标注规范、训练参数和推理后处理这三件事能不能对齐真实路况。2. 从数据集到 YOLOv8 训练一条能跑通的链路2.1 路面坑洼数据集怎么标才不返工坑洼检测翻车八成翻在标注上。路面影像里坑洼边界模糊水渍、修补痕迹、井盖阴影都容易被误标成坑洼。我一般会先定一份标注规范再动手只标有明显凹陷或破损、且面积大于图像短边 1% 的区域边界框贴紧破损外沿不把周围裂纹圈进去对反光水面下的疑似坑洼标记为ignore区域而不是硬标成负样本。类别上建议先只保留两类——pothole坑洼和patch修补块修补块和坑洼在视觉上高度相似分开标能显著降低误检。标注工具用 LabelImg 或 X-AnyLabeling 都行导出 YOLO 格式。YOLO 格式每行是class_id x_center y_center width height全部归一化到 0~1。这里有个血泪经验不同标注人导出的坐标精度不一致有人保留 6 位小数有人保留 2 位合并时会出现框偏移。统一用脚本重写一遍保留 6 位小数。import os def normalize_label_file(label_path): 把 YOLO 标签统一成 6 位小数避免多人标注精度不一致 with open(label_path, r) as f: lines f.readlines() out [] for line in lines: parts line.strip().split() if len(parts) ! 5: continue # 跳过空行或异常行 cls_id parts[0] coords [f{float(v):.6f} for v in parts[1:]] out.append( .join([cls_id] coords)) with open(label_path, w) as f: f.write(\n.join(out)) # 批量处理 labels 目录 for root, _, files in os.walk(datasets/labels): for name in files: if name.endswith(.txt): normalize_label_file(os.path.join(root, name))这段脚本做的是格式归一化:.6f控制小数位len(parts) ! 5用来过滤掉标注工具偶尔写出的空行。跑完记得抽查几个文件确认类别 id 没有越界。2.2 目录结构与 data.yaml 的四个必填项YOLOv8 对目录结构有约定常见做法是 images 和 labels 平行放置train/val 分开datasets/ images/ train/ val/ labels/ train/ val/data.yaml里四个字段必须写对path指向数据集根目录train/val写相对路径nc是类别数names是类别名列表。很多人path写成绝对路径后换机器就报找不到文件建议用相对路径并在训练命令里cd到项目根目录。path: ./datasets train: images/train val: images/val nc: 2 names: [pothole, patch]提示nc和names长度必须一致否则训练启动时会直接抛索引错误这个报错信息很不直观容易卡半天。2.3 用命令行跑通第一次训练环境配置是新手最容易卡住的地方。Python 建议 3.8~3.10先装 PyTorch按显卡 CUDA 版本选再装 ultralytics。GTX1660Ti 这类 6G 显存卡跑 YOLOv8n 完全够用batch 设 8~16。# 创建环境并安装依赖 conda create -n pothole python3.10 -y conda activate pothole pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python numpy # 开始训练 yolo detect train \ modelyolov8n.pt \ data./data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/pothole \ nameexp1参数说明modelyolov8n.pt用官方预训练权重做迁移学习小数据集上比从头训收敛快得多imgsz640是输入分辨率坑洼目标偏小可以提到 960但显存和速度会明显变差lr00.01是初始学习率数据量小于 2000 张时建议降到 0.005patience20表示 20 轮验证指标不提升就早停省时间。训练完在runs/pothole/exp1/weights/下拿到best.pt和last.pt部署用best.pt。2.4 损失曲线怎么看才算正常训练日志里box_loss、cls_loss、dfl_loss三条曲线是判断训练是否健康的黑匣子。正常情况是前 10 轮快速下降之后缓慢收敛并伴随小幅震荡。如果cls_loss一直不降多半是类别不平衡或标注里类别 id 写错如果box_loss下降但mAP不涨通常是验证集和训练集分布差异太大比如训练集全是晴天、验证集全是雨天。用 ultralytics 自带的results.csv画曲线最省事import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/pothole/exp1/results.csv) df.columns df.columns.str.strip() # 列名常带空格必须清理 plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.plot(df[epoch], df[train/cls_loss], labelcls_loss) plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.legend() plt.xlabel(epoch) plt.savefig(loss_curve.png, dpi150)df.columns.str.strip()这行别省ultralytics 导出的列名前后有空格直接按名字取会 KeyError。3. 推理、后处理与坑洼面积估算3.1 单张与批量推理的最小代码训练完只是拿到权重真正出结果靠推理。单张图推理用model.predict批量处理视频或图片目录用循环加stream更省内存。from ultralytics import YOLO import cv2 model YOLO(runs/pothole/exp1/weights/best.pt) # 单张推理 results model.predict(test.jpg, conf0.35, iou0.5, imgsz640) for r in results: for box in r.boxes: cls_id int(box.cls) conf float(box.conf) x1, y1, x2, y2 map(int, box.xyxy[0]) label f{model.names[cls_id]} {conf:.2f} cv2.rectangle(r.orig_img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(r.orig_img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(result.jpg, results[0].orig_img)conf0.35是置信度阈值路面场景建议 0.3~0.4太低会把水渍误检成坑洼iou0.5是 NMS 的 IoU 阈值坑洼密集时调到 0.6 能减少相邻框被误删。box.xyxy[0]拿的是左上右下坐标画框前转 int否则 OpenCV 会报类型错误。3.2 连续帧去重同一个坑别数三遍巡检视频里同一个坑会在连续几十帧里被检出直接统计数量会严重虚高。常见做法是引入简单的跟踪或基于位置的去重记录已确认坑洼的中心坐标新检测框中心落在已有坑洼半径内就合并只保留置信度最高的那次。import math confirmed [] # 存 (cx, cy, conf) def dedup(cx, cy, conf, radius80): 中心距离小于 radius 视为同一个坑保留高置信度 for i, (px, py, pc) in enumerate(confirmed): if math.hypot(cx - px, cy - py) radius: if conf pc: confirmed[i] (cx, cy, conf) return False confirmed.append((cx, cy, conf)) return Trueradius80是像素阈值跟分辨率和车速强相关分辨率 1080p、车速 30km/h 时80 像素大约对应 1.5 米基本能覆盖同一个坑在相邻帧的位移。车速快或分辨率低就调大反之调小。这个方案简单但有效比上完整跟踪算法省事得多。3.3 从像素框估算真实坑洼面积巡检报告里光有框没用得给出面积。核心是标定在拍摄平面上放一个已知尺寸的参照物比如 30cm 的标定板算出每像素对应的实际长度scale米/像素再用框的宽高乘 scale 得到近似面积。参数含义典型取值scale_x水平方向米/像素由标定板宽度除以像素宽得到scale_y垂直方向米/像素由标定板高度除以像素高得到area坑洼近似面积框宽×scale_x × 框高×scale_y注意相机有俯角时垂直方向存在透视畸变scale_y 会随位置变化粗略估算可以接受要精确就得做透视校正这是另一个话题。4. 部署到边缘设备与踩坑排查4.1 导出 ONNX 与 RK3588 部署路径模型要在边缘盒子上跑先导出 ONNX再用 RKNN 工具链转成 RK3588 能吃的格式。导出时imgsz必须和训练一致opset建议 12。# 导出 ONNX yolo export modelruns/pothole/exp1/weights/best.pt formatonnx opset12 imgsz640 # 在 RKNN 工具链环境里转换示意 python convert_rknn.py --onnx best.onnx --output best.rknn --target rk3588转换脚本里要指定mean_values和std_values必须和训练时的归一化一致YOLOv8 默认 0~1即 mean0、std255否则精度会掉一大截。RK3588 上推理用 RKNN Runtime 的 Python 或 C APINPU 跑 YOLOv8n 单帧大概几十毫秒具体数字跟量化方式有关INT8 量化后速度提升明显但小目标精度会降建议先用 FP16 验证精度再决定是否量化。4.2 坑洼检测常见问题排查现象一训练 loss 正常但推理全是误检。原因通常是验证集和实际场景光照差异大模型过拟合到训练集的光照。解决在数据增强里加hsv_h、hsv_v扰动并补一批阴天、逆光、夜间补光的数据。现象二小坑洼完全检不出。原因是 640 分辨率下小目标特征被下采样丢掉了。解决把imgsz提到 960 或 1280或者在数据里对小目标做复制粘贴增强提高小目标占比。现象三同一个坑在视频里被反复计数。这是没做去重参考 3.2 的中心距离合并或者引入 ByteTrack 做跨帧关联。现象四ONNX 转 RKNN 后精度暴跌。多半是归一化参数没对齐或者量化校准集和真实分布差太远。解决核对 mean/std量化校准集从验证集里随机抽 200~500 张覆盖各种光照。现象五训练时显存爆了。GTX1660Ti 只有 6Gbatch16加imgsz960很容易 OOM。解决降 batch 到 8或者开ampTrue混合精度再不行就降 imgsz。5. 把 mAP 从 0.6 推到 0.8 的几个实操技巧模型能跑通只是及格线真正决定这套系统能不能交付的是 mAP 和误检率。我踩过的坑里提升最明显的往往不是换更大的模型而是数据层面的动作。第一招是难例挖掘先用初版模型跑一遍验证集和实际巡检视频把漏检和误检的帧挑出来重新标注加进训练集通常一轮就能涨 5~10 个点。第二招是类别平衡坑洼和修补块数量差太多时对少样本类做过采样或者在 loss 里给类别权重YOLOv8 本身不直接暴露类别权重参数但可以通过复制样本文件实现等效过采样。第三招是锚框和输入尺寸的匹配。YOLOv8 是无锚框的但输入尺寸对小目标影响极大。我做过对比同一份数据imgsz640时 mAP50 约 0.62提到imgsz960后到 0.71代价是单帧推理从 12ms 涨到 28msGTX1660Ti。如果部署端算力有限可以训练用 960、推理用 640精度损失大概 2~3 个点但速度翻倍这个取舍在边缘设备上很常见。第四招是 TTA测试时增强推理时对图像做水平翻转和不同尺度缩放把多次预测做 NMS 融合。ultralytics 推理时加augmentTrue就能开启mAP 通常能再涨 1~3 个点但推理耗时翻几倍只适合离线出报告的场景实时巡检别开。验证方法上别只看 mAP 一个数。我习惯同时看三个指标mAP50 看整体、recall 看漏检、precision 看误检。市政场景里漏检比误检更不可接受所以我会把conf阈值调低到 0.25 保 recall再用后处理规则过滤明显误检比如框面积过小、长宽比异常的。最后留一句我自己的习惯每次改完数据或参数一定固定用同一批 200 张的「回归测试集」跑一遍对比前后指标不然改了什么、涨了还是跌了全靠玄学。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。