ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

无人机航拍道路缺陷检测:YOLO格式数据集与裂缝坑洞识别训练实战

无人机航拍道路缺陷检测:YOLO格式数据集与裂缝坑洞识别训练实战

用无人机代替人工去市政道路上跑病害巡检,这几年几乎成了很多养护单位的标配动作。但我在这类项目里见过最多的翻车现场,不是飞机摔了,也不是照片拍糊了,而是模型根本不认识路面——拿通用目标检测模型直接往航拍图上套,裂缝没框出几条,树影、沥青补丁、砖缝反而被标得密密麻麻。归根结底,通用数据集里几乎没有专门为“无人机俯拍道路缺陷”准备的正样本,模型没见过的东西,自然认不出来。所以像“无人机航拍巡检道路缺陷图像数据集”“道路裂缝识别、道路坑洞YOLO格式数据集”这类面向市政道路巡检的专用数据,才真正有落地价值。

这篇我就以这类数据集为引子,聊聊它到底长什么样、为什么用YOLO格式组织、拿到手之后怎么训练出一套能在市政巡检里真正干活的裂缝和坑洞检测器。内容不绕弯子,直接说标的和坑。

1. 一套面向无人机航拍的病害数据,为什么不是“拿现成模型就能凑合”

1.1 通用数据集的模型,栽在无人机这个视角上

先说个很多人容易忽略的事实:COCO、VOC这类通用数据集里,“路面裂缝”和“路面坑洞”从来都不是主流类别。模型在那个世界里学的是人、车、猫、狗、杯子,是一个个边界清晰、有完整轮廓的“东西”。但裂缝是什么?它本质上是一条贯穿路面的纹理断裂,没有封闭轮廓,颜色和背景几乎没差别,离开上下文你甚至很难说它是个独立物体。

更麻烦的是视角。车载相机拍道路病害,是贴地斜视,裂缝在画面里是横向展开的长条,阴影和车辙干扰相对可控。无人机航拍则是从几十米高空俯拍,画面更平、目标更小、纹理更多,路面的车道线、新旧沥青接缝、树影、井盖边缘全都混在一起。同一个病害,在两种视角下的特征表达完全不同,模型在车载数据上练出来的特征,拿到俯拍图上基本失效。

所以,一套“无人机航拍巡检道路缺陷图像数据集”存在的意义,不是锦上添花,而是解决最根本的正样本缺失问题。它给模型提供的不是泛泛的“特征提取能力”,而是“从高空俯拍画面里认出路面病害”这一种被通用数据集体量稀释掉的能力。

1.2 专用数据集的三个关键价值:类别、口径、可迭代

第一是类别体系干净。像这套数据集,类别就锁定两类:道路裂缝(crack)和道路坑洞(pothole)。别觉得两类太少,实际工程里把类别控制得越少,模型越容易把注意力集中在“病害本身”上。你非要一口气加进去龟裂、沉陷、车辙、拥包,每一类的标注样本都得成百上千,否则模型学出来就是一团浆糊。

第二是标注口径统一。道路病害检测最怕一件事:同一个东西,在这张图里算裂缝,在另一张图里不算。数据集如果由多人标注且没有统一规则,模型会学到互相矛盾的特征,训练出来的结果就是验证集上指标还行,换个路段立刻现原形。所以靠谱的数据集,内部一定有一套明确的“什么算裂缝、什么不算”的口径,这个我后面会展开讲。

第三是可迭代。像标题里的“第10402期”,说明这是一套批次化更新的数据资产。市政道路巡检不是一次性项目,而是每个月、每个季度都在重复的任务。每一期新的航拍数据产生后,可以在旧模型基础上做增量训练,同时淘汰过时机型和低质量样本。这种“官网式数据资产”的运营方式,恰恰是很多临时采集一次就跑路的项目最缺的。

2. YOLO格式的巡检数据集,从文件结构到标签内容的完整拆解

2.1 最常见的目录组织方式

拿到一套YOLO格式的数据集,第一件事不是急着解压训练,而是先看目录结构。规范化的数据集通常长这样:

road_defect_10402/ ├── images/ │ ├── train/ │ │ ├── 10402_000001.jpg │ │ ├── 10402_000002.jpg │ │ └── ... │ └── val/ │ ├── 10402_005001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 10402_000001.txt │ │ ├── 10402_000002.txt │ │ └── ... │ └── val/ │ ├── 10402_005001.txt │ └── ... └── road_defect.yaml

关键点在于:images和labels里每一个文件的文件名严格一一对应,train和val的划分已经做好了。不用自己再折腾原始的图片和标注文件配对,省掉最枯燥的第一步。

