
简介面向YOLO目标检测任务这套路上障碍物检测数据集提供现成的训练与验证样本涵盖障碍物、小动物、路障、减速带4个类别。所有图像均为640×640分辨率RGB图片标注采用YOLO相对坐标格式边界框完整且每张包含多个目标适合用于自动驾驶或行车场景中的障碍物识别。压缩包采用7z封装共2000个文件以89张jpg图片、1910个txt标签文件为主另附1个py可视化脚本整体大小约124.8MB。数据已按YOLO标准文件夹结构划分训练集1337张、验证集572张并附带类别txt文件下载即可直接训练可视化脚本无需修改随机传图即可绘制并保存边界框便于核查标注效果。目前已有151人学习下载适合需要标准数据集进行YOLO系列模型训练或算法验证的开发者。1. 一份4类别障碍物数据集为什么是YOLO新手最该先跑通的东西如果你的目标是快速搞懂 YOLO 目标检测数据集怎么组织、训练脚本怎么跑、结果怎么验证别一上来就啃 COCO 或者 VOC 那种几百 MB 起步、类别几十个的大包。路上障碍物检测4类别这类小数据集反而是最合适的入门载体类别少、目标集中、划分好的 train/val/test 直接用连类别 class 文件和可视化脚本都给你备好了。它解决的问题很具体——让轿车、行人之外的锥桶、护栏、坑洞、遗撒物这类“路面异常”能被模型认出来适合0基础纯小白起步也适合做课程设计、毕设预研或者拿它验证一条 YOLO 训练流水线到底通不通。我拿到这类资源时一般先按“4类别怎么定、标签怎么对齐、可视化怎么查错、训练怎么调参”这条链走一遍下面就把每一步的细节拆开讲。2. class文件与YOLO标签格式4类别怎么对齐、ID为什么从0开始2.1 路上障碍物4类别的常见选型cone、barrier、pothole、debris标题只说“4类别”没写具体是哪四类。从业者按路上障碍物检测最常见的落地方案来对齐一般是cone交通锥/锥桶、barrier护栏/水马、pothole路面坑洞、debris遗撒物/散落物。这四类几乎覆盖了自动驾驶和辅助驾驶里“不该出现在车道上的东西”而且每一类都是典型的小目标——锥桶可能只有 30×30 像素坑洞更是贴在地面上对比度低、容易被背景吞掉。建议把 ID 固定下来0-cone、1-barrier、2-pothole、3-debris。这个顺序不是随便排的它决定 class 文件里的行号、label 文件里的第一个数字、以及训练时 data.yaml 里的 name 列表。注意第一类是 0 而不是 1YOLO 的类别索引一律从 0 开始。很多人第一次写标注脚本时习惯从 1 数起结果训练时所有框的类别全部错位——这是这类数据集最典型的翻车点。如果你拿到的资源里四个类别定义和上面不完全一样比如把 debris 换成了 barrel防撞桶不要慌class 文件顺序就是唯一标准。你只需要保证“class 文件第几行 label 文件里数字几 训练时 names 列表第几个”三者一致就能跑通。可视化脚本也是按 class 文件顺序读类别名的所以拿到数据包第一件事就是打开 class 文件看一遍别直接开训。2.2 class文件就是一行一个类名的纯文本顺序即ID很多新手会把 class 文件和 Python 里的 class 搞混其实这里说的是一个纯文本名单文件常见名字是 classes.txt 或 class.txt内容长这样cone barrier pothole debris它的规则只有一条每一行是一个类名行号从 0 开始计数。所以上面这个文件里cone 的 ID 是 0barrier 是 1pothole 是 2debris 是 3。你把它改名、挪位置所有关联的标注文件就全废了因为它不是给人看的备注而是被训练脚本和可视化脚本当索引表用的。对比一下 YOLO 官方预训练模型用的 COCO 80 类coco.yaml 里第一行是 person、第二行是 bicycle……如果你的数据包给了 class 文件就不要再去套 COCO 的类别顺序直接用这份 4 类的。这也是为什么这类数据包一定带 class 文件而不是让你自己猜——省掉的就是这段对齐功夫。2.3 label格式对照class_id 归一化中心点 归一化宽高YOLO 的标注格式和 VOC 那种 XML 完全不同它不给左上角和右下角坐标而是给归一化之后的中心点坐标和宽高。每张图片对应一个同名的 .txt 文件一行一个目标格式是类别ID 中心点x 中心点y 宽度 高度比如下面这个 label 文件内容0 0.512 0.438 0.082 0.116 2 0.620 0.581 0.045 0.052第一行表示一个锥桶ID0中心点在图片横向 51.2%、纵向 43.8% 的位置宽度占整张图的 8.2%高度占 11.6%第二行是一个坑洞ID2体积小得多。要注意这里的四个小数全部在 0 到 1 之间因为坐标做过归一化实际像素值除以图片宽高。归一化是 YOLO 格式的灵魂它让同一份标注在不同分辨率下都能直接用但代价是——一旦有人不小心把像素坐标直接写进去可视化时框会飞出去训练时 loss 也会异常这个坑放在第 5 章细说。读取 label 文件时还有两个约定一是文件里每行只有第一个数字是类别 ID后面四个是坐标二是如果某个目标没标txt 文件就是空文件这在数据包里是正常情况不是损坏。3. 用划分好的数据集跑通YOLOv8训练目录结构、data.yaml与训练命令3.1 划分好的数据目录长什么样图片与label一一对应标题强调“划分好的数据集”意思是 train、val、test 的图片和标签已经按 8:1:1 左右的比例分好你不需要再写划分脚本。常见的目录组织方式是 images 和 labels 分开两层我一般会先检查是不是下面这个结构road_obstacle/ ├── train/ │ ├── images/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── labels/ │ ├── 001.txt │ └── 002.txt ├── val/ │ ├── images/ │ └── labels/ ├── test/ │ └── images/ ├── class.txt └── data.yaml图片和标签一一对应靠“同名不同后缀”来配对001.jpg 的标签是 001.txt。如果图片是 .png标签还是 .txt不受影响但图片后缀必须和 images 目录里实际文件一致否则训练时会找不到图。test 目录经常只有图片没有标签这是正常的因为测试集是最后用来推理的不是用来算 loss 的。这里有个细节值得确认每个 label 文件里的目标数量应该和图片里实际存在的目标总数对得上。拿到的数据集不一定保证这一点所以我后面的可视化脚本第一步就是干这个——把框画回图上数一数有没有漏标、错标。3.2 data.yaml与最小训练命令先跑通再调参准备好目录后先写 data.yaml。它是 YOLO 训练时的数据集说明书内容如下path: D:/datasets/road_obstacle # 改成你本机的绝对路径 train: train/images val: val/images nc: 4 names: 0: cone 1: barrier 2: pothole 3: debrispath 是数据集根目录train 和 val 写的是相对于根目录的图片路径千万别把 labels 也写进去YOLO 会自动到同级 labels 目录里找标签。nc 是类别数必须等于 class 文件行数names 的键值对顺序要保持和 class 文件一致。这里如果写错训练不会报错但模型会拿 A 类别的数据去学 B 类别的名字效果完全无法用。环境配置上现在的主流做法是直接用 ultralytics 库一条 pip 命令装完再训练pip install -U ultralytics yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16参数说明data 指向你刚写好的 yamlmodel 用 yolov8n.pt 是官方预训练权重它会从 COCO 上迁移学习训自定义数据集时千万别写 modelyolov8n.yaml那个是从零训练收敛慢很多epochs 对这类小数据集 50 到 100 都够多了也没有明显收益imgsz640 是默认输入尺寸后面小目标坑会专门讲batch16 是显存占用最大的参数如果你是笔记本 6G 显存改成 8 或 4否则会 OOM。跑通之后做一次预测验证整条链路没有断yolo detect predict modelruns/detect/train/weights/best.pt sourcetest/images save_txtTrue这条命令会读 test 目录里所有图片把检测结果写到 runs/detect/predict。save_txtTrue 会额外输出 YOLO 格式的预测标签文件方便和真实标注做对比。0基础纯小白能跑通上面两条命令就算正式进入目标检测的门了。3.3 训练开始后看什么损失曲线、mAP与混淆矩阵模型训练时控制台会滚动输出 loss 和指标很多人只看最终结果错过了最容易发现问题的阶段。YOLOv8 的损失函数由三部分组成box_loss边框回归损失、cls_loss分类损失、dfl分布焦点损失。对障碍物数据集来说我最关注 box_loss 和 val 的 mAP50。正常走势是train 和 val 的 box_loss 一起下降到第 40 轮左右开始走平cls_loss 先快后慢。如果 train loss 降了、val loss 不降反升说明过拟合换更多数据增强或提前停训。如果 box_loss 从头到尾都高八成是标注框本身有问题别急着改参数先回第 4 章做可视化。等训练结束看一下 runs/detect/train 目录下的 confusion_matrix.png——4 类别的混淆矩阵比 mAP 数字更直观能看出哪两类互相认错。比如 cone 和 barrier 都是地面竖条物体角度一变就会混淆这是障碍物检测的经典问题需要后面靠数据角度多样性去压。4. 数据可视化脚本画标注框、统计类别分布、生成目标热力图4.1 第一件事把标注框画回原图检查坐标和类别数据可视化脚本绝不是“锦上添花”它是训练前唯一能帮你检查数据有没有错的工具。拿到数据包我第一步就是写个脚本把标注框画回原图40 秒能看完几百张图的错误率。下面这段是核心函数import cv2 def draw_yolo_labels(img_path, label_path, class_names, out_path): 把YOLO格式标签画回原图class_names从class文件读取 img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 空行或损坏行直接跳过 cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) # YOLO存的是归一化坐标先还原成像素 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) # 类别名写在框上方别让文字跑到图外 cv2.putText(img, class_names[cls_id], (x1, max(y1 - 5, 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.imwrite(out_path, img)逻辑说明先读图片拿到真实宽高 h 和 w再逐行读 label 文件把归一化的中心点 cx、cy 和宽高 bw、bh 还原成像素坐标最后用 OpenCV 画矩形和类别名。转换公式是 x1 (cx - bw/2) * wy1 同理这里的 0.5 倍宽高偏移是还原左上角的关键。参数说明class_names 必须是按 ID 排好序的列表比如 [cone, barrier, pothole, debris]否则类别名会张冠李戴out_path 建议输出到一个单独文件夹不要覆盖原图。批处理时只要遍历 train/images 下的所有图片找同名 txt调这个函数就行。画完看三类问题框有没有包住整个物体、框中心是否偏得离谱、类别名是不是对的。我见过最离谱的情况是坑洞的框画在天空上那基本是标注时把归一化坐标写错了。4.2 第二件事统计每类样本与实例数发现类别失衡画完几批图之后下一步是看类别分布。障碍物数据集的典型问题是实例失衡——锥桶可能有几千个坑洞只有几十个。模型对多数类别过拟合对少数类别几乎不学最后 mAP50 看着 0.8实际坑洞一个都检不出。写个小脚本统计实例数import glob from collections import Counter # 统计train/labels下所有txt里每个类别出现了多少次 counter Counter() for label_path in glob.glob(train/labels/*.txt): with open(label_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 1: counter[int(parts[0])] 1 print(counter)逻辑说明遍历训练集所有 label 文件每读一行就把该行第一个数字类别 ID计数加一。Counter 输出形如 {0: 1200, 1: 350, 2: 80, 3: 160}一眼能看出哪类是少数类。参数说明如果某一类占总实例数不到 5%就要重点处理——要么补充数据要么训练时在 data.yaml 里调 class weights要么干脆把这一类剔除别让模型把大量参数浪费在学不会的类别上。这里有个反直觉的经验光看“图片张数”没用得看“实例数”。4 类障碍物数据集可能一共只有 2000 张图但锥桶在每张图里出现 5 个坑洞只在 50 张图里出现 1 个模型对坑洞的拟合能力会非常差。可视化脚本的价值就在这里——它把“数据是黑匣子”变成“数据是透明的”。4.3 进阶把框中心点叠成热力图定位漏检死角除了画框和统计数量可视化脚本还有一个冷门但好用的功能把图片归一化坐标映射到固定画布上用热力图看目标集中在画面哪个区域。这对障碍物检测特别关键——如果坑洞全部出现在图片底部模型会在推理时对下半屏格外敏感遇上上半屏的坑洞直接翻车。import cv2 import numpy as np def make_heatmap(label_dir, img_w640, img_h640, out_pathheatmap.jpg): 把所有label框的中心点叠加成热力图找出目标分布死角 heat np.zeros((img_h, img_w), dtypenp.float32) for label_path in glob.glob(f{label_dir}/*.txt): with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cx, cy float(parts[1]), float(parts[2]) # 归一化坐标映射到640x640画布 heat[int(cy * img_h) - 1, int(cx * img_w) - 1] 1 heat cv2.GaussianBlur(heat, (15, 15), 0) # 平滑成连续热区 heat heat / max(heat.max(), 1) * 255 cv2.imwrite(out_path, heat.astype(np.uint8))逻辑说明遍历所有 label 文件把每个目标的中心点按归一化坐标放大到 640×640 的画布上累加计数再用高斯模糊把散点变成连续热区。参数说明img_w 和 img_h 要和训练时的 imgsz 保持一致否则热力图和模型的感知区域对不上热力图越亮的地方是目标越集中的区域全黑的区域就是模型的“感知盲区”。做完这一步你会发现大部分障碍物数据集的中心点集中在画面中下方这是车载相机的天然视角决定的。所以训练时不要开太大的随机裁剪否则把目标裁出画面模型反而学歪了。5. 障碍物数据集从标注到训练的高频翻车点4条避坑记录5.1 类别ID错位class文件顺序与标注顺序不一致现象训练时 loss 下降正常但可视化脚本画出来的框类别全是乱的——明明看着是锥桶标注的名字却显示“debris”或者训练完实测时模型把路边的水马全部认成坑洞。原因class 文件的顺序和标注时使用的 ID 映射不一致。常见场景是数据包作者先按 A 顺序标注后来重排了 class 文件但 label 文件里的数字没有跟着改也有人复制了网上别人的 class 文件直接覆盖了自己那份。ID 错位的可怕之处在于它不报错训练能正常跑完指标甚至不低只是模型学的东西和真实语义对不上。解决训练前写个 5 行的核对脚本打印 train/labels 下所有出现过的类别 ID再和 class 文件行数逐一比对import glob ids set() for txt in glob.glob(train/labels/*.txt): with open(txt) as f: for line in f: ids.add(line.split()[0]) print(label中出现过的ID:, sorted(ids)) print(class文件行数:, open(class.txt).read().count(\n))把输出和 class 文件逐行对一遍如果出现 ID 大于 3 或者缺失某个 ID先修数据不要浪费时间去调训练参数。5.2 数据划分泄漏同一场景的连续帧同时进了train和val现象train 的 mAP50 一路爬到 0.95val 却卡在 0.3 上不去而且 val loss 在训练中段开始反弹。很多人第一反应是过拟合加增广、改权重衰减折腾半天没用。原因划分数据集时用的是随机抽样但障碍物数据通常是车载视频抽帧来的——同一辆车、同一个路口的连续帧画面高度相似。随机划分会把同一场景的帧同时分到 train 和 val模型其实“背过”了这些画面val 指标虚高换到真正的场景就露馅。还有一种更隐蔽的泄漏划分前没做图片去重trian 里有一张图val 里也有它的镜像或近邻帧。解决正确的做法是按场景切分——把属于同一段连续视频的帧放在同一个集合里再整段分配。拿到数据包后我一般先看文件名是否含序列号如果是 frame_001、frame_002 这种就按序列号区间切分保证 val 里出现的是模型完全没见过的路段。如果数据包已经是划分好的文件只能靠可视化脚本抽查 val 图片和 train 图片有没有相似度过高的样本发现了就手动挪出来。5.3 小目标检不出imgsz、mosaic与极小障碍物现象训练完测试锥桶能检出路面坑洞几乎全部漏检precision 很高但 recall 极低或者大目标框很准小目标框要么偏大要么干脆没框。原因YOLOv8 默认输入 imgsz640下采样 32 倍后特征图最小分辨率是 20×20。一个 20×20 像素的坑洞在深层特征图上只剩 1 个像素网络基本没有信息可用。另外默认开启的 mosaic 增强会把四张图拼在一起坑洞这类贴地目标被拼接边界切碎标注框里混进大量背景学习信号被严重污染。解决我做障碍物检测时会跑两组对比一组 imgsz640一组 imgsz1280。显存允许的话小目标数据用 1280 通常能涨 5 到 10 个点。开不了大分辨率就退而求其次关闭 mosaic 增强ultralytics 里设 mosaic0.0或把 mosaic 概率降到 0.5对坑洞这种目标比 mosaic 更有效的是轻度旋转和亮度扰动。最后还有一招是 SAHI 切片推理把大图切成 640×640 的块分别检测再合并推理时间变长但小目标召回率确实提升明显。5.4 可视化脚本画出的框错位坐标没归一化或宽高写反现象用可视化脚本画出来的框比真实目标大一圈或者框的位置整体偏移更夸张的情况是框画到图片外面OpenCV 直接报错。原因最常见的两种一是 label 文件里存的是像素坐标而不是归一化坐标脚本却按归一化去乘宽高二是中心点坐标和宽高的顺序写反了YOLO 格式是 cx, cy, bw, bh有人标成了 x1, y1, x2, y2。还有一种隐蔽的情况图片本身是 PNG有透明通道cv2.imread 读出来是 3 通道宽高没问题但如果脚本按 RGBA 读shape 就变成了 4 通道后续画框的坐标不受影响但保存时可能报错。解决先打印 img.shape 确认图片宽高再检查 label 文件数值范围——归一化坐标一定在 0 到 1 之间如果出现大于 1 的数直接判定这批标签格式有问题。写一个校验规则cx 和 cy 必须小于等于 1.0bw 和 bh 必须大于 0 且小于等于 1.0。不符合规则的 label 文件单独列出来人工修正而不是让训练和可视化脚本硬扛。6. 训练完别急着收工预测框与真实框叠加抽检的技巧模型训练完runs/detect/ 里会生成一堆指标图但指标是数字数字会骗人。我会做一件很简单的事把 test 集或 val 集的图片同时画出真实框和预测框输出成对比图——真实框用绿色预测框用红色一眼就能看出模型在哪些场景下漏检、哪些场景下误检。一个轻量做法是利用训练时自动保存的预测结果在训练命令里加上 save_jsonTrue结束后 predictions.json 里存着每张图的预测框坐标、类别和置信度再用第 4 章的画框脚本把 JSON 和 label 文件同时画到原图上效果就是真值 vs 预测的叠加图。抽检的重点看三类样本一是小目标密集的路段坑洞和遗撒物扎堆出现时模型会不会漏掉其中一半二是逆光或阴影下的锥桶障碍物数据集最常见的失败场景三是远处极小目标——这种目标 mAP 贡献很小但实车场景里恰恰最危险。如果三类都翻车厉害检查训练时是否用了 1280 分辨率以及测试时有没有加 NMS 的 conf 阈值调低到 0.15 试试有时只是阈值设太高把低分正确框全滤掉了。我自己的习惯是每个数据集训练完都做一次 30 张图的抽检存成对比图留档。这比盯着 mAP 数字涨跌有用得多——有一次数字显示模型整体挺好抽检才发现它把一个红色水马全认成了锥桶原因是训练数据里红色锥桶占比过高颜色先验盖过了形状特征。数据可视化不是训练前的例行公事它应当贯穿到训练后验证这一步。希望这个流程能帮你在障碍物检测上少走一段弯路至少别让标注错误和类别错位浪费掉你调参的时间。本文还有配套的精品资源点击获取