
简介本资源是一个面向人工智能与计算机视觉方向学习者、智能驾驶系统开发者及高校科研人员的实战项目聚焦驾驶员疲劳与危险行为如闭眼、低头看手机的实时检测与预警问题。项目基于YOLOv5实现高精度行为识别结合DeepSORT完成鲁棒性目标追踪构建端到端可运行的预警系统适用于车载监控、ADAS辅助驾驶等实际场景。压缩包共58个文件含20个核心Python脚本如mydetect.py、myfatigue.py、main.py、18个YOLO配置yaml文件支持s/m/l/x多模型切换、13个编译缓存pyc文件、1个Qt界面ui文件、1个预训练权重best.pt及1个面部关键点dat模型另有演示视频MP4与Dockerfile等整体大小110.68MB。已有510人学习下载提供完整工程结构、可直接运行的GUI主程序、疲劳检测逻辑封装、YOLOv5DeepSORT集成范例及实操演示视频便于快速复现、二次开发与嵌入式部署验证。 做这个项目的起因我自己跑过车队的安全监控项目坐在监控室盯八个画面看一天眼睛先受不了更别说人脑注意力能撑多久。交通事故里导致重大伤亡的原因往往集中在两类一是疲劳驾驶二是分心行为——打电话、看手机、喝水、抽烟哪个单独拎出来都是血淋淋的事故样本。所以我才把“基于深度学习的驾驶员分心驾驶行为疲劳危险行为预警系统”这套东西认真做了一遍。整套系统选择了YOLOv5做目标检测、DeepSORT做目标跟踪最终输出一个可直接落地的预警工程包。这篇文章就是把这个项目的完整技术链路、训练过程、踩坑记录和交付结构全部摊开来讲适合正在做驾驶员状态监测、ADAS辅助功能、边缘端视觉部署的开发者参考。1. 项目要做的事分心驾驶预警系统的目标拆解1.1 预警系统到底要解决什么先说清楚这套系统不是“把YOLOv5跑通”就完事。它要处理的是连续视频流里的实时行为判断司机有没有闭眼超过两秒、有没有连续打哈欠、有没有长时间低头看手机、有没有单手离开方向盘打电话、有没有点烟抽烟、有没有拿水杯喝水。这些行为有一个共同特征——它们都是“短暂且非静止的”。如果你只是在单帧图片上做检测那稍微一卡顿或者光线变化就会导致漏检、误检。这也是为什么标题里同时出现YOLOv5和DeepSORTYOLOv5负责每一帧里把人、手、手机、水杯、烟等目标找出来DeepSORT负责把这些目标在时间轴上连续绑定让系统知道“这个打电话的人就是刚才那个司机”从而统计行为持续时间判断是不是真的发生了危险动作。1.2 系统功能模块划分从功能上拆这套项目包含四个核心子模块目标检测模块基于YOLOv5检测驾驶舱内的司机、手机、水杯、烟、手部以及人眼和嘴巴状态。目标跟踪模块基于DeepSORT对检测结果做ID分配与轨迹平滑降低单帧误检。行为判定模块通过跟踪得到的轨迹和状态结合疲劳判定规则闭眼时长、打哈欠频率、PERCLOS和分心行为判定规则输出告警事件。告警输出模块输出到本地日志、HTTP接口、串口或语音播报方便接入车队管理平台。这些模块之间是串联关系视频帧进来之后先检测、再跟踪、最后进规则引擎任何一环出问题都会影响最终告警准确率。所以下面每个模块我都会讲实现原理和实际调试中容易忽视的点。2. 为什么是YOLOv5DeepSORT检测与跟踪的技术选型逻辑2.1 目标检测方案的取舍做目标检测可选的方向太多了传统图像处理、Faster R-CNN、SSD、YOLO系列、EfficientDet、DETR、YOLOv8。我最终选了YOLOv5理由很实在一是生态成熟。YOLOv5的代码结构、权重格式、部署工具链ONNX、TensorRT、TFLite都是被社区反复验证过的遇到问题基本都有现成答案。二是速度与精度平衡。驾驶员监控的场景通常是30fps或者更高帧率的摄像头输入推理速度必须跟上。YOLOv5s在RTX 3090上单帧推理只要十几毫秒普通工控机上也跑得动这是双阶段检测器很难做到的。三是自定义类别灵活。驾驶员行为检测不是像COCO那样只分人、车、猫、狗我需要把“手”、“手机”、“水杯”、“烟”、“闭眼”、“张嘴”这些特定小目标单独建模YOLOv5的配置文件和数据集格式非常适合这套逻辑。2.2 为什么必须加DeepSORT而不是只用检测器很多人刚接触这个任务时会有疑问既然YOLOv5已经能检测出司机和手机那直接用检测框叠加判断不就行了问题在于单帧检测结果不连续。实际摄像头画面里司机的手可能因为运动模糊突然检测不到手机可能因为角度变化从侧脸方向消失一两帧。如果只看单帧系统会不断产生“有-无-有”的告警抖动这种抖动在真实监控场景下根本无法用。DeepSORT的作用有两个一是轨迹平滑。它通过卡尔曼滤波预测目标在下一帧的位置并用匈牙利算法把当前帧检测框与已有轨迹关联匹配这样即使某几帧检测失败轨迹也能保持不会出现ID频繁切换或目标“闪没”。二是为行为判定提供时长数据。危险行为不能只看一帧比如打电话如果只是司机手从耳边滑过去你也不能每次都报警。通过DeepSORT维护的轨迹帧序号我能算出某个行为状态持续了多少毫秒超过阈值才触发告警这个逻辑在后面的规则引擎里非常重要。2.3 DeepSORT与传统跟踪方案对比这里顺便把DeepSORT和另外两种常见的跟踪思路做个对比。方案原理优点缺点帧差法/光流法基于像素运动估计无需训练实现简单在强光照、复杂背景下极易失效无法区分不同目标ByteTrack基于检测置信度的关联策略简单高效适合密集场景依赖检测质量对于长时间遮挡鲁棒性一般DeepSORT卡尔曼滤波匈牙利匹配外观特征ID稳定性好能跨短时遮挡工业落地多需要额外ReID模型对小型目标特征提取要求高项目中采用DeepSORT还有一个原因是它内部集成了外观特征描述子ReID能利用目标的外观特征来区分同一类别的不同个体。在一个驾驶舱里通常只有司机一个人但有时副驾驶也会入镜这时ReID能明显降低把副驾驶误判成司机的概率。3. 训练前的准备环境、数据与标注3.1 开发环境配置清单先说环境免得有人卡在第一步。项目开发环境我用的Ubuntu 22.04Python 3.9PyTorch 1.12CUDA 11.6。显卡是RTX 3090显存24G。训练时数据比较多显存不够很容易爆所以如果显存只有8G左右建议用YOLOv5s或者更小的YOLOv5n并且把batch size调低。核心依赖建议直接按requirements.txt装torch1.7.0torchvision0.8.0numpyopencv-pythonpillowpyyamltqdmscipyseabornmatplotlibDeepSORT部分额外需要scikit-learnfilterpy安装没什么特殊技巧唯一要注意的是PyTorch的CUDA版本必须和你机器上的驱动匹配。装好后可以用一条命令验证python -c import torch; print(torch.cuda.is_available())如果输出True说明环境没问题。很多人在“yolov5安装步骤”上卡壳实际多数都是cuda和torch版本不匹配或者缺少gcc编译组件。3.2 数据集来源与核心数据处理驾驶员行为识别最常用的公开数据集是State Farm Distracted Driver Detection里面把驾驶员行为分成10类c0: 安全驾驶c1: 右手发短信c2: 左手发短信c3: 右手打电话c4: 左手打电话c5: 操作收音机/中控c6: 喝水c7: 向后伸手c8: 整理头发和化妆c9: 和乘客交谈这个数据集是图片分类格式不是目标检测的标注框格式。如果要把它转成YOLOv5需要的检测数据集需要先用预训练检测器比如YOLOv5s在人脸、手部、手机等目标上半自动生成标注框再手动修正。我的做法是直接把模型改造为“多层目标并行检测”检测目标1驾驶员全身/上半身区域用于确定“谁是司机”。检测目标2手、手机、水杯、烟等手持物体用于分心行为判断。检测目标3人脸、眼睛、嘴巴用于疲劳状态判断。这三个目标不一定要放在同一个模型里训练但强烈建议眼睛和嘴巴状态单独训练一个子模型因为闭眼/打哈欠这种小目标的尺度特征和大目标差很多放在同一模型容易训练不充分。3.3 数据标注的实操细节标注工具我用的LabelImg输出YOLO格式的txt文件每行格式是类别ID 中心点x 中心点y 宽度 高度坐标值都归一化到0-1之间。标注时最需要注意的是遮挡和相似目标。标注“手机”时如果手和手机高度重叠我建议把手机作为独立目标标注不要把手和手机合并成一个框。因为后续行为判定逻辑需要知道手机是否靠近耳朵或嘴部。手部如果严重遮挡可以单独标注为“hand”不需要细分左右手但条件允许的话分左右手对判断打电话和发短信会有帮助。疲劳数据方面我用了YawDD打哈欠数据集作为基础再补充自采的闭眼/睁眼数据。自采数据时让我自己坐在驾驶位用普通USB摄像头拍了几十段视频包括正常睁眼、眯眼、完全闭眼、张嘴打哈欠、说话时张嘴。然后把视频抽帧逐帧标注出眼睛的“open”/“closed”状态和嘴巴的“open”/“close”状态。标注一个容易忽略的点同一批数据最好固定标注人员或者两个人标注完再做交叉校验。否则同一个“眯眼”状态不同人可能一个标成open、一个标成closed训练出来的模型在阈值附近的样本上会很飘。4. 训练驱动注意力的YOLOv5模型参数配置与调优过程4.1 训练文件配置与关键命令我建了一个driver.yaml用来指定数据集路径和类别名称train: /data/driver_dataset/images/train val: /data/driver_dataset/images/val nc: 10 names: [safe_driving, text_right, text_left, call_right, call_left, operate_radio, drink, reach_behind, hair_makeup, talk_passenger]主行为模型训练命令如下python train.py \ --img 640 \ --batch 16 \ --epochs 150 \ --data driver.yaml \ --weights yolov5s.pt \ --cache这里cache参数表示将图片预先缓存到内存中能够显著减少训练时频繁读盘造成的等待。img设为640是平衡速度和精度的经验值如果摄像头画面里的人脸和手部占比较小可以试试训练时用800左右但推理速度会相应下降。4.2 超参数调优的真正重点YOLOv5的官方超参文件里有lr0、momentum、weight_decay、hsv_h等一堆参数。很多人喜欢直接去调这些但实际上对训练结果影响最大的可能不是这些数值而是数据本身和anchors设置。训练自定义数据集时一定要让YOLOv5自动重新计算anchors。系统会在训练开始时用k-means算法在当前训练集上重新聚类生成一组anchors。如果你不放心可以手动触发python train.py --data driver.yaml --weights yolov5s.pt --noautoanchor然后查看anchors生成结果如果发现聚类出来的宽高比和你的小目标比如眼睛、手机相差很大就要考虑是不是标注框精度的问题。闭眼这种小目标我建议用yolov5m甚至yolov5l模型或者在检测层增设针对小目标的高分辨率检测头。4.3 类别不均衡与过拟合处理驾驶员行为数据里“safe_driving”和“talk_passenger”类别样本量会远超“drink”“reach_behind”等类别。直接训练会导致模型对少数类召回率很低。我的处理方式是对少数类做重复抽样让每一轮epoch里少数类样本被重复看到。调整YOLOv5损失函数中的类别权重把少数类的cls loss权重适当调高。数据增强时对少数类做更多的旋转、平移、裁剪变化增加样本多样性。训练过程中还要注意过拟合。我自己训练时前80个epoch表现很好到后30个epoch验证集mAP不升反降这就是过拟合征兆。处理办法是设置early stopping patience或者把mosaic增强在最后阶段关闭。YOLOv5的mosaic增强在末段可以设置为0例如在训练脚本里动态调整前80个epoch使用mosaic1.0后20个epoch改为mosaic0.0这样能让模型在接近真实分布上做最后的精调。最终我的主行为模型在验证集上mAP0.5约为0.93打电话、喝水、抽烟这几类召回率都在0.88以上。这个精度其实还有提升空间但已经满足实际告警需求了。5. 疲劳检测子模块闭眼、打哈欠与PERCLOS统计5.1 疲劳检测的方案选择疲劳检测不是简单“检测到闭眼就报警”而是需要在一个时间窗口内综合判断。业界最常用的指标是PERCLOS单位时间内眼睛闭合时间所占的百分比。除此之外打哈欠频率和连续闭眼时长也是重要辅助指标。我采用双模型方案主模型检测驾驶员行为类别。疲劳子模型检测眼睛open/closed和嘴巴open/closed区域由人脸检测框裁剪后送入小模型。这样做的原因很简单人脸区域的闭眼/张嘴状态在640x640的整体画面里太小了直接在大图上检测小目标效果不好先做face detection再把眼睛嘴巴区域剪出来做状态分类准确率高很多也方便独立优化。5.2 PERCLOS核心公式与阈值设定PERCLOS的定义是统计一定时间内眼睛闭合帧数占总帧数的比例。公式PERCLOS 闭眼帧数 / 窗口总帧数针对驾驶员疲劳预警我设置的判定规则如下指标阈值判定结果眼睛闭合时长连续大于2秒疲劳预警PERCLOS30秒窗口大于40%疲劳预警打哈欠频率60秒窗口大于3次疲劳预警频繁低头10秒窗口低头累计超过6秒分心预警具体实现时我用了一个滑动窗口列表来保存过去60秒内每一帧的状态窗口滑动更新。伪代码如下# 维护一个长度为窗口内期望帧数的队列 state_queue deque(maxlen1800) # 60s * 30fps def update_eye_state(is_close): state_queue.append(is_close) close_count sum(state_queue) perc close_count / len(state_queue) * 100 return perc这个实现要注意取样间隔问题。不同摄像头帧率不同如果队列长度固定而帧率变了窗口实际覆盖的时间长度就不对。所以我建议用时间戳来计算而不是用帧数记录每帧的眼睛状态和时间戳统计时只取过去60秒内的记录。5.3 打哈欠判定的细节打哈欠检测我用的是嘴巴张开程度持续时间双条件判定。当嘴巴检测为“open”且维持超过1秒认为是一次哈欠如果嘴巴只是快速说话、咳嗽通常张开状态不会持续超过1秒。这里有一个实测中的坑司机大声说话、唱歌时嘴巴持续张开的时长很容易超过1秒误报率明显上升。为降低误报我在判定哈欠时增加了“连续哈欠间隔”和“眼睛状态”两个辅助条件如果同一时间窗口内眼睛没有持续闭合就降低哈欠置信度如果60秒内连续出现3次以上哈欠才触发疲劳告警。这样比单纯检测一个“张嘴”动作可靠得多。6. Deepsort接入的实战细节与跟踪稳定化6.1 Deepsort跟踪的核心原理回顾DeepSORTDeep Simple Online and Realtime Tracking是在SORT基础上的改进版本。SORT依靠卡尔曼滤波预测目标运动状态再用匈牙利算法基于IOU关联检测框和已有轨迹。DeepSORT在其基础上补充了外观特征匹配通过一个ReID网络提取目标的外观特征计算特征之间的余弦距离当IOU关联失效时仍然可以依靠外观特征把同一个目标对应上解决目标遮挡后ID Switch的问题。接入整个系统的流程是from deep_sort_pytorch.deep_sort import DeepSort deepsort DeepSort( weights/deepsort/ckpt.t7, max_dist0.2, max_iou_distance0.7, max_age70, n_init3, nn_budget100 )参数含义可以理解为max_dist特征最大余弦距离超过这个值则认为不是同一个目标。max_iou_distance关联时允许的最大IOU距离太小了会丢失目标。max_age轨迹最大保留帧数超过该帧数仍未匹配则轨迹删除。n_init轨迹需要连续匹配成功的帧数达到后才认为轨迹有效。nn_budget存放历史特征的样本数量。实际运行中我建议先把检测结果做置信度过滤只保留高于0.5的框进入跟踪器否则一些低置信度的误检框会污染轨迹导致ID频繁跳变。6.2 如何让跟踪结果服务行为判定跟踪器输出的每个目标会带一个唯一IDID对应的轨迹上关联了目标类别、检测框、帧序号。在行为判定模块里我需要把“手机”和“手”的关系、把“脸”和“眼睛状态”的关系都建立在“同一个目标/同一个区域”上。一个实用的做法是只保留跟踪器输出的“人”这个最大目标作为司机并把该目标的人脸检测框位置与手、手机、水杯等目标做空间距离计算。我定义了一个简单的关系判断逻辑# 判断手机是否靠近耳朵是否在打电话 if phone_center and face_center: horizontal_distance abs(phone_center.x - face_center.x) vertical_distance abs(phone_center.y - face_center.y) if horizontal_distance 0.15 * frame_width and vertical_distance 0.2 * frame_height: potential_call True这个距离阈值要根据摄像头安装位置调整。后装DMS摄像头通常安装在方向盘上方或仪表台手机放在耳边时和脸的水平距离很小垂直距离略大如果是放在大腿上看屏幕那手机和脸的垂直距离会很大不会被误判为打电话。6.3 跟踪导致的问题与解决第一阶段跑完我发现跟踪器对“人”的检测框稳定但对“手机”、“水杯”这种小目标的跟踪并不稳定表现是ID频繁切换。原因是小目标的ReID特征不明显而且运动幅度大。解决办法是我对非人体目标不做过长的轨迹关联而是直接使用检测框做短时缓存。也就是说只有“司机”这一主目标走完整DeepSORT流程手机、水杯等小目标只做最近三帧的IOU关联。这样既保留了对主目标的稳定跟踪又减少小目标ID切换给行为判定带来的干扰。7. 系统联调、实测表现与误报排查7.1 整体运行流程与告警规则系统主程序跑起来后的流程是打开视频流或摄像头。每隔一帧送入YOLOv5主模型得到行为类别和检测框。根据前一步输出的人脸框裁剪眼睛嘴巴区域送入疲劳子模型。将检测框送入DeepSORT获取目标轨迹。行为判定模块结合轨迹、距离关系、时间窗口触发告警条件。满足告警条件则输出告警信息并截图保存。行为判定规则可以做成一个独立的JSON配置方便现场调参。比如{ call_duration_threshold: 3.0, drink_duration_threshold: 3.0, smoke_duration_threshold: 3.0, eye_close_threshold: 2.0, yawn_threshold_per_minute: 3, perc_threshold: 40.0 }这样去现场部署时不需要改代码只调配置文件就能适配不同客户对灵敏度要求不一致的情况。7.2 实测数据帧率、延迟与检测精度我在自己的测试环境上做的实测结果如下项目数值测试视频32段总时长约6小时测试场景白天光照、夜间无红外、逆光、戴墨镜、副驾驶入镜GPURTX 3090主模型推理平均耗时约18msDeepSORT跟踪平均耗时约5ms疲劳子模型平均耗时约6ms全流程帧率约30fps行为类别mAP0.5约0.93疲劳状态分类准确率约95%帧率基本能满足实时处理需求。如果CPU环境部署强烈建议把模型导出成ONNX用OpenVINO推理或者直接上TensorRT做INT8量化这样才能在Jetson、RK3568这类边缘设备上达到实时效果。7.3 实测中出现的误报场景与排查思路误报是这类监控系统的生命线误报率太高客户就把系统关了。我遇到的误报场景以及排查思路大致如下一是副驾驶入镜干扰。后排坐人时系统偶尔把副驾驶的脸当成司机。解决方法是划定驾驶区域ROI根据摄像头安装位置和车内空间把检测框中心点限制在预设的驾驶员座椅区域。超出区域的目标直接不进入行为判定。二是司机摸脸、整理头发被误判为打电话。因为手部靠近脸部时手和脸的相对距离满足“靠近”条件。我增加了一个前置条件必须先检测到手机并且手机框与脸部区域有交集才判定为打电话。单纯手靠近脸不再触发。三是喝水动作被识别为打电话。水杯和手机都是小目标有时跟踪轨迹ID不稳定导致水杯偶尔被当作手机。解决方式是把目标类别信息加入跟踪器的特征判断只对同类目标做关联。四是司机戴墨镜导致闭眼检测失效。夜间或逆光下眼睛区域画面非常暗疲劳子模型会把墨镜或阴影误判为闭眼。我针对这个场景增加了图像预处理对裁剪出的眼睛区域做直方图均衡化然后训练模型时会加入一些加了暗化增强的样本。但这个无法完全解决最好的办法是使用带红外补光的DMS摄像头。五是张嘴边说话误判为打哈欠。解决办法上面讲过增加哈欠连续时长条件并且看同一窗口内眼睛是否闭合双条件综合判断。7.4 告警截图与事件管理实测中发现逐帧告警没有意义生产环境需要的是“事件”。所以我在告警输出层做了聚合如果同一个ID在连续10秒内反复触发同类告警只生成一条事件事件里附带最清晰的一张截图、告警起止时间、行为类型和置信度。这样监控中心看到的不是刷屏的告警流而是一条条可确定的事件记录方便运输公司做安全台账。另外事件数据我同时以JSON格式输出到本地文件并提供一个HTTP POST回调接口便于接到已有车队管理系统。回调服务端如果5秒内没有返回200客户端会重试三次避免网络抖动导致事件丢失。8. 交付文件说明与后续改进方向8.1 zip压缩包结构解析拿到压缩包后先看目录结构driver-distraction-warning-system/ ├── README.md # 项目说明、环境安装、运行步骤 ├── requirements.txt # Python依赖清单 ├── models/ # YOLOv5相关模型定义文件 ├── utils/ # 数据处理、模型工具函数 ├── deep_sort/ # DeepSORT跟踪代码 │ ├── deep_sort.py # DeepSORT主模块 │ └── configs/ # 跟踪参数配置 ├── weights/ # 训练好的权重文件 │ ├── yolov5s_driver.pt # 主行为检测模型权重 │ ├── eyes_mouth.pt # 疲劳状态检测权重 │ └── deepsort/ # ReID模型权重 ├── data/ # 数据集配置和标注样例 ├── detect.py # 主程序入口 ├── rule_engine.py # 行为判定规则引擎 └── edge_export/ # ONNX/TensorRT快速导出脚本weights里的模型文件是整个包的核心资产。detect.py运行前需要按README里提示修改视频路径或摄像头编号以及配置文件里的参数。8.2 二次开发方向边缘部署与多传感器融合这个项目做完之后有几个方向值得继续深入。第一个是边缘端部署。YOLOv5本身有很好的部署生态把训练好的模型导出到ONNX再用TensorRT转成engine文件在Jetson Orin NX这类设备上可以做到30fps以上。INT8量化后模型体积和延迟都大幅下降但要注意校准数据集的选择如果用错了校准图片精度会掉得厉害。第二个是加入方向盘角度、车速、车道偏移等车辆CAN数据。视觉检测只能判断“行为动作”但结合车辆动力学数据才能更准确判断“驾驶风险”。比如司机在高速上闭眼2秒系统除了给出疲劳预警还可以根据车速计算车辆在这2秒内行驶了多少米自动提升告警等级。第三个是接红外DMS摄像头。普通USB摄像头在夜间和逆光条件下对眼睛细节的捕捉非常有限而疲劳检测极度依赖眼睛细节。带红外补光的DMS摄像头能极大提升夜间疲劳状态的检测准确率这是实测中效果最明显的硬件升级。第四个是把告警事件做成闭环管理。现在系统是单向输出告警后续可以在事件管理平台上增加司机确认、管理人员审核、绩效考核等功能形成一个完整的安全管理闭环。8.3 我做这个项目的一些体会整套系统从搭建环境到完成实测最花时间的不是写代码而是数据标注和阈值调优。代码层面YOLOv5和DeepSORT都是成熟框架开箱即用但真实场景里的灰尘、逆光、墨镜、副驾驶聊天这些变化全靠数据质量和规则设计来兜底。如果是从零复现这个项目我建议不要把精力花在魔改网络结构上先把数据集做扎实把规则阈值调成符合该项目实际场景的水平效果提升会非常明显。这个项目后面还可以继续扩展的地方很多比如用时序模型如LSTM或Transformer对行为序列做建模让系统不只是识别“单手打电话”这个静态动作而是能预判“司机即将分心”的趋势。不过那属于进阶内容了等这套检测和跟踪平台稳定跑起来之后再升级也不迟。本文还有配套的精品资源点击获取