☰
基于YOLOv8的智慧交通闯红灯检测全流程:小目标与类别区分优化
2026/10/20 4:35:40 网站建设 项目流程

做智慧交通项目,尤其是“车辆闯红灯检测”这类需求,多数人的第一反应是:这不就是个目标检测嘛。真把路口数据拉出来开始训练之后才发现,难点根本不在“认出那是一辆车”,而在认出“远处驶来的一辆小车”、区分“红灯和绿灯”、再把信号灯状态、车辆位置、停止线这几样信息放进同一个逻辑里做违章判定。我基于 YOLOv8 完整做过一套智慧交通场景下的车辆闯红灯目标检测训练流程,涉及数据集组织、小目标优化和类别区分策略,这篇文章把全链路拆开讲。适合已经能跑通 YOLO 基础训练、想往交通违规检测方向落地的开发者,尤其是正在被小目标和红绿灯误检折磨的朋友。

1. 智慧交通闯红灯检测:为什么是 YOLOv8

1.1 任务拆解:不是“检测车”那么简单

闯红灯检测从来不是单模型识别任务。真正要上线,视觉模型只是其中一环:信号灯状态要从画面里识别出来,车辆要被框出来并跟踪,停止线和斑马线用于判断车辆是否越过路口边界。只有这些信息叠加在一起,才能判定一次闯红灯事件。

从视觉感知的角度看,YOLOv8 要输出的目标类别至少有这些:红灯、绿灯、黄灯、车辆(可细分小车、公交车、货车、摩托车)、停止线。这里的“类别区分”包含两层:第一层是不同目标类型的区分,比如信号灯和车辆;第二层是同类目标不同状态之间的区分——红灯和绿灯在形状上几乎一样,只是颜色通道不同,远处灯光只有几个像素时,端到端网络很容易学混。

还有一个容易被忽视的潜在需求:违章取证的可解释性。取证图片要求能看到红灯亮着、车辆越过停止线,所以模型输出的不仅是 bbox,还要支撑规则引擎去核对时间线。如果模型把绿灯下正常通行的车辆框出来,业务方看到的直接就是“绿灯车被标成闯红灯”,项目基本就黄了。也是因为这个原因,我反复强调:宁可召回低一点,也要把红灯状态判准,把类别混淆压下去。

1.2 为什么选 YOLOv8 而不是其他方案

我用 YOLOv8 的原因可以归纳成四点。第一,anchor-free 检测头让小目标训练更直接,少了一层 anchor 匹配的麻烦;第二,ultralytics 的训练、验证、导出链路完整,后续转 ONNX、TensorRT 再落到边缘设备都很顺;第三,n/s/m/l/x 模型家族伸缩性很强,demo 用 s,边缘盒子可以用 m 甚至 n,选型空间大;第四,社区资料多,遇到问题好排查,不会卡在一个框架私有坑里出不来。

当然 YOLOv8 不是万能的。交通场景里的“小目标”和“类间相似”恰好是通用检测的通病,模型结构本身并不能自动解决。最终效果好不好,六分在数据,三分在分辨率与训练策略,一分在模型结构调整。下面我会把这几层分别拆开讲清楚。

1.3 智慧交通场景的独有约束

固定视角是交通场景最大的先验:摄像头安装高度、角度基本不变,车辆朝向相对规律,这比随机开放场景的检测容易不少。但约束也很明显:夜晚、逆光、过曝、雨雪、摄像头白平衡不一致,每一个都能让模型翻车。另外,真实路口视频涉及隐私,数据必须脱敏,车牌和人脸都要模糊处理,只能在受控环境内训练。

单帧检测同样不够用。路口信号灯变化有固定时序,车辆闯红灯是连续几帧内发生的事,最终判决最好用多帧结果投票。所以我在项目里会把 YOLOv8 的输出喂给跟踪器和规则判断模块,而不是拿单帧置信度直接拍板。这一点和很多公开 demo 不一样,却恰恰是能不能落地的关键。

2. 数据集构建与标注规范:决定模型天花板

2.1 数据来源与合规处理

数据来源通常有三种:真实路口数据、公开数据集、合成数据。真实数据最接近部署场景,但需要经过严格的脱敏处理;公开数据可以补充类别平衡性,但相机安装角度差异大,最好只做预训练或少量微调;合成数据针对小目标样本特别有用——用渲染工具模拟远距离信号灯和车辆,能补充大量难例。对红灯状态识别来说,合成数据可以人为控制光照和距离,是提升小目标分类性价比最高的方式。

样本覆盖也要分层。不能只拿下午两点的视频训练,白天、夜晚、雨天、逆光、过曝最好都按比例覆盖。摄像头安装位置不统一时,每条数据最好记一下相机ID,方便后续发现“某个相机表现特别差”的问题时回溯。

