
简介面向智能小车赛道自动驾驶场景的交通指示牌目标检测数据集覆盖左转、右转、红灯、绿灯、人行道等八个常见类别图像分辨率为两百乘一百二十的RGB彩色图片贴合赛道真实环境可服务于自动循迹、红绿灯识别、转向决策等任务。所有数据已按YOLOV5标准目录格式整理训练集包含九百零一张图片及对应的txt标签文件测试集包含二百二十五张图片及标签无需额外预处理训练集与验证集划分清晰可直接用于YOLO系列模型训练与精度评估。资源共两千个文件主体为八百七十二张jpg图片与一千一百二十七个txt标注文件与字典文件另附一个可直接运行的边界框可视化Python脚本随机传入一张图片即可绘制检测框并保存结果便于快速核对标注质量。压缩包大小约十点三一MB同时附带类别字典txt方便训练时核对类别索引与映射关系。目前已有151人学习适合正在落地智能小车视觉识别、或需要快速获取规范格式数据集的开发者与竞赛团队。1. YOLOv5目录格式的自动驾驶数据集先看目录再谈训练第一次拿到智能小车赛道数据集多数人会被“自动驾驶”“交通指示牌”这类词带偏直接上去调网络。我建议反过来先检查目录再打开一个txt标签最后才谈训练。这套YOLOv5目录格式的数据集共11MB训练集901张图片对应901个标签txt验证集225张图片对应225个标签txt图像分辨率统一为200×120的RGB图标注覆盖8个类别包含左转、右转、红灯、绿灯、人行道等赛道常见交通指示牌。图像尺寸不大类别也不算多但正好贴近智能小车摄像头循迹、智能导航避障小车这类场景。数据已经按datasets/images/train、datasets/images/val保存不需要额外转格式属于拿到手就能进训练管线的那类资源。这组数据里真正值得拆的不是“自动驾驶”这个词而是标签文件怎么组织、类别字典怎么对应、低分辨率下训练参数怎么调。我按目录结构、标签校验、训练命令、数据增强四条线往下拆最后会用一个迁移技巧把赛道数据用到交通物流场景里。2. labels标注文件与类别字典txt格式如何被YOLOv5读取YOLOv5的训练脚本不关心图片里有什么只按目录找同名txt再把每一行解析成一个边界框。理解这套逻辑比急着点train.py更重要。2.1 目录结构怎么看压缩包解压后首先确认是否满足这样的YOLOv5约定datasets/ ├── images/ │ ├── train/ # 901 张图片 │ └── val/ # 225 张图片 ├── labels/ │ ├── train/ # 901 个标签 txt │ └── val/ # 225 个标签 txt ├── classes.txt # 类别字典 └── visualize.py # 可视化脚本图片和标签的主文件名必须完全一致例如790.jpg对应790.txt中间只差扩展名。实际项目里如果看到labels里多了一个没有图片的txt或者图片没有对应txt训练时容易被静默跳过或报FileNotFoundError。先跑一个数量对比ls images/train | wc -l和ls labels/train | wc -l都应该是901。这是最便宜的检查也比任何代码都直观。类别字典classes.txt保存的是8个类别的名称通常一行一个。我一般先执行cat classes.txt看顺序再随机打开一个标签cat classes.txt cat labels/train/790.txtclasses.txt里的顺序就是模型输出层的类别ID顺序不是按字母表排就安全。比如第0行是“左转”那所有标签里第一列为0的框都表示左转后续转成COCO格式或者做模型部署时这个顺序必须原样保留。我用一张表说明类别ID和训练脚本的关系类别IDclasses.txt示例训练时names对应0左转names[0]1右转names[1]2红灯names[2]3绿灯names[3]4人行道names[4]5-7以classes.txt实际内容为准names[5]-names[7]数据里标注了8类但其中3个类别名称没有在描述中明确列出一切以压缩包里classes.txt为准。常见的坑是把classes.txt里的类别再排序比如想按“红灯、绿灯、人行道”重排结果所有标签跟着错位训练指标看起来正常实际推理输出全是错位标签。2.2 YOLO格式标签如何解析YOLOv5的标签文件每一行是5个数字空格分隔class_id x_center y_center width height注意这里不是角点坐标而是中心点坐标和宽高并且都做了归一化。原图是200×120所以一个框的x_center如果等于0.5对应像素坐标x就是0.5 * 200 100像素。用Python读一下更直观with open(datasets/labels/train/790.txt, r, encodingutf-8) as f: for line in f: cid, xc, yc, w, h map(float, line.split()) print(fclass{int(cid)}, center({xc:.3f},{yc:.3f}), fsize({w:.3f},{h:.3f}))line.split()按任意空白字符切分YOLO官方工具写的是空格但制表符也能被split处理。map(float, ...)把字符串转成浮点数cid最后要int是因为后续要当数组下标用。如果某一行只有4个数或者6个数通常是标注软件导出配置错了这类文件需要提前筛掉。2.3 归一化坐标转像素坐标可视化、计算IoU、统计目标大小都先把归一化坐标还原成像素坐标。转换关系很简单x_min (x_center - width / 2) * img_width y_min (y_center - height / 2) * img_height box_w width * img_width box_h height * img_heightimg_width200img_height120。如果算出来的box_w只有15像素说明该目标在整张图里占很小比例归到“小目标检测”范畴。这类目标在YOLOv5默认640输入尺寸下会被放大3倍以上标签不需要改因为归一化坐标和分辨率无关。3. 智能小车训练前的数据校验目录、标签与可视化脚本拿到任何目标检测数据集我都会先跑一遍底噪检查。这一步不能省尤其是从网盘、学校项目、比赛群分享来的数据最常见的问题是“看起来没问题一训练就少样本”。3.1 检查图片与标签是否一一对应写一个不到20行的Python脚本遍历图片目录import os from pathlib import Path img_dir Path(datasets/images/train) label_dir Path(datasets/labels/train) img_count 0 missing [] for img_path in sorted(img_dir.glob(*.jpg)): img_count 1 label_file label_dir / (img_path.stem .txt) if not label_file.exists(): missing.append(img_path.name) print(f图片总数: {img_count}) print(f缺少标签: {len(missing)}) for name in missing[:10]: print(name)img_path.stem返回的是去掉扩展名的文件名比如790.jpg的stem是790。用sorted排序保证输出稳定不然每次打印的顺序可能不同。这个脚本的输出如果缺少标签不为0不要急着训练先回到标注软件或源目录补齐YOLOv5在数据加载阶段遇到缺失会跳过图片最后的mAP会偏低且难以定位原因。3.2 校验标签内容是否越界YOLOv5对标签的容错有限坐标越界并不总是报错但会让损失函数出现nan或者收敛变慢。我一般做两级检查第一是类别ID在0到nc-1之间第二是中心和宽高都在0到1之间宽高必须大于0。from pathlib import Path from collections import Counter label_dir Path(datasets/labels/train) nc 8 dist Counter() errors [] for txt in sorted(label_dir.glob(*.txt)): for line_no, line in enumerate(txt.read_text().strip().splitlines(), 1): parts line.split() if len(parts) ! 5: errors.append(f{txt.name}:{line_no} 列数{len(parts)}) continue cid, xc, yc, w, h map(float, parts) if not (0 cid nc): errors.append(f{txt.name}:{line_no} 类别越界{cid}) if not (0 xc 1 and 0 yc 1): errors.append(f{txt.name}:{line_no} 中心坐标越界) if w 0 or h 0: errors.append(f{txt.name}:{line_no} 宽高非正) dist[int(cid)] 1 print(类别分布:, dist.most_common()) print(错误条数:, len(errors))这段代码可以同时统计类别分布。红绿灯类别样本多、人行道样本少的时候阶段性的loss会被多数类主导模型偏向学大类特征。看到分布后再决定要不要对少样本做复制增强或者调整类别权重。注意我在判断里用了0 xc 1严谨一点应该用 1但很多标注工具会生成等于1的边界值训练时YOLOv5能做裁剪所以这里放宽容错。3.3 可视化脚本随机抽一张画框数据集中自带的visualize.py就是干这件事的。常见用法是直接在脚本里指定图片路径后运行脚本会读取同名标签文件画出框并保存到当前目录。如果脚本支持命令行参数可以这样写python visualize.py --image datasets/images/val/790.jpg \ --label datasets/labels/val/790.txt \ --output out_790.jpg我自己更习惯用OpenCV实现一版等价逻辑方便后续集成到随机采样脚本里import cv2 import sys img_path sys.argv[1] label_path sys.argv[2] out_path sys.argv[3] img cv2.imread(img_path) h, w img.shape[:2] colors [(0, 255, 0), (0, 0, 255), (255, 0, 0), (0, 255, 255), (255, 0, 255), (255, 255, 0)] with open(label_path) as f: for line in f: cid, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) color colors[int(cid) % len(colors)] cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, str(int(cid)), (x1, max(0, y1 - 4)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imwrite(out_path, img) print(fsaved to {out_path})int((xc - bw / 2) * w)做的是从归一化坐标到像素坐标再取整OpenCV画框要求整数坐标直接用float会报TypeError。颜色数组只有6组8个类别会循环取色这只是可视化用和训练无关。如果你在服务器上跑记得安装的是opencv-python-headless而不是完整版避免依赖GUI库报错。4. 用YOLOv5训练与验证的命令行参数与常见坑数据校验通过后训练本身反而是流程化的。但参数怎么设不同分辨率、不同场景差别很大。4.1 准备dataset.yamlYOLOv5不直接读classes.txt而是读一个数据配置文件。我在项目根目录建data.yamltrain: datasets/images/train val: datasets/images/val nc: 8 names: 0: left_turn 1: right_turn 2: red_light 3: green_light 4: crosswalk 5: class_5 6: class_6 7: class_7这是按8类别占位写的其中class_5到class_7要按classes.txt里实际名称替换。train和val指向的是图片目录不要指向images根目录。nc必须和names列表长度一致YOLOv5训练脚本会校验这个值。类别名用英文或拼音避免画预测结果图时中文乱码尤其当服务器缺少中文字体时会影响排错效率。4.2 训练命令与参数含义在YOLOv5仓库根目录执行训练python train.py --img 200 --batch 16 --epochs 100 \ --data data.yaml --weights yolov5s.pt \ --device 0 --cache ram参数说明如下参数含义本项目建议--img训练输入尺寸200但YOLOv5会向上取整到224--batch每批图片数16显存不足降为8--epochs完整训练轮数100起步--data数据集配置路径data.yaml--weights预训练权重yolov5s.pt--device计算设备0表示第一块GPUCPU设cpu--cache是否缓存图片到内存ram可加速小数据集迭代200×120的图输入后会先letterbox到stride的倍数。YOLOv5默认stride是32所以200被调整到224。这里的200是短边还是长边取决于YOLOv5版本训练脚本会按输入尺寸和图片纵横比做缩放。标签文件是归一化的所以resize过程不影响标注坐标。如果想快速看一次完整流程可以把epochs改成10batch改成8。数据集总共只有11MBCPU也能跑只是慢一些。训练结束后会自动在val集上评估结果保存在runs/train/exp/weights/best.pt和last.pt。4.3 常见坑与对策第一个坑是输入尺寸过小。200×120原始像素很低强行用640训练会先把图放大到640图像变模糊模型学到的纹理特征偏弱。我在类似场景下会先用320或416训练一个baseline看mAP变化再用200微调几轮用于边缘端推理。标签不需要重新标因为归一化坐标在任意分辨率下都成立。第二个坑是anchor与目标尺度不匹配。YOLOv5的默认anchor是按COCO数据集聚类出来的本数据集中交通牌在200×120图里可能只有20×20像素。常见做法不是手动改anchor而是用--img 320让目标在输入图上占更大面积或者用小模型yolov5n.pt减少过拟合。第三个坑是红绿灯这种类别对翻转敏感。训练时如果开启fliplr红灯和绿灯的位置会互换语义不变但“灯在左边还是右边”这类位置特征会被破坏。最好在hyp配置里调低甚至关闭fliplr具体下一章说。另外数据集路径和YOLOv5仓库路径尽量全英文OpenCV在部分系统上读取中文路径会直接返回None训练进程不报错但所有样本的标签都被跳过损失一直是0。遇到这种情况先cv2.imread一张图确认是否为空。5. 数据增强与迁移从赛道8类到交通物流场景赛道数据集的8类交通牌本质上和交通物流场景里的指示牌、红绿灯同源。我不建议重新收集几万张图训练而是用这套数据做增强和迁移的试验床。5.1 按小车场景修改hyp.yamlYOLOv5训练时默认读取data/hyps/hyp.scratch-low.yaml。可以复制一份成my_hyp.yaml只覆盖要改的项hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 degrees: 0.0 translate: 0.1 scale: 0.5 shear: 0.0 perspective: 0.0 flipud: 0.0 fliplr: 0.0 mosaic: 1.0flipud和fliplr对小车赛道不是必需的赛道本身没有纵向翻转场景左右翻转会改变红绿灯、左右转指示牌的空间语义所以我全关掉。degrees0避免旋转让“人行道”这类横条标志变得难以辨认。scale0.5保留缩放增强模拟小车摄像头与指示牌距离变化。修改后训练时加参数python train.py --data data.yaml --weights yolov5s.pt \ --img 320 --epochs 80 --batch 16 --hyp my_hyp.yaml用这套增强重新训练后再看runs/train/exp/val_batch0_pred.jpg低分辨率下的漏检率通常会明显下降特别是红灯、绿灯这类小目标。5.2 冻结前面层做迁移如果要把赛道模型迁移到更复杂的交通物流场景比如工厂里的搬运小车识别货物标识可以在已有best.pt基础上冻结浅层特征继续训练python train.py --data new_data.yaml --weights runs/train/exp/weights/best.pt \ --img 320 --epochs 50 --freeze 10--freeze 10表示冻结前10层这些层学到的是边缘、颜色、纹理等通用特征不需要在新场景重新学。冻结后显存占用和可训练参数量都会下降适合在小batch下跑迁移。注意--freeze后面跟的是层数不是epoch数网上很多教程把这两个混在一起。5.3 验证时用同一套参数最后验证模型时不建议随手换conf和iou阈值。训练默认置信度是0.001NMS IoU是0.45验证时我一般保持一致python val.py --data data.yaml --weights runs/train/exp/weights/best.pt \ --img 200 --conf-thres 0.25 --iou-thres 0.45--conf-thres 0.25能过滤掉低置信度预测但如果你在部署到Jetson Nano这类设备后发现漏检严重第一反应不是降conf而是回到--img 320重训。200×120的原图分辨率上限摆在那里后处理只能调阈值不能恢复丢失的细节。把conf降到0.1往往引入一堆误检不如输入尺寸提高到320来得直接。训练和验证的img参数保持一致否则mAP会虚高或虚低。本文还有配套的精品资源点击获取