简介这是一份基于深度学习的司机危险驾驶行为识别与告警系统完整Python项目源码配备GUI界面和详细注释主要面向计算机相关专业毕业生、课程设计及期末大作业需要完整实战项目的学习者。项目围绕驾驶员打电话、抽烟、打哈欠、分神等危险行为构建检测流程集成人脸检测、表情识别与SSD目标检测等模块可帮助理解从模型训练到界面交互的完整工程实现。压缩包共64个文件包含Python源码py/pyc、预训练模型和权重文件pth/h5/hdf5/npy、测试图片jpg、演示视频mp4、GUI界面文件ui/xml及说明文档等整体约212.52MB目录结构便于按模块查阅。项目源于高分毕业设计评审分98分目前已有274人学习下载适合作为毕设参考和深度学习项目实战练习的完整范本。1. 司机危险驾驶行为识别告警系统先解决“机器替你盯人”这道题一个车队调度室同时挂了三十路行车记录仪画面安排专人盯着屏幕看谁在打瞌睡头五分钟还有效半小时后全是视觉噪音。基于深度学习实现的司机危险驾驶行为识别告警系统就是把这件反人性的工作交给机器视频流逐帧分析驾驶员的脸部和手部遇到闭眼、打哈欠、低头、玩手机这类危险行为GUI 界面立刻弹告警并留存影像证据。它适合三类人做车联网或车队安全管理的开发者想做深度学习和 GUI 结合的课程设计或毕业设计的学生以及想把手头检测模型接到上位机界面的工程师。它解决的问题不是一个“模型能识别猫狗”的演示而是“视频从进来开始怎么检测、怎么判定、怎么告警、怎么让人看见并反应”的完整链路。2. 危险行为定义与数据准备先让计算机知道“要盯的是什么”2.1 行为判定矩阵不同危险动作要对应不同的视觉证据很多人一上来就把所有危险行为塞进一个多分类网络里训练集又小又乱最后模型把打哈欠和低头混在一起。我在实际项目里惯用的拆法是按视觉证据分三类行为类型典型动作判定依据告警输出建议疲劳类闭眼、打哈欠人脸关键点算 EAR、MAR疲劳预警姿态类低头、视线偏离头部姿态欧拉角 pitch/yaw分心预警操作类手持手机、吸烟、喝水目标检测框之间的空间关系高风险告警这样分级的原因在于时间尺度完全不同。闭眼半秒钟就足够触发预警低头可能持续几分钟手持手机和吸烟则要求目标检测的框足够稳定。如果都用同一个多分类模型去判决要么把短暂动作漏掉要么把连续动作反复告警最后只能靠后处理救场还不如一开始就拆开。2.2 数据集从哪里找公开集起步自己补一轮差异样本公开的开源驾驶行为数据集是常见的初版选择例如早期常被引用的 State Farm 分心驾驶识别数据集已经按手机通话、操作中控、喝水、正常驾驶等类别分好。但我不会拿它直接训练最终模型。公开数据集的摄像头多位于驾驶室正前方固定角度而实际部署时摄像头可能装在仪表台偏左、偏上甚至 A 柱附近视角一变人脸关键点坐标和手部遮挡关系都会整体偏移。自己采一轮数据是必须做的。让不同司机分别做正常驾驶、看导航、打电话、喝水、抽烟的动作每人五到八分钟。条件有限至少覆盖不同身高和坐姿这直接决定了检测框的上下位置范围。标注建议直接用 YOLO 格式每帧一个 txt每行一条class cx cy w h。这里有个容易踩的坑类别设计。一开始只标了phone和smoke结果模型把喝水也分到phone因为手柄形态太接近。后来把drink单列为一类误报降了三分之一。类别边界先想清楚喝水、手持手机、吸烟三者画面相似业务上不需要区分时直接合并成“手持异物”也行同时要保证标注标准一致。训练前先统计类别分布避免某个行为样本过少导致模型压根不学它。import os from collections import Counter def count_yolo_classes(labels_dir, class_names): 遍历YOLO格式标注目录统计每个类别的目标数量 counter Counter() for file_name in os.listdir(labels_dir): if not file_name.endswith(.txt): continue path os.path.join(labels_dir, file_name) with open(path, r) as fp: for line in fp: cls_id int(line.strip().split()[0]) counter[class_names[cls_id]] 1 return counter class_names [normal, phone, smoke, drink, yawn] counter count_yolo_classes(datasets/train/labels, class_names) total sum(counter.values()) # 用类别频率的倒数做损失权重缓解样本不均衡 weights { name: min(10.0, total / (count * len(class_names))) for name, count in counter.items() } print(类别数量:, dict(counter)) print(建议loss权重:, weights)这段代码的用途是给训练配置里的 loss 权重做参考。min(10.0, ...)是限制稀缺类别的权重不能无限放大否则训练早期会出现震荡。实际操作中更稳的做法是把normal类权重压到 1.0稀缺类别权重设在 2 到 5 之间而不是直接采用纯反比计算结果。2.3 模型选型检测和关键点是两条线不是一个模型干完这个项目的模型链路不是“一个网络识别所有行为”。常见可靠做法是分成两条线目标检测模型负责人脸、手、手机、烟的框用 YOLOv5s 这类轻量模型就够PC 上能跑到实时帧率人脸关键点模型负责输出眼睛、嘴巴、鼻尖等坐标因为疲劳判定需要亚像素级别的稳定性检测框的顶点坐标不够用。为什么不直接训练一个端到端的视频分类模型因为实时性不好做。端到端视频分类要累积几十帧才有稳定的判断而关键点加状态机每帧都能算出相对量告警延迟能控制在几百毫秒内。另外换了摄像头角度时关键点模型的标定逻辑几乎不用改端到端模型通常要重新采数据训练。算力要考虑清楚。普通 PC 上同时跑 YOLOv5s 和 68 点关键点模型CPU 可以勉强到 15 帧有 GPU 可以流畅到 30 帧。如果部署目标是 Jetson 这类边缘设备检测模型导出 TensorRT FP16关键点模型用 ONNX Runtime才能保证车载场景下的稳定性。标题里带 Python 源码先把 YOLOv5 训练跑通再谈边缘优化更现实。3. 模型训练与行为判定链路从检测框到“该不该告警”3.1 训练目标检测模型先跑通 YOLOv5 最小流程把标注好的数据整理成 YOLOv5 能吃的目录结构然后写一个数据配置文件driver_behavior.yaml。# driver_behavior.yaml train: datasets/train/images val: datasets/val/images nc: 5 names: [normal, phone, smoke, drink, yawn]接下来用官方训练脚本跑一轮。python train.py --img 640 --batch 16 --epochs 80 \ --data driver_behavior.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --hyp data/hyps/hyp.scratch-low.yaml--img 640是在精度和速度之间最常见的折中再大就明显掉帧率。--batch 16按显存调整8G 显存建议降到 8。--epochs 80对行为类别不多、背景相对固定的项目够用不是说越多越好。如果看训练日志发现验证集 loss 在第 40 轮左右开始反弹基本就是过拟合直接回调到 50。--hyp hyp.scratch-low.yaml用较低的数据增强强度因为驾驶室场景光线变化大增强太激进反而让模型学到假的纹理。训练完用best.pt而不是last.pt。这轮训练里最容易翻车的是类别名带空格YOLOv5 读配置时会把名字切开类别对不上推理时全是错标签。3.2 人脸关键点与欧拉角EAR、MAR、低头角怎么算目标检测只给了人脸框疲劳判定还需要眼睛和嘴巴的关键点。以 68 点模型为例左眼取索引 36 到 41右眼取 42 到 47嘴巴外轮廓取 48 到 59。眼睛长宽比 EAR 和嘴巴长宽比 MAR 的计算方式如下。import math def eye_aspect_ratio(eye): 输入为单眼6个关键点坐标返回眼睛长宽比EAR v1 math.dist(eye[1], eye[5]) v2 math.dist(eye[2], eye[4]) h math.dist(eye[0], eye[3]) return (v1 v2) / (2.0 * h) def mouth_aspect_ratio(mouth): 输入为嘴部8个关键点坐标返回嘴巴长宽比MAR v1 math.dist(mouth[2], mouth[10]) v2 math.dist(mouth[4], mouth[8]) h math.dist(mouth[0], mouth[6]) return (v1 v2) / (2.0 * h)EAR 的闭眼参考阈值在 0.2 附近MAR 打哈欠阈值约 0.6但这两个数会随摄像头安装高度和司机眼部轮廓变化。我把它们做成 GUI 里的可调参数而不是写死在代码里。低头判定看的是头部姿态的 pitch 角当 pitch 小于负 15 度时判定低头。具体正负方向取决于关键点模型坐标系第一次接入时先打印一段正常驾驶的 pitch 均值确认方向。与其纠结绝对值不如记录正常驾驶时的 pitch 基线用相对偏移判断低头行为换司机时不用重新调参数。3.3 告警状态机连续帧确认加冷却时间疲劳告警最怕“一秒响一下”。单一帧的 EAR 低于阈值不代表人真的在闭眼可能是光线抖动或关键点飘了一下。合理做法是连续多帧确认告警后加冷却时间。class AlertStateMachine: 对单个危险行为做帧级去抖和防重复告警 def __init__(self, trigger_frames10, cooldown_sec3.0, fps30): self.trigger_frames trigger_frames # 连续多少帧判定有效 self.cooldown_frames int(cooldown_sec * fps) self.frame_count 0 self.last_alert_frame -self.cooldown_frames def update(self, hit, frame_id): hit 表示本帧该行为是否成立返回 alert / standby / ok if hit: self.frame_count 1 if (self.frame_count self.trigger_frames and frame_id - self.last_alert_frame self.cooldown_frames): self.last_alert_frame frame_id return alert return standby else: # 未命中时帧计数回落但不清零防止边缘抖动 self.frame_count max(0, self.frame_count - 2) return ok fatigue_machine AlertStateMachine(trigger_frames10, cooldown_sec3.0, fps30)trigger_frames10意味着一帧闭眼到告警的最坏延迟约 10 帧也就是三分之一步。冷却时间 3 秒是防止同一轮疲劳反复弹窗。未命中时计数器减 2 而不是清零是因为一次打哈欠可能被张嘴幅度的小波动分成两段彻底清零会导致第二次误报。这个细节是调试中一点点磨出来的看起来很不起眼但直接决定告警体验是“烦人”还是“可信”。3.4 实时链路三个线程各干各的别挤在一起完整的实时推理链路建议拆成采集、推理、消费三个部分中间用队列连接。采集线程只负责读摄像头推理线程从队列取帧做检测和判定GUI 线程只拿结果做展示。import queue import threading import cv2 frame_queue queue.Queue(maxsize2) result_queue queue.Queue(maxsize2) def capture_loop(source): cap cv2.VideoCapture(source) while True: ok, frame cap.read() if not ok: continue # 队列满则丢旧帧保证画面是实时的 if frame_queue.full(): frame_queue.get() frame_queue.put(frame) def infer_loop(): frame_id 0 while True: frame frame_queue.get() dets detector(frame) # YOLO检测结果 faces keypoints(frame, dets) # 关键点坐标 hit judge_fatigue(faces) # 综合EAR/MAR/姿态 state fatigue_machine.update(hit, frame_id) frame_id 1 result_queue.put((frame, dets, state))maxsize2是为了让队列只保留最新两帧推理速度跟不上时直接丢弃旧帧而不是越积越多导致画面延迟越来越大。视频流处理里“丢帧保实时”是常规操作比阻塞等待更合适。结果队列的消费端在 GUI 线程里只负责刷新画面和触发告警提示不参与任何模型计算。4. GUI 告警界面让系统不只是“跑在终端里”4.1 界面框架怎么选PyQt5 不是唯一答案但最顺手GUI 框架选择直接决定开发效率。我长期用的是 PyQt5核心原因是 QThread 和信号槽让推理与界面天然解耦QLabel 显示视频帧只是最基础能力后续想加统计图表、轨迹回放也都有现成组件。Tkinter 上手快但做多线程刷新时要自己处理线程安全实时视频一卡一卡的。PySide6 是 PyQt5 的现代替代品API 基本一致适合 Python 3.10 以上环境。框架实时视频刷新多线程安全性界面复杂度上限上手成本PyQt5/PySide6流畅QTimer 精确信号槽机制完善高中等Tkinter可用但易卡需要自己加锁低低如果这个系统最后要拿去演示或给非技术同事用直接选 PyQt5省下的调试时间远比学习成本值。4.2 主界面布局视频、状态、告警记录、参数四个区常见布局是左侧 16:9 视频区右侧从上到下放四个区块当前状态标签、告警记录列表、检测结果置信度、参数调节区。参数区至少放 EAR 阈值、MAR 阈值、低头角度阈值、冷却秒数这四个方便在演示现场直接调阈值看反应。把阈值做成可调不是懒是因为司机个体差异真的很大固定参数的系统换个人坐进去就翻车。4.3 QThread 把推理放后台界面只负责显示直接在 QTimer 回调里跑推理是 GUI 卡死的第一原因。推理一次 50 到 300 毫秒这段期间主线程无法处理窗口事件画面就冻结。正确做法是把推理放进 QThread通过信号把结果发回主线程。from PyQt5.QtCore import QThread, pyqtSignal import cv2 class DetectWorker(QThread): frame_ready pyqtSignal(object, object) # (图像, 检测结果) def __init__(self, source0, parentNone): super().__init__(parent) self.source source self._running True def run(self): cap cv2.VideoCapture(self.source) while self._running and cap.isOpened(): ok, frame cap.read() if not ok: continue boxes, labels self.detect(frame) self.frame_ready.emit(frame, (boxes, labels)) cap.release() def stop(self): self._running False信号槽的机制是跨线程安全的frame_ready信号在主线程里被槽函数接收等于把图像从工作线程安全地送回了界面线程。这里有个很容易犯的错在run()里直接调用label.setPixmap()看起来能跑但偶尔会崩溃而且很难复现。所有界面操作都走信号槽别绕过。4.4 告警联动界面闪烁、语音播报、日志留痕告警不能只是弹一行字现场使用需要三种通道同时触发。界面层把状态标签切到红色并闪烁几次用 QTimer 控制闪烁次数。语音层播放预生成的 WAV 片段比如“请勿疲劳驾驶”“检测到吸烟”比实时 TTS 稳定且不占 CPU。数据层把告警写入 CSV 或 JSONL方便事后回放。import csv import time def write_alert_log(path, behavior, conf, source): 每行一条告警记录字段含时间、行为、置信度、视频源 with open(path, a, newline, encodingutf-8) as fp: writer csv.writer(fp) writer.writerow([ time.strftime(%Y-%m-%d %H:%M:%S), behavior, round(conf, 3), source ])日志文件名按天拆分避免单文件无限增长。写日志的动作不要直接放在告警回调里一旦磁盘慢会拖累主线程通常做法是丢进一个独立小队列由后台线程统一落盘。5. 避坑与排查从“能跑”到“不误报”的 5 个坎5.1 检测框乱跳先看 NMS 与置信度不是模型不收敛现象检测框不停抖动人脸框一会儿大一会出现关键点跟着漂移告警状态在正常和危险之间反复横跳。原因检测模型对遮挡和低头姿态天然不稳定但更多时候是推理时的置信度设得太低模型把一些模糊背景也当成了目标。NMS 阈值太高时同一个人脸会同时保留两个框。解决把检测置信度从 0.25 提到 0.45 以上NMS IoU 取 0.5对相邻帧的检测框再做一次指数平滑。牺牲 5% 的召回率换稳定性对告警系统非常值。5.2 GUI 界面卡死推理绝不能放在主线程现象界面打开后拖动窗口发卡关闭视频时直接假死。原因把检测函数写在了 QTimer 的回调里每次推理阻塞主线程几十到几百毫秒界面事件全部排队。解决把推理挪进 QThread详情参考第 4.3 节。关闭窗口时先stop()线程再释放摄像头资源否则下次启动时摄像头端口还在被占用。这个顺序错一次下次运行就打不开视频流排查半天才想起来是释放顺序的问题。5.3 光线突变集体误报逆光与隧道是重灾区现象车辆刚进隧道系统连续报三四个“疲劳”出隧道又报一轮。原因亮度剧变让关键点坐标整体偏移EAR 和 MAR 随噪声变化剧烈固定阈值基本失效。解决预处理里加自适应直方图均衡化 CLAHE同时让关键点模型输出置信度置信度低于阈值的关键点不参与判定。还可以统计大窗口内的亮度均值检测到突变后暂停疲劳类告警 1 秒只保留目标检测类告警。真实疲劳行为的亮度变化是平缓的不会因为暂停一秒就被漏掉。5.4 固定阈值不通用小眼睛和高矮坐姿都绕不过去现象司机 A 一坐进驾驶室系统立刻报“闭眼”司机 B 明明在打哈欠系统没反应。原因EAR 固定阈值 0.2 对不同眼型、不同摄像头安装高度完全不适用。眼睛天生小的人 EAR 本来就低等于一开机就处于“闭眼”状态。解决系统启动后做一个两分钟的个人基线标定统计正常驾驶时的 EAR 均值实际判定阈值设为均值乘 0.7 或 0.75。换司机时重新标定不修改任何代码。这个方案的代价是系统开机后有两分钟静默期但换来的误报下降非常明显。5.5 手摸方向盘被当成手持电话判定维度太单一现象司机双手扶方向盘过弯时触发“手持电话”告警。原因判定逻辑只用了“手部框与脸部区域重叠”这一个条件方向盘在画面里恰好也接近人脸区域。解决引入手机目标类别只有“手部框与手机框重叠”才判定手持电话。手部框与脸部重叠时还要同时检出手机才会触发。如果手机类别不可用就降级为“低头/视线偏离”预警不上升到高风险告警。分级处理比一刀切精准得多。提示以上五条没有一条是数据量不够造成的多数是判定逻辑和线程模型的问题调试时别一股脑去采集更多数据。6. 用一段真实驾驶视频做回归测试比刷论文指标更管用6.1 回归测试怎么设计用小样本“告警曲线”取代主观感觉训练完别急着看 mAP。我会准备一段两到三分钟的真实驾驶视频包含匀速驾驶、变道、闭眼五秒、低头看手机几秒、正常聊天几个片段手动记录真值行为发生的时间段再跑一遍系统对比系统告警事件与真值的时间重叠度。统计两类指标该报的有没有报出来不该报的有没有误报。一次测试就把阈值暴露得很清楚。闭眼漏报时看的是 EAR 中间值和阈值差多少如果中间值只比阈值高 0.03说明阈值需要抬一点如果误报多看集中在哪个行为多半是类别边界没画清。6.2 一个我一直沿用的技巧告警灵敏度三档热切换把每个行为的trigger_frames做成三档灵敏度高灵敏度 6 帧、中档 10 帧、低档 15 帧在 GUI 里用下拉框随时切换。演示时先用中档现场评价说反应慢了就切到高档说误报多了就切回低档。这比每次改代码改重启高效得多也让系统能适配不同车队的管理风格。我在这上面吃过亏有一版把疲劳检测调到了零误报结果连续两天系统一次警都没报查历史视频才发现司机频繁闭眼只是所有告警都被“稳健”的阈值滤掉了。后来我再也不追求零误报而是保留灵敏度档位让使用者自己权衡。识别告警系统的价值不是永远不出错而是把值得人关注的异常挑出来。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
PyTorch+ResNet18实现司机危险驾驶行为识别告警系统 简介:这是一份基于深度学习实现的司机危险驾驶行为识别告警系统完整项目,评审得分98分,适合正在准备毕业设计、课程设计或期末大作业的计算机相关专业学生,也适合希望积累实战经验的学习者。项目围绕驾驶场景中的分神、打电话、吸… · 2026/9/27 23:05:51
零信任架构实战:基于天远人企关联构建自动化供应商审查网关 破解供应商入驻痛点:从传统人工核查到数据直连
在B2B跨境电商平台的日常运营中,确保新入驻供应商的真实经营状态与相关人员的关联资质,是防范履约隐患与保障平台生态健康的核心环节。传统模式下,招商团队往往需要人工收集并比对各… · 2026/9/27 23:05:51
搞定域名服务器:DIY网站源码落地的最佳实践指南 搞定域名服务器:DIY网站源码落地的最佳实践指南 域名解析报错 404,服务器 SSH 连接超时,Nginx 配置改完直接白屏。对于想自己动手搭建网站的朋友来说,这“域名服务器搞不懂”的三座大山,是拦路虎也是试金石。别慌,这其实是 DIY… · 2026/9/27 23:40:34
上网第二十二课:Mesh 组网为什么能“无缝切换“?藏在背后的四件事 上网第二十二课:Mesh 组网为什么能"无缝切换"?藏在背后的四件事上周去给一个客户装 Mesh,他问我一句话把我问住了:“师傅,我这三台路由器都叫一个名,咋手机从客厅走到卧室,微信视频一… · 2026/9/27 23:40:28
Woodpecker 插件开发实战指南:用 `PLUGIN_` 环境变量约定构建你的第一个 CI/CD 插件 CI/CDDevOps 【免费下载链接】woodpecker Woodpecker is a simple, yet powerful CI/CD engine with great extensibility. 项目地址: https://gitcode.com/gh_mirrors/wo/woodpecker 点击查看 免费下载 插件(Plugin)是 Woodpecker 生态中最… · 2026/9/27 23:40:15
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01