ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Yolov5道路裂缝检测实战:从数据标注到Jetson Nano部署

2026/9/15 21:47:49 拓冰建站 浏览量
Yolov5道路裂缝检测实战:从数据标注到Jetson Nano部署 路面养护圈子里有一句话裂缝是道路病害的“最先信号”但发现它靠人眼盯着路面一寸一寸扫效率实在太低了。我当时接这个需求时第一反应就是直接用目标检测模型来做最后选型落在Yolov5上完整跑通了一个道路裂缝检测项目——从数据采集、标注规范、训练调参到Jetson Nano边缘部署整个过程踩了不少坑也总结了一套对这个特定场景很有效的处理方式。这篇文章就把整个项目从0到1的完整路径写出来如果你正打算用Yolov5做道路裂缝检测或者想训练一个自己的自定义数据集但卡在参数和部署环节那这篇内容应该能帮你少走很多弯路。1. 为什么是Yolov5裂缝检测的选型逻辑与项目定位1.1 传统图像处理为什么做不好裂缝检测先说个反直觉的事:很多人以为裂缝检测就是个边缘检测的活拿Canny或者Sobel算子找一找暗色线条就行了。我在项目预研阶段还真的试过这条老路——灰度化、高斯滤波、Canny边缘提取、形态学闭运算把断开的裂缝连起来。在实验室干净、光照均匀的几张测试图上效果确实像模像样能描出裂缝的大致轮廓。但一到真实路面就崩了。路面上有树荫、有油渍、有轮胎印、有水渍反光、有人行道接缝这些纹理在灰度图上和裂缝长得极其相似。传统CV靠的是人工设计规则本质上是猜路面长什么样而真实路面的纹理变化比想象中大得多规则永远写不全最后的结果就是误检率居高不下完全没法用。退一步说就算用CNN分类网络判断这张图里有没有裂缝也只能给个整体结果。巡检工人的真实需求是裂缝在哪个位置、大概多长多宽一个框的坐标比一句可能有裂缝有用得多。这也是当初把这个项目定位成目标检测而不是分类或分割的核心原因——检测模型能同时给出位置和类别直接对接后续的派单维修流程。1.2 Yolo家族怎么选Yolo家族到了2024年版本已经很多了为什么选Yolov5而不是更新的Yolov8或者YoloX我的理由非常实际。第一是生态成熟度。Yolov5从2020年发布到现在社区积累的资料、踩坑记录、第三方工具脚本非常完善。做工程最怕的不是模型精度低而是报错找不到人问。搜Yolov5 报错出来一堆解决方案搜其他新框架的报错可能只有官方issue里孤零零的几条。第二是部署链路完整。这个项目最终要上Jetson Nano这种边缘设备Yolov5官方就提供了TensorRT导出的脚本FP16推理、engine格式封装都给你写好了省掉了很多自己造轮子的时间。Yolov8当然也不错但它的优势更多体现在统一API和新的训练策略上对于道路裂缝这种对细节纹理敏感、类别极其单一的场景Yolov5s和Yolov8n的精度差距并不大。第三是可控性。Yolov5的代码结构是出了名的模块化从数据增强到NMS后处理都能改。比如裂缝目标长宽比特别夸张需要关掉自动锚框或者修改anchor大小再比如mosaic增强在裂缝场景下会导致标签错位我在第四部分会细说——这些魔改在Yolov5里改起来都很顺手换别的框架未必有这么灵活。1.3 项目目标与类别设计我最后只定义了两个类别:横向裂缝和纵向裂缝。有的项目也会加网状裂缝和修补区域但我的建议是类别不要一上来就搞太细。原因有两个一是标注工作量会爆炸网状裂缝的边界本身就模糊一个框很难框得准二是模型学到的区分特征不够横裂和纵裂在形状上有本质区别但网状裂缝和横向裂缝掺杂在一起时框的边界很难定义。输出就是一个标准的检测结果每个框带四个坐标、一个类别、一个置信度。后期接业务系统时可以根据框的宽度估算裂缝长度按路段编号汇总上报这就是完整的落地闭环了。2. 裂缝数据的获取、清洗与标注规范2.1 数据从哪来跑通Yolov5自定义数据集最先卡住你的往往是数据而不是代码。道路裂缝虽然常见但网上现成的高质量标注数据集并不多。我当时主要用了两条腿走路。一是公开数据集像CrackDataset、DeepCrack这类学术数据集可以下载它们的好处是标注相对规范、类别已经分好适合先跑通流程但缺点是拍摄机位多为固定角度、光照均匀和实际巡检场景手持手机、车载相机、不同天气差距很大直接拿来做最终模型泛化能力不够。二是自己采集。这个方法最土但最有效拿手机贴着路面拍或者装一个行车记录仪在园区、老旧小区、市政道路转几圈。我建议拍的时候刻意覆盖阴天、逆光、傍晚、雨后天晴这些场景。裂缝检测最大的敌人是光照变化训练数据里没见过的光照模型在线上一定翻车。公开数据加自采数据我当时凑了大概2600张有效图。如果预算充足或者项目周期紧还有一条捷径是找道路养护单位要历史巡检照片但要注意版权和数据合规问题。2.2 清洗比标注更重要拿到原始数据之后第一件事不是打开标注软件干活而是删图。删除标准很明确严重模糊的、虚焦的、过曝到白茫茫一片的、运动模糊到看不出纹理的一律删。这些图强行标进去只会让模型学到一堆噪声。视频抽帧得到的图片还要做去重同一段路面连续几十帧高度相似全部喂进去会造成数据冗余训练时模型反复看同一块路面验证集稍微有点变化就露馅。还有一个容易忽略的点是类别平衡。横向裂缝和纵向裂缝的数量要尽量接近如果纵裂是横裂的三倍模型会偏向预测纵裂横裂的召回率会明显偏低。我当时专门数了数把数量少的那一类又补拍了些数据。划分数据集时也要注意按场景划分不要按文件随机划分。比如同一个小区路面连续拍的100张图如果不加处理随机分到train和val验证集里就会出现和训练集几乎一样的图指标虚高到真实场景马上露出原形。2.3 标注规范要提前定死标注工具我用的是LabelImg和AnyLabeling。AnyLabeling支持半自动分割辅助效率高一些LabelImg更轻量、启动快随便哪个都行。真正重要的是标注规范团队协作时尤其如此。我踩过最大的坑就是对裂缝框怎么框没有提前定义导致几个标注助手各框各的有的贴裂缝边缘贴得死死的有的留了很大边距模型训练出来很飘。后来统一成一条规则框要贴合裂缝的主体区域但四周留出大约裂缝宽度20%~30%的余量给模型一点上下文信息锚框回归会更稳。另外一个争议点是裂缝断开怎么处理。一整条裂缝中间有积水或者杂物看起来断开了但人眼能看出它是同一条。我的规范是断开的段落如果走向一致且间隔小于裂缝长度的一半就当作同一条裂缝框一个整体框如果间隔很大或者走向明显变化就各标各的。标注最忌讳的是随手一点、随性一框模型会学到混乱的模式。还要特别警惕一类标注错误把路面伸缩缝、裂缝阴影、轮胎印标成裂缝。这些目标在灰度图上和裂缝长得太像了一旦标进去模型就会把它们当成正样本学检测时误检率直线上升。我在项目中期检查标注文件时发现这类噪声占比接近8%后来专门组织了一次清洗把错误标注全部挑出来重新标。导出格式用YOLO标准格式每张图片对应一个txt文件每行是class x_center y_center width height四个坐标都是归一化到0~1之间的浮点数。标注软件一般会自动转换不熟悉的同学打开生成的txt看一眼就明白了。2.4 数据量到底要多少很多人喜欢问我到底要标多少张图这个问题没有标准答案取决于场景复杂度。裂缝检测有个好消息和一个坏消息。好消息是裂缝在视觉上的模式相对固定就是暗色线条状纹理不需要像通用目标检测那样学几千个类别。坏消息是裂缝在画面里的尺寸跨度极大有的裂缝宽到占画面三分之一有的细到只有几十个像素模型需要同时学会检测大象和蚂蚁。从我经验看单一场景下每类裂缝有500张以上干净的标注图就能训出一个能用的模型要做到稳定上线每类800~1500张合适。如果数据量不够优先保质量——1000张干净图比3000张糊弄的图训练效果好得多。3. 训练环境搭建与工程目录设计3.1 环境准备Yolov5环境配置是很多人第一道坎尤其是PyTorch和CUDA版本对应关系搞不清楚一装就是半天。我用的组合是Python 3.8 PyTorch 1.12 CUDA 11.6这个组合在Yolov5官方requirements.txt里运行得非常稳。创建环境用condaconda create -n yolov5 python3.8 conda activate yolov5然后从GitHub官方仓库拉取Yolov5代码git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt如果显卡驱动已经装好PyTorch用pip直接装即可。建议安装时留意一下PyTorch的版本号别拿到CPU版本还浑然不知训练起来能慢到怀疑人生。我自己踩过的一个坑是换了新显卡之后旧环境里的PyTorch CUDA版本不匹配训练时直接报找不到CUDA设备。后来统一用conda管理环境、把CUDA相关的变量写在环境激活脚本里再没出过这种问题。3.2 项目目录结构Yolov5对数据集的目录结构有硬性要求官方文档写得很清楚但新手还是经常搞错。datasets/ └── crack/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/images和labels的train/val目录名必须完全一致否则训练时找不到对应的标签文件报错。文件命名也保持一致比如IMG_001.jpg对应IMG_001.txt。3.3 数据集配置文件在Yolov5根目录下新建一个crack.yaml内容如下# 数据集的根路径相对于yolov5仓库执行命令的位置 path: ../datasets/crack train: images/train val: images/val # 类别信息 nc: 2 names: [transverse_crack, longitudinal_crack]一个很蠢但常见的报错是path路径写错。如果你把crack.yaml放在yolov5/目录下path: ../datasets/crack意味着yolov5仓库外层的datasets目录。建议第一遍跑之前用绝对路径比如path: /home/user/datasets/crack排除路径问题之后再改成相对路径。3.4 起步训练命令环境搭好、目录结构确认无误之后先跑一个最小的训练命令验证流程python train.py \ --data crack.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100--cfg指定模型结构yolov5s是small版本显存够用、速度还行适合第一轮实验--weights yolov5s.pt是用COCO预训练权重做迁移学习初始化千万别从头训练裂缝特征虽然和COCO的日常物体不同但底层纹理特征边缘、角点、梯度是通用的迁移学习能让收敛速度快很多。训练起来之后日志里会打印每个epoch的box_loss、obj_loss、cls_loss、mAP等指标。第一步的目标不是追求最好指标而是确认数据流、训练流程、验证流程全部正常模型确实在学习。4. 裂缝小目标场景的参数调优方法4.1 分辨率与切图如果你直接拿Yolov5默认的--img 640去训裂缝大概率会得到一个漏检严重的模型。原因是裂缝在640分辨率下可能只有二三十个像素宽、几百个像素长目标太小特征图上的信息被压缩得很厉害。最简单的优化是提高输入分辨率。我把--img从640提到1280之后mAP0.5直接涨了差不多6个点。但代价也很明显显存占用翻倍训练时间变长。如果你的显卡只有6G或8G显存1280分辨率下batch size就只能开到4左右训练速度慢很多。这时候我更推荐切图方案把大图比如3000x4000的路面照片按1280x1280的窗口切成有重叠的patch分别推理最后再把所有patch的检测结果合并到大图坐标上。这个方案在工程上有一个现成工具叫SAHI专门为小目标检测设计实测能把裂缝漏检率再降低不少。4.2 超参数设置别急着魔改Yolov5官方有一堆超参数文件hyp.scratch-low.yaml是低数据增强配置hyp.scratch-high.yaml是高增强配置。我第一轮实验用的是hyp.scratch-low.yaml只在COCO预训练权重基础上微调默认学习率0.01就能收敛得很好。如果你做裂缝检测感觉收敛慢第一件事不是去调学习率而是检查数据。漏标太多、标注框不贴目标、类别不平衡这些数据问题会直接体现在loss曲线异常上。我见过太多人在参数里折腾半天最后发现是标注文件没配对。几个关键超参数的经验值参数推荐值说明epochs100~150裂缝特征不复杂更多轮次容易过拟合batch-size16或尽量大裂缝背景占比极高过大的batch意义不大lr00.01使用官方默认即可迁移学习微调够用mosaic0.0~0.5见下文详细说明hsv_h/hsv_s/hsv_v减小裂缝颜色基本是深灰颜色抖动意义不大4.3 数据增强的取舍mosaic是个双刃剑这是裂缝检测项目里我踩得最深、也最想提醒后来人的一个坑。Yolov5默认开启mosaic增强把四张图拼成一张再训练。这个策略在通用目标检测里效果很好因为物体通常是紧凑的、独立的。但裂缝不一样——裂缝可能横贯整张图长度远超一个拼图块。mosaic在裁剪拼贴时极大概率会把一条完整的裂缝从中间截断标签跟着被切成两半甚至四张图的裂缝拼在一起变成一条奇怪的折线。模型大量学习这种半截裂缝之后表现就是对整条完整的裂缝反而检测不出来了因为训练时没见过完整的长裂缝长什么样。我的解决办法是自定义一个hyp.custom.yaml把mosaic关掉或降到0.5以下mosaic: 0.0 # 0.0表示关闭0.5表示50%概率使用 mixup: 0.0 copy_paste: 0.0同时把hsv_h、hsv_s这些颜色增强参数下调因为裂缝的颜色、饱和度变化很小过强的颜色抖动反而会让模型学到错误的颜色关联。这个改动之后模型的召回率提升非常明显尤其是长裂缝的检测。做裂缝检测的同学强烈建议一上来就检查一下mosaic在你自己数据集上的表现不要被默认参数牵着走。4.4 锚框要不要自己算Yolov5默认情况下训练开始前会跑一遍autoanchor根据你的数据集标签自动计算新的anchor大小。这是一个非常好的功能但对裂缝这种极端长宽比的目标自动anchor的效果未必理想。裂缝的边界框长宽比经常是1:10甚至1:30而默认COCO预训练模型里的anchor是按通用物体尺寸设计的长宽比最大也就1:3左右。autoanchor会在训练前计算并提示你anchors mismatch有时候它自己算出来的新anchor长宽比还是不够极端。我当时的做法是先跑一次python train.py让它打印出autoanchor的结果然后打开代码里utils/autoanchor.py生成的anchor手动调整成更适合裂缝长条形状的值。例如把三组anchor调整为anchors: - [10, 6, 24, 8, 16, 16] # 小目标 - [32, 12, 48, 20, 64, 32] # 中等目标 - [96, 48, 192, 96, 384, 192] # 大目标调整完之后训练mAP又涨了一截。这块属于锦上添花但确实是裂缝检测这种极端形状目标最容易见效的优化点。5. 从评估指标到误检分析判断模型能不能上线5.1 先看哪些指标训练结束后runs/train/exp/目录下有一整套评估结果包括PR曲线、混淆矩阵、F1曲线、标注错误的图片。我不建议直接看最后的mAP数字就完事要有重点地看。对裂缝检测来说mAP0.5是最主要的参考标准mAP0.5:0.95在小目标上通常很难看不用太焦虑。更重要的是召回率。道路巡检业务里漏检一条裂缝比误检一个非裂缝要严重得多——漏检意味着病害没有被发现误检顶多让工人多跑一趟。我当时给自己定的标准是mAP0.5做到0.78以上召回率0.85左右才敢进入实测阶段。5.2 混淆矩阵怎么读Yolov5训练完会生成confusion_matrix.png这张图能直接告诉你模型在哪些类别之间容易混淆。我的项目里最常见的误检有三类第一类是路面伸缩缝。这是道路工程施工时人为留的缝视觉上和裂缝实在太像连人眼都要凑近了才能分辨。模型把它检测成裂缝几乎不可避免我的办法是在后处理阶段加一个已知伸缩缝GPS坐标的过滤规则检测框落在伸缩缝区域就过滤掉。第二类是阴影和轮胎印。这个靠数据解决——多采集一些有阴影、有轮胎印的路面负样本专门训练模型学会把它们和裂缝区分开。负样本在裂缝检测里几乎和正样本一样重要。第三类是横裂和纵裂的分类混淆。尤其是斜向裂缝介于横和纵之间模型容易给出错误的类别。这种只能靠提高分辨率、让模型看清裂缝的走向来缓解。5.3 视频流上的稳定性问题单帧图片检测得好不代表视频流里就稳定。我实测时发现一个大问题同一段裂缝这一帧有框下一帧没框再下一帧又有框了。这种时断时续的输出在业务系统里没法用不可能每一帧都触发一次报警。解决办法是在检测后加一个简单的时序过滤模块连续三帧以上都检测到同一个位置的裂缝才触发上报框的位置按跟踪ID聚合避免重复上报。这个模块用OpenCV的跟踪器或者简单的IoU匹配就能实现不需要上复杂的多目标跟踪算法。我另外测了夜晚、雨天、逆光三个场景发现逆光下的漏检率最高。后续补拍了一些逆光数据重新训练情况改善了很多。数据集对光照的覆盖度直接影响模型上线后的表现这一条经验在任何视觉项目里都适用。6. 把模型塞进Jetson NanoTensorRT导出与边缘部署6.1 为什么选Jetson Nano做边缘端巡检场景通常是要把摄像头装到小车或者路侧设备上不可能扛着训练用的GPU服务器到处跑。而Jetson Nano这块板子功耗只有5~10WCPUGPU整合在一块小板上完事可以跑深度学习推理生态也成熟是这个项目当时最合适的选择之一。选型时我对比过树莓派但树莓派没有NVIDIA CUDA生态跑Yolov5只能靠CPU硬扛延迟完全不可接受。Jetson Nano虽然算力也不算强但至少能跑TensorRT加速实时性勉强够用。6.2 Jetson上跑Yolov5的环境细节Jetson Nano用的是ARM架构和普通PC的x86环境差异很大不能直接照搬PC上的环境配置。我的步骤如下烧录JetPack系统镜像注意选对版本老版本JetPack 4.6 自带的是PyTorch 1.10左右。用miniforgeconda的ARM版创建Python3.8环境。从NVIDIA官方渠道安装PyTorch和torchvision的ARM版wheel包。把PC上训练好的weights拷过来。一个非常重要的细节是swap空间。Jetson Nano内存只有4G跑PyTorch稍微大点的模型很容易OOM。我设置了一个4G的swapfile训练不行但推理有很大改善。6.3 TensorRT导出与FP16量化在PC上直接导出engine文件再拷贝到Jetson上我试过不一定能跑通因为TensorRT和CUDA版本可能不一致。最稳妥的方式是在Jetson上现场导出python export.py \ --weights best.pt \ --include engine \ --device 0 \ --halfTensorRT的原理是先把PyTorch的动态图模型转换成静态计算图然后做层融合、精度校准、kernel自动选择。对于裂缝检测这种灰度目标FP16精度几乎没有损失但推理速度能提升很多。导出的时候如果报维度相关的错误可以手动指定固定输入尺寸python export.py --weights best.pt --include engine --device 0 --half --img 6406.4 实测表现我最终在Jetson Nano上跑Yolov5s FP16 TensorRT输入640x640实测推理速度大约8~12 FPS。看起来不高但对道路巡检这种低速移动场景已经够了拍摄一张、停顿一下、再检测一帧完全可以实现实时巡检。显存占用很低主要的瓶颈在CPU端后处理和图像读取上。如果你觉得FPS不够有两个优化方向一是改用更小的Yolov5n模型速度几乎翻倍精度也就低一两个点二是把图像读取和推理放到两个线程并行处理用队列缓存帧FPS提升明显。部署时不建议直接套用官方detect.py脚本那个脚本加载了很多调试功能正式环境内存开销大。我最终自己写了一个TensorRT推理封装加载engine、预处理、推理、后处理NMS、输出坐标全部在一个Python类里完成简洁很多也好维护。7. 裂缝检测做深之后的几个方向与个人建议7.1 用分割代替检测边界框有一个天然劣势它给不出裂缝的精确轮廓和宽度。如果你的业务需要计算裂缝的宽度、判断裂缝的发展趋势那检测模型就帮不上忙了。这时候可以考虑Yolov5-seg或者Yolov8-seg输出的是像素级的掩膜能精确到裂缝的每一个像素。分割对标注的要求高很多可能要手工逐像素描边工作量成倍增长。不过现在有一些半自动标注工具可以帮忙先用检测模型框出裂缝再用分割模型在框内自动生成掩膜人工只做微调效率能提上来。7.2 网络结构的轻量改造裂缝是低层纹理特征主导的目标不需要太强的高层语义信息。如果你在实验中发现Yolov5s的模型容量溢出了或者说小裂缝漏检严重可以考虑在backbone里加一个轻量的注意力模块。我做过实验在C3模块后面接一个简单的SE注意力mAP能再涨1~2个点。但这类改动属于锦上添花先把数据和训练调稳再来动结构才不至于浪费调试时间。7.3 落地时的三个工程建议第一摄像头角度固定后把画面里跟路面无关的区域裁掉再推理。只保留路面区域等于变相提高了模型对有效区域的注意力漏检率和推理时间都能改善。第二上线后要坚持收集bad case。把每天检测漏掉的、误检的图定期汇总回标后增量训练。视觉模型的提升就是个持续迭代的过程没有一劳永逸的模型。第三第一次跑这个项目的同学我建议的路线是先用公开数据集把完整流程跑通再换自己的数据最后才去优化指标。很多人一上来就拿自己的数据折腾环境、代码、参数同时出问题根本定位不了原因。流程先通优化是后面的事。最后说一点个人体会。做完整个道路裂缝检测项目最大的感受是Yolov5本身不难难的是数据一致性。模型从能跑到能用的关键从来不是某条命令或者某个超参数而是你愿不愿意花几天时间把数据标准和标注规范想清楚。另一个经验是测试模型不要只看指标拿一段真实的道路视频让它跑视频里模型的犹豫和失误比任何指标都更能告诉你下一步该优化什么。希望这篇记录能帮到正在做类似项目的你。