news 2026/10/1 4:28:58

4类路上障碍物YOLO数据集:从标注划分到训练避坑全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
4类路上障碍物YOLO数据集:从标注划分到训练避坑全指南

简介:面向YOLO目标检测实战的数据集资源,专为路上障碍物检测设计,包含4个类别:障碍物、小动物、路障、减速带。图像为640×640大分辨率RGB图片,每张图像均有多个目标,边界框完整,并已按YOLOv5目录格式划分好训练集与验证集,可直接用于模型训练。资源共2000个文件,包括1910个txt标签文件、89个jpg图像和1个Python可视化脚本,压缩包整体124.8MB。txt标签采用YOLO相对坐标格式(classes, x_centre, y_centre, w, h),训练集含1337张图片及标签,验证集含572张图片及标签,另附类别名称txt文件,目录结构清晰。同时提供可视化脚本,随机传入一张图片即可自动绘制边界框并保存到当前目录,便于检查标注质量。整体适合目标检测入门、算法评测及实际路况感知项目的数据准备,目前已有151人学习/下载。

1. 一份4类路上障碍物 YOLO 数据集,先把最耗时的数据环节替你走完

第一次跑自主巡航项目时,我被数据整理环节坑得最惨:四千多张图,逐张标注不现实,自己写的划分脚本又让 val 集偏成单类别。后来我养成一个习惯:接手任何 YOLO 目标检测任务,第一件事永远是检查标签格式和 class 文件顺序——这两处错一位数,模型就白训。路上障碍物检测(4类别)这份数据集的定位,就是把这套最繁琐的环节预先做完:图片和标签按 train/val 划分好,类别 class 文件就摆在根目录,还带一个数据可视化脚本用于快速核查标注质量。它适合两类人:刚上手 YOLO 但不想耗在数据流程上的新手,和需要一份现成基准去调整数据策略的工程师。

2. 4类障碍物的类别定义与 class 文件:为什么顺序和 ID 必须先定死

2.1 4类常见的落地选型:person、car、cyclist、锥桶

路上障碍物的候选类别远不止4类:行人、骑行者、轿车、卡车、锥桶、施工围挡、绿化带边缘都能算。做成4类是有意的减法,类别越多,小样本类别越拖拽整体收敛速度;类别太少,模型学不出目标间的区分度。这份数据集的4类通常落在行人(person)、汽车(car)、骑行者(cyclist)、交通锥桶(traffic_cone)上。这四类在城市道路和园区道路上出现频率高,覆盖了移动障碍和固定障碍,目标尺度差异也明显——行人和锥桶偏小,汽车偏大,骑行者长宽比特殊,方便训练阶段直接检验模型的多尺度能力。

选类别的判断标准我一般看三个。一是可区分度:行人和骑行者虽然都是人形,但框的长宽比和运动特征差异大,值得拆成两类,如果你把它们合一起,模型对骑着电动车的人会摇摆不定。二是出现频率:平均十张图见不到一次的类别,在样本均衡性差的标注集里基本是浪费输出头,除非你有专门增强它的预算。三是边界明确:“障碍物”这种大杂烩类别让模型永远学不干净,宁可拆细一点再合并后处理。

如果你想沿用这份数据集的结构但换成自己的类别,我只建议替换 class 文件里的名字,不要轻率地加类别数。现有标注的分布是按4类平衡好的,加第5类意味着你必须提供足量的新标注并把旧标注里属于新类的框重新归类,这个工程量比重新标一份还大。

2.2 class 文件与标注 ID 映射

class 文件在 YOLO 系列里是一行一个类别名,顺序与类别 ID 严格绑定。打开这份数据集的 class 文件,你会看到类似这样的内容:

person car cyclist traffic_cone

这个顺序的含义是:标注 txt 里每行第一个数字 0 代表 person,1 代表 car,2 代表 cyclist,3 代表 traffic_cone。注意不可以在 class 文件里按自己的想法调顺序,因为所有标注框的整数 ID 只认原始顺序。若你把 cyclist 挪到第一行,旧标注里 ID 为 2 的框在新 class 文件下全被解读为 car,训练出的混淆矩阵每一行总和都对不上,损失函数照样下降,模型却学了一堆错标签。

一个标注 txt 文件长这样,每行五个值:

0 0.5312 0.4359 0.1823 0.3456 3 0.7821 0.5344 0.0987 0.0983

从左到右依次是:类别 ID、归一化框中心点 x、归一化框中心点 y、归一化框宽、归一化框高。YOLO 使用中心点加宽高的归一化写法,不是目标检测里常见的左上右下坐标。class 文件在训练流程里只承担“给人看”和“给可视化脚本取名”的作用,真正参与训练的是标注文件里的整数 ID。所以只想改类别显示名是安全的,动顺序不动 ID 必翻车。

