
简介这是一份面向计算机视觉与智能交通方向开发者、研究者的交通锥桶与路障目标检测数据集标签为barricade可用于训练和验证车辆环境感知、道路障碍物识别等算法适合具备一定深度学习基础的目标检测实践者。压缩包共1561个文件包含520张jpg图像、520个xml标注文件与521个txt标签文件xml对应VOC格式的边界框与类别信息txt对应YOLO格式的归一化标注两种格式并行便于在Faster R-CNN、YOLO等不同框架间直接切换训练。资源包整体约88.64MB规模适中便于本地快速加载与迭代实验。目前已有1747人学习下载说明其在交通场景目标检测任务中具有一定参考价值。读者可借助该数据集完成从数据读取、格式转换到模型训练与精度评估的完整流程并针对锥桶、路障等小目标在复杂交通场景下的检测难点开展对比实验为自动驾驶安全行驶与智能交通系统研究提供可复用的数据基础。1. 交通锥桶路障数据集从标注文件到可训练模型的完整路径拿到一个交通锥桶路障数据集最怕的不是数据量不够而是标注格式和训练框架对不上。我见过太多人把压缩包解压完看到一堆 XML、JSON 和 JPG 混在一起就懵了——明明标注框画得挺准跑起来却全是漏检。问题往往出在格式转换那一步VOC 的bndbox坐标是左上右下YOLO 要的是归一化中心点加宽高中间但凡有一个类别映射写错训练出来的模型就只认“锥桶”不认“路障”。这个数据集的核心价值在于它同时覆盖了锥桶、水马、警示柱、施工围挡这几类常见路面障碍物场景从城市快速路到夜间施工区都有适合做交通场景的细粒度检测。如果你正在做自动驾驶感知、道路巡检或者施工区域安全监控这套数据能帮你省掉至少两周的采集和标注时间。但前提是你得先把它跑通。2. 拆开压缩包先看什么目录结构与标注格式的对应关系2.1 典型目录布局与文件命名规律一个结构清晰的交通锥桶路障数据集解压后通常长这样dataset/ ├── annotations/ # 标注文件 │ ├── 000001.xml │ ├── 000002.xml │ └── ... ├── images/ # 原始图片 │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── ImageSets/ # 训练/验证/测试划分 │ └── Main/ │ ├── train.txt │ ├── val.txt │ └── test.txt └── classes.txt # 类别名称列表先别急着写训练脚本用三条命令把家底摸清楚。第一条看图片总数和格式分布# 统计图片数量和扩展名分布 find dataset/images -type f | sed s/.*\.// | sort | uniq -c # 输出示例1200 jpg 300 png第二条看标注文件数量是否和图片一一对应# 对比图片和标注文件数量 echo images: $(ls dataset/images | wc -l) echo annotations: $(ls dataset/annotations | wc -l)如果两个数字差得远说明有图片没标或者标注文件命名对不上这种脏数据直接进训练会引入大量背景误检。第三条抽查一个标注文件的内容# 查看第一个标注文件的类别和坐标 head -50 dataset/annotations/000001.xml重点看name标签里的类别名有没有拼写不一致——比如cone和traffic_cone混用这种问题在多人协作标注的数据集里特别常见训练时会被当成两个完全不同的类。2.2 VOC 与 YOLO 格式的核心差异交通锥桶路障数据集最常见的标注格式是 Pascal VOC XML因为标注工具默认导出这个。但训练时大家普遍用 YOLO 系列所以转换是绕不开的。两者的核心差异就三点维度VOC XMLYOLO TXT坐标表示左上角 (xmin, ymin) 和右下角 (xmax, ymax)中心点 (x_center, y_center) 加宽高数值范围绝对像素值归一化到 0~1类别存储标注文件内嵌name独立classes.txt 行首类别索引转换公式本身不复杂但坑在于VOC 的坐标可能超出图片边界YOLO 要求所有值在 0~1 之间。我一般会在转换脚本里加一步裁剪把越界的框拉回来而不是直接丢弃——因为越界往往意味着标注时手抖了框本身还是有效的。2.3 用脚本完成格式转换与数据集划分下面这个脚本把 VOC 转 YOLO、生成classes.txt、按 8:1:1 划分训练验证测试集一次跑完import os import xml.etree.ElementTree as ET import random from pathlib import Path # 配置路径和类别 DATASET_ROOT Path(dataset) ANNOTATIONS_DIR DATASET_ROOT / annotations IMAGES_DIR DATASET_ROOT / images OUTPUT_LABELS_DIR DATASET_ROOT / labels CLASSES [cone, water_barrier, warning_post, construction_fence] # 创建输出目录 OUTPUT_LABELS_DIR.mkdir(exist_okTrue) for split in [train, val, test]: (DATASET_ROOT / ImageSets / Main).mkdir(parentsTrue, exist_okTrue) def voc_to_yolo(xml_path, img_width, img_height): 将单个 VOC XML 转为 YOLO 格式的行列表 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in CLASSES: continue # 跳过未定义类别 cls_id CLASSES.index(cls_name) 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) # 裁剪越界坐标 xmin max(0, min(xmin, img_width)) xmax max(0, min(xmax, img_width)) ymin max(0, min(ymin, img_height)) ymax max(0, min(ymax, img_height)) # 转为中心点宽高并归一化 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return lines # 遍历所有标注文件 all_files [] for xml_file in sorted(ANNOTATIONS_DIR.glob(*.xml)): img_file IMAGES_DIR / (xml_file.stem .jpg) if not img_file.exists(): img_file IMAGES_DIR / (xml_file.stem .png) if not img_file.exists(): print(f跳过无对应图片: {xml_file.name}) continue # 读取图片尺寸用 PIL 或 opencv 均可这里用 PIL from PIL import Image with Image.open(img_file) as im: w, h im.size yolo_lines voc_to_yolo(xml_file, w, h) if not yolo_lines: continue # 写入 YOLO 标签文件 label_path OUTPUT_LABELS_DIR / (xml_file.stem .txt) label_path.write_text(\n.join(yolo_lines)) all_files.append(xml_file.stem) # 随机划分 8:1:1 random.seed(42) random.shuffle(all_files) n len(all_files) train_end int(n * 0.8) val_end int(n * 0.9) splits { train: all_files[:train_end], val: all_files[train_end:val_end], test: all_files[val_end:] } for split_name, stems in splits.items(): txt_path DATASET_ROOT / ImageSets / Main / f{split_name}.txt txt_path.write_text(\n.join(stems)) print(f{split_name}: {len(stems)} 张) # 写入类别文件 (DATASET_ROOT / classes.txt).write_text(\n.join(CLASSES)) print(转换完成)这段代码的关键逻辑有三处。第一voc_to_yolo里先做坐标裁剪再归一化避免出现负数或大于 1 的值导致训练时 loss 爆炸。第二类别过滤用if cls_name not in CLASSES: continue把不在预定义列表里的标注直接跳过而不是报错中断——实际数据里经常混入几个标错的类别。第三划分时固定random.seed(42)保证每次运行结果一致方便复现实验。参数方面CLASSES列表必须和标注文件里的name完全一致大小写敏感划分比例 8:1:1 适合数据量在 1000~5000 张之间的场景如果数据量少于 500 张建议改成 7:1.5:1.5 或者做交叉验证。3. 训练参数怎么设从 anchor 聚类到学习率调度3.1 先跑 anchor 聚类再定网络结构交通锥桶和路障的尺度差异很大远处的锥桶可能只有 20×30 像素近处的施工围挡能占满半个画面。直接用 COCO 的默认 anchor 去训小目标召回率会很难看。我一般先用 K-means 在标注框上聚一遍import numpy as np from pathlib import Path # 读取所有 YOLO 格式标注的宽高 labels_dir Path(dataset/labels) wh_list [] for txt_file in labels_dir.glob(*.txt): for line in txt_file.read_text().strip().split(\n): parts line.split() if len(parts) 5: w, h float(parts[3]), float(parts[4]) wh_list.append([w, h]) wh_array np.array(wh_list) print(f标注框总数: {len(wh_array)}) print(f宽高范围: w [{wh_array[:,0].min():.4f}, {wh_array[:,0].max():.4f}]) print(f h [{wh_array[:,1].min():.4f}, {wh_array[:,1].max():.4f}]) # 简单 K-means 聚类k9 from sklearn.cluster import KMeans kmeans KMeans(n_clusters9, random_state42, n_init10) kmeans.fit(wh_array) anchors kmeans.cluster_centers_ # 按面积排序输出 anchors anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] for i, (w, h) in enumerate(anchors): print(fanchor {i}: {w:.4f} x {h:.4f} (像素参考: {w*640:.0f} x {h*640:.0f}))跑完你会得到 9 组归一化宽高。如果最小那组对应的像素尺寸小于 16×16说明数据里小目标占比高网络输入分辨率至少要设到 640最好用 1280 再训一版对比。anchor 数量保持 9 个不变但具体数值要替换掉配置文件里的默认值。3.2 学习率与 warmup 的配合策略YOLO 系列默认用 SGD 加余弦退火初始学习率 0.01。但交通锥桶数据集有个特点背景占比极高正样本稀疏。如果一上来就用 0.01前几个 epoch 梯度会被背景主导模型学到的全是“预测背景”。我的做法是加 3 个 epoch 的 warmup学习率从 0.001 线性升到 0.01再走余弦退火# 训练配置片段以 YOLOv8 风格为例 lr0: 0.01 lrf: 0.01 # 最终学习率 lr0 * lrf warmup_epochs: 3 warmup_momentum: 0.8 warmup_bias_lr: 0.1 box: 7.5 # 框回归损失权重 cls: 0.5 # 分类损失权重 dfl: 1.5 # 分布焦点损失权重box权重设 7.5 是因为锥桶的定位精度比分类更重要——把锥桶认成水马还能接受但框偏了 50 像素在自动驾驶里就是事故。cls降到 0.5 是因为类别本身区分度够大不需要太强的分类梯度。如果训练时发现cls_loss降得比box_loss快很多说明分类太容易了可以再降cls权重把容量让给定位。3.3 数据增强的边界哪些增强会帮倒忙交通锥桶数据集的增强策略要克制。Mosaic 和 MixUp 能提升小目标召回但会把不同场景的锥桶拼在一起导致模型学到“锥桶可以出现在任何背景上”的错误关联。我的经验是Mosaic 概率设 0.5 而不是默认的 1.0MixUp 直接关掉。HSV 增强里色调扰动范围要收窄因为锥桶的橙色是强判别特征色调转太多会变成红色消防栓。# 增强配置 hsv_h: 0.010 # 色调扰动默认 0.015 偏大 hsv_s: 0.5 # 饱和度扰动 hsv_v: 0.3 # 明度扰动 degrees: 5.0 # 旋转角度交通场景锥桶基本直立 translate: 0.1 # 平移 scale: 0.3 # 缩放 shear: 2.0 # 剪切 perspective: 0.0 # 透视变换关掉会引入不真实的形变 flipud: 0.0 # 上下翻转关掉锥桶不会倒过来 fliplr: 0.5 # 左右翻转保留 mosaic: 0.5 mixup: 0.0flipud必须关掉因为倒置的锥桶在物理上不存在开了只会让模型困惑。perspective也建议关交通场景的相机畸变在采集时已经固定了训练时再加透视变换等于引入噪声。4. 避坑与排查标注和训练里最容易翻车的五个点4.1 类别名大小写不一致导致类别数翻倍现象训练日志里classes显示 8 个类但classes.txt里只写了 4 个。原因标注文件里同时存在Cone和cone转换脚本按字符串精确匹配生成了两个不同的类别索引。解决在转换脚本开头加一步统一小写cls_name obj.find(name).text.strip().lower()同时检查classes.txt是否也统一了小写。4.2 图片和标注文件命名对不上导致静默丢数据现象训练完发现验证集 mAP 很高但实际测试时漏检严重。原因部分图片的扩展名是.jpeg而标注是.xml转换脚本只找了.jpg和.png这些图片被静默跳过没进训练集。解决在遍历时用img_file list(IMAGES_DIR.glob(xml_file.stem .*))匹配所有扩展名并打印跳过的文件列表不要静默处理。4.3 越界框未裁剪导致 loss 出现 NaN现象训练几个 epoch 后 loss 突然变成 NaN。原因VOC 标注里xmax大于图片宽度归一化后值大于 1计算宽高时出现负值开方后 NaN。解决转换时强制裁剪并在裁剪后检查width 0 and height 0不满足的框直接丢弃。同时加一行日志记录被裁剪的框数量如果超过总框数的 5%说明标注质量有问题需要回头修标注。4.4 背景图未参与训练导致误检率偏高现象模型在空旷路面上把白色标线认成锥桶。原因训练集里全是含目标的图片模型没见过“没有锥桶的路面”长什么样学到的是“白色长条锥桶”。解决从数据集中挑 5%~10% 的纯背景图没有标注框的图片在 YOLO 格式下生成空的.txt文件一起参与训练。这些图会教模型抑制背景误检。4.5 验证集和训练集场景重叠导致指标虚高现象验证集 mAP 0.9但换一段路测就掉到 0.5。原因随机划分时同一段连续视频的帧被分到了训练集和验证集两边的场景几乎一样。解决按视频片段或采集批次划分而不是按单帧随机分。如果数据里没有片段信息用图片的 EXIF 时间戳或文件修改时间做分组同一分钟内的图片归到同一侧。5. 用五十张图快速验证数据集可用性的技巧数据集值不值得投入不用等训完 300 个 epoch 才知道。我的习惯是从训练集里抽 50 张覆盖不同场景的图用预训练模型做一次 10 epoch 的微调看三个指标——box_loss是否稳定下降、cls_loss是否在 5 个 epoch 内降到 0.5 以下、验证集上锥桶的召回率是否超过 0.6。如果这三个都满足说明标注质量和类别区分度没问题可以放心跑完整训练。如果box_loss震荡或者召回率低于 0.4先别调参回去查标注——大概率是框画歪了或者类别标错了。# 快速验证命令以 YOLOv8 为例 yolo detect train \ datadataset.yaml \ modelyolov8n.pt \ epochs10 \ imgsz640 \ batch16 \ lr00.001 \ warmup_epochs1 \ mosaic0.0 \ valTrue \ plotsTrue这里mosaic0.0是为了排除增强干扰先看模型在原始数据上的拟合能力。lr00.001比正常训练低一个数量级因为只跑 10 个 epoch学习率太大会直接跳过最优解。跑完后看runs/detect/train/val_batch0_pred.jpg如果预测框和真实框的重叠度肉眼可见地高说明数据没问题如果框到处乱飞先检查dataset.yaml里的nc和names是否和classes.txt一致。还有一个更快的办法用labelimg或anylabeling打开几张训练图肉眼过一遍标注框。我一般会重点看夜间图和远距离小目标图——这两类最容易标错。夜间图的锥桶反光会导致标注员把反光区域也框进去远距离小目标则经常漏标。如果这两类图的标注质量过关整个数据集的质量基本就有保障了。最后说个血泪教训别在格式转换上省事。我见过有人直接用网上找的转换脚本跑完不检查就开训结果类别索引全错位——锥桶的标签指向了水马训出来的模型把锥桶全认成水马。后来花了三天返工。现在我的习惯是转换完先跑一个verify.py随机抽 20 张图把 YOLO 标签画回原图上看一眼确认框的位置和类别都对得上再进训练。这个步骤花不了十分钟但能省掉后面几天的后悔药。希望帮到你。本文还有配套的精品资源点击获取