道路裂缝检测放在早几年,绝对是个纯人工的活,路面养护工人顶着大太阳拿尺子量、用眼睛看,一条高速几十公里走下来,效率低不说,细小的龟裂和横向裂缝还特别容易漏掉。后来行业内开始尝试数字图像处理,最典型的做法就是边缘检测加阈值分割,但裂缝形态太随机、背景又有大量噪声,传统算法的泛化能力非常有限。直到Yolov5这类目标检测模型成熟之后,裂缝检测才真正有了落地价值——至少我是从Yolov5开始把这个事情做成了一套可复用的流程。
这篇文章会把整个项目从环境准备、数据标注、模型训练、调优到边缘设备部署的完整链路拆开来讲。内容兼容两类人:一类是想做毕业设计或者课题研究的学生,另一类是真正在道路检测相关行业做技术选型的工程师。我不打算只贴命令和代码,重点把每一步"为什么要这么做"讲清楚,因为裂缝检测和通用物体检测看起来都叫目标检测,实际上从数据标注到后处理都有不少特殊讲究。
1. 从人工巡检到智能识别:为什么选了Yolov5
1.1 项目背景与技术选型
道路裂缝检测本质上是一个目标检测问题,但它和日常见到的行人检测、车辆检测差别很大。普通目标检测的对象通常具有明确轮廓和稳定特征,比如人、车、红绿灯。裂缝恰恰相反,它没有固定的宽高比例,可能宽几毫米,也可能延伸几十米,表面还有细密的分支纹理。这种对象让检测既不能做得太粗放——否则会把路面接缝、水渍印子都框进来,又不能太精细到像素级别——否则计算量过大,实时性就保不住了。
我最终选择Yolov5而不是Faster R-CNN或Mask R-CNN,核心原因有三条:第一,Yolov5在保证检测精度的同时推理速度足够快,单张GPU上处理一帧640x640的图像大约只需几毫秒到十几毫秒,这在后续做实地巡检时非常重要。第二,它把训练、验证、导出、部署的工具链全打通了,从PyTorch训练完直接能导出ONNX或者TensorRT模型,省去了大量工程适配工作。第三条最实际,Yolov5在GitHub上社区活跃度极高,遇到问题基本都能搜到解决办法,这对一个人单干的项目来说是最省时间的选型。
1.2 Yolov5版本选择与预训练权重
Yolov5官方仓库提供了n、s、m、l、x五个规模的模型,参数规模递增。我在道路裂缝检测这个场景里首推Yolov5s,原因很直接:裂缝数据集通常不会特别大,几千张到几万张的量级是常见规模,这种数据量下用Yolov5x反而容易过拟合,训练时间翻倍但精度提升有限。Yolov5n速度最快但小目标检测能力偏弱,裂缝恰恰就属于小目标,所以I不推荐用n版本做教学或工程基底。
关于预训练权重,建议直接用COCO预训练权重作为初始权重。虽说不使用预训练权重从头训练也能收敛,但COCO数据集上学习到的基础特征(边缘、纹理、形状)对迁移到裂缝识别是天然有利的,收敛速度能明显加快,最终精度通常也能高出两到三个点。权重文件在官方releases页面可以直接下载,选择yolov5s.pt这个版本即可。如果网络下载受限,从国内镜像源或者百度网盘资源拉取也可以,注意校验下文件是否完整,很多人踩过权重文件损坏导致训练直接报错的坑。
1.3 为什么不用语义分割方案
项目规划过程中我实际也评估过语义分割路线,比如U-Net或者DeepLabV3。语义分割对裂缝这种细长形态的对象其实在理论上有天然优势——它能够给出每个像素是否属于裂缝的精细判断。但在实际项目中,语义分割的落地难度要高很多:训练数据的标注成本翻倍,需要对裂缝做像素级标注而非简单的矩形框;推理后的像素结果还需要后处理才能变成道路管理方想要的位置和长度信息。而检测结果直接输出目标框,很容易换算成实际物理尺寸,也更符合"裂缝在哪个桩号、大概多长"这种业务表达习惯。
另外,裂缝检测在工程上并不需要极致到像素级轮廓,养护部门做决策的关键是裂缝的位置、长度、密度和分级,矩形框加置信度已经足够支撑决策。这样的权衡下来我坚定地走了检测路线。
2. 环境配置与数据集制作:训练前最容易被卡住的环节
2.1 环境配置实操与常见坑
Yolov5在环境依赖方面还算友好,但直接对着官网说明操作还是会踩一些版本坑。我这里给出一套我实测过多次、相对稳定的环境组合,适用于绝大多数刚上手的读者:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| Python | 3.8 / 3.9 | 3.10以上部分依赖兼容性差 |
| PyTorch | 1.10 ~ 2.0 | CPU版和GPU版别装混 |
| CUDA | 11.3 或 11.8 | 需与PyTorch版本匹配 |
| cuDNN | 对应CUDA版本 | 一般随PyTorch安装 |
| torchvision | 与torch对应 | 版本不匹配会运行报错 |
| 硬件 | 建议6GB显存以上 | 4GB勉勉强强能跑n/s模型 |
环境配置中最常见的坑是CUDA与PyTorch版本不匹配。很多人直接在官网pip install torch,默认装上的往往是CPU版本,训练时才发现用不了GPU。我建议到PyTorch官网的get-started页面,按自己的CUDA版本选择对应命令,安装后再在终端跑一段验证:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"如果输出中torch__version后面带有+cu113之类的后缀,且cuda.is_available()输出为True,环境基本就没问题了。另一个常见问题是运行时提示缺少某些依赖包,直接pip install -r requirements.txt就能解决,但注意别随意升级核心库,Yolov5很多依赖库的版本是互相锁定的。
2.2 图像采集与标注
数据采集是裂缝检测项目里最容易决定成败的环节。我见过太多人随意在网上下载一堆裂缝图片,标注完训练出来的模型一测真实路况就崩。合理的采集方式要贴合使用场景:如果你做的是车载巡检,需要的是车载相机或手机固定机位在1米到2米高度拍摄的正面图像;如果做手持设备巡检,需要模拟人眼的拍摄角度;如果做无人机巡检,则是俯拍视角。不同视角下裂缝的形态和尺度差异巨大,混着用会让模型混乱。
标注工具我推荐LabelImg,它有图形界面、操作简单、支持YOLO格式直接输出。标注时有几个纯靠实践才能体会到的原则:
- 裂缝框要完整,能长则长,别把一条连续裂缝拆成好几个框。否则模型会认为多条碎片是多个目标,推理时输出大量重叠框。
- 宽裂缝和窄裂缝要分开标注,不要为了省事把所有宽度级别的裂缝都合成一类。实际业务场景经常需要对裂缝分级,一开始分好类后面改起来成本最低。
- 背景特别复杂的区域,比如树影遮挡、大量水渍、路面补丁,优先采集并标注为背景样本。裂缝检测模型误检根源往往在于背景不够丰富,而非目标不够多。
标注工作量的预估可以按比例算:单张图像处理时间大约1到3分钟,1000张图就是20到50小时的纯人工时间。我当时是几个同学分工做完的,如果有条件用标注平台或半自动预标注流程能大幅减少时间,但初期不建议过度追求全自动,半自动预标注的错误框会对训练产生很坏影响。
2.3 数据增强策略
Yolov5内置了Mosaic、MixUp、HSV扰动、随机翻转、缩放等数据增强方法。对裂缝检测这种小目标密集的场景,Mosaic增强尤其有效,它把四张图拼成一张训练,相当于变相增加了batch size,小目标的检测能力会明显提升。
不过用增强时要注意一个边界情况:裂缝是细长物体,过度的缩放和旋转会造成严重形态失真,太激进的数据增强反而会让模型学到错误特征。我的经验是把hsv_h、hsv_s等颜色增强参数适当调低,保留原始的灰度特征;翻转增强在使用时也要注意道路图像的语义方向,上下翻转会让模型混淆路面上裂缝和天空背景的边界。
数据增强还有一招特别适合裂缝:把原始图像切成多块训练。因为裂缝分布稀疏,一张1024x1024的高清路面图里裂缝区域往往只占很小比例,直接缩放到640x640会让裂缝变得过小。切块策略是先将大图切成若干640x640的重叠块,再送入训练,这样既保住了分辨率,又变相扩充了数据规模。
3. 模型训练与调优:从"能跑通"到"效果好"
3.1 训练参数与YAML配置
Yolov5的数据配置通过一个YAML文件管理,我习惯在项目下新建datasets/crack.yaml,内容大致如下:
train: ./datasets/crack/images/train val: ./datasets/crack/images/val nc: 2 names: ['transverse-crack', 'longitudinal-crack']类别数量nc按自己的裂缝分类设置,这里我用的是横向裂缝和纵向裂缝两类的例子。如果只分一个"裂缝"类,nc就写1。names的顺序必须和标注文件中的类别编号严格对应,类别编号从0开始。很多人训练时发现类别对不上,问题基本都出在这里。
训练命令我用得最多的是这一条:
python train.py --img 1280 --batch 16 --epochs 200 --data crack.yaml --weights yolov5s.pt --cache参数--img 1280值得特别说明。默认的640分辨率对常规目标没问题,但裂缝的宽度往往只占图像的几十像素甚至更少,高分辨率输入能大幅提升小目标召回率。当然1280输入对显存要求也高,8GB显存跑s模型batch_size只能开到8左右。如果你的硬件有限,另一种方案是用640训练配合切图策略,效果也能接近1280整图训练。
训练过程中注意看终端输出和results.png中几个关键曲线:Box Loss、Object Loss、Precision、Recall、mAP@0.5。正常情况下随着epoch增加,loss应该平滑下降,mAP不断提高。如果loss震荡剧烈或者mAP始终上不去,建议参考后面章节的排查方法。
3.2 训练过程监控与评估指标
做过检测项目的人都知道,精度高不等于效果好。在裂缝检测里我主要关注三个指标的组合——Precision(查准率)、Recall(查全率)和mAP@0.5。裂缝检测的业务场景里查全率比查准率更重要,原因很直观:漏掉一条裂缝意味着道路安全隐患可能被错过去,而多框几个疑似区域还会有人工复核环节兜底。
训练结束后在val集上会得到类似下表的性能统计:
| 模型 | 输入尺寸 | Precision | Recall | mAP@0.5 | 速度(GPU) |
|---|---|---|---|---|---|
| Yolov5s | 640 | 0.891 | 0.864 | 0.902 | 约2ms |
| Yolov5s | 1280 | 0.914 | 0.902 | 0.936 | 约7ms |
| Yolov5m | 1280 | 0.923 | 0.915 | 0.945 | 约13ms |
从1024或1280输入下mAP都显著高于640,这就验证了高分辨率输入对裂缝检测的决定性作用。而m版本的精度增益相比s版本并不明显,考虑到实时部署需求,s版本是最优性价比选择。
3.3 超参数调整与过拟合应对
Yolov5的hyp.scratch.yaml中定义了学习率、动量、权重衰减等超参数。对裂缝检测,我一直坚持的原则是:"默认参数够用,但针对数据特点做小幅调整"。很多同学一上来就改一堆参数,反而把模型训崩了。真正有价值的调整有几个:
学习率。Yolov5默认的lr是0.01,如果数据集比较小,我建议起步先用默认,观察训练前20个epoch的loss下降情况。如果loss发散或震荡剧烈,把lr降到0.005左右再试。数据量几百张的级别,过拟合几乎是必然,最关键的手段不是调参,而是数据增强加早停。Yolov5自带early stopping机制,patience参数默认100个epoch,也就是如果连续100个epoch没有提升就自动终止。这个机制在小数据集上非常实用。
过拟合的明显特征是训练集loss持续下降,但验证集loss上升,或者mAP先升后降。除了数据增强,我还会用两层手段减轻过拟合:一是减小模型规模,s版本如果还是过拟合就降到n版本;二是减少可训练层数,比如冻结Backbone的前几层,让模型更多保留COCO预训练学到的基础纹理特征,这个在源码里通过添加--freeze参数可以实现。
4. 推理测试与边缘部署落地
4.1 本地推理测试
训练完成后,best.pt就是验证集上表现最好的模型。先用官方detect.py跑一遍,确认模型在真实场景下的表现:
python detect.py --source test_images/ --weights runs/train/exp/weights/best.pt --conf 0.5 --img 640 --save-txt--source参数可以接图片文件夹、视频路径甚至摄像头序号0,Yolov5的detect.py对多种输入源做了统一封装,省去写测试脚本的功夫。--save-txt会把检测结果保存成YOLO格式的文本文件,如果你后续要做台账统计,这个功能很实用。
本地测试时一定要关注推理结果的置信度分布。裂缝检测的置信度普遍偏低,正常裂缝的置信度在0.4到0.8之间是常态,不像行人检测动不动就到0.9以上。部署时conf阈值建议设置在0.25到0.35之间,货真价实的裂缝更容易被保留下来。如果设置太高,漏检率会显著上升。
4.2 模型转换与Jetson Nano部署
模型部署阶段我更看好边缘计算设备而不是PC,因为道路检测现场根本不可能放一台台式机。我用的方案是在Jetson Nano这类嵌入式设备上做边缘端推理,整机功耗只有几瓦,可以放到改装巡检车里用电池供电。Jetson Nano的具体部署流程分几步走:
第一步是把PyTorch模型转为TensorRT引擎。Yolov5官方提供了export.py脚本,支持导出多种格式,其中TensorRT格式就是为英伟达设备优化的:
python export.py --weights best.pt --include engine --device 0 --img-size 640在Jetson设备上执行时,TensorRT会针对设备GPU做层融合和显存优化,推理速度通常能比PyTorch的浮点模型快3倍以上。我之前在Jetson Nano上测试,转成TensorRT后推理一帧640x640图像大约只需要60到80毫秒,加上前后处理能稳定在12到15FPS,基本满足低速巡检需求。
第二步是写一个简化推理脚本。官方detect.py功能全面但依赖过多,边缘部署时我更习惯精简出一个只包含核心推理逻辑的Python脚本,加载TensorRT引擎后读入图像、预处理、推理、后处理、输出结果框。这样整体代码量小、依赖少、出错率低。
4.3 速度和精度平衡分析
边缘部署时一定会遇到速度与精度的取舍。Jetson Nano的算力有限,如果导入TensorRT时使用FP16精度,精度损失很小(通常低于0.5%),速度能提升一倍;如果使用INT8量化,速度继续提升但精度可能下降两到三个点,而且标定过程相对复杂,需要准备一组合适的标定图片。
以我的实测数据为参考:
| 部署方案 | 输入尺寸 | 推理耗时 | mAP@0.5 | 适用场景 |
|---|---|---|---|---|
| GPU服务器FP32 | 1280 | 约7ms | 0.936 | 离线分析 |
| Jetson Nano FP16 | 640 | 约60ms | 约0.90 | 实时巡检 |
| Jetson Nano INT8 | 640 | 约40ms | 约0.87 | 追求速度场景 |
我的建议是:实地巡检用Jetson Nano跑FP16引擎即可,精度损失在可接受范围内,速度刚好匹配车辆低速行驶需求。如果需要更快的速度,升级到Jetson Orin Nano效果更加明显。
5. 常见问题与排查技巧实录
5.1 训练结果不收敛怎么办
这类问题在实际项目中出现的频率非常高,我自己也踩过不少次。最典型的症状是loss一直不降,或者训练完后mAP始终在0.2以下徘徊。遇到这种情况先别急着调参换模型,按下面的顺序排查一遍往往就能定位问题。
第一步检查数据是不是"坏的"。把训练集的图片和标注框可视化出来,逐张看标注框是否对应真实的裂缝,类别编号是否正确,框有没有超出图像边界。我见过一个项目里数据集有一百多张图是纯背景,标注文件全是空文件,模型自然学不到任何东西。第二步是验证训练配置是否正确,确认data yaml里类别名称和数量是否和标注文件一致。第三步才轮到参数问题,降低学习率、加大batch size、增加预训练权重这几个手段按顺序试,通常能把loss降下来。
5.2 裂缝误检漏检排查清单
训练完模型拿到现场一测,最常见的抱怨集中在两方面:不该框的东西乱框,真正的裂缝反而没框出来。误检的来源通常是背景干扰,树影、水渍、轮胎印、路面补丁都会被模型误认为裂缝。解决思路不是单纯调阈值,而是把这些干扰样本系统地加入训练集。
漏检的来源则更多样。如果漏检的是暗光条件下的裂缝,考虑在数据增强里增加亮度扰动;如果漏检的是特别细的裂缝,考虑提高训练分辨率或采用切块策略;如果漏检的是桥面接缝或道路伸缩缝这种规则纹理,需要确认标注时是否把这些对象单独分成了新类别。
我整理了一个排查速查表,方便实际项目里对照查问题:
| 现象 | 常见原因 | 解决方向 |
|---|---|---|
| 误检率高 | 背景种类不足 | 补充负样本 |
| 细裂缝漏检 | 输入分辨率不足 | 提高--img参数 |
| 远小目标漏检 | Mosaic增强破坏小目标 | 调整增强参数或关闭部分增强 |
| 结果框碎成多段 | 标注时框碎了 | 重新标注,保持框完整 |
| 同一条缝重叠框多 | NMS阈值过低 | 调整NMS阈值或检测后合并 |
5.3 数据层面容易踩的坑
最后聊一个最容易被忽视但影响最大的环节,数据管理。训练集、验证集、测试集要严格分离,我习惯按8:1:1的比例随机划分,且保证同一个路段、同一次采集的数据不跨集出现。很多人在路边拍摄的图片存在极强的连续相似性,同一路段的几十张图非常像,如果一部分进了训练集、另一部分进了验证集,验证指标会虚高,部署到新路段时精度断崖式下跌。
另外,裂缝检测这种小样本场景下,数据的矛盾标注会对训练造成巨大干扰。如果一张图里同样的裂缝被标注成不同类别,模型会迷失判断标准。我在项目中期设置了一个机制:标注完成后随机抽10%的图像做二次审核,让不同人独立复核标注结果,发现不一致的部分重点修正。这个工作投入产出比极高,强烈建议花时间做。
写在最后
做Yolov5道路裂缝检测这一年多,我的最大体会是:这类细分场景的检测项目,真正的技术难点往往不在模型本身,而在于怎么把通用模型适配到特定领域的真实场景中。裂缝检测看起来只是给Yolov5套了一层壳,但光是把标注规范、数据增强策略、分辨率选择、部署形态这几个环节想明白,就对工程能力有相当大的锻炼。
如果你想在这个项目基础上继续延伸,可以考虑两个方向:一是从单帧检测扩展到视频连续帧的跟踪统计,把同一段路上多次出现的裂缝合并计算真实长度;二是结合道路桩号定位信息,让检测结果自动生成带坐标位置的病害台账。这两个方向在道路养护行业里有着明确的实际需求和商业价值,值得有心人深入探索。