提示:检查 ID 是否越界的方法很简单,打开任意一个标注文件,确认最大值小于类别数,再把每个 ID 对应到 class 文件第几行,逐个核对目标形状是否符合该类别的直观特征。

2.3 类别命名与文件编码:UTF-8 无 BOM 和 LF 行尾

class 文件看起来小,却被训练配置和可视化脚本同时读取,最容易踩的雷是编码。我建议保存成 UTF-8 无 BOM,行尾用 LF。Windows 记事本默认保存的 UTF-8 带 BOM,会在第一个类别名前多出不可见字符,Python 的strip()能去掉一部分,但 C++ 部署端做类别映射时可能把这个字符带进类别名,推理输出显示成 person 前面带个黑点。处理办法:用 VS Code 或 Notepad++ 重新打开 class 文件,右下角编码切到 UTF-8,保存后文件大小比原来少三个字节,那就是 BOM 被去掉了。

命名本身也有讲究。类别名不要带空格、斜杠和中文。YOLOv8 的 names 字段能接收中文,但导出 ONNX 或 TensorRT 后,部分推理框架对类别名的编码处理不完善,可视化时会出现乱码。优先使用英文小写加下划线,这也是 YOLO 生态最常见的命名习惯。如果业务上必须显示中文类别名,class 文件保持英文,只在 UI 层维护一张英文到中文的映射表。

3. 划分好的数据集目录:images/labels 双目录与 data.yaml 三行改动

3.1 images/labels 双目录与 train/val 划分

一份按 YOLO 规范整理好的数据集,解压后目录一般长这样:

road_obstacle/ ├── classes.txt ├── data.yaml ├── images/ │ ├── train/ │ │ ├── frame_000001.jpg │ │ └── frame_000002.jpg │ └── val/ │ ├── frame_002405.jpg │ └── frame_002406.jpg ├── labels/ │ ├── train/ │ │ ├── frame_000001.txt │ │ └── frame_000002.txt │ └── val/ │ ├── frame_002405.txt │ └── frame_002406.txt └── scripts/ └── visualize_labels.py

YOLO 训练器读取数据的机制很机械:它扫描images/train下所有图片,然后用同前缀去labels目录找同名.txt。frame_000001.jpg必须能对应到frame_000001.txt,缺一个,训练时那张图会被静默丢弃,你都不知道数据里少了什么。拿到数据集第一步,我会用命令行查一遍一一对应关系:

提示:在 Linux 下执行ls images/train | wc -l和ls labels/train | wc -l,数量不一致就需要做一次全量核对。把两份列表按文件名排序后 diff,多出来的标注文件通常意味着图片被误删,多出来的图片意味着有目标没被标。

train/val 划分比例常见两档:8:2 和 9:1。选择依据是总量。总量在两三千张时,8:2 能让 val 有几百张图,足够观察各类别的 PR 曲线走势;总量五千张以上时,9:1 把更多样本留给训练。这里最关键的是划分方式:不能按文件名单纯随机分。路上障碍物数据大多来自连续视频抽帧,相邻帧背景几乎相同,随机切会让同一场景同时进 train 和 val,验证分虚高。应该按视频片段或场景前缀做分组,整组进 train 或 val,具体坑在第5章展开。

在实际训练工程里,val 比例调到 5% 也常见,前提是 val 完全独立且你只看趋势不看绝对指标。数据总量偏少时,我会额外留出 30 张做最终测试,这 30 张既不进 train 也不进 val,专用来模拟“完全没见过的路况”。对于障碍物检测这种场景分布变化很大的任务,这个额外留出集往往比 val 更能说明模型能不能落地。

3.2 换训练框架时 data.yaml 怎么改

拿到数据集直接开训 YOLOv8 最省事。data.yaml 通常长这样:

path: ./road_obstacle train: images/train val: images/val names: 0: person 1: car 2: cyclist 3: traffic_cone

字段不多,但每行都有讲究。path是数据集根目录,YOLOv8 里写相对路径时,它相对的是你启动训练命令的工作目录,所以不要写成./road_obstacle/road_obstacle这种嵌套路径,除非目录结构真的长这样。train和val是相对path的图片目录,注意写的是图片目录,不是标签目录。训练器内部会把images这段字符串替换成labels去定位标签,如果你把标签和图片放在同一个目录,它反而找不到标签。

