☰
YOLOv8路面缺陷检测实战:数据集构建、模型训练与部署全流程
2026/10/19 4:11:30 网站建设 项目流程

路面缺陷检测这个方向,这几年在公路养护、市政巡检里需求量一直很大。最早我用传统图像处理做过裂缝识别,什么Canny边缘检测、灰度阈值分割、形态学处理,实验室数据集上跑得还不错,一到真实路况就崩——光照一偏、影子一压、路面纹理一乱,检测结果根本没法看。后来切到深度学习,换过几个检测模型,最后稳定用下来的是YOLOv8。这篇文章把我从数据集构建到模型训练、再到部署推理的完整过程整理出来,重点聊聊训练道路缺陷数据集时那些容易踩的坑,以及YOLOv8针对性调优的思路,想少走弯路的朋友可以照着参考。

整套系统解决的核心问题很简单:把巡路车上或者手持设备拍到的路面图像输入模型,实时或者近实时地输出裂缝、坑槽、修补区域的位置和类别。养护单位拿它做路段体检、病害统计、维修决策的辅助依据,比纯靠人工肉眼扫图效率高太多了。适合三种读者参考:刚接触目标检测、想拿真实项目练手的学生;已经用YOLOv8做过通用检测、但在细长线状目标或者小目标场景上效果不理想的人;还有市政或养护公司里负责技术选型的工程师。

1. 为什么我最终选了YOLOv8做道路缺陷检测

1.1 传统视觉方案在路面裂缝上的天花板

先说结论:裂缝检测用传统图像处理不是不能做,但它的适用条件非常苛刻。经典的思路是先做灰度化,然后用Canny提取边缘,再用形态学闭运算把断裂的边缘连接起来,最后用连通域面积过滤噪声,剩下的大块区域当成裂缝。这套流程在光照均匀、路面干净、裂缝明显的图里效果尚可,可真实巡检车拍到的是什么?上午十点的硬阴影、雨后深色的水渍、路面本身的沥青颗粒纹理、车辆反复碾压产生的黑亮车辙带,这些东西的灰度特征和裂缝高度重叠,阈值怎么调都会误检。

另一个问题是裂缝的形态跨度太大。横向裂缝、纵向裂缝、网状裂缝、块状裂缝,宽度从一两毫米到几厘米,连续性也千差万别。传统方法里"边缘闭合"这个假设在网状裂缝上就站不住脚——龟裂区域的边缘是碎成一段一段的,用形态学强行连接会把大量非裂缝区域也连进来。所以传统方案顶多做个粗筛,真正到了自动化统计阶段,还是得靠深度学习来做语义级别的区分。

1.2 YOLOv8相比其他版本的核心改动

YOLOv8是Ultralytics在2023年发布的版本,架构上和YOLOv5/7比有几处关键变化。首先是backbone里的C2f模块替代了C3模块,简单说就是让梯度流动的路径更丰富,特征复用更充分,这对于裂缝这种在整张图里占比极小的目标是有意义的——小目标本来就依赖浅层和高层的特征融合,C2f在这方面比之前的C3表现更稳。其次是检测头换成了Anchor-Free结构,不再需要预设一堆anchor框去匹配目标,每个位置直接预测到目标四边的距离,这有个直接好处:不再需要为裂缝这种"长宽比极其极端"的目标专门调anchor参数。YOLOv5时代我经常要针对细长裂缝设置细长的anchor,v8把这步省了。

还有个容易忽略的点是损失函数。YOLOv8分类分支用了VFL(Varifocal Loss),回归分支用了DFL(Distribution Focal Loss)+CIoU的组合。实际训练体验就是:同等数据量下,v8在细长目标上的回归框稳定性明显好于v7,定位框不会老是歪出去半截。这一点对于裂缝检测太重要了,因为一条横向裂缝的框如果偏移10个像素,统计裂缝长度时会差出很大一块误差。

1.3 实际对比测试的直观感受

我自己在同一套裂缝数据集上用YOLOv5和YOLOv8各训了50轮做AB对比,YOLOv8在mAP50-95上大约高3到5个点,别看绝对数值不大,在裂缝这类小目标场景下,这个提升直接反映在"一条细裂缝能不能稳定框出来"。v5的检测框经常在裂缝的中间区域断掉,一条1米长的裂缝被框成三小段,后处理合并逻辑写得再好也是有损耗的。v8的连续性明显好一截。

另外要提的是生态成熟度。Ultralytics框架把训练、验证、导出、部署的链路都打通了,train完直接可以export成ONNX或者TensorRT的engine文件,一个包搞定。对于做工程落地而不是发论文的人来说,这个效率优势是完全值得考虑的。

2. 训练道路缺陷数据集的核心心得

