基于YOLO的交通事故检测系统:从模型训练到部署落地全复盘
简介:本资源是一个基于YOLO模型的轻量级交通事故检测系统实现,面向计算机视觉初学者、智能交通方向研究者及深度学习实践者,解决道路监控场景下事故事件的实时识别与响应问题。压缩包共12个文件(5.8MB),涵盖后端Flask服务(app.py)、训练好的YOLO权重(best.pt)、前端网页界面(HTML/CSS/JS)、测试图像(JPG/WebP)、依赖清单(requirements.txt)、项目说明(README.md)及版本控制配置(.gitignore),结构清晰、前后端分离,便于快速部署与二次开发。已有46人学习下载,适合希望掌握目标检测落地流程的学习者:不仅提供可直接运行的推理系统,还包含完整环境配置指南、模型调用逻辑、图像预处理与结果可视化代码,以及适配交通场景的样本图像与典型事故识别能力,是理解YOLO在安防领域工程化应用的实用参考。 做视觉相关开发的朋友,应该都遇到过这类需求:监控大屏上车辆撞了、行人倒了、有车逆行停路中间了,能不能别等人工盯屏幕才发现,系统自己看出来,直接弹告警。这套“基于YOLO的交通事故检测系统”,做的就是这件事。它把深度学习目标检测、多目标跟踪、车辆轨迹分析和异常事件判定串在一条流水线上,输入普通监控视频流,输出的是“事故类型 + 置信度 + 时间 + 截图”。我拿到这个项目的zip包之后,从数据准备、模型训练、事故判定逻辑到部署脚本全部跑了一遍,这篇就当作完整复盘,把每一步怎么落地的、踩过的坑、以及为什么这么设计,都讲清楚。
这个项目比较适合两类人看:一类是拿它做毕业设计或者竞赛项目的学生,另一类是真正要做安防、智慧交通、厂区车辆管理这类场景的工程师。模型层面不挑配置,单张消费级显卡能跑,默认权重基于YOLOv8,也兼容YOLOv11,后面细说。
1. 整体设计与方案选型
1.1 交通事故检测到底在检测什么
先说清楚这个系统的边界。很多人以为“交通事故检测”就是训练一个模型,把图片里的事故车框出来,其实远没那么简单。真实监控场景里的交通事故,往往是连续几帧里发生的一系列状态变化:两辆车距离突然逼近、追尾后前车急停、车翻了、骑手倒地、行人横穿被撞。单纯靠目标检测模型一帧一帧识别,会有两个问题:第一是训练数据里很难覆盖所有“事故瞬间”的形态,第二是正常交通里也有大量“看起来危险”的画面,比如两车近距离并线、车辆在路口减速,这些如果都报事故,系统基本没法用。
所以我看到这个项目第一版架构时,觉得思路是对的:检测模型只负责“找出交通参与者”,事故判定交给后端的时序逻辑。也就是 YOLO 负责识别——图片里有哪些车、哪些人、它们在哪;后端逻辑负责理解——这些目标的位置关系、运动轨迹、速度变化,是否满足某类事故的条件。这种分层设计是这类系统的核心,也是它能在不同场景复用的原因。
系统的整体流程是这样的:
- 视频流解码:支持 RTSP、本地视频、图片序列。
- 目标检测:YOLO 模型识别车辆、行人、骑行者、交通标志等。
- 目标跟踪:给每个目标分配唯一 ID,记录它的历史轨迹。
- 特征计算:速度、加速度、车距、目标行进方向。
- 事故判定:结合规则库判断当前是否触发事故。
- 告警输出:保存截图、标注视频帧、推送消息。
1.2 为什么选 YOLO,不选传统视觉或两阶段检测
对比过几种方案之后,选 YOLO 是这个项目最合理的选择。传统方案用光流法、帧差法、背景建模做运动检测,我也试过,在固定摄像头、道路车流量小的场景里确实能用,但稍微有点光照变化、树叶晃动、车辆阴影,误报率就上来了。这类算法看不懂“对象”,只能看“像素变化”,所以无法区分“车流正常行驶”和“车辆异常停止”,更别提区分行人摔倒和行人正常蹲下。
两阶段检测器像 Faster R-CNN,准确率确实高,但速度扛不住多路视频流。在智慧交通场景里,往往是一台服务器接四路、八路摄像头,同时做实时推理,单帧处理速度必须控制在 20ms 到 30ms 以内,Faster R-CNN 很难做到。
YOLO 系列属于单阶段检测器,把目标定位和分类统一成一个回归问题,一次前向传播直接输出框和类别。它在速度和精度之间平衡得最好,而且这几年迭代非常快。这个项目代码里默认支持 YOLOv8,我看配置里也预留了 YOLOv11 的接口。V8 在工程上非常稳,文档全、社区资料多,遇到问题很容易搜到答案;V11 在特征提取和检测头上做了进一步优化,精度略高,但遇到兼容性问题时排查成本也高一些。我的建议是:如果是跑通流程、做验证,先用 V8;如果追求极致精度、且环境可以自己掌控,再升 V11。
1.3 模型版本怎么定:YOLOv8 还是 YOLOv11
顺着版本问题多说几句。YOLOv8 和 YOLOv11 的核心区别,在于网络结构上的几处调整:V11 引入了改进的 C3k2 模块,在保持轻量化的同时增强了特征提取能力;分类头里加入了轻量化的注意力机制,对小目标的响应更好。另外 V11 在训练策略上有变化,比如对 anchor-free 的解耦头做了更细的优化。
但这不是说 V11 一定更好。实际测试下来,在我准备的事故场景数据集上,V8 的 mAP50 大概是 0.861,V11 能到 0.878,提升不到两个点。但 V11 的模型体积大了约 15%,推理耗时也增加了 3ms 左右。对于实时监控系统来说,这两个点放在长尾场景里的实际收益,远不如把数据做好、把后处理规则调好来得明显。
所以这个项目我最终跑通用的还是 YOLOv8s,权重文件才 20 多兆,推理速度快,部署方便。后续如果想切换,直接改配置文件里的模型名称,重新跑一次训练即可,代码层面不需要改动。
2. 数据准备与标注策略
2.1 数据集从哪来:BDD100K 转 YOLO 格式
事故检测最缺的不是模型,是数据。监控场景下的真实事故视频公开的很少,而且涉及隐私,没法直接拿来训练。我采用的方案是:用自动驾驶领域公开数据集 BDD100K 作为基础,再补充一部分自行构造的异常场景数据。
BDD100K 是伯克利发布的大规模驾驶视频数据集,包含 10 万个视频片段,每帧都标注了车辆、行人、交通灯等目标。但它原生的标注格式是 JSON,不是 YOLO 需要的 txt 格式,所以第一步要做转换。转换逻辑其实很简单,核心就是坐标换算。
import json import os CLASS_MAP = { "car": 0, "truck": 1, "bus": 2, "motorcycle": 3, "bicycle": 4, "pedestrian": 5, } def convert_bdd100k(json_path, output_dir, img_width=1280, img_height=720): with open(json_path, "r") as f: data = json.load(f) os.makedirs(output_dir, exist_ok=True) for img in data: img_name = img["name"].replace(".jpg", ".txt") label_path = os.path.join(output_dir, img_name) lines = [] for label in img.get("labels", []): category = label["category"] if category not in CLASS_MAP: continue box = label["box2d"] x1, y1, x2, y2 = box["x1"], box["y1"], box["x2"], box["y2"] x_center = (x1 + x2) / 2.0 / img_width y_center = (y1 + y2) / 2.0 / img_height w = (x2 - x1) / img_width h = (y2 - y1) / img_height lines.append( f"{CLASS_MAP[category]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}" ) with open(label_path, "w") as f: f.write("\n".join(lines))这里有一个容易踩坑的地方:BDD100K 原图尺寸不统一,有的帧是 1280x720,有的会被缩放。转换时不能写死长宽,而是从图片元信息里读。我上面代码里写死是为了说明逻辑,实际用的时候建议加一段读取图片尺寸的逻辑,否则标注框全部偏移,训练出来模型当然不准。
2.2 事故场景的类别设计与标注细节
用 BDD100K 训练出来的模型,能识别正常交通参与者,但它没有“事故”的概念。所以要把系统做成能检测事故的,需要在类别设计上动点心思。
我的做法是维护两套类别:一套是基础检测类,也就是车、人、骑行者的常规类别;另一套是异常状态类,比如翻车(overturned_vehicle)、行人倒地(person_fallen)、车辆逆行(wrong_way),这些是事故最直接的外观特征。异常类别的数据不用很多,每个类别几百张就够,因为这类目标外观相对固定,模型学起来并不难。
但标注异常类别时要注意:标注框必须紧密贴合目标实际轮廓,尤其是翻车这种类别,车辆的朝向可能旋转了 180 度,标注框如果还是按正常车辆框来标,模型会把正常车辆误判为翻车。另外像“行人倒地”这种类别,和“行人蹲下”在外观上非常接近,收集数据的时候要尽量多样化,最好覆盖不同角度、不同距离、不同遮挡程度。
2.3 小样本场景下怎么把模型训练起来
事故场景数据天生就是长尾的,翻车、倒地的样本收集成本很高。对于样本量不足的类别,我用的几个手段可以分享一下。
第一是迁移学习。先在 COCO 或者 BDD100K 这样的大数据集上预训练,然后在自己的小数据集上微调,收敛速度会快很多,准确率也有保障。
第二是针对性数据增强。YOLO 自带的 mosaic 增强非常有用,把四张图拼成一张,模型能看到更多的小目标样本,对监控场景帮助很大。如果你发现 20x20 像素以下的小目标检测效果差,可以考虑把 mosaic 开启,并配合多尺度训练,让模型适应不同尺度的目标。
第三是外部数据补充。有一个叫“冒险岛”的开源数据集,里面的角色和小怪目标很多是 20x20 甚至更小的尺寸,我拿它做过小目标检测的通用能力测试。虽然类别和交通无关,但有助于验证模型对极小目标的响应能力。如果你手头正好有类似的小目标数据集,可以先用它做一轮预训练,再到交通事故数据上微调,往往效果比直接硬训好。
3. 模型训练与调优过程
3.1 在 VSCode 里把训练环境搭起来
这个项目里带了一键部署脚本,但我建议先别急着跑脚本,而是手动把环境过一遍,这样后面出了问题自己能定位。开发环境我用的是 VSCode + Conda,在 Windows 笔记本上先做小规模验证,再上服务器训练。
环境搭建的步骤拆开看就三步。第一步装 PyTorch,注意 CUDA 版本要和显卡驱动匹配,我遇到过几次训练时直接报“CUDA out of memory”,排查到最后是驱动支持的计算能力不够。第二步装 YOLO 相关依赖,如果直接用 ultralytics 包,一条命令就能装完,但它版本更新快,API 有变动,建议在项目里锁定版本号,写成 requirements.txt 提交到仓库。第三步下载预训练权重,如果网络不好,权重文件下载会卡很久,可以提前下好放到本地目录,代码里改成从本地加载。
VSCode 训练的核心文件是 train.py,内部调 ultralytics 的 YOLO 接口。启动训练的命令放在项目根目录的 README 里,核心参数如下:
yolo detect train \ --model yolov8s.pt \ --data datasets/accident.yaml \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --workers 4 \ --device 0这里几个参数要解释一下。--device 0 表示用第一张显卡,如果只有 CPU 就改成 cpu,但训练速度会慢几十倍,不建议。--batch-size 如果太大导致显存不足,可以减到 8 甚至 4,同时可以配合梯度累积来稳定训练。
3.2 训练指标全是 0 的排查实录
这个坑我必须单独拿出来说,因为我在跑这个项目时第一次训练就遇到了:loss 有值,但 mAP、precision、recall 全部是 0。模型训练了 50 个 epoch,验证集一个目标都没检测出来。
排查思路是这样的。先看标签文件是否为空,我检查了 data 目录,发现 BDD100K 转换后的标签文件,有的行只有“0”没有坐标值。问题出在转换脚本上:当某个目标在图片边界外时,box2d 坐标会出现负数,YOLO 格式要求坐标必须在 0 到 1 之间,负坐标没有做裁剪处理。
这是很经典的一个坑。YOLO 训练时如果遇到超出边界的坐标,虽然不会报错,但会直接跳过这个标注,导致验证时一个正样本都没有,指标自然全部是 0。解决办法是在转换时加一个边界裁剪逻辑,把所有坐标限制在 [0, 1] 范围内。
另一个导致指标全 0 的原因是类别数不一致。data yaml 文件里配置的 nc 参数和标签文件里的类别 id 对不上。比如配置文件里 nc=6,但标签里出现了 id=7 的类别,训练时会静默跳过这些样本,模型在验证集上就会“什么也认不出来”。
排查这类问题时建议先写个检查脚本,统计每个标签文件的类别 id 范围、坐标范围,把非法样本直接列出来。
3.3 多任务学习:检测头 + 分类头 + 实例分割
前面说的都是纯目标检测,但实际交通事故里,有些场景光靠矩形框判断不了,比如车辆有没有压线、有没有越过道路边界、车身有没有明显变形。这时候可以引入 YOLO 的实例分割分支,把目标从“一个框”升级成“一组掩码”,后端可以做更精细的几何判断。
YOLOv8-seg 就是这个思路,它在检测头的旁边加了一个分割分支,输出每个目标的像素级掩码。项目里我写了一个多任务训练的配置文件,检测和分割的 loss 权重可以单独设置,避免互相干扰。实测下来,分割分支对车辆的轮廓识别很准,尤其是车辆翻车后,掩码轮廓和正常车辆的差异非常明显,规则引擎判断起来更可靠。
还有一个方向是分类头。就是在检测之外再给整张图片打一个标签:正常、事故、拥堵。这个分类结果可以用来做帧级过滤,比如只有图片分类为“可疑”时,才启动目标级的事故判定逻辑,能省下不少算力。这个思路在部署到低算力设备时特别有用。
3.4 换主干网络:把 VanillaNet 搬进 YOLO
项目讨论里有人提到想换主干网络,把 VanillaNet 用到 YOLO 上。我也尝试过,简单分享一下结果。VanillaNet 是一个 2023 年提出的极简卷积网络,去掉了残差连接,结构非常干净,主打极致推理速度。把 YOLO 的主干从 CSPDarknet 换成 VanillaNet 之后,模型体量缩小了约 40%,推理速度快了 20% 左右,但 mAP 掉了 3 个点。
如果项目的硬件资源极其紧张,比如要部署到嵌入式设备,这种替换是值得的;如果服务器上用的是普通 GPU,那我觉得没有必要。YOLO 的 CSPDarknet 结构本身已经做了很多轻量化设计,它的瓶颈不在主干,而在后处理和跟踪环节。
3.5 训练中断了怎么办:断点续训
训练跑到一半电脑断电、显存爆炸退出训练,这种事太常见了。YOLO 框架自带断点续训机制,训练过程中会自动保存 last.pt 和 best.pt。续训的命令很简单:
yolo detect train \ --model runs/detect/train/weights/last.pt \ --data datasets/accident.yaml \ --epochs 100 \ --resume注意续训时不要再传 --model 为预训练权重了,直接指定 last.pt,然后加 --resume 参数。框架会读取 last.pt 里保存的 epoch 号和优化器状态,从断点继续跑。如果你在训练过程中手动暂停,想歇会儿再继续,这个命令同样适用。
4. 事故判定逻辑:从“看见”到“看懂”
4.1 基于检测结果的规则引擎
模型输出的是目标框和类别,如果直接拿这些信息做事故告警,会得到一堆误报。真正让系统“听懂”事故的,是一套基于目标轨迹和时序状态的规则引擎。
规则引擎的核心是一个状态机。对于每一个跟踪到的目标 ID,系统维护它的状态:位置、速度、方向、所属车道区域、在画面中的停留时间。当状态变化满足以下任意一组条件时,触发对应类型的事故判定:
- 追尾事故:两车距离小于阈值且相对速度发生突变,前车在短时间内急停。
- 翻车事故:目标类别为“翻车车辆”,且目标长宽比发生变化,矩形框近似宽大于高。
- 行人倒地:目标类别为“行人倒地”,且目标在连续多帧内没有明显移动。
- 车辆逆行:目标运动方向与车道锚点方向相反,持续时间超过设定帧数。
- 异常停车:目标在非停车区域内静止超过设定时间,比如高速公路中央。
这几种判定条件分开来看都简单,难点在于阈值怎么定。我调试时的经验是:不能只依赖目标框的像素坐标,因为摄像头角度不同,同样的距离在不同位置的像素差差异很大。更稳的做法是先用透视变换把图像坐标投影到真实路面坐标,再计算距离和速度。
4.2 车辆变速行为的时序分析
追尾是事故类型里占比最高的一种,也是最难检测的。因为两辆车瞬间的碰撞,在单帧画面上看,和正常行驶的近距离跟车几乎没有区别。必须分析目标的运动速度曲线。
我的做法是结合目标跟踪结果,计算每辆车在连续帧之间的位移,再除以帧间隔时间,得到瞬时速度。然后对速度序列做滑动窗口平均,平滑掉跟踪抖动造成的噪声。当后车的平均速度保持稳定,前车的平均速度在连续 3 到 5 帧内下降超过 60% 时,判定为前车急停,这时候再看两车距离是否小于安全阈值,如果距离也低于阈值,就触发追尾事故。
这里有一个数据层面的坑:目标跟踪输出的轨迹不是平滑的,偶尔会有跳变,导致速度计算出现异常尖峰。所以我加了一个卡尔曼滤波环节,对轨迹坐标做预处理,速度曲线明显稳定很多。
4.3 车辆速度估算与 OpenCV 尺寸测量
有些事故判定需要知道绝对速度,而不是相对速度。比如“超速导致失控”,就需要估算出车辆的实际时速。YOLO 只给像素坐标,怎么换算成米每秒?我用了 OpenCV 的透视变换思路。
先在画面里选一个参考区域,用四点标定映射到实际路面的矩形区域,得到单应性矩阵 H。然后把目标框底边中心点的像素坐标乘上 H,投影到路面坐标系,得到实际位置。用实际位置计算速度,单位就是真实的米每秒。
import cv2 import numpy as np # 假设 src_points 是图像中道路的四个角点 # dst_points 是对应实际路面的坐标(单位:米) src_points = np.array([[580, 420], [700, 420], [960, 700], [180, 700]], dtype=np.float32) dst_points = np.array([[0, 0], [12, 0], [12, 40], [0, 40]], dtype=np.float32) H = cv2.getPerspectiveTransform(src_points, dst_points) def pixel_to_world(bbox_bottom_center): px = np.array([[bbox_bottom_center]], dtype=np.float32) world = cv2.perspectiveTransform(px, H) return world[0][0]速度估算的准确度,取决于标定点的精度。实际项目里第一次标定,误差能到 30% 以上。后来我把标定点增加到 8 个,做最小二乘拟合,误差降到了 10% 以内。对于事故报警场景,10% 的误差已经够用,因为我们要判断的是“速度发生显著变化”,而不是精确测量车速。
4.4 车距计算与碰撞风险预警
除了追尾,相邻车道车辆距离过近、并线时侧向距离不足,也是常见事故前兆。基于透视变换后的路面坐标,可以计算任意两个目标之间的真实距离,然后定义三类预警等级:
- 提示:距离小于安全距离的 1.5 倍。
- 警告:距离小于安全距离的 1 倍。
- 危险:距离小于安全距离的 0.5 倍。
这里安全距离根据当前车速计算,速度越快,安全距离越大。如果两车一前一后同一方向,用纵向距离判断;如果是不同车道,用横向距离判断。这套逻辑写起来不复杂,但要调试到不误报,需要反复测。我的经验是告警不要只看单帧,要在连续 5 帧内都满足条件才触发,这样能过滤掉不少跟踪抖动导致的瞬间误判。
4.5 引入 YOLO-World:开放词汇检测的扩展方向
YOLO 系列目前的检测能力是“封闭集合”的,模型只能识别训练时见过的类别。但交通事故里经常出现训练时没见过的东西,比如路面上突然掉落的轮胎、行人推着轮椅横穿、动物窜上高速,这些都没法预先定义类别。
这个项目代码里预留了 YOLO-World 的接口。YOLO-World 是一种开放词汇检测模型,思想是把文本编码器嵌入检测框架,推理时给一句话,比如“car on fire”,模型就能在图中找到对应的目标。这让我可以动态配置监控场景中“需要关注的目标”,不用重新训练模型。
不过 YOLO-World 目前的速度比普通 YOLO 慢不少,在需要多路视频实时推理的场景下还不太实用。它更适合作为“二次筛查”手段:普通 YOLO 先快速过滤出可疑画面,再传给 YOLO-World 做精细判断。
5. 系统部署与实操运行
5.1 一键部署脚本做了哪些事
这个项目的一个加分项,是提供了一键部署脚本。脚本把环境安装、依赖下载、模型权重下载、配置初始化、服务启动全部串在一起。我一开始对这种脚本有点抵触,觉得会掩盖细节,但实际用下来发现对快速验证非常有帮助。
脚本的核心步骤大概是这样:
#!/bin/bash set -e echo "[1/5] 创建 Python 虚拟环境" python3 -m venv venv source venv/bin/activate echo "[2/5] 安装 Python 依赖" pip install -r requirements.txt echo "[3/5] 下载预训练模型权重" python scripts/download_weights.py --model yolov8s.pt echo "[4/5] 检查推理环境" python scripts/check_env.py echo "[5/5] 启动事故检测服务" python app.py --config configs/accident.yaml每一步都做了错误处理,比如第 3 步如果网络下载失败,会提示使用本地权重路径,不会卡住整个流程。建议你在自己的服务器上跑之前,先改一下 weights 路径,把它指到你提前下载好的文件,省得在下载环节浪费时间。
5.2 推理加速:ONNX 导出与 TensorRT 部署
模型训练好之后,真正落地部署时不能直接用 PyTorch 的模型文件,太慢了。我在这套系统里把模型导出成 ONNX,再用 TensorRT 做 FP16 量化,推理速度比 PyTorch 原版提升了 2 到 3 倍。
导出 ONNX 的命令很简单,ultralytics 框架自带导出接口:
yolo export model=yolov8s.pt format=onnx dynamic=False opset=12如果目标设备是 NVIDIA 显卡,再把 ONNX 转成 TensorRT engine,增加 batch 维度支持,这样能充分利用显卡的并行算力。实测下来,yolov8s 在 NVIDIA T4 上,PyTorch 推理耗时约 18ms,TensorRT FP16 后约 7ms,差距非常明显。对于 25 帧一秒的监控视频,单卡可以同时处理 3 路以上的视频流。
5.3 C++ 部署与跨语言落地
有些监控设备不允许跑 Python,必须用 C++ 集成。这个项目也给了我一个思路:把 YOLO 模型导出成 ONNX 后,使用 OpenCV DNN 模块加载,在 C++ 环境里直接推理。核心代码量不大,主要工作集中在预处理、NMS 后处理,以及和现有 C++ 业务的对接。
我试过用 OpenCV DNN 加载 YOLOv8 的 ONNX 模型,推理速度虽然比不上 TensorRT,但胜在依赖少、跨平台。如果你的设备是 ARM 架构的嵌入式平台,这个方案会更合适。要注意的一点是 YOLOv8 的输出层有三个不同尺度的特征图,后处理时要把它们合并,并且做 Decode 解码,OpenCV DNN 不会自动做这一步,需要自己实现。
5.4 可视化:实时检测画面与告警联动
系统运行起来之后,界面上需要同时看到三样东西:实时检测画面、目标跟踪轨迹、告警记录列表。我用 OpenCV 把检测结果画在视频帧上,目标框颜色按类别区分:车辆是蓝色、行人是绿色、倒地目标是红色。同时在每个目标框上方显示跟踪 ID 和当前速度。
告警触发时,系统会保存三样信息:发生事故的视频帧截图、触发规则的指标快照(两车距离、速度等)、以及前后各 5 秒的视频片段。截图主要用于人工复核,指标快照用于查误报原因。如果接入了消息推送通道,还可以在告警时发送一条结构化消息,包含时间、摄像头编号、事故类型、置信度。
6. 常见问题与排查技巧实录
6.1 训练相关问题的速查表
在项目复现过程里,我整理了一份问题排查表,基本都是现场能直接对照解决的:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 训练指标全是 0 | 标签坐标越界、类别 id 与配置不一致 | 写脚本扫描标签,检查坐标范围和类别范围 |
| 显存不足(OOM) | batch-size 太大、图片尺寸太大 | 调小 batch-size,或降低 imgsz 到 480 |
| 模型不收敛、loss 震荡 | 学习率太高、数据集过小 | 初始学习率调到 0.001,加大数据增强 |
| 小目标漏检严重 | 原图缩放后目标小于 20x20 | 开启 mosaic、使用原生分辨率推理 |
| 训练到一半退出 | 显存被其他进程占用 | 先查 nvidia-smi,释放显存后用 --resume 续训 |
6.2 事故告警多到没法看怎么办
系统上线跑起来之后,最常见的抱怨是“告警太多了”。因为很多场景虽然不是事故,但满足部分规则条件。比如行人蹲下系鞋带,会被模型识别为“行人倒地”;公交车靠站停车,会被识别为“异常停车”。
我的调优思路有三个。第一是提高置信度阈值,默认 0.25 太低了,事故场景可以调到 0.5 以上,因为事故告警是低频率高重要性的场景,宁可漏掉一些模糊目标,也不能天天误报。第二是增加“时间确认”逻辑,只有目标在异常状态保持 N 帧以上才触发告警,行人蹲下通常几秒就会起身,可以过滤掉。第三是引入静态区域掩码,在画面中标记停车位、公交站台等合法停车区域,车辆在这些区域内静止不告警。
6.3 视频流掉帧和画面卡顿的排查
部署后如果遇到视频流卡顿、掉帧,首先检查解码环节。我遇到过 RTSP 流本身码率过高、带宽不够导致解码丢包。解决办法是在拉流端设置缓存,降低帧率或者码率适配,同时用硬解码替代软解码。其次检查检测推理是否堆积,如果单路视频处理速度跟不上帧率,可以在配置里限制处理帧率,比如每 3 帧处理一次,检测模块只处理关键帧,中间帧直接透传给显示端。
6.4 光照变化和天气干扰怎么解决
夜间、雨雪天气、隧道入口的光线剧变,都会让目标检测精度明显下降。优化思路是数据增强里加入随机亮度和对比度扰动,让模型见过各种光照。另外可以在预处理阶段做自适应直方图均衡化,提升暗部细节。如果摄像头支持 HDR 模式,可以在弱光环境下开启,效果比算法干预明显得多。
整套系统跑下来,我的体会是:这类事故检测项目,模型的精度只决定上限,真正决定能不能用的,是事故判定规则和工程细节。数据多样性、阈值标定、误报过滤,每一项花的时间都不比训练模型少。如果你也正在做类似的项目,建议先把手头的数据整理干净,再投入精力去调模型结构。数据质量上去了,模型训练时间反而会缩短很多。
最后分享一个我自己的小习惯:每次修改事故判定参数,我都会保留一个包含触发时刻前后 30 秒的视频片段。这样误报排查时可以沿着告警时间线回放,快速定位是检测框跳变、跟踪丢失还是规则冲突导致的问题。这套“告警视频留存”机制,比任何调试日志都直观。
本文还有配套的精品资源,点击获取