如果数据集是单目录结构,只给一个all文件夹外加train.txt、val.txt两个清单文件,也常见。这种情况下,清单文件里每一行是一个图片的相对路径,训练脚本按清单加载,效果一样。你自己做数据集时,建议直接采用train/val分开的目录结构,因为后面所有训练工具、脚本都是默认兼容这个模式的,少写很多自定义逻辑。

2.2 标签txt里那一行数字代表什么

YOLO格式的标注文件是纯文本,每一行对应一个目标实例,格式固定为:类别ID、归一化中心点x、归一化中心点y、归一化宽度、归一化高度。

一个典型的label文件长这样:

0 0.521034 0.367821 0.082153 0.014672 1 0.118475 0.839201 0.041233 0.031892

第一行的含义是:类别0,即裂缝,目标框中心点位于图像宽度方向的52.1%处、高度方向的36.8%处,框宽度占整张图宽度的8.2%,高度占整张图高度的1.5%。

注意几个容易踩的细节。第一,所有坐标全部归一化到0到1之间,如果你的框本来是用像素坐标画的,必须先除以图像宽高,否则训练会直接崩。第二,如果目标框超出了图像边界,标注时应该把超出的部分裁掉再归一化,不要留下大于1的数值。第三,类别ID从0开始计数,不是从1开始,这在写配置文件时最容易搞错。

配套的配置文件road_defect.yaml是所有训练脚本的人口,至少包含路径、类别数、类别名三样东西:

path: /data/road_defect_10402 train: images/train val: images/val nc: 2 names: 0: crack 1: pothole

这个文件里的nc必须和标注文件里的类别ID最大值得对上,names的顺序必须和训练时预期的一致。很多人训练到一半才发现names写反了,明明数据里裂缝最多,模型预测出来的类别1却是坑洞,白白浪费两天时间。

2.3 数据集的“批次期号”在实操中怎么用

“第10402期”这个编号不是摆设。在真实的巡检项目里,每一期数据对应着一次具体的巡查任务:有航线、有日期、有一个明确的地理覆盖范围。批次号至少能做三件事。

第一,追溯。模型在新路段上误检了,想知道是不是训练数据里缺少同类样本,直接按批次号查这一期的原始航线照片,看采集条件,比在一堆无编号图片里大海捞针效率高得多。第二,防止数据泄漏。划分训练集和验证集时,如果按文件随机切,同一期数据里同一个路段的不同帧会同时进训练和验证,指标虚高,换路段立刻露馅。正确的做法是按批次或按路段划分,保证验证集里的位置信息对模型完全陌生。第三,增量训练。新一期航拍数据回来之后,只需要在新数据上做增量训练,不用把历史数据全部重跑一遍,成本低很多。

3. 无人机俯拍照里的裂缝和坑洞,标注细节决定模型上限

3.1 飞行高度决定你能看到多细的裂缝

很多团队把无人机照片拍回来了才发现,模型怎么训都学不出细裂缝。问题往往不在模型,而在照片的地面分辨率。

地面分辨率用GSD(Ground Sample Distance)表示,物理意义是每个像素对应地面上多大范围。估算公式不复杂:GSD等于飞行高度乘以图像传感器像元尺寸,再除以镜头等效焦距。举个直觉例子,飞行高度50米、常见行业级相机参数下,地面分辨率大概在1到2厘米每像素的量级。一条常见的横向裂缝宽度也就是1到2厘米,在画面里只占一个像素左右。

一个像素宽的裂缝,人的肉眼在屏幕上放大还能勉强看出来,但对目标检测模型来说,它要依赖周围十几个像素的上下文才能形成判断,难度陡增。所以标注和训练之前,先回看采集参数:巡检细裂缝时,飞行高度不要一味追求大覆盖面,该压低就压低;或者使用长焦相机,保证病害在画面里至少有5到10个像素的宽度。数据集的图像质量上限,从起飞那一刻就定死了,标注和训练都改变不了这个物理约束。

3.2 裂缝是“长条状”目标,坑洞是“块状”目标,框法完全不同

YOLO格式只能用水平矩形框标注目标,但裂缝根本不是个矩形。它可能是一条横跨半幅路的横向裂缝,可能是沿着行车方向的纵向裂缝,也可能是弯弯曲曲的网状裂纹。用矩形框去框,必然会有大量空白区域框进来,这是格式本身的固有约束,但标注时要尽量控制误差。

我的经验是让标注员遵守几条简单规则。横向裂缝和纵向裂缝,框要沿着裂缝主轴去拉,尽量减少框内无效背景;能把一条完整裂缝框进去就不要拆成多段;但裂缝长度超过图像宽度三分之一且形状明显不规则时,宁可拆成两三段分别标注,也不要硬拉一个包含大量背景的大框。拆段的坏处是同一物理裂缝会被计数成多个目标,好处是模型学到的框内特征更集中,检测精度明显提升。