2.1 缺陷类别到底应该怎么分

类别方案直接决定模型的区分难度和使用价值。我在实际项目里推荐过一个5类别方案:横向裂缝(transverse crack)、纵向裂缝(longitudinal crack)、网状裂缝(alligator crack)、坑槽(pothole)、修补区域(repair patch)。再往细里加块状裂缝、车辙、泛油这些,新手阶段不建议,类别一多互相之间的视觉混淆就上来了。比如块状裂缝和网状裂缝在外观上本身就是渐进关系,标注员自己也容易分不清,模型学起来更费劲,最后验证集的混淆矩阵会很难看。

如果你只需要裂缝定位,不想区分横纵方向,那就只标一个"crack"类,把所有裂缝都归进来。这样数据量集中、模型简单、漏检率低,是很多养护场景更务实的选择。区分横纵方向的意义在于:养护规范里横向裂缝和纵向裂缝的处理工艺不同,需要针对性统计,所以这个分类要看最终业务需要,不要为了“看起来专业”而强行加类别。

2.2 路采图像的数据质量和标注规范

数据的采集质量比数量重要得多。固定机位拍摄时,相机离地高度一般保持在1.2米到1.5米,镜头略微俯视路面,保证视野里能看到前方3到5米的路面。车载拍摄要控制车速,最好在30km/h以下,速度一快图片糊,小裂缝直接变成模糊的一条灰线,人眼都难分辨,模型自然学不会。光照条件要刻意覆盖阴天、晴天、清晨、傍晚四个时段,真实运营场景里光线变化比想象中复杂得多,我见过不少模型在晴天训练集上mAP很高,一到阴雨天全部拉垮,就是因为采集时没做光照控制。

标注环节是整套系统成败的关键。裂缝的标注框要特别注意:不要用正方形的框去套一条细长的横向裂缝,那样会把大量非裂缝区域划成正样本,模型学到的特征就被污染了。正确做法是让标注框紧密贴合裂缝走向,允许出现长条形、斜躺着的框,虽然标注速度会慢一些,但对模型特征学习的质量影响非常大。还有一条:断裂的裂缝建议分段标注,不要强行用一个框把三个断段包住,因为宽框包围的中间空白区域会让模型困惑。

2.3 公开数据集怎么配合自有数据使用

做道路缺陷检测有公开数据可以用,比如Road Damage Detection(RDD2022)这类公开数据集整理了多个国家的道路路面图像,包含裂缝、坑槽、修补等类别的标注。实际做法建议是:公开数据作为预训练基础,先用它训练一版初版模型,再扔到自己的场景数据上微调。不要直接把公开数据和自己采集的数据混在一起训练,因为不同国家的路面材料、标线样式、标注习惯差异很大,混合训练容易让模型抓不住某一场景的特征。

自有数据量建议至少每个类别积累1000到2000个实例再考虑训练,少于这个量级,除非场景极度单一,否则很容易过拟合。数据量不够时可以靠增强算法来补,但增强不能替代真实数据分布,特别是不同路面材料(沥青、水泥、碎石)带来的纹理差异,只能靠多采集解决。

3. 数据集目录结构与训练配置准备

3.1 YOLO格式的数据集怎么组织

数据集目录结构按YOLO标准组织,代码如下:

datasets/road_defect/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/

标签文件是txt文本,每一行代表一个目标框:

class_id center_x center_y width height

坐标值全部归一化到0-1之间。如果我用某款标注工具画的是COCO格式的JSON,要转成YOLO格式的txt,转换时有一个高频坑:COCO的坐标是左上角x、左上角y、框宽、框高,而YOLO需要的是归一化的中心点坐标和宽高。转的时候公式是:

x_center = (x_min + width / 2) / image_width y_center = (y_min + height / 2) / image_height w = width / image_width h = height / image_height

看起来简单,但实际很多人会漏掉除以图片宽高这一步,导致训练时loss不收敛。

3.2 标注工具的选型与效率技巧

标注工具我用过LabelImg、X-AnyLabeling、CVAT。单机小项目用X-AnyLabeling最顺手,它自带的交互式分割辅助功能可以用来半自动标注裂缝——先点几下生成一个粗糙的mask,再转成检测框,虽然细长裂缝的分割效果一般,但矩形框的生成效率比纯手拖快了很多。CVAT适合团队协作,多人同时标注、互相校验都比较方便。

标注效率上有个血泪经验:每张图标注前先看一眼整图,把裂缝和"长得像裂缝的东西"在心里过一遍再动手。盲目的边看边标,容易漏掉图角落里的纵向裂缝,后期补标注非常痛苦。标完一批后建议做一次交叉验证,让另一个人随机抽10%的图重新标一遍,对比一致性。我遇到最多的问题是同一个裂缝两个标注员一个框得紧、一个框得松,这种标注尺度不一致会让模型学出来的框忽大忽小。