names的顺序必须和 class 文件一致。可以写成数组形式names: ['person', 'car', 'cyclist', 'traffic_cone'],效果等价。改任何一行之前,把 class 文件打开放在旁边逐行对应,这是我的死规矩。配好 data.yaml 后,下一步通常是下载对应 YOLO 预训练模型作为初始权重,不要让人家从头开始学特征提取,能省大量训练时间。

如果要把这份数据喂给 Detectron2 或 MMDetection,需要处理坐标格式:YOLO 的归一化中心点写法要换算成 xyxy 绝对坐标。换算公式不复杂:x1 = (cx - w/2) * img_w,y1 = (cy - h/2) * img_h,x2 = (cx + w/2) * img_w,y2 = (cy + h/2) * img_h。转换完做一次回读校验,把转出来的坐标重新画在图上,眼睛扫一遍确认没有漂移再进训练。这一步十几分钟,挡掉的是后面几轮白训。

4. 数据可视化脚本:用三类图快速核查标注质量

4.1 可视化输出看什么:框叠加图、类别柱状图、尺寸散点图

可视化脚本是这套数据集的质检工具。第一次跑,我会盯三类输出:框叠加图、类别分布柱状图、bbox 尺寸散点图。

框叠加图解决“标得对不对”的问题。脚本把每张图的标注框画回原图再拼网格,一眼能看出框有没有贴住目标外沿、是否漏掉远处小目标。半自动标注加上人工修正的流程里,最常见的毛病是大目标标得准,小目标被惯性跳过,框叠加图对这种漏标最敏感。如果一批连续图片里超过三成存在漏标,不要犹豫,先补标再继续。

类别分布柱状图告诉你“偏不偏”。4个类别的框数量差异超过5倍时,模型的预测会明显偏向样本多的类别。训练阶段的类别权重调整,也得先看这张图才能定系数。这份数据的可视化脚本会把 train 和 val 的框数分别统计,柱状图最能暴露划分是否按类别均衡。

bbox 尺寸散点图把每张图所有框的归一化宽和高画成二维散点。大量点挤在左下角 0.1 以下区域,说明数据里有大量小目标,默认的 640 输入分辨率会对它们不友好。这个信息直接决定你要不要把训练分辨率上调到 960 或 1280,属于投入产出比很高的预判。

4.2 跑通脚本的最小命令与 OpenCV 画框的细节

可视化脚本的主逻辑不复杂,核心是读取、转换、画框:

import os import cv2 def draw_boxes(img_dir, label_dir, out_dir, class_file): # 逐行读类别名,strip 去掉换行和可能的 BOM 残留 with open(class_file, 'r', encoding='utf-8') as f: classes = [line.strip() for line in f if line.strip()] os.makedirs(out_dir, exist_ok=True) for img_name in sorted(os.listdir(img_dir)): if not img_name.lower().endswith(('.jpg', '.png', '.jpeg')): continue stem = os.path.splitext(img_name)[0] # 图片名和标签名必须同前缀对应 label_path = os.path.join(label_dir, stem + '.txt') img = cv2.imread(os.path.join(img_dir, img_name)) if img is None or not os.path.exists(label_path): continue h, w = img.shape[:2] with open(label_path, 'r', encoding='utf-8') as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id, cx, cy, bw, bh = ( int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) ) # 归一化中心点坐标转像素左上右下 x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 类别名从 class 文件按 ID 索引 cv2.putText(img, classes[cls_id], (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite(os.path.join(out_dir, img_name), img)

四处理解清楚就够改这个脚本。第一,类别名读取后要strip(),否则classes[cls_id]里带着换行或不可见字符,画出来的文字不对,但程序还不报错,这种隐藏问题最难排查。第二,归一化坐标转像素坐标,宽高要先减半再加中心坐标,顺序反了框会整体偏移半个框宽,画面表现就是所有框朝右下角挪了一截。第三,cv2.putText的坐标是文字左下角,y1 - 8把文字顶在框上沿,y1小于 8 时会变成负数,OpenCV 直接不画,必须用max(0, y1 - 8)兜底。第四,脚本处理完要检查输出目录里有没有“空框图”,如果有图画了零个框,多半是标注坐标越界或文件前缀对不上。

调用命令一般是这种风格:

python visualize_labels.py \ --img_dir road_obstacle/images/train \ --label_dir road_obstacle/labels/train \ --class_file road_obstacle/classes.txt \ --out_dir output/train_check

--img_dir和--label_dir要分别指到 images/labels 下的同一子集目录,不要传根目录或混传 train 和 val。--out_dir不存在时脚本会自动建,建议把 train 和 val 的检查结果分开目录存,后面对照方便。跑完挑二十张有代表性的图看一眼,重点是画面深处的小目标有没有带框,以及框边缘是不是紧贴着目标轮廓。

