恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLOv5+DeepSORT+疲劳检测:驾驶员分心预警系统全链路解析
首页
资讯中心
/
YOLOv5+DeepSORT+疲劳检测:驾驶员分心预警系统全链路解析
YOLOv5+DeepSORT+疲劳检测:驾驶员分心预警系统全链路解析
发布时间:2026/9/28 13:27:30
简介基于YOLOv5与DeepSORT的驾驶员分心驾驶预警监测项目面向人工智能、深度学习方向的毕业设计、期末大作业及课程设计场景完整覆盖疲劳驾驶和危险驾驶行为的实时识别流程。压缩包共60个文件容量约110.69MB包含Python源码、YAML模型配置、PyTorch权重文件、面部关键点检测的dat模型、PyQt界面文件、演示视频与数据集等其中图形界面可直观呈现实时监测结果。核心代码涵盖目标检测、目标跟踪、疲劳判定和危险行为预警模块训练好的模型已集成可直接运行并观察检测效果也便于对照源码理解多目标跟踪与状态判别逻辑。项目经助教审定本地编译可运行评审分达98分难度适中配套README说明环境配置和启动方式。目前已有120人学习下载对有完整可落地参考需求的开发者而言是完成高分毕设或课设的高价值实践资源。1. 分心驾驶预警监测YOLOv5DeepSORT疲劳判定一次把检测链路跑通拿到这套 YOLOv5DeepSORTPython 的驾驶员分心驾驶行为预警监测源码时我第一反应是它本质上是两套模型加一条流水线YOLOv5 负责框出驾驶员并识别打电话、抽烟、喝水这类危险行为DeepSORT 负责给同一目标挂上稳定的跨帧 ID 防止重复报警再用 dlib 的 68 点人脸关键点算 EAR 做疲劳眨眼判定。真跑通之后我才意识到这个毕设能拿到 98 分靠的不是某个模型多先进而是把三种技术在同一帧画面里协调输出既不漏报也不刷屏。资源里带了训练好的 best.pt 权重、face landmarks 模型、演示视频和完整数据集适合做毕业设计、课程设计或者想快速搭一个可演示的驾驶行为检测 demo 的人。下面我按复现顺序写先拆文件再配环境然后讲核心判定逻辑最后是排坑和调参。2. 拆包看结构先把十几个模块的调用关系理清再谈跑通解压这套源码包之后根目录下的 .py 文件一眼看过去很劝退——myfatigue、myframe、mydetect、main、ui_mainwindow 加上一个 mainwindow.ui再带 models、utils、weights 三个目录。很多第一次拿到的同学习惯直接python main.py结果抛出一堆 import 错误就是因为没搞明白这些文件的层次。我建议按「界面层、检测层、工具层」三层去看理解和排错都顺手很多。2.1 三个主程序的分工myfatigue / mydetect / myframe从文件名就能猜个大概mydetect.py 是 YOLOv5 的检测封装负责把一帧图像变成一组带类别和置信度的目标框myfatigue.py 是疲劳检测分支用 dlib 人脸关键点计算眼纵横比 EAR判定闭眼和打瞌睡myframe.py 是帧调度中枢把视频流拆成一帧帧依次送进上面两个模块再把结果合起来交给界面或日志输出。main.py 是入口只负责创建 PyQt5 的 QApplication 和主窗口真正干活的在另外三个文件里。先看 main.py 的入口它是整个程序最先执行的代码# main.py程序入口只做界面初始化和事件循环 import sys from PyQt5.QtWidgets import QApplication from ui_mainwindow import Ui_MainWindow def main(): app QApplication(sys.argv) win Ui_MainWindow() # 界面类定义在主窗口文件里 win.show() sys.exit(app.exec_()) # 进入 Qt 事件循环等待用户点击 if __name__ __main__: main()逻辑说明main 函数里没有碰任何检测代码它只是把 PyQt5 界面类实例化并 show 出来。视频读取、模型推理都发生在用户点击「开始检测」之后触发的事件里排查问题时要优先看 ui_mainwindow.py 里按钮绑定了哪个槽函数而不是盯着 main.py 看。参数说明注意这里是from ui_mainwindow import Ui_MainWindow而不是直接读 mainwindow.ui。Qt Designer 生成的 .ui 是 XML 文件Python 不能直接 import。源码包里同时出现 .ui 和 .py就是因为作者用 pyuic5 把布局文件转成了 Python 类。如果你改了 .ui 布局要重新执行pyuic5 mainwindow.ui -o ui_mainwindow.py再运行否则界面改动不生效。接着看 mydetect.py这是和模型打交道最多的地方# mydetect.py加载本地 YOLOv5 权重返回可推理的模型对象 import torch from models.experimental import attempt_load def load_detector(weights_pathweights/best.pt): # 优先用 GPU没有显卡就退回 CPU device torch.device(cuda:0 if torch.cuda.is_available() else cpu) model attempt_load(weights_path, map_locationdevice) # 加载权重到指定设备 model.conf 0.45 # 目标置信度阈值低于该值的检测框被过滤 model.iou 0.5 # NMS 的 IoU 阈值控制重叠框合并强度 return model, device逻辑说明这里用的是models.experimental里的 attempt_load不是 torch.hub说明项目引用的是本地 yolov5 工程目录下的代码而不是在线拉取 GitHub 最新版。这样做的优点是离线能跑、版本锁定缺点是 torch 版本必须匹配这套源码的写法这一点第 5 章会专门讲。参数说明conf0.45 是偏保守的阈值适合演示场景。如果实测漏报率高降到 0.3 能捡回更多低置信度目标误报也会跟着涨。iou0.5 控制 NMS 合并当两个重叠框的 IoU 超过 0.5 时只保留置信度高的那个方向盘和手离得近时可以调到 0.4 减少粘连。myfatigue.py 的结构更单纯基本就是 dlib 的人脸检测加关键点提取# myfatigue.py加载 dlib 人脸检测器和 68 点关键点预测器 import dlib # get_frontal_face_detector 是 dlib 内置的 HOG 人脸检测器不需要额外权重 face_detector dlib.get_frontal_face_detector() # shape_predictor_68_face_landmarks.dat 是官方预训练的 68 点关键点模型 landmark_predictor dlib.shape_predictor( weights/shape_predictor_68_face_landmarks.dat )逻辑说明dlib 的两个对象一个框出人脸区域一个在框内回归 68 个关键点坐标。注意 shape_predictor 的路径要和 weights 目录下实际文件名完全一致大小写都不能错否则在初始化阶段直接抛文件不存在的异常。参数说明dlib 的人脸检测器对侧面脸和中远距离小脸召回一般。如果实际场景是车内广角摄像头、脸占比很小建议先对图像做裁剪或放大预处理第 6 章展开。最后是 myframe.py它把上面两个模块串成一条流水线# myframe.py一帧输入输出检测结果、疲劳标志和追踪 ID def process_frame(frame, detector, landmark_predictor, tracker): # 第一步YOLOv5 检测拿到目标框和类别 dets detector(frame) # 第二步对检测到的驾驶员区域做人脸关键点计算 EAR ear compute_ear(frame, dets, landmark_predictor) # 第三步检测框送进 DeepSORT获得跨帧稳定 ID tracks tracker.update(dets, frame) return tracks, ear逻辑说明这个函数是整条链路的骨架——检测是基础疲劳判定在检测出的人脸区域上做追踪在检测框之上做。实际源码里会比这里复杂比如要区分左右眼、要做闭眼连续帧计数但调用顺序基本就是这三步。调试时建议在这个函数入口和出口各打一个断点先确认输入帧格式再确认输出结构。参数说明compute_ear 是我按工程习惯起的示意函数名实际项目里可能叫 fatigue_detect 之类跑之前先 grep 一下函数名再改别照抄。2.2 模型与工具层best.pt、关键点模型与 models / utils 两个目录把界面层和检测层搞清楚之后剩下两个目录是支撑整个项目的骨架。models 目录存放网络结构定义里面的 yolov5s.yaml、yolov5m.yaml、yolov5l.yaml、yolov5x.yaml 分别对应从小到大四档模型yolo.py 是检测头定义common.py 是公共模块CSP、SPP、Focus 等experimental.py 里就有刚才用到的 attempt_load。utils 目录则是一整套配套工具我用表格整理一下关键文件文件/目录职责什么时候会用到weights/best.ptYOLOv5 训练好的驾驶行为检测权重推理和演示时加载weights/shape_predictor_68_face_landmarks.datdlib 官方 68 点人脸关键点模型疲劳检测分支加载models/*.yaml网络结构配置深度、宽度、类别数训练或改结构时models/yolo.pyDetect 检测头定义推理时被引用utils/loss.py训练损失函数重新训练才用utils/metrics.py计算 mAP、混淆矩阵等指标评估模型时utils/datasets.py数据加载、增强训练和推理数据读取utils/general.py通用工具非极大值抑制、路径处理等全程都在用utils/autoanchor.py自动计算 anchor 尺寸训练新数据集时这份清单是 YOLOv5 工程的标准布局和官方仓库基本一致。如果你只想跑推理真正绕不开的只有 best.pt、shape_predictor_68_face_landmarks.dat 和 models、utils 目录里被 import 的那几个模块其余的都是给训练和评估预备的。推理时验证模型类别的最快方式是直接打印 model.names# 打印模型实际输出的类别索引和名字避免后面写死标签 names model.names print(names) # 输出形如 {0: normal, 1: phone, 2: smoke, 3: drink}逻辑说明YOLOv5 的模型对象自带 names 属性它是训练时数据集类别顺序的映射。后面所有报警判断都要以这个字典为准而不是靠猜。很多同学改类别后模型预测错乱就是这里没对齐。参数说明类别的具体名称和顺序由训练数据集决定不同版本的数据集标注可能不一样。拿到资源后先跑这一句把输出记录下来后面第 4 章的类别映射直接从这里抄。2.3 数据集、演示视频与 README先读懂资源清单再决定从哪跑起资源包里还有数据集、演示视频 MP4 和 README.md。数据集这部分走的是 YOLO 标准布局目录结构通常是这样的# 数据集目录结构YOLO 标准布局 datasets/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 训练标注每张图一个同名 txt │ └── val/ # 验证标注 └── classes.txt # 类别清单一行一个类逻辑说明YOLO 格式的标签不是 XML 或 JSON而是纯文本每一行代表一个目标框格式是「类别索引 中心点x 中心点y 宽度 高度」五个值全部归一化到 0~1。比如1 0.5 0.5 0.3 0.4表示类别 1 的目标位于图像中心宽高占图的 30% 和 40%。images 和 labels 里同名的文件一一对应缺一个就会在加载时报掉。README.md 是最容易被忽略的文件但对这套源码来说它记录了原始运行顺序和依赖项# 先读README它写了这个项目最初的运行入口和依赖说明 head -50 README.md逻辑说明很多毕设源码的 README 里会写明「先运行 main.py然后打开摄像头」也会列出额外安装的依赖。我一般拿到压缩包第一件事就是解压、读 README、列目录三分钟之内就能判断这套东西能不能在当前机器上跑起来。参数说明images 目录里那张 1620298460804.gif 是演示素材但 cv2 对 GIF 的支持很弱直接用 VideoCapture 读会踩坑。真想拿它做测试要么先转成 MP4 或图片序列要么用 PIL 读第 5 章有详细说明。演示视频 MP4 反而是最稳的测试输入建议第一次跑通就用它。3. 环境配置与首次运行Python 3.9 锁版本权重路径按原样放毕设源码最怕的不是代码难而是环境装不上。这套资源在打包机上编译过、跑通过换一台机器失败十有八九是版本错配。从pycache里的 cpython-39 文件名可以确定原环境是 Python 3.9这不是巧合dlib 和 PyTorch 对 Python 版本都很敏感锁同样的版本是最省事的路径。3.1 版本矩阵Python、PyTorch、CUDA 怎么搭配我整理了一份经过验证的版本搭配照这个装能避开大部分坑组件推荐版本说明Python3.9.x与源码里 pycache 一致dlib 有预编译 wheelPyTorch1.10.0 / 1.11.0兼容 YOLOv5 旧版源码避免 Upsample 报错CUDA11.3 左右对应 torch 1.10.0 的官方 wheeldlib19.22.1稳定Windows 下容易装opencv-python4.x 即可版本不用锁太死PyQt55.15.x和 ui_mainwindow 生成代码兼容这个搭配不是玄学是实测跑过这套 YOLOv5 旧版源码的组合。torch 版本尤其关键太新的版本会删掉旧接口导致 YOLOv5 老代码直接报 AttributeError具体报错和修法放在第 5 章。用 conda 建独立环境是必须的别往系统 Python 里塞# 用 conda 建独立环境避免污染系统 Python conda create -n drive python3.9 conda activate drive # 先装 PyTorch再装视觉和深度学习依赖 pip install torch1.10.0 torchvision0.11.0 pip install opencv-python dlib19.22.1 numpy pandas matplotlib pip install pyqt5逻辑说明第一行创建名为 drive 的独立环境第二行激活。接下来先装 torch 是因为它体积大、依赖多先把它钉死可以避免 yolov5 的 requirements 自动升级 torch 到不兼容版本。opencv-python 负责图像读写dlib 做人脸关键点PyQt5 提供界面。参数说明如果机器有 NVIDIA 显卡torch 装 CUDA 版时指定一下版本后缀没有显卡就直接装 CPU 版检测速度会慢但能跑通。dlib 指定 19.22.1 是经验——它是最常见踩坑点之一版本太新在某些平台上没有预编译 wheel装起来非常痛苦。装了 build tools 的同学可以尝试让 pip 自动编译 dlib但大多数情况下直接装预编译版更省事。3.2 权重和关键点模型放置路径、大小、校验源码里所有加载路径都是相对路径所以目录结构不能随便动。weights 目录下必须同时有 best.pt 和 shape_predictor_68_face_landmarks.dat文件放错位置程序会在初始化阶段就报错。# 目录检查确认权重和关键点模型都在 weights 下 ls -lh weights/ # 预期看到两个文件 # best.pt 几十 MB训练好的 YOLOv5 权重 # shape_predictor_68_face_landmarks.dat 约 99MBdlib 官方关键点模型逻辑说明best.pt 是 YOLOv5 训练好的驾驶行为检测权重后面所有危险行为识别都靠它shape_predictor_68_face_landmarks.dat 是 dlib 官方预训练的关键点模型用来定位两只眼睛的 12 个关键点算 EAR 判断疲劳。两个文件缺一不可。参数说明如果从网盘下载后文件大小和上述相差太远先别急着跑重新解压或重新下载大概率是下载截断。这个尺寸检查是我每次拿到模型权重都会做的一步成本极低但能省大量排错时间。3.3 三种运行入口界面、命令行、单帧调试依赖装好、文件放对之后至少要能跑通其中一个入口。我按由易到难列一下# 方式一跑完整界面 python main.py # 方式二命令行跑检测如果存在命令行入口的话 python mydetect.py --source images/1620298460804.gif # 方式三单帧调试最好用 python -c from myframe import process_frame; print(process_frame.__doc__)逻辑说明方式一适合演示窗口弹出来、选视频、开始检测一气呵成方式二适合在没有界面的服务器上验证模型能不能推理方式三适合开发调试——不启动界面、不读视频流直接调用核心函数断点打起来非常舒服。我个人的习惯是先方式三确认核心函数没问题再方式一跑完整演示。参数说明mydetect.py 具体支持哪些命令行参数以源码里的 argparse 定义为准不同毕设版本差异很大。不确定就python mydetect.py --help看一下不要拿网上教程的参数硬套。如果三个入口全部跑通恭喜环境这关过了。接下来才到真正有意思的部分——它到底怎么判定一个人「疲劳」或「分心」。4. 核心判定链路从一帧画面到一次报警的全过程这套系统的技术栈是深度学习加传统 CV 混合YOLOv5 是深度学习目标检测DeepSORT 是多目标追踪而疲劳检测的 EAR 计算是典型的传统计算机视觉算法。三个模块各管一段配合起来才构成完整的预警链路。4.1 疲劳检测68 点关键点与 EAR 眼纵横比疲劳检测不依赖 YOLOv5独立走 dlib 的人脸关键点。dlib 的 68 点模型会在人脸区域标出 68 个关键点其中右眼是第 36 到 41 点左眼是第 42 到 47 点。每只眼睛 6 个点用它们算一个叫 EAREye Aspect Ratio眼纵横比的标量。EAR 的核心思想是眼睛睁开时上下眼睑的距离相对较大闭眼时上下眼睑几乎贴在一起。计算方式如下import dlib def calculate_ear(eye_points): # eye_points 是 dlib 返回的 6 个关键点按 36~41右眼或 42~47左眼顺序排列 p1, p2, p3 eye_points[0], eye_points[1], eye_points[2] p4, p5, p6 eye_points[3], eye_points[4], eye_points[5] # 垂直距离上下眼睑两对点的欧氏距离 vertical_1 abs(p2.y - p6.y) vertical_2 abs(p3.y - p5.y) # 水平距离眼角的水平跨度 horizontal abs(p1.x - p4.x) if horizontal 0: return 0.0 # EAR (垂直距离之和) / (2 * 水平距离) ear (vertical_1 vertical_2) / (2.0 * horizontal) return ear逻辑说明EAR 的分子是两对上下眼睑关键点的垂直距离分母是眼角水平距离。眼睛睁得越大垂直距离越大EAR 越高闭眼时垂直距离趋近于零EAR 就掉到一个很小的值。正常睁眼时 EAR 通常在 0.25~0.35 区间闭眼时掉到 0.2 以下。参数说明人脸关键点坐标来自 dlib 的 shape 对象每个点有 x 和 y 属性。实际工程里要对左右眼各算一次 EAR 再取平均因为人眼在自然状态下存在微小的非对称。注意 dlib 返回的坐标是整数像素眼睛较小时量化误差会被放大所以宁愿把脸裁剪放大再送关键点模型也不要直接在小图上硬算。只靠单帧 EAR 判定疲劳会误报——正常眨眼也会让 EAR 短暂下降。工程上的做法是加一个「连续闭眼帧数」计数器# 疲劳判定EAR 低于阈值并持续超过 FRAME_LIMIT 帧才触发报警 EAR_THRESHOLD 0.21 # 闭眼判定阈值低于它认为眼睛处于闭合状态 FRAME_LIMIT 3 # 连续闭眼超过 3 帧约 0.1 秒视为一次眨眼或瞌睡 def judge_fatigue(ear, closed_counter): if ear EAR_THRESHOLD: closed_counter 1 # 闭眼帧计数器累加 else: closed_counter 0 # 眼睛睁开清零 if closed_counter FRAME_LIMIT: return True, closed_counter # 触发疲劳报警 return False, closed_counter逻辑说明正常眨眼持续约 0.1~0.15 秒按 30 帧视频算也就 3~5 帧阈值取 3 帧会偏灵敏我一般取 5 帧左右。真正的瞌睡闭眼通常持续 1 秒以上连续几十帧维持在低 EAR是非常明显的长下跌。FRAME_LIMIT 的值要根据视频帧率换算帧率越高同样的闭眼时长对应的帧数越多。参数说明EAR_THRESHOLD0.21 是经验值受人的眼型影响很大。有人天生眼睛小睁眼 EAR 只有 0.22取 0.21 会不停误报。跑自己数据前先统计一段正常驾驶视频的 EAR 分布把阈值定在「睁眼 EAR 最小值往下留 15% 余量」的位置后面第 6 章有具体做法。4.2 分心行为分类YOLOv5 输出的类别怎么映射成报警疲劳检测管瞌睡分心行为分类管「手离开方向盘、眼睛不看路」。这部分完全交给 YOLOv5它在一帧画面上输出多个目标框每个框带类别索引和置信度。真正决定报警语义的是把类别索引翻译成行为名的那张映射表。用 2.2 节打印出来的 model.names假设是{0: normal, 1: phone, 2: smoke, 3: drink}那么后处理逻辑通常是这样的# mydetect.py 里对检测结果的常见后处理 def parse_detections(results, class_names, conf_threshold0.45): # results.pandas().xyxy[0] 是单帧的检测框表格每行一个目标 alerts [] for _, row in results.pandas().xyxy[0].iterrows(): cls int(row[class]) # 类别索引例如 1 代表 phone conf float(row[confidence]) # 置信度 if conf conf_threshold: label class_names[cls] bbox [row[xmin], row[ymin], row[xmax], row[ymax]] alerts.append({label: label, bbox: bbox, conf: conf}) return alerts逻辑说明results.pandas().xyxy[0] 是 YOLOv5 提供的便捷输出形式把检测结果整理成 pandas DataFramexmin/ymin/xmax/ymax 是边框像素坐标confidence 是置信度class 是类别索引。parse_detections 做的就是过滤低置信度框、把索引换成可读标签。如果类别命中 phone、smoke、drink 而同时 label 不是 normal报警逻辑就可以触发。参数说明conf_threshold 要和模型加载时的 model.conf 保持一致两处都过滤一遍是为了逻辑清晰——一处管模型输出一处管业务报警。实际项目中 label 是否报警要写成一个列表比如ALERT_CLASSES [phone, smoke, drink]normal 永远不报警。这样一个模块新增类别时只改列表就行不用动检测代码。4.3 DeepSORT 追踪给目标挂 ID消除重复报警如果只做单帧检测会出现一个很尴尬的情况驾驶员连续 10 秒打电话每帧都检测到 phone报警逻辑每帧都触发一次日志里刷出一百条重复记录。DeepSORT 的价值就在于给每个目标一个跨帧稳定的 ID让系统知道「这 100 帧里的电话是同一个人的同一个动作」。DeepSORT 的输入是当前帧的检测框和置信度输出是带 ID 的追踪结果# 把当前帧的检测框喂给 DeepSORT得到带稳定 ID 的跟踪结果 from deep_sort import DeepSort tracker DeepSort( model_pathweights/deepsort.pt, # ReID 特征模型用于跨帧身份匹配 max_dist0.2, # 级联匹配的最大余弦距离越小越严格 max_iou_distance0.7, # IoU 匹配阈值控制同一目标框的重叠要求 max_age30, # 目标丢失后最多保留 30 帧再删除 n_init3 # 新目标连续命中 3 帧才确认 ID ) def update_tracks(detections, frame): bboxes [d[bbox] for d in detections] confs [d[conf] for d in detections] outputs tracker.update(bboxes, confs, frame) # outputs 每行是 [x1, y1, x2, y2, track_id] return outputs逻辑说明DeepSORT 的核心是「检测-跟踪两级关联」。先用运动特征卡尔曼滤波预测下一帧位置和外观特征ReID 网络提取的行人/目标特征做级联匹配再用 IoU 做最后兜底。tracker.update 每次接收检测框和原图返回的每个目标都带 track_id同一目标在不同帧里 ID 相同。报警模块只要记住「某个 ID 已经报过 phone」后续帧里相同 ID 相同类别就不再重复报只在动作持续超过设定时长后升级报警。参数说明max_age 决定目标短暂被遮挡后是否继续保留 ID。车内场景驾驶员不会消失但转头时人脸和手部特征可能被遮挡max_age 取 25~30 比较合理。n_init3 意味着一个新目标连续出现 3 帧才确认 ID避免单帧误检出一个短命 ID 造成报警抖动。如果发现 ID 频繁切换、一个目标被当成多个优先调大 max_age而不是动检测阈值。5. 避坑与排查复现这套源码时最容易翻车的五个位置整套源码我在不同机器上复现过几次环境、依赖、数据路径的坑基本都踩过。下面五条是最常见的翻车记录按「现象、原因、解决」的格式写每条都是血泪经验。5.1 dlib 装不上出现 Microsoft Visual C 14.0 is required现象pip install dlib执行到一半报错提示需要 Visual C Build Tools或者 CMake 编译中断装了一个小时都没成。原因dlib 是 C 库pip 在没有预编译 wheel 的情况下会走源码编译Windows 上必须有完整的 MSVC 工具链和 CMake 才能编过。很多机器只装了 Python没装 Visual Studio Build Tools。解决锁版本并优先找预编译 wheel。Python 3.9 对应 cp39 的 dlib 19.22.1 有现成 wheel执行pip install dlib19.22.1用国内镜像源基本一次过。如果还是编译临时装上 Visual Studio Build Tools 里的「C 桌面开发」组件再重试。不要装最新版 dlib最新版在部分平台只有源码包。5.2 torch 版本不匹配报错Upsample object has no attribute recompute_scale_factor现象启动 mydetect.py 后直接崩traceback 最后一行是这个 AttributeError看起来完全不像项目代码的问题。原因YOLOv5 旧版源码在 nn.Upsample 上调用了 recompute_scale_factor 接口新版 PyTorch 把这个接口删了。pip 装 torch 时默认装最新版和旧版 YOLOv5 源码不兼容。解决把 torch 锁到 1.10.0 或 1.11.0对应 torchvision 0.11.0 或 0.12.0装完确认一下版本再跑。想用新 torch 也行但要同步升级整个 yolov5 源码目录工作量更大毕设场景不值当。5.3 检测标签和训练类别对不上box 框对了但名字全错现象检测框位置很准但标签乱跳把正常驾驶框成打电话把喝水框成抽烟。模型看起来「疯了」。原因YOLOv5 模型输出的类别索引是纯数字报警逻辑里写死的类别列表顺序和 best.pt 训练时的数据集类别顺序不一致。比如训练时类别 1 是 smoke报警代码里ALERT_CLASSES[1]写的却是 phone所有结果整体错位。解决永远以 model.names 为准。推理前先print(model.names)打印真实映射然后照着这张表改报警逻辑里的类别列表。这是最隐蔽的坑因为从界面上看只是标签错位不对比根本发现不了。5.4 PyQt5 界面卡死点击「开始检测」后窗口转圈无响应现象main.py 能启动窗口能打开但一点开始检测就卡住窗口拖不动也关不掉只能强制结束进程。原因检测循环跑在了 Qt 的主线程里。PyQt5 的主线程要不停处理界面刷新和鼠标事件检测推理耗时较长主线程被阻塞界面就失去响应。解决把检测放 QThread 工作线程主线程只接收结果并更新界面。最小改法是自定义一个继承 QThread 的 Worker 类run 方法里跑 process_frame 循环通过信号把结果发回主线程刷新 Label。如果短期不想动代码至少保证检测源是本地视频而不是摄像头减少帧率压力。5.5 GIF 与视频混用cv2.VideoCapture 读 gif 只出第一帧现象用 images/1620298460804.gif 作为测试输入程序只处理了第一帧后面全是黑屏或空检测而换 MP4 完全正常。原因OpenCV 的 VideoCapture 对 GIF 支持很差GIF 本质上是帧序列但不是标准视频编码VideoCapture 读 GIF 通常只解出第一帧。解决测试素材统一用演示视频 MP4。非要跑 GIF 的话用 PIL 逐帧读再转 numpy 数组或者提前把 GIF 转成 MP4ffmpeg -i 1620298460804.gif out.mp4。我的习惯是调试时只用 MP4少给自己添堵。提示以上五条是按出现频率排的第 5.2 和第 5.3 最容易骗人因为它们报错的位置和真实原因隔得很远。遇到诡异问题先打印版本和 model.names这两步能筛掉一半以上的坑。6. 进阶调优把阈值、报警延迟和输出形式改成你的参数跑通只是起点真实落地要面对的是「你的驾驶员、你的摄像头、你的报警方式」。我按参数调整、报警去抖、输出定制三块讲每块都给你可直接替换的数值和思路。先看阈值矩阵。这套系统里值得调的参数就三个层级参数默认值调低后果调高后果建议YOLOv5 conf0.45漏报减少、误报增加误报减少、漏报增加0.3~0.5 区间试EAR 阈值0.21更难判疲劳、漏报更容易误报按自己眼型统计定连续闭眼帧数3~5反应快但误报高反应慢但更稳30FPS 取 5~6EAR 阈值的标定方法很简单录一段自己正常驾驶的视频统计所有睁眼帧的 EAR 最小值然后往下留 15% 的余量。比如统计出来最低是 0.24阈值就定 0.24 × 0.85 ≈ 0.2。这套做法是我验收疲劳检测模型时必走的流程比拍脑袋取 0.2 靠谱得多。然后是报警去抖。单帧报警会刷屏我这里用过最有效的方案是「类别 ID 时间窗」三重去抖# 报警去抖同一 ID 同一类别在 COOLDOWN 秒内只报一次 last_alert {} # key: (track_id, label)value: 上次报警时间戳 COOLDOWN 5.0 # 冷静时间单位秒 def should_alert(track_id, label, now): key (track_id, label) if now - last_alert.get(key, 0) COOLDOWN: last_alert[key] now return True return False逻辑说明每次检测到危险行为先取 track_id 和 label 拼成 key查上次报警时间。5 秒内同一个人的同一个动作不再重复触发只有持续超过 5 秒才再次报既保留持续性信息又不刷屏。这个字典结构简单放内存足够毕设阶段不用上数据库。输出形式方面界面报警是最基础的。我建议在 ui_mainwindow 里加一个状态栏文字变化同时在命令行打日志[ALERT] track 3: phone, conf0.82。有条件的可以加声音提示用 winsound 或 QSound播放一段 1 秒的 beep注意别把报警声做成长鸣长鸣会盖过驾驶环境里的其他声音。最后说验证方法。改完任何阈值我的标准动作是拿一段 30 分钟连续驾驶视频人工标注出危险行为出现的时间段再跑一遍程序统计误报和漏报次数。误报率超过 5% 就把阈值往严调漏报超过 5% 就放宽。不要用几秒钟的演示视频验证样本太少调出来的参数在长视频上必然翻车。从那以后我每次改完阈值都会强制走一遍 30 分钟基准视频对照标注统计报表数字过了才敢交付。这套流程看着笨但能救命的恰恰是它——希望帮到你。本文还有配套的精品资源点击获取