2.2 类别定义:一定要克制

类别定义要克制,不是越细越好。我常用的是这套:

  • red_light / green_light / yellow_light:信号灯状态按颜色分
  • car / bus / truck / motorcycle:车辆类别按业务需求合并
  • stop_line:停止线,可选

千万不要设“闯红灯车辆”这个类别。闯红灯是事件,不是静态物体;单帧图像里模型不可能可靠判断“这辆车正在闯红灯”。正确做法是输出要素,让规则引擎判断。有些公开数据里有“闯红灯”标注,那是视频片段级别的人工事件标注,直接拿来做目标检测类别会非常乱。

类别数量也要控制。智慧交通画面里信号灯和车辆的尺度差异巨大,类别越多,分类头在小目标上的压力越大。能合并的类别尽量合并,比如小轿车和 SUV 在交管业务里往往不需要分开,统一成 car 反而能减少类内差异,提升小目标召回。

2.3 标注细节与质检

标注是“小目标+类别区分”问题的第一道关卡。红绿灯小目标往往只有 8~12 像素,多标几像素少标几像素,对 IoU 影响非常大。我的经验是:bbox 要紧贴灯体亮起区域,而不是框整个灯盘;如果模型需要分颜色,灯盘暗色部分对识别反而是噪声。

停止线是细长目标,整段框会引入大量背景,建议按可见断段标注。车辆遮挡超过一半就不标,只标完整可见的车。标完之后必须做类别混淆抽检,红灯被标成绿灯、车辆尾灯被标成红灯,都是重点核查对象。

建议最后统计每个类别的目标尺寸分布。一个小脚本就能看出:哪些类别主要落在 32×32 以下,哪些主要落在 300×300 以上。如果红灯、绿灯几乎全在小目标区间,后续所有优化都要优先针对这个区间做,而不是盯着整体 mAP。

2.4 数据增强:不要无脑套默认

增强策略不能默认全开。Mosaic、随机透视、亮度扰动都可以用,但有两个坑:一是不要对 HSV 的 H 通道做大幅度扰动,否则红灯和绿灯的色相会互相污染;二是 mixup 在红绿灯分类任务上建议谨慎,它会混合出“红绿之间”的奇怪像素,让分类头学出问题。

小目标样本少,推荐复制粘贴增强:把真实小目标抠出来,贴到不同背景的路口图像上,并做多尺度缩放。这不改变图像语义,又能把远处小车的数量快速提上来。同时可以按场景分层抽样,白天、夜晚、雨天、逆光各占一定比例,避免模型被白天样本带偏,到晚上就崩。

负样本也要进训练集。没有信号灯的路口、绿灯放行的车辆、红灯但是远处无车的画面,都要作为背景出现。目标检测模型的误检往往不是因为正样本不够,而是因为背景负样本没见过。

3. YOLOv8 训练实操与参数调优

3.1 目录结构、标注格式与 dataset.yaml

训练前准备经常被忽略,但格式错了基本白费。YOLOv8 的 label 是归一化格式:类别索引 + 中心点 x/y + 宽高,全部归一化到 0~1。目录结构建议这样:

traffic_dataset/ images/ train/ val/ labels/ train/ val/

dataset.yaml 配置如下:

path: ./traffic_dataset train: images/train val: images/val nc: 6 names: ['red_light', 'green_light', 'yellow_light', 'car', 'bus', 'truck']

建议使用绝对路径,或者在固定的项目目录下运行,避免和 ultralytics 默认设置冲突导致数据集加载失败。

3.2 模型选型、分辨率与关键参数

模型优先选 YOLOv8s 或 m。YOLOv8n 在小目标上的特征表达能力有限,l/x 在边缘设备上又跑不动。我最后用的是 yolov8s 加 1280 分辨率,兼顾精度和推理速度。

分辨率必须提。默认 640 输入下,远处车辆只有十几像素,降采样两次就基本没了。imgsz=1280 后小目标召回会有肉眼可见的提升,代价是显存和耗时翻倍,后期可以配合切图、双路部署来解决。

训练命令参考:

yolo train data=traffic.yaml model=yolov8s.pt \ epochs=100 imgsz=1280 batch=16 device=0 \ optimizer=AdamW lr0=0.001 close_mosaic=10 patience=20

batch 要根据显存灵活调整:16 跑不动就降到 8,同时把 lr0 调小。最后 10 个 epoch 关掉 mosaic,是为了让模型适应真实数据分布,避免 mosaic 的拼接痕迹影响最终精度。

3.3 训练指标怎么读

不要盯着训练 loss 自我感动,直接看验证集指标。重点看 box_loss、cls_loss、dfl_loss 曲线,还有 val 的 P、R、mAP50、mAP50-95。尤其要看 YOLOv8 按尺寸分组的验证结果:small、medium、large 的 AP。

