
简介这份雨雪天气路面状况数据集面向自动驾驶感知、道路巡检与计算机视觉方向的学习者和开发者用于训练和验证结冰路面、雪地、下雨湿滑与干燥路面四类场景的识别模型适合具备一定目标检测基础、需要真实道路样本进行实验或课程项目的人群。压缩包共1293个文件包含646张原始jpg图片、646个同名txt标注文件以及1个yaml配置文件整体约26.98MB图片均保留原始分辨率标注采用YOLOv5格式可直接接入YOLOv5训练流程yaml文件用于定义类别与数据路径。目前已有709人学习下载说明该数据集在路面状态识别任务中具有一定参考价值。读者可据此快速搭建四分类检测实验省去自行采集与标注的成本同时借助真实雨雪场景样本检验模型在湿滑、结冰等复杂路况下的泛化能力为后续调参与部署提供可靠的数据基础。1. 雨雪天气路面状况数据集从四类路面到 YOLOv5 可训练标签的完整链路冬季自动驾驶和道路巡检项目里最容易被低估的一环不是模型结构而是路面状况数据。干燥、湿滑、积雪、结冰这四种状态在视觉上高度相似尤其结冰路面和湿滑路面在阴天或逆光下几乎只差一层反光纹理。我见过不少团队直接拿通用目标检测数据集去训结果模型把结冰识别成湿滑把积雪识别成过曝上路就是玄学。这个标题讲的是一份按四类路面状况标注、保留原始图片、并用 YOLOv5 格式落地的数据集它解决的是「路面状态分类 检测」这个具体任务里数据从哪来、标签怎么对齐、训练怎么不翻车的问题。适合做道路巡检、冬季辅助驾驶、机场跑道监测的工程师也适合刚接触 YOLOv5 训练自己数据集、想找一个真实场景练手的人。下面我按数据拿到手之后的真实顺序把校验、转换、训练、排查整条链路拆开讲。2. 四类路面状况的标注边界为什么结冰和湿滑最容易标混2.1 四类路面的视觉特征与判定标准拿到一份路面状况数据集第一件事不是急着转格式而是先确认标注口径。干燥路面通常纹理清晰、无连续反光带、颜色偏灰白或沥青黑下雨湿滑路面有连续水膜会出现天空或车灯的镜面反射但反射区域边缘相对柔和雪地路面覆盖松散或压实雪层整体亮度高、纹理颗粒粗结冰路面则是透明或半透明冰层反光锐利、常伴随裂纹和车辙压痕局部会出现高光溢出。这四类里干燥和雪地靠颜色和纹理就能分开真正难的是湿滑和结冰。我的经验是看反光边缘湿滑的水膜反光是漫反射叠加边缘过渡宽结冰是镜面反射高光边缘硬、常出现条带状亮线。如果数据集里这两类标注本身就有歧义后面训练再调参也救不回来。路面类型主要视觉线索易混类型判定优先级干燥纹理清晰、无连续反光湿滑低湿滑连续水膜、柔和反光结冰中雪地高亮、颗粒粗、覆盖感干燥薄雪中结冰锐利镜面反光、裂纹车辙湿滑高提示如果标注规范里没写清结冰和湿滑的优先级建议自己补一条——出现锐利镜面高光且伴随裂纹时判结冰只有连续水膜无硬边高光时判湿滑。2.2 原始图片保留意味着什么标题里「图片均采用原始图片」这句话实际价值比看起来大。很多公开数据集为了减小体积会做统一缩放或裁剪路面反光这种高频细节一旦被重采样就丢了结冰的锐利高光会被抹成一片模糊模型学到的就是错的纹理。保留原始分辨率意味着你可以自己决定输入尺寸也意味着数据增强时不会在已经损失过的像素上再损失一次。但原始图片也带来两个现实问题一是体积大加载和训练时 IO 容易成为瓶颈二是分辨率不一致直接送进 YOLOv5 会被统一 resize 到 640长宽比差异大的图会变形。常见做法是先统计所有图片的宽高分布再决定 letterbox 还是直接 resize。我一般会写个脚本先摸清楚数据底细而不是凭感觉开训。import os from PIL import Image from collections import Counter img_dir raw_images sizes Counter() for name in os.listdir(img_dir): if name.lower().endswith((.jpg, .jpeg, .png)): with Image.open(os.path.join(img_dir, name)) as im: sizes[im.size] 1 # 打印出现频率最高的 10 种分辨率判断是否需要统一 letterbox for size, count in sizes.most_common(10): print(size, count)这段脚本只做一件事统计原始图片的分辨率分布。im.size返回(宽, 高)most_common(10)让你快速看出数据是集中还是散乱。如果前几种分辨率就覆盖了绝大多数图片说明采集设备统一可以直接定一个输入尺寸如果分辨率五花八门就要在数据加载阶段用 letterbox 保持长宽比避免结冰高光被拉伸变形。3. 把原始图片转成 YOLOv5 标签目录结构与转换脚本3.1 YOLOv5 数据集的目录约定YOLOv5 对目录结构有固定预期不按这个来训练时最常见的报错就是找不到标签或类别数对不上。标准结构是 images 和 labels 平行存放train 和 val 各自一套标签文件名和图片文件名一一对应只是扩展名换成.txt。dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── 0002.jpg │ └── val/ │ ├── 0003.jpg │ └── 0004.jpg ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ └── 0002.txt │ └── val/ │ ├── 0003.txt │ └── 0004.txt └── data.yaml标签文件每行格式是class_id x_center y_center width height后四个值都是相对图片宽高的归一化值范围 0 到 1。这里最容易翻车的是有人直接写像素坐标训练不报错但 loss 一直不降因为坐标全超过 1 了。3.2 从标注文件生成 YOLO 标签的转换脚本假设原始标注是常见的 XML 或 CSV里面存的是绝对像素坐标。下面这个转换脚本处理 XML 格式把每个框转成归一化坐标并写入对应 txt。import os import xml.etree.ElementTree as ET from PIL import Image # 类别顺序必须和 data.yaml 里的 names 完全一致 classes [dry, wet, snow, ice] class_map {name: i for i, name in enumerate(classes)} def convert(xml_path, img_path, out_path): tree ET.parse(xml_path) root tree.getroot() with Image.open(img_path) as im: w, h im.size lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并限制在 0~1防止标注越界 xc min(max((xmin xmax) / 2 / w, 0), 1) yc min(max((ymin ymax) / 2 / h, 0), 1) bw min(max((xmax - xmin) / w, 0), 1) bh min(max((ymax - ymin) / h, 0), 1) lines.append(f{class_map[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) # 批量处理 for split in [train, val]: img_dir fdataset/images/{split} xml_dir fraw_annotations/{split} out_dir fdataset/labels/{split} os.makedirs(out_dir, exist_okTrue) for name in os.listdir(img_dir): stem os.path.splitext(name)[0] xml_path os.path.join(xml_dir, stem .xml) if os.path.exists(xml_path): convert(xml_path, os.path.join(img_dir, name), os.path.join(out_dir, stem .txt))关键点在class_map的顺序它必须和data.yaml里的names列表逐位对应否则模型学到的类别会整体错位。归一化时用min(max(...))做钳制是因为原始标注偶尔会有框超出图片边界的情况不处理的话 YOLOv5 会警告甚至产生异常梯度。:.6f保留六位小数足够精度又不会让文件过大。3.3 data.yaml 的写法与类别数核对path: ./dataset train: images/train val: images/val nc: 4 names: [dry, wet, snow, ice]nc必须等于names的长度也等于转换脚本里classes的长度。这三处任何一处不一致训练时要么报维度错误要么静默把某一类当成背景。我习惯在开训前用一行命令核对标签里的最大类别 id。cat dataset/labels/train/*.txt | awk {print $1} | sort -u输出应该是 0 到 3。如果出现 4 或更大说明转换时类别映射写错了回去查class_map。4. YOLOv5 训练四类路面参数怎么设、指标怎么看4.1 从预训练权重起步的最小训练命令路面状况数据集通常不会特别大从零训容易过拟合常见做法是加载 COCO 预训练权重做迁移学习。下面是最小可跑通的命令。python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data dataset/data.yaml \ --weights yolov5s.pt \ --project runs/train \ --name road_surface--img 640是输入尺寸如果原始图片分辨率很高且结冰细节重要可以提到 960但显存占用会明显上升。--batch 16在单卡 8G 显存下比较稳显存不够就降到 8。--weights yolov5s.pt用 s 版本起步速度快、适合先验证数据质量确认没问题再换 m 或 l。--epochs 100对四类任务通常够用但要看验证集 mAP 是否还在涨。4.2 超参数里真正影响路面识别的几个YOLOv5 的超参数文件里和这个任务最相关的是这几项参数默认值路面任务建议原因lr00.010.005~0.01迁移学习时略低更稳hsv_v0.40.3~0.4亮度增强模拟阴天逆光hsv_s0.70.5路面颜色信息重要别过度扰动mosaic1.01.0四类拼接能增加同框对比fliplr0.50.5路面左右对称安全hsv_s我一般会调低因为干燥和湿滑的区分部分依赖颜色饱和度增强太猛会把水膜的颜色特征搅乱。mosaic保持开启它把四张图拼成一张能让模型在同一张图里同时看到结冰和湿滑对区分易混类有帮助。4.3 训练日志里该盯哪几个指标开训后不要只看 loss。YOLOv5 的results.txt和results.png里重点看metrics/mAP_0.5和每一类的 AP。如果整体 mAP 在涨但ice类的 AP 明显低于其他三类基本可以确定是结冰和湿滑标混了回去查标注而不是调参。# 训练结束后单独看每一类的指标 python val.py --data dataset/data.yaml --weights runs/train/road_surface/weights/best.pt --task testval.py会输出每一类的 P、R、mAP。ice类召回低说明漏检结冰精确率低说明把别的类误判成结冰。这两个方向对应的处理完全不同漏检要补结冰样本误判要清理湿滑里的错标。5. 避坑与排查路面数据集训练最常见的五个翻车点5.1 现象loss 正常下降但 mAP 一直是 0原因通常是标签路径不对。YOLOv5 会根据图片路径自动推导标签路径把images替换成labels、扩展名换成.txt。如果目录不是标准结构它找不到标签就把所有图当负样本训loss 会降但学不到任何框。解决用--data指向的 yaml 里path和train拼出来的路径手动确认标签文件真实存在。或者跑一次train.py看它打印的Scanning labels数量是否和图片数一致。5.2 现象结冰类 AP 始终上不去原因多半是结冰和湿滑的标注边界模糊或者结冰样本本身太少。四类里结冰采集难度最大数量往往最少类别不平衡会让模型偏向多数类。解决先统计各类别框数量如果结冰明显偏少用--weights做类别加权或者对结冰样本做针对性增强旋转、亮度扰动。更根本的是回到标注把湿滑里带锐利高光的图重新判一遍。5.3 现象验证集指标好实车或实拍效果差原因是训练集和实际场景的域差异。数据集里的路面可能是固定机位、固定天气实际部署时角度、光照、镜头都变了。解决在验证集里刻意留一部分不同机位或不同时段的图不要全用同源数据。如果条件允许采集一小批目标场景的图做微调哪怕只有几十张效果提升也很明显。5.4 现象训练时显存溢出或速度极慢原因是原始图片分辨率过高dataloader 在 resize 前就把大图读进内存。保留原始图片的代价就在这里。解决用--img控制输入尺寸同时在 dataloader 里设置--workers合理值一般 4 到 8。如果 IO 是瓶颈可以预先离线生成一份 640 或 960 的缓存副本用于训练原始图保留做验证和回溯。5.5 现象同一张图里干燥和湿滑同时被检出原因是标注时把过渡区域拆成了两个框或者模型对边界不敏感。路面状况在空间上往往是渐变的一张图里可能前半段干、后半段湿。解决明确标注规则——按主导状态标一个框还是按区域拆多个框。如果拆多个框要保证框之间不重叠太多否则 NMS 后会出现重复检测。我一般建议按主导状态标减少歧义。6. 进阶技巧用混淆矩阵反查标注质量与类别阈值训练跑通之后真正能帮你持续提升的不是继续调参而是把验证结果反过来当标注质检工具。YOLOv5 的val.py支持输出混淆矩阵这个矩阵能直接告诉你哪两类在互相误判。python val.py \ --data dataset/data.yaml \ --weights runs/train/road_surface/weights/best.pt \ --conf-thres 0.25 \ --iou-thres 0.5 \ --plots跑完会在输出目录生成confusion_matrix.png。看这个矩阵的方法对角线是正确分类非对角线是误判。如果wet行ice列的值很高说明大量湿滑被误判成结冰回去查这些图的标注大概率是结冰标多了。反过来ice行wet列高就是结冰漏标或被标成湿滑。--conf-thres这个阈值值得单独调。默认 0.25 偏宽松路面任务里如果误检多可以提到 0.4 再看混淆矩阵观察误判是否集中在低置信度区域。如果提高阈值后误判大幅减少说明模型其实分得开只是阈值太低如果提高阈值后正确检测也掉得厉害那就是特征本身不够得回到数据。我自己的习惯是每轮标注修订后都跑一次混淆矩阵把它当成标注质量的后悔药。有一次项目里结冰 AP 卡在 0.6 上不去调了两天超参数没动静最后看混淆矩阵发现ice和wet互相误判占了误检的七成回去重新判了三百多张图AP 直接到 0.82。参数能解决的问题其实有限数据里的歧义才是黑匣子。这套流程跑下来一份原始图片加 YOLOv5 标签的路面数据集从校验到训练到反查基本能形成闭环。希望帮到你。本文还有配套的精品资源点击获取