Robotaxi红绿灯检测实战:YOLOv8从数据到部署的完整复盘

发布时间:2026/10/20 20:17:39
Robotaxi红绿灯检测实战:YOLOv8从数据到部署的完整复盘
跑 Robotaxi 的工程师们私下有个共识感知系统里红绿灯检测是看起来最简单、实际最折磨人的模块。一个高清摄像头对着路口画面里就是几个发光的圆点YOLOv8 随便训一版都能检出框来可一旦把这套模型放进开放道路的 Robotaxi 上跑你会发现真正的难点全在“检测”之外——数据怎么定义、状态怎么判、时序怎么稳、地图先验怎么配合每一环都会反过头来折腾模型。我参与过一整个 Robotaxi 感知项目专门负责用 YOLOv8 搭建实时红绿灯检测模块从数据标注一路做到实车部署今天把整个决策过程踩过的坑、验证过的路径完整复盘出来。这不是一篇 API 调用教程而是一条从实验室 Demo 到路测可用的完整路线图。1. 场景认知先行红绿灯检测在Robotaxi里到底难在哪在动手训练模型之前我们花了很长时间统一对问题的认识。这个阶段看起来很虚实际上是后面所有选型的基础。1.1 它是安全关键模块不是“感知一个目标”那么简单通用目标检测里“检出目标 给个框”就算完成但红绿灯不一样它直接决定了车辆是走还是停一旦判断错误轻则造成乘坐体验崩溃重则触发安全事故。这个定位决定了后续每一个设计都要考虑兜底模型不确定时怎么办、检测丢失时怎么办、分类置信度低时怎么办。我见过一些团队把红绿灯检测当成普通检测任务来做结果模型 mAP 很高却在转灯瞬间给了规划模块一个震荡的状态车辆在路口前急刹乘坐体验很差。所以做这个模块的第一原则准确性和稳定性优先召回率和速度是约束条件下尽可能去保证的指标而不是反过来。1.2 高精地图能帮忙但不能背锅Robotaxi 一般都有高精地图上面标注了信号灯的三维位置、朝向、杆件关联、停止线位置。我们确实利用地图把“哪些灯管我这条车道”这件事前置把检测范围收敛到地图投影的 ROI 附近。但必须想清楚地图是静态的灯的状态是实时的地图不能告诉你现在是红灯还是绿灯。它的作用是把候选空间压缩让模型少做无谓的全图搜索同时在后处理里提供位置先验来过滤误检。如果试图用地图投影直接代替视觉检测就会在定位漂移、地图过期、施工变灯这些场景里吃大亏。我们的分工很明确视觉负责“看到灯并判断状态”地图负责“这盏灯在哪个位置、管哪条车道”两者谁也别替代谁。1.3 传统图像方案为什么会被淘汰早期很多团队做过基于颜色阈值和形状匹配的红绿灯检测把 RGB 转到 HSV找红色和绿色的高亮连通域再用圆形度、面积过滤。这个方案在固定摄像头、固定光照条件下确实能用但开放道路上会遇到一连串让它崩溃的输入逆光下绿灯发白、夜间车灯与红灯同色、LED 灯在传感器上产生频闪和色偏、雨天地面积水反光。YOLOv8 这类数据驱动检测器的本质是把“什么形状、什么颜色、什么上下文组合在一起才算红绿灯”这组判断交给大量标注数据去学习泛化性和鲁棒性比手工特征高一大截。这也是我们选择它作为主检测器的根本原因。2. 数据工程小目标、状态定义、样本分布三个硬骨头数据是红绿灯检测真正的主战场。模型结构做得再花哨数据没理清楚实车一定带不动。2.1 公开数据集能用但得会组合我先后摸过几个公开红绿灯数据集情况很不一样。有一个以远距离小目标标注著称对训练小目标召回非常有价值另一个来自固定路口摄像头视角稳定但和车端前视视角差异大还有一个包含大量复杂交叉口和不同类型信号灯更接近实际运营路口的灯型组成。我的用法是几个数据集混合训练但按比例控制主力放在与车端视角接近的数据上。一个容易踩的坑是直接把某个数据集拿来训完就上车——数据采集视角不同泛化几乎必然失败。2.2 先量一下目标尺度再谈模型配置红绿灯的目标尺度问题比很多人想象得更严重。以 1080p 车规相机为例50 米外的信号灯灯体在画面里通常只有 20×40 像素左右比 YOLOv8 默认 640×640 输入下的很多常见目标小一个数量级。训练前我会先对标注框做一次宽高分布统计如果大量框落在 32×32 以内就必须认真考虑输入分辨率、P2 特征层、切片推理这类小目标优化手段否则后面调模型结构全是白费力气。这一步看起来简单但直接决定了后续模型选型和训练策略我们项目初期没有意识到等到夜间样本召回上不去才回头补功课浪费了不少时间。2.3 检测框怎么标直接影响系统上下限这是数据工程里最容易忽略的决策。不同数据集的标注习惯完全不同有的框整个灯壳含黑底边框有的只框发光的灯珠有的把同一个信号灯拆成多个箭头灯分别标注。我们最终统一为检测器只负责框出整个灯面/灯壳灯珠的状态交给后续分类逻辑。理由很简单——Robotaxi 需要知道一盏灯从头到尾的完整状态包括故障和熄灭的情况如果检测目标只是一颗发光的圆点灯灭的时候就等于目标消失系统会对“灯灭了”和“没有灯”完全分不清。箭头灯的处理上我们把箭头整体视为灯面的一部分不在检测层拆细拆细会让数据样本迅速稀疏反而不利于检测泛化。2.4 状态类别设计检测和状态判定必须解耦很多第一次做红绿灯检测的团队会直接把“红灯”“绿灯”“黄灯”“左转红灯”做成检测类别。我在项目初期也这样干过后来发现数据分布极不均衡尤其是左转箭头和右转箭头的样本少得可怜模型很难在保持基础状态召回的同时学会细粒度箭头。后来调整为检测器只输出一个通用类别 traffic light再加一个独立的分类阶段做状态判定。这样检测器样本量大、类别均衡分类器可以专门优化“这小片区域到底是什么颜色和方向”的问题两个模型各自调优整体效果提升明显。2.5 样本分布与增强策略要贴着场景走真实路采数据里白天晴天的占比天然偏高但真正让感知系统翻车的是傍晚逆光、夜间、雨雾这些低发样本。我们对训练集做了重采样白天样本降权黄昏、夜间、雨天样本升权让模型在困难场景上学得更充分。数据增强方面HSV 扰动对红绿灯特别有效因为灯珠在不同相机曝光和白平衡下的色偏非常大我还加了一种模拟过曝导致高光糊化的增强否则模型看到强曝光下的“白红色大圆斑”时往往会不知所措。3. YOLOv8选型与配置一切选择围绕算力预算和小目标模型方面我们最终选定 YOLOv8但过程中做过好几轮对比这里把选型和关键配置讲清楚。3.1 在检测器选型上我们是怎么权衡的两阶段检测器精度有优势但结构重车端算力不允许其他单阶段模型我们也都试过YOLOv8 的 C2f 结构、解耦头、anchor-free 设计在速度和精度平衡上做得不错而且官方生态完善训练、导出、部署一条龙节省了大量工程时间。对 Robotaxi 这种“红绿灯检测只是整个感知系统里一个小模块”的场景工程效率和稳定性往往比某一个指标刷高分更重要。我还试着推理过多帧信息融合方案比如把时序信息引入检测器。后来发现收益有限因为红绿灯的帧间变化更多是频闪和短暂遮挡把这些交给时序后处理去解决比塞进模型里更可控。3.2 模型大小和输入分辨率最优解不是模型最大一开始团队里也有人倾向直接上 YOLOv8x觉得精度一定最高。实测对比下来在相同输入下 x 相比 m/l 的 mAP 提升最多一两个点推理耗时却几乎翻倍。真正带来质变的是输入分辨率从 640 提升到 1280小目标召回提升非常明显。原因不难理解——分辨率上来了灯珠在特征图里保留的空间信息更多模型更容易学到灯面结构和颜色的组合。我们最终落地的组合是 YOLOv8m 1280 输入。这个选择是在车端域控的算力预算下反复测出来的单帧推理大概 15~20 ms既保证了精度也不至于挤占其他模块的计算资源。3.3 小目标优化三件套P2、切片推理、多尺度训练YOLOv8 官方提供了带 P2 层的模型结构在特征提取早期额外引出一条高分辨率特征图对小目标检测增益明显。我们开启 P2 之后小目标召回涨了一截代价是训练和推理开销增加。切片辅助推理的思路则是把大图切成多个 tile 分别推理再融合离线评测里能显著提升小目标召回但由于实车算力和延时限制没法全程使用我们把它定位为“离线难例挖掘工具”——路测数据拿回来先用切片推理跑一遍找出模型最容易漏的帧再针对性补充训练。多尺度训练我建议无条件开启YOLOv8 原生支持对尺度变化的泛化帮助非常大几乎零成本提升。3.4 常被忽略的相机链路曝光策略要先于模型优化模型再强也架不住输入图像本身出了问题。夜间红灯过曝成一团白色光斑、强逆光下绿灯完全看不清颜色这些情况里模型的失败根源在相机。我们后来跟相机团队一起调整了曝光权重让曝光策略更照顾交通灯区域尽量避免高光饱和模型的表现立刻上了一个台阶。这个经验很重要做自动驾驶感知ISP 和曝光参数本身就是感知系统的一部分别只在模型里找原因。4. 训练与评估mAP是面子实车曲线才是里子数据、模型选好以后进入训练和评估阶段。这一阶段最大的认知更新是mAP 会骗人不能只看它。4.1 训练流程与验证集划分训练基本沿用 YOLOv8 官方推荐的超参SGD 优化器、约 200 epoch、带动量先在混合数据集上预训练再在自有场景数据上微调最后用难例集做第二轮微调。有一个关键细节是验证集必须按场景而不是按帧随机切分。按帧随机切分会把同一路口的连续帧同时分到训练集和验证集模型等于提前见过答案验证 mAP 会虚高不少。按场景切分后验证结果才代表真实泛化能力。4.2 除了 mAP还要看哪些指标红绿灯是小目标密集的任务mAP0.5 到 0.8 都很常见但安全实车场景下我建议额外盯住这几个指标低置信度下的小目标召回率防止模型在关键时刻漏检夜间、雨雾、逆光子集的单独 AP状态分类的混淆矩阵尤其是红灯和绿灯、左转箭头和直行灯的混淆。更重要的是把评测从单帧指标推进到时序指标看整个视频片段里状态切换是否稳定、有没有闪烁。这些指标单看 mAP 是发现不了的我们项目后期绝大部分打磨时间都花在这些“看不见”的指标上。4.3 难例挖掘把离线推理变成数据闭环训练集不是攒完就完事我们后面建立了一条固定流水线路测视频定期跑离线模型把所有低置信度、漏检、错检的帧自动抽出来人工筛查后进入训练集。这件事看着土却是项目后期精度提升最大的单一来源。特别是那些罕见灯型新建路口的倒计时灯、临时信号灯只有靠这种闭环不断积累样本模型才会越来越稳。我建议把难例挖掘做成固定机制而不是项目里的临时任务。红绿灯场景的角落很多靠静态数据集永远覆盖不全。5. 后处理才是真正的护城河状态判定、时序稳定、地图匹配模型输出一个框离车辆能用的“红灯/绿灯/黄灯 方向语义”还有很长一段路。这段路就是后处理也是整个模块工程含量最高的部分。5.1 把状态判定从检测器中拆出来之后分类器怎么设计检测器输出灯面后我会对灯面区域做 ROI 裁剪再送入一个轻量状态分类网络。状态类别要覆盖实际路口的常见组合圆形红、圆形绿、圆形黄、左转箭头红/黄/绿、右转箭头红/绿、直行箭头绿以及“熄灭/未知”。这里有个容易翻车的细节不同状态类别的样本量差异极大左转红灯和右转绿灯都非常稀有需要单独做样本扩充。分类器输入不一定要很大因为灯面区域通常已经比较小关键是从原始分辨率中直接裁剪不要经过去噪和缩放损失细节。5.2 状态机与滑动窗口平衡快速响应和防抖单帧分类结果直接给规划模块车辆会像一个神经质乘客——红灯转绿灯瞬间模型犹豫几帧车辆就开始猛地加速又刹车。我们设计的后处理很简单但很有效对每个跟踪灯维护一个状态计数器状态一致连续两帧才切换而黄灯只需要一帧就生效因为黄灯窗口太短必须立即响应。不同状态的确认帧数不完全一样这个参数要和规划团队一起定他们最清楚车辆能容忍多少延迟。这里用一段简化的伪代码表示这个逻辑# 简化版红绿灯状态机连续确认机制 def update_state(obs, prev_state, counter): if obs prev_state: return prev_state, counter 1 else: # 黄灯单帧生效红灯/绿灯需要连续CONFIRM_FRAMES帧确认 if counter CONFIRM_FRAMES[obs]: return obs, 0 return prev_state, counter 1这样处理下来状态输出又稳又快车辆在路口的决策节奏明显改善了。5.3 地图先验、目标跟踪和车道约束高精地图给了每盏信号灯的三维位置和朝向。推理时先把灯的三维点投影到图像平面得到预期位置再和当前帧的检测框做匹配。这一步能解决很多问题一是过滤掉远处广告牌、刹车灯等非信号灯的高亮物体二是把检测框和地图里的具体灯绑定从而知道这盏灯是否管当前车道。实际工程里投影会受定位标定误差影响偏移几十像素因此匹配搜索框要适当放大不能死抠投影给出的精确坐标。没有地图的场景我们也做了一版纯视觉版本依靠灯的位置聚类和跟踪来近似但可靠性明显弱一档说明地图先验对这个模块的价值很高。5.4 数字倒计时可用但要放在安全边界之外国内不少路口的信号灯带数字倒计时这个信息对舒适性很有用——知道还剩三秒红灯车辆可以提前准备起步。但我们在工程上严格区分倒计时只参与舒适性策略不参与任何安全判定因为倒计时屏可能存在故障、或者与主信号灯不同步如果把安全建立在倒计时上一次不同步就可能造成误判。倒计时识别相对独立用轻量 OCR 或数字分类器就能完成输出给规划模块做参考。6. 实车部署与踩坑从离线优秀到路测靠谱的距离模型训好、后处理调完接上真车才是真正考验的开始。这一阶段把我在项目里遇到的坑按影响程度排了个序。6.1 部署链路与算力分配我们把 PyTorch 训练的模型导出成 ONNX再转成推理引擎以 FP16 精度推理。刻意没有用 INT8因为小目标对量化噪声敏感INT8 掉点明显不值得为一点延时牺牲精度。在车端整体算力分配上红绿灯检测与目标检测、车道线、可通行空间等模块是并行跑的红绿灯模型占大约 15~25ms 的帧预算通过独立进程推送结果避免任何一个模块抖动拖垮整条链路。6.2 LED频闪和曝光问题实车第一个大坑我们第一次路测就碰上了 LED 频闪肉眼看着常亮的绿灯在摄像头采集序列里却一颗亮一颗灭甚至颜色从绿变成黄绿再变回绿。原因是部分 LED 信号灯采用交流供电或脉宽调制刷新频率和相机曝光节拍打架。这个问题靠模型解决不了要从两个方面下手一是与相机团队协商针对信号灯场景调整曝光时间和白平衡策略二是后处理里做帧间容忍——一帧没看到灯不立即判定为“灯灭/故障”允许连续少数帧缺失。更彻底的方案是让相机帧同步跟随市电频率这需要硬件支持有条件的话务必和相机供应商联调。6.3 误检黑名单刹车灯、广告屏、地面反光夜间误检主要集中在红色高亮区域前方排队车辆的刹车灯经常被当成红灯尤其当车辆刚好停在路口等待区时刹车灯的位置与信号灯位置在画面上很接近。我们通过在匹配阶段引入地图杆件先验把不在信号灯杆位附近的红色检测直接过滤掉。其他常见误检还包括路边 LED 显示屏、施工警示灯、雨天积水里的灯光倒影。对这些单纯依赖模型不太现实通常需要结合位置先验、尺寸过滤和跟踪一致性来共同压制。误检来源现象主要处理手段刹车灯夜间红色亮点出现在路口附近被识别为红灯地图杆件先验 位置匹配过滤LED广告屏 / 施工警示灯高亮色块随机出现尺寸过滤 稳定跟踪积水反光地面出现第二个“信号灯”利用灯面位置高于路面的先验过滤白色故障灯体灭灯后白色灯珠被识别为绿灯状态分类器强制白灯不参与绿判断6.4 安全兜底模型不可用时的默认行为最后说一个容易在设计阶段被忽略的问题当检测器持续丢失信号灯或置信度过低时车辆该按什么状态行驶我们的做法是引入“信号灯不可用”状态当连续多帧无法可靠确认当前灯色模块主动上报不可用规划模块进入保守模式——宁可停在路口等待也不猜测绿灯通行。这个兜底设计可能让通行效率偶尔降低但从安全角度看却是整个模块最有价值的一块。整个模块从无到有跑下来我最深的体会有两个。第一红绿灯检测真正的技术含量不在模型而在围绕模型构建的那一整条链路——数据怎么定义、状态怎么解耦、时序怎么滤波、地图先验怎么配合、相机曝光怎么调每一环出了问题最后都会反馈到模型的输出上。第二别被训练榜单上的漂亮 mAP 迷惑Robotaxi 要的是连续视频片段里稳定、可解释、有兜底的状态输出这个标准比任何论文指标都苛刻。YOLOv8 在这个项目里只是一个相当可靠的零件真正让系统跑起来的是那些文档里不会写、只有踩过坑才知道的工程细节。希望这篇复盘能帮你把这条路走得顺一点。