典型问题是 small 的 AP 明显低于 medium。这说明小目标优化还没到位,优先回数据层补样本和提分辨率,而不是盲目加 epoch。如果训练 mAP 很高但验证 mAP 上不去,多半是过拟合或数据分布偏差,先减少 epochs、增强早停,或者降低模型容量。如果 cls_loss 一直下不去,检查类别标注是否一致,比如有的红灯框了灯盘、有的只框了亮灯区域。

3.4 模型导出与部署注意

训练结束后导出:

yolo export model=runs/train/exp/weights/best.pt format=onnx imgsz=1280

在 NVIDIA 边缘设备上可以直接导出 TensorRT engine,FP16 精度损失在小目标场景可以接受,INT8 必须用真实路口数据做校准,否则红灯颜色可能被量化到没法看。

部署时另一个容易踩的坑是:后处理参数要和训练保持一致。置信度阈值、NMS IoU 阈值、类别过滤是否开启,都会影响最终输出。业务端要求低误报时,conf 可以提到 0.5 以上;离线取证分析可以降到 0.3,再用时序投票过滤。

4. 小目标检测专项优化:从数据到结构

4.1 小目标在路口场景到底有多小

先量化一下:COCO 里 small 目标指小于 32×32 像素的目标。在 1080p 固定路口相机的画面里,距离停车线 50 米的小轿车经常只有 20×10 像素。这个尺寸经过 YOLOv8 的 stride 8 下采样后,在特征图上只剩 4×2 个格子,分类和回归信息都非常少。所以小目标问题本质是两件事:特征分辨率不足,加上正样本分配不足。

4.2 不魔改模型的四条路

优先级最高的是提高输入分辨率。imgsz=1280 是性价比比较高的档位,1536 还能再涨一点,但显存和推理速度代价明显;继续堆到 2048 收益已经很小,部署时也跑不动。

第二个方案是切图推理,也就是 SAHI 思路。把大图按重叠率切成若干子图,每个子图各自推理,再把检测结果映射回原图合并。对 4K/8K 摄像头抓拍,这个方案通常比单纯提分辨率更有效,尤其适合离线取证。

第三个方案是多尺度训练。YOLOv8 训练时本身会随机缩放,但我更倾向于在数据集里固定放 960 和 1280 两种分辨率子集混合训练,让模型同时适应不同下采样尺度。这对相机分辨率不统一的真实项目非常有用。

第四个思路是视频多帧聚合。车辆在路口连续出现,同一个目标在 10 帧里往往能被检出 7~8 帧,偶尔漏检可以通过跟踪器补上,不需要单帧模型强到完美。

4.3 结构改动路线

如果数据和分辨率优化都做到位,小目标还是差,再考虑改结构。YOLOv8-P2 是一个方向:在 stride 4 的位置增加一层检测头,小目标的特征分辨率直接翻倍,代价是 FLOPs 上涨约 20%,显存占用也会升高。

另一个改动是 SPD-Conv:用空间到深度卷积替换部分 stride=2 卷积,减少低分辨率下信息丢失,对极小目标有实验性提升。还有一类思路是改正样本分配尺度,YOLOv8 的 anchor-free 匹配对小目标比较苛刻,可以放大匹配范围或者对小目标样本加权,但这需要深入读框架内部代码,建议作为最后手段。

4.4 怎么验证优化效果

不要只看整体 mAP,要单独看 small 目标 AP 和每一类的召回率。我习惯把小目标漏检样本按原因归类:遮挡、过曝、雾天、本身太小、夜间对比度低,然后针对占比最高的两类补数据。这样迭代几轮,比无脑调参快得多。可视化的漏检样本一定要保存下来,很多问题一眼就能看出来,不用猜。

5. 类别区分的核心实践:红灯绿灯分得清

5.1 类别区分难在哪

红灯和绿灯的相似度极高,在画面里只差颜色通道的数值。而且不同摄像头、不同白平衡下,同一个颜色在 RGB 空间里能差出很大的范围。如果直接端到端学“红灯”和“绿灯”两个小目标类别,分类头很容易把颜色信息学成固定 RGB 阈值,换个相机就失效。夜间更麻烦:车辆尾灯、刹车灯都是红色,形状和信号灯相近,误检率一下就上来了。

5.2 我的最终方案:检测+分类两级

最终我在项目里用的是两级方案。第一级 YOLOv8 只检测 traffic_light 位置,不做颜色分类;第二级把每个灯框裁剪出来,缩放到 64×64 或 96×96,再交给一个小分类网络判断红/黄/绿。这样小目标分类的分辨率由分类网络决定,不再绑死在原图尺度上;颜色判定也能做得更稳,甚至可以在分类网络里加入 HSV 颜色统计作为辅助特征。

