做违规识别项目这么久,最深的体会是:模型能框出目标,不等于系统能识别违规。“基于YOLOv26 的电动车违规识别系统:多特征融合”这个项目,核心就是把“看见”升级成“理解”。电动车违规很多是动态行为,闯红灯、逆行、占用机动车道,不是一张图上画个框就能断定的;而像不戴头盔、违规载人,虽然单帧能看出来,但受光照、遮挡和角度影响,也不如综合判断来得稳。所以我在这套系统里没有让YOLOv26单打独斗,而是把目标检测结果、轨迹运动特征、信号灯状态和人体关键点全部汇到一个判定流程里,用多特征融合的方式输出违规告警。这篇文章会把这套方案的选型理由、数据设计、实现细节和踩坑记录全部拆开讲,很适合正在做交通场景视觉项目,或者想从纯检测升级到行为分析的同学参考。
1. 项目整体设计与核心思路拆解
1.1 为什么不直接让检测模型输出“违规”
我刚接触这个方向时,也想过直接把“闯红灯”“逆行”“不戴头盔”做成YOLO的类别,让模型一通输出就完事。实际做下去才发现,这条路基本走不通。违规本质上不是一个稳定的视觉类别,而是一个事件。比如“闯红灯”,需要同时知道目标在画面里的位置、信号灯当前是什么颜色、目标在红灯亮起之前有没有越过停止线、它越过停止线之后是不是还在继续前进。这些信息分布在画面的不同区域,还跨了好几个时间帧,如果硬塞进一个单帧检测模型里,模型只能学到一堆伪相关特征:看到红色就报警、看到电动车就报警、甚至看到路口就报警。
更麻烦的是,这类行为的样本极其不平衡。真实路口一天下来的违规次数可能是个位数,而检测模型需要大量“违规”样本才能收敛,光靠人工去采集和标注,成本完全扛不住。所以我从一开始就定了一个原则:YOLOv26只负责感知“画面里有什么”,把所有“是不是违规”的判断全部交给多特征融合的规则引擎。这样既不用逼检测模型学习复杂时空语义,也能把每个误报追溯到具体的特征环节,调试起来非常清晰。
1.2 系统到底要解决什么问题
这个系统面向的是路口、卡口和园区出入口这类固定视角场景,自动识别电动车常见违规行为。我当时把需求收敛成了五类:不戴头盔、违规载人、闯红灯、逆行、占用机动车道。前两类属于静态属性识别,一张图上基本能判断;后三类属于时空事件,必须连续追踪目标的运动轨迹,再叠加场景信息才能判定。
系统最终输出的不是一张画满框的图,而是一条结构化告警,包含时间、点位、违规类型、目标截图、关联短视频片段,还有一条置信度得分。这样即使后端审核人员不看实时视频,也能快速确认。这个定位很重要,它决定了整个系统架构:边缘端做实时检测和轻量融合,中心端做复核、存储和统计。模型不是终点,事件才是终点。
1.3 选型YOLOv26的关键考量
说实话,选模型我不太喜欢追新,但YOLOv26这次确实值得用。我在测试环境里跑下来,最明显的感受是小目标检测能力比前几代提升了不少。路口画面里,一辆远处的电动车可能只占几十个像素,头盔更是小得可怜,以前用旧版模型经常漏检,到了YOLOv26上漏检率好了很多。
另外速度和部署也是硬指标。违规识别要跑实时视频流,边缘设备上的算力有限,不能拿一个太大的检测头在上面慢慢推理。YOLOv26在保持高精度的同时,推理延迟控制得不错,而且它的后处理比较简单,导出ONNX、再走TensorRT加速这套流程很顺。生态成熟也省了不少事,团队里已经有同事熟悉这套工具链,出了问题查资料也快。如果只看论文指标选模型,很容易选出一个跑不动的模型。我对YOLOv26的评价就一句话:它是这个项目当前情况下能同时满足精度、速度和工程化要求的选择。
2. 多特征融合框架:从“看见”到“理解”
2.1 特征矩阵:模型之外还需要哪些信息
做多特征融合之前,我先把“下棋需要的棋子”列了出来,避免想到什么特征就塞什么特征。整套系统用到的信息大致分四类:
| 特征类别 | 具体特征 | 能够支撑的违规判断 |
|---|---|---|
| 目标级特征 | 检测框类别、置信度、位置尺寸、人体关键点、头盔检测结果 | 不戴头盔、违规载人 |
| 轨迹级特征 | 跟踪ID、质心轨迹、速度、方向、加速度 | 逆行、闯红灯 |
| 场景级特征 | 信号灯状态、停止线区域、车道区域、路口范围 | 闯红灯、占用机动车道 |
| 时序级特征 | 目标历史状态、状态转移、持续运动时间 | 区分正常等待与闯红灯 |
这里面有些特征是YOLOv26直接给的,比如框和类别;有些要自己做,比如轨迹和速度;还有一些在视觉之外,比如信号灯状态。我在项目里做了一个很重要的决定:信号灯状态优先从信号机协议获取,而不是靠摄像头识别红绿灯。因为视觉识别红绿灯本身就有误差,如果把这个误差叠加到违规判断里,整个链路的误报率会成倍放大。信号机直接给状态,又稳定又省算力。
2.2 融合不是拼接:三个层次的解耦
多特征融合最忌讳的做法就是把所有特征向量拼在一起,然后丢给一个全连接层去回归。这样做特征失去了物理含义,模型变成黑盒,出了问题根本定位不了。我更倾向于把融合拆成三个层次。
检测级融合负责“单帧目标属性”,比如判断骑车人有没有戴头盔。具体做法是让YOLOv26同时输出骑行者框和头盔框,再计算两者的空间关系,比如头盔框是否基本包含在骑行者头部区域、两者中心点距离是否小于阈值、置信度是否都足够。这个层次不需要跨帧,逻辑清晰。
轨迹级融合负责“动态行为”,先把同一辆车的检测框通过跟踪器串成一条轨迹,然后计算速度、方向、位移量、加速度。比如逆行,只要目标轨迹的运动方向和规定的车道方向夹角超过一定度数,并且持续一段时间,就能给出强信号。
决策级融合把轨迹结果和场景规则组合成最终事件。比如闯红灯等于“当前信号灯为红灯”加“目标越过停止线”加“目标继续向前运动”加“目标在红灯亮之前没有越线”。每个条件都由独立的子模块输出,最后再通过规则引擎合并。这样做的好处是:如果误报了,我能立刻知道是信号灯错了、跟踪断了、还是停止线标歪了,而不会面对一个莫名其妙输出高分的黑盒。
2.3 标签体系与数据集设计的坑
这个项目里最容易被低估的是数据设计。如果只做物体检测,那标签只需要框和类别。但要支撑多特征融合和后续行为判定,光有框远远不够。我在构建数据时,把标签拆成了两层。
第一层是图像级标签,用于训练YOLOv26检测器。类别包括电动车、骑行者、行人、头盔、乘客。这里我没有设计“未戴头盔”作为检测类别,因为未戴头盔本质上是“有骑行者但没有头盔框”的组合逻辑,硬做一个类别会导致正负样本边界模糊。
第二层是片段级标签,用于调规则引擎和验证融合逻辑。标注时需要在连续视频片段里标注事件类型、目标ID、事件起止帧、信号灯状态。这一步很费人力,但必不可少。没有片段级标签,规则阈值就只能靠拍脑袋,后期评估召回率和误报率时完全没有依据。我建议自建数据集时,至少保证每个违规类型有几百条片段,白天、夜晚、逆光、雨天都要覆盖到,否则模型上线以后容易被场景切换打懵。
3. 系统实现细节与关键环节落地
3.1 YOLOv26检测模型训练流程
检测模型是整个系统的地基,必须练扎实。我讲一下我实际采用的训练配置和流程,这套配置在项目中验证过,可以直接作起点参考。
首先是数据准备。从监控视频里抽帧时,不要均匀地每分钟抽几张,而是要根据场景复杂度调整。路口等红绿灯时,画面里车和人基本不动,抽太多帧全是重复;绿灯放行时,一个片段里能看到大量动作,多抽几帧。我一般按“场景切换”来抽帧,同一镜头下采样到每秒2到3帧就好。标注类别按之前设计的:person、ebike、helmet、passenger、car。其中头盔这个类别是最容易标漏的,特别是远处的头盔只有十几个像素,标注时我要求工人把“能看清是人头就尽量标头盔”,宁可多标也不能漏,漏标会直接带歪模型。
训练参数我用的是输入分辨率1280x1280,batch size 16,初始学习率0.01,采用SGD加余弦退火调度,一共训练300个epoch。前50个epoch冻结backbone,只训练检测头,后面再解冻全模型。这里有一个经验:不要一上来就用大学习率,小目标任务很容易发散。YOLOv26建议使用官方预训练权重做热启动,而不是从零训练,能省很多时间并且收敛更稳。
配置文件大概长这样:
train: ./datasets/train.txt val: ./datasets/val.txt nc: 5 names: ['person', 'ebike', 'helmet', 'passenger', 'car']训练完之后,我先看每个类别的AP,而不是只看整体mAP。头盔类别的AP如果低于0.8,我就会回到数据层面查漏检,而不急着调模型。测试时也要用与训练一致的分辨率,不要训练1280、测试640,否则小目标性能会白白流失。
3.2 多特征提取与融合实现
检测模型跑起来以后,接下来是把检测结果变成轨迹特征。我采用的是检测加跟踪的经典方案:每一帧跑YOLOv26,检测框送到跟踪器做数据关联,得到一个带唯一ID的轨迹对象。轨迹对象里维护了最近若干帧的检测框中心点、类别、置信度。
在这个基础上,计算运动特征就得注意坐标统一。画面坐标系里直接用像素算速度没有物理意义,因为近处和远处的目标同样移动一米,像素位移差别很大。我做了一个简单的透视变换,从路面上选取几个参考点,把图像坐标映射到地面平面坐标,然后用地面坐标计算速度和方向。不需要做特别精确的标定,只要能保证“近处目标不超速、远处目标也不漏检”就可以了。
下面是融合判定闯红灯的一个核心逻辑骨架:
def check_red_light_violation(track, signal_log, stop_zone): # 当前信号灯必须是红灯 if signal_log.current_state != RED: return False # 找到目标第一次进入停止线附近的时间 enter_time = track.find_time_entering(stop_zone) if enter_time is None: return False # 如果目标在红灯亮起之前就已经越过停止线,视为正常通过或已在路口内 if enter_time < signal_log.red_start_time: return False # 目标在红灯期间必须持续向前运动,不能是静止等待 if track.average_speed() < SPEED_THRESHOLD: return False if track.motion_direction() != FORWARD: return False return True这只是逻辑骨架,真正实现时还需要处理跟踪ID跳变和信号灯时间戳对齐。但核心思想很明确:模型给证据,规则做判断,特征融合不是把数字塞进一个网络,而是让不同来源的证据互相印证。
3.3 业务规则引擎与二次判定
规则引擎承担最终的事件输出,我把它设计成两段式。第一段是候选触发,当某个轨迹同时满足“目标类别是电动车或骑行者”“在事件区域内停留超过一定时间”“速度方向符合异常特征”时,就产生一个候选事件。这个阶段要放宽阈值,宁可多留候选,也不能漏。第二段是二次确认,候选事件要经过更严格的复核,比如连续多少帧满足条件、多特征置信度的加权得分是否超过阈值。
多特征置信度得分是我比较看重的一个创新点。它不是简单地把模型置信度拿出来用,而是把视觉得分、行为得分和时序得分加权融合。视觉得分来自YOLOv26对目标类别的置信度,行为得分来自轨迹特征和场景规则的匹配程度,时序得分来自事件在连续时间窗口内的一致性。我的初始权重是视觉0.4、行为0.4、时序0.2,实际调优时通过统计误报样本倒推权重,而不是靠感觉。
二次确认最大的用途是过滤“正常等红灯”被误判成“闯红灯”的情况。一辆车在红灯亮起前已经越过停止线,然后停在路口中间等待,这种目标的高速和方向特征都不满足闯红灯条件,会被规则引擎排除。这类逻辑在纯神经网络里很难表达,但在规则引擎里就是几条条件的事。
3.4 部署架构与性能优化
系统部署我分了两层。边缘端用一台装有GPU的工控机,负责拉取摄像头的RTSP流、解码、跑YOLOv26检测、跟踪器以及轻量规则判断。边缘端只上传候选事件和对应证据片段,不上传全部视频,这样对带宽压力小得多。中心端服务器接收候选事件后,会调出信号机日志做复核,并汇总生成告警报表。
性能调优上,我最推荐的一个技巧是“检测加跟踪插值”,不是每一帧都跑YOLOv26。比如我用检测帧率为5FPS,但跟踪器运行在25FPS,中间缺失的检测框由跟踪器根据运动模型插值补出来。这样GPU占用能降到原来的五分之一,而违规识别这类慢动作事件完全不受影响。再加上TensorRT FP16量化,单路1080P视频的检测延迟能控制在100毫秒以内。如果你的边缘设备更弱,可以把输入分辨率降到960,但要注意小目标性能会下降,需要结合场景权衡。
4. 常见问题与排查技巧实录
4.1 小目标漏检:远处的电动车还没一个头盔大
这是所有交通场景项目的通病。画面里靠近镜头的电动车很大,远处路口中央的电动车却只有几十个像素。YOLOv26虽然改进不少,但依然会漏。我试过几种解法,最有效的是训练时把输入分辨率拉高到1280或1536,让模型在特征图上保留更多细节。然后开启多尺度训练,输入尺寸每几个epoch在0.8到1.5倍之间随机变化,提升模型对不同目标尺寸的适应能力。
但分辨率不是越高越好,到1536以上,显存占用和推理延迟会快速增长,而且收益会边际递减。另一个更工程化的解法是分区检测:把画面切成分近景和远景两个区域,近景用大分辨率检测,远景用低分辨率检测,两个结果再合并。这种方式适合固定机位的路口,但对算力要求更高,我只有在边缘设备性能充足时才会开。
4.2 夜晚和逆光场景大面积漏检
路灯不足、车灯过曝、雨天反光,都会让检测器的表现断崖式下跌。只靠YOLOv26单模检测,夜晚的漏检率可能是白天的三倍甚至更高。我遇到过最夸张的一次,夜晚逆光下整辆黑色电动车都融进背景里,模型完全没输出。
解决思路有两个。第一,训练数据里必须加入足够的夜间样本,并且做图像增强模拟不同亮度和对比度,比如随机伽马变换、随机亮度扰动,不能只在白天数据上训练。第二,条件允许时采用双模态输入,融合红外热成像或者低照度增强图像。这个方向是我比较推荐的“真正的多特征融合”,因为红外对光照不敏感,哪怕可见光画面里看不见人,红外画面里也能看到明显的热源轮廓。硬件成本会增加,但换来的夜晚可用性是值得的。
4.3 跟踪ID频繁跳变导致轨迹中断
跟踪ID一旦跳变,一条完整的轨迹会被断成两截,逆行和闯红灯这种需要连续轨迹的逻辑就会失灵。我排查过好几次,最典型的场景是两辆电动车在画面里交错,跟踪器把两个ID换了,或者被行人遮挡几帧之后ID就丢了。
解决办法是给跟踪器加外观重识别特征。简单说,不要只依赖运动模型的IoU匹配,还要把检测框内的视觉特征提取出来,即使运动距离远一点,只要外观相似也能匹配上。同时我会保留一个“轨迹缓存队列”,目标短暂消失后不立即删除ID,允许它在接下来的几帧里重新关联,超过一定时间再清理。这一套组合下来,ID跳变率下降非常明显。
4.4 规则误判:正常等红灯被当成闯红灯
规则引擎最容易翻车的地方就是闯红灯。最初一版误报率很高,排查发现是停止线标定不准确,目标明明停在停止线前面,坐标换算后却显示已经越线。我重新用多个地面参考点做透视变换,并且在停止线区域附近加了缓冲带,只有目标中心点超过缓冲带才算真正越线。
还有一个关键点是红灯时间同步。视觉识别的信号灯状态必然有延迟,如果直接用当前帧的红灯状态去判断目标,可能会落后一两百毫秒。更稳的做法是从信号机直接读取状态,并且记录事件日志,在判定时用目标越线时刻前后的信号状态序列,而不是只看当前帧。这样能大幅减少因为时间戳错位导致的误判。
4.5 常见问题速查表
| 问题表现 | 可能原因 | 解决方案 |
|---|---|---|
| 远处小目标漏检 | 输入分辨率低、小目标样本不足 | 提高输入尺寸、多尺度训练、分区检测 |
| 夜间漏检严重 | 夜间训练样本过少 | 加强夜间样本、图像增强、红外融合 |
| 跟踪ID跳变 | 目标遮挡、外形相似、纯IoU匹配 | 增加重识别特征、轨迹缓存 |
| 正常等待被误判闯红灯 | 停止线标定不准、信号灯时间不对齐 | 透视变换校准、缓冲带、信号机日志对齐 |
| 违规载人不稳定 | 乘客目标太小、遮挡严重 | 提高分辨率载人检测头、增加人体关键点注意力 |
| 告警太多审核不过来 | 规则阈值过低 | 提高二次确认阈值、调整多特征权重 |
5. 一些实际操作体会与后续扩展建议
5.1 如果再重做一次,我会怎么改
这个项目让我最受益的一件事,是学会了先定义“事件”,再定义“模型”。最开始我满脑子都是怎么把mAP刷高,真的上线以后才发现,漏检和误报是一对矛盾,只盯着指标没有意义。如果让我重新做,我会先用一版很粗的规则引擎把整个链路跑通,哪怕所有阈值都是拍脑袋拍的,也要先让系统从“输入视频”到“输出事件”闭合成环。有了这个闭环之后,我再根据真实漏报和误报去调整检测模型和特征权重,而不是在模型训练阶段死磕。
另一个我会早做的是数据闭环。上线后系统产生的误报和漏报样本,要定期拉回来补入训练集。交通场景的天气、光照、车型变化都很大,模型必须持续更新。没有这个回流机制,系统上线越久,性能退化越严重。
5.2 值得一试的扩展方向
这个系统目前做的是单点违规识别,往大了扩展还有很大的空间。比如把多个路口的检测结果汇聚起来,做成区域级的电动车交通行为分析,统计哪些时段、哪些路段违规高发。再比如接入车牌识别,把违规事件和车辆身份关联起来,就可以支撑后续的提醒和教育体系。传感器方面,除了我前面提到的红外热成像,还可以尝试用激光雷达提供目标的精确位置和速度,视觉和点云做深度融合,大幅提升恶劣天气下的可靠性。
另外,规则引擎虽然可解释性好,但维护起来工作量不小。现在很多团队开始尝试用大模型做视觉语言联合推理,直接问模型“这辆电动车是不是闯红灯了”,模型结合画面和文本上下文也能给出答案。不过这种方案的延迟和成本目前还降不下来,更适合离线复核,实时告警路由还是规则引擎更务实。
整套系统做下来,我最深的体会其实是:多特征融合不是技术炫技,而是交通场景本身决定了你必须这么做。单帧检测是“点”,轨迹跟踪是“线”,加上信号灯和道路规则才是“面”。YOLOv26是那把又快又准的“眼睛”,但真正让系统懂规矩的,还是背后的这套特征融合逻辑。如果你也在做类似的违规识别项目,不要急着堆模型换结构,先把你要识别的“事件”拆清楚,再让每个特征去回答一个具体的问题,这条路会顺很多。