☰
基于YOLOv8的Robotaxi红绿灯识别:从数据到部署的工程实践
2026/10/20 2:40:01 网站建设 项目流程

做Robotaxi感知的人,最容易被外行问的一句话就是:红灯停、绿灯行,这有什么难的?但真把这段逻辑搬到系统里跑一个季度,你会发现这句话简直是当代工程陷阱。我前阵子刚完成一轮面向L4级出租车的红绿灯识别改造,主力检测器就是YOLOv8。这套方案要解决的问题很具体:在城市公开道路上,让自动驾驶出租车在白天、夜间、雨雾、逆光、远距离、遮挡、闪烁倒计时等条件下,稳定判读每一个灯杆上跟本车行驶方向相关的红绿灯状态,并且把它转换成规控能用的通行指令。

这篇文章适合三类人看:一是刚接触Robotaxi感知、想了解信号灯模块怎么设计的同学;二是已经用YOLOv8做过通用目标检测、想扩展到交通场景的开发者;三是准备做车路协同或辅助驾驶感知方案、需要参考工程落地细节的从业者。我尽量把数据准备、模型选型、训练调参、推理部署、车道关联这些环节都讲透,也会把我的踩坑记录直接摆出来,省得你再走一遍弯路。

1. 先想清楚:Robotaxi的红绿灯检测到底在解决什么问题

很多项目一开始就栽在任务定义上。红绿灯检测并不是"从图像里找出红绿灯框"这么简单,它本质上是一条完整的信息链路:图像中哪个位置有灯体 → 这个灯体现在处于什么状态 → 这个灯和本车的哪条车道相关 → 状态连续变化后规控应该怎么决策。任何一个环节断掉,整个模块就不能用。

1.1 拆开看,其实是四个子问题

第一是灯体检测。这个任务的难点不在算法,而在目标尺寸。一个路口远处的红绿灯,在640分辨率图像里往往只有10到20个像素宽,属于典型的小目标。通用的目标检测器在COCO上跑得很好,但直接拿来做红绿灯,远距离召回率会惨不忍睹。尤其Robotaxi要在60km/h巡航时提前看到150米外的信号灯,光靠单帧小目标检测很难稳。

第二是状态识别。信号灯不是简单地"红、绿、黄"三分类。同一个灯杆上可能有圆形红灯、左转箭头、直行箭头、倒计时数字;夜晚还有红黄同时点亮等待起步的特殊状态;黄灯闪烁和稳定黄灯的语义还不一样。把这些状态揉进一个类别体系里,是数据设计的第一步,也是最容易乱的一步。

第三是时序稳定。单帧检测结果必然有抖动,上一帧是红灯、下一帧变成绿灯,这种跳变对规划控制是灾难性的。规控收到错误的"绿灯"指令后可能直接起步,而前方实际上还是红灯,后果不用多说。所以检测模块必须做状态机或时序滤波,让状态输出保持连续可解释。

第四是车道关联。这也是很多人忽略的一点。路口视野里同时存在十几个灯体,有本车道直行灯、对向左转灯、人行横道灯、非机动车灯。如果不管语义,把"识别到的最近红灯"当成"本车不能走",系统会在某些路口反复急刹。真正的做法是结合自车定位、地图和车道拓扑,只关注与本车当前通行方向相关的那几组灯。

1.2 为什么不能直接拿通用检测方案照搬

我在项目初期试过直接把YOLOv8在自采数据上训练,然后接一个颜色分类器,效果很不理想。原因有四个,每一个都是通用模型直接迁移时绕不开的坑。

