☰
基于YOLO的路面坑洞检测系统:从数据标注到部署的完整实践
2026/10/20 5:29:37 网站建设 项目流程

路面病害检测这几年在计算机视觉领域算是“老话常谈”,但我发现真正能把一套系统从算法选型、数据集制作一路做到可用的工程化部署,能讲清楚的人其实不多。这个项目标题——基于YOLO系列的路面坑洞检测系统设计与实现——我在实际项目里完整走了一遍,从最早拿现成权重在测试图上跑着玩,到最后在边缘设备上实时推流检测,中间踩过不少坑。这篇文章就把整条链路拆开揉碎,把选型逻辑、数据标注里的门道、训练调参的实操记录、部署优化的取舍都写清楚,给正在做类似项目或者打算入坑视觉检测的朋友一个可以直接抄作业的参考。

先交代一下背景。很多同学看到“基于YOLO”就以为核心工作是调个库跑通demo,实际上真正要交付一个能用的检测系统,工作量的分布大概是:数据准备占四成,训练调优占三成,系统集成与部署优化占两成,算法代码本身只占一成。这个项目就是围绕路面坑洞(pothole)这一特定目标,搭建完整的数据采集标注流程,训练一个高精度高召回的目标检测模型,并把它嵌入到一个能够处理视频流、输出检测结果和告警信息的软件系统里。适合的人群很明确:有Python和深度学习基础、想了解从算法到落地全流程的学生或工程师。

1. 整体方案设计:为什么选YOLO系列作为检测底座

路面坑洞检测不是冷门方向,你随便查都能看到各种论文,但绝大多数都停留在算法对比阶段。真正到了系统设计环节,第一件事不是选模型,而是想清楚检测目标的形态和运行场景的特殊性。

坑洞在图像里的特征是边界不规则、内部纹理与周围路面差异大、常常伴随裂缝、积水、阴影干扰。它不是那种“特征极其明确”的物体,比如行人、车辆有非常强的结构化特征,坑洞更像是一个纹理异常区域。这就意味着模型对大目标和小目标的适应能力都要强,浅层特征和深层语义特征都要用得上。YOLO系列在这一点上有天然优势,它从YOLOv3开始就引入了多尺度预测,不同尺度的特征图分别负责检测大小不同的目标,这对坑洞这种尺度跨度很大的目标非常友好——近处摄像头下的坑洞能占满画面的一半,远处的坑洞可能只有二十几个像素。

再就是实时性。道路检测系统分两种工作模式,一种是巡检车后装相机边走边拍,一种是固定点位或者手持设备定点采集。两种模式对实时性的要求都很高。巡检车要保证车速在30-60km/h时依然能连续检测每一帧画面,固定点位要能对视频流做接近实时的分析。目前的Transformer类检测器在精度上确实有优势,但在边缘设备上的推理速度根本跑不起来。YOLO系列是典型的单阶段检测器,把目标分类和定位统一在一个卷积网络里完成,不需要两阶段方法那种先区域提议再逐框分类的流程,因此帧率能高出一个量级,在Jetson系列设备上跑个30帧以上很轻松。这个性能余量在做“多路视频流并发”的时候就显得特别重要,单路能跑快,后面加路数才有的谈。

还要考虑工程生态。YOLO系列经过多年迭代,社区积累非常厚。无论你用的是哪个版本,预训练权重好找,导出ONNX和TensorRT的脚本成熟,部署到不同平台都有现成案例。做项目最怕的就是算法跑通了部署时卡住,YOLO系列的生态能省掉非常多排查成本。

2. 数据准备与标注:决定模型上限的隐形关键

很多人训练完模型发现精度上不去,第一反应是调网络结构、换更强的骨干网络,实际上大多数精度问题出在数据。坑洞检测的数据集不像人脸识别有海量公开资源,高质量的开源坑洞数据虽然有,但普遍存在场景单一、天气条件受限、标注不统一的问题。直接用别人现成的数据训练出来的模型,换个城市、换个路面材质,大概率直接失效。别问我是怎么知道的——我第一次测出来模型在测试集上效果很好,结果在一条水泥路面道路上跑了不到两百米,误检率直接翻倍。

2.1 数据来源与筛选策略

数据来源我建议走“公开数据预训练+自采数据微调”的组合路线。公开数据集可以选几种典型的做预训练,比如常见的坑洞识别数据集、无人机航拍路面数据集,或者直接用COCO上预训练好的YOLO权重作为起点。自采数据才是决定你模型能不能用的关键。