5. 数据集落地使用中的避坑记录:漏标、错位与划分污染

5.1 训练 loss 不降,一跑可视化发现大量漏标

现象:我用这份数据训练 YOLOv8,前 20 个 epoch loss 一直高位震荡,val 的 mAP50 卡在 0.2 上下,调整学习率和 batch size 都没有本质变化。

原因:起初怀疑网络配置,后来把可视化脚本跑了一遍,发现连续三十多张图里,距离超过 15 米的锥桶全部没有标注框,所有框都集中在画面中下方近处目标上。这类漏标在半自动标注加人工修正的数据里非常普遍:标注员只盯着大目标修,小目标被惯性跳过。模型见不到远距离锥桶的正样本,自然学不会召回它。

解决:漏标不是训练能兜住的,必须回到数据侧修复。把可视化框叠加图里漏标严重的二十张挑出来,用标注工具补上小目标,重新生成标注文件后再跑一次可视化确认。补标后同样的训练参数,loss 在十几个 epoch 内明显下降。这个教训我记了很久:任何数据集上手,第一个动作永远是可视化抽样,而不是直接开训。

5.2 类别 ID 错位:class 文件顺序和标注 txt 对不上

现象:训练结束后查看混淆矩阵,person 类里混着大量 car 的预测,矩阵每行总和都不是正常的 100%。重新翻标注文件,框位置都正确,loss 也收敛了,找不到问题在哪。

原因:某次我把 class 文件里traffic_cone挪到了第二行,但标注 txt 里的 ID 还是按旧顺序写的。模型学到的是“ID 2 这个框对应这类外观”,而可视化脚本按新 class 文件把 ID 2 显示成另一个名字,于是人的认知和模型的认知完全错位。

解决:class 文件一旦定稿就不要再动顺序。如果非要在中途调整,必须同步重写所有标注 txt 内的 ID 并重新可视化抽检。我的检查技巧是:挑出 ID 为 3 的标注框,看可视化画出来的框是否落在锥桶这类目标上,如果框住的是汽车,说明 ID 已错位,趁早回滚。训练日志里混淆矩阵每行总和不为 1 是个明显信号,不要放过它。

5.3 连续视频帧同时进了 train 和 val

现象:train 的 mAP50 到 0.92,val 也有 0.89,看起来性能不错,但拿着模型去新的路段视频做实测,mAP 掉到 0.5 以下。

原因:数据来自连续视频抽帧,划分时只看文件名随机分,导致同一场景的相邻帧同时出现在 train 和 val。验证集和训练集高度相似,模型等于“复习”过考题,测出来的分数虚高。

解决:按场景划分而不是按文件名随机分。稳妥做法是先按时间戳或视频片段前缀给帧分组,把整个片段划入 train 或 val,保证 val 里不出现 train 的同源帧。这份数据集如果已经划分好,先确认划分逻辑,若不确定就重划一次,用场景前缀做 group 划分。重划后 mAP 可能略有下降,但那个数字才是真实泛化水平的参考。

5.4 可视化脚本在中文路径下崩溃

现象:在 Windows 上双击运行脚本,报UnicodeEncodeError或cv2.imread返回 None,一张图都没画出来。

原因:命令行默认编码不是 UTF-8,脚本拼接的中文路径传给 OpenCV 时解码失败。数据集放在D:\数据集\road_obstacle这类路径下必然触发。

解决:最低成本的改法是数据集解压到纯英文路径下跑。长期方案是脚本里把cv2.imread换成cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR),写文件时用cv2.imencode加tofile。这套组合是 Windows 中文路径下的标准解法,其余业务逻辑不用动。

5.5 小目标框在训练时被过滤

现象:训练日志里 targets 数量明显比标注文件目标总数少,可视化能看到的小目标在 loss 里几乎没贡献。

原因:YOLOv8 默认输入分辨率 640,标注框宽高小于 1 像素会直接被内部过滤;大于 1 像素但接近阈值的框,梯度占比也很低。数据里远距离锥桶的归一化宽高常在 0.03 附近,缩放到 640 像素后只有约 20 像素,属于典型小目标。

解决:两类调整。一是把训练分辨率提到 960 或 1280,等效放大小目标后再进网络,显存不够就降低 batch size 或开梯度累积。二是在增强阶段加大 scale 范围,让模型见过更多不同尺度的目标实例。决定要不要调之前,先看可视化脚本产出的尺寸散点图,如果小目标占比超过三成,这两个参数值得一改。

