尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv5路面状态检测实战:结冰/雪地/湿滑数据集训练与部署

YOLOv5路面状态检测实战:结冰/雪地/湿滑数据集训练与部署 简介面向智能交通与自动驾驶场景的雨雪天气路面状况数据集覆盖结冰路面、雪地、雨天湿滑及干燥路面等常见路况可用于训练路面状态识别目标检测模型。压缩包共1293个文件包含646张原始jpg路面图片、646个对应txt标注文件及1个yaml配置文件整体大小约26.98MBtxt文件记录YOLOv5格式的目标框坐标yaml用于配置数据集类别与路径。目前已有707人学习下载适合自动驾驶、道路养护、智慧安防等方向的算法工程师与研究者也可作为计算机视觉初学者的项目练习数据。所有图片均为原始路面图片文件名与标签一一对应便于按场景筛选和整理结合yaml配置即可快速接入YOLOv5训练流程省去手动格式转换和标注整理环节。相较于自行采集标注这份数据能显著降低雨雪天气样本的获取成本为模型在恶劣天气下的鲁棒性提升提供直接支撑。1. 结冰路面识别不是配色题这个YOLOv5数据集怎么用下雨天跑在路上判断路面是湿滑还是结冰靠的不是颜色而是纹理和反光。这组数据集的特别之处在于把四类路面状态——结冰路面、雪地、下雨湿滑、干燥路面——用原始图片配YOLOv5格式的txt标签打包拿到手就能直接送进YOLOv5训练管线训练一个路面状态检测器。和网上那些分好类的图片库不同这份数据保留了原始分辨率和YOLOv5的标记格式适合用来做自动驾驶感知、冬季道路养护巡检、结冰预警等场景。如果你对YOLOv5网络结构和训练自己的数据集还没完全跑通拿这个场景练手正好。2. 数据集结构与YOLOv5标注格式从原始图片到txt标签2.1 四类路面状态怎么定义与区分一个常见误解是结冰路面看颜色实际上黑冰和湿滑路面在视觉上非常接近。冰面反光更均匀雪地纹理更粗糙干燥路面有颗粒感下雨湿滑路面则有明显的镜面反光。这份数据集用YOLOv5标记时类别定的是这四类但标注的时候容易出现边界模糊尤其是结冰路面和下雨湿滑在夜间灯光下几乎难以区分。所以拿到数据后第一件事不是直接训练而是把标签过一遍看有没有标错的框。从项目正文里的文件名可以看出图片命名类似ice-road31_jpg.rf.647734b2a0c45eadc7006abc92df2e1c.jpg其中的.rf.是Roboflow导出时常见的命名标记。这类文件名的数据集通常已经按YOLOv5的标准目录结构整理过images/和labels/两个大类下面再分train/和val/。如果手头只有图片和同名txt需要自己补成这个结构。另一种情况是下载包只有图片和标注文本没有划分训练集和验证集这时我会用下面的方式按 8:2 随机划分避免同类图片全堆在一侧。import os import random import shutil image_dir ice_road/images label_dir ice_road/labels for split in [train, val]: os.makedirs(fice_road/images/{split}, exist_okTrue) os.makedirs(fice_road/labels/{split}, exist_okTrue) images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.seed(42) random.shuffle(images) val_cnt int(len(images) * 0.2) for i, img_name in enumerate(images): split val if i val_cnt else train label_name img_name.rsplit(., 1)[0] .txt shutil.move(os.path.join(image_dir, img_name), fice_road/images/{split}/{img_name}) shutil.move(os.path.join(label_dir, label_name), fice_road/labels/{split}/{label_name})这段代码的意义在于训练集和验证集不能只按文件序号硬切。雨雪天气有很强的场景连续性比如同一路段连续几帧画面如果全落在训练集或验证集评估结果会虚高。先随机打乱再划分至少能避免连续帧都进同一侧。val_cnt用图片总数乘以0.2取整种子固定为42保证每次复现的结果一致。2.1.1 类别边界容易混淆的地方四类路面状态并不是天然可分的。干燥路面在逆光下会局部反光看起来像湿滑薄冰刚形成时透明度高完全能看到沥青纹理更像下雨湿滑而不是结冰。标注时如果只看单张图很容易写错class_id。我的习惯是训练前用图片查看器把同一文件名对应的txt渲染出来重点检查ice和wet两类框是否覆盖了相同区域。2.2 YOLOv5标注文件解析YOLOv5的标签不是用矩形坐标而是归一化后的中心点加宽高。每行对应一个目标框格式如下cls_id x_center y_center width height五个值都用空格分隔x_center、y_center、width、height都是0到1之间的浮点数除以图片宽高得到归一化坐标。cls_id从0开始对应yaml里的names列表顺序。下面这段代码可以快速扫描一个标签目录统计四类目标数量同时检查有没有越界框import os from collections import Counter label_dir labels/train counter Counter() broken [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: broken.append(fname) continue cls int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) width float(parts[3]) height float(parts[4]) if not (0 x_center 1 and 0 y_center 1): broken.append(fname) if not (0 width 1 and 0 height 1): broken.append(fname) counter[cls] 1 print(类别统计:, sorted(counter.items())) print(异常文件:, set(broken))这段代码的逻辑是逐行读取txt按空格拆出5个字段把类别ID取整、坐标转浮点然后检查中心点坐标和宽高是否落在0到1区间内。YOLOv5训练时如果标注框越界不一定报错但会影响anchor匹配导致loss不下降。我一般在跑训练前都会做一次这个检查尤其从网上下载的数据集标签经常有坐标越界或格式不完整的行。2.3 数据分布与训练前检查以下是一份路面状态数据集的分布示意实际数字要根据你的标签统计类别ID名称视觉特征易混淆对象0干燥路面纹理清晰色调偏灰阴影下的雪地1下雨湿滑镜面反光颜色加深薄冰覆盖路面2雪地白色带纹理边界模糊亮光下的干燥路面3结冰路面反光均匀半透明湿滑路面、玻璃质感如果是这种分布训练时优先让模型学会区分干燥路面和湿滑路面因为这两类在雨天夜间光照下最容易误判。另外还要检查图片的EXIF方向信息手机拍摄的原图有时带旋转标记YOLOv5在LoadImages阶段默认用OpenCV读取不会自动处理EXIF旋转导致标签坐标和实际画面错位。遇到这种情况先用以下命令把所有图片转正再训练。echo no exif orientation issue但这条命令并不能解决EXIF旋转真正可靠的做法是用PIL里的ImageOps.exif_transpose批量转正from PIL import Image, ImageOps import os for fname in os.listdir(ice_road/images): if not fname.lower().endswith(.jpg): continue path os.path.join(ice_road/images, fname) img Image.open(path) img ImageOps.exif_transpose(img) img.save(path, quality95)这段代码把每张原图按EXIF里的方向字段转正后覆盖保存。YOLOv5的标签坐标是按转正后的图像标注的如果图片没有转正模型看到的物体和框的位置会整体偏移mAP表现会非常奇怪。3. 用YOLOv5训练自己的数据集环境、yaml配置与训练命令3.1 数据集yaml怎么写YOLOv5官方仓库训练前要准备一个数据集配置文件指向图片目录并声明类别。路面状态这个任务可以直接写成这样train: ./datasets/ice_road/images/train val: ./datasets/ice_road/images/val nc: 4 names: 0: dry 1: wet 2: snow 3: icetrain和val指向图片目录YOLOv5会自动把路径中的images替换成labels去加载标注。nc是类别数量必须和names长度一致。names的顺序要和标签txt里的 class_id 严格对应。我见过有人把names写成了[ice, snow, wet, dry]但标签里类别0是干燥路面模型训练全程loss表现很奇怪最后才发现是顺序错了。3.2 训练命令与关键超参数数据准备好后训练命令我一般直接用官方仓库的train.pypython train.py \ --data ice_road.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache \ --device 0几个关键参数的解释--data指向刚才写的yaml--weights用COCO预训练权重路面状态识别属于底层外观特征分类迁移学习比从头训练收敛快得多--img是输入分辨率路面场景不需要识别极小的障碍物640足够原图如果很大可以试1280但显存占用会翻倍--batch根据GPU显存调整显存不够就减半--cache在训练前把图片一次性加载进内存IO慢的机器上能明显提升训练速度。以下是YOLOv5默认超参数和推荐调整范围可以直接当作参照超参数默认值推荐范围说明lr00.010.005-0.01初始学习率数据量少时建议偏低lrf0.010.01-0.1最终学习率因子决定后期微调幅度momentum0.9370.8-0.95SGD动量太大会震荡cls0.50.3-1.0分类损失权重样本不均衡时调高box0.050.02-0.1边框回归损失权重这些超参数在data/hyps/hyp.scratch-low.yaml里修改。对道路场景我一般会把cls提到0.7因为路面类别之间非常容易混淆分类损失权重太低模型会偏向框回归而忽略类别判断。训练时也可以加--multi-scale让YOLOv5在训练中随机变换输入分辨率对雨雪天气下远近不同的路面目标泛化更好。如果显存不够常见做法是--batch 8 --img 416先跑一版基线确认流程没问题后再换--img 640 --batch 16正式训练。还可以加--freeze 10冻结前10层主干只训练检测头适合数据量只有几百张的情况。冻结层数越多训练越快但特征提取能力越弱冻结超过10层一般不值得。3.3 训练过程中的loss观察训练启动后不要只盯着终端要看runs/train/exp*/results.csv里的train/box_loss、train/cls_loss和val/cls_loss。正常情况是前10轮下降很快后面慢慢收敛。如果val损失在20轮后开始反弹说明过拟合了先把epochs降下来或者加大数据增强。路面状态数据的增强和普通目标检测不完全一样hsv_h、hsv_s这类颜色扰动可以适当调低因为雨雪和冰面本身靠颜色纹理区分颜色被随机改过后模型反而学不到真实特征。3.3.1 最容易踩的路径错误YOLOv5对路径很敏感。很多人把标签和图片放在不同命名的目录里比如图片在images/train标签却叫label/train。YOLOv5只认labels这个目录名差一个字母会直接零训练因为找不到标签文件。训练前用下面这条命令验证一下目录是否匹配ls datasets/ice_road/images/train | head -3 ls datasets/ice_road/labels/train | head -3两条命令输出的文件名应该一一对应。如果发现一个目录有jpg而另一个没有对应的txt说明标注文件缺失需要回到第2章的脚本检查。4. 训练结果评估与雨雪天气的坑mAP、混淆矩阵与失败样本4.1 从results看收敛质量训练结束后runs/train/exp/下会生成results.png和confusion_matrix.png。不要只看mAP还要看验证集的P和R。路面状态数据里如果P高而R低说明模型宁可少报也不误报这对结冰预警来说很危险——漏报比误报更致命。如果R高而P低误报太多比如雨天把干燥路面标成湿滑下游系统会被频繁告警打扰。我一般优先保证R。下面这条命令用于在验证集上重新评估权重python val.py \ --weights runs/train/exp/weights/best.pt \ --data ice_road.yaml \ --task val \ --conf-thres 0.25 \ --iou-thres 0.45--conf-thres是置信度阈值低于它的框会被过滤掉。日志输出里会给出每个类别的mAP0.5。此时如果某个类别的AP明显低于其他类别先别急着加数据去看混淆矩阵大概率是视觉特征相近导致分类错误。4.2 混淆矩阵里看易混类别路面状态检测的混淆矩阵通常会出现这样的情况示意数据实际\预测drywetsnowicedry78832wet769113snow42851ice518267从这个表能看到wet和ice互相串得厉害原因就是两类都存在反光而且下雨天路面湿度变化很快标注时前后帧可能分别标成不同类别。遇到这种情况我会回到原图上查看同一段路不同时间的照片确认是不是标签不一致。如果标签没问题再考虑调高cls权重。4.3 数据不均衡与类别权重调整如果某一类样本只有其他类的三分之一可以在训练时给少数类更高的分类损失权重。YOLOv5没有直接的类别权重参数常见做法是在训练脚本里对分类损失加权# 假设 cls_losses 是每个类别分类损失的张量 cls_power [1.0, 1.0, 0.5, 1.5] # 对应 dry, wet, snow, ice weighted_cls sum( c * w for c, w in zip(cls_losses, cls_power) ) / sum(cls_power)这段代码把各类别损失乘以一个权重再归一化。权重越高的类别参数更新时越受重视。但要控制幅度权重差太大会让低权重类别几乎不学mAP整体下降。我一般把最大权重控制在1.5以内然后观察val集的R是否回升。另外results.png里的val/cls_loss如果到后期一直抖动多半是标注边界不一致。YOLOv5网络结构对这种细粒度纹理变化很敏感训练到后半程可以增加--hyp参数并关闭mosaic增强用--no-mosaic跑最后20轮减少合成图片对路面纹理的干扰。4.4 置信度阈值如何选择不同应用场景要卡不同置信度阈值。在混淆矩阵里降低--conf-thres会提升召回但wet假阳性增多提高阈值则相反。部署前建议做一次阈值扫描记录不同阈值下各类别的F1阈值mAP0.5wet的F1ice的F10.150.810.630.710.250.820.680.730.450.790.660.70上面表格是示意数据实际值要用验证集跑出来。重点在于结论阈值不是越高越好wet和ice这类难分样本会在高阈值下大量丢失导致路面状态切换的检测延迟变大。部署道路预警系统时我更倾向于低阈值加后续时序滤波而不是靠单帧高阈值硬刚。5. 把模型接到视频流和边缘设备TensorRT导出与多帧投票5.1 导出TensorRT引擎训练好的best.pt直接跑视频推理在CPU上很慢。如果目标是道路摄像头实时检测我会先把权重导出成TensorRT引擎python export.py \ --weights runs/train/exp/weights/best.pt \ --include engine \ --imgsz 640 \ --device 0导出后得到.engine文件推理速度比PyTorch原图快2到3倍。--imgsz必须与训练尺寸一致否则精度会掉。导出时要注意TensorRT版本和YOLOv5仓库版本匹配版本差太多会报engine加载失败。5.2 多帧投票减少单帧误检路面状态是连续物理量单帧误检很正常。我一般用滑动窗口做多帧投票取最近5帧类别出现次数最多的作为最终状态只有一致帧数达到阈值才触发切换。from collections import Counter WINDOW 5 state_history [] def decide(state_list): counter Counter([s for s in state_list if s is not None]) if not counter: return None most, cnt counter.most_common(1)[0] return most if cnt 3 else None # 每帧推理后调用 state_history.append(pred_class) if len(state_history) WINDOW: state_history.pop(0) final_state decide(state_history)这段代码把最近5帧的预测结果统计一遍超过3帧一致才输出最终状态。代价是状态切换最多延迟5帧但能滤掉大部分单帧闪烁。雨雪天气视频里这种时序滤波比单纯调高置信度阈值更可靠。本文还有配套的精品资源点击获取
返回列表