自采数据不需要一步到位,但要保证覆盖度。我当时列了一个数据采集计划表,包含以下维度:

  • 路面类型:沥青路面、水泥路面、砖石路面,不同材质背景对坑洞检测干扰程度差异很大
  • 光照条件:晴天强光、阴天漫射光、黄昏低照度、夜间路灯照明
  • 天气状态:干燥路面、湿滑路面、雨后积水路面
  • 拍摄角度:平视、俯视、倾斜角度(手持设备或车头安装角度不同)
  • 拍摄距离:近处(1-3米)、中距离(3-8米)、远处(8米以上)
  • 坑洞状态:完好轮廓坑洞、边缘碎裂坑洞、带积水坑洞、带裂缝坑洞、浅层磨损坑洞

每组至少采集200-300张有效图像。初期数据量不需要追求太多,但是多样性要保证。我自己花了三周时间在不同路段、不同时段采集了大概5000张图像,剔除掉严重模糊、遮挡严重、目标过小的,剩了4200张能用的。这个数量对于微调一个YOLOv8s模型来说基本够用了。

数据筛选这一步值得多说两句。很多同学喜欢把采集到图像全部丢进去训练,这其实是错误做法。一定要做质量筛检,去掉三类图:完全模糊的、目标被树叶或者车辆严重遮挡的、坑洞占据画面面积小于千分之一的。第三类图会让模型学到“在没有坑洞的地方找坑洞”的坏习惯。

2.2 标注规范与工具实操

标注规范和坑洞检测的最终效果直接相关。项目中的坑洞定义是“路面结构完整性受到破坏形成的凹陷区域”,但实际标注时边界怎么画,不同人理解完全不同。我整理了一套标注规范,项目的坑洞标注遵循以下原则:

  • 坑洞边界按视觉可见的破损边缘为框,不是按坑洞实际物理边缘(因为实际边缘在图像上往往不明显)
  • 如果坑洞边缘有裂缝延伸,裂缝不单独标注,统一算作坑洞的一部分
  • 坑洞内有积水,只要还能看出坑洞轮廓,正常标注
  • 两个坑洞距离较近时,如果它们之间有明显的路面结构分隔,分别标注;如果视觉上已经连成一片,标成一个
  • 井盖沉陷、施工切割缝都不算坑洞,反过来这些区域往往会成为误检来源,在后续需要重点观察

标注工具我用的是LabelImg和X-AnyLabeling,前者是老牌工具,适合小批量标注,后者支持半自动辅助标注,能节省不少时间。但半自动标注有一个坑,就是初始模型训练得不够好的时候,辅助标注出来的框往往偏大或偏小,如果你直接接受了AI的建议标注,后面训练出来的模型也会继承这个偏差。我的经验是,AI辅助标注只适合做预标注,人工修正框的四个边是必不可少的一步。坑洞检出框的贴合度直接影响mAP中IoU的计算结果,框偏大或偏小五个像素,在0.5 IoU阈值下可能看不出来,但到了0.75阈值就很明显了。

整个数据集的标注给我最大的感受是:坑洞这个目标看起来好标,实际上比行人车辆这类目标难很多,因为边界模糊是常态,必须靠规则约束而不是靠眼睛感觉。标注完成之后做一次全量框检查,把所有明显过大过小的框都过一遍,值得花这个时间。

3. 模型选型对比:算力约束下YOLO家族的选择逻辑

项目正式进入模型选型阶段时,我拿YOLOv5、YOLOv8、YOLOv9、YOLOv10的多个子版本做了横向跑分。这里要明确一个观点:选模型不是在精度榜单上挑最高的,而是在你的算力预算和实时性要求约束下选最合适的那个。

模型版本参数量(M)输入尺寸推理耗时(Jetson Orin Nano)实测mAP 50
YOLOv5s7.2640约22ms87.6
YOLOv8n3.2640约15ms84.3
YOLOv8s11.2640约28ms90.2
YOLOv9t2.0640约14ms85.1
YOLOv10n2.3640约13ms86.4
YOLOv10s7.2640约26ms91.0

跑分环境统一为TensorRT FP16推理。需要说明的是,这个mAP是在我标注的数据集上训练后的测试结果,不同数据集之间不可直接对比,主要是看相对差距。

结论比较明确:如果部署设备是低算力的嵌入式平台,YOLOv8n或YOLOv10n是首选,因为参数量小、帧率充沛,虽然mAP略低但胜在稳定性好,社区生态成熟。如果部署设备是巡检车内置的高算力工控机,YOLOv10s是最优解,mAP最高,推理耗时仍然在实时范围以内。