坑洞则是另一回事。坑洞是有边界的塌陷区域,形状大体接近圆形或不规则多边形,用水平矩形框天然合适。标注坑洞时反而要小心一个情况:很多坑洞边缘是破碎的,沥青剥落向外发散,框是把整个破碎区域都框进去,还是只框中间塌陷核心区。我认为只框核心塌陷区更利于训练,因为发散状的碎边在俯拍图上和普通裂缝混杂,框进去只会让类别特征不清晰。

3.3 边界样本:该标不该标,直接影响模型认知

这是标注环节里最重要、也最容易被压缩的部分。无人机俯拍图上,有三种东西和裂缝、坑洞长得极其相似:新旧沥青路面的拼接缝、路面标线磨损后的不规则边缘、树荫下呈现深色纹理的普通裂缝状阴影。

到底算不算病害,不能靠标注员个人感觉,否则十个人能标出十种结果。数据集在标注时如果没有给出一套边界样本规则,训练出来的模型就会把这些“视觉上像、实际上不是”的目标统统学成裂缝。我处理这类问题时会给标注员一张决策表:拼接缝宽度均匀、走向规则、延伸距离长,不标;拼接缝边缘有局部剥落、明显比缝宽更宽的破损,按裂缝标,并在备注里说明;阴影呈连续条状但没有纹理断裂,不标;阴影下确实能看到路面材料断裂的,按裂缝标。

这些边界规则看起来麻烦,但直接决定了模型在真实巡检画面里的行为。漏标边界样本,模型容易把拼接缝当裂缝误报;全标进去,又会让模型学到错误的裂缝概念。这个度的把握,是数据集质量最重要的分水岭。

3.4 多人标注的一致性校验

如果数据集是多个人标注的,一致性校验环节必须有。最简单的做法是抽5%到10%的图做二次标注,对比同一张图上两个标注员画出来的框。

我习惯用两个指标校验。一是框位置一致性,计算两个人标注框之间的IoU,裂缝目标IoU低于0.5就要拉出来复核,坑洞目标IoU低于0.7也要复核。二是类别一致性,同一目标两个人一个标裂缝、一个标坑洞,更是必须全部回炉。实际操作中,曲线裂缝的框画法差异很大,所以裂缝的IoU阈值我会放得宽一些,但类别错误一个都不能忍。

4. 拿这套数据训练YOLO系检测器的实操路径

4.1 数据划分时最容易犯的“区域泄漏”错误

训练数据处理好之后,第一步永远是划分数据集。不少人有习惯直接用脚本把所有文件随机打乱,按8比1比1拆成训练、验证、测试三份。在这类无人机巡检数据上,这个习惯很危险。

无人机航拍通常是沿着某条道路航线飞行,相邻帧之间高度重叠,同一路段会被拍到几十张相似的照片。如果随机打乱,同一个路段的照片会被同时分到训练集和验证集,模型等于提前看过考试答案,验证指标会虚高。等部署到一条全新的路上,真实效果立刻打折。

正确处理方式是:按路段目录、按照片采集时间段落来划分,确保验证集和测试集里的任意一张图,与训练集里所有图像在地理位置上都不重叠。数据集的批次编号在这个时候就有用了,直接按批次切,训练集用前几期,验证和测试用最后两期,这样测出来的指标才反映模型的真实泛化能力。

4.2 大图不能直接硬缩,切片推理才是正路

无人机单张原图通常达到4000×3000甚至更高,直接缩放到模型输入尺寸,等于把裂缝目标按比例缩小成几个像素,模型再强也白搭。拆分思路是:训练前先把原图切成1280×1280的tile,相邻tile之间留出10%到20%的重叠,再喂给模型。

切片的最大价值不止是保住分辨率,还在于解决了小目标密集的问题。道路场景里大片路面往往没有病害,直接全图缩放会让“无目标”背景占绝对主导,模型学得很慢。切片之后,每个tile里的正样本占比明显提高,训练效率完全不是一个量级。

切片时要注意同步切标注。如果原图标注是像素坐标,直接按tile的坐标偏移换算即可;如果数据集本身已经是YOLO格式的归一化坐标,可以先还原成像素坐标,切完再归一化回去。重叠区域的意义在于,目标如果恰好落在tile边界附近,只被切成半截,模型大概率认不出来;留出重叠后,同一个目标至少在一个完整tile里出现过,检测天然更稳。

4.3 训练参数与评估指标的经验设置

起步用中等规模的预训练模型最合适。小模型跑得快,但对细裂缝这种小目标,特征表达能力偏弱;超大模型训练慢,部署也费劲,在嵌入式设备上根本跑不动。以YOLO系列为例,我会优先试s或m规格,imgsz设为1280,batch大小根据显存调整。