3.3 数据增强的参数要怎么调

YOLOv8训练时自带了Mosaic、MixUp、HSV扰动、随机旋转等增强,默认参数对通用目标好用,但路面裂缝场景要做针对性调整。最需要注意的是不要开太强的旋转,路面图像里的裂缝方向是有物理意义的——横向裂缝、纵向裂缝的定义就是基于方向的,如果训练时把图随机旋转了90度,模型的方向判断就被扰乱了。建议把degrees限制在10度以内,只处理轻微的拍摄角度抖动。

亮度对比度扰动我反而建议开大一点。因为巡检车在不同时间段、不同天气下拍摄,曝光差异非常大,把亮度区间拉大能显著提升模型在弱光条件下的鲁棒性。有些采集图偏暗,裂缝对比度极低,这就还得配合input分辨率——我一般把imgsz调成1280,因为裂缝属于小目标,640分辨率下很多细节信息已经丢了。

4. 模型训练的实操全过程

4.1 环境配置和显存估算

YOLOv8要求Python 3.8以上,PyTorch框架跑。安装直接用pip:

pip install ultralytics

显卡建议显存不低于8G。我用一张显存16G左右的卡训练yolov8s和yolov8m都比较从容。显存不够时优先降batch而不是降imgsz——裂缝检测对分辨率的敏感度远高于batch size。batch太小会让BN层的统计量不稳定,实测下来batch至少保持8以上,否则模型收敛明显变慢。显存和batch的对应关系简单估算:yolov8s在640分辨率下每张图大约占1.5G到2G显存(含梯度),1280分辨率会跳到4G左右。

4.2 数据配置YAML文件和关键超参

自定义数据集的配置文件:

# config/road_defect.yaml path: datasets/road_defect train: images/train val: images/val test: images/test names: 0: transverse_crack 1: longitudinal_crack 2: alligator_crack 3: pothole 4: repair

这里注意class_id要从0开始连续编号,不能跳过。如果标签文件里出现了配置里不存在的class_id,训练时会直接报错,排查起来还挺费时间。

训练命令如下:

yolo detect train \ data=config/road_defect.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=1280 \ batch=16 \ patience=50 \ device=0

几个关键参数的选择逻辑说一下。预训练权重建议用yolov8s.pt甚至yolov8m.pt,不要一上来就用yolov8n.pt,nano版本的backbone提取细粒度特征的能力弱一些,裂缝检测这种精细化任务用nano会明显掉点。patience设为50表示连续50轮验证集mAP没有提升就自动停止,防止过拟合。训练时日志里重点看验证集上P(precision)、R(recall)、mAP50、mAP50-95四个指标,正常情况下mAP50-95应该一路缓慢爬升,如果某个epoch开始大幅回落,说明开始过拟合了。

4.3 类别不平衡的处理方法

路面缺陷数据集中,网状裂缝往往数量多,坑槽数量少,这个不平衡会导致模型对坑槽的recall特别低。处理优先级有三步:第一优先是扩充坑槽的样本量,第二用类别权重,第三才考虑在损失函数上做文章。Ultralytics框架支持在训练配置里传cls系数,比如把坑槽类的loss权重提高一些。但实际操作中我发现最有效的还是采样层面的平衡——把坑槽样本做多尺度复制增强,保证训练每个epoch里坑槽的实例数量占比不低于15%。纯靠调loss权重不是不行,只是没有采样策略来得直接。

4.4 训练完必做的三件事

训练结束后,不要急着拿去部署。先确认三件事:第一,看训练曲线里验证集的mAP是否还在涨,如果最后一轮还在涨说明epochs不够,可以加载断点继续训几轮。第二,跑一遍保存的best.pt在测试集上的混淆矩阵,重点看横纵裂缝是否互相混淆,这是路面缺陷里最常见的错误模式。第三,用模型跑几十张完全没有见过的路拍图,人工看一遍漏检情况——这一步能暴露的错误比任何指标都直观。

5. 常见问题与排查技巧实录

5.1 训练loss不收敛,或是mAP始终很低

这种情况我遇到最多次的根因是标签格式错误。YOLO格式的txt是归一化坐标,如果从COCO或其他格式转换时忘记除以图片尺寸,模型训练时的anchor匹配全部错乱,从训练日志看就是box_loss和cls_loss都在降,但mAP一直是个位数。排查方法:随机抽几张训练图,把对应的txt坐标反算回像素坐标,画在原图上检查框是否贴合目标。这是5分钟就能完成的自检,但很多人会忽略。

