首页 / 资讯中心 / 文章详情

基于YOLOv10的道路坑洼检测系统实战:从数据标注到UI界面部署

基于YOLOv10的道路坑洼检测系统实战:从数据标注到UI界面部署 ★ FEATURED ARTICLE
做道路巡检相关的视觉项目做久了我越来越觉得坑洼识别是个被严重低估的难题。公路上那些大小不一的坑洞白天看是黑色阴影一到雨天直接变成镜面反光再加上车辆穿插遮挡、树影晃动、施工痕迹干扰传统图像处理算法在这种场景下几乎一碰就碎。所以我现在最推荐的做法是直接用深度学习来做道路坑洼识别检测系统配合YOLOv10这类目标检测模型在成本、速度和精度之间找到一个非常实际的平衡点。这篇文章把我用YOLOv10从零搭起一套带UI界面的坑洼检测系统的完整过程拆开讲。你会看到数据集怎么做、标注有哪些讲究、训练参数该往哪个方向调、界面功能怎么组织以及最后真实路面上会遇到哪些调试资料上不会写的坑。无论你是想拿它做毕业设计、课程项目还是真的有道路巡检相关的业务需求这条链路都完全可以照抄复用。1. 道路坑洼检测为什么要从人工肉眼换成算法1.1 传统巡检的成本和遗漏先说个现实情况。早期做道路病害检测最常见的方式是人工巡检几个人开着车沿路看或者步行一段段检查看到坑洼就拍照、记录、养护。听起来简单但这里面的问题不少。首先是频率不够一条省道上百公里靠人工跑一遍可能要一两周中间下了场雨、过了一批重载车路面情况就完全变了。其次是漏检率高小坑洼在车速稍微快一点的时候几乎不可能被肉眼捕捉夜间和逆光条件下更难。还有一种做法是给巡检车装振动传感器通过加速度变化来推测路面平整度。这个方法能覆盖大范围但问题在于它只能告诉你这段路颠簸不能直接告诉你坑在哪、边界在哪、面积多大。养护部门拿到数据之后还是得再派人去现场确认整个链路被拉得很长。三维激光扫描能测出高精度断面可设备成本和数据处理成本高得吓人不是所有项目都能承受的。于是在这个背景下视觉方案就变得特别有吸引力。一个普通摄像头架在巡检车挡风玻璃后面或者装在无人机、路侧监控杆上就能持续采集画面。识别部分交给深度学习模型跑完直接输出坑洼位置和置信度。这套方案不需要改变车辆结构不需要昂贵传感器人工智能算法本身就能完成大部分识别劳动人力只需要在最后做复核就行。1.2 视觉检测方案的优势与真实难点视觉方案最核心的优势是高频巡检成了可能。同样的车和人员装上摄像头以后每天出车就能把固定路线扫一遍数据自动累积。日积月累还能形成同一路段在时间维度上的坑洼变化趋势这是人工巡检很难做到的。但真做起来坑洼检测并没有网上教程说的那么轻松。它的难点集中在这几个方面。第一是光照变化。晴天强光下坑洼边缘会有强烈的高光和长阴影阴天的时候坑洼和普通沥青路面之间的灰度差又很小。同一个坑在不同时段看起来就是两张图。第二是积水和反光。雨水填进坑里以后坑洼变成了一个反光镜面边缘信息完全被亮斑吃掉很多模型会在这一步直接漏检。等到积水退去坑里残留的黑色泥浆又会造成误检把普通的深色路面区域当成坑。第三是目标尺度差异大。一条路上可能有刚形成的5厘米小坑也可能有超过一米的严重塌陷。小目标在大分辨率图像里只占不到几十个像素模型捕捉很费力。第四是样本不均衡。正常完好路面的画面数量远远大于坑洼路面的画面数量。训练集如果随机抽样模型很快就会被正常路面洗脑变成只会输出低置信度结果的保守派。这些难点决定了坑洼识别不能靠几个固定规则写死必须用数据驱动的方式让算法自己去学习什么形状的边缘、什么纹理组合、什么上下文线索才属于坑洼。这就是深度学习模型入场的原因。2. YOLOv10在坑洼检测里的优势来源核心结构拆解2.1 从YOLOv8到YOLOv10关键变化不是参数数量很多同学看到YOLOv10的第一反应是又多了一个版本改改参数又出了个模型。但实际用过以后你会发现YOLOv10和YOLOv8之间的变化是结构性的不是简单堆层。先说影响最深的点YOLOv10把NMS去掉了。过去YOLO系列检测完以后必须接一个非极大值抑制后处理用来把同一个目标周围的重复框合并掉。这个操作涉及到IOU计算、排序、筛选不复杂但会增加推理延迟而且在这类后处理过程中相邻很近的目标容易被误删。YOLOv10引入了无NMS的训练范式用一致的匹配度量来给每个目标唯一的正样本分配推理时直接输出最终结果不需要再做NMS这一步。对于道路坑洼场景这个改进的价值很直接。坑洼经常是成群出现的一段破损路面可能有五六个坑连在一起互相之间的距离非常近。传统NMS在后处理时很可能把紧挨着的两个坑当成重复的同一个目标给合并了白白丢失信息。无NMS的结构允许模型在端到端训练中学到哪些框该保留、哪些该放弃保留相邻坑洼的能力强不少。YOLOv10里还有几个在工程上很实在的改动。一个是轻量化分类头分类分支不再和回归分支共享同样的计算量整体参数和计算量降了下来一个是空间通道解耦下采样在降低特征图分辨率的同时把空间信息融合和通道信息变换分开处理减少信息丢失还引入了大核卷积和部分自注意力机制让模型在特征提取阶段能获得更大的感受野。2.2 这些设计对坑洼识别的实际意义把这些技术名词翻译成坑洼检测的实际收益大概是这样的。消解后处理环节意味着推理流程简化。部署到实车上的时候模型推理完直接拿结果画框在Python层面省掉一段后处理时间对实时性有正面作用。尤其是在车内小主机上跑CPU资源本来就不宽裕省一点是一点。轻量分类头降低了整体计算量让同样大小的模型在边缘设备上跑得更快。道路巡检车上的主机往往不是顶配游戏电脑而是一块工控板或者车载电脑计算能力有限。YOLOv10-s在保证精度的前提下速度比同量级的YOLOv8-s有明显提升这就给了我们在精度够用的前提下选择更小型号的空间。特征提取侧的大感受野设计对坑洼识别更是一种隐性福利。坑洼不是一个孤立像素点它的周围通常伴随裂纹、碎裂边缘、松散颗粒等上下文特征。感受野更大的模型等于能看到更大的上下文范围在判断某个深色区域到底是坑还是普通柏油路时依据更充分。最后模型版本选择很灵活。YOLOv10提供了n、s、m、l、x五个规格。做实验可以用m或者l追求精度最终部署在车机上的时候切到s甚至n基本不需要改代码只换一个权重文件就行。这个特性对项目来说非常友好前期精度验证和后期性能优化可以解耦。3. 坑洼数据集远比模型更决定上限3.1 数据来源自采、公开集与增强补足做坑洼检测我有个很深的体会模型框架只要选熟一个就行真正决定效果上限的几乎全是数据。YOLOv10再强喂进去的数据没覆盖真实场景跑出来还是一塌糊涂。数据来源我一般分三路。第一路是自采数据用行车记录仪或手机固定在车前视角去拍不同路段的画面。采集的时候要有意识地覆盖天气和时间段比如晴天的上午下午、阴天、雨后、夜晚路灯照明条件下都收集一部分。这一路数据最关键因为它决定了系统在你实际使用环境里的表现。第二路是公开数据集。网上有不少路面病害、坑洼检测的开放标注数据集类别大多包含坑洼、裂缝、修补等。直接用它们的好处是量大、类别丰富能帮助模型学到不同路面材质下的坑洼形态。但要注意很多公开数据来自国外道路路面结构、标线样式、周边环境跟国内差异很大直接拿去训练模型会对陌生画面产生大量误检。所以这类数据只适合做预训练或辅助训练不建议当主力。第三路是数据增强。增强不是越多越好而是要针对坑洼场景做选择。亮度扰动、对比度扰动、小角度旋转、随机裁剪、仿射变换这几类对坑洼增强很有效。有一类增强要谨慎过度使用HSV颜色空间扰动会把路面变成不自然的颜色反而让模型学到错误特征。我自己的做法是亮度在上下30%范围扰动饱和度扰动控制在很小范围避免训练分布偏离真实路面。3.2 类别定义别贪多第一次做这个项目的时候我犯过一个典型错误把类别设计成坑洼、裂缝、网裂、松散、修补、隆起六类觉得越全越好。训练完以后发现mAP根本拉不上去原因很直接裂缝和网裂在图像上边界模糊标注一致性也差同一张图两个人能标出完全不同的框松散和隆起样品数量又极少类别之间严重不均衡。后面我砍到三类坑洼pothole、裂缝crack、修补块patch效果立刻稳定下来。如果只针对你标题里坑洼识别这一个目标甚至可以直接只做单类pothole模型专注度高训练收敛也快。我给的建议是第一版项目一定要让问题足够简单单类起步跑通后再考虑多类别扩展。3.3 标注的细节与格式转换标注工具我用过开源工具也有直接在云标注平台做的经历。开源工具纯本地数据不泄到外部对很多项目来说更可控。标注的时候有几个细节一定要强调。第一框要贴合凹陷区域的真实边界。很多人喜欢把坑的边缘放得宽一些多框一点周围路面觉得这样能增加上下文。这在小目标上影响尤其大因为框大了目标在框里占比变小模型学到的特征会被背景污染。第二雨水积满的坑洼如果肉眼还能判断出凹陷边缘就按凹陷边界标如果完全分辨不出建议直接放弃这样的样本而不是勉强标注一个模棱两可的框。模棱两可的标注会让模型训练信号变得混乱。第三相邻坑洼之间即使距离很近也一个坑一个框不要合并成一个框。前面讲过YOLOv10对相邻目标友好但如果标注阶段就合并了模型想学也学不到。标注完以后要转成YOLO格式的txt文件。每一行对应一个目标格式是类别的索引加上归一化后的中心点坐标和宽高import os import cv2 def convert_to_yolo(label_path, image_path, output_dir, class_map): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r, encodingutf-8) as f: lines f.readlines() out_lines [] for line in lines: # 这里按你标注工具导出的格式解析 parts line.strip().split() # 假设标注工具导出的是 x1,y1,x2,y2,class_name 的CSV格式 x1, y1, x2, y2 map(float, parts[:4]) class_name parts[4] cls_id class_map[class_name] cx ((x1 x2) / 2) / w cy ((y1 y2) / 2) / h bw (x2 - x1) / w bh (y2 - y1) / h out_lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(output_dir, os.path.splitext(os.path.basename(label_path))[0] .txt), w) as f: f.write(\n.join(out_lines))数据划分的时候还有一个容易踩的坑同一段路连续拍摄的相邻帧会非常相似如果随机划分相似帧会同时出现在训练集和验证集里导致验证指标虚高。正确的做法是按路段分组把同一路段的所有帧归到同一个集合里然后再划分训练验证集。4. 训练全流程4.1 环境准备训练环境其实不用太多花活。推荐用Python 3.10或者3.11PyTorch用当前稳定版本显卡驱动和CUDA版本对应好就行。运行YOLOv10最常见的方式是通过Ultralytics库加载所以先把基础依赖装好pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics然后按YOLOv10项目的方式安装模型相关依赖。这个细节很多人不注意Ultralytics主库版本更新很快不同版本对YOLOv10权重文件的兼容性可能有差异。我的建议是固定用一个经过验证的版本组合不要每天升级。训练不是非得上顶级显卡。我试过在8G显存的显卡上训练yolov10sbatch size调到8开启半精度照样能跑完整个流程。如果你的数据集只有几百到一两千张完全不需要担心算力不够的问题。4.2 数据配置与训练命令准备好数据之后要把数据集组织成固定目录结构datasets/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── pothole.yamlyaml文件是这个样子的path: datasets train: images/train val: images/val names: 0: pothole如果你把裂缝也算进去就改成三行类别。上面的结构是YOLO系列通用的YOLOv10直接喂这个配置就行。训练命令yolo detect train \ datapothole.yaml \ modelyolov10s.pt \ epochs200 \ batch8 \ imgsz640 \ patience30 \ ampTrue用 yolo detect train 入口时如果传的是模型权重文件它会在该权重基础上继续训练这相当于自动帮你用预训练权重初始化了对小数据集收敛非常有帮助。第一次训练我建议先用yolov10s.pt做预训练权重而不是从yaml结构随机初始化。数据量不够大的时候随机初始化很容易过拟合到几百张训练图上。4.3 值得细调的几个参数训练参数里最常被忽略的是imgsz。很多教程默认填640对坑洼这种小目标场景640往往不够。坑洼在原图上可能只有二三十像素模型下采样几轮之后特征图上的响应已经非常微弱。如果显存允许我建议尝试imgsz960甚至1280对小坑洼的召回率通常有明显提升。代价是训练时间翻倍显存占用变大batch size可能要跟着降。第二个要关注的是mosaic增强。Ultralytics默认在训练前10个epoch开启Mosaic把四张图拼成一张喂给模型。这对坑洼检测有个很关键的副作用四张图缩放之后小坑洼会被缩得更小细节信息直接被破坏。官方默认会在最后close_mosaic个epoch关闭它我通常把这个值调大到10甚至15让模型在最后阶段专门用原尺寸图精调。第三个是早停参数patience。坑洼数据集通常比较小几十个epoch就可能过拟合训练损失还在降但验证集损失开始反弹。patience30意味着连续30个epoch验证指标没提升就自动停能省下大量冗余训练时间。batch size的选择跟显存直接相关。8G显存跑yolov10s、imgsz640batch8是一个相对稳妥的值如果改成1280batch降到2到4。开启AMP混合精度训练之后显存占用能下降不少在支持的显卡上建议始终保持开启。4.4 从结果指标判断问题训练结束后判断模型好坏不能只看一个mAP。我习惯先看验证集PR曲线如果曲线右下角压得很低说明precision或者recall有明显短板。再看mAP50和mAP50-95的差值如果两者差距很大说明模型虽然能识别坑洼但预测框的定位不够准确跟真实边界贴合度差这种模型在实际画框时看起来会很别扭。还有一点经常被忽略检查验证集上的小目标表现。Ultralytics在验证结果里不会单独按尺寸分桶但你可以自己按标注框面积把验证集拆成小目标和大目标两组分别测试。多数情况下坑洼项目的瓶颈都集中在小的那组。如果小目标表现差优先考虑的办法不是盲目加训练轮数而是提升imgsz或者在标注时把大坑洼切块做成若干张小图参与训练。训练完成后模型文件会存在runs/detect/train的目录里weights文件夹下同时有best.pt和last.pt。实际部署用best.ptlast.pt只用于断点续训。5. 带UI界面的路况检测系统5.1 UI技术选型训练好模型之后离能用还差一步需要一个界面把模型包起来。市面上的选择不外乎三种PySide6/PyQt5桌面程序、Gradio快速Web界面、Streamlit数据应用框架。从部署形态说道路巡检场景大多是在车内使用或者接监控摄像头流桌面程序更合适。它不依赖浏览器不怕网络断开硬件接口调用也直接。Gradio和Streamlit更适合快速演示如果你想临时给甲方看个效果几分钟就能起一个网页。但对正式项目来说桌面程序的可定制性和稳定性都高不少。对比下来我个人选PySide6。它基于Qt官方更新维护许可证对开源项目友好视频画面的渲染、按钮交互、图标资源都用原生Qt控件做出来的界面观感比较工整。5.2 界面功能和布局一个实用的坑洼检测界面核心功能其实就是五个图片检测、视频文件检测、摄像头实时检测、结果保存、信息展示。界面布局不用花哨。左侧放一个大的图像显示区域用来展示原始画面和检测结果右侧放操作区模型路径选择、置信度阈值滑块、IOU阈值、开始和停止按钮、检测结果统计表格。底部可以放一个日志输出框把每次检测的帧率、目标数量、处理耗时打印出来。置信度阈值要设计成滑块而不是固定值。前面说过雨天反光场景下误检率明显上升这时把置信度阈值往上拉一点能滤掉大部分虚警晴天光线好的时候可以降低阈值提高召回率。部署时用户不会改代码但会拖滑块这个交互细节非常救命。结果导出要有。每张图片检测完用OpenCV把画了框的结果保存到指定目录视频检测完成后同时导出标注过的视频文件和每一帧检测位置的CSV记录包含时间戳、目标类别、置信度、中心点坐标。这个设计是为了后续对接养护管理系统用的有坐标记录才能按路段聚合统计。5.3 线程处理是界面流畅的关键很多第一次写界面的人会犯同一个错误把YOLO推理直接写在按钮点击的槽函数里。这样点击开始检测之后界面会整个冻结鼠标变成等待状态视频画面一顿一顿用户体验极差。原因是推理是耗时操作一帧推理少则几十毫秒多则几百毫秒如果在UI主线程里执行Qt的消息循环被阻塞就无法刷新界面和响应鼠标。解决办法是把推理放到独立线程界面线程只负责显示结果。PySide6里可以用QThread加信号槽实现from PySide6.QtCore import QThread, Signal import cv2 class DetectionWorker(QThread): frame_ready Signal(object, object) # 原始帧和检测结果 def __init__(self, model, source, conf_thres0.25): super().__init__() self.model model self.source source self.conf_thres conf_thres self._is_running True def run(self): cap cv2.VideoCapture(self.source) while self._is_running: ret, frame cap.read() if not ret: break results self.model.predict(frame, confself.conf_thres, imgsz640) annotated_frame results[0].plot() self.frame_ready.emit(frame, results) cap.release() def stop(self): self._is_running False界面这边连接信号拿到结果以后更新显示区。实时监控场景还要注意帧处理速度如果摄像头30帧而模型推理只能跑到10帧可以采取抽帧策略每隔两三帧处理一次其余帧直接丢弃保证画面流畅不积压。6. 真实场景实测与踩坑记录6.1 检测效果的直观感受模型训练完之后我用一段没有参与训练的实拍道路视频做了测试。晴天、阴天、雨后路面各选一段。晴天和阴天场景下中等以上尺寸的坑洼基本都能稳定框出来置信度在0.5到0.85之间波动明显破损的路面甚至能连续多帧保持高置信度。雨后的画面里部分积水坑洼因为边缘被水面反光冲掉确实出现了漏检但把置信度阈值从0.25降到0.15之后找回了一部分目标代价是偶尔会把路面上深色的新修补痕迹误判成坑洼。这其实反映了一个很本质的问题坑洼检测在计算机视觉里没有一个绝对完美的标准答案。同一个坑不同光照条件下呈现的特征完全不同。模型能做的是把置信度调整成可交互的旋钮让使用方根据当天天气灵活变化。6.2 踩过的坑和解决办法这一路走下来踩的坑不少我整理成表格方便对照。遇到的问题根本原因解决办法雨天把反射光斑识别成坑洼训练数据缺少积水反光样本增加雨天后路面采样配合亮度扰动增强小坑洼完全没检出imgsz太小小目标特征丢失推理和训练都改用960或1280分辨率图片训练中途显存溢出batch过大或开启了大分辨率batch降到4开启AMP必要时换更小型号验证mAP很高但实地框偏移同一路段帧混入训练和验证集按路段分组划分数据集重新训练视频检测时界面卡死推理阻塞了UI主线程改用QThread独立线程信号槽更新界面导出ONNX时模型报错Ultralytics版本与YOLOv10结构兼容性差异固定在一个验证过的版本组合不轻易升级最让我印象深刻的坑是验证集数据泄露。第一次训练出来的模型在验证集上mAP0.9我一度以为已经大功告成用行车记录仪新拍的视频一测表现稀碎。复盘的时候才发现原来在划分数据集时相邻帧被随机分配到了不同集合里模型在训练时已经见过验证集的几乎同款画面。教训就是时间序列图像做目标检测分区必须按路段时间块来。6.3 可以继续扩展的方向如果要把这个项目做成更完整的道路巡检方案下一步有几个很自然的扩展点。第一个是分割。坑洼的边界通常是不规则多边形目标检测输出的矩形框无法准确描述真实面积而面积恰恰是养护部门计算修复材料量的关键指标。换用YOLOv10的实例分割版本输出掩膜后可以直接按像素面积估算比矩形框精确得多。第二个是位置信息融合。检测系统如果接入GPS每一帧检出目标都能关联到经纬度坐标后续可以自动生成坑洼分布热力图和重点路段追踪列表这是从能检测到能管理的关键跨越也是真实道路养护项目中最有价值的输出。第三个是车载边缘部署。把模型导出成TensorRT或者放到边缘计算板卡上运行彻底摆脱电脑主机把整个系统做成一个独立的车载设备。YOLOv10轻量级的设计在边缘部署上有天然优势n和s版本可以在低功耗设备上轻松跑满实时帧率。最后分享一个我踩过不少次后养成的习惯每次训练完别急着看mAP先把验证集里的假阳性图片一张张翻一遍把误检图片按原因归个类。是反光问题就补反光样本是排水井盖误检就在样本里加入大量井盖负样本是标线边缘误判就要考虑把标线清晰的卡片式图片补充进去。这个手动浏览误检的过程比盯着loss曲线反复猜原因高效得多也是判断一个检测系统能不能真正落地的最快路径。
阅读完成 · 觉得有帮助?
咨询建站