红绿灯目标检测数据集:1000张图+三格式标签+YOLO训练全流程 简介面向目标检测学习与实战训练的YOLO红绿灯数据集包含1000张真实场景高质量图片数据场景丰富覆盖不同路口、时间段与拍摄角度能较好支撑交通灯检测算法的训练与评估。使用LabelImg软件完成人工标注标注框质量高同时提供VOCxml、COCOjson、YOLOtxt三种格式标签并分别存放在独立文件夹中无需格式转换即可直接接入YOLO系列模型训练。压缩包共2000个文件以1000个xml和990个txt标签文件为主另有6个HTML教程与说明文档、3个Python数据划分脚本和1个yaml配置文件整体大小487.95MB。随包附赠Linux与Windows环境搭建教程、YOLO训练案例教程并配套训练集/验证集/测试集划分脚本便于用户按实际需求重组数据、快速开始实验。已有865人学习下载适合正在做红绿灯检测、自动驾驶视觉感知相关课程设计、毕业设计或算法复现的学生与研究人员。1. 红绿灯目标检测数据集为什么值得单独攒一套1000张图、三格式标签和一套可复现流程做自动驾驶或辅助驾驶感知的人都有个共识红绿灯检测是目标检测里最容易翻车的一类任务。通用数据集里也有 traffic light 这个类别但真正把模型拿到真实路口跑一遍你会发现白天逆光、夜间过曝、灯色和倒计时数字混在一起的情况通用模型基本扛不住。这套 YOLO红绿灯目标检测数据集包含1000张图片并且把 voc、coco、yolo 三种格式的标签全部配齐附带划分脚本和训练教程属于「拿到手不用再花一周转格式、踩划分坑」的落地型资源。适合正在做红绿灯识别、车路协同或智慧交通项目需要一个干净数据集把模型先跑通的人。2. 三种标签格式的分工voc、coco、yolo 各自解决什么问题2.1 voc 负责「人能看懂」coco 负责「机器好读」yolo 负责「训练直达」很多第一次接触三格式数据集的人会问既然 yolo 训练只需要 txt 文件为什么还要保留 voc 和 coco 的标签答案是三个格式在一条数据流水线里各管一段。voc 格式的标注存在 xml 文件里标签是object节点下的bndbox四个坐标是xmin ymin xmax ymax人眼直接打开就能对着图片检查框是不是标歪了是最适合做标注质检和人工修正的格式。coco 格式则是把整个数据集的所有标注汇总进一个annotations.json结构里包含images、annotations、categories三张表坐标变成bbox加area并且标注类别有全局统一的 id适合做跨数据集的评测基准和预训练迁移。yolo 格式是每张图对应一个同名 txt 文件每行记录class cx cy w h坐标是归一化后的中心点和宽高模型加载标签时不用做任何换算直接喂给 loss 计算。三格式标签同时存在最实用的意义是你可以随时换训练框架。今天用 ultralytics 的 yolo v8 跑一版明天想拿 detectron2 复现一个 Faster R-CNN 做对比实验后天天想用 mmdetection 跑 COCO 评测不需要重新标注直接转换即可。从工程效率上讲三格式配齐省掉的不是一次转换时间而是标注人员几天的重复劳动。常见做法是标注阶段用 voc 格式做存储和 review随后再用脚本批量转出 coco 和 yolo这个数据集落地时就把这条链路走完了。2.2 坐标换算逻辑从 voc 的左上右下到 yolo 的中心宽高三种格式的坐标换算在转换脚本里是最容易写错的地方。voc 给的是绝对像素坐标xmin ymin xmax ymaxyolo 要的是相对图片宽度和高度的归一化中心坐标换算公式如下。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() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) with open(out_txt_path, w) as f: for obj in root.findall(object): cls obj.find(name).text if cls 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) center_x (xmin xmax) / 2.0 / img_w center_y (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h f.write(f{class_map[cls]} {center_x:.6f} {center_y:.6f} {width:.6f} {height:.6f}\n)这段代码的核心是先把 xml 里的文件名、图片宽高和 bndbox 坐标取出来然后做两次除法一次把左上右下角坐标转成中心点一次把宽高转成相对宽高。class_map需要手动定义类别到数字 id 的映射比如{red: 0, green: 1, yellow: 2}这一步决定后续训练时类别顺序是否和 data.yaml 一致。参数说明里最容易被忽略的是img_w和img_h必须取原始图片的宽高而不是标注工具界面显示的尺寸。如果图片在标注前被压缩过xml 里记录的尺寸和实际图片不一致转换出来的 yolo 标签全部偏移训练时模型学到的边界框是错位的。另一个常见坑是浮点精度yolo 标签一般保留 6 位小数就够了过长的尾数会让 txt 文件体积膨胀且没有任何精度收益。2.3 从 voc 转 coco 时 bbox 格式的坑xywh还是xyxy从 voc 转 coco 的常见误区是把 bbox 直接写成[xmin, ymin, xmax, ymax]。coco 官方标注里 bbox 定义是[x, y, width, height]也就是左上角坐标加宽高和 voc 的左上右下完全是两种语义。更隐蔽的是area字段它应该等于宽乘以高而不是四个坐标值的某个差值。如果从 voc 转 coco 时忘记把xmax - xmin算成width训练时 coco 评测脚本计算 IoU 会直接出错。转 coco 的另一个细节是annotation里的id必须全局递增唯一image_id要能对应到images表里的 id。很多人转换时直接用循环变量做 id一旦图片顺序在后续处理中被 shuffleimage_id 就和图片对应不上评测分数会莫名掉几个点。建议的做法是先把图片列表定死按文件名排序后给每张图分配 id再遍历标注生成 annotation 列表。3. 用划分脚本把1000张图拆成训练集、验证集和测试集3.1 随机划分为什么会让小类别直接消失1000张图看起来不少但红绿灯数据有一个特点类别天然不均衡。同一张图里红灯可能只占十来个像素黄灯的出现频率远低于红灯和绿灯。如果直接按 8:1:1 做随机抽样划分黄灯这类小样本类别很可能在验证集里一条都没有训练时模型会认为「只要不是红灯就是绿灯」验证集却因为恰好包含一张黄灯图而给模型打出低分——这不是模型变差了是划分本身毁掉了数据分布。所以划分脚本必须做分层抽样按每个类别在图片中的出现频率确保训练集、验证集、测试集里各类别占比接近总体分布。红绿灯数据还有一个特性同一摄像头同一路口连续拍出来的几十帧图非常相似。如果这些帧被随机拆到训练集和验证集相当于验证集里出现了训练集的近复制样本mAP 虚高几倍模型一上真实路口立刻现原形。因此划分前要按拍摄批次或者场景先做分组再对组内做整体划分。3.2 可直接运行的按类别分层划分脚本下面是一个按类别分层、同时支持按场景分组划分的脚本可以直接改路径使用。import os import random import shutil from collections import defaultdict from sklearn.model_selection import train_test_split def load_yolo_labels(txt_path): 读取 yolo txt 标签返回该图片包含的类别集合 classes set() with open(txt_path, r) as f: for line in f: parts line.strip().split() if parts: classes.add(int(parts[0])) return classes def group_images_by_scene(image_dir): 按文件名前缀分组例如 scene_001_frame_002.jpg 归入 scene_001 组 groups defaultdict(list) for name in os.listdir(image_dir): if name.endswith((.jpg, .png)): scene_id _.join(name.split(_)[:2]) groups[scene_id].append(name) return groups def stratified_split(image_dir, label_dir, train_ratio0.8, val_ratio0.1, seed42): random.seed(seed) groups group_images_by_scene(image_dir) image_paths [] for scene_id, names in groups.items(): for name in names: image_paths.append((name, scene_id)) # 第一次按场景分组切训练/其余 train_candidates, temp [], [] for name, scene_id in image_paths: (train_candidates if random.random() train_ratio else temp).append((name, scene_id)) # 第二次把剩余按 val_ratio 拆分 val_count int(len(image_paths) * val_ratio) random.shuffle(temp) val_set, test_set temp[:val_count], temp[val_count:] return train_candidates, val_set, test_set这段脚本先按文件名前缀把图片归入场景组再以组为单位切分。第一次按比例切出训练集第二次把剩余样本随机分给验证和测试。这样同一个场景的连续帧不会出现在两个集合里验证集的评估结果更接近真实部署表现。脚本里关键参数是train_ratio和val_ratio对 1000 张图来说常见的比例是 0.8 和 0.1。seed必须固定方便别人复现你的实验结果。分层逻辑在代码里没有单独体现类别约束只做了场景分组严格的分层策略还需要在切分后打印各类别分布做检查。3.3 划分完成后必做的三类检查划分脚本跑完后不要急着开训练先做三个检查。第一个是文件配对检查遍历 txt 目录统计没有对应标签的图片以及没有对应图片的 txt这些脏数据会在训练时报错。第二个是类别分布直方图打印训练集、验证集、测试集里 red、green、yellow 三类各自出现的次数如果某个集合里黄灯占比明显低于全局比例说明划分脚本的随机种子或者分组策略有问题需要重新划分。第三个是图片尺寸一致性检查红绿灯图像来自不同摄像头时尺寸可能是 1920x1080 和 1280x720 混在一起yolo 训练时超参里如果开了rect模式问题不大不开的话建议统一 resize。一个实用习惯是划分完成后立刻生成清单文件把三张图片划分到三个 txt 文件里同时在清单旁边生成一个 hash 文件保存样本名。后续训练重复跑的时候直接用清单不需要重新执行划分脚本避免两次划分结果不同导致试验对比失效。4. 从零跑通红绿灯 yolo 训练配置、命令与参数解读4.1 data.yaml 的写法和类别顺序拿到三格式标签后训练的第一步是写对data.yaml。这个文件的路径前缀决定了训练时去哪里找图片和标签很多人在这一步翻车是因为用相对路径而当前目录不对模型报AssertionError: Label class 1 exceeds nc1或者直接找不到图片。# data.yaml放在数据集根目录下 path: /home/user/traffic_light_dataset # 数据集绝对路径 train: images/train val: images/val test: images/test names: 0: red 1: green 2: yellownames里的顺序必须和你转换脚本里class_map的顺序完全一致否则训练出来的模型输出类别编号和实际灯色对不上。训练时的类别数由 yaml 里的 names 个数决定不需要单独写nc参数。一个容易被忽略的点是path使用绝对路径更稳妥尤其当你用 pycharm 启动训练时工作目录经常不是数据集根目录相对路径会直接打偏。还要确认 labels 目录的结构。yolo 训练默认标签路径是和 images 同级的labels目录内部按 train/val/test 子目录和图片一一对应。若你的数据集把标签单独放在另一个顶层目录需要在train和val配置里用datasets/traffic_light/images/train加labels/train的方式指定或在数据目录内做软链接。脚本里我通常直接建立 images 和 labels 的对称目录结构省得训练时改一堆参数。4.2 训练命令与超参数调整用 yolo v8 训练红绿灯模型的最小命令如下。yolo detect train datatraffic_light.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch32 \ lr00.01 \ optimizerAdamW \ seed42 \ device0这个命令用yolov8n.pt作为预训练权重初始化imgsz设为 640这是 yolo 系列在 COCO 上表现稳健的默认输入尺寸。红绿灯目标在整幅图像里通常很小如果你用的摄像头是 1920 分辨率输入图可以提高到 960 或 1280小目标检测召回率会明显上升但训练速度和显存消耗也随之增加。epochs和batch是影响最大的两个参数。1000 张图的数据量不算大150 个 epoch 通常足够收敛如果训练过程中验证集 loss 还在下降就继续加到 200如果已经回弹就回调到 120。batch取决于显存单张 24G 卡可以开 3216G 卡降到 16显存不足时报错CUDA out of memory是正常的减小 batch 而不是调小 imgsz 是优先选择。优化器默认是 auto对于这个数据量显式指定AdamW往往比 SGD 收敛更快。lr0初始学习率新手爱用 0.001实际上 pre-trained 模型用 0.01 起步更常见配合 warmup 和 cosine 衰减才能让 loss 顺利下降。具体数值要在训完第一个 epoch 后看 loss 变化趋势再调整。4.3 模型导出onnx 与部署训练完成后runs/detect/train/weights/下会出现best.pt和last.pt。best.pt是验证集上 mAP 最高的权重直接用它做后续导出和推理。导出 ONNX 的命令如下。yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640 dynamicTrue导出 onnx 的核心参数是dynamicTrue这会让导出的模型支持动态 batch 和动态输入尺寸。如果你的部署环境是 jetson agx orin 或者 d435i 深度相机配套的边缘盒子动态尺寸能省下不少重导出的时间。导完后用onnxruntime跑一次推理确认输出维度是[1, 84]或者[1, 25200, 84]这类格式前 4 个是 bbox 的 cx、cy、w、h第 5 个是置信度后面是类别概率。红绿灯检测在部署时常遇到的问题是相机 ISP 把红色灯罩的色彩平衡掉导致红灯被识别成黄色甚至灰色这一步在导出模型阶段解决不了属于数据增强范畴。5. 避坑与验证红绿灯检测最容易翻车的五个问题5.1 五条高频踩坑记录现象一训练时提示Label class 0 missing from class list。原因是数据集里存在空标签文件或者类别 id 超出 data.yaml 定义的 names 数量。解决方法是遍历标签目录把类别 id 超过 2 的 txt 文件删除或重新标注同时统计每个类的样本数确认没有空文件留下。现象二mAP 很高但推理时把绿色倒计时数字框成目标。原因是标注时把「红灯/绿灯/黄灯」整个圆灯和数字连带框进去了模型学到了倒计时数字的特征。解决方法是严格按灯罩圆形区域标注只框灯体不框数字如果灯和数字共用一个发光区域则考虑把类别拆成 red含数字和 red_none纯灯让模型学会两者都归为红灯。现象三夜间图片检测精度骤降。原因是训练集里白天图占七成夜间过曝和光晕图太少模型对夜景域分布学习不足。解决方法是训练时增强夜景对图片做随机亮度抖动、高斯模糊、模拟光晕或者在数据集中额外补拍夜间视频帧。yolo 训练参数里hsv_h、hsv_s、hsv_v三个增强项可以适当加大尤其把hsv_v从默认 0.4 调到 0.7 能明显提升低光场景的鲁棒性。现象四训练和验证划分后小目标召回率低于 10%。原因是 640 输入下红绿灯目标在图里只有 8x8 像素yolo 下采样到 80x80 特征图时目标信息几乎丢失。解决方式是用 960 输入尺寸训练必要时开启saver多尺度训练或者在 yolo 配置里加上 P2 层小目标检测头。现象五转换脚本跑完后 coco json 加载报 KeyError。原因是 json 里的is_crowd字段漏填coco 官方 api 读取时会强制检查这个字段。解决方法是给每条 annotation 补上is_crowd: 0同时在 images 表里补license和flickr_url字段这些字段虽然不参与计算但 cocoapi 的加载器会校验字段存在性。5.2 用混淆矩阵判断模型是否真的可用mAP 是宏观指标红绿灯场景判断模型是否可用必须看混淆矩阵。训练完成后运行yolo detect val会在runs/detect/val/下生成confusion_matrix.png重点看三个数字红色被误判成绿色的比例、绿色被误判成红色的比例、黄灯被误判成其他灯色的比例。前两项超过 5% 就直接下线因为这是行车决策的信号灯语义错误比漏检更危险。误判多发生在目标尺寸小或被遮挡时可以针对性补充难负样本而不是盲目增加总训练数据。5.3 进阶用法同一份数据集同时喂给 yolo 和多模态分析当你只拿 1000 张图训练一个红绿灯检测模型可能会觉得数据量太小不踏实。实际上这份数据集的价值在迁移学习场景下会放大不少。coco 格式标注可以直接用于训练 swin transformer 或 detr 等检测框架voc 格式可以用于半监督学习的分批标注。红绿灯检测往往只是智慧交通系统的一个环节yolo 模型负责输出灯色和位置后接一个多模态大模型分析灯色状态与车流的关系这时数据集的 coco 格式让检测结果与语言描述对齐变得非常直接不需要再写一次坐标转换。另一个值得尝试的方向是用这份数据集做跨摄像头域适应实验。取出某个场景的图片作为源域另一个摄像头拍到的图片作为目标域利用划分脚本把场景按摄像头分开就能在域适应研究中跑基线。模型最终要在新路口上线保守做法是先只用这份数据集训练再收集新路口两小时视频做半自动标注微调而不是指望一个模型吃遍所有路口。这是我的个人经验尤其在红绿灯这类外观变化有限但环境光照差异大的目标上花时间做微调往往比攒十万张图更有收益希望帮到你。本文还有配套的精品资源点击获取