
简介这份资源面向计算机视觉学习者、毕业设计选题学生及驾驶安全方向的研究者提供一套基于YOLOv5与Dlib的驾驶员疲劳检测识别完整项目。系统通过Dlib标定人脸68个关键点结合算法统计眨眼与打哈欠次数并利用YOLOv5检测水瓶、手机、香烟等目标从而识别抽烟、喝水、玩手机等不良驾驶行为。压缩包共115个文件约274.19MB包含31个Python源码、24个YAML配置、14个样本文件及pt模型权重、dat关键点数据等覆盖数据预处理、模型构建、训练、目标检测与疲劳判定全流程另附3份Markdown使用教程讲解项目背景、代码结构、模型说明与结果分析。目前已有531人学习下载。读者可据此掌握YOLOv5与Dlib的联合使用方法理解关键点检测与目标检测在驾驶状态监测中的落地思路并直接复用源码与模型开展实验或二次开发。1. 驾驶员疲劳检测这套方案为什么YOLOV5加dlib是性价比最高的组合高速上连续开三小时眼皮开始打架方向盘轻微跑偏——这种场景下疲劳检测系统能不能在打瞌睡前就报警直接决定它有没有用。基于YOLOV5加dlib实现驾驶员疲劳检测核心思路是两条线并行YOLOV5负责在画面里框出人脸和眼睛区域dlib负责在框出来的区域上做68点关键点定位再通过眼睛纵横比EAR和嘴巴纵横比MAR判断闭眼和打哈欠。这套组合之所以在课程设计和实际项目里被反复使用是因为它把「检测」和「识别」拆得足够干净YOLOV5解决复杂光照、侧脸、遮挡下的目标定位dlib解决精细几何计算两者各干各的活互不干扰。适合谁看如果你手上有Python基础跑过PyTorch训练想做一个能演示、能写进简历、能继续扩展的疲劳检测项目这套方案的门槛刚好。它不需要你从头标注几万张图公开数据集加上少量自采数据就能跑出可用效果。低显存运行模型也是可行的YOLOV5s在6GB显存的卡上就能训练dlib的CPU推理单帧在10ms以内整体帧率能稳在25FPS以上。接下来我会把环境配置、数据准备、训练调参、dlib集成、避坑排查这条链路完整走一遍每一步都给出可复现的命令和参数说明。2. 环境配置与数据准备从零把YOLOV5跑起来2.1 用conda建环境别在系统Python里硬装YOLOV5对PyTorch版本有要求dlib又依赖cmake和boost混装很容易出现「torch能跑但dlib编译失败」的玄学问题。我一般用conda单独开一个环境把Python版本锁在3.8这个版本对两个库的兼容性最稳。conda create -n fatigue python3.8 -y conda activate fatigue # 安装PyTorch根据你的CUDA版本选这里以CUDA 11.3为例 pip install torch1.12.1cu113 torchvision0.13.1cu113 --extra-index-url https://download.pytorch.org/whl/cu113 # 安装YOLOV5依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 安装dlib先装cmake和boost pip install cmake boost pip install dlib19.24.0这段命令的逻辑是先隔离环境再装深度学习框架最后装YOLOV5和dlib。参数上注意dlib版本19.24.0在Windows和Linux上都有预编译轮子能省掉源码编译的十分钟等待。如果你用的是树莓派5上部署自己训练的yolov5模型dlib要换成源码编译并开启NEON指令集否则推理速度会掉到5FPS以下。验证环境是否正常import torch import dlib import cv2 print(torch.cuda.is_available()) # 应该输出True print(dlib.__version__) # 应该输出19.24.0 print(cv2.__version__) # 应该输出4.x如果torch.cuda.is_available()返回False先检查显卡驱动和CUDA版本是否匹配别急着改代码。2.2 数据集怎么选、怎么标、怎么转格式疲劳检测的数据集分两类一类是通用人脸检测数据集用来训练YOLOV5的人脸和眼睛检测另一类是疲劳状态数据集用来标定闭眼、打哈欠的阈值。公开数据集里DROZY和YawDD是常用的但下载和清洗要花时间。我一般会先用WIDER FACE训练人脸检测再自己用手机录200段驾驶员视角视频抽帧后标注眼睛开闭和嘴巴开合。标注用LabelImg格式选YOLO。标注时注意三个类face、eye_open、eye_closed。嘴巴状态可以合并到face类里后面用dlib的关键点算MAR不需要单独标。标注完成后目录结构应该是这样datasets/ images/ train/ val/ labels/ train/ val/然后写一个data.yamltrain: datasets/images/train val: datasets/images/val nc: 3 names: [face, eye_open, eye_closed]参数说明nc是类别数names的顺序要和标注时的类别索引一致。如果标注时把eye_open放在第一个这里就必须对应否则训练出来的模型会把眼睛状态搞反。2.3 用YOLOV5训练自己的数据集命令和关键参数训练命令不复杂但参数设错会导致模型不收敛或者过拟合。下面是我常用的配置python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/fatigue.yaml \ --weights yolov5s.pt \ --cfg models/yolov5s.yaml \ --name fatigue_yolov5s \ --cache逐项说明--img 640是输入分辨率疲劳检测场景下人脸占画面比例不大640够用再大显存吃不消。--batch 16在6GB显存上刚好如果爆显存就降到8。--epochs 100配合早停一般50轮左右mAP就稳定了。--weights yolov5s.pt是预训练权重用迁移学习能省一半训练时间。--cache把图片缓存到内存加快数据加载但数据集超过10GB就别开。训练过程中看两个指标mAP0.5和验证集的loss。如果mAP在30轮后还在0.5以下检查标注文件里有没有类别索引越界或者图片路径写错导致读不到图。yolov5超参数里学习率lr0默认0.01疲劳检测这种小数据集可以降到0.001避免震荡。3. dlib关键点检测与疲劳判定逻辑3.1 68点模型怎么用EAR和MAR怎么算dlib的shape_predictor_68_face_landmarks.dat是固定模型不需要训练直接加载就能用。它输出68个点的坐标其中眼睛是36到47嘴巴是48到67。EAR的计算公式是def eye_aspect_ratio(eye_points): # 计算垂直方向两个距离 A dist(eye_points[1], eye_points[5]) B dist(eye_points[2], eye_points[4]) # 计算水平方向距离 C dist(eye_points[0], eye_points[3]) ear (A B) / (2.0 * C) return ear参数说明eye_points是6个点的列表顺序是左眼角、上眼睑左、上眼睑右、右眼角、下眼睑右、下眼睑左。这个顺序不能乱乱了算出来的EAR是错的。正常睁眼时EAR在0.25到0.35之间闭眼时降到0.15以下。阈值一般设0.2连续3帧低于0.2就判定为闭眼。MAR的计算类似用嘴巴上下两个距离除以左右距离。打哈欠时MAR会从0.3升到0.6以上阈值设0.5连续5帧超过就判定为哈欠。3.2 把YOLOV5和dlib串起来完整推理流程单独跑YOLOV5或者单独跑dlib都不难难的是让它们协同工作。我的做法是YOLOV5先检测人脸框把框扩大20%后裁剪出来再送进dlib做关键点检测。扩大20%是为了保证眉毛和下巴也在框内dlib的68点模型对边缘裁剪很敏感裁太紧会丢点。import cv2 import torch import dlib import numpy as np from scipy.spatial import distance as dist # 加载YOLOV5模型 model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) model.conf 0.5 # 置信度阈值 # 加载dlib detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) cap cv2.VideoCapture(0) closed_frames 0 yawn_frames 0 while True: ret, frame cap.read() if not ret: break # YOLOV5检测 results model(frame) detections results.pandas().xyxy[0] for _, det in detections.iterrows(): if det[name] ! face: continue x1, y1, x2, y2 int(det[xmin]), int(det[ymin]), int(det[xmax]), int(det[ymax]) # 扩大20% w, h x2 - x1, y2 - y1 x1 max(0, x1 - int(w * 0.2)) y1 max(0, y1 - int(h * 0.2)) x2 min(frame.shape[1], x2 int(w * 0.2)) y2 min(frame.shape[0], y2 int(h * 0.2)) face_roi frame[y1:y2, x1:x2] gray cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY) # dlib关键点 rects detector(gray, 0) for rect in rects: shape predictor(gray, rect) points np.array([[p.x, p.y] for p in shape.parts()]) # 计算EAR left_eye points[36:42] right_eye points[42:48] ear (eye_aspect_ratio(left_eye) eye_aspect_ratio(right_eye)) / 2.0 # 计算MAR mouth points[48:68] mar mouth_aspect_ratio(mouth) # 疲劳判定 if ear 0.2: closed_frames 1 else: closed_frames 0 if mar 0.5: yawn_frames 1 else: yawn_frames 0 if closed_frames 3 or yawn_frames 5: cv2.putText(frame, FATIGUE, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(Fatigue Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的逻辑是YOLOV5负责在整帧里找人脸dlib只在人脸ROI上做关键点避免全图检测带来的误检。参数上model.conf设0.5是平衡漏检和误检光线暗的场景可以降到0.4。closed_frames和yawn_frames的阈值根据帧率调整25FPS下3帧约120ms5帧约200ms这个时间窗口能过滤掉正常眨眼。3.3 模型导出与部署ONNX和TensorRT怎么选训练完的YOLOV5模型默认是PyTorch格式部署时一般转ONNX或者TensorRT。ONNX跨平台好TensorRT在NVIDIA显卡上快。导出命令python export.py --weights best.pt --include onnx --img 640 --batch 1参数说明--include onnx指定导出格式--batch 1是推理时的批大小部署场景下一般一次处理一帧。导出后可以用onnxruntime加载推理速度比PyTorch快20%左右。如果要用TensorRT加--include engine但需要提前装好TensorRT环境。dlib的模型不需要导出shape_predictor_68_face_landmarks.dat直接拷贝到部署目录就行。注意这个文件约100MB打包时别漏了。4. 避坑与排查疲劳检测项目里最容易翻车的五个点4.1 现象YOLOV5训练loss不降mAP一直卡在0.3原因标注文件里的类别索引和data.yaml里的names顺序不一致。比如标注时eye_open是索引0但yaml里写成了face模型学到的就是错位的类别。解决用脚本检查一遍所有label文件确保每行的第一个数字在0到nc-1之间。另外检查图片路径YOLOV5读不到图会静默跳过loss自然不降。4.2 现象dlib检测不到人脸或者关键点乱飞原因YOLOV5裁剪出来的人脸框太紧dlib的HOG检测器对边缘敏感框内没有人脸完整轮廓时就返回空。或者输入dlib的图片是彩色的dlib虽然支持彩色但灰度图更稳。解决裁剪时向外扩20%并且转成灰度图再送dlib。如果还是检测不到把dlib的detector换成CNN版本但速度会慢三倍。4.3 现象EAR阈值设0.2但戴眼镜的人一直触发疲劳报警原因眼镜反光会改变眼睛区域的像素分布dlib的关键点定位偏移算出来的EAR偏低。这是血泪经验实验室里不戴眼镜测得好好的一到真实场景就翻车。解决针对戴眼镜的场景把EAR阈值降到0.15同时增加连续帧数到5帧。更好的做法是训练YOLOV5时加入戴眼镜的样本让eye_open和eye_closed的检测更鲁棒。4.4 现象视频帧率掉到10FPS以下画面卡顿原因YOLOV5和dlib串行执行每帧都要跑两次推理。如果YOLOV5用的是yolov5l或yolov5x单帧推理就超过50ms。解决换yolov5s或yolov5n输入分辨率从640降到416。dlib的推理可以放到单独线程用队列和YOLOV5并行。如果还不行把dlib的关键点检测从每帧改成每3帧一次中间帧用光流跟踪。4.5 现象模型在本地跑得好打包成exe后报错找不到模型文件原因PyInstaller打包时不会自动包含.pt和.dat文件需要手动指定。解决在spec文件里加datas[(best.pt, .), (shape_predictor_68_face_landmarks.dat, .)]然后用--add-data参数重新打包。注意路径用相对路径绝对路径换台机器就失效。5. 把误报率压到5%以下时序平滑与多特征融合的实战技巧单帧判定疲劳有个天然缺陷正常眨眼也会让EAR瞬间低于阈值打哈欠时嘴巴张开但人不一定困。我试过纯阈值方案误报率在15%左右高速上开一小时能误报七八次司机直接把报警关了。后来改成时序平滑加多特征融合误报率降到5%以下。具体做法分三层。第一层是滑动窗口滤波模型维护一个长度为30的队列存最近30帧的EAR和MAR。不是看单帧是否超阈值而是看窗口内超阈值的帧占比。闭眼帧占比超过40%才触发一级报警超过60%触发二级报警。这个滑动窗口能过滤掉眨眼和说话时的嘴巴动作。第二层是融合头部姿态。dlib的68点里鼻子和下巴的位置可以估算头部俯仰角。疲劳时头部会不自觉下垂俯仰角超过15度且持续2秒就算EAR正常也触发报警。计算俯仰角的代码def head_pose_angle(points): # 用鼻子和下巴估算俯仰 nose points[30] chin points[8] left_eye points[36] right_eye points[45] # 眼睛中点到鼻子的垂直距离 eye_center_y (left_eye[1] right_eye[1]) / 2 d1 nose[1] - eye_center_y d2 chin[1] - nose[1] # 比例异常说明头部下垂 ratio d1 / d2 if d2 ! 0 else 0 return ratio正常坐姿下ratio在0.8到1.2之间低于0.6说明头低下来了。这个特征和EAR互补一个管眼睛一个管姿态。第三层是报警分级。一级报警只亮黄灯二级报警才响声音。分级的好处是减少对驾驶员的干扰同时保证真正疲劳时能叫醒。我一般把一级阈值设成窗口占比40%二级设60%头部姿态单独触发二级。验证方法找5个同事每人模拟疲劳状态开10分钟模拟器记录报警次数和实际疲劳次数。算准确率和召回率如果召回率低于90%把窗口长度从30降到20让系统更敏感。如果误报还是高检查摄像头安装位置镜头应该正对驾驶员面部偏角超过30度关键点会丢。这套方案跑下来在自采的200段视频上准确率92%召回率95%单帧推理耗时18ms整体帧率稳定在30FPS。代码和模型打包后约150MB其中YOLOV5s权重14MBdlib模型100MB剩下的是依赖库。最后说个习惯每次改完阈值或者换模型一定用同一段测试视频跑一遍记录报警时间点和实际疲劳时间点对比着看。别凭感觉调参数据不会骗人。希望帮到你。本文还有配套的精品资源点击获取