简介面向计算机视觉方向的本科毕业设计与入门级实战项目源码用 Python 实现基于驾驶员面部特征的疲劳状态检测通过人脸识别定位眼部与脸部关键点判断驾驶员当前是否需要休息可直接用于毕业设计演示也能延伸到交警摄像头筛查、高速收费站等疲劳驾驶预警场景。压缩包共 24 个文件约 68.33MB其中 5 个 py 脚本负责人脸检测与疲劳判定的核心逻辑10 个 xml 为工程配置与模型相关文件另含 README 说明、docx 设计文档、mp3 提示音与 dat 数据文件目录保持可直接导入运行的结构。目前已有 1360 人学习下载。读者可获得一套完整可运行的检测流程参考涵盖人脸特征提取、疲劳阈值判定与提示音联动的实现思路以及项目模块划分与配置方式便于替换模型、调整判定参数或补充界面用于毕业设计选题与二次开发。1. 从驾驶位摄像头画面说起这套系统到底在算什么驾驶位上的人眼皮一耷拉摄像头画面里的 EAR 值从 0.28 掉到 0.12系统在不到两秒里把状态从正常推到重度疲劳——这套 Python 基于驾驶员面部特征的疲劳检测系统源码毕业设计做的事就是把这种现场判断拆成可计算的帧级指标。它不训练大模型靠 dlib 的 68 点人脸关键点、OpenCV 取帧、EAR/MAR 和 PERCLOS 做规则判定再配一个能实时看画面和报警的桌面界面。适合拿它做毕业设计二次开发、课程项目验收或者给车载摄像头做一个低成本疲劳告警原型不适合直接当量产 DMS因为光照、眼镜、夜间红外这些工程问题还要另外补。它更像一个“可解释的人脸识别检测系统”每一帧为什么判疲劳都能从眼睛开合度、嘴部开合度和时间窗口里找到依据。2. 环境与依赖OpenCV、dlib 和 68 点模型怎么排安装顺序拿到 .rar 先别急着双击 main.py。这类疲劳检测项目源码的依赖里dlib 是最容易翻车的一环安装顺序不对后面摄像头能打开也跑不出关键点。我一般先把 Python 版本固定在一个 dlib 轮子覆盖较好的区间再按“数值库 → OpenCV → 界面库 → dlib → 项目其他依赖”的顺序装。这样做的原因很实际OpenCV 和 PyQt5 的 wheel 基本能直接拉下来dlib 则可能现场编译一旦编译失败至少前面的环境已经验证过不会把所有问题混在一起。2.1 Python 版本与依赖安装的推荐顺序这类项目常见入口是main.py界面可能是 PyQt5也可能是 Tkinter。先看源码根目录有没有requirements.txt有就按里面的包名装但建议把 dlib 单独拎出来最后装。下面是一套保守的 conda 流程Python 选 3.8 或 3.9 都行关键是不要一上来就混用系统 Python 和 conda 环境# 创建独立环境避免和系统里的 opencv、dlib 互相覆盖 conda create -n fatigue python3.8 -y conda activate fatigue # 先升级 pip老版本 pip 对 wheel 标签识别不好 python -m pip install --upgrade pip # 先装不会现场编译的包 pip install numpy opencv-python PyQt5 pyyaml # dlib 放最后失败时不影响前面环境 pip install dlib这段命令的逻辑是先把“能直接装”的依赖落地再处理 dlib 这个变量。opencv-python提供cv2.VideoCapture、灰度转换和绘图numpy负责数组运算PyQt5是界面线程的基础pyyaml在二次开发里用来读阈值配置。dlib 单独安装的好处是报错时能快速判断是编译工具链问题还是 Python 包冲突。Windows 上如果pip install dlib卡在 CMake 或 Visual Studio Build Tools不要反复重装 opencv先补编译工具链或者换一个与当前 Python 版本匹配的预编译 wheelLinux 上则先确认cmake、g和 Python 开发头文件是否齐全。如果源码包自带requirements.txt里面大概率长这样numpy opencv-python PyQt5 dlib pyyaml这种清单不要直接无脑pip install -r然后去泡咖啡。先把 dlib 那行注释掉装完其余包再单独处理 dlib能省掉很多“到底是谁坏了”的排查时间。项目里如果还有pygame或playsound通常是用来播放报警音的没有也不影响核心检测可以先注释掉等主流程跑通再补。2.2 68 点人脸关键点模型放哪、怎么验证dlib 的人脸检测器只给矩形框疲劳判定需要眼睛和嘴巴的轮廓点所以项目里一定依赖shape_predictor_68_face_landmarks.dat这个模型文件。它通常放在models/、model/或源码根目录。解压 .rar 后先全盘搜一下这个文件名确认它没有被杀毒软件隔离也没有因为压缩包层级多套了一层目录。模型路径写错时常见报错是RuntimeError: Unable to open shape_predictor_68_face_landmarks.dat不是摄像头问题也不是 OpenCV 问题。下面这段最小验证代码可以直接贴到项目根目录跑目的不是做完整检测而是确认“dlib 能加载模型 摄像头能出帧 人脸能出 68 点”这条链路是通的import os import cv2 import dlib # 模型路径按你解压后的实际位置改建议用相对项目根目录的路径 predictor_path os.path.join(models, shape_predictor_68_face_landmarks.dat) # 正面人脸检测器适合驾驶位这种相对正对摄像头的场景 detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(predictor_path) cap cv2.VideoCapture(0) if not cap.isOpened(): raise RuntimeError(摄像头打开失败先检查索引和占用情况) while True: ok, frame cap.read() if not ok: break # 镜像一下更符合驾驶位自拍视角不影响关键点 frame cv2.flip(frame, 1) gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 第二个参数 0 表示不放大检测驾驶位人脸通常够大 faces detector(gray, 0) for face in faces: shape predictor(gray, face) # 画 68 个点确认模型确实在工作 for i in range(68): x, y shape.part(i).x, shape.part(i).y cv2.circle(frame, (x, y), 1, (0, 255, 0), -1) cv2.imshow(landmark check, frame) if cv2.waitKey(1) 0xFF 27: break cap.release() cv2.destroyAllWindows()这段代码里detector(gray, 0)的第二个参数是上采样次数设 0 是为了速度驾驶位画面人脸占比大通常不需要放大。shape.part(i)返回第 i 个关键点索引范围 0 到 67。跑起来后画面里应该能看到绿色点铺在眉毛、眼睛、鼻子、嘴唇和下巴上。如果只有人脸框没有点优先查模型路径如果连人脸框都没有查光照、摄像头焦距和gray是否正常。模型文件接近百兆加载第一次会慢一点不要以为程序卡死就强杀。提示模型文件不要用文本编辑器打开也不要用 Git LFS 指针文件代替。确认文件大小正常、扩展名是.dat再谈后面的阈值调参。2.3 摄像头与视频源切换实时预览、离线回放毕业设计答辩现场不一定允许你对着摄像头做疲劳动作所以源码里最好保留“摄像头 / 视频文件”两种输入。常见写法是把cv2.VideoCapture(0)改成可配置的 source整数走摄像头字符串走视频路径。下面这个小函数可以直接塞进utils/camera.pyimport cv2 def open_video_source(source): source 支持 0 / 0 - 默认摄像头 video.mp4 - 离线视频 注意字符串数字要先转 int否则 OpenCV 会当文件名 if isinstance(source, str) and source.isdigit(): source int(source) cap cv2.VideoCapture(source) if not cap.isOpened(): raise RuntimeError(f无法打开视频源: {source}) # 摄像头可以尝试设分辨率视频文件设了也可能被源分辨率覆盖 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) return capsource的参数说明很关键0是默认摄像头外接摄像头可能是1或2视频文件路径建议用绝对路径或相对项目根目录的路径不要用中文空格路径去考验 OpenCV。CAP_PROP_FRAME_WIDTH和CAP_PROP_FRAME_HEIGHT只是请求值实际分辨率以cap.get读到的为准。离线视频回放时帧率不要用cv2.waitKey(1)硬跑最好按视频原始 FPS 做延时否则 30 秒的视频几秒就播完PERCLOS 的时间窗口会失真。3. 源码结构与运行链路入口脚本、检测线程和界面怎么接这类项目源码的目录不会完全一样但职责划分大体逃不出“入口、检测、界面、模型、配置、工具”六块。读源码时不要从每个文件第一行开始啃先找入口脚本再看它 import 了谁顺着调用链把“谁负责取帧、谁负责算指标、谁负责弹报警”画出来。常见结构如下表里的文件名是这类毕业设计项目里出现频率较高的命名实际以你解压后的为准路径职责阅读优先级main.py/run.py程序入口创建窗口或启动检测循环高detector/人脸检测、关键点、EAR/MAR 计算高ui/PyQt5/Tkinter 界面、按钮、视频显示中models/68 点模型文件高configs//config.py阈值、摄像头索引、报警开关中utils/摄像头封装、日志、声音、绘图低到中requirements.txt依赖清单高如果源码里没有detector/这种包而是所有函数都堆在main.py也不用意外。毕业设计项目常见做法是一个文件几百行检测、绘图、阈值判断全在一起。二次开发时第一件事不是重写而是把阈值和路径抽出去先把“能跑”保住。3.1 典型源码包目录怎么读先看main.py的底部有没有if __name__ __main__:那里通常是启动入口。再看它创建的是QApplication还是tk.Tk()这决定你后面是改信号槽还是改after循环。然后搜三个关键词VideoCapture、shape_predictor、EAR或eye_aspect_ratio。搜到这三处基本就能定位核心链路。常见调用链是入口创建主窗口 → 主窗口点“开始检测” → 启动检测线程或定时器 → 循环读帧 → 灰度化 → dlib 检测人脸 → 预测 68 点 → 计算眼睛和嘴巴指标 → 更新疲劳状态机 → 把画面和状态发回界面。你要改摄像头索引去VideoCapture要改报警灵敏度去阈值常量要换模型路径去shape_predictor那一行。不要一上来改算法公式先把这条链路跑通。3.2 主循环与帧处理流程下面这段是命令行版主循环的骨架和界面版的核心逻辑一致只是少了信号发送。把它放进main.py或单独写成detect_loop.py都能跑import time import cv2 def run_detection(cap, detector, predictor, engine, draw_overlay): while True: ok, frame cap.read() if not ok: break frame cv2.flip(frame, 1) gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 每帧都检测人脸开销大常见做法是隔帧检测、跟踪补位 faces detector(gray, 0) now time.time() if len(faces) 0: # 驾驶位一般只取最大人脸避免后排乘客干扰 face max(faces, keylambda r: r.width() * r.height()) shape predictor(gray, face) state engine.update(shape, now) draw_overlay(frame, face, shape, state) else: # 丢脸时不要立刻报警状态机内部做超时处理 engine.mark_face_missing(now) cv2.imshow(fatigue detection, frame) if cv2.waitKey(1) 0xFF 27: break这段代码里max(faces, key...)取面积最大的人脸是一个很实用的工程习惯驾驶位画面里可能有座椅、后排或海报上的人脸dlib 偶尔会误检取最大脸能显著降低误判。engine.update(shape, now)把关键点和当前时间戳一起传进去是因为 PERCLOS 和眨眼频率都依赖时间不是单纯数帧。mark_face_missing用来处理驾驶员低头、侧脸导致关键点丢失的情况丢脸超过两三秒可以单独提示“未检测到人脸”但不要直接判疲劳否则等红灯低头看手机都会被报警。帧率方面dlib 的 68 点预测在普通笔记本上跑 640×480 一般够用但不要每帧都做detector(gray, 1)上采样。上采样会明显拖慢速度导致 PERCLOS 的时间窗口被拉长阈值看起来“变钝”。如果画面卡先把分辨率降到 640×480再考虑隔帧检测不要先怀疑算法公式。3.3 界面与检测线程解耦PyQt5如果源码是 PyQt5 界面最容易踩的坑是把while True直接写在主线程里界面按钮点不动、关闭窗口卡死。正确做法是把检测循环放进QThread通过信号把帧和状态发回主线程主线程只负责setPixmap和更新标签。下面是一个够用的线程骨架from PyQt5.QtCore import QThread, pyqtSignal import cv2 import time class DetectThread(QThread): frame_ready pyqtSignal(object) # 给界面显示 BGR 帧 state_ready pyqtSignal(dict) # 给界面更新疲劳等级 error_ready pyqtSignal(str) def __init__(self, source, detector, predictor, engine, parentNone): super().__init__(parent) self.source source self.detector detector self.predictor predictor self.engine engine self._running True def stop(self): self._running False self.wait(1000) def run(self): cap cv2.VideoCapture(self.source) if not cap.isOpened(): self.error_ready.emit(摄像头打开失败) return try: while self._running: ok, frame cap.read() if not ok: break frame cv2.flip(frame, 1) gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces self.detector(gray, 0) now time.time() if faces: face max(faces, keylambda r: r.width() * r.height()) shape self.predictor(gray, face) state self.engine.update(shape, now) else: state self.engine.mark_face_missing(now) self.frame_ready.emit(frame) self.state_ready.emit(state) time.sleep(0.01) # 给 UI 线程一点调度空间 finally: cap.release()pyqtSignal(object)用来传 numpy 数组不要传QImage后再跨线程释放容易出玄学崩溃在主线程里把 BGR 帧转成QImage更稳。stop()里先置标志再wait不要在run()里直接terminate()否则摄像头释放不干净下一次启动会报设备占用。time.sleep(0.01)不是必须但在高帧率摄像头上能避免信号队列堆积。界面关闭时记得在closeEvent里调用thread.stop()否则窗口关了进程还在后台读摄像头。4. 核心算法拆解EAR、MAR、PERCLOS 与头部姿态的判定闭环疲劳检测的规则不复杂难的是把“眼睛闭了多久”“嘴巴张了多久”“多长时间窗口内闭眼占比多少”拆成不互相打架的指标。dlib 的 68 点给的是坐标EAR 和 MAR 是坐标的几何变换PERCLOS 是时间维度上的统计头部姿态是辅助证据。四者不要并列投票常见做法是以 PERCLOS 为主、眨眼持续时间为辅、打哈欠和点头做加权提示。这样报警逻辑可解释答辩时也能说清楚每个阈值怎么来的。4.1 68 点索引与人脸 ROIdlib 的 68 点索引是固定的眼睛和嘴巴的位置必须记准否则公式写得再漂亮也是错的。左眼是 36 到 41右眼是 42 到 47嘴巴外轮廓 48 到 59内轮廓 60 到 67。下面这张表建议直接抄进代码注释后面调参时不用反复翻文档区域索引范围用途左眼36–41左眼 EAR右眼42–47右眼 EAR嘴巴外轮廓48–59嘴部区域定位嘴巴内轮廓60–67MAR鼻梁/鼻尖27–35头部姿态辅助下巴8低头/点头比例实际计算时不要把整张脸缩放到固定尺寸再取点dlib 返回的坐标就是原图坐标直接用即可。人脸框只用来画 ROI 和裁剪不要拿人脸框宽度当眼睛宽度两者比例不稳定。驾驶位画面里人脸可能偏左或偏右眼睛点的水平距离会变化但 EAR 是比值对整体缩放不敏感这正是它比“眼睛像素高度”更可靠的地方。4.2 EAR 计算与眨眼判定EAR 的核心是“眼睛纵向距离除以横向距离”。dlib 左眼六个点按顺时针排列取上下两对点算纵向左右眼角算横向。下面这个函数同时支持左右眼返回值越小表示眼睛越闭import math def _dist(p1, p2): return math.hypot(p1[0] - p2[0], p1[1] - p2[1]) def eye_aspect_ratio(pts, eye_idx): pts: [(x, y), ...] 长度 68 的关键点列表 eye_idx: 左眼 [36,37,38,39,40,41] 或右眼 [42,43,44,45,46,47] p1, p2, p3, p4, p5, p6 [pts[i] for i in eye_idx] vertical _dist(p2, p6) _dist(p3, p5) horizontal 2.0 * _dist(p1, p4) if horizontal 1e-6: return 0.0 return vertical / horizontal def is_eye_closed(pts, threshold0.21): left eye_aspect_ratio(pts, [36, 37, 38, 39, 40, 41]) right eye_aspect_ratio(pts, [42, 43, 44, 45, 46, 47]) # 取平均比取最小更稳单眼遮挡时不至于整帧判闭眼 return (left right) / 2.0 thresholdthreshold0.21是一个常见起点不是万能值。戴眼镜、双眼皮、摄像头俯仰角都会让基线漂移正确做法是让驾驶员正常睁眼 10 秒记录 EAR 均值再把闭眼阈值设成均值的 60% 到 70%。is_eye_closed里取左右眼平均而不是取最小是为了防止单侧反光或头发遮挡导致一只眼 EAR 突然掉下去。眨眼判定不要只看单帧连续 2 到 3 帧低于阈值才算一次闭眼否则摄像头噪声会让眨眼计数虚高。4.3 MAR 与打哈欠打哈欠的典型表现是嘴巴纵向张开、持续时间比说话长。MAR 用内唇上下点和左右嘴角算比值下面这个版本用 60、64 做水平62、66 和 63、65 做纵向def mouth_aspect_ratio(pts): left, right pts[60], pts[64] upper1, lower1 pts[62], pts[66] upper2, lower2 pts[63], pts[65] vertical _dist(upper1, lower1) _dist(upper2, lower2) horizontal 2.0 * _dist(left, right) if horizontal 1e-6: return 0.0 return vertical / horizontalMAR 阈值常见从 0.45 到 0.6 开始试但单帧超过阈值不能直接报“打哈欠”因为说话、唱歌、咀嚼都会让嘴巴张开。更稳的写法是加一个持续时间MAR 连续超过阈值 1.2 到 1.8 秒才记一次哈欠两次哈欠之间至少间隔 3 秒避免一个长哈欠被拆成多次。打哈欠在疲劳分级里通常作为“中度”证据不建议单独触发最高级报警。夜间红外画面下嘴唇轮廓可能粘连MAR 会偏低这时候要么提高阈值要么把打哈欠权重降下来。4.4 PERCLOS 滑动窗口和报警状态机PERCLOS 是疲劳检测里最值得保留的指标它统计一段时间内闭眼帧所占比例比“连续闭眼几帧”更抗帧率波动。实现上用deque存(timestamp, closed)每次更新时把窗口外的数据弹掉再算闭眼样本占比from collections import deque class FatigueEngine: def __init__(self, window_sec30.0, perclos_th0.30, closed_th0.21): self.window_sec window_sec self.perclos_th perclos_th self.closed_th closed_th self.samples deque() self.last_face_ts 0.0 self.level normal def update(self, pts, now): left eye_aspect_ratio(pts, [36, 37, 38, 39, 40, 41]) right eye_aspect_ratio(pts, [42, 43, 44, 45, 46, 47]) ear (left right) / 2.0 closed ear self.closed_th self.samples.append((now, closed)) self.last_face_ts now # 只保留时间窗口内的样本避免内存一直涨 while self.samples and now - self.samples[0][0] self.window_sec: self.samples.popleft() if len(self.samples) 10: return {level: warming, ear: ear, perclos: 0.0} closed_count sum(1 for _, c in self.samples if c) perclos closed_count / len(self.samples) if perclos self.perclos_th: self.level severe elif perclos self.perclos_th * 0.6: self.level mild else: self.level normal return {level: self.level, ear: ear, perclos: perclos} def mark_face_missing(self, now): # 丢脸超过 3 秒才降级短暂侧脸不算疲劳 if now - self.last_face_ts 3.0: self.level no_face return {level: self.level, ear: 0.0, perclos: 0.0}window_sec30.0表示用最近 30 秒做统计perclos_th0.30表示闭眼样本占到 30% 触发重度。这个阈值不要照抄先录一段正常驾驶视频跑一遍看 PERCLOS 基线再把阈值放在基线之上。len(self.samples) 10是预热保护刚启动时样本太少算出来的比例没有意义。mark_face_missing用 3 秒超时是为了区分“等红灯低头”和“真正检测不到人脸”。报警状态机最好做迟滞重度触发后要连续几秒回落到正常才解除否则界面会疯狂闪红。4.5 头部姿态辅助判定头部姿态可以用solvePnP做完整三维解算但毕业设计项目里更常见的是比例法看鼻尖在眼睛中点和下巴之间的相对位置。下面这个函数不做相机标定只输出一个 0 到 1 左右的低头比例够用来做辅助提示def head_pitch_ratio(pts): eye_mid_y (pts[36][1] pts[45][1]) / 2.0 nose_y pts[30][1] chin_y pts[8][1] denom chin_y - eye_mid_y if abs(denom) 1e-6: return 0.5 return (nose_y - eye_mid_y) / denom正常抬头时这个比例大致在 0.4 到 0.6 之间明显低头会往 0.7 以上走。但它对摄像头高度非常敏感摄像头装得高一点比例整体就变了。所以我一般只把它当“点头频繁”的辅助证据30 秒内低头比例超过 0.75 的次数大于 3 次给一个轻度提示不直接触发重度报警。真要做得稳还是上solvePnP用 6 个面部模型点解欧拉角再配合相机内参那部分代码量会上去但答辩时更好讲。5. 避坑与排查摄像头、dlib 编译、模型路径和误报的现场处理这类项目跑不起来绝大多数不是算法错而是环境、路径和摄像头三件事。下面五条是我拆这类源码时最常遇到的现场问题每条按“现象 → 原因 → 解决”记照着排查比盲目重装快。5.1 摄像头打不开或黑屏现象运行后窗口弹出来但画面全黑或者直接抛RuntimeError: 摄像头打开失败。原因通常有三种VideoCapture(0)的索引不是当前摄像头摄像头被其他程序占用笔记本隐私开关或驱动把摄像头禁用了。解决顺序是先换索引试1、2再关掉会议软件、浏览器网页和后台录制工具最后去系统设置里看摄像头权限。Linux 下可以用ls /dev/video*确认设备节点但不要同时开两个进程抢同一个节点。还有一种黑屏是读到帧了但画面全黑那是摄像头曝光或镜头盖问题不是代码问题先拿系统相机应用验证。5.2 dlib 安装失败现象pip install dlib卡在Building wheel for dlib或者报 CMake、Visual Studio、Boost 相关错误。原因当前 Python 版本没有匹配的预编译 wheelpip 退回源码编译而本机缺少 C 编译工具链。解决Windows 先装 CMake 和 Visual Studio Build Tools 的 C 桌面开发组件再重试仍然失败就换 Python 3.8/3.9 的干净环境不要在一个装满科学计算包的环境里反复升降级。Linux 上补cmake、g、python3-dev。如果项目里已经有能跑的 dlib wheel优先用项目自带的环境说明不要自己追最新版。5.3 模型文件路径不对或被隔离现象代码报Unable to open shape_predictor_68_face_landmarks.dat或者程序启动就退出。原因解压后模型在models/模型/多一层目录代码里写的是绝对路径换机器就失效杀毒软件把.dat当可疑文件隔离。解决在项目根目录用os.path.exists打印实际路径统一改成相对路径把模型放到models/下并在配置里只写文件名去隔离区恢复文件。不要用空文件占位测试shape_predictor加载空文件会直接崩。5.4 误报漏报阈值不能一套打天下现象正常睁眼也频繁报疲劳或者明显闭眼却不报警。原因EAR 基线受眼镜、光照、摄像头角度影响PERCLOS 窗口太短会被一次长眨眼拉高MAR 把说话当成打哈欠。解决先跑 30 秒正常睁眼视频记录 EAR 均值把闭眼阈值设成均值的 60% 到 70%PERCLOS 窗口不要低于 20 秒打哈欠加持续时间过滤。夜间红外画面下嘴唇对比度低MAR 阈值要单独调不要和白天共用一套配置。调参时一次只改一个变量改完录一段回放对比不然你都不知道是哪个参数起的作用。5.5 界面卡顿与内存增长现象运行几分钟后画面越来越卡或者关闭窗口后进程还在。原因在 UI 主线程里跑检测循环每帧都 new 一个QImage且没有释放摄像头没在finally里release信号队列堆积。解决检测放QThreadUI 只做显示帧转QImage时保留 numpy 引用或立即copy()关闭窗口调thread.stop()和cap.release()高帧率摄像头加轻微sleep。如果内存持续涨重点查是否把每一帧都塞进了列表或日志PERCLOS 的deque要按时间窗口清理。6. 二次开发把阈值标定和回放验证做成能复用的工具这套源码最值得改的地方不是换人脸检测器而是把阈值从代码常量挪到配置文件再加一个回放验证工具。我一般先加一个config.yaml把 EAR 闭眼阈值、MAR 哈欠阈值、PERCLOS 窗口和报警等级都放进去启动时用 dataclass 读进来。这样答辩现场换驾驶员、换摄像头不用改代码重跑只改一个文件。配置结构可以是这样detector: camera_index: 0 face_downsample: 0 fatigue: ear_closed: 0.21 mar_yawn: 0.55 perclos_window_sec: 30 perclos_severe: 0.30 perclos_mild: 0.18 alarm: sound_enabled: true severe_hold_sec: 2读取时不要用yaml.load不带 Loader常见做法是yaml.safe_load。然后把每次检测的timestamp, ear, mar, perclos, level写进 CSV 或 SQLite回放视频时按时间戳对齐画出指标曲线。验证方法很直接拿一段正常驾驶视频和一段模拟闭眼视频分别跑同一套配置看正常段的 PERCLOS 是否稳定在阈值以下、闭眼段是否能在 2 到 3 秒内触发。表结构不用复杂字段够对比就行字段类型用途tsREAL帧时间戳做窗口统计earREAL眼睛开合度marREAL嘴部开合度perclosREAL当前窗口闭眼占比levelTEXTnormal/mild/severe改阈值时一次只动一个参数跑同一段回放记录触发时间点别凭感觉说“好像更灵敏了”。我在这类项目上吃过亏早期把所有阈值写死在if里换一个戴眼镜的测试对象正常眨眼都能报重度现场只能临时改代码很被动。从那以后我每次做疲劳检测都先把阈值外置、把回放日志留好再谈算法优化。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
AI模型可解释性与安全防护技术解析 1. 为什么AI模型需要可解释性与安全防护的双重保障上周调试一个图像分类模型时遇到了诡异现象:在测试集上准确率高达98%的模型,实际部署后却把消防栓错误识别成停车标志。排查发现训练数据中恰好有张消防栓照片背景里有个模糊的停车标志——模型其实是通… · 2026/11/5 11:47:55
WMSST与MCNN结合的工业设备智能故障诊断方法 1. 项目概述在工业设备故障诊断领域,如何从复杂的振动信号中准确提取故障特征一直是个技术难点。传统方法往往受限于信号噪声干扰和特征提取能力不足的问题。这个项目提出了一种创新性的解决方案——将小波多尺度同步压缩变换(WMSST)与多尺度卷积神经网络(MCNN)相结… · 2026/11/6 22:56:46
多线程编程中的线程同步机制与实战应用 1. 为什么我们需要线程同步当我在2013年第一次接触多线程编程时,曾天真地认为只要创建多个线程就能自动提升程序性能。直到有一天,我的银行账户模拟程序出现了诡异的现象:两个线程同时取款,结果账户余额竟然变成了负数!… · 2026/11/6 18:45:53
郓城县新华技校电工证多少钱 实操考试心里没底怕挂科 郓城县新华技校电工证多少钱 实操考试心里没底怕挂科 实操考试心里没底怕挂科,这是很多想报考郓城县新华技校电工证学员最真实的心理状态。尤其是初次接触电工操作的人,面对高压电、设备接线、安全操作规范等实操内容,往往会感到焦虑和迷茫。别担心,本文… · 2026/11/7 11:23:19
特种作业证过期了不知道怎么复审?2026河南最新政策参考电工证费全解析 特种作业证过期了不知道怎么复审?2026河南最新政策参考电工证费全解析 证书过期了不知道怎么复审延期?2026河南最新政策下,特种作业证的复审流程和费用成为很多持证人的关注焦点。特别是对于新乡水利工程从业者来说,电工证的复审不仅关系到能否继… · 2026/11/7 11:23:19
参考电工证费上岗必须要这个证吗 安阳应届生怕考不过电工证费白交?上岗必备的证书你真了解吗? 怕考不过白交培训费?这是安阳某高校应届工程专业毕业生小李最担心的事。他听说电工证是从事电气工作的上岗必备,但对考试内容、培训流程、费用构成一无所知,生怕自己花了钱最后考不过,连证书… · 2026/11/7 11:23:19
舞阳电工证培训官方回应:工地太忙根本没时间复习考试怎么办? 舞阳电工证培训官方回应:工地太忙根本没时间复习考试怎么办? 工地太忙根本没时间复习考试,这是很多一线电工的真实写照。尤其是像舞阳这样的工业重镇,电工证培训需求量大,但考试难度也不容小觑。官方回应称,电工证考试是确保作业安全的重要一环,但考生… · 2026/11/7 11:23:19
长寿在哪考电工证通过率揭秘你真的了解吗 长寿在哪考电工证通过率揭秘你真的了解吗 实操考试心里没底怕挂科,这几乎是每个想考电工证的人都会有的顾虑。特别是现在电工证越来越严格,从报名到考试,每一步都像是在闯关。很多人不知道, 长寿在哪考电工证… · 2026/11/7 11:22:19
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23
澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄? 澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄? 证书过期了不知道怎么复审延期?别慌,很多人在拿到【澳洲水电工证考试】的电子证书后,一不小心就忽略了复审时间,结果证书失效,工作受阻,甚至被单位追责。今天就从【澳洲水电工证考试… · 2026/11/7 0:00:41
电工作业提示版通过率揭秘:怎么考才能一次过? 电工作业提示版通过率揭秘:怎么考才能一次过? 怕考不过白交培训费?别急,今天我就从驻马店应届工程类毕业生的视角,带你全面了解电工作业提示版的考试流程、备考要点和真实通过率,帮你少走弯路、提高一次通过率。… · 2026/11/7 0:00:41
电工证年审报名千万别踩坑 外省证转过来要搞清这几点 电工证年审报名千万别踩坑 外省证转过来要搞清这几点 之前考的证在外省能不能转过来?这个问题是很多电工从业者最关心的,特别是像你这样在商丘从事公路工程的人员,经常需要跨省流动,证书的 有效性 和 可迁移性… · 2026/11/7 0:00:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23