YOLOv10没有NMS后处理这个特性在部署时确实省事,但它的训练细节有点特殊,epoch拉长到300时反而更容易收敛到好的结果,这个特点需要留意。如果追求最稳妥的方案,我建议中低算力环境还是选YOLOv8n。原因很简单,YOLOv8系列的导出工具链和部署案例最多,踩坑成本最低。在工程交付时间紧迫的时候,不要选最新版本的模型,选社区验证最充分的模型。

4. 训练实现:从预训练权重到高精度模型的调节记录

训练阶段是整个项目里可调参数最多、最容易让人反复折腾的部分。我把整套训练方案记录下来,希望你能少走一些弯路。

4.1 训练环境与参数配置

算力方面,我用了单张消费级显卡,显存24G。在这个显存条件下,batch size选16比较合适。如果你用的是8G显存,batch size降到4-8,输入尺寸从640降到512,效果也不会差太多。关键是不要在低显存条件下强行上大分辨率输入。

训练关键参数配置如下:

  • 初始权重:YOLOv8s在COCO上的预训练权重
  • Epoch:200轮
  • Batch size:16
  • 输入尺寸:640 × 640
  • 优化器:SGD,初始学习率0.01,配合余弦退火
  • 权重衰减:0.0005
  • 数据增强:Mosaic增强开启(概率1.0),HSV扰动设置饱和度±0.5、明度±0.4,翻转概率0.5

这里说一个实测感受:坑洞检测的数据增强,HSV扰动非常重要。不同光照条件下坑洞的视觉表现差异很大,HSV扰动等于免费扩充了大量不同光线环境下的样本。我当时做了一个消融对比,不开HSV扰动训练的模型,在黄昏低照度测试集上的召回率掉了8个百分点。

Mosaic增强在坑洞检测里要特别小心。Mosaic会把四张图拼在一起然后随机裁剪,这对小目标检测很有帮助,但是坑洞属于中等尺度目标偏多,Mosaic拼图后坑洞的上下文信息会被截断,模型容易学到“坑洞周围一定有路面纹理”的错误规律。实测下来Mosaic开启概率设成1.0反而比0.5差一些,说明Mosaic的上下文扰动对坑洞确实有负面影响。

4.2 迁移学习的正确打开方式

我强烈建议用预训练权重做迁移学习,而不是随机初始化从头训。原因不只是收敛快,更大的意义在于预训练骨干网络已经学会了通用的纹理特征、边缘特征,这些低级特征在坑洞检测中是共用的。随机初始化从头训200轮,mAP大概在80左右,而用COCO预训练权重起步,同样200轮能到90。差距非常大。

迁移学习过程中有一个值得注意的阶段划分。前50轮不要冻结骨干网络,让骨干网络随着坑洞数据一起微调。等50轮之后模型逐渐稳定,再解冻所有层全量训练。有些教程教你先冻结骨干训头、再解冻全量训,这个策略在坑洞数据量比较少时是合理的,可以防止骨干特征被少样本破坏。但如果你的自采数据到4000张以上,直接全量训练效果更好,因为坑洞数据本身的特征分布和COCO差异很大,冻结骨干反而限制了模型适配能力。

4.3 样本不平衡的处理思路

坑洞标注框的尺度分布非常不均衡。近处的大坑洞框面积能到200×200以上,远处的小坑洞只有20×20左右。这个时候如果直接训练,模型会偏向学习大坑洞的特征,小目标召回率会很惨。YOLO系列内部的anchor机制会缓解一部分,但不能完全解决。

我在训练时做了一项调整:将训练集中小目标(面积小于32×32)的比例统计出来。如果小目标占比低于15%,那么我从测试集中选取了部分包含小坑洞的图像,以复制粘贴的方式增强到训练集中,但注意目标不能直接粘贴在完全无关的背景上,要选择同类型路面背景。这个方法粗糙但有效,小坑洞的召回率提升了6个百分点。如果你追求更精细的做法,可以尝试在训练时使用多尺度训练,将输入尺寸在480到800之间随机切换,这项操作对大小目标兼顾的效果很明显。

4.4 指标观察与过拟合判断

训练过程中我不是只盯着mAP,每天训练完成之后会单独看Precision和Recall曲线。坑洞检测有其特殊性:漏检比误检更危险。漏检一个坑洞意味着巡检车辆直接碾过去,可能造成轮胎损坏或悬挂系统损伤;误检一个只是多派一次人工复核,成本低得多。所以在模型收敛阶段,我会刻意选择一个Recall更高但Precision稍微低一点点的checkpoint作为最终模型。