第一,样本不均衡极其严重。真实路采里,绿灯和红灯出现的频率受到路口放行时间影响,而左转箭头只有在特定相位才有;夜间数据里刹车灯和红灯高度相似,会让分类器产生大量误检。第二,颜色受曝光影响太大。强逆光下红灯会拍成淡黄色,夜间LED灯过曝后变成白色光晕,动态范围不够的相机很难区分。如果只是简单地在RGB空间里找红色,工程上根本站不住脚。第三,灯体的空间上下文太重要。同样的红灯,出现在画面右侧可能是另一条进口道的信号灯,这时候检测器再准也没用,必须靠位置、语义、地图一起判断。第四,单一网络的输出形式不够用。RGB检测只能告诉你有灯,不能告诉规控"左转车道是否可以进入待转区",这需要结构化输出。

所以我在项目启动时给团队的交付标准只有一句话:不仅要"看得见灯",还要"读得懂灯"。这个定位决定了后面所有数据标注和类别设计的方向。

2. 为什么选YOLOv8作为主力检测器,而不是其他模型

选型阶段我对比过YOLOv5、YOLOv8、RT-DETR和几个开源的红绿灯专用检测方案。最后综合精度、推理速度、工程生态和部署难度,我选了YOLOv8,而且在实际路测里验证了它的稳定性。

2.1 YOLOv8到底强在哪

YOLOv8相比前代不是简单加了一层结构。它把CSPDarknet的C3模块升级成了C2f模块,简单理解就是梯度流动路径更丰富,Backbone提特征的能力更强,尤其是在小目标语义上比YOLOv5的C3表现更稳。Neck部分保留了PAN-FPN结构,让高层语义和底层细节双向融合,这对小尺寸灯体非常关键。检测头换成了Decoupled Head,分类和回归分支不再共享全连接层,训练时收敛更快、精度更高。同时抛弃了Anchor,改成Anchor-Free,省掉了聚类和调参的麻烦。

最让我看重的是Ultralytics的生态。数据格式、预训练权重、训练脚本、导出ONNX、再转TensorRT,这一整套流程非常顺畅。团队里新同学上手也快,不会因为工程细节卡住。YOLOv8的官方仓库支持close_mosaic这类细节参数,我在小目标训练时能用很低的成本做很多实验。

2.2 模型尺寸怎么选

Robotaxi车端平台的算力通常有限。我用的工控机加车规级GPU大概在50到100TOPS区间,还要同时跑目标检测、车道线、可行驶区域、障碍物跟踪等多个模块,分给红绿灯检测的算力窗口不能太大。我实测YOLOv8s在640输入下,TensorRT FP16推理大概20到30毫秒左右;YOLOv8m在同等条件下要到40到60毫秒。红绿灯检测的精度收益在m和s之间的差距并没有想象中大,但推理耗时实实在在多了近一倍。

所以我最终选的是YOLOv8s作为主力,在高价值路口或者远距离需要提前看清灯体的场景,单独开一路高分辨率ROI推理,而不是全局把模型换大。这个策略在算力和效果之间取了个折中,实际路测下来是最稳的。

2.3 类别体系设计是重中之重

这里我踩过最大的坑。第一版数据我按照"red、green、yellow"三分类来做,结果发现完全不够用。一个左转红灯和一个直行红灯在图像里长得一样,但语义完全相反;倒计时数字闪烁时,红灯会周期性偏暗,分类器很容易误判成熄灭。

第二次迭代我把类别细分为状态即类别,比如red_circle、green_circle、yellow_circle、red_left、green_left、red_right、green_right,再加一个red_yellow组合状态。这样设计的好处是,模型输出的类别本身就携带语义,后处理不用再做复杂的二次判断。代价是类别数量变多,数据标注成本上升,某些组合类别样本少,需要专门采集。但我做过对比实验,完整的多状态类别体系比"灯体检测+颜色解耦"在真实路测里的鲁棒性要高不少。

有一种方案是"灯体检测网络 + 灯芯分类网络"解耦,分类网络只对灯芯区域做精细化颜色判断。这种做法能够缓解小目标检测和颜色分类的冲突,但工程复杂度高,两个网络之间的依赖链也容易出问题。我的建议是:样本充足就选状态即类别;样本有限且只有单灯型场景,再考虑解耦方案。

