基于YOLOv5+DeepSORT的驾驶员疲劳与危险行为预警系统实践

发布时间:2026/9/1 10:52:43
基于YOLOv5+DeepSORT的驾驶员疲劳与危险行为预警系统实践 简介本资源是一个面向人工智能与计算机视觉方向学习者、智能驾驶系统开发者的实战项目聚焦驾驶员分心驾驶含疲劳闭眼、低头看手机等危险行为的实时检测与预警问题。项目基于YOLOv5实现高精度行为关键点检测结合DeepSORT完成鲁棒性目标追踪构建端到端可运行的预警系统适用于车载监控、ADAS辅助驾驶等实际场景适合具备Python基础与PyTorch入门经验的中级开发者进阶实践。压缩包共58个文件含20个核心Python脚本如mydetect.py、myfatigue.py、main.py、18个YOLO配置与模型参数yaml文件、13个编译缓存pyc、1个Qt界面ui文件、1个预训练模型best.pt及1个演示视频MP4等结构清晰、模块解耦涵盖数据处理、模型推理、多目标跟踪与GUI交互全流程总大小110.68MB。目前已有510人学习下载配套演示视频与完整工程目录可直接运行调试显著降低算法落地门槛。 拿到这个项目标题我第一反应是这不就是现在车载DMSDriver Monitoring System里最热门的方向之一么。把目标检测和目标追踪串成一条流水线用视觉方式实时判断驾驶员是不是疲劳了、是不是在玩手机、是不是在喝水抽烟——这套东西单看每个算法都不算新但真正要组装成一个“预警系统”而不是一个只能跑demo的脚本中间的坑是相当多的。这个压缩包里的项目说白了就是一个比较完整的工程化实现YOLOv5负责认出画面里的关键目标DeepSORT负责让这些目标在连续帧里保持同一个身份再往上叠一层行为判定逻辑最终输出疲劳和危险行为的报警事件。这篇文章我就按这个系统的真实工作流从算法选型、数据处理、模型训练到行为判定、部署优化和踩坑实录完整拆一遍给正在做毕设、入门深度学习落地或者打算在嵌入式设备上做DMS的朋友一个可以直接参考的路线。1. 项目整体拆解与方案选型逻辑1.1 预警系统到底要解决什么问题先把这个系统的需求边界画清楚。它做的不是自动驾驶那种全场景感知而是把摄像头装在驾驶座前方或者仪表盘附近对着驾驶员的上半身和面部实时判断两类危险状态第一类是疲劳状态。人在疲劳时最典型的表现就是眼皮打架、打哈欠、点头打瞌睡。这几种表现在视频里都是有迹可循的眼睛持续闭合超过一个时间阈值、嘴巴张大保持一段时间、头部频繁低头点头。这类行为的特点是“状态性”而不是“瞬间性”单独看一帧根本判断不了必须看连续几十帧的变化趋势。第二类是危险行为。最常见的是手持电话、喝水、抽烟、操作中控屏。这类行为的特点是有一个明确的“物体”参与比如手机、水杯、香烟、手部检测到这些物体还不够还要判断它们之间的空间关系比如手机是不是在耳朵附近、手和水杯是不是同时出现在嘴巴区域附近。搞清楚需求你就能理解为什么这个项目要选择“检测追踪规则判定”的三层结构而不是简单地训练一个分类模型然后在每帧图片上做分类。单帧分类只能告诉你“这一帧里这个人有没有在打电话”但它无法回答“这个电话是不是同一个人打的”“这次闭眼持续了多久”“这次危险事件和上一次报警是不是同一件事”这类对预警系统至关重要的问题。1.2 为什么是YOLOv5DeepSORT这个组合YOLOv5和DeepSORT的组合在工业界已经是非常成熟的一套搭配了这个项目选它不是因为它最前沿而是因为它在工程落地上的综合成本最低。先看检测端。YOLOv5在精度和速度之间的平衡点做得很好。同样是检测模型基于Transformer的DETR系列精度可能更高但推理速度在边缘设备上拉不上去老一代的Faster R-CNN又太重单帧推理在嵌入式设备上可能要一百多毫秒。YOLOv5s这种轻量版本在Jetson这类设备上配合TensorRT单帧推理可以压到二三十毫秒级别基本能满足实时性要求。而且YOLOv5的训练和部署资料非常多从零开始训练一个自定义数据集的流程也足够清晰这对于车载场景这种需要反复迭代数据、重新训练的场景来说尤其重要。再看追踪端。DeepSORT最大的价值是给每个检测到的目标分配一个稳定的ID。举个实际例子YOLOv5检测到画面里有手机但下一帧手机被手挡住了一部分单帧检测就可能漏检或者误检如果没有追踪ID你的行为判定逻辑会被这种抖动干扰得疯掉。有了DeepSORT即便中间有一两帧检测失败了追踪器也能根据之前的运动轨迹和历史外观特征把同一个目标关联起来保持ID连续。另外DeepSORT的ReID模型会把目标的表观特征编码成一个特征向量后续做“是不是同一个目标”的判断时这个特征向量比单纯的位置信息可靠得多。网上也经常看到有人复现YOLOv8DeepSORT的组合核心追踪部分其实是一样的只是检测头换成了新版本。YOLOv8的精度通常略高一些但如果你要部署到嵌入式平台YOLOv5的成熟参考案例明显更多尤其是在RV1106、RK3568、RK3588这些国产平台上的适配资料YOLOv5依然是最丰富的。这个项目选YOLOv5不是落后而是求稳方向是对的。1.3 从摄像头输入到报警输出的完整数据流整个系统的数据流可以归纳成一条很清晰的流水线摄像头取流 → 抽帧 → YOLOv5目标检测 → DeepSORT目标追踪 → 行为判定模块 → 报警输出摄像头取流这一步在车载场景里要特别注意画质和安装角度光照变化大、逆光、夜间红外补光都会直接影响后面所有环节的效果。YOLOv5拿到一帧图像后会输出一组检测框每个框带一个类别标签和置信度比如“闭眼”“打哈欠的嘴”“手机”“手”“水杯”这些类别具体检测哪些类别是由你的训练集决定的。这组框送到DeepSORT里DeepSORT根据位置信息和外观特征把当前帧的框和历史帧的框做关联输出带唯一ID的追踪结果比如ID为7的“手机”已经连续追踪了120帧。行为判定模块拿到的就是这些带ID的追踪结果。它要做的事情是按照ID分组观察每个目标在一段时间窗口内的状态变化然后套用预设的判定规则。比如ID为3的“闭眼”类目标连续出现了20帧按照视频帧率来算可能已经闭眼接近1秒了这时判定为疲劳预警ID为5的“手机”目标在500毫秒内和ID为8的“手”目标保持高度重叠同时“手”的位置又靠近头部区域这时判定为手持电话事件。最后报警模块根据判定结果驱动声音、灯光弹窗或者通过消息队列推送到后台监控端。这个架构的核心优势是每一层只干一件事检测层不关心目标持续了多久追踪层不关心目标属于什么行为判定层不关心画面里物体的具体坐标计算各层之间的接口就是标准的目标框和ID方便单独替换和优化。实际开发中你甚至可以先只用YOLOv5跑通检测再逐步加入追踪和判定每一步都能验证。2. 核心技术细节与原理落地2.1 YOLOv5目标检测识别“当前帧里有什么”YOLOv5的整体结构可以参考它的名字来理解You Only Look Once意思是你只需要让模型对整张图片“看一次”就能一次性预测出图片里所有目标的位置和类别。它把目标检测任务建模成一个回归问题主干网络CSPDarknet负责提取图像特征PANet结构负责让不同尺度的特征做融合让模型既能检测大目标也能检测小目标最后在输出层给每个候选位置预测出边界框坐标、置信度和类别概率。在驾驶员监控这个场景里用YOLOv5检测什么是一个需要动脑子决定的类别设计问题不是简单地把常见目标类别全都检测一遍就好。我见过很多新手把YOLOv5当万能工具直接拿COCO预训练的80类模型去跑视频然后天真地以为模型输出里有“person”类就能判断驾驶员行为。这种做法的问题是行为判定的核心是“眼睛有没有闭”“嘴巴有没有张”“手里有没有手机”这种细粒度状态COCO模型根本不会检测眼睛和嘴巴状态它对“cell phone”这类小目标的检测能力也一般。所以这个项目里你的检测类别要围绕行为判定来定制。一个比较合理的类别设计思路是眼睛睁/闭两个状态作为两个类别嘴巴正常/打哈欠两个类别或者只检测嘴巴把打哈欠判定放到后续逻辑里再加手、手机、水杯、香烟这类参与危险行为的目标。这样训练出来的检测器输出信息直接能被行为判定逻辑使用中间不需要再套一个额外的分类器。注意疲劳检测里“闭眼”和“睁眼”建议作为两个独立类别因为行为判定时你需要闭眼的连续帧数如果只检测一个“眼睛”类别你还需要额外的模型去判断这个眼睛是睁还是闭反而增加了复杂度。2.2 DeepSORT多目标追踪建立跨帧ID连接DeepSORT的核心任务就是回答一个问题当前帧里的这个目标框和上一帧里的哪个目标框是同一个人或同一个物体它本质上是在做多目标数据关联分为两个阶段。第一阶段是运动信息关联。DeepSORT用卡尔曼滤波器对每个目标的历史运动做一个匀速运动假设预测出它在下一帧可能出现的位置。因为相邻两帧之间的时间间隔非常短大部分情况下目标移动的距离很小这个预测在驾驶员场景下是很准确的尤其是司机头部这种相对固定的目标。然后用匈牙利算法把预测框和实际检测框做最优匹配衡量指标是马氏距离距离越小越可能是同一个目标。第二阶段是外观特征关联。运动预测在目标被遮挡后往往会失效这时候DeepSORT就启用ReID模型把每个检测框对应的图像区域编码成一个特征向量然后计算当前检测特征和已追踪目标历史特征的余弦相似度。有了这层外观匹配即便目标短暂丢帧或者运动突变只要外观没变化太大追踪器也能把它认回来。DeepSORT的级联匹配策略会把外观特征更丰富、更新时间更近的追踪目标放到优先匹配的位置保证ID的优先级合理。使用DeepSORT时几个关键参数需要根据场景调优。在驾驶员监控场景常见参数可以这样参考参数作用实际建议max_dist门控距离阈值决定外观匹配的容忍度默认0.2驾驶员场景目标遮挡频繁可适度放宽到0.3max_iou_distanceIO U匹配阈值决定位置重叠的容忍度默认0.7不要调得太低否则目标稍微移动就丢失IDmax_age目标丢失后允许存活的最大帧数默认70驾驶员场景可降到30左右避免长遮挡后ID错乱n_init目标连续匹配多少帧后才正式确认ID默认3保持不变即可nn_budget缓存的外观特征数量上限默认100目标长时间追踪时特征会积累设太小容易遗忘2.3 疲劳与危险行为怎么用检测加追踪来判定有了YOLOv5的检测结果和DeepSORT的追踪ID接下来就是整个系统的核心价值层——把“有什么目标”转化为“处于什么状态”。疲劳检测方面工程上最成熟也最常用的指标是PERCLOS也就是单位时间内眼睛闭合帧数所占的比例。这个指标源于几十年的驾驶疲劳研究它比单纯数眨眼次数更能反映真实的嗜睡程度。在代码实现上定义一段时间窗口T统计窗口内“闭眼”类别目标出现的帧数N同一ID的同类别目标PERCLOS的数值就是N除以窗口总帧数。实测下来当PERCLOS超过40%时触发预警配合“持续闭眼超过0.8秒”这个条件能把点头瞬间的短暂闭眼和真正的疲劳闭眼区分开。打哈欠判定和闭眼判定有些类似利用“打哈欠的嘴巴”这类目标观察它在一个时间窗口内的持续存在时长如果超过一定阈值比如持续1秒以上判为一次哈欠事件。点头瞌睡判定则需要利用头部目标的垂直位置变化一个人清醒时头部的上下晃动频率和幅度是有限的而瞌睡时的点头往往是快速低头再快速抬起通过追踪头部目标中心点的Y坐标序列计算短时间内比如1到2秒的上下波动幅度和频率就能识别出典型的点头动作。危险行为判定则更依赖空间关系。手持电话最典型特征是手机目标靠近头部、且手部目标和手机目标重叠。具体逻辑可以这样设计当手机ID和手ID的检测框中心点距离小于手框宽度的0.5倍同时手机框的中心点位于头部框的下半部分区域时判定为一次“疑似手持电话”连续多帧都保持这个空间关系才升级为“手持电话事件”。喝水判定逻辑类似水杯/瓶子目标与手部目标重叠且两者一起靠近嘴巴区域。抽烟的判定稍微麻烦一点因为香烟是小目标检测难度大一般的设计是检测“靠近嘴部的手”同时在嘴部附近的小区域内做一个二分类判断是否有香烟。所有行为判定的最后一层都推荐加一个时间窗口状态机疑似状态持续N帧才确认确认状态持续M帧才报警报警之后进入冷却时间冷却时间内不重复触发同类报警。这个机制能大幅降低误报率实测效果非常明显——没有状态机时司机挠一下头都可能被误判为手持电话。3. 完整实操从环境准备到训练部署3.1 环境准备与数据集处理先把开发环境搭起来。YOLOv5是基于PyTorch的Python版本建议3.8到3.10PyTorch按照你的显卡CUDA版本安装就行。网上很多人卡在环境配置环节最常见的坑是PyTorch的CUDA版本和本机显卡驱动不匹配装完检测不到GPU其实判断方法很简单在Python里执行import torch; print(torch.cuda.is_available())输出True就说明GPU环境没问题。如果输出False查一下驱动版本去官网下载对应CUDA版本的PyTorch。Ubuntu 22.04和24.04上配置深度学习环境重点也是先把NVIDIA驱动和CUDA版本理清楚再装PyTorch。数据集方面这个项目如果没有自己的私有数据可以用公开数据集起步。AUC Distracted Driver Dataset和State Farm Distracted Driver Dataset都包含了驾驶员分心行为的图像类别基本覆盖了打电话、喝水、操作中控等场景YawDD和NTHU Drowsy Driver Detection则偏疲劳场景包含打哈欠和闭眼样本。这几个数据集加起来做冷启动足够了。但真实的车载场景和公开数据集还是有差异尤其是摄像头安装角度和光照。建议在项目中期收集自己场景的数据做增量训练把摄像头装在目标位置录几段不同时间、不同天气、不同驾驶员的视频然后抽出关键帧用LabelImg或Labelme标注成YOLO格式。标注时有个经验闭眼和睁眼的边界框要紧贴目标本身不要包含太多背景背景信息多了会让模型学到错误特征比如把车窗外的一团暗色误判成闭眼。所有标注完成后按YOLOv5要求的目录结构整理数据dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml里最重要是两行类别名列表和类别数量类别顺序必须和标注文件里每个txt的类别编号一一对应这个对应关系一旦搞错模型训练出来就是个废品。3.2 训练自己的YOLOv5检测模型训练流程很标准化。下载YOLOv5官方仓库在requirements.txt里列出的依赖装好然后执行训练命令python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --cache几个关键参数单独说一下。--img 640代表输入分辨率检测大目标比如头部、手部用640就够了但如果你要检测香烟这种小目标建议分辨率提到768或者896代价是推理变慢。--batch受显存限制显存不够就把batch调小同时配合梯度累积。--epochs建议至少100小数据集上训练更久容易过拟合所以同时要开--patience早停机制。--cache可以把训练数据预先加载到内存能显著加速训练过程前提是内存够大。YOLOv5会基于你的训练数据自动计算锚框大小这点不用操心。超参数方面新手可以直接用YOLOv5默认配置训练一个基础版本出来作为baseline然后再考虑用--hyp指定修改过的超参文件来调优。实际项目中真正影响模型效果的往往是数据质量和类别设计而不是超参数本身不要在一开始就沉迷调参。训练完成后用val.py验证模型效果重点关注每个类别的mAP和PR曲线。如果某个类别的AP明显低于其他类别比如“闭眼”AP只有0.5而“手机”AP有0.85那大概率是这一类别的样本量不足或者样本多样性不够优先补数据而不是去调算法。3.3 行为判定逻辑与报警模块实现模型训练好之后系统的核心流程就可以串起来了。这里给出行为判定的核心伪代码结构方便理解它的工作逻辑# 伪代码行为判定模块 def judge_behavior(tracks, frame_time): # 按ID聚合追踪目标 for track_id, track in tracks.items(): # 疲劳判定闭眼状态持续时长 if track.type closed_eye: if track.last_seen_time - track.first_seen_time 0.8: trigger_event(fatigue, track_id, frame_time) # 危险行为判定手机与手部空间关系 if track.type phone: hand find_nearby_hand(track, tracks) head find_nearby_head(track, tracks) if hand and head and is_overlapping(track, hand): if is_in_head_lower_region(track, head): trigger_event(handheld_phone, track_id, frame_time)实际开发时上面的每一条规则都要配合时间窗口和冷却机制。比如闭眼判定不是检测到一帧“闭眼”目标就立刻报警而是要求同一个追踪ID的“闭眼”目标在最近0.8秒内持续存在。报警事件触发后进入一个冷却期比如疲劳预警的冷却期是30秒同一个驾驶员在30秒内不会重复触发同类报警避免连续报警对驾驶员造成干扰。报警模块本身的实现比较简单开发阶段直接在画面里画框、显示状态标签同时用旁路打印日志要接实际应用的话可以加一个声音报警线程播放一段急促的提示音同时通过MQTT或者HTTP把报警事件推送到后台监控平台。这里有一个易被忽略的点报警事件要记录当前帧的图像保存到本地或者上传到平台方便事后人工复核。没有这个功能你的算法误报的时候你连分析素材都没有。3.4 边缘设备部署与性能优化如果项目要装到车载设备上而不是只在电脑上跑demo那就必须考虑性能优化。最常用的手段是把YOLOv5模型导出为TensorRT或者ONNX格式利用TensorRT的算子融合和精度校准来加速推理。YOLOv5官方仓库提供了导出脚本python export.py --weights best.pt --include engine --device 0 --half这里的--half是FP16量化通常情况下精度损失很小但速度提升非常明显。实测在Jetson Orin Nano这类设备上YOLOv5s用FP16的TensorRT引擎推理单帧延迟能从纯PyTorch的一百多毫秒降到二三十毫秒勉强满足实时性。硬件选型上如果你用Jetson系列官方对TensorRT支持得最完美省事如果用的是瑞芯微RK3588、RK3568这类平台YOLOv5也经常出现在它们的模型转换工具链支持列表里但需要自己踩一遍RKNNAPI的适配流程。网上有人在RV1106上搭YOLOv5模型这种超低功耗平台的算力非常有限需要将模型剪枝量化成INT8才能跑得动整个流程会比较折腾但对量产成本敏感的场景来说是必要的。还有一个性价比很高的优化点降低检测频率。驾驶员头部动作的变化相对缓慢让主检测循环跑到每秒15帧就足够不用追求满帧率检测把省下来的算力留给其他任务。如果用DeepSORT做追踪追踪本身的算力开销并不大瓶颈还是在检测端。4. 常见问题与排查技巧实录4.1 训练和数据集相关坑先说一个几乎所有人都会踩的坑类别混淆。比如“闭眼”和“睁眼”这两个类别如果标注的时候边界框不准确或者训练数据里两者的样本数量差异极大模型就很容易把闭眼误判成睁眼或者反过来。解决办法是标注时严格按照标准每张图都检查一遍同时确保两个类别的样本数量尽量均衡实在不均衡就在训练时增加欠采样类别的数据增强权重。小目标漏检是另一个高频问题。香烟、手机这类目标在摄像头画面里的像素面积往往很小YOLOv5s这种轻量模型容易漏检。我处理这个问题的方式是将输入分辨率提高到768同时在预处理阶段做一次ROI区域放大把驾驶员头部和手部的区域裁剪放大后再送进检测器相当于用小算力实现了局部细节增强。这么做之后香烟的检测率提升非常明显代价是代码复杂一点。还有一个要注意的是过拟合的识别。训练集上loss降到很低、AP很高但一到实拍视频里就频繁出错这是典型的过拟合表现。光看训练日志很难发现所以一定要单独留出一段完全没有参与训练的真实场景视频做冒烟测试这个做法比任何指标都直观也更能暴露数据分布偏差的问题。4.2 DeepSORT追踪效果相关坑DeepSORT在驾驶员场景里最常见的毛病是ID Switch也就是同一个目标追踪着追踪着ID突然变了。这个现象会导致行为判定逻辑以为出现了一个新目标从而误报警。主要原因通常是目标外观突变比如驾驶员转头导致面部特征变化或者帽子、眼镜的遮挡。别急着嫌弃DeepSORT先检查你的检测质量检测框抖动剧烈会直接导致追踪关联失败把YOLOv5的置信度阈值从默认的0.25提高到0.4左右让追踪器的输入更稳定ID Switch会明显减少。另一个坑是DeepSORT对静止目标的处理。驾驶员在车里坐着头部目标大部分时候几乎不动卡尔曼滤波器对静止目标的预测会非常稳定但一旦目标突然大幅度移动比如司机俯身去捡东西预测框和实际检测框的差距会骤然变大导致匹配失败。解决方式是把max_iou_distance稍微放宽并把max_age调小一些让超过一定时间无法关联的旧轨迹尽快销毁避免旧ID一直占用资源造成后续匹配混乱。4.3 行为判定误报和漏报误报是行为判定模块最头疼的问题。常见误报场景驾驶员用手调整后视镜、摸头发、擦汗这些动作在视觉上都和人手靠近头部类似很容易被误判成手持电话。我常用的缓解手段是加“空间约束时间验证”要求手机目标、手部目标和头部目标同时存在且空间位置满足特定关系其中手机目标在判定中必须有真实检测框仅仅手靠耳朵但没检测到手机就不能判为打电话。这个约束非常有效因为真正的打电话行为几乎一定有手机这个目标出现而摸头发的动作没有手机参与。疲劳误报主要来自光照跳跃和佩戴墨镜。夜间开车时摄像头的红外补光灯打开前和打开后的几帧图像亮度会剧烈变化可能导致眼部检测错误。处理方式是在行为判定模块入口加一个全局亮度检测画面亮度突变时暂时抑制报警输出几百毫秒。戴墨镜的驾驶员特征更麻烦闭眼和睁眼都检测不到建议在系统设计阶段就考虑可以把墨镜检测作为一个辅助类别检测到墨镜后降低疲劳报警的置信度要求或者改用头部姿态下跌趋势作为疲劳提示信号。4.4 调试验证阶段的实用技巧整个系统联调阶段有几个工具和习惯我认为价值很大。第一是可视化调试窗口把YOLOv5的检测框、DeepSORT的ID、行为判定模块的当前状态全部实时绘制在同一帧画面上输出到视频流。虽然听起来很基础但它能让你一眼看出问题是出在检测、追踪还是判定层定位效率提升好几倍。第二是报警日志与同期画面的对齐保存每次报警事件触发时截取事件前五秒到事件后两秒的视频片段用于事后复盘这也是算法迭代的一手素材。如果你在离线视频上调试建议逐步放慢帧率观察先用每一帧都停一下的步进模式看追踪ID是否稳定再正常速度播放测试行为判定是否合理。这个“慢动作-正常-快进”三步调试法帮我解决过很多看起来毫无头绪的问题。还有一个容易被忽略的点是帧率对齐DeepSORT追踪和疲劳时长判定都是基于帧数计数的如果画面实际帧率和你代码里假设的帧率不一致闭眼0.8秒这个阈值会被算错导致疲劳判定时而灵敏时而迟钝所以在视频流入口处务必统一做一次时间戳校准。从我自己的实际经验看这类“检测追踪规则判定”的预警系统真正决定上线效果的是两个环节一是数据是否贴近真实场景二是行为判定规则是否经过充分的样本验证。模型层面的优化空间反而没有想象中大YOLOv5s这类骨干模型在合理调优下已经足够支撑大多数车载应用与其反复折腾模型结构不如把精力花在数据收集和报警策略打磨上。这个项目拿到手之后我的建议也是先跑通官方demo再用自己的场景视频去测试记录所有误报漏报案例然后针对性标注、增量训练、调整判定阈值沿着这个闭环迭代几轮系统的稳定性会肉眼可见地提升。本文还有配套的精品资源点击获取