两级方案的收益非常明显:红绿灯分类准确率能到 99% 以上,端到端单模型经常卡在 90% 左右。代价是流程多一级,推理增加一点开销,但交通取证场景完全能接受。如果坚持单模型多类别,至少要把 mixup 关掉,并对红灯类样本做重复采样。

5.3 端到端多类别训练的保底策略

如果因为部署限制必须走端到端单模型,有几个保底策略。不要在增强里开 mixup,不要大幅扰动 H 通道。样本不均衡可以用 cls_loss 权重,或者对红灯类做重复采样。类别定义上,要把尾灯和信号灯场景尽量区分开,必要时用负样本专门压一下。模型输出后,还要对相邻帧做信号灯状态投票,红灯必须在连续多帧内保持才能进入违章判断逻辑,这能有效抵消单帧误检。

5.4 类别信息如何变成业务逻辑

视觉模型的输出最好是这样的结构化记录:每帧里每个目标一个 json——frame_id、class、bbox、conf。跟踪器给车辆分配 ID 后,规则引擎拿到车辆 ID + 信号灯状态 + 停止线位置,判断车辆越过停止线时信号灯是否为红灯。

在这个流程里,模型类别错误会被指数级放大。所以类别区分不是模型的炫技指标,而是业务正确率的基础。项目评审阶段就要和业务方对齐口径:他们更接受“红灯状态判断必须极准,车辆类别偶尔分错没关系”,还是必须所有类别都高精度。对齐之后,数据投入方向完全不同。

6. 常见问题与排查技巧实录

6.1 高频问题速查表

问题现象常见原因排查方法解决措施
红灯被识别成绿灯H通道增强过猛、mixup污染、白平衡差异看训练增强配置;查混淆矩阵 red/green 行关掉颜色扰动和mixup;改用两级分类
远处的车漏检imgsz=640、目标本身太小看 small AP;可视化漏检样本imgsz 提到1280,或切图推理
停止线断断续续标注不连续、跨尺度抽检停止线标注按断段标注,或改成分割辅助
夜间尾灯误检红灯负样本不足、conf阈值低统计误检帧来源加入无信号灯夜晚样本;提高conf
训练loss不下降标注噪声、类别定义混乱抽查label;看cls_loss统一标注规范、合并相似类别
mAP高但部署效果差训练/部署前处理不一致对比letterbox、缩放、NMS参数严格对齐预处理和后处理参数

6.2 实战案例:小目标漏检从哪来

有一个项目初始用 imgsz=640,整体 mAP50 0.9 以上,但真实视频里 40 米外的小车漏检很严重。后来先用 SAHI 切图推理,漏检大幅改善;再把训练统一改到 imgsz=1280,两者结合后小目标召回率提升了约 12 个百分点。这个案例说明,小目标问题第一刀永远从分辨率和输入策略上切。

6.3 实战案例:红绿灯颜色错乱

另一轮实验,训练时开了 mixup=0.2,验证 mAP 看着挺高,但夜间红灯老被识别成绿灯。关掉 mixup、降低 H 通道扰动强度后,红灯/绿灯混淆矩阵里的交叉错误几乎消失。演练结论是:在颜色强相关的小目标分类任务里,语义混合类增强要非常非常谨慎。

6.4 实战案例:停止线标到怀疑人生

还有一次停止线检测不稳定,排查后发现是标注偷懒,把整段线框成一个 bbox,模型对线中间段召回一直不稳定。重新按可见断段标注后,停止线 mAP 才真正可用来判断越线。细长目标的标注规范,最好一开始就定死,后面返工成本很高。

6.5 被硬件坑过的一次

过曝的摄像头下,红灯灯盘中心一片白,边缘才是红色,模型怎么调都容易误判。这本质上属于硬件问题,不是模型问题。后来一边把标注 bbox 框到中心白区,一边协调设备方调整曝光参数,问题才真正压下来。智慧交通项目里,摄像头质量参差不齐是常态,模型不可能补偿硬件所有缺陷,尽早把问题反馈到设备侧。

最后,说点个人体会

做这类项目最深的坑就在类别区分上。我一开始图省事,把所有信号灯都归成 traffic_light 一个类,训练出来总 mAP 很高,以为万事大吉。结果业务方一看路口视频说“这红灯状态全是绿的,怎么出取证报告”,我才意识到端到端类别区分和业务可用之间还差着十万八千里。后来重新梳理类别定义,把“信号灯位置检测”和“红灯状态分类”拆成两级,配合多帧投票,系统才真正到达可用状态。

如果你也是第一次做类似智慧交通项目,我的建议很直接:先别急着堆模型参数量或魔改网络结构,先把小目标样本、信号灯颜色、停止线标注这三件事做到位。地基扎实了,YOLOv8 只是把成果放大了而已;地基不牢,换什么模型都是在沙子上盖楼。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询