训练轮次建议设在200个epoch左右,配合早停机制。无人机航拍数据集通常样本量不大,往往训练到150个epoch左右验证集指标就触顶了,继续硬跑反而过拟合。优化器用默认的AdamW或SGD均可,但学习率不要图快开大,初始学习率调低,配合warmup会稳得多。数据增强方面,Mosaic和MixUp保留,HSV扰动加大饱和度变化范围,因为无人机巡检跨季节、跨天时,光照差异非常大。

评估指标不要只看mAP。裂缝检测场景,我更关注两个维度:一是小目标的AP,直接反映细裂缝检出能力;二是置信度阈值变化时误检的数量,这一步决定模型上线后会被现场人员骂多少次。另外,正负样本极度不均衡是这类数据的常态,AP50和AP50-95都要看,只看AP50容易被大目标的高分带偏。

我还习惯在测试阶段统计每个类别在不同置信度阈值下的检测数量曲线,曲线越陡越好。如果0.15阈值下有几百个检测框、0.5阈值下只剩几十个,说明模型虽然学了些特征但置信度不稳定,落地时要非常谨慎。

5. 模型过了验证集,真正上路后会遇到的坑

5.1 市区巡检现场的误检高发场景

模型在验证集上mAP漂亮,不代表进了市区就靠谱。无人机航拍市政道路的最大特点是背景极度复杂,我把自己反复遇到过的误检场景整理了一下。

误检表现常见原因我的处理对策
新旧沥青拼接处被框成裂缝纹理断裂形成的连续线状特征,与裂缝太像专门收集拼接缝样本做难例挖掘,同时降低检测置信度阈值验证真实误检率
树影、路灯阴影被框成坑洞深色区域和坑洞颜色特征接近训练时加入更多阴天、逆光、树影覆盖样本,必要时用颜色空间特征做后处理过滤
车道标线磨损边缘被框成裂缝标线边缘不规则、呈细长条状尝试加入“标线破损”类别或手动收集负样本,让模型学会区分
井盖、检修孔边缘被框成坑洞圆形边界和深色填充,与坑洞高度相似依赖上下文判断,训练时不要裁剪掉井盖周围的完整路面结构

处理误检的核心思路不是调大置信度阈值,阈值调高了,裂缝也跟着漏检,得不偿失。真正有效的是做难例挖掘:把现场误检的图片收集回来,补充到训练集里重新训练。这就是为什么数据集要持续更新,单靠一次性数据打天下的时代早就过去了。

5.2 从单帧检测到连续视频巡检的稳定性处理

无人机巡检拍的是连续视频,模型逐帧检测时会出现一个很现实的问题:这一帧框出来了,下一帧又没了,来回跳变。给养护部门看的成果如果忽有忽无,信任度会大打折扣。

我处理跳变会用两个办法。第一个是帧间重叠检测优化,因为航拍视频相邻帧重叠率高,同一病害在连续几帧里都会被检测到,统计每个病害在连续帧里出现的频次,出现次数低于阈值的直接丢弃,保留高频稳定出现的检测结果。第二个是对检测框中心点做轻量级平滑,用相邻帧的位置做均值滤波,防止单帧定位大幅跳动。

还有一个操作细节:病害如果连续几帧都被检测到,只需要保留一次检测结果并记录它的首次出现时间和持续帧数。这个信息对后续生成巡检报告很有用,一次病害的严重程度不能只看单张照片,连续多帧都被模型锁定,比单独一张图更有说服力。

5.3 输出给养护部门之前,你还需要做的一件事

检测模型输出的只是像素坐标系里的矩形框,养护部门拿到手没法直接派工。要让检测结果真正进入市政养护流程,必须把目标框映射到地理坐标。

无人机巡检影像本身就带POS数据,每一帧图像的位置、姿态都是已知的。检测到病害后,取目标框中心点像素坐标,结合本帧的位姿数据和相机参数,可以解算出病害对应的经纬度。再把经纬度输出成标准表格或工单系统能直接读取的格式,养护人员就能带着设备直接去现场复核修补。

这一步做不做,决定了整个巡检方案是“演示级”还是“生产级”。很多项目卡在最后一公里,模型会跑、检测会出,但成果落不了地,问题就出在坐标映射没有打通。我在项目里习惯把病害中心点的地理坐标作为必填字段,和类别、置信度、图像编号一起输出,宁可多花一天调坐标转换,也不让下游多跑一趟冤枉路。

最后分享一个我的习惯:模型训练完,我从来不看验证集报告直接收工,而是带着模型跑一段从没去过的新采集航拍视频,模拟一次完整巡检。连续帧上会不会跳变、拼接缝是否误报、细裂缝能不能锁住,这些问题只有到了这一轮盲测里才会现原形。数据集是起点,但这个起点能不能变成好用的模型,最终考验的还是从采集、标注到落地这一整条链路上有没有耐心把细节抠干净。

返回列表