3. 数据、标注与训练:精度其实不在网络结构里

很多团队拿到YOLOv8后第一件事就是拉数据开训练,结果精度上不去就怀疑模型不行。实际上在红绿灯这个任务里,数据质量和标注细粒度决定了性能上限。我甚至可以说,同一个网络,换了数据规范后mAP能差10个点以上。

3.1 数据来源与覆盖策略

模型要见得足够多、足够杂,才能应对真实运营场景。公开数据集里的交通灯子集是个不错的起始点,做预训练没问题,但完全依赖公开数据做Robotaxi落地根本不现实,因为不同城市的信号灯样式差异很大,同城不同路口甚至都可能有不同的灯杆布局。我采取的是预训练加自采微调路线:先在公开数据集上训练一个基础模型,然后在自己车队的采集数据上做长尾补充,重点覆盖三类稀缺场景。

第一类是夜间数据,尤其是各种LED灯曝光、泛光、频闪的状态;第二类是雨雾天气,雨水在挡风玻璃上会造成光晕和重影,灯体边缘极其模糊;第三类是黄昏和逆光,天空亮度高,灯体和背景对比度极低。这三类如果每个路口只采几段,模型基本能稳住;如果按普通目标检测的做法随手采一段白天数据就训练,路测时碰到晚高峰就会频繁误判。

数据标注也是个容易忽略的环节。红绿灯框的物体边界往往不清晰,尤其是过曝后的光晕边缘,不同标注员会给出完全不同的框。我在标注规范里明确规定:框必须贴合灯体的物理外框,不能把光晕包进去;遮挡超过一半的灯不标;距离过小、人眼也无法判断状态的灯不标。这样虽然会损失一部分样本,但训练标签的置信度大幅提高。

3.2 一个经常被忽略的点:灯体框和灯芯框

这是我在项目里觉得最有价值的一个技巧。远距离时,整个灯体在图像里非常小,但灯芯区域更小,可能只有三五个像素,颜色信息几乎丢失。如果只用一个矩形框标注整个灯体,分类分支看到的特征里混杂了黑色外壳和背景,状态判断精度很难上去。

我后来采用双框标注:一个灯体框负责定位,另一个灯芯框负责状态分类。训练时,YOLOv8的主检测头输出灯体框,同时要求分类分支只从灯芯区域提取颜色特征。这个改动看似只是标注工作量大了一点,但对夜间红绿灯识别和过曝场景的改善非常明显,误检率能降低三分之一左右。

你们做时不需要一步到位,可以先按单框跑通流程,等发现远端信号灯识别率低、状态误判集中在夜间时,再引入双框方案。系统整体架构不用动,只需要改标注规范和训练数据的组织方式。

3.3 训练参数与增强策略实录

这里我直接给出一份跑过多次、验证有效的训练配置,供参考:

输入分辨率:YOLOv8默认640,但对灯体这种小目标,我建议提升到960甚至1280训练,推理时保持640,精度和速度能取得更好的平衡。我的实验证明,仅提升训练分辨率,远端信号灯召回率就有明显提升。不过要注意,这会让显存和训练时间上升,需要根据设备情况调整。

增强策略上,Mosaic和MixUp开启,但最后10个epoch关闭Mosaic,只保留轻微的光照扰色和随机平移。我自己试过全程开Mosaic,模型在真实场景的泛化反而变差,小物体被裁坏的情况尤其多。

关键超参方面,batch size根据显存来,我常用64到128;初始学习率0.01配合Warmup 3个epoch;使用EMA指数滑动平均;训练轮数120到150轮,配合余弦退火调度器。混合精度训练开启,整个训练流程能快30%左右。

验证集划分必须按路口和时间切片来分,不能随机把几帧相似画面拆到训练和验证里。否则测出来mAP很高,上路一测就露馅。我通常是按"城市+路口+时段"做分组,比如同一个路口晚上的数据整体作为验证集,训练集里不放同路段同方向的数据,这样才能评估真实泛化能力。