6. 把这份数据集用出效果:类别权重与混淆矩阵验证

6.1 类别不平衡时的 loss 权重调整

从可视化柱状图看到 4 类数量明显不均衡时,先别急着上采样,那会成倍拉长训练时间且容易过拟合。我一般先把各类样本比例算出来,按“总样本数 / 类别样本数”取倒数作为权重,再把权重乘到对应类别的 loss 项上。YOLOv8 里可以给data.yaml增加weights列表,或在传给model.train()的参数里给损失函数指定cls与对应系数;YOLOv5 的--cls参数也支持类似作用。调完先跑 30 个 epoch,看各类别 AP 是否拉平。多数场景下,给样本少的类别提高 loss 系数 1.5~3 倍就够,太高会导致模型过度关注小类而把大类框全漏掉。除了 loss 权重,我还习惯在增强阶段给数量少的类别单独做一次离线复制粘贴增强——把行人、锥桶的小图抠出来,贴到没有重叠目标的背景区域,再重新生成标注。这个操作比单纯复制整图更不容易过拟合,但前提是你已经跑过可视化脚本,确认背景中没有未标注的同类目标,否则贴上去的框会引发新的漏标。

6.2 训练完先看混淆矩阵,再谈 mAP

mAP 是单个数字,看不出错在哪类。我养成一个习惯:训练结束第一个输出不是 val 的 mAP,而是归一化混淆矩阵。矩阵每行代表真实类别,每列代表预测类别,行总和不为 1 通常意味着漏检,也就是有目标没被预测出。矩阵里最能说明问题的是对角以外的亮格:person 被错分成 cyclist,说明两类特征重叠,优先检查是不是标注框边界定得太松;car 被错分成 truck,则要回头核对类别定义在工作手册里是否写清楚了。看完矩阵,再结合这份数据集的可视化脚本把 val 预测结果画成框叠加图,挑几个典型错分场景存图。这两步做完,你会比只盯着 mAP 的人更快找到下一次数据迭代的方向。

数据准备阶段多花一小时看这三样东西,训练阶段就能少熬夜调两周。希望这个流程对你有帮助。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 4:28:16

Spring Boot毕业生就业数据填报小程序:需求、数据库、接口与答辩全解析

做计算机毕业设计,选“springboot毕业生就业数据填报小程序”这类题目的人特别多。这个题看着简单,实际做起来比想象中复杂得多——它不是一个普通的增删改查,而是一个带审核流程、多角色权限、统计汇总的数据收集系统。我从头到尾把这个项目…

作者头像 李华
网站建设 2026/10/1 4:27:39

2026企业邮箱选型迁移安全与管理实战指南

我最近在整理2026年企业邮箱续费方案时,发现一个很明显的变化:企业邮箱早就不再是“发信收信的软件”,而是权限管理、合规审计、AI协作入口和数据资产控制权的集合体。不少公司想换邮箱,诱因居然不是“容量不够”或“收费太高”&a…

作者头像 李华
网站建设 2026/10/1 4:27:39

2026企业邮箱选型避坑指南:从需求拆解到安全运维全攻略

如果你还在用个人QQ邮箱或者126邮箱给客户发报价单,那我觉得你离翻车不远了。这不是危言耸听,而是我这些年看过的真实事故:域名邮箱发出去的信被当成垃圾邮件拦截,离职员工手里还攥着公司客户列表,财务发发票被中间人掉…

作者头像 李华
网站建设 2026/10/1 4:27:37

真正的Alpha无法写进全自动代码:量化交易的核心认知

很多人第一次接触量化交易的时候,心里想的基本都是同一件事:写一套 python 量化交易策略代码,回测跑出漂亮曲线,挂到服务器上全自动运行,然后自己躺在沙滩上等钱进账。我在量化团队里待了这些年,见过太多抱…

作者头像 李华
网站建设 2026/10/1 4:27:10

AI编程代码风格不统一?Java团队规范落地实操指南

1. 问题到底出在哪:从一次代码评审说起上周组里来了个新同事,干活特别快。一个订单导出接口,从建表到联调,半天搞定,跑起来一点毛病没有。结果代码评审的时候,被组长打回去重写了三遍。理由不是有 bug&…

作者头像 李华
网站建设 2026/10/1 4:27:02

Python列表vs元组:可变性、内存机制与实战选型

列表和元组,是Python里最容易被初学者混为一谈的一对组合类型。很多人学的时候觉得"不就是方括号和圆括号的区别嘛",可真到了写代码的时候才发现各种别扭:为什么函数参数默认值不能放列表?为什么字典的键不能用列表但能…

作者头像 李华