
简介一套基于PyQt、YOLOv5与Dlib的驾驶员行为监控系统课程设计资源包适合高校计算机、人工智能相关专业学生完成课程设计或毕业设计使用。资源从图形界面搭建、实时视频流处理到疲劳与分心行为识别均有完整实现既有摄像头画面捕获、人脸关键点定位、闭眼与低头状态判断也有声音警报触发、阈值设定和日志记录等模块代码可以直接运行也便于在此基础上做二次开发。包体共105个文件以Python源码66个py为主体同时包含界面文件、模型权重、配置文件、音频提示与说明文档模型涵盖YOLOv5权重、Dlib人脸关键点模型及分类映射表压缩包整体约224MB目录结构清晰便于按功能模块查找。目前已有272人学习下载。借助Dlib提供的68点人脸关键点检测器使用者可以系统掌握YOLOv5目标检测与关键点定位结合应用的完整流程并结合OpenCV完成视频帧预处理通过阈值判定、多行为联合分析构建一套可演示、可答辩的驾驶员状态监测原型资源也预留了可扩展接口适合继续加入新行为识别或与车载系统集成。1. 基于PyQt YOLOv5 dlib的驾驶员行为监控系统到底在监控什么刚开始做驾驶员行为监控课设的人很容易在逆光和偏头场景里被OpenCV自带的检测器搞得怀疑人生。这套基于PyQt YOLOv5 dlib的驾驶员行为监控系统之所以常被选为课程设计是因为它把问题拆得清楚YOLOv5在画面里找出人脸、手机、香烟等目标dlib在人脸区域内提取68个关键点用EAR/MAR等几何特征判断闭眼和打哈欠PyQt5负责把这套逻辑包进一个能显示视频流、检测框和报警状态的界面。两个模型并行工作、结果互补既能覆盖疲劳检测又能扩展到分神、吸烟、打电话等行为识别这也是它在求职项目中一直有热度的原因。作为课程设计它不需要做到企业级的精度但必须让人看到“每个组件为什么选它、数据在模块间怎么流动、参数调整怎么验证”。2. YOLOv5与dlib协作的数据流从检测框到疲劳判定的算法基线2.1 YOLOv5检测类别与驾驶员行为目标的取舍在这套系统里YOLOv5不是拿来检测人脸的唯一途径它更合适的定位是“宽泛场景的目标识别”。如果你只想判断驾驶员有没有闭眼dlib的关键点已经足够但一旦要检测“手里拿着手机”“正在吸烟”“视线偏离前方”单靠关键点就不够了。所以常见做法是让YOLOv5同时检测人脸、手机、香烟、方向盘等目标。课程设计一般不会从头训练一个复杂模型而是先用yolov5官网下载的预训练权重比如yolov5s.pt在摄像头画面上跑通用80类检测。COCO类别里包含person、cell phone、remote等开箱就能识别手机但“香烟”并不在COCO类别里这就需要在后处理里做二次判断或者干脆自己标注“smoking”这一类的数据。这也引出了第一个设计决策行为判定应该基于YOLOv5的类别置信度还是基于位置关系举个例子手机App的检测框和司机手部的检测框重叠面积超过一定比例才判定为“使用手机”单纯检测到手机放在副驾驶座上不应报警。这个规则用IoUIntersection over Union或者归一化中心点距离来计算即可。课程设计阶段建议先不做复杂的姿态估计而是用2-3个检测框的几何关系来构造行为特征这样既能体现YOLOv5的价值又不会把工程难度推到不可控。2.2 基于dlib的EAR与MAR疲劳特征计算YOLOv5给出了“目标在哪”dlib则负责回答“人看起来是否疲劳”。dlib提供的人脸检测器和人脸关键点模型shape_predictor_68_face_landmarks.dat输出68个关键点。左眼用点36到41表示右眼用点42到47表示嘴巴用点49到68表示。EAREye Aspect Ratio就是眼睛纵横比定义是垂直方向两点距离的均值除以水平方向两点距离MARMouth Aspect Ratio则用同样的思想衡量嘴的张合幅度。计算EAR的代码如下import dlib import cv2 import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) def eye_aspect_ratio(eye_points): # eye_points 是6个关键点坐标组成的numpy数组 A np.linalg.norm(eye_points[1] - eye_points[5]) B np.linalg.norm(eye_points[2] - eye_points[4]) C np.linalg.norm(eye_points[0] - eye_points[3]) return (A B) / (2.0 * C) def mouth_aspect_ratio(mouth_points): # 嘴巴外轮廓点取内侧两点计算张合幅度 D np.linalg.norm(mouth_points[13] - mouth_points[19]) E np.linalg.norm(mouth_points[15] - mouth_points[17]) F np.linalg.norm(mouth_points[14] - mouth_points[18]) return (D E) / (2.0 * F)这里的坐标顺序需要与dlib的68点索引严格对应。实际工程中不能每帧都对整幅图像跑一次dlib人脸检测器那会很慢正确做法是先用YOLOv5给出的人脸框作为ROI再在ROI里调用dlib检测器或者直接用关键点预测器。这样既能减少计算量又能缓解倾斜和遮挡带来的漏检。EAR与MAR判定疲劳的核心逻辑是当EAR连续多帧低于阈值时认为正在瞌睡当MAR连续多帧高于阈值时认为在打哈欠。代码里通常还需要一个眨眼计数器避免把正常眨眼误判为闭眼。2.3 判定阈值怎么定从课程设计到真实驾驶的差异阈值设置是这类系统的灵魂。课程设计阶段可以给出一个静态阈值表但这并不代表它可靠。常见做法是统计一段时间内正常睁眼、正常说话时的EAR/MAR基线然后以基线均值的70%或85%作为动态阈值。下面给出一个参考配置行为特征计算指标常见阈值区间判定条件闭眼/瞌睡EAR0.18 - 0.25EAR低于阈值连续20帧以上打哈欠MAR0.55 - 0.70MAR高于阈值连续15帧以上低头/姿态异常头部俯仰角-20°到-40°俯仰角超出基线±15°使用手机IoU(手机框, 手框)大于0.35持续时间大于3秒吸烟检测到Smoking类或烟与手框重叠置信度大于0.45连续检测超过1秒表中的低帧数要求是为了滤除眨眼和瞬间张嘴。如果你拿到的摄像头是15fps连续20帧意味着1.3秒左右比较合理但如果是Jetson Nano这类设备跑起来只有5-8fps同样的帧数就对应2.5秒以上响应就太慢了。所以“基于pyqt yolov5 dlib的驾驶员行为监控系统”在课程设计答辩中老师最常问的问题就是“你这个阈值为什么这样设”回答的重点应该是先定语义时间比如闭眼超过1.2秒报警再根据实际帧率换算成帧数。真实驾驶场景还要考虑白天逆光、夜间红外补光、佩戴墨镜等因素静态阈值很容易失灵。课程设计里做到“有基线、有时间窗口、有可调参数”就已经领先大部分同类作业了。再补充一下数据流的组织YOLOv5输出的检测框是缩放回原图尺度后的像素坐标dlib的输入通常是RGB图像OpenCV读出来的是BGR因此在做ROI裁剪时要记得通过cv2.cvtColor转换色彩空间否则关键点容易跑偏。很多人在集成测试时发现dlib检测的眼部位置总是偏下一点十有八九是颜色通道顺序问题。3. 环境配置与最小推理链路YOLOv5官网下载、dlib安装和摄像头检测3.1 yolov5环境配置torch、CUDA与官方仓库拉取作为一个课程设计环境配置往往比代码本身更消耗时间。yolov5环境配置的第一步是根据本机显卡驱动版本选择CUDA和PyTorch。如果机器有NVIDIA显卡且驱动已经装好在命令行里执行nvidia-smi看右上角支持的CUDA版本再去PyTorch官网选对应版本安装如果只有CPU也可以直接用CPU版PyTorch跑yolov5s一帧300毫秒左右足够演示。然后是拉取YOLOv5官方仓库。标准的做法是从yolov5官网下载源码压缩包或者用git clone拉取。不要直接去下载别人二次修改过的包否则后续训练自定义数据集的yaml文件格式会跟官方脚本不匹配。conda create -n drive python3.9 -y conda activate drive pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里用Python 3.9是因为它是YOLOv5官方仓库做回归测试较多的版本不容易出现依赖冲突。requirements.txt里包含opencv-python、numpy、matplotlib等基础库安装完成后先跑一个快速验证python detect.py --source data/images/bus.jpg --weights yolov5s.pt --conf 0.5。如果能看到输出图片上的检测框说明yolov5环境配置成功。这里出现的--weights yolov5s.pt会自动下载预训练权重如果下载速度不理想就需要提前把yolov5s.pt放到根目录并设置--weights指向本地路径。3.2 dlib库安装的常见坑与验证dlib库安装比YOLOv5更依赖编译环境。在Windows上直接pip install dlib经常会因为缺少CMake和VS Build Tools而报错。建议先执行pip install cmake再安装Visual Studio的C开发组件最后重新执行pip install dlib。如果你用的是Anaconda也可以尝试conda install -c conda-forge dlib它会自动带上cmake。dlib下载完成后最大的问题是找不到shape_predictor_68_face_landmarks.dat这个权重文件需要单独下载官方和第三方都有提供。下载后不要放在带中文的路径下dlib的read函数在部分Windows版本下对非ASCII路径处理得不好。故障现象原因处理方式dlib安装报错缺少CMakepip install cmake 后重试import dlib失败依赖库冲突使用conda独立环境隔离.dat文件读取失败路径包含中文移到纯英文路径重新加载验证dlib安装是否成功可以用下面这段代码import dlib detector dlib.get_frontal_face_detector() print(dlib version:, dlib.__version__) print(detector:, detector)如果能正常打印版本号说明dlib库安装完成。但注意这里加载的关键点模型后续会用到你需要确认shape_predictor能正常读取.dat文件否则在运行阶段才会爆出读取失败的RuntimeError那时候排查成本更高。建议在验证脚本中连模型一起加载predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat)注意模型权重文件路径不要包含中文dlib在部分Windows版本下可能无法读取。3.3 用YOLOv5跑通第一帧摄像头检测摄像头检测与图片检测的不同点在于你需要处理实时视频流。YOLOv5的detect.py本身支持--source 0调用本机摄像头但课程设计通常不会直接这样用因为detect.py做了太多与界面无关的封装。更常见的做法是把YOLOv5作为模块调用加载模型后对摄像头帧推理。下面的代码演示了最小推理链路import cv2 import torch model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break results model(frame) rendered results.render()[0] cv2.imshow(yolov5, rendered) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这里先用torch.hub.load加载预训练模型yolov5s是最常用的轻量版本在普通笔记本上可以达到20-30fps。model(frame)返回的是一个Results对象.render()[0]会在原图上绘制检测框。逻辑说明这段代码没有做任何预处理或后处理YOLOv5内部会完成letterbox缩放和NMS所以输入原图尺寸即可。参数说明cap.read()返回的帧是BGR格式YOLOv5的推理接口内部会自动转换颜色空间所以你不需要手动改。但如果后续要同时喂给dlib就一定要把这一帧先保留一份改成RGB再传给dlib。3.4 dlib人脸关键点与YOLOv5检测框的坐标对齐把两个模型串起来的第一步是让检测框坐标对齐。YOLOv5的Results对象里包含xyxy格式的坐标单位是原图像素可以直接用来裁剪人脸区域。由于dlib的关键点预测器接受的是Dlib的rectangle对象或简单的左上右下坐标我们需要在调用前把YOLOv5的坐标转成整数并做边界裁剪防止越界。示例import dlib boxes results.pandas().xyxy[0] for _, row in boxes.iterrows(): if row[class] 0 and row[confidence] 0.5: x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) face_rect dlib.rectangle(x1, y1, x2, y2) shape predictor(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), face_rect) # shape.num_parts 应该等于68后续可以提取眼睛和嘴巴坐标这里的row[class] 0指的是COCO数据集里的person类别如果你自己训练了人脸类别就替换成对应的类别ID。坐标对齐的坑主要来自两点一是YOLOv5的高置信度人脸框可能略微大于人脸边缘没关系dlib的关键点预测器有一定容错二是当画面中有多个人时只会对检测到的每一个人都预测关键点你需要用row[confidence]和与驾驶位的相对位置过滤掉后排乘客。课程设计里通常只取画面中心面积最大的那个face框作为目标驾驶员。4. PyQt5界面集成视频流显示、报警事件与多线程处理4.1 PyQt5里用QThread跑YOLOv5推理避免界面卡死PyQt5的GUI主线程不能执行耗时操作而YOLOv5的推理和dlib关键点计算加起来一帧需要100-200毫秒如果直接在主线程跑界面会一直无响应用户会以为程序卡死了。常见做法是写一个继承自QThread的工作类负责读取摄像头、调用模型、发出图像信号和事件信号。下面是一个最小工作线程的骨架class DetectThread(QThread): frame_signal pyqtSignal(object, object) # 原始帧、绘制后帧 event_signal pyqtSignal(str, bool) # 事件名、是否触发 def __init__(self): super().__init__() self.running True self.model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) self.predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) def run(self): cap cv2.VideoCapture(0) while self.running: ret, frame cap.read() if not ret: continue # 这里串联YOLOv5 dlib检测逻辑 annotated frame self.frame_signal.emit(frame, annotated) cap.release()信号定义在类体中pyqtSignal(object, object)的两个object参数可以传任意类型PyQt5不会帮你做类型检查。run方法里的while循环会持续读取摄像头遇到无法读帧时就跳过。这个线程不负责显示图像只把结果通过信号发回主线程。这样做的好处是界面始终能响应用户点击关闭按钮同时event_signal可以随时触发报警而不必等到一帧完整处理完。4.2 信号槽把检测结果和报警事件推送到界面主窗口只需要连接信号槽不需要关心检测细节。假设主界面里有一个QLabel用于显示视频一个QTextEdit用于打印日志。在MainWindow.__init__里做如下连接self.detect_thread DetectThread() self.detect_thread.frame_signal.connect(self.update_frame) self.detect_thread.event_signal.connect(self.handle_event) self.detect_thread.start() def update_frame(self, raw, annotated): rgb_image cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) qt_image QImage(rgb_image.data, rgb_image.shape[1], rgb_image.shape[0], rgb_image.strides[0], QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(qt_image)) def handle_event(self, event_name, active): if active: self.log.append(f[{time.strftime(%H:%M:%S)}] {event_name}) self.status_label.setText(event_name)update_frame里的重点是rgb_image.strides[0]这是图像每行的字节数QImage的构造函数需要它来正确解析内存对齐。如果你直接省略这个参数在分辨率变化时画面可能会扭曲。handle_event先判断事件是否激活激活时往日志控件里追加一条带时间戳的记录同时更新状态栏。参数说明frame_signal里的两个object如果只用于显示只传一个annotated也可以保留raw的目的是后续做视频回放或帧缓存。4.3 报警策略与日志记录报警策略不能只依赖单帧结果至少要做一个滑动窗口统计。一个实用的做法是在工作线程里维护一个collections.deque每次检测完记录当前行为状态当某个行为在最近N帧中出现的比例超过阈值时再通过event_signal发出报警。这样能避免摄像头偶发的一帧误检导致界面频繁闪烁。报警触发的动作可以是显示红色边框、播放声音、或者写入CSV日志。日志字段建议包含时间、事件名、EAR值、YOLOv5置信度这样答辩时可以展示这些中间指标来证明系统不是简单的“套一个模型”。环节线程归属耗时估算优化方式摄像头读取DetectThread5-10ms降低分辨率到640x480YOLOv5推理DetectThread60-150ms使用TensorRT或半精度FP16dlib关键点DetectThread5-20ms用YOLOv5人脸框作为ROI图像缩放与转换DetectThread3-8ms避免重复分配numpy数组表格里的耗时是按普通笔记本虚拟机估计的如果部署到Jetson NanoYOLOv5推理时间会翻2-3倍。优化方向上先把YOLOv5的检测帧率限制在每两帧处理一次然后dlib每帧都做因为关键点相对便宜。这里要注意工作线程里所有的OpenCV图像都要避免被主线程同时访问PyQt的信号槽是队列连接会自动做一次拷贝所以即使主线程在绘制工作线程也可以继续写入新的annotated帧不会有数据竞争。5. 训练自己的数据集提升行为识别YOLOv5训练流程与验证技巧5.1 从COCO预训练到自定义驾驶员行为数据集的迁移如果需要识别“吸烟”或“低头看手机”这种COCO类别里没有的行为最可靠的方式是用YOLOv5训练自己的数据集。首先是标注用labelImg或labelme把视频帧里的目标框成YOLO格式的txt文件类别从0开始。数据集比例可以按训练集:验证集:测试集7:2:1划分。训练命令示例python train.py --data drive_dataset.yaml --weights yolov5s.pt --epochs 50 --batch-size 8 --img 640其中drive_dataset.yaml需要配置train、val和nc、names。这里--img 640表示把输入图像缩放到640x640像素--batch-size 8在8G显存的显卡上比较可靠。在yolov5超参数上课程设计不需要追求完全收敛50轮一般就能看到明显效果。需要监控的训练指标是mAP0.5建议训练过程中打开--cos-lr让学习率按余弦曲线下降能减少震荡。5.2 验证技巧用视频回放定位误报根因训练完成后不要只看测试集指标真正的坑往往在连续视频流里。一个非常有效的验证技巧是录制一段5分钟的模拟驾驶视频把YOLOv5的检测结果和dlib的EAR值同时画到视频上导出后再逐帧回放。当你看到误报时先别急着调阈值而是记录误报发生前后的EAR曲线和置信度曲线。如果某次“闭眼报警”其实只是侧脸EAR本身也在波动那问题出在dlib关键点漂移如果EAR值没有明显掉下来但报警触发了那问题就在你的判断逻辑里没把连续帧数算对。这个验证方法的力量在于它把模型性能和决策逻辑分开调试比直接调一个小数点后两位的阈值高效得多。本文还有配套的精品资源点击获取