同时跑各项指标的实验时要有记录表格,我一般盯mAP50、mAP50-95、红灯类别的召回率,以及最关键的"绿灯误检数"。对Robotaxi来说,漏检红灯最多引起保守停車,但误检绿灯会让系统错误起步,这是完全不同的风险等级。所以我把绿灯误检单独列为考核指标,模型迭代时优先压它。

3.4 模型蒸馏与预训练,值得做就别省

红绿灯检测这种任务看起来任务简单,但样本少、长尾多,直接用随机初始化的YOLOv8从零训练不是不行,只是很容易过拟合。我用的是两阶段策略:先在公开数据集的大模型上预训练,蒸馏到YOLOv8s,再用自采数据微调。蒸馏过程不复杂,把大模型最后一层的输出作为软标签,计算KL散度加在原有分类损失上,YOLOv8支持蒸馏配置,覆盖模型文件里换Teacher即可。效果方面,夜间低对比度场景的稳定性提升很明显,训练收敛也更快。

4. 推理部署与车端工程化:瓶颈在细节

模型训练到一定精度后,真正的挑战是部署。车端环境没有大显存服务器,没有PyTorch,也不允许动辄几十毫秒的推理开销。我在这一环节把精力几乎全部放在TensorRT优化和前后处理细节上。

4.1 TensorRT量化怎么选

我的默认策略是FP16,极少场景用INT8。FP16的精度损失几乎可以忽略,推理速度相比FP32提升明显,而且部署流程简单,不需要校准数据集。INT8虽然更快,但量化后红灯类别、偏暗灯体这类小目标的检测精度有时会掉得厉害,需要反复校准和验证。对Robotaxi来说,就算INT8能省10毫秒,我也不愿意用安全换性能。如果确实需要INT8,建议先用一个月路测数据做校准集,保证量化前后的混淆矩阵一致,再考虑上线。

TensorRT版本选择上,我用的是支持EfficientNMS插件的版本,把YOLOv8的Detect头转换成NMS算子放进了TensorRT里,整个模型导出、序列化、推理都走C++接口。这样可以免掉在CPU上做后处理的步骤,GPU上直接出框,延迟更稳定。

4.2 letterbox和分辨率匹配:一个经常出问题的环节

很多项目训练和推理用的数据预处理不一致,表现是训练时精度不错、部署后掉点。YOLOv8默认letterbox做了灰度填充,目的是保持宽高比不变。我在导出模型时把letterbox参数固定死,保证训练时也是640/960/任意尺寸,推理时严格使用相同的填充值。如果不固定,某些帧会被拉伸变形,灯体的形状和色彩比例失真,检测结果会飘。

此外,时域抖动问题在部署端也会被放大。摄像头采集的每一帧,letterbox的填充尺寸如果跟着动态变化,画面内容位置会有轻微漂移,跟踪模块容易产生跳变。我的做法是固定推理分辨率,比如640x384或960x544这类适合道路画面的比例,并关闭动态输入shape,让整条pipeline的时序最稳。

4.3 后处理参数别盲目套用

YOLOv8的默认置信度阈值和NMS参数适合通用目标,红绿灯场景下需要重新调。我最终用的conf阈值是0.3到0.4,IOU阈值0.5。置信度设太高,远距离红灯这种本来就弱的小目标会被滤掉;设太低,护栏上的红色反光、前车尾灯会被误检成红灯。实际项目中我建议做一个"置信度-误检率"曲线,针对红灯和绿灯分别标定一个阈值。

另外,在ROI区域外直接过滤掉检测框,是降低误检最实用的一招。靠近路口时,信号灯只可能出现在画面中几个固定区域,我根据自车定位和地图数据,预先计算信号灯的图像投影区域,再做几何过滤,把检测框限制在ROI矩形内,误检大幅下降。这个方案比单纯提高模型能力更便宜、更稳。

4.4 帧间状态机与多帧确认

