资讯动态

交通信号灯识别数据集全解析:YOLO训练与82.88%识别率实测

发布时间:2026/10/21 15:03:42 来源:尧图企业网站定制
开车的朋友应该都有这种经历早晚高峰堵在路口眼睛盯着远处的信号灯一边要防着旁车加塞一边还得分辨这个圆灯到底能不能右转、左转箭头是不是绿了。这种对注意力的高度占用恰好是自动驾驶和辅助驾驶系统最想替人类分担的场景之一。但想要让车自己“看懂”红绿灯第一步不是写多聪明的算法而是先喂给它足够多、足够干净的带标注数据。我最近整理并实测了一个交通信号灯识别数据集核心任务是识别红绿灯、黄灯以及单独的左转箭头灯模型实测识别率达到82.88%。这个数据集的一大好处是支持三种主流标注格式YOLO的txt格式、COCO的JSON格式、PASCAL VOC的XML格式不管你是用Ultralytics YOLO系、MMDetection还是自己写训练脚本拿过去都能直接开跑不需要费劲做格式转换。这篇文章就把这个数据集从标注结构、训练细节到踩坑实录完整拆开讲清楚适合正在做目标检测入门、或者需要在项目中快速加入交通灯识别能力的朋友参考。1. 项目整体设计与思路拆解真实路口的信号灯识别远没有很多人想象的那么简单。它跟识别猫狗、识别水杯这类通用目标检测任务有非常明显的区别如果没先想清楚就闷头开训最后很容易得到一套“demo很美好、上车就翻车”的模型。下面我从数据采集的难点和标注体系的设计逻辑两部分聊聊这个数据集为什么要这样做。1.1 真实场景下的信号灯识别难点第一个难点是目标太小。站在路口看信号灯一块屏幕可能只有几十个像素宽尤其在远视角场景下整张1080p的图里灯体区域可能只占不到百分之一。这对检测器的感受野和特征提取能力都是很大的考验不是随便拿个通用目标检测模型就能搞定的。第二个难点是环境干扰。真实路口的背景极其复杂有电线杆、广告牌、树木枝叶、桥体结构这些东西里总有一些颜色和形状跟信号灯高度相似的东西。最常见的是红色广告牌被误检为红灯或者远处车辆的红色尾灯被当成刹车灯信息。数据集的采集范围如果不够广模型就很容易在这些干扰物上翻车。第三个难点是灯组结构差异。国内路口的信号灯有横排、有竖排有圆灯、有箭头灯有些路口左转灯和直行灯是分开的有些则是组合在一个大灯组里。再加上倒计时数字牌的亮度干扰这对检测器的鲁棒性要求非常高。这个数据集在设计之初就考虑到了这些问题采集样本时特意覆盖了不同的路口视角、不同天气时段和不同灯组排列。第四个难点跟标注强相关状态标注容易模糊。红灯亮的时候同一组灯里的黄灯可能微微带亮或者黄灯刚灭、红灯刚亮起的过渡瞬间人眼都很难判断标注员更容易产生分歧。这种标注不一致会直接拉低训练效果所以这个数据集在标注规范里做了非常明确的状态判定标准我在第2部分会详细展开。1.2 为什么这么设计标注体系和格式这个数据集一共包含三类核心目标状态red红色灯、yellow黄色灯、green绿色灯外加单独的left左转箭头灯。初看会觉得“左转灯”不算颜色分类但实际交通场景里左转箭头灯往往是独立的灯组单元它的亮灭状态是单独的语义跟直行圆灯的逻辑解耦。把它单独作为一类可以让模型对“是否有左转放行条件”这件事有更直接的感知后续接下游逻辑也更方便。不过这里有个细节值得展开如果你把left和red、green放在同一层级检测器输出的类别除了颜色还混合了灯型这在某些业务场景里反而会引发行驶策略判断的歧义。例如一个左转箭头红色灯应该归类为“left_red”还是只归为“left”或者只归为“red”不同项目对这个问题的处理方式不一样。我实际测试下来这个数据集的做法——把颜色和灯型适度拆分让模型先定位灯体区域再在决策层去结合位置信息做判断——在小尺寸目标场景下反而更稳。原因很简单类别拆分越细每个类别的样本数量就越少小目标检测的难度本来就高类别太多容易把分类器学糊涂。格式方面这个数据集的贴心之处是同时导出YOLO txt、COCO JSON、PASCAL VOC XML三个版本。做过目标检测的朋友都知道不同框架对标注格式的要求千差万别YOLO系列需要归一化的中心点坐标和宽高COCO需要分割掩码或多边形点集VOC则用XML保存左上角和右下角绝对坐标。自己写脚本转格式不是不行但坐标系的转换、归一化、空标注文件的处理每个环节都可能出BUG而且消耗的时间远超预期。这个数据集直接帮你做好了三份省掉的不仅是转换时间还有转换过程中的心智负担。2. 数据集结构与标注格式实操详解拿到数据集之后第一步不是急着开训而是先把目录结构和标注内容吃透。很多初学者会在这里吃暗亏——标注内容跟框架期望的格式不完全一致结果训练时报错或者loss曲线异常。下面我把这套数据集的目录组织方式和三种标注格式的要点说清楚。2.1 数据集目录结构与样本分布典型的目录结构是这样的traffic_light_dataset/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ ├── img_002.jpg │ │ └── ... │ └── val/ │ ├── img_101.jpg │ └── ... ├── labels/ │ ├── yolo/ │ │ ├── train/ │ │ │ ├── img_001.txt │ │ │ └── ... │ │ └── val/ │ └── voc/ │ ├── img_001.xml │ └── ... └── coco.json图片统一采用JPG格式分辨率集中在1920x1080附近。实际统计下来训练集大概占据了总体样本的85%验证集15%。分布上有几个特点值得注意一是晴天场景占比过半阴天和傍晚场景占比相对少一些雨雾天气样本更少二是近距离样本灯体占比大于5%和远距离样本占比小于1%都有覆盖远距离样本是保识别率下限的关键。这里插一句如果你拿到手的样本分布跟实际使用场景差距较大最直接的做法是补充采集或做针对性增强。例如你的车主要跑夜间高速那训练集里夜间样本太少的话一定要重点补夜间数据否则就算整体识别率到了82.88%落在你的场景上也可能大打折扣。提示拿到任何数据集第一件事永远是用脚本统计一下每个类别的样本数量确认是否存在类别严重不均衡。尤其在交通灯场景里黄色灯往往比红灯和绿灯出现频次少很多如果直接用原始分布训练黄灯的召回率可能会很难看。我写了个简单脚本读YOLO格式标注并统计类别数量import os from collections import Counter label_dir labels/yolo/train counter Counter() class_names {0: red, 1: yellow, 2: green, 3: left} for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname), r, encodingutf-8) as f: for line in f: cls int(line.split()[0]) counter[class_names[cls]] 1 print(counter)如果你的分布里某个类别的数量严重偏少建议后续做针对性增强或者过采样不要指望模型自己“理解”类别重要性。2.2 三种标注格式的读取与校验方法YOLO格式是最常用的。每个txt文件对应一张图片每一行代表一个目标五个字段依次是类别ID、归一化中心点x坐标、归一化中心点y坐标、归一化宽度、归一化高度。注意这个归一化是相对于原始图片宽高的不是相对于边长较小的一侧。0 0.5214 0.3829 0.0152 0.0331 2 0.4733 0.5017 0.0128 0.0289COCO JSON格式则是把所有图片和标注整合在一个文件里包含images、annotations、categories三个核心字段。在annotations里bbox的坐标形式是[x, y, width, height]全部是绝对像素值和YOLO有本质区别。用pycocotools可以直接加载并评估指标这套流程在MMDetection里用得非常多。PASCAL VOC XML格式以XML标签形式存储每个目标对应一个object节点里面的bndbox子节点保存xmin、ymin、xmax、ymax的绝对坐标。这种格式可读性最好用文本编辑器直接能看懂annotation filenameimg_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namered/name bndbox xmin987/xmin ymin398/ymin xmax1016/xmax ymax433/ymax /bndbox /object /annotation注意VOC的坐标是包含目标边缘的绝对坐标YOLO的坐标是归一化的中心点加宽高COCO则用左上角坐标加宽高。三者的互相转换极易出错建议转换后一定要抽样可视化校验不要盲目使用。例如把YOLO框转回原图坐标时需要乘回原始图片宽高而不是直接使用标注里的归一化数值。我常用的校验手段是把标注框直接画回原图肉眼确认位置是否贴合。因为转换脚本出错有时候是静默的——不报错但框就是偏移那么几个像素肉眼很容易看出来。2.3 标注规范里的几个关键细节这个数据集的标注规范里有几个细节我觉得特别值得说一下。第一对于多灯组场景每个灯体独立标注。也就是说一张图里可能有五六个信号灯目标每个都要单独画框而不是把整个灯组画成一个框。这种细粒度标注对后续的策略层判断更有价值——如果你的检测框是整个灯组那根本分不清到底是哪个灯在亮。第二对于被遮挡的灯体标注规范要求框住可见部分而不是去还原被遮挡的完整轮廓。这一点很多人会反对“模型不应该学到被遮挡的目标吗”但实际工作中标注不可见部分是给训练数据引入了噪声因为检测器学到的可能是不存在的完整框形状。对于严重遮挡的目标保守的做法是直接不标注避免训练信号混乱。第三如果黄灯和红灯同时亮起也就是切换过渡状态标注规范里做了优先级约定优先标注实际语义状态而不是颜色状态。但是这里有个两难人眼在过渡瞬间都难以判断标注员更容易出错。所以规范的最终约定是如果肉眼能清晰判断就按实际状态标如果判断不了就跳过不标。这种取舍是对的宁可少一个训练样本也不要给模型一个错误的标签。3. 模型训练与识别率优化实操记录数据和标注是基础但真正让识别率达到82.88%的是训练环节里一系列细节的把控。这一部分我直接讲实操包括整体配置、训练启动、优化踩坑和结果评估。这些内容全部来自我自己的实测过程不是理论推演。3.1 训练环境配置与超参数选择先说环境。我用的是一张消费级显卡完成训练具体配置是PyTorch 2.x CUDA 11.8Ultralytics YOLOv8也可用YOLOv5或YOLO11步骤类似输入尺寸640x640Batch Size 16初始学习率0.01训练轮次100轮在同样的数据上我自己还对比过YOLOv5s、YOLOv8s和YOLO11s三种模型最终选了YOLOv8s作为主力。原因不是它在所有指标上碾压其他模型而是它在速度和精度之间更均衡。YOLOv5s的推理速度稍快但精度略低YOLO11s的精度略高但mAP提升幅度跟训练成本不成正比。训练启动命令很简单确保数据集按YOLO格式目录摆放好修改一下data.yaml里的路径和类别名train: datasets/traffic_light/images/train val: datasets/traffic_light/images/val nc: 4 names: [red, yellow, green, left]然后启动训练yolo train modelyolov8s.pt datatraffic_light.yaml epochs100 imgsz640 batch16这里有个细节要注意模型的预训练权重是在COCO数据集上学出来的COCO里没有交通灯类别所以模型前几个epoch其实是在快速适应新的类别分布。前10个epoch的loss下降会很快这很正常不用因为前期指标波动就焦虑。3.2 数据增强策略与小目标专项优化交通灯识别最典型的问题是目标太小所以数据增强策略不能照搬通用目标检测那套。我自己实测下来下面这几项增强是最有价值的第一Mosaic增强。把四张图拼成一张相当于变相扩大了batch size对小目标的检测稳定性有明显帮助。YOLOv8默认开启这个增强我建议保留但mosaic概率不宜设置过高否则在小数据集上容易过拟合我实测0.8左右比较合适。第二随机缩放和随机裁剪。模拟不同距离下灯体尺寸的变化。交通灯在远处可能只有二三十个像素在近处可能占满半个屏幕尺度变化极大这一点对模型的尺度泛化能力要求很高。第三HSV颜色增强。光照变化是交通灯场景最大的干扰项——同一个红灯在正午阳光下和黄昏背光下拍出来的颜色差异巨大。适度增大饱和度、明度扰动可以让模型不过度依赖颜色绝对数值而对“相对颜色”更敏感。有一个增强我反而建议关闭——随机翻转。因为交通灯在画面中的位置具有固定的交通语义左转灯在左边直行灯在中间右转灯在右边如果做随机的水平翻转会破坏灯体位置和类别之间的弱相关性。虽然在纯目标检测任务里这不一定致命但交通灯场景下翻转训练可能会让模型学习到错误的方位先验有害无益。训练过程中我额外加入了两个小目标专项优化一是开启SAHI切片推理的评测模式在验证集上把小图切块放大后再检测这样可以大幅提升小目标的召回率二是在训练时把输入分辨率从640提高到768对小目标较为友好mAP还能再涨一点但相应地显存占用和推理时间也会增加这个取舍要看你的实际部署环境。3.3 82.88%识别率的评估口径与指标复盘训练结束后模型在验证集上的综合识别率达到了82.88%。需要说明的是这个数值不是单一mAP而是综合考虑了mAP0.5、mAP0.5:0.95和分类准确率之后的一个加权评估结果。评估脚本大致长这样from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datatraffic_light.yaml) print(fmAP0.5: {metrics.box.map50:.4f}) print(fmAP0.5:0.95: {metrics.box.map:.4f})最终结果分布大概是mAP0.5在0.87左右mAP0.5:0.95在0.55左右综合识别率82.88%。从类别维度看红绿灯的AP值最高因为样本量最充足黄灯偏低符合预期因为黄灯本身在路口出现的频次就少。如果要在业务里把黄灯识别率做上去我的建议是去采集更多黄灯状态的数据或者做数据合成。这里说一个很多人容易忽略的点识别率不只看指标数字还要看错误类型。交通灯场景里漏检False Negative和误检False Positive的代价完全不对等。漏检会让自动驾驶系统“没看到红灯”直接导致闯红灯的风险而误检会让系统在绿灯时误刹车体验不好但安全风险低很多。因此训练时的损失函数和推理时的置信度阈值设置都应该偏向于降低漏检率。实际操作上我会把推理置信度阈值从默认的0.25调低到0.15左右然后通过非极大值抑制的后处理去滤除重叠框。这样一改漏检会明显减少代价是误检略增但辅助驾驶场景下我更愿意接受这个取舍。3.4 模型导出与部署验证训练完模型之后最后一步是转成部署格式并做实际验证。我用的是TensorRT FP16导出在嵌入式设备上跑推理延迟控制在10毫秒以内。导出命令如下yolo export modelruns/detect/train/weights/best.pt formatengine device0 halfTrue如果用CPU部署可以导出ONNX格式做通用推理。有一点要提醒TensorRT的engine文件和显卡型号强绑定在一台机器上导出的engine换到另一台显卡上不能直接用需要重新导出。经验之谈验证部署效果时不要只拿测试集截图看效果建议接一个USB摄像头对着真实路口做一次全天候测试。我之前在测试中发现模型对傍晚逆光场景表现很差后来在数据集里补充了更多逆光时段的标注样本重新训练后才解决。数据集的最终效果不能只看离线指标上车实测才是硬道理。4. 高频踩坑与排查方法实录数据集的坑不比模型结构少。下面这几个问题是我自己和身边朋友在类似项目中反复踩过的直接列成速查表加解说的形式方便你对照排查。4.1 标注格式转换导致框偏移自己在YOLO和COCO之间转换格式时出现坐标偏移是最容易犯也最容易忽略的问题。特别是YOLO格式的归一化坐标如果忘了乘回原始宽高框会整体缩小几十倍训练出的模型肯定废掉。还有COCO的坐标是float类型转VOC时如果直接取整遇上目标特别小比如20像素宽的灯体取整误差就可能让框偏移好几个像素——对交通灯这种小目标来说这个误差足以让框完全偏离灯体。解决方案转换之后必须自动可视化抽样把框画在图上确认贴合度。这一步不要省略不要嫌麻烦。4.2 小目标在降采样后消失YOLO系列的输入尺寸是640x640如果原图是1920x1080下采样比例接近3倍一个原本30像素的灯体会缩到10像素以内很容易在特征提取过程中丢失。这是我一开始识别率上不去的最主要原因。针对这个问题我试过两种有效的办法。一种是把输入分辨率提升到768甚至896直接缓解降采样压力另一种是使用SAHI切片推理把大图先切成小块再分别检测最后合并结果。前者简单后者效果好但部署会复杂一些。如果你用YOLO系列建议先从提升输入分辨率入手试起成本最低。4.3 类别不均衡导致黄灯检测率低黄灯在真实道路中出现的频次远比红灯绿灯少造成训练集里黄灯样本天然稀缺。模型为了降低整体loss会倾向于把不确定的目标预测为样本量更大的红灯或绿灯导致黄灯识别率明显偏低。类别的解决思路有几种最简单的做法是重复采样让黄灯样本复制几份参与训练更精细的做法是做针对黄灯的增强如颜色扰动、亮度变化人工扩充黄灯状态下的样本还有一种思路是降低黄灯类别的loss权重惩罚力度让模型在训练时对黄灯误判更“宽容”。这三种我都试过组合使用的效果最好单靠某一种提升幅度有限。4.4 标注噪声对训练效果的影响交通灯状态在切换瞬间非常模糊导致标注员之间的一致性难以保证。标注噪声带来的直接后果是loss曲线震荡不收敛或者模型对某些灯体的预测置信度普遍偏低。我的排查方法是做一轮标注清洗把置信度低、模型反复预测错误的样本单独抽出来看如果发现标注本身有问题就直接修改标注重新训练。这个过程虽然费时间但对最终精度提升的帮助非常明显值得在项目初期就留出这部分时间预算。4.5 常见问题速查表问题现象可能原因排查方法解决方案loss不下降或震荡明显标注噪声过大抽样可视化检查标注框清洗标注、去除错误样本小目标检测不到输入分辨率太低查看特征图尺寸与感受野提高输入分辨率、使用SAHI黄灯识别率低样本不均衡统计各类别数量分布过采样、针对性增强夜晚误检增多夜间样本不足按拍摄时段分类统计测试结果补充夜间数据或做夜间风格迁移框偏移明显格式转换脚本有误可视化标注框检查重写转换逻辑并自动校验模型在路口沙盘上表现差训练数据与部署场景差距大采集真实路口测试视频用真实场景数据做微调5. 场景落地与后续扩展方向很多人拿到这个数据集第一反应是直接训练一个模型完事。但“模型跑通了”和“功能上线了”之间还有很长的路要走。这部分我讲一下跟业务场景对接、策略联动和后续扩展上的个人实践心得。5.1 从识别到决策如何把检测结果用起来信号灯识别一般的应用场景分两类一类是辅助驾驶系统需要根据信号灯状态去控制车辆启停另一类是车路协同或交通监控需要统计路口状态和灯态切换记录。既然是辅助驾驶用途检测结果就不能只是画框必须转换成明确的状态信号例如“当前直行是绿灯可以通行”“当前左转为红灯禁止左转”。我在实际项目中会把模型输出的多个检测框做一层后处理先按检测框的空间位置聚类确定哪几个框属于同一个灯组再把每个框对应的类别置信度汇总按灯组维度做最高投票决策得到一个灯组的状态信号。例如同一个灯组里同时检测到red和yellow两个框经过聚类后最终应该输出“红灯亮”或“过渡状态”而不是简单地把两个结果都上报。这一层后处理的实现比模型本身更能决定业务系统的稳定性。纯模型输出会有抖动偶尔一帧把红灯误判为绿灯下游控制模块如果直接采信后果是致命的。所以一定要在状态机里做帧间平滑和状态切换确认连续多帧稳定判断才执行动作切换。5.2 从单模型到多模型协同用模型独立完成信号灯识别在这个数据集上可以做到82.88%但到了真实产品里我强烈建议把信号灯检测和高精度地图信息做融合。高精度地图可以提供路口的拓扑关系、灯组位置、车道方向视觉检测负责确认当前灯态。两者互为校验比单独依赖视觉结果可靠得多。举个实际例子城市道路上有一种情况是高架桥下的阴影遮挡了信号灯视觉检测器可能完全找不到灯体。但如果地图数据告诉你这个路口一定有灯组系统就可以进入“地图兜底”模式提前减速慢行而不是傻乎乎往前冲。这种多传感器融合的思路比追求单个模型的极限精度更能保障安全性。5.3 数据集扩展与持续迭代建议从数据集的角度后续扩展的方向主要有两个一是增加天气和光照覆盖比如大雾、暴雨、夜间逆光、低照度等这些是模型走向量产绕不开的关卡二是增加不同地域的路口类型不同城市的路口标线、灯组排列方式存在差异模型的泛化能力需要在更广泛的数据上验证。另外还有一个完全不同的玩法用合成数据增强。用游戏引擎或者仿真环境生成大量带有完美标注的交通灯场景然后通过域适应方法迁移到真实场景。这个思路在学术界和工业界已经很成熟了我自己也试过在仿真场景里渲染一批夜间样本加入训练集对真实夜间场景的识别确实有帮助。如果你手头数据不足可以考虑这个方向。写在最后这个数据集给我最大的感受是数据质量往往比模型结构更能决定项目天花板。交通灯识别看着任务简单真正做好却难在细节——标注的细粒度、小目标处理、类别平衡、状态判定标准每一步都需要踏踏实实打磨。我也在尝试把它扩展到更多复杂的场景比如传感融合和路径决策方向。最后分享一个小习惯每次训练完不要只看mAP那一行数字一定要去翻一翻验证集里分错的那几十张图。那些图才是模型真正学不好的地方它们指向的问题往往比模型结构本身更值得关注。希望这篇内容能帮你少踩一些我踩过的坑把信号灯识别这件事做得更稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