
简介一套面向路上障碍物检测场景的YOLO格式目标检测数据集适用于自动驾驶、辅助驾驶与道路安全监控等方向可直接用于训练YOLOv5等主流检测模型免去数据清洗和格式转换的额外工作。压缩包共2000个文件主要由1910个txt标签、89张jpg图像和1个Python可视化脚本构成整包约124.8MB目录按训练集和验证集划分清晰。目前已有151人学习适合需要现成道路障碍物数据集的检测初学者或项目开发者使用。数据包含障碍物、小动物、路障、减速带4个类别训练集1337张图像及对应txt标签验证集572张图像及对应txt标签全部为640×640分辨率的RGB图片标注采用YOLO相对坐标格式边界框完整且每图含多个目标txt文件包含类别与坐标信息可视化脚本可直接运行随机传入一张图片即可绘制边界框并保存结果便于快速检查标注质量。1. 路上障碍物检测数据集与 YOLO 的 4 类别划分为什么不能只下载一个 COCO 子集想象你正在做一个园区巡检的移动机器人摄像头对着路面识别对象只有行人、汽车、自行车、摩托车四类还要能离线跑。很多人直接去 COCO 里筛出这四类图结果发现类别不平衡、分辨率参差不齐、训练验证划分还漏了同一视频的相邻帧最终 loss 降不下去。这份标题里的「划分好的数据集 class 文件 数据可视化脚本」解决的是 YOLO 目标检测流程里最容易被低估的一步数据进入网络之前目录、类别顺序、坐标格式是否自洽。别急着调网络结构先把数据集当成代码仓库一样审查一遍下面用 4 类路上障碍物检测作为例子把这套结构讲清楚。2. 4 类别障碍物训练集的目录设计与 class 文件规范2.1 YOLO 数据集目录结构与标注 txt 的坐标含义一个能被 YOLO 直接训练的数据集不是「一个文件夹里堆满图」。以最常用的 YOLOv8 / YOLO11 为例目录通常长这样road_obstacle/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── classes.txt # 类别文件一行一个类别 └── dataset.yaml # 用于训练的配置images 和 labels 下必须同名同后缀的图片与 txt 文件图片0001.jpg对应labels/0001.txttxt 里每一行代表一个检测框。这里要特别强调YOLO 的标注不是 VOC 的像素坐标而是归一化后的中心点坐标。拿路上障碍物里的汽车举例图片宽度 1920盒子的左上角和右下角像素坐标为(100, 200)和(500, 600)那么x_center (100500)/2 / 1920 0.15625y_center (200600)/2 / 1080 0.37037width (500-100)/1920 0.20833height (600-200)/1080 0.37037。对应的 txt 文件内容是1 0.15625 0.37037 0.20833 0.37037最后一个隐藏规则是文件里不能有空行也不能缺少某个盒子。无论用 CVAT 标注还是把 KITTI 等数据集转成 YOLO 格式最终落盘必须满足「一行四个空格分隔浮点数 换行」的严格结构。逻辑说明第一列1表示类别 id后续四列依次是x_center、y_center、width、height均除以图片宽高的归一化值。图片在训练时会被缩放但标签坐标仍然相对原图归一化所以 YOLO 在训练内部会按 letterbox 后的比例换算。因此千万不要在准备标签时额外乘以 1000 或保留整数归一化到[0,1]是唯一正确写法。参数说明归一化后的小数精度一般保留到六位小数。过长的浮点数会增大文件体积而四舍五入到 3 位可能在 4K 图上造成几十像素的偏移建议统一用round(value, 6)处理。2.2 class 文件决定了类别顺序和模型输出层的含义标题里提到的「类别 class 文件」很多初学者以为是纯文档实际上它的行号就是模型输出层的索引。classes.txt 写成person car bicycle motorcycle意味着检测头输出的 4 个类别通道里第 0 通道永远映射到 person第 1 通道映射到 car以此类推。这里有两个隐蔽问题。第一个问题是数据集里实际没有 bicycleclass 文件中依然必须保留 bicycle 那一行否则 val 计算 mAP 时类别数对不上。第二个问题是在多来源数据合并时A 来源按car, person顺序B 来源按person, car顺序合并前必须把所有 txt 第一列的 id 重映射不然可视化脚本里 person 会画成 car 的框训练 loss 也会一路乱走。排查顺序和 class 文件是否一致最省事的办法不是人眼去翻 txt而是拿可视化脚本随机抽一批图把预测框上显示的类名和实际物体对照。如果显示的全部错位大概率不是标注画错而是 class 文件顺序和标注 id 没对齐。我一般会在完成数据集准备后立刻用脚本打印一张抽样图这张图里每个框的颜色和类名由继承关系决定一眼就能看出顺序问题。2.3 train/val/test 划分比例与随机种子划分好的数据集不是随便把文件拖进三个文件夹。障碍物检测必须考虑同一条路上的连续帧如果同一辆车出现在训练和验证里面验证分数会被高估跑上线后才发现泛化不行。常见做法是先按时间戳或视频片段分组再对组做随机切分而不是对单张图片切分。一个相对稳妥的比例是数据集比例典型图片量用途train70%2800训练与剪枝val20%800训练中做早停和调参test10%400最终指标评估训练时不触碰以上比例对应 4000 张左右的障碍物数据集。如果只有 800 张建议把 val 压到 15%保证 train 有足够数据。凡是用 Python 脚本划分一定固定 random seed最好把 seed 写进脚本并在运行日志里打印出来否则同一份数据两次划分出来的 val 不同后续对比实验的基准就不成立。划分脚本逻辑不复杂但要注意标签文件也要跟着动。只用shutil.move移动图片而忘了移动同名 txt会造成 txt 在 labels 目录下堆积训练脚本不报错但全部跳过等于所有样本被当成了背景。2.4 划分之后必须做的三项一致性检查划分完成后我一般跑三段 shell 命令做快速校验。第一段统计数量第二段比对同名文件第三段看 txt 行数是否有零标注。可以用下面这段 Bash# 统计 images/train 和 labels/train 中文件数量 echo images train: $(ls images/train | wc -l) echo labels train: $(ls labels/train | wc -l) # 找出 images/val 中缺少 labels 的文件 comm -23 (ls images/train | sort) (ls labels/train | sort)这段命令的逻辑是前两行分别统计数量数量不一致说明有图没标或者有标签没图comm -23显示只在左边出现的文件名也就是缺少标签的图。遇到这类文件可以直接删掉或补标不能留在数据集里靠训练脚本兜底因为训练过程中网络会把它们识别成背景虽然也能训练但验证指标会失真。对只有几百张的小数据集这三行命令足够把大部分低级错误暴露出来。3. 把原始图片和标注整理成 YOLO 格式四类障碍物的坐标转换与可视化验收3.1 图片筛选与分辨率下限路上障碍物检测的难点往往不在大货车而在十几米外的人或摩托车即便标注框正确分辨率不够照样训不出来。数据准备阶段的第一个硬性条件是所有图片写入 images 之前统一检查宽高低于训练输入尺寸的图片要么剔除要么先做同分辨率填充。实际项目里常见做法是设置一个最小边长 640 的阈值如果图片是 520×800不直接缩小而是保持长宽比缩放为 640×984 再补边这样不会把远处目标压缩成不可辨识的噪点。对 4 个类别的数据还要检查类别数量均衡程度。理想状态下每类至少 500 个实例少于这个数字就会在 val 里出现 mAP 方差很大结果好坏完全取决于这一批样本里负例的占比。最简单的检验方法是把所有 txt 的第一列切出来数频次发现某类数量太少时优先做数据增强而不是直接复制同一张图典型的增强组合是 HSV 饱和度扰动、随机水平翻转和 Mosaic这些增强在 YOLO 训练参数里就能开。3.2 坐标转换脚本VOC/COCO 到 YOLO以及 KITTI 转出来的坑如果你拿到的数据是 XML 格式的 VOC 或 JSON 格式的 COCO就必须写一次性转换脚本。我比较推荐直接写一个独立脚本而不是把转换逻辑塞进训练入口因为转换往往只跑一次跑完就用不上了。下面这个 Python 脚本处理 VOC 格式假设 XML 和 jpg 同目录import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt_path, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) with open(out_txt_path, w) as f: for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h line f{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f} f.write(line \n) # class_map 的 key 是源数据里的类名value 是重映射后的 id class_map {person: 0, car: 1, bicycle: 2, motorcycle: 3} voc_to_yolo(0001.xml, labels/0001.txt, class_map)脚本逻辑很容易读先从 XML 里读图片宽高和每个目标的bndbox再用中心公式转成归一化坐标最后把class_map映射好的类别 id 写进 txt。class_map里的 value 必须与 classes.txt 的行号一致这是最容易踩的坑。另一点是if name not in class_map: continue不能漏源数据里可能会有 truck、bus 等类别标题限定参数为四类遇到这些类应该跳过而不是报错中断。KITTI 标注转 YOLO 是另一个常见任务坑在于 KITTI 数据集的坐标系里目标的高度用像素表示但雷达 3D 框的投影会和 2D 框不一致不能直接拿投影高度当 h正确做法始终以 2D 框的四条边为准。CVAT 导出 YOLO 格式时也会附带一个obj.names文件该文件内容就是类别顺序导入前一定要人工比对一次。3.3 训练数据标记与类别映射的自动化如果多个来源的标注类名不统一比如有人标 pedestrian有人标 person合并前要先做类名归一化。写一段小脚本读出所有 txt 的第一列并统计频率再对照 class_map 生成一张映射表比手写静态映射更可靠。映射表里没有出现的类别 id 可以打印出来人工判断避免漏掉一个原本数量很少的类。做完映射后全数据集跑一次有效性校验重点检查三类错误负坐标、越界框、以及宽或高小于 1 像素的框。YOLO 训练时不会因为一个负坐标直接报错但损失函数会对离谱框给出不稳定梯度早停也可能被异常值干扰。推荐在训练前把全部 txt 用一段 OpenCV 脚本读一遍统计边界越界比例超过 1% 就该回头查转换逻辑。对应关系可以整理成一张排错表错误类型检查方式处理建议坐标为负遍历 txt 全部数值回看 xml 的 bndbox修正归一化公式越界框统计是否超出图片边距做边缘裁剪而不是直接删除宽或高小于 1 像素统计最小 bbox 尺寸删除或合并到邻近框3.4 用数据可视化脚本检查标注对齐效果标题里的数据可视化脚本作用就是这时候发挥把 image 和对应的 txt 画出来让人眼快速确认标签是否贴边、类别名称是否正确。典型调用方式如下python visualize.py \ --root road_obstacle \ --split train \ --classes classes.txt \ --sample 60 \ --out output/preview参数说明--root指向数据集根目录--split指定要画训练集还是验证集--classes指向类别文件--sample是随机抽取张数--out是保存目录。脚本通常会按每个类别抽样确保不是 60 张全是同一类。看图的重点是宽高比是否夸张、多目标场景有没有覆盖、每张图平均目标数是否是预期水平。平均目标数不到 1 说明大量图片没有实例这会导致背景帧太多训练时网络更偏向预测无目标。提示可视化不是可选项。训练前花 15 分钟看 100 张随机抽样能省下训练后排查 mAP 异常的半天时间。4. 在划分好的数据集上跑通 YOLOv8yaml 配置与损失曲线排查4.1 准备 dataset.yaml 与数据集挂载现在把上述数据集交给 YOLOv8 训练。先写数据集配置路径建议用绝对路径排除相对路径带来的坑path: /home/user/road_obstacle train: images/train val: images/val test: images/test names: 0: person 1: car 2: bicycle 3: motorcycle这个 yaml 里的names列表顺序必须和 classes.txt 一致。path是根目录train/val/test是相对于path的目录。YOLO 训练脚本会自动到同名 labels 目录下找 txt无需手动写 labels 路径。配置完先跑一句yolo detect train dataroad_obstacle.yaml modelyolov8n.pt epochs100 imgsz640 batch16用最小模型验证数据链路完整性。参数说明这里强推训障碍物检测先用 nano 模型而不是直接上 large因为目的是验证数据不是出最好指标。nano 会把速度压到几分钟一轮任何标签错误会更快暴露。batch 大小根据显存调整8GB 以下建议 batch8 或者更小。4.2 YOLO 目标检测流程里的损失函数与训练参数开始完整训练前还需要理解损失函数随 epoch 的变化训练日志里的box_loss、cls_loss、dfl_loss是三个关键指标。路上障碍物检测属于类别数很少的目标检测正常情况下cls_loss在前 30 个 epoch 快速下降并趋于平缓box_loss则缓慢下降。如果cls_loss跑了几十个 epoch 还是十几的水平说明某类图片数太少或被标注错误主导。YOLO 的训练损失函数由三部分组成其中分类损失使用 BCEWithLogitsLoss回归部分同时考虑 IoU 和 Distribution Focal Loss这个组合决定了数据不干净时曲线会明显抖动。训练命令如下yolo detect train \ dataroad_obstacle.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/road \ nameobs_nano参数含义逐个说明epochs控制最大训练轮数patience表示验证集指标连续 30 个 epoch 不提升就早停lr0是初始学习率project和name决定输出目录。对于 4000 张的小数据集默认的 mosaic 增强足够用不要额外把degrees调太大路上障碍物不存在 90 度旋转的情况训练时给太多旋转角度反而会误导模型。4.3 从训练曲线和数据分布定位问题训练中要盯住验证集mAP50-95和mAP50两个指标。mAP50只要求预测框与真值框 IoU 超过 0.5 才算正确mAP50-95则平均多个 IoU 阈值。路上有小目标人站在 50 米外可能只有十几个像素这时mAP50可能到 0.85而mAP50-95只有 0.5这种差距说明位置精度不如分类精度可以回头检查最小目标比例不要盲目加模型复杂度。下面是 4 类障碍物常见的损失曲线形态与对应判断现象可能原因对策cls_loss 先降后升验证集类别不均衡重做划分增加少数类box_loss 下降极慢标签框偏移严重修 xml 转 yolo 的偏移换算训练集与验证集 mAP 差大于 0.2同帧画面泄漏到两个集合按视频片段重新划分前 20 epoch mAP 为 0标签 id 与 class 文件没对齐打印可视化核对类别颜色这张表是排查路线实际训练时如果出现前三行多半不是网络问题而是数据集本身。我见过很多团队一上来就换模型结果损失出现大断层的原因是把同一视频连续帧分到了 train 和 val帧间高度相似导致验证指标虚高。4.4 训练数据标记不完整导致的三个高频报错训练中常见的两个致命错误是AssertionError: train: No labels in ...和FileNotFoundError: label。第一个表示 labels/train 下没有 txt但 images/train 有图片检查目录名大小写以及是否误把压缩包解压成带层级的嵌套目录。第二个表示某张图没有对应的 txt多数是同步脚本漏拷了文件。第三个高频问题是图片格式YOLO 依赖 OpenCV 读取某些渠道下载的.jpg实际是 webp 编码虽然扩展名是 jpg 读出来也会异常建议遍历一遍做真实格式识别。提示收集数据完成后直接跑find images -type f | wc -l与find labels -type f | wc -l两者数量一致是最低门槛数量一致不代表内容一致仍要靠可视化兜底。5. 收尾技巧给四类障碍物数据集加一张「中心点散点图」做质量审计5.1 目标中心点分布反映视野盲区数据可视化脚本通常画的是框但有一个比框更有诊断价值的输出把所有标注框的中心点画到同一张归一化坐标图上。这个图能直观反映模型在哪些位置见过的目标多哪些位置目标完全没出现过。路上障碍物数据如果中心点图左下大片空白说明左下角长期没有目标训练后模型在该区域漏检率偏高因为 Anchor 匹配和特征图采样都依赖训练样本的空间分布。散点图脚本逻辑非常简单读取全部 txt 的第二和第三列直接plt.scatter即可。5.2 用每图目标数找出重复帧与漏标段第二个小技巧是统计每张图的标注目标数并输出直方图。直方图最左边那一柱如果异常高比如超过 10% 的图目标数为 0问题通常来自视频抽帧视频静止时段或者长时间红灯停车产生的几乎重复的背景帧混入数据集。这类帧会拉低训练效率还容易让模型把背景学成负样本。此时要回到抽帧脚本加入帧间差分连续两帧像素变化低于阈值就直接丢弃。如果直方图右端出现目标数超过 30 的图则要检查是否把大图的密集标注重复计算了四类路上障碍物单张图超过 30 个目标已经非常拥挤模型训练时下采样会丢失很多小目标。5.3 把审计结果写进数据集的构建产物最后把这个审计脚本固定下来每次重新生成数据集后自动跑一遍输出一张中心点散点图、一张目标数直方图和一份每类频次的 CSV。CSV 的作用是让后来接手的人一眼看到数据集的类别分布。这个三维审计产物比让同事看 100 张可视化原图高效得多目标检测的调优往往不在网络结构而在于对数据分布的把握。把这段脚本放在数据集的 tools 目录下和 classes.txt 放在同一层方便后续任何人复验。把这三件套的输出作为数据集的构建产物用 CI 在每次数据集变更后自动重新生成并保留历史版本后面任何一次指标回退都能从数据版本里找到原因。本文还有配套的精品资源点击获取