单帧输出直接送给规控是绝对不行的。我实现了一个轻量级状态机,每个信号灯跟踪对象记录最近N帧的状态序列,输出规则是:红灯必须连续3帧确认才输出红灯,绿灯连续2帧确认,黄灯连续2帧确认,红黄组合状态连续3帧确认。这样可以过滤掉单帧抖动、闪烁、传感器噪声造成的瞬时误判。

这里要特别注意"去抖"不能让决策延迟太长。红灯变绿瞬间,如果系统还按红灯处理,车辆起步会有滞后;相反绿灯变黄灯瞬间,如果还按绿灯处理,系统会继续通过路口,危险系数很大。我的经验是红转绿时用2帧确认、优先级高处理;绿转黄时用3帧确认、但不拦优先级,规控端再配合限速策略做兜底。

倒计时数字的读取不建议放在检测网络里。我另外跑了一个轻量级OCR模块,针对倒计时区域做二值化和模板匹配。检测网络只判断灯色状态,倒计时只作为辅助信息融合到决策中,不参与主状态机。

4.5 多相机时间对齐

Robotaxi通常有环视多个摄像头,红绿灯检测主要用前视相机。如果一个前视相机带HDR和另一个不带,画面亮度和色彩差异会很大,两个相机同时输出的状态可能不一致。我遇到过前视相机在强逆光下把红灯判成黄灯,而后侧相机完全看不到灯的情况。排查后发现是摄像头曝光策略不一样,一台开了自动曝光限制,另一台没开。统一所有相机参数后,这个问题基本消失。

5. 感知输出不是终点:与地图先验和车道拓扑结合

前面说的一切,最终都要落到规控能用的语义指令上。这也是Robotaxi和普通ADAS辅助驾驶最大的区别:红绿灯检测模块不能只输出"图像里有红灯",必须输出"本车当前车道应停车/可通行"这个决策级信息。要达到这个目标,必须把检测结果和地图先验融合起来。

5.1 车道级信号灯关联,核心问题在哪

城市路口最复杂的场景是:同一个方向上可能存在三个灯,左转箭头、直行圆灯、右转箭头;如果本车在直行车道,左转箭头变绿也与你无关;如果本车在左转车道,直行变绿同样不能走。如果检测模块把所有绿灯全输出给规控,车辆会在桥下左转道直行灯变绿时错误起步。

所以我在系统里增加了一层关联逻辑:构建一个"车道-信号灯"映射表,记录每个停车线前哪些灯管哪些车道。自车定位后,先判断本车在哪条车道,再通过地图拿到该车道对应的信号灯编号,最后只在候选灯编号中匹配视觉检测框。这套逻辑在标定准确的地图下非常好用,误报率几乎可以降到个位数。

5.2 高精地图或者标准地图,两种不同玩法

没有高精地图的条件下,RSU/路侧设备和导航地图通常只能给出路口大致位置、停止线位置、车道数量。这种情况我用的是投影ROI办法:把当前路口关联的一组候选信号灯的位置投影到图像平面,得到多个ROI,检测网络只在ROI区域做高置信度目标搜索。这种方式仍然能大幅降低误检,因为图像中其他位置的任何"红灯"都会被直接忽略。

如果连地图都没有,只靠纯视觉硬扛,我的建议是把"最近信号灯"和"当前可通行方向"直接绑定,但必须增加一条安全规则:多个方向信号灯状态冲突时输出"未知/停车等待"。这在工程上宁可保守一些,也不要去赌规控系统的判断。

5.3 多传感器怎么辅助红绿灯检测

激光雷达本身看不出灯的颜色,但它能提供精确的灯杆位置和空间范围。我把激光雷达点云聚类得到的灯杆位置投影到图像,和相机检测框做IOU匹配。匹配成功后,检测框的置信度可以适当上调;如果相机出了一个框但雷达没有对应目标,那可能是反光板、广告牌和车灯造成的误检,直接降低置信度甚至过滤掉。导航的粗略定位也能帮上忙,它至少能告诉系统前方300米有个路口,可以提前缩小搜索范围,而不是全图扫描。

