电动车违规识别实战:多特征融合提升小目标检测精度
去年接手某智慧交通试验段项目时甲方提了一个很具体的要求识别电动车违规行为包括不戴头盔、违规载人、闯红灯、逆行。我一开始挺乐观毕竟目标检测这几年成熟得很拿着现成的YOLO系列改一改应该就能交付。结果第一次跑通Demo我就傻了监控画面里的电动车又小又密一个路口十几辆车挤在一起通用检测器能把车框出来就算不错把“不戴头盔”和“戴头盔但头低着”分清楚更是奢望。后来我们把方案重做了一遍核心就是用多路特征融合的思路去解这个问题内部实验代号就叫YOLOv26。这套系统最终在试验段跑通了mAP从最初的78.4涨到87.6误报率降了一半以上。这篇文章把整个项目的来龙去脉和踩坑过程整理一下给同样在做非机动车检测、智慧路口、城市治理视觉方案的朋友做个参考。我不写太多理论废话重点放在数据怎么攒、特征怎么融、训练怎么调、部署怎么落地这几件实事上。1. 通用检测器搞不定电动车违规问题出在三个硬约束先说说为什么不能直接拿通用检测器套。很多人以为电动车违规识别就是一个目标检测任务模型负责把电动车框出来再按框里的内容判断有没有戴头盔。真这么做精度上不去是小事关键是逻辑上就走不通。1.1 小目标与高密度画面里全是“指甲盖大”的目标监控摄像头一般架在路口立杆上高度6到8米俯拍视角下电动车在1080P画面里通常只占七八十个像素最小的甚至只有40個像素左右。对比一下COCO数据集里的常见目标一辆汽车在同等画面里能占到300像素以上电动车只有它的四分之一。目标越小特征信息越少检测头能依赖的纹理细节几乎为零。更麻烦的是密度。早晚高峰的路口电动车不是一辆一辆出现的而是一群一群的。我统计过一段10秒的监控片段画面里同时出现的电动车最多到23辆彼此遮挡严重车把、车轮、后座经常被别的车挡住。通用检测模型训练时面对的大多是单目标或少量目标的场景遇到这种密集遮挡环境候选框互相抑制、漏检率飙升。1.2 “违规”不是物体属性而是关系属性再往深了想违规行为本身不是一个“物体类别”而是一个“关系判断”。拿不戴头盔来说模型要同时知道“有一辆电动车”和“骑车人的头上没有头盔”。问题在于如果车里的人背对着摄像头或者戴着帽子光看一帧画面人眼都容易看错模型就更难。闯红灯和逆行更特殊它们依赖时间信息。某个瞬间电动车在停止线内下一瞬间它出现在路口中间这才能判断它闯红灯了。单帧画面只能看到“有一辆车在路口”无法判断它是不是“闯”过来的。通用检测器的设计哲学是“看图说话”给它一张图它输出物体框和类别但给不了“这个物体正在违反交通规则”这种跨帧结论。这也是我们后来坚定走多特征融合路线的根本原因单靠检测分支解决不了行为语义必须叠加车道线、红绿灯状态、历史轨迹这些上下文特征。用我的话来说电动车违规识别不是“目标检测问题”而是“目标检测场景理解时序推理”的复合问题。1.3 类别模糊电动车、自行车、摩托车在画面里分不清楚第五个让人头疼的点是类别之间的模糊边界。电动车和自行车在俯拍视角下外形几乎一致只能靠车身的宽度、轮胎粗细、有没有电池仓来区分。摩托车混杂其中就更乱高速运动时拍出来的照片经常带着运动模糊别说模型人眼都得多看两秒才能分辨。如果分类只有“电动车”和“非电动车”还好办但违规判断往往需要细颗粒度的类别。不戴头盔的处罚逻辑和违规载人的处罚逻辑不一样撞到一块就乱套了。所以我们后来把类别体系重新设计了一遍这个后面详细说。2. 数据是第一道关口真实样本不够合成数据和预标注怎么配合数据是这套系统里投入时间最多的环节占了整个项目周期的四成以上。这个比例不算夸张因为电动车违规场景有两个天然短板一是负样本稀疏二是标注标准不统一。2.1 先解决“有没有”负样本造不出来怎么办一个路口的电动车流量虽然大但违规行为也就占百分之几。不戴头盔、逆行、闯红灯这些负样本采集一周下来可能也就几百条有效片段。训练深度学习模型几百张正样本完全不够看。我们当时做了两件事来扩充数据。第一件是跨路段采集把周边路口和街区路段的监控数据都拿过来场景多样化之后样本量勉强到了三千条。第二件就是合成数据这个方法在工业视觉里常用在交通场景里也完全可以迁移。合成数据的做法不复杂把电动车从真实监控画面里抠出来做成带透明通道的贴图再通过随机旋转、缩放、亮度扰动、加高斯噪声等方式贴到新的背景画面上。背景可以来自真实的道路监控也可以是随机生成的简单街道场景。贴上之后标签是自动生成的框的位置、类别、属性都是已知的省去了人工标注的麻烦。当然合成数据不能无脑用关键是在“真实感”和“多样性”之间找平衡。如果只贴十张图反复变模型很快就过拟合到贴图上。我们最终合成了一万多张图每张图里随机贴1到10辆车背景、遮挡、光照都随机变化。这一批数据对模型早期收敛帮助特别大但最终精度还是靠真实数据拉上来的。2.2 一个更重要的操作不把“违规”当类别而是当属性我在最初的项目方案里犯过一个设计错误把“违规电动车”直接当成一个检测类别来做给标注人员提供“电动车违规”和“电动车不违规”两个框选选项。结果标注质量很差同一个目标两个人能给出两种完全不同的判断。后来我们把类别体系改成了“属性”模式。检测模型只负责识别电动车这一个类别然后给每个电动车框额外打上几个属性标签头盔戴没戴、后座有没有载人、车身颜色、行驶方向。判断“违规”是后处理阶段的事规则引擎根据属性组合去推断头盔属性为“未戴”电动车类别就是违规。这个改动的优势有两个。一是标注一致性大幅提升标注人员只需要判断视觉上能不能看到的东西不需要做主观的法律判断。二是模型训练更稳定因为属性分类比违规判断更简单、更容易学。后面我把这个类别体系整理成了表格层级内容标注方式模型输出目标检测电动车、自行车、摩托车边界框类别坐标属性分类头盔状态戴/未戴/看不清框内分类属性概率属性分类载人状态无人/一人/多人框内分类属性概率属性分类运动方向左/右/上/下框内分类属性概率后处理是否违规规则引擎违规类型置信度2.3 半自动预标注流程人工只修正不画框标注成本是另一个容易被低估的坑。两万张图每张图里十几辆车全人工画框的工作量算下来是两个人干三周根本排不开工期。我们用的办法是半自动预标注。先用一个弱一点的模型YOLOv8的预训练权重直接推理跑一遍全部素材把识别出来的电动车框自动生成。然后标注人员只需要做两件事删掉错误的框、修正偏移的框。属性标签做成快捷按钮键盘一次性点选效率比从零画框快了三倍多。这个流程跑通之后我又加了一个校验环节让两个标注人员各标一批数据我随机抽1%算一致率。一致率低于90%的类别说明标注标准有歧义需要重新定义而不是继续往下标。这个方法帮我提前发现了一个问题头盔属性里“看不清”这一类当时标得特别乱因为夜间和雨天的画面确实很难判断后来我们专门针对这两类场景单独训练了分类分支。3. 多特征融合架构检测、分割、时序三条支路并行整个系统的模型思路可以用一句话概括与其让一个网络把所有事情都干完不如把任务拆开让不同的支路负责不同的特征最后在一个融合层汇总判断。这就是我们内部叫YOLOv26的由来它不是一个全新的网络结构而是一个把多个支路整合到统一推理框架里的组合方案。3.1 检测支路为小目标调整的相机视角模型检测这部分用的还是YOLO系的主干加检测头但做了几处关键调整。第一处是输入分辨率从常规的640x640提高到了960x960。分辨率提上去之后小目标的特征保留效果好很多代价是推理速度下降但我们部署的是GPU服务器所以能接受。第二处是候选框的设置。监控画面的视角和自然图像完全不同电动车在画面里的宽高比大致在0.4到0.8之间和COCO里默认的锚框比例不太匹配。我们用聚类算法对自己标注的电动车框重新做了统计设置了四组新的先验框尺寸专门适配俯拍视角下的非机动车目标。第三处是特征金字塔的浅层支路。YOLO系列的PANet已经在多尺度特征融合上做得不错但对于40像素左右的小目标我们额外加了一条更高分辨率的特征图支路让检测头能接触到更细节的纹理信息。这一处改动让电动车检测的召回率涨了接近3个点代价是计算量增加了一些。3.2 场景分割支路给模型一张“地图”单靠检测框模型看不到车道线和路口边界在哪儿。逆行判断、闯红灯判断都依赖“车在哪儿”和“应该往哪走”这两条信息的比对。所以我们在架构里加了一条分割支路负责输出车道线、停止线、人行横道和路口区域这几个类别的分割掩码。分割支路的设计有点讲究。它不是独立训练再融合而是和检测支路共享主干网络在浅层特征上单独分出一路卷积层做分割输出。这样训练时两个任务互相影响分割任务学习到的边缘特征也能辅助检测任务框得更准实测下来互相都有提升。推理时分割掩码会和检测框一起送到融合模块。我们设计了一个简单的规则对齐逻辑检测框的底边中心点投到分割掩码上如果落在“非机动车道”区域之外就标记为疑似占用机动车道。停止线同理如果电动车整体越过了停止线且红绿灯状态为红灯就标记为疑似闯红灯。3.3 时序特征模块用短时轨迹识别逆行逆行判断最依赖时间信息单帧图像里一辆逆行电动车可能看起来和正常行驶没有任何区别。我们用了一个非常轻量的时序模块对同一个检测框进行跨帧跟踪把连续20帧的检测框中心坐标串成一条轨迹线再和车道方向比对。轨迹线怎么和车道方向比对先计算轨迹线的主方向向量再计算该电动车所在车道上允许行驶的方向向量两者夹角超过60度就判定为疑似逆行。这个方法比端到端的视频行为识别网络轻量得多而且可解释性好——每一个判断都能追溯回具体的轨迹线方便后续人工复核。完整的三条支路组合起来整个前向推理流程是这样的输入连续帧图像检测支路输出电动车框和属性分割支路输出车道类掩码时序模块输出轨迹方向最后融合模块根据规则输出违规类型和置信度。这个设计的好处是每一路的职责单一模型训练和调优都更容易定位问题。4. 训练调参mAP从78到87的关键操作架构搭好之后训练过程也是踩了一路的坑。我把关键的经验整理出来有些是常规文档里不太会写的细节。4.1 多任务损失权重谁难学就多给它一点我们要同时优化检测损失、分割损失、属性分类损失三个目标三个损失的尺度完全不在一个量级直接加权求和很容易让大尺度的分割损失压过检测损失。我最初用等比损失去训练前20轮还好后面出现了一个奇怪的现象检测mAP停滞分割mAP一路涨说明模型把所有学习能力都花到了任务上来。这对我是一个教训。后来改用了梯度归一化的思路计算每个任务回传梯度的范数根据范数动态调整权重让三个任务的梯度保持在一个数量级上。具体公式不在这篇里展开了核心思想是“让模型在每个任务上都学得动”。这一改之后检测mAP和属性分类准确率同步上升三轮训练后涨了5个点左右。再往后我在梯度归一化的基础上做了微调。因为属性分类是相对容易的任务我手动给它加了0.4的降权系数把更多的容量留给检测和分割。最终多任务损失的权重是检测1.0、分割0.8、属性0.6这个组合在验证集上的表现最稳定。4.2 类别不均衡负样本稀疏问题在前置训练阶段也有后患对象层级的不均衡好解决合成数据把正样本数量拉上来了。属性层级的不均衡才麻烦戴头盔的属性占比85%不戴头盔只有15%模型往“戴头盔”方向偏移几乎是天然趋势。我试过两种处理手段。第一种是Focal Loss给难分类样本更高的损失权重但这个场景下效果不明显因为难分类的样本往往来自夜间或雨天的极端光线损失再高模型也学不动特征。第二种是两阶段训练先用均衡采样的数据把模型训到收敛再用原始分布的数据微调。这样既保留了小类别学到的特征又让模型贴合真实场景的数据分布这个方案实测下来比单一Focal Loss好不少。4.3 训练配置清单与一个容易忽视的坑给大家一份我们最终使用的训练配置做个参考配置项数值说明输入分辨率960x960小目标关键预训练权重COCO预训练迁移学习基础优化器SGD momentum0.937比Adam收敛更稳初始学习率0.01前10轮warmup批大小16单卡A100训练轮数150第120轮开始衰减多任务权重1.0 / 0.8 / 0.6检测/分割/属性数据增强Mosaic CopyPaste配合属性标注使用有一个坑特别想提醒Mosaic增强和属性标签是冲突的。Mosaic会把四张图拼在一起电动车框也跟着被切割拼接这导致框内的属性特征被打乱属性分类准确率掉得很厉害。我们后来只能在Mosaic增强时保持属性标签的同步映射但效果依然不好最终放弃了Mosaic改用CopyPaste增强。这是一个文档不会告诉你的细节自己踩过才知道。5. 部署实测与迭代闭环误报少了三分之二模型训练完只是第一步真正考验人的是部署之后在真实环境里跑出来的效果。试验段部署了6个路口的监控点位连续跑了45天每天没有人工干预的情况下处理大约8000张抓拍图。5.1 实测指标精度和速度的取舍最核心的实测结果我先放在表里指标初版模型最终模型电动车检测mAP78.4%87.6%头盔属性准确率72.1%89.3%载人属性准确率81.5%91.2%逆行判定准确率65.2%84.7%单帧推理耗时38ms52ms每百次抓拍误报数47次14次速度慢了是因为输入分辨率提高到960加上分割支路的额外计算但52ms换算下来是每秒约19帧完全够用于路口视频流分析。部署环境用的是单张专业加速卡没有做TensorRT加速就已经达到了实时要求。5.2 误报案例分析把每个误报当成一次免费标注机会迭代过程中最有价值的行为是把所有误报都攒下来做复盘。45天里最有代表性的误报案例有三个我各举一个说明。第一个是雨天反光误报。路面湿滑时电动车倒影出现在路面上看起来就像一辆电动车骑进了机动车道。检测模块把倒影当成车逆行的判断也跟着出错。解决办法是在融合模块里加了一条影子检测逻辑如果目标的底边中心点落在某个已知目标的附近且面积和外观高度相似就标记为影子候选项。本质上是利用分割支路输出的区域边缘信息做了几何校验。第二个是夜间相机过曝。低照度环境下电动车灯和路灯反光会把局部画面打成一片白检测框在亮斑上产生了大量假阳性。我们给夜间图像专门做了一遍对比度增强然后在融合阶段对低置信度检测框增加了联动抑制逻辑同一区域出现多个置信度相近的框时都降级处理。第三个是公交车遮挡连锁误报。大车经过时短暂遮挡了电动车恢复后跟踪ID发生跳变时序模块把不同车的轨迹接在一起出现了一条“穿越路口再掉头”的假轨迹被判定为逆行。这个问题的本质是跟踪模块的ID切换我们后来在融合模块里增加了一个轨迹连续性校验如果轨迹中出现突然的大角度转折需要额外置信度证据支持才能判违规。每个误报case复盘之后我们把对应的难例加入了训练集重新训练了一版模型。这就是一个完整的闭环上线、抓误报、分析根因、标难例、重训、再上线。三轮迭代下来误报率从47次/百次降到了14次/百次这个数字基本达到试点要求。6. 路线总结多特征融合不是炫技而是场景逼出来的必然选择现在回头看这个项目最深的体会是不要上来就想着换个多牛的网络结构先搞清楚问题的本质。电动车违规识别的本质决定了它必须依赖时空信息和场景上下文因此多特征融合不是可选项而是必需项。我建议后来者先认真算一笔账你的场景里目标尺寸多大违规行为靠单帧能不能判断如果靠单帧能判断那普通检测模型加属性分类就够了如果靠单帧判断不了再加时序特征和分割特征也不迟。想清楚这个问题比选模型重要得多。最后分享两个小技巧。第一合成数据是非常被低估的工具尤其适合小目标密集场景因为它能精确控制目标数量、位置和遮挡关系这是真实数据很难做到的。第二属性分类和违规判定一定要拆成两层哪怕最终效果一样这个分层对调试和迭代的友好度也是天壤之别。以上都是这个项目里真实有效的操作习惯希望对正在做同类系统的人有帮助。