5.2 验证集指标高,真图实测却疯狂误检

一个高频场景是:mAP50看起来有0.85,一拿到实际道路上跑,路面伸缩缝、窨井盖边缘、车道线磨损处全被框成裂缝。问题出在训练数据里缺负样本,或者说采集数据时太“干净”了。模型学会了“长的深色线条”这个通用特征,但没有区分“裂缝”和“结构缝”的判断能力。解决办法是在数据集里加一批包含伸缩缝、车道线、井盖、刹车痕的路面图,标签文件留空,训练时模型会从这些图上学到负样本信息。这一步对实际落地的影响比调任何超参数都大。

5.3 细长裂缝被框成一段一段

前面提过标注不规范是主因,另一个原因是NMS的阈值设置。细长裂缝响应分散,同一个目标可能生成多个高分候选框,如果NMS的IoU阈值设得太低或太高,都会导致框被切成好几段。Ultralytics的默认NMS参数多数场景够用,但如果断了,先把conf阈值稍微调高0.1试试,无效就适当调低iou阈值——当然这属于后处理层面的补救,最推荐的还是从训练数据的标注贴合度上修。

5.4 数据标注交叉验证这样做

有预算可以隔段时间做一次标注抽查,看已有标注框是否贴合、类别是否正确。更简单的做法是用训练好的模型对全部训练集做一遍预测,把预测结果和原标注框的IoU挑出来,低于阈值的样本大概率是标注问题。我之前用这个方法一次找出了十几张裂缝区域漏标的图,重新补标后mAP直接涨了2个点。这比反复调参性价比高得多。

6. 模型部署与推理优化的落地经验

6.1 模型导出流程:从pt到engine

训练完的best.pt不能直接用,要先导出成推理格式。我用的是ONNX到TensorRT的链路:

yolo export model=best.pt format=onnx opset=12

ONNX出来后在TensorRT上构建engine:

trtexec --onnx=best.onnx --saveEngine=best.engine --fp16

FP16推理精度损失在裂缝检测任务上几乎感知不到,速度却能提一倍以上。INT8量化我也试过,速度再快一波,但裂缝细线结构在量化过程中容易损失细节,mAP下降在3到5个点,量产场景慎用。

6.2 推理时的前处理细节

很多人只关注模型本身,忽略了推理链路的前处理一致性。训练时Ultralytics把letterbox、归一化封装在内部,导出模型后这些步骤要自己实现。最容易出的问题是resize方式不一致:训练时用的是letterbox(等比缩放加灰边),推理时如果直接粗暴resize成640×640,会导致目标变形,裂缝这种细长结构尤其敏感——变形会让框的回归结果偏移好几个像素。推理前处理务必复刻训练时候的参数。

6.3 实时的处理速度参考值

部署在边缘设备的推理速度,用主流嵌入式平台的GPU跑yolov8s在FP16下大致能做到单帧20到30毫秒,对应30FPS左右的处理能力,完全够巡检车正常速度下的实时检测需求。纯CPU跑640分辨率大约是每帧200到400毫秒,适合做离线批量分析,实时性不够。如果业务只需要巡检车停下来拍照或者低速抓拍,CPU方案也能用。要实时就要上GPU或边缘AI芯片,这个选择完全取决于使用场景。

6.4 视频流的稳定策略

视频流检测还有个隐蔽的坑:相邻帧的检测结果抖动很厉害,单帧看是正常的,连起来看框在跳来跳去。简单有效的处理是做帧间平滑——对同一个目标的检测框按时间做加权平均,或者用ByteTrack之类的追踪器给目标分配ID,稳定输出。裂缝检测系统最适合的是“检测+追踪”组合:模型输出裂缝框后,追踪器维护一个裂缝ID,同一道裂缝在连续帧里用同一个ID输出,前端展示效果会稳定得多。

7. 一点个人的实操总结

回看这个项目的全过程,我最深的体会是:道路缺陷检测这种场景,数据和标注质量决定了模型效果的天花板,模型架构和超参调整只是在逼近这个天花板。如果你手上也有一批断断续续积累的路面图像,不要上来就到处调YOLOv8的参数,先把数据集翻一遍,确认标注贴合度、类别定义一致性,这一步花的时间在后续训练里会加倍赚回来。另一个反复验证过的经验是:负样本永远比正样本多放一些,开阔路面上真正需要检测的目标其实很少,大量的路面纹理、井盖、标线在模型眼里都高度可疑,如何忍住“少检”比如何检出来更考验数据配置的功底。这套流程我后来又复制到桥梁伸缩缝、墙面空鼓裂缝等场景上,数据换了一批,整体方法论完全通用,有路面检测需求的朋友可以直接套用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询