ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLOv5与dlib结合的驾驶员疲劳检测系统设计与实现

YOLOv5与dlib结合的驾驶员疲劳检测系统设计与实现 简介这是一套面向计算机视觉课程设计、毕业设计及 PyQt 入门者的驾驶员行为监控系统完整工程。系统以 PyQt5 搭建可视化界面联合 YOLOv5 完成人脸与目标实时检测并借助 Dlib 人脸关键点模型定位眼部、嘴部特征进而识别闭眼、低头、分心驾驶等危险状态在设定阈值后触发声音与界面告警。资源包共 105 个文件、约 224MB包含 66 个 Python 源码、4 个 UI 界面文件、训练权重.pt/.hdf5、Dlib 模型文件.dat、yaml 配置、报警 mp3 音频以及 run_torch 等一键启动脚本目录划分清晰涵盖视频采集、OpenCV 预处理、模型推理、GUI 展示、告警触发与日志记录完整链路。已有 273 人学习下载适合希望掌握深度学习与桌面应用集成、需要快速搭建可演示课程设计原型的读者也可作为扩展更多驾驶员状态识别功能的起点。系统整体结构完整适合用于课程设计答辩与演示。1. 驾驶员行为监控系统到底在做什么课程设计版的功能边界如果你接过基于PyQt、YOLOv5、dlib的驾驶员行为监控系统这类课题第一反应多半是又是一个把三个热门词拼在一起的课设题目。但真把它拆开你会发现它其实是一条完整的视觉处理流水线dlib负责框出人脸并定位68个关键点YOLOv5负责检测方向盘、手机、香烟这类物体PyQt则把这两路检测结果汇总成实时警告界面。课程设计要的不是一个能上车的产品而是你能把这套流程跑通、讲清楚、调出合理参数。常见做法是只取疲劳驾驶作为核心闭眼、打哈欠由dlib关键点判玩手机、抽烟交给YOLOv5判最后用PyQt弹出疲劳等级。这套方案适合自动化、计算机、软件工程专业的课设或毕设硬件成本只要一台带摄像头的电脑难点不在模型训练而在两套模型如何协同、如何不互相拖垮帧率。我见过太多人把时间耗在训练自定义YOLOv5模型上结果界面还停在草稿阶段——这个方向最现实的做法是把YOLOv5当作开箱即用的检测器把精力留给逻辑层和UI层。2. 从课题到可运行系统用YOLOv5与dlib搭出最小检测框架拿到课题后建议先画一张数据流图摄像头帧先分两路一路送YOLOv5做目标检测另一路送dlib做关键点定位两条线程的结果在PyQt主线程里汇合再按判定规则输出警告。很多课设翻车就翻在一路跑完再跑另一路帧率直接降到个位数答辩演示时卡成PPT。这一章先解决单帧处理和组合策略界面放在下一章。2.1 为什么是这个组合两套模型的分工与选型理由YOLOv5的新版本在COCO预训练权重上能直接识别人、手机、瓶子等80类物体不需要你重新训练就能覆盖玩手机场景。但因为YOLOv5不做关键点回归你拿不到眼睛、嘴巴的精细坐标无法算EAR眼部纵横比和MAR嘴部纵横比。dlib恰好补这个缺口它的人脸检测器配合68点预测模型能在普通CPU上跑到20~30毫秒一帧足够支撑疲劳判定。选型上还有一层考虑dlib的HOG人脸检测对正面人脸很稳但对大幅转头比较敏感此时恰好可以把它当作分心检测的信号——连人脸都丢了说明驾驶员注意力大概率不在前方。这套分工比单独用YOLOv5做全套要少写大量训练代码也比纯dlib方案多了一层物体级检测能力是课设性价比最高的组合。如果机器性能允许也可以考虑用YOLOv8做替换但课设答辩时老师更关心你对为什么选这两者的解释而不是版本新旧。2.2 环境准备与依赖安装PyTorch、dlib与PyQt5的版本配平环境配置是第一个大坑。建议用Python 3.8或3.9配合PyTorch 1.13左右dlib用19.24.x版本。dlib在Windows上直接用pip install dlib经常报CMake编译错误常见做法是先安装Visual Studio Build Tools的C生成工具或者直接使用预编译的wheel包。PyQt5用5.15.x版本即可注意Python 3.10以上某些PyQt5旧版本会缺少对应cp310的wheel。# 建议顺序执行避免依赖冲突 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install dlib19.24.0 pip install pyqt55.15.9 pip install opencv-python4.8.0.76 pip install pandas numpy逻辑说明先装PyTorch是因为它体积最大且YOLOv5会从torch.hub拉取模型dlib单独装能第一时间暴露编译问题。--index-url指定CUDA 11.7的轮子源纯CPU机器可去掉这行但检测速度会明显下降。opencv-python的版本建议锁定4.8.x因为新版4.10在某些环境下与PyQt5的QImage转换存在字节序兼容问题。安装完成后运行python -c import dlib, torch, PyQt5; print(ok)做冒烟验证。2.3 加载YOLOv5检测器用torch.hub而不是克隆仓库很多教程会让你git cloneYOLOv5仓库这在课程设计里其实不必要。直接用torch.hub加载官方权重代码更短且依赖更少。检测器的封装要留出置信度阈值和IOU阈值两个接口因为玩手机检测与普通物体检测的最佳阈值不一样。import torch class YOLOv5Detector: def __init__(self, weightsyolov5s.pt, conf_thres0.4, iou_thres0.5): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights, force_reloadFalse) self.model.conf conf_thres self.model.iou iou_thres self.class_names self.model.names def detect_objects(self, frame_bgr, target_classes(67,)): 返回目标物体中心点列表默认只检测手机COCO类别67 results self.model(frame_bgr, size640) detections [] for *xyxy, conf, cls in results.xyxy[0].tolist(): if int(cls) in target_classes and conf self.model.conf: x_center (xyxy[0] xyxy[2]) / 2 y_center (xyxy[1] xyxy[3]) / 2 detections.append({ class: self.class_names[int(cls)], confidence: conf, center: (x_center, y_center), box: [int(v) for v in xyxy] }) return detections逻辑说明torch.hub.load会从ultralytics仓库加载模型定义custom参数指向本地权重文件。force_reloadFalse表示本地缓存存在时直接用反复加载权重会造成启动卡顿。results.xyxy[0]是形状为[N, 6]的Tensor每行依次是x1、y1、x2、y2、置信度、类别ID。target_classes(67,)对应COCO数据集里cell phone这一类别如果以后要扩展检测香烟就把类别ID加进元组比如(67, 0)加人(67,)保持只检测手机。这里故意不放缩放到results.pandas().xyxy是因为Tensor格式在循环里取值速度更快。2.4 dlib人脸关键点定位封装68点检测器并计算EARdlib和OpenCV的人脸框坐标体系都是左上角宽高可以直接互通。dlib需要一张灰度图检测到人脸后用shape_predictor输出68个关键点常见做法是把关键点数组转成NumPy矩阵便于后续计算欧氏距离。注意dlib的检测器对小分辨率人脸不敏感如果摄像头距离超过1.5米人脸像素面积太小容易出现时检测到时检测不到的现象。import dlib import numpy as np import cv2 class DlibLandmarkDetector: def __init__(self, predictor_pathshape_predictor_68_face_landmarks.dat): self.detector dlib.get_frontal_face_detector() self.predictor dlib.shape_predictor(predictor_path) def get_landmarks(self, frame_bgr): gray cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY) faces self.detector(gray, 1) if len(faces) 0: return None, None # 取面积最大的人脸避免远处小脸干扰 main_face max(faces, keylambda r: r.width() * r.height()) shape self.predictor(gray, main_face) points np.array([(p.x, p.y) for p in shape.parts()]) return main_face, points staticmethod def eye_aspect_ratio(landmarks, eye_indices): EAR 垂直距离均值 / 水平距离闭眼时数值趋近于0 left, right eye_indices[0]], eye_indices[1] vertical np.linalg.norm(landmarks[left[1]] - landmarks[left[5]]) \ np.linalg.norm(landmarks[left[2]] - landmarks[left[4]]) horizontal np.linalg.norm(landmarks[left[0]] - landmarks[left[3]]) return vertical / (2.0 * horizontal)逻辑说明get_frontal_face_detector(gray, 1)的第二个参数表示图像上采样次数设为1会先把图像放大一倍再检测小脸召回率更高但速度变慢。detector返回人脸矩形列表乘以宽高选出最大人脸是为了防止后排乘客的脸被错误当成驾驶员。eye_aspect_ratio中eye_indices是包含两个眼睛索引数组的元组左眼索引为[36, 37, 38, 39, 40, 41]右眼为[42, 43, 44, 45, 46, 47]代码里eye_indices[0]代表左眼。这个EAR计算方法源自论文的六个关键点方案正常平视大概在0.25~0.35闭眼时跌到0.1以下阈值通常设在0.2。3. 用PyQt5把检测结果变成可交互的监控界面模型层就绪后需要解决画面显示、警告弹窗、参数调整三件事。PyQt5在这里不只做界面它还要充当线程管理和事件分发的角色。常见错误是在主线程里跑while循环读摄像头导致界面假死。正确做法是把视频流和检测放进QThread通过信号把图像和检测结果发回主线程更新控件。3.1 界面布局与实时视频显示QThread、QImage与信号槽建议用三个主要区域左侧QLabel显示摄像头画面并叠加检测框右侧用QTableWidget展示当前检测状态人脸是否缺失、是否是疲劳、是否检测到手机下面放一个QPushButton用于开始/停止检测。画面不要直接在子线程里操作QLabel因为Qt的UI操作必须发生在主线程。子线程只管把图像转换成QImage后emit出去主线程的槽函数负责setPixmap。import sys import cv2 from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget class VideoThread(QThread): change_pixmap_signal pyqtSignal(QImage) detection_status_signal pyqtSignal(dict) def __init__(self): super().__init__() self.running True self.yolo YOLOv5Detector() self.landmark DlibLandmarkDetector() def run(self): cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while self.running: ret, frame cap.read() if not ret: continue status self.process_frame(frame) self.detection_status_signal.emit(status) rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qt_image QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.change_pixmap_signal.emit(qt_image.copy()) self.msleep(30) cap.release()逻辑说明change_pixmap_signal携带QImage是因为QPixmap不能在非主线程创建而QImage是值类型可以跨线程传递。qt_image.copy()很关键因为rgb_image.data指向的缓冲在下一帧会被覆盖不拷贝会出现画面闪烁或花屏。msleep(30)控制帧率到33fps左右留出时间给界面刷新。detection_status_signal用字典把是否疲劳是否分心等布尔值打包传递避免为每种状态单独定义信号。跑起来后如果界面卡顿优先检查是不是在主线程里调用了frame.read()而不是先怀疑模型太慢。3.2 叠加检测框与警告状态把YOLOv5和dlib结果画到同一帧上YOLOv5返回的坐标是相对于传入帧的像素坐标dlib的坐标也基于同一帧可以直接画在同一张图上。建议用不同颜色区分来源YOLOv5的物体框用红色dlib的人脸关键点用绿色圆点疲劳时把眼睛区域用黄色高亮。画完后把这一帧发给UI显示同时在状态栏用文字提示当前风险等级。def process_frame(self, frame_bgr): face_rect, landmarks self.landmark.get_landmarks(frame_bgr) objects self.yolo.detect_objects(frame_bgr, target_classes(67,)) status {face_detected: face_rect is not None, phone_detected: len(objects) 0, ear: 0.0, fatigue: False} if face_rect is not None: ear self.landmark.eye_aspect_ratio(landmarks, EYE_INDICES) status[ear] round(ear, 3) status[fatigue] ear EYE_CLOSE_THRESHOLD # 画人脸框 x, y, w, h face_rect.left(), face_rect.top(), face_rect.width(), face_rect.height() cv2.rectangle(frame_bgr, (x, y), (x w, y h), (0, 255, 0), 2) for point in landmarks: cv2.circle(frame_bgr, tuple(point), 1, (0, 255, 0), -1) for obj in objects: x1, y1, x2, y2 obj[box] cv2.rectangle(frame_bgr, (x1, y1), (x2, y2), (0, 0, 255), 2) label f{obj[class]} {obj[confidence]:.2f} cv2.putText(frame_bgr, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) return status逻辑说明process_frame返回的status字典通过信号发给主线程主线程里的QLabel和表格直接读这个字典刷新。EAR阈值EYE_CLOSE_THRESHOLD建议设为0.2但是注意不同人眼型差异很大戴眼镜和没戴眼镜的EAR基础值不一样。课程设计答辩时最好把阈值做成可调项否则评委戴上眼镜模拟测试时系统可能把正常眨眼误判成疲劳。如果你打算跑通疲劳检测之外的动作识别也可以把objects里yk的检测类别扩展成包含香烟类别ID为74但COCO里香烟样本少误检率会比手机高不少需要额外处理。4. 疲劳与分心判定逻辑参数怎么调才能真正可用检测模型只是眼睛判定逻辑才是大脑。很多系统的翻车点是检测到了但判定策略太蠢比如闭眼超过0.5秒就报警结果驾驶员正常眨眼也会闹脾气。这一章从状态机和时间窗口两个角度说明怎么设计判定流程让监测变成监控。4.1 疲劳判定不再靠单帧引入连续N帧窗口逻辑单帧EAR低于阈值只能说明那一刻闭眼不能说明疲劳。正常眨眼约100~400毫秒如果摄像头30fps一帧约33毫秒所以眨眼也就持续3~12帧。疲劳的特征是闭眼时间显著变长或者频繁打哈欠。常见做法是把最近30帧的EAR数据缓存起来统计其中低于阈值的帧占比超过某一比例才触发疲劳预警。这才符合眼睑闭合程度的PERCLOS疲劳评价标准。from collections import deque class FatigueJudge: def __init__(self, ear_threshold0.2, window_size30, closed_ratio0.3): self.ear_threshold ear_threshold self.window deque(maxlenwindow_size) self.closed_ratio closed_ratio def update(self, ear_value, face_missingFalse): if face_missing: self.window.append(1.0) # 无人脸按闭眼处理保守一点 else: self.window.append(1.0 if ear_value self.ear_threshold else 0.0) closed_ratio sum(self.window) / len(self.window) return closed_ratio self.closed_ratio, closed_ratio逻辑说明队列长度为30帧若30帧里有30%即9帧处于闭眼状态就判定为疲劳。face_missingTrue时记录为1.0是考虑到疲劳时驾驶员头部下垂容易脱离画面把它当成闭眼处理能减少漏报。window_size的调整逻辑帧率低时不宜设太大否则迟缓严重帧率稳定在30fps时30帧就是1秒窗口阈值0.3即累计0.3秒闭眼触发这个参数接近真实驾驶疲劳监测设备的标准。如果是在树莓派4b上部署YOLOv5帧率可能掉到10fps建议把window_size减到15否则要3秒才能触发一次报警。closed_ratio是核心超参数答辩时可以现场演示增大到0.6后会明显减少误报用来展示你对误报漏报权衡的理解。4.2 分心判定逻辑人脸跟踪丢失与目标检测持续性分心比疲劳更难定义。常见做法是同时监测三件事人脸框中心是否长时间偏离画面中心区域YOLOv5是否在连续帧中检测到手机人脸是否完全消失超过设定时间。人脸丢失这件事要小心处理——驾驶员偶尔低头看仪表盘也会丢脸一丢就报警会被当成狼来了。建议为丢脸设置独立阈值例如连续15帧约0.5秒无人脸才触发视线偏离警告。另一条主线是手机检测的去抖。YOLOv5单帧检测经常出现前五帧检测到、第六帧消失的闪烁现象原因是置信度在阈值附近抖动。常见做法是维护一个连续命中帧数计数器累计超过5帧才判定正在使用手机并使用更激进的低置信度阈值0.25来保证召回率。但低阈值也会带来误检所以还要加一个就近原则手机框中心与人脸框中心的距离必须在200像素内不然副驾驶玩手机会被误判成驾驶员玩手机。class DistractionJudge: def __init__(self, missing_threshold15, phone_threshold5): self.missing_frame_count 0 self.phone_hit_count 0 self.distracted False def update(self, face_rect, phone_detected, face_center_near_phoneTrue): if face_rect is None: self.missing_frame_count 1 self.phone_hit_count 0 else: self.missing_frame_count 0 if phone_detected and face_center_near_phone: self.phone_hit_count 1 else: self.phone_hit_count 0 if self.missing_frame_count 15 or self.phone_hit_count 5: self.distracted True else: self.distracted False return self.distracted逻辑说明missing_frame_count与phone_hit_count分两条计数互不干扰。face_center_near_phone需要由外部根据两个中心点的距离布尔值传入这样可以把几何判断与状态计数解耦。这里的阈值都是帧数而不是秒数答辩时记得强调监控系统里时间单位用帧是有意的设计因为不同机器帧率不同同样的秒数会导致报警灵敏度完全不同帧计数方案天然与帧率同步。如果摄像头在强光下出现严重拖影YOLOv5检测手机闪烁得更厉害此时把phone_threshold提高到8帧避免误报。4.3 头顶和掉线的边界缺帧、无人脸、摄像头被遮挡时的保底策略课程设计答辩现场最容易翻车的不是算法错而是摄像头被拔了、画面全黑、人脸角度太偏。典型场景评委坐得离摄像头很近把驾驶员人脸挡住系统瞬间判定为疲劳或分心触发连续报警场面一度失控。所以保底策略必须和正常人脸逻辑分开写。常见做法是维护一个数据健康状态枚举相机正常、相机断开、画面黑暗、无人脸超过5秒、人脸正常。这个状态不进疲劳判定逻辑而是直接显示在界面右上角的状态指示灯里。同时如果读取帧失败超过1秒应自动暂停检测线程并弹出提示而不是继续用上一帧数据硬算。dlib检测器在处理极端侧脸时会返回无人脸这不算BUG但要在界面上区分无人脸侧面和摄像头断开两种情况前者提示驾驶员后者提示系统管理员。def check_health(self, ret, frame_bgr, face_rect): if not ret: return camera_disconnected gray_mean cv2.mean(cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY))[0] if gray_mean 18: return scene_too_dark if face_rect is None: # 超过5秒无人脸由外部计数器决定是否报分心 return no_face_detected return healthy逻辑说明gray_mean 18这个阈值来自经验值正常室内环境灰度均值约80~150盖上镜头盖约5~10阳光直射可能超过200。阈值设18是为了避免夜间行车时系统崩溃因为夜间路灯下灰度均值也能有30~60。camera_disconnected状态下应停止后续检测模型的调用因为OpenCV会返回空帧导致dlib抛异常。课设验收时主动演示遮挡摄像头→系统提示环境异常比让评委偶然触发出错要有说服力得多。5. 课程设计避坑手册答辩前必须解决的五个实际障碍写系统只占一半工作量另一半是排错和适配环境。这里把我在多台机器上跑类似课设时踩过的坑集中列出来每一条都是现象→原因→解决的结构你不一定全遇到但遇到时可以直接照方抓药。5.1 dlib编译失败与卡死式安装别在dlib上浪费太多时间现象Windows终端执行pip install dlib后长时间停在Building wheel最后报错CMake was unable to find a build program。原因dlib需要编译C代码系统缺VS Build Tools。这不是Python问题是C工具链问题。同样的问题也会出现在只装了MinGW但没配环境变量的机器上。解决直接下载预编译的.whl文件使用常见渠道是PyPI的备用镜像或微软维护的dlib-19.24.0-cp38-cp38-win_amd64.whl。如果你用的是Python 3.10建议先降级到3.8再装因为部分预编译wheel不覆盖3.10。另一点装不上dlib时可以退一步用OpenCV的人脸检测器加face_recognition库替代关键点定位但face_recognition库本身也依赖dlib换汤不换药不如直接解决编译问题。5.2 YOLOv5启动奇慢与torch.hub拉取卡住现象程序启动后界面白屏十几秒日志停在Downloading...或Checking file。原因torch.hub.load首次执行会检测缓存内置的force_reloadFalse虽不强制下载但若权重文件路径写错或缓存被清它会重新从GitHub拉取墙内网络环境下常常卡住。解决训练或首次运行前手动下载yolov5s.pt并放到项目根目录再将torch.hub.load的cache_dir参数指向本地缓存目录。具体做法是torch.hub.set_dir(./yolo_cache)这样断网也能跑。如果已经卡住GitHub连接在工程里额外检查DNS设置或者把git clone的仓库整个放本地用torch.hub.load(./yolov5, custom, path...)加载本地源码。答辩现场没有稳定外网这一点务必提前确认。5.3 PyQt5界面未响应问题出在阻塞主线程现象点击开始检测后窗口立刻变白鼠标转圈几秒后提示程序未响应。原因把VideoThread里的cap.read()和detect循环直接写在了按钮的槽函数里主线程被掉进死循环Qt事件循环无法处理重绘和按键消息。解决检查代码里是否所有耗时操作都在自定义QThread的run()方法中且通过pyqtSignal传数据。常见错误写法是在while循环里调用QApplication.processEvents()来补救这会让界面刷新与检测抢CPU时间越补越卡。正确做法是子线程msleep(30)主线程只负责setPixmap两边的队列交给Qt信号槽机制自动排队。如果仍有间歇性卡顿检查信号参数是否传递了大尺寸QPixmap而不是小尺寸QImage图像对象跨线程赋值会触发深拷贝。5.4 dlib检测框时有时无摄像头分辨率与采样上采样参数的博弈现象同一个场景下人脸检测框一会儿有、一会儿没有尤其人离摄像头稍远时。原因dlib的HOG检测器对目标最小尺寸有约束输入图分辨率太低时人脸缩小到难以检出detector接收的灰度图来自cv2.VideoCapture默认的640x480时效果尚可但如果你把它缩放成320x240就不行了。解决把传给get_landmarks的图像保持640x480或更高同时把detector(gray, 1)的缩放系数从1提到0不上采样来提升速度。如果人脸框确实不稳定给检测结果加一个最近人脸位置记忆上一帧有框而这一帧突然丢失且间隔小于3帧就沿用上一帧框的位置继续算关键点能明显缓解闪烁。注意别用脸部追踪器替代检测器OpenCV的tracker在长时间运行时会漂移最后框住背景黑板场面更尴尬。5.5 摄像头被占用导致cap.isOpened()为False现象程序打开摄像头时直接报错但单独测试摄像头明明是好的。原因Windows下多个进程同时占用同一个摄像头常见于调试时前一个Python进程没退出或微信/腾讯会议等软件还占着摄像头通道。解决关掉所有可能占用摄像头的软件在任务管理器里确认没有残留的python.exe。如果通过cv2.VideoCapture(0)打开失败有一个小技巧先cap.release()再cv2.destroyAllWindows()等待0.5秒重试两次。另外某些笔记本人脸识别驱动会虚拟出多个摄像头索引cv2.VideoCapture(0)不一定是物理摄像头申请加一个下拉框选设备索引会显得更专业。5.6 界面显示花屏/颜色发绿现象OpenCV画面在QLabel里显示出绿色或蓝色异常色块人脸皮肤呈紫红色。原因OpenCV的BGR通道顺序和Qt的RGB顺序不一致没有做颜色空间转换就构造QImage。解决构造QImage之前必须cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)这条代码在第三节代码里已经写了。如果仍花屏检查bytes_per_line是否用w*3而不是ch*w元组顺序错误会导致显示图像有斜切感。顺手提一句不要为了省事直接改cv2的存储顺序那会导致YOLOv5和dlib的检测结果全都错位。6. 答辩演示与能力延伸用超参数影响分析和长时间稳定性证明你懂的这行模型跑通、界面能弹窗只是及格线答辩拿高分靠的是你对自己系统的边界说得出个所以然。这一章重点讲两件事如何设计一套可复现的验证流程以及从课设延伸到生产环境要补哪些东西。6.1 用录制视频而不是摄像头做验收测试答辩前强烈建议录一段3分钟的视频包含正常驾驶睁眼、平视、闭眼5秒、打哈欠、拿起手机接听、低头看手机五个片段。测试时把VideoCapture(0)替换成VideoCapture(demo.mp4)把同样的检测代码跑一遍并记录报警帧时间点。这样一个好处是结果可重复你在答辩时可以随时重放不用看评委脸色另一个好处是方便你调整阈值后对比同一个片段在不同ear_threshold下的表现差异就是你讲参数如何影响结果的素材。# 用视频文件替代摄像头的完整骨架 cap cv2.VideoCapture(demo.mp4) fps cap.get(cv2.CAP_PROP_FPS) frame_count 0 alarm_events [] while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 status process_frame(frame) if status[fatigue]: alarm_events.append({ time_sec: round(frame_count / fps, 2), type: fatigue, ear: status[ear] }) cap.release() print(alarm_events)逻辑说明用文件测试时fps是根据视频元数据读出来的千万不要硬编码30因为录屏或手机视频的帧率可能是25或60。alarm_events记录了事件发生的时间点和当时的EAR答辩时可以列出表格说明系统在第4.2秒首次触发疲劳报警、在第6.5秒停止报警——这种精确到秒的分析能让评委相信你的系统具备真实的监控能力而不是演示看起来好像不错。如果条件允许可以准备两段视频一段阈值0.2一段阈值0.25对比报警时长的变化以此展示你在超参数上确实做了实验。6.2 超参数怎么调才叫会调EAR阈值、置信度阈值与窗口大小YOLOv5训练自己的数据集是另一个大话题课设一般不需要走到那一步。但答辩时老师很可能问你的conf_thres0.4是怎么来的你要能回答先用results.xyxy打印出所有检测结果的置信度观察正确检测和误检的分布选择一个两者重叠最少的值。同理ear_threshold0.2不是拍脑袋而是录下自己平视、眨眼、闭眼时EAR的均值与极值通常平视0.35、闭眼0.08、眨眼瞬间0.15取0.2刚好能区分。把这些实验记录整理成一张简单的3行表格放进答辩PPT比放十张检测截图更有说服力。# 用一段测试脚本统计EAR分布 python ear_statistics.py --video demo.mp4 --output ear_report.csvear_statistics.py的核心逻辑是把每帧的EAR写入CSV再用pandas做描述性统计。这里有个小技巧戴眼镜的人EAR基线会偏高因为眼镜框边缘有时候会被识别成眼睛轮廓建议录三段不同人员的视频做均值对比结论是阈值需要按人微调系统里预留了参数接口。这套思路也能用来解释YOLOv5网络结构图和超参数文档里那些抽象概念——你不需要背出C3模块的结构但你能说明白自己的系统在哪一层引入了什么误差。6.3 从课设到长期现场稳定性还差哪几步如果这个课题要被改造成真正能跑的驾驶监控设备只靠PyQt程序是不够的。pyqt长期现场稳定性测试这个词背后涉及的是内存泄漏、日志回滚、断线重连、电源异常恢复等等。我在做完课设后做过一次72小时连续运行测试首日一切正常第二天开始内存持续增长最后定位到两个问题一是QImage.copy()在信号槽里没被及时释放二是opencv的视频缓冲没有定期清空。解决方法是用weakref封装UI引用并在每次cap.read()后判断frame是否为None再送入检测日志模块要用RotatingFileHandler限制体积否则一晚上日志就能写满一张128G存储卡。如果部署到树莓派4b这类ARM平台YOLOv5的推理时间会从30ms暴涨到200~300ms此时需要将window_size从30下调到10并把检测帧间隔从1改成2相当于牺牲时间分辨率换取可运行的帧率。yolov5量化rk3568这类边缘端优化课设阶段不必做但你要知道模型从x86换到ARM后默认浮点权重直接加载会非常慢需要对模型做INT8量化同时验证量化后手机检测置信度是否明显下降——这个部署后精度回调的思路本身就是答辩时展示工程素养的好素材。我在做类似课题时养成了一个习惯所有调过的参数都写进config.py而不是散落在代码里跑完测试后把每组参数对应的报警时间点导出成JSON存档。这个习惯在答辩现场帮了大忙——评委问你试过哪些阈值时我能直接调过去对比而不是支支吾吾说我大概试了试。这也是我建议你动手前先建好的后悔药机制参数错了可以回滚报告里也有据可查希望帮到你毕竟课设是拿来学东西的不只是拿来过的。本文还有配套的精品资源点击获取
返回列表