做路面缺陷检测这两年,有一个体会越来越深:模型本身很少是瓶颈,数据和场景才是。不管是裂缝、坑槽还是修补破损,真正难处理的是路面环境里那种“说不清道不明”的干扰——树影、水渍、油污、井盖边缘,稍有光照变化就能让模型幻觉不断。这篇文章不打算罗列文档式的步骤,而是把我基于不同版本 YOLO 模型做路面缺陷识别的完整实践链路梳理一遍,包括数据准备、版本选型、训练调参、部署落地和针对小目标的优化路线。
如果你正打算用目标检测解决道路巡检问题,或者已经在跑 YOLO 但效果始终上不去,这篇文章应该能帮你避开不少我踩过的坑。我写的所有结论都来自实际项目反馈,不是理论推演,但具体指标和场景依赖自建数据,换一个数据集可能就有差异,这点先说明白。
1. 路面缺陷数据的真实形态:这活儿最难的不是模型
1.1 数据来源和采集难点
做路面缺陷检测,第一步不是下载公开数据集,而是想清楚你的数据从哪来。公开的路面缺陷数据集不是没有,比如某些学术数据集里主要是俯拍裂缝图,但实际巡检场景要复杂得多。我经历的项目里,数据来源基本有三类。
第一类是专业巡检车采集。这种车顶部装工业相机阵列,配合补光灯,拍出来的图像分辨率高、光照相对均匀、视角固定。优点是好标注,缺点是设备贵,而且拍摄姿态和普通人手持设备差异很大,模型上了手机或无人机视角后性能暴跌。
第二类是无人机低空拍摄。视角灵活,一次能覆盖大面积路段,但高度变化导致目标尺度变化剧烈,还有抖动模糊和运动模糊问题。同一段裂缝,飞 10 米高和 20 米高拍出来的尺寸差好几倍,这对目标检测的尺度泛化是很直接的考验。
第三类是手持设备或者行车记录仪视频抽帧。这是我最推荐起步的方式,成本低、样本量大、场景碎片化程度高,能自然覆盖不同光照、不同路面材质、不同天气条件。缺点同样明显:拍摄距离不固定、画面存在畸变、标注难度大,遮挡和反光也特别多。
我最终的自建数据集大概三千多张图像,混合了以上三种来源,标注了七类缺陷:横向裂缝、纵向裂缝、网状裂缝、坑槽、松散、修补破损、井盖周边沉降。这里我要强调一个比较容易忽视的点——类别定义直接决定模型能力边界。比如“修补破损”和“坑槽”在视觉上经常有交叉,如果标注标准不统一,模型训完必然在两类之间摇摆。我在项目早期就是因为标注人员对“修补破损”的边界理解不一致,导致验证集 mAP 虚高但实际推理时乱跳,最后被迫把所有标注框重新过了一遍。
1.2 标注痛点:裂缝类目标的框到底怎么打
裂缝是路面缺陷里最典型也最烦人的目标。它细长、连续、可能断断续续,还可能跨越整张图像。用目标检测的矩形框去框一条横向裂缝,你会立刻遇到一个灵魂问题:这个框到底该框多长?如果框太短,模型学到的特征是“一小段裂缝”,实际推理时遇到长裂缝就会输出一堆碎片框;如果框太长,框里大部分区域是正常路面,正样本噪声太大。
我试过几种策略,最后实际采用的是“按断裂段独立成框,段与段之间若距离小于 15 像素则合并为一框”。这个规则听起来简单,但能极大减少标注人员的随机性。实际操作中我会用标注工具的分割模式预先画裂缝线段,再后处理转成框,而不是让人直接拉矩形框,这样做出来的框更贴合裂缝走向。
另一个坑是本质不均衡。坑槽和修补破损这类“块状缺陷”数量少但好检测,裂缝类数量多但难检测。如果标注时只追求总量,训练出来的模型会对裂缝的敏感度高得离谱,连路面伸缩缝、施工切割线都当成裂缝报出来。伸缩缝在视觉上和裂缝极像,但它是规则的人工切缝,不是缺陷。这个细节如果不在标注标准里写清楚,模型几乎必错。
1.3 数据清洗与划分的隐藏技巧
很多人拿到数据就直接按 8:1:1 划分训练集、验证集、测试集,这在路面场景下容易出问题。因为连续帧抽帧得到的图像高度相似,如果来自同一路段的数据同时落在训练集和验证集里,验证集的分数会虚高,部署到真实路段后性能立刻露馅。
我的做法是按“路段”划分而不是按“图像”划分。具体来说,给每一批采集数据打上路段编号,然后按路段分组,确保同一路段的图像只在训练集或验证集中出现一次。这一步会让你的验证分数“变难看”,但更接近真实泛化水平。另一个经验是验证集里一定要留一部分“脏数据”,就是包含阴影、水渍、井盖、树影等干扰的图,别只放干净图片。模型最终要应对的就是脏数据,验证集太干净等于掩耳盗铃。
数据清洗方面,我遇到最多的问题是重复图。巡检车在低速行驶时,同一个坑槽可能被拍了几十帧,抽帧后看起来相似但又不完全一样。如果不做去重,训练集中某一类缺陷被反复强化,其他类别学不充分。我用的是感知哈希加人工抽检的方式,先算图像哈希过滤相似度高的图,再对剩余数据按类别分布做抽样检查。这套流程花不了太多时间,但能显著避免训练集内部“自我重复强化”。
2. YOLO 各版本在路面场景下的对比实验:不是越新越好
2.1 为什么锁定 YOLO 系做主力
选 YOLO 系不是因为它完美,而是因为它综合性价比最高。路面缺陷检测属于典型的中等目标尺度任务,单张 640 分辨率下缺陷占图面积普遍在 1% 到 10% 之间,既不像遥感图像那样有大量极小目标,也不像工业质检那样目标位置固定。YOLO 的 anchor 策略和特征金字塔结构,配合近几年不断迭代的 backbone,对这种尺度范围天然友好。
另一个关键因素是部署生态。路面巡检最终要落到实际场景,不管是边缘盒子还是手机 App,都需要成熟的推理框架支持。YOLO 系列在 TensorRT、OpenVINO、ONNX Runtime 上的支持度都是最成熟的,踩坑的人多意味着解决方案也多,这对项目落地是实打实的优势。
还有一个容易忽略的点:YOLO 的迭代版本多,社区活跃,预训练权重容易获取且质量稳定。路面缺陷数据和 COCO 的分布差异很大,但预训练权重里的基础视觉特征仍然非常有价值。迁移学习比从零训练省太多时间,这也让 YOLO 系成了我最常用的起点。
2.2 实测对比:几个版本的代表性差异
我先后拿 YOLOv5、YOLOv8、YOLOv9 和更新一些的 YOLO 版本在同一套路面数据上做过对比实验。这里需要强调,我不追求跑满所有变体,因为项目时间是有限的,我只是从工程角度评估它们的核心差异。
先说 YOLOv5。它的优点是极度稳定、资料全、部署方案成熟,几乎任何问题都能搜到答案。我在早期项目中用 YOLOv5s 跑通了一版路面裂缝检测,mAP@0.5 能到 70% 出头,但小裂缝漏检明显。后来换了 YOLOv8s,同样的数据、同样的训练配置,mAP 大概提升三到四个点,主要收益来自 anchor-free 头带来的定位改进,细长目标的框质量明显变好。
YOLOv9 给我印象最深的是 ELAN 结构对信息保留做得更好,在复杂背景下训练 loss 下降更稳。但实测来说,它在我的路面数据上精度提升并没有显著拉开和 v8 的差距,推理速度还略慢。YOLOv10 的优势是端到端无 NMS 设计,推理管线更干净,部署时少一步后处理,但精度与 v8 基本持平。到了更新版本,比如 YOLO11,主要提升体现在小目标召回和整体架构微调上,但收益属于锦上添花,不像 v5 到 v8 那种质的提升。
下面是我在自建数据集上的粗略对比表,使用相同的 640 输入、100 个 epoch、默认增强配置:
| 模型版本 | 输入尺寸 | mAP@0.5 | mAP@0.5:0.95 | 推理耗时(ms, GPU) | 备注 |
|---|---|---|---|---|---|
| YOLOv5s | 640 | 0.711 | 0.432 | 4.2 | 稳定但小裂缝漏检 |
| YOLOv8s | 640 | 0.749 | 0.468 | 5.1 | 框定位更灵 |
| YOLOv9s | 640 | 0.756 | 0.473 | 5.8 | 训练稳,速度略慢 |
| YOLOv10s | 640 | 0.745 | 0.461 | 6.2 | 无NMS,部署略简单 |
| YOLO11s | 640 | 0.762 | 0.482 | 5.6 | 小目标召回有改善 |
这个结果仅供参考,换一个数据分布可能结论就不同。但里面有一个相对稳定的趋势:对路面缺陷这类中尺度目标,v8 之后的版本在定位质量上明显优于 v5,而 v9 到 v11 之间更多是工程细节改进,不是代差级提升。
2.3 版本选择的最终建议
如果让我给一个选型结论,我会说:追求稳定就选 YOLOv8s,作为主力训练模型;如果是边缘设备部署,可以先用 YOLOv8n 或者 YOLO11n 跑基准,看精度能不能接受;如果你的场景里有大量细长裂缝,重点不是换新版本,而是调高输入分辨率或者做切片推理。
另外,不要因为某个新版本在 COCO 上刷了点就立刻迁移。COCO 是通用物体检测,路面缺陷的数据分布和 COCO 相差甚远,COCO 上的收益不一定能迁移到你的场景里。我的经验是,用一个稳定版本先建立完整的训练、评估、部署链路,比反复换版本试新方法重要得多。模型版本更替的成本不只是换代码,还要重新验证所有部署环节,这个隐性成本经常被低估。
3. 训练与调参:三个真正改变结果的关键点
3.1 输入分辨率对细长目标的影响
路面缺陷检测最容易踩的调参坑,就是输入分辨率设得太低。YOLO 默认是 640,但裂缝这种细长目标在缩放到 640 后,宽度可能只剩两三个像素,特征非常微弱。把输入分辨率从 640 提升到 1024,mAP 提升往往会很明显,代价是显存占用增加,训练和推理都变慢。
我做过一个很直接的对比:同一份数据、同一个 YOLOv8s 模型,640 输入下 mAP@0.5 是 0.749,1024 输入下能到 0.801,提升超过 5 个点。代价是训练时间暴涨约一倍,推理耗时也增加。这个投入产出比在缺陷检测场景非常划算,尤其是裂缝类目标。
但无脑拉到 1280 也有问题。一方面训练显存压力大,另一方面路面纹理细节太多,模型可能学到过于细碎的纹理特征,反而对整体缺陷结构不敏感。我的建议是先用 640 跑通流程,再尝试 960 或 1024,观察 mAP 增益是否明显,如果增益小就回到低分辨率以换取速度。
3.2 样本不平衡与损失函数调整
路面缺陷数据里,裂缝占比可能超过 60%,坑槽和松散可能只有百分之十几。默认的 BCE 分类损失对多数类有天然偏向,训练出来的模型会倾向于把模糊区域预测成裂缝。我试过直接改类别权重,让少数类损失权重更大,但效果不稳定,有时候权重拉太高会导致精度崩掉。
更有效的做法是结合数据层面调整。一方面对少数类做过采样,让每个 epoch 里不同类别的样本数量尽量接近;另一方面用 Mosaic 增强让不同类别的目标出现在同一张训练图里,增加背景多样性。类别权重我只敢轻微调整,比如给坑槽和松散乘个 1.2 到 1.5,再高就会有副作用。
还有一个容易忽视的点是 focal loss 的作用。YOLOv8 默认的分类损失不是 focal loss,如果你发现模型对难分类样本很吃力,可以在 loss 配置里手动启用 focal loss 并调 gamma 参数。我试过 gamma=1.5 对裂缝和阴影难分的情况有一定帮助,但训练时间会变长,对插值得到的 mAP 不一定有决定性的提升。这个属于“锦上添花”项,不是“雪中送炭”项。
3.3 增强策略与过拟合控制
路面图像有一个特点:背景高度重复。同样是柏油路,不同路段的纹理差异其实不大,模型很容易对某些特定纹理模式过拟合。我在早期实验里发现,模型在训练集上 mAP 高得吓人,但拿到新路段上一测就掉十个点以上。这就是典型的过拟合,而不是泛化问题。
应对策略我用得最多的是随机光照扰动和随机透视变换。路面检测的痛点之一就是光照不稳,晴天阴影、阴天均光、夜间路灯照射都不一样。我在训练时把亮度、对比度、饱和度的扰动幅度调得比默认值更大,让模型对光照变化更鲁棒。透视变换模拟不同拍摄角度带来的形变,对无人机和手持设备的视角变化很有帮助。
过拟合的另一个解法是迁移学习。用 COCO 预训练权重做初始化,可以显著降低训练收敛所需的数据量。我不建议路面数据量低于两千张时从零训练,效果大概率不理想。但也要注意,预训练权重里的 BN 统计量迁移到路面数据时需要重新适应,通常在训练前几十个 iter 内会自行调整,不需要额外处理。
训练过程里我习惯盯三条曲线:训练 loss、验证 loss、验证 mAP。如果训练 loss 和验证 loss 差距越来越大,说明在过拟合,优先增加增强强度或者减少训练轮数;如果验证 loss 在下降但 mAP 不动,说明分类和定位的平衡有问题,考虑调整损失权重或检查数据标注质量。很多人只盯 mAP,不看 loss 曲线,遇到问题无从下手,这是很可惜的。
4. 部署与落地实践:从验证集漂亮到现场翻车之间的距离
4.1 推理加速:不止是换模型
训练完模型,真正到部署阶段又会遇到新的麻烦。很多人以为导出 ONNX、TensorRT 就够了,其实部署管线里的细节比想象中多。我用的部署方案是 TensorRT 半精度推理,在边缘盒子上的实测速度比 PyTorch 直接跑快三到五倍。但 TensorRT 对动态尺寸支持不太好,固定输入尺寸能简化很多麻烦。
这里有个必须注意的点:部署时不要直接对原始大图做全图推理。巡检图片经常是 4000×3000 的超大图,直接缩放到 640 输入会让小目标丢失严重。我的方案是先把大图分块,比如切成 1024×1024 的 patch,每个 patch 送入模型,再把结果拼回去。这样推理次数增多,但每块的分辨率有保障,小裂缝的召回率会高很多。
分块方案在工程上要处理的细节是重叠区域。如果 patch 之间没有重叠,正好落在边界的裂缝会被切掉一半,导致漏检。我习惯重叠 10% 到 20% 的分块,拼回结果时做 NMS 合并去重。这个方案的推理耗时比整图推理多不少,但检测效果提升明显,在速度和精度之间需要做权衡。
4.2 真实场景中的典型误检:井盖、阴影、水渍
部署到现场后最打击人的不是模型跑得慢,而是它在“错误的地方”疯狂输出。路面上的井盖是重灾区。井盖边缘的圆形弧线、井盖表面纹理、井盖与路面交接处的阴影,很容易被模型当成裂缝或坑槽。如果训练数据里没有足够的井盖负样本,模型几乎必检。
阴影也特别烦。一棵树的影子打在路面上,边缘的明暗对比类似裂缝;傍晚时分阳光斜射,路面本身的纹理被拉长,形成假裂缝。这些负样本如果不单独收集放进训练集,模型很难自己学会区分。
我后来专门为这些负样本建了一个“干扰样本池”,里面全是阴影、井盖、水渍、油污、轮胎印、伸缩缝等非缺陷目标,然后通过 Mosaic 增强随机混入训练图。这个方法极大地降低了误检率,甚至比调整模型结构更有效。数据层面的问题,最终还是要靠数据层面解决。
4.3 视频流处理的帧率稳定策略
路面巡检很多场景是连续视频流,不是单张图片。这时候除了检测模型本身,还要处理丢帧、队列积压、结果平滑等问题。我的经验是,检测频率不需要匹配视频帧率,比如视频是 30 FPS,但模型只需要每 3 到 5 帧检测一次,中间帧直接复用上一次结果,这样能极大降低计算压力。
另一个策略是级联检测。第一遍用轻量模型快速扫描全图,只负责粗定位“可疑区域”,第二遍用高精度模型对可疑区域做精细分类。这个两阶段方案在实际巡检中效果很好,既能控制漏检,又不至于让整体推理速度慢到不可用。我在实际项目里做了一套基于 YOLOv8n 加 YOLO11s 的级联方案,整体吞吐量比单一大模型提升了约两倍。
结果平滑也值得做。单帧检测结果抖动很严重,同一个缺陷这一帧有框、下一帧没框,这对巡检报告是不可接受的。用一个简单的滑动窗口记录最近五帧的检测结果,只有出现次数超过三帧的缺陷才输出,能显著减少跳变。这个逻辑在工程上非常简单,但对最终交付体验的提升非常明显。
5. 针对小目标和杂乱背景的路线优化
5.1 切片推理:让裂缝不再“一框多缝”
我前文提到大图分块,其实和切片推理类似但不止于此。如果数据里存在大量长连续裂缝,直接训练原始图容易让模型学到“框住整个裂缝区域”,而不是精准回归裂缝本体。解决办法是把裂缝切成多段独立标注,训练时让模型学习的是“段”而不是“整条缝”。
推理阶段同理,把图像切成小块分别检测,小块上的长裂缝自然会被拆成若干段,拼回去后通过后处理按邻接关系合并。这个思路对细长目标非常有效,代价是要把推理管线和后处理逻辑写得更复杂。我在项目里用 SAHI 思路做了切片推理封装,核心逻辑是设定 patch 大小和重叠率,每块都做归一化缩放到同一尺寸,最后把检测框映射回原图坐标,用 NMS 合并。
切片大小需要根据目标尺度调整。路面裂缝在 1024×1024 的 patch 上表现最好,如果 patch 太小,裂缝被切成太多段,合并逻辑容易出错;如果 patch 太大,分辨率优势丢失。这里推荐直接做实验对比:取 512、640、1024、1280 四档,对比 mAP 和耗时,选一个最优组合。
5.2 两阶段筛查:先定位区域再细分病害类别
路面缺陷检测的另一个实用路线是“先粗后细”。第一阶段用检测模型定位缺陷大致区域并判断大类,第二阶段对每个候选框做裁剪、放大,再跑一个更精细的分类网络判断具体病害类型。比如第一阶段输出“疑似裂缝区域”,第二阶段判断它是横缝、纵缝还是网状裂缝的开端。
两阶段的思路对类别边界模糊的缺陷很有效。我在项目里发现,直接用一个大模型区分横缝和纵缝,效果很不稳定,因为拍摄角度稍微变化,横缝在画面里就可能变得像纵缝。但第一阶段只输出“是否存在裂缝”,第二阶段基于裁剪图判断方向,准确率会高很多。
代价是训练和维护两套模型。不过现在 YOLO 生态比较好,分类头可以直接用 torchvision 的轻量网络,MobileNet 级别的模型就够用。这个方案尤其是对巡检报告需要明确缺陷类型和严重等级的场景,价值很大。
5.3 低对比度与恶劣光照下的补救
路面缺陷检测绕不开光照问题。阴天低对比度、夜间路灯、雨天反光,每一种情况都会拉低模型表现。我在前期数据采集时就特别强调要覆盖这些场景,但在实际部署中还是难免遇到没见过的光照条件。
一个有效手段是图像预处理增强。在推理时对图像做 CLAHE 对比度受限自适应直方图均衡,能让低对比度路面的裂缝纹理更清晰。这个操作在大多数 YOLO 模型上不需要重新训练,只要推理前做一次预处理就行。我测试过,对阴天和雨天场景,CLAHE 大概能提升 3 到 5 个点的召回率,成本几乎为零。
夜间场景更麻烦。单纯靠预处理很难解决,最稳妥的办法是采集夜间数据训练。如果实在采集不到,另一个思路是在训练阶段做更狠的光照增强,随机把图像调暗、加噪声、改变色温,让模型对光照变化更鲁棒。但它的提升有限,毕竟夜间图像和白天图像之间的域差距不是增强能完全弥补的。
5.4 数据回流与持续迭代闭环
最后聊一个很容易被忽略但很关键的环节:上线后的数据回流。模型部署到现场后,一定会在真实数据上产生各种误检和漏检,这些数据如果只是随用随丢,那模型就永远无法变好。我在项目里搭了一个很简单的闭环:现场检测结果自动保存,定期人工抽样标注,标完自动合并进训练集,重新训练和评估。
这个闭环看起来简单,但坚持三个月后模型效果会有明显提升,因为训练集逐渐覆盖了各种“现场真实意外”。常见的做法是按周或者按两周回流一次,每次新增几百张现场图和对应错误检测记录,人工修正标注后重新训练。这个过程里最容易忽视的是记录模型的错误模式,比如误检集中在井盖还是阴影,这决定了你该去采集哪种负样本。
6. 回头聊聊几个值得坚持的实践习惯
写到这里,整个项目的技术主线基本梳理完了。最后分享几个我反复实践后确认值得坚持的习惯。
第一个习惯是先跑通最小闭环再优化。任何新项目,先用默认配置、小数据集、轻量模型把整个链路跑通,确认数据格式、训练脚本、部署流程没有问题,再开始做版本对比和调参。我在早期犯过一个错误,一上来就想用最新版本追求最高精度,结果光环境配置就折腾了几天,才发现某个数据预处理环节有问题。先跑通,再做优,这个顺序能帮你节省大量时间。
第二个习惯是回归简单。很多提升并不来自复杂的模型结构,而是来自数据质量、输入分辨率、增强策略和部署后处理。我们总觉得换一个更强的模型自然有更好的效果,但实践下来,数据侧的投入回报往往比网络结构侧的投入更稳定。尤其是路面缺陷这种背景复杂、类别分布不均的场景,把标注标准统一、把负样本收集齐、把输入分辨率提上去,效果比换模型版本明显得多。
第三个习惯是持续做错误分析。不要只看 mAP 数字,要定期打开验证集上的错误预测图,观察模型在哪类缺陷上错、在哪类背景上误报。这个习惯看起来费时间,却是最快找到瓶颈的方法。如果误报集中在阴影,就去补阴影样本;如果漏检集中在小裂缝,就去调分辨率或切片策略。问题永远在数据里,模型只是在替你表达。
如果你准备开始做路面缺陷识别项目,我建议不要急着买最优设备或者追最新模型版本,先把一套稳定可复现的流程建立起来,让数据流转起来,让错误模式可见,提升自然会跟上来。