怎么在训练过程中找到这个checkpoint?很简单,train阶段的P-R曲线截距。当Precision接近峰值而Recall还在上升的那段时间里,挑一个满足“Recall > 90%且Precision > 85%”的checkpoint。我当时在160轮附近锁定了这样一个权重,比最后200轮收敛的权重在Recall上高了3个点,虽然Precision低了1.5个点,但整体误检数量在可接受范围内。

关于过拟合,坑洞数据量不算大,训练到150轮以后val loss如果开始缓慢上升,说明模型开始记忆训练集细节了。这种情况下有两个选择:一是直接停止训练,用150轮的权重;二是把Mosaic概率降下来,降低数据增强强度,重新从头训练。我更推荐前者,简单直接,效果也不差。

5. 系统实现:从模型到端到端可用的检测系统

模型训练完成只是系统的一半。整个检测系统设计为三个核心模块:图像采集模块、模型推理模块、告警与可视化模块。下面按模块拆解。

5.1 图像采集与视频流管理

采集模块有两种输入形式。第一种是实时视频流,适用于巡检车或者固定点位摄像头。第二种是本地图像文件或者视频文件批量处理,适用于离线巡检数据的后处理。

采集模块用OpenCV做视频流读取,但要特别小心解码延迟的问题。OpenCV的VideoCapture在读取RTSP流时,缓冲区设置不当会导致画面延迟越来越大。实测中我发现用cv2.VideoCapture直接读RTSP流,大约半小时后延迟会从500ms增长到3秒以上。解决办法是使用FFmpeg的命令行做拉流解码,或者设置CAP_PROP_BUFFERSIZE参数。如果追求更稳定,可以直接用FFmpeg的subprocess调用,把解码后的原始帧通过管道传给Python,这样延迟始终能控制在200ms以内。

图像采集频率也值得设计。巡检车车速60km/h时,如果每帧都做推理,对算力压力很大。我的方案是做抽帧检测:每秒抽10帧做检测,相邻帧之间做了简单的目标追踪匹配,同一个坑洞在连续多帧中都被检测到时,取置信度最高的那帧作为最终确认结果。这样既保证了实时性,又避免了重复检测上报同一个小坑洞。

5.2 模型推理加速与部署

推理加速这块,模型从PyTorch权重到最终部署,踩过的细节不少。核心流程是:PyTorch权重转ONNX再转TensorRT,中间有一个关键步骤是固定batch size。

很多同学在转ONNX的时候不指定动态轴,默认导出的是动态batch版本。TensorRT对动态batch的支持没有问题但会损失一定优化空间。我的做法是直接导出固定batch=1的ONNX,后面如果需要多路并发,就开多个推理实例。这样TensorRT可以针对输入尺寸做层融合、内存复用优化,实测推理速度比动态batch版本快15%左右。

精度上还有一个特别注意——TensorRT的FP16推理。正常情况下FP16对精度影响不大,但在夜间低照度图像上,有时会出现小坑洞的检测框抖动问题。这是因为FP16的数值范围较小,低对比度区域的激活值量化误差被放大了。解决方案是,夜间场景单独做一个预处理步骤,先用伽马校正提升暗部细节再喂给模型,这样能在不退回FP32的前提下保住召回率。如果你要处理的是红外热成像图像,这一步更是必不可少。

5.3 告警与可视化交互

告警模块在PC端和边缘设备端表现不同。PC端我用了PyQt5做图形界面,中间件负责接收检测结果,界面上实时绘制检测框,并附带坑洞坐标、面积估测、置信度三项信息。面积估测需要做像素距离标定,不同安装高度的摄像头换算关系不一样,这个标定数据存在配置文件中,不同监控点位各一份。

告警策略上做了两级过滤。第一级是置信度阈值过滤,只有超过0.4的检测结果才会进入告警队列。第二级是连续帧确认:同一个坑洞在连续三帧中都被检测到才生成最终告警。这两级过滤联合起来能把误报率压到非常低的水平。注意,如果你在NMS之前做多帧确认,比在NMS之后做效果更好,因为NMS会给同一目标只保留一个框,多帧确认就需要做跨帧目标匹配,麻烦。我最后是在NMS之后增加了一个简单的IoU跨帧匹配,效果也不错。

可视化方面,除了画框,我还会在图像下方生成一个实时曲线图,显示最近5分钟内的检测数量变化。这个功能在巡检场景中很好用,道路养护人员可以直观看到哪一段路面的坑洞密度最高,从而决定优先修复区域。

6. 常见问题实录:误检漏检与性能问题的排查手册