这套融合逻辑在雨雾天气、摄像头被泥水遮挡时很关键。相机模糊时可能完全看不见灯,但雷达位置还在,规控至少知道"这里有个信号灯存在",再配合地图和状态机的最后已知状态,可以做出更安全的决策。

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

做红绿灯识别最耗时间的不是写模型,而是在真实道路上排障。我总结了几类出现频率极高的问题,直接做成速查表。

现象可能原因解决方案
远距离红灯召回率低训练分辨率过低,小目标特征丢失提升训练分辨率到1280,开启多尺度
夜间红灯误检成绿灯颜色通道曝光失衡,LED泛光偏白统一相机HDR策略,增加夜间样本
雨后灯体重影挡风玻璃水痕和LED反光引入雨雾增强数据,增加水痕合成
红绿灯和汽车刹车灯混淆刹车灯颜色接近、尺寸接近增加刹车灯正样本,设置ROI + 时序过滤
上一帧绿灯下一帧红灯跳变单帧置信度抖动状态机多帧确认,调低conf阈值
左转箭头直行圆灯互相误用车道关联缺失引入地图车道-信号灯映射表
黄灯闪烁被当成稳定黄灯类别定义不细单独增加flash_yellow类别
摄像头强逆光过曝动态范围不足开启HDR、限制自动曝光、增加逆光样本

重点说三个我在实际路测中反复踩的坑。

第一个是雨天夜间。低照度加积水反光,图像里到处是红色光斑,模型很容易把所有亮斑都当成红灯。我一开始靠调高置信度阈值来压误检,结果远端真实红灯也被压掉了。后来我把刹车灯数据集专门拉出来做负样本训练,加了一部分合成雨痕增强,问题才缓解。这个过程的经验是,不要只调阈值,要从数据侧去解决混淆。

第二个是不同城市灯型差异。有的路口是横杆灯,有的路口是竖杆灯,有的灯杆在左边有的在右边,还有的用T型灯、箭头灯。同一个模型从A市迁到B市,如果B市用了完全不同的灯型结构,检测精度会掉得很明显。我后来的策略是在每个运营城市的前两周专门做数据采集和模型微调,把"城市适配"当成一次标准的发布流程。

第三个是高架桥下和环境光极差的路口。没有光照时红灯会非常暗,YOLOv8的输出置信度普遍偏低。我测试过几个方案,其中效果最好的是给检测网络的输入图像做一个轻量化的对比度自适应增强,只在暗环境下启用。这相当于把预处理策略也做成动态的,不要一套参数走天下。

调试过程的记录习惯也很重要。我要求每次路测都保存原始图像、检测结果、后处理状态机日志,并打上时间戳。出了问题先回到日志里回放,看是检测掉了、状态机判错了、还是映射表标错了,三个环节单独排查,效率会高很多。如果上来就盲目改模型,很容易把数据问题和新引入的问题混在一起,越调越乱。

最后再分享一点我个人的体会。做完这套红绿灯检测系统,我最深刻的感受是:模型结构选型只是开始,真正的护城河在数据规范、标注细节、后处理状态机和地图融合这些看不见的地方。YOLOv8作为检测器非常称职,但让它在Robotaxi场景里站住脚的,是围绕它搭建的那一整套工程体系。

如果在现有基础上想继续扩展,我建议团队下一步可以尝试两件事:一是引入BEV视角的信号灯投射模型,把图像检测结果统一投影到自车坐标系里,让规控收到的信息天然和地图对齐;二是把城市级路测数据回传形成自动化的数据闭环,每次接管记录自动进入训练集,模型迭代速度和最终上限都会有本质提升。这个方向是我接下来准备在项目里继续推进的,也值得正在做类似系统的团队认真投入。

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

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

立即咨询