这部分我结合自己做这个项目时踩过的坑,整理成一个问题排查速查表,每一条都是实际动过手之后的结论。

问题现象可能原因排查方法解决方案
阴影被误检为坑洞训练集中缺少带树影/桥影的图像检查测试样本中误检图的场景分布补充阴影场景训练数据,或增加使用负面样本
积水反光被误检积水反光的纹理特征与坑洞内部纹理相似单独统计积水反光误检频率增加积水坑洞和干净积水反光作为正负样本对比,微调模型
远处小坑洞漏检小目标训练样本不足统计训练集bbox面积分布复制粘贴增强小目标,或使用多尺度训练策略
夜间漏检严重图像整体亮度低,特征不清晰可视化夜间中间层特征图加伽马校正预处理,或调整HSV增强参数提升曝光鲁棒性
高置信度误检集中在井盖区域井盖纹理与坑洞边缘形态相似统计误检框的位置分布增加井盖负样本,或引入位置先验过滤规则
模型在湿滑路面误检率飙升湿滑路面反光导致坑洞边缘模糊对比干湿路面的特征分布把湿滑路面图像单独作为一个场景类别参与训练,或做色彩归一化
边缘设备推理速度不达标TensorRT优化不足或模型偏大看TensorRT profiling,找耗时层尝试FP16量化,输入尺寸从640降到512,或换更小的模型变体
视频流延迟持续增长解码缓冲区堆积或推理队列阻塞查看CPU/内存占用和队列深度改用FFmpeg拉流,使用异步推理队列

关于井盖误检我想单独展开一下。很多城市的道路上井盖是圆形的,坑洞也是圆形或椭圆形的,而且井盖周围常年有沉降裂缝,视觉上非常接近坑洞。我一开始训练出来的模型在测试集上mAP很高,一跑到市政道路上就疯狂误检井盖。解决思路是数据驱动的:我专门采集了300张带井盖的正常路面图像,在标注时标注为独立类别“cover”,训练的时候这个类别参与损失计算,但推理时直接屏蔽掉这个类别的输出。这样模型学到了井盖和坑洞的差异特征,误检率大幅下降。这是一个通用做法,值得试一下。

还有一个比较隐蔽的坑:坑洞检测模型的误检和阈值关系密切,但是阈值到底设多少,不能靠拍脑袋。我的做法是画出一张P-R曲线,然后结合道路养护的“漏检代价远高于误检代价”这一事实,在曲线上找一个Recall掉点前、Precision相对较高的点。最后选出来的阈值大概是0.35-0.45之间,而不是默认的0.25或0.5。每个数据集的最佳阈值都不同,这个必须自己画曲线后选择。

部署阶段还有一个高频问题:模型在小batch推理时速度正常,但接上视频流后帧率骤降。这多半不是模型算力问题,而是推理线程和采集线程耦合了。图像采集线程和解码、推理在线程模型上分离,用生产者-消费者模式,设置一个最大队列长度,队列满时丢帧而不是阻塞采集。这样即使某一帧推理耗时突增,视频流也不会卡死。

7. 扩展空间与个人经验总结

这个系统做完以后,我实际拿它去了一些真实道路场景做测试,效果基本达标。但我个人觉得最有价值的不是这一个模型本身,而是梳理出来了一套可以复制到其他道路病害检测上的方法流程。裂缝、龟裂、松散、沉陷,这些其实都是同一类视觉问题——路面纹理的局部异常。你只要针对每种病害重新采集和标注一批数据,换一个输出类别的模型头,整套系统就能平移覆盖其他病害类型。

后续如果有余力扩展,我建议优先做三件事。第一,把坑洞检测和GPS定位数据结合,输出带地理坐标的路面病害地图,这对道路养护部门做巡检计划非常有用。第二,加入深度估计或者使用双目摄像头,把检测结果从二维框升级成三维尺寸估测,能更准确地评估修复所需材料量。第三,主动收集误检样本做持续学习,每隔一段时间用新数据增量训练一次模型,防止场景分布漂移导致精度衰减。

最后分享一点个人体会。做这种视觉检测项目,算法模型的mAP并不是项目的全部,真正决定落地效果的反而是那些“看起来不起眼”的部分——数据标注的规则统一性、差异场景的覆盖度、训练指标的选择偏向、部署时线程模型的合理性。你在这些细节上花的时间,最后都会体现在系统稳定性上。如果你正在做类似的检测项目,我建议你把一半以上的时间留给数据和工程,不要沉迷于换模型提效果。先把数据搞扎实,模型效果自然就上来了。

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

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

立即咨询