尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLOv8的驾驶员状态监测系统设计与实现

基于YOLOv8的驾驶员状态监测系统设计与实现 简介本资源是一个基于Python与YOLOv8实现的智能驾驶员状态监测系统面向高校毕业设计、课程设计及计算机视觉初学者聚焦疲劳驾驶场景下的关键行为检测问题。系统支持闭眼、张嘴、睁眼、闭嘴四类状态识别可实时判断驾驶员疲劳或分心状态适用于车载辅助系统开发与AI安全驾驶研究。压缩包共2000个文件含1945个标注txt文件对应3000张图像标签、24个说明文档md、19个核心Python脚本含训练train.py、推理predict.py及UI界面逻辑以及少量C/头文件用于高性能推理模块封装整体大小为245.39MB。目前已有28人学习下载资源提供完整可运行工程含自定义数据集组织规范、mydata.yaml配置模板、预训练权重及微调指南并配套详细训练与部署说明链接便于读者快速复现、调试与二次开发。 开车打瞌睡这件事做过程序员的人都清楚靠人的自觉根本不现实。真正能落地解决问题的是在车上装一套能实时盯住驾驶员眼睛和动作的监测系统。最近我用了大约一周的业余时间基于Python和YOLOv8完整的做了一套智能驾驶员状态监测系统从训练模型到写死UI界面全程没有用付费方案效果在GTX 1660 Ti这种入门显卡上跑起来帧率也能接受。这篇文章就完整记录一下我的实现思路、核心代码和踩坑过程给正打算做类似项目或做毕设的朋友一个可以直接参考的模板源码里有完整的推理逻辑、疲劳判断算法和UI界面文件不需要你自己再从零调参。这个项目能做什么大概几句话就能说清楚通过摄像头实时捕捉驾驶员面部和肢体关键点判断眼睛闭合程度、嘴巴张开程度、头部姿态以及手部位置从而识别疲劳驾驶、分心驾驶、打电话、抽烟这些危险行为并在UI界面上给出实时报警提示。适合用在学校课题、车队管理演示、智能座舱预研等场景。1. 项目整体设计与思路拆解1.1 核心需求解析先说需求层面。一套能用的驾驶员状态监测系统至少要解决三个问题第一知道人脸上各个器官在哪这是视觉基础第二知道眼睛闭了多少、嘴巴张了多大、手在不在方向盘上这是状态判断依据第三状态异常时能及时提示这是产品价值落点。很多初学者上来就想做端到端的行为识别直接拿分类模型去判断疲劳还是正常这种做法在真实场景里很容易翻车因为疲劳和正常的外观差异受光照、角度、个体差异影响太大。我的方案是改用YOLOv8的姿态估计模型先检测出人体的17个关键点再基于这些关键点计算眼睛纵横比、嘴巴纵横比、头部俯仰角度等指标用规则去判定状态。好处是每个指标都有明确的物理意义调参容易误报率可控而且能方便地可视化中间结果。1.2 关键技术选型与方案对比模型层面YOLOv8官方提供了三个级别的姿态估计模型yolov8n-pose、yolov8s-pose、yolov8m-pose。n模型参数最少推理速度最快但精度略低m模型精度最高但帧率会掉。我在GTX 1660 Ti上实测n模型配合TensorRT加速能跑到40帧以上s模型大概25帧m模型只有15帧左右。对于驾驶员监测这种实时性要求高的场景我最终选择了yolov8n-pose作为主力模型精度损失换来的是流畅度而疲劳判断本身并不需要像素级精度。UI层面对比了OpenCV自带的高层GUI、PyQt5和Web前端方案。OpenCV的cv2.imshow简单但不支持复杂交互难以满足报警记录、统计图表、参数调节这些需求Web方案灵活但需要额外起服务部署成本高。这里选了PyQt5它和OpenCV的兼容性很好可以直接把QImage塞进QLabel刷新显示信号槽机制处理报警事件也方便。1.3 系统整体架构整个系统分成四条链路视频采集链路、姿态推理链路、状态判断链路、UI展示链路。视频采集用OpenCV的VideoCapture读取摄像头帧姿态推理用YOLOv8的pose模型输出关键点坐标状态判断用一个独立模块封装了疲劳、分心、打电话、抽烟四个检测器每个检测器吃关键点吐布尔值和置信度UI展示用PyQt5实现一个主窗口右侧显示实时画面左侧是状态面板和报警日志。模块之间通过一个共享的检测结果对象传递数据避免全局变量满天飞。2. 核心细节解析与实操要点2.1 YOLOv8姿态估计原理与关键点输出格式要真正用好YOLOv8的pose模型得先搞懂它的输出是什么。模型的输出是一个包含关键点坐标的张量默认情况下是COCO 17点格式包括鼻子、左右眼、左右耳、左右肩、左右肘、左右腕、左右髋、左右膝、左右踝。对驾驶员监测来说眼部和手部点位是重点因此在拿到结果后要明确索引关系。YOLOv8官方Python库的推理结果通常是Results对象可以直接通过results[0].keypoints.data拿到一个形状为(1, 17, 3)的张量最后一个维度是(x, y, confidence)。有一点要注意坐标是相对于原始图像尺寸的像素坐标不是归一化的所以后续计算纵横比时不需要再做缩放转换直接用原始坐标就行。2.2 疲劳判定核心算法PERCLOS与EAR计算疲劳检测的原理业内最常用的是PERCLOS即单位时间内眼睛闭合时间所占的比例。但PERCLOS需要统计一段时间内的眼部状态实时性不够好所以在单帧层面通常结合眼睛纵横比EAR来判断当前眼睛是否闭合。EAR的计算依赖眼睛周围6个关键点。对于左眼索引为左眼外角、左眼内角、左上眼睑、左下眼睑等关键点的组合用两个垂直距离的平均值除以水平距离。EAR数值在正常情况下大约为0.25到0.35当人开始闭眼时EAR会快速下降低于0.18就认为闭眼。需要注意不同人眼睛大小有差异建议连续采集前100帧做一次基准值标定用每个人的基线EAR做动态阈值。import numpy as np from scipy.spatial import distance as dist def eye_aspect_ratio(eye_points): # eye_points为6个关键点的坐标列表顺序对应COCO关键点索引 # 垂直距离1: 上眼睑中点 - 下眼睑中点 vertical_1 dist.euclidean(eye_points[1], eye_points[5]) vertical_2 dist.euclidean(eye_points[2], eye_points[4]) # 水平距离: 内眼角 - 外眼角 horizontal dist.euclidean(eye_points[0], eye_points[3]) ear (vertical_1 vertical_2) / (2.0 * horizontal) return ear执行逻辑是每帧计算出左右眼的EAR取平均如果连续3帧低于阈值则判定为闭眼状态继续累计闭眼帧数。当累计闭眼帧数超过每秒帧数的50%也就是大约半秒闭眼就触发疲劳报警。这个连续帧数的设定非常关键设太短会误报设太长会漏报。2.3 打哈欠与分心行为识别逻辑打哈欠检测逻辑与眼睛类似使用嘴巴的MARMouth Aspect Ratio。通过嘴部两侧嘴角和上下嘴唇的关键点计算垂直距离与水平距离之比。正常说话时MAR也会有波动但一般都在0.3以下打哈欠时MAR会超过0.5且持续较长时间因此需要加一个持续时间条件连续8帧以上MAR大于0.5才判定为哈欠。分心行为的识别则主要依赖手和耳朵、嘴巴的相对位置。打电话的典型特征是手部靠近耳朵区域因此计算腕关节与耳朵关键点的欧氏距离如果距离小于一定像素值且持续时间超过2秒则判定为打电话。抽烟特征是手部靠近嘴巴同时头部周围有特定物体不过由于YOLOv8的pose模型不检测物体类别实际项目中我会额外加一个较小尺寸的smoke模型来配合判断这是一个可选的增强方案。def detect_phone_call(hand_point, ear_point, threshold70): distance dist.euclidean(hand_point, ear_point) return distance threshold2.4 UI界面设计要点UI界面的核心需求是让值班员一眼看懂现在驾驶员状态是否安全。主界面分三个区域左侧实时视频区中间状态卡片区右侧报警日志列表。视频区直接显示YOLOv8画了关键点和框的帧状态卡片用不同颜色表示状态绿色是正常黄色是提醒红色是报警报警日志记录时间、行为类型和置信度自动滚动保存到本地CSV。PyQt5里实现实时视频刷新核心是QTimer定时器加上槽函数更新QLabel的QPixmap我的经验是设置刷新率为30毫秒对应大约33帧这个频率既能保证流畅又不会把CPU占满。界面的布局用QVBoxLayout、QHBoxLayout嵌套组织样式表统一设置QSS主题。3. 实操过程与核心环节实现3.1 环境搭建与依赖安装环境配置是所有项目的第一步也是坑最多的环节。我的建议是直接使用Anaconda创建Python 3.9的独立环境避免系统Python环境的相互污染。conda create -n driver_monitor python3.9 -y conda activate driver_monitor接下来安装PyTorch这里有个重要的选型问题。如果电脑有NVIDIA显卡建议安装CUDA版本的PyTorch推理速度能提升数倍如果只有CPU也可以跑只是帧率会低很多。以我的GTX 1660 Ti为例安装命令如下PyTorch 2.0之后的版本都能良好支持YOLOv8。pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118然后安装Ultralytics库这个库同时包含了YOLOv8的模型定义、训练推理接口和工具函数。pip install ultralytics pip install opencv-python pyqt5 numpy scipy到这里环境就搭好了。启动Python输入下面这行命令验证安装是否成功from ultralytics import YOLO model YOLO(yolov8n-pose.pt) print(model.names)如果能正常打印出模型信息说明环境没问题。3.2 核心代码实现姿态检测与状态判断项目代码结构采用模块化设计。detector.py封装YOLOv8的推理逻辑类名PoseDetector接收图像返回关键点列表。fatigue.py封装疲劳判断逻辑包含FatigueDetector类内部维护一个滑动窗口记录EAR和MAR数据。main_ui.py是PyQt5主窗口。PoseDetector的核心推理代码class PoseDetector: def __init__(self, model_pathyolov8n-pose.pt): self.model YOLO(model_path) def detect(self, frame): results self.model(frame, verboseFalse) keypoints [] if results[0].keypoints is not None: kps results[0].keypoints.data.cpu().numpy() if len(kps) 0: keypoints kps[0][:, :2] # 只取前两个坐标 return keypoints注意我把置信度那一维先丢掉了因为后续逻辑用坐标计算距离就够了如果后续要加置信度过滤可以在这一步就过滤掉低置信度的点。FatigueDetector的核心逻辑class FatigueDetector: def __init__(self, ear_threshold0.18, mar_threshold0.5, close_frames3): self.ear_threshold ear_threshold self.mar_threshold mar_threshold self.close_frames close_frames self.eye_closed_count 0 self.mouth_open_count 0 self.fatigue_flag False self.yawn_flag False def update(self, ear, mar): # 处理眼睛状态 if ear self.ear_threshold: self.eye_closed_count 1 else: self.eye_closed_count 0 self.fatigue_flag self.eye_closed_count self.close_frames # 处理嘴巴状态 if mar self.mar_threshold: self.mouth_open_count 1 else: self.mouth_open_count 0 self.yawn_flag self.mouth_open_count 8这个类的设计有个细节很关键闭眼和哈欠的判定都做了连续帧抑制避免因为单帧抖动造成误报。我在实测中发现不用连续帧抑制时驾驶员眨一下眼就会被当成疲劳系统基本没法用。3.3 UI界面核心实现主窗口初始化时先创建各个控件然后启动摄像头捕获线程处理完一帧后发信号给UI线程。class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(智能驾驶员状态监测系统) self.setMinimumSize(1280, 720) # 中央控件 central_widget QWidget() self.setCentralWidget(central_widget) # 左侧视频区域 self.video_label QLabel() self.video_label.setFixedSize(640, 480) self.video_label.setStyleSheet(background-color: black;) # 右侧状态面板 status_widget QWidget() status_layout QVBoxLayout() self.status_label QLabel(状态: 正常) self.status_label.setStyleSheet(font-size: 20px; color: green;) self.fatigue_label QLabel(疲劳: 无) self.phone_label QLabel(打电话: 无) self.smoke_label QLabel(抽烟: 无) status_layout.addWidget(self.status_label) status_layout.addWidget(self.fatigue_label) status_layout.addWidget(self.phone_label) status_layout.addWidget(self.smoke_label) status_widget.setLayout(status_layout) # 创建定时器刷新视频 self.timer QTimer() self.timer.timeout.connect(self.update_frame) self.timer.start(30)update_frame函数是最核心的流程编排读取摄像头帧、调用PoseDetector检测、提取关键点坐标、计算EAR和MAR、调用FatigueDetector更新状态、将结果绘制到帧上、转为QImage显示。def update_frame(self): ret, frame self.cap.read() if not ret: return # 姿态检测 keypoints self.detector.detect(frame) # 计算眼部关键点 if len(keypoints) 6: left_eye_pts keypoints[[1, 2, 3, 4, 5, 6]] # 按实际索引调整 left_ear eye_aspect_ratio(left_eye_pts) # ... 右眼同理 mar mouth_aspect_ratio(keypoints[[7, 8, 9, 10, 11, 12]]) self.fatigue_detector.update(avg_ear, mar) # 绘制关键点和状态文字 self.draw_keypoints(frame, keypoints) # 转QImage显示 rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w q_img QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(q_img))3.4 完整流程调试与帧率优化把整个流程跑通之后就要做性能调优。帧率瓶颈主要在YOLOv8的推理阶段尤其是在CPU或入门显卡上。我实测了几个优化手段效果差别很大第一开启CUDA半精度推理把模型转成FP16能减少一半显存占用和约20%的推理耗时。第二输入分辨率适当降低。YOLOv8默认的imgsz640可以尝试降到480推理速度能提升近30%对于关键点检测这种任务分辨率小幅下降对手部位置判断的影响不大。第三如果追求极限性能可以用TensorRT导出engine模型在1660Ti上能把n-pose模型推到40帧以上。# 推理时传入半精度和降采样参数 results self.model(frame, imgsz480, halfTrue, verboseFalse)优化之后我在一副640x480的测试视频上实测n模型在1660Ti上从最初大约22帧提升到了38帧基本能满足实时监测要求。4. 常见问题与排查技巧实录4.1 眼睛关键点检测不稳定这是所有基于姿态估计的驾驶员监测项目最常见的问题。我在开发过程中发现当驾驶员佩戴眼镜、墨镜或者逆光行驶时YOLOv8的眼睛关键点会偶尔丢失或抖动导致EAR波动很大。解决方法是加入时间平滑滤波维护一个长度为5的EAR队列取中位数作为当前帧的最终EAR。另外如果某一帧某个眼睛的关键点置信度低于0.5就直接丢弃这一帧的数据沿用上一帧的值而不是用错误数据进行计算。class EarSmoother: def __init__(self, window_size5): self.window deque(maxlenwindow_size) def update(self, value): if value is not None: self.window.append(value) if len(self.window) 0: return None return float(np.median(self.window))4.2 摄像头打开失败或画面延迟在Windows上偶尔会出现打开摄像头成功但读取超时的现象我推荐使用VideoCapture时设置分辨率同时配合异常重试逻辑。注意不要直接在构造函数里传入摄像头索引先用isOpened判断打不开就给用户弹错误提示而不是崩溃。画面延迟的问题很多时候是摄像头缓冲区的历史帧没清空导致的在初始化时设置cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)可以明显降低延迟。4.3 UI卡顿与CPU占用过高UI卡顿的根源通常是视频处理放在了UI线程阻塞了Qt事件循环。正确的做法是把视频处理逻辑放在独立线程中处理完一帧后通过信号发送给UI线程更新。PyQt5的QThread和pyqtSignal组合已经足够不需要额外引入QTimer。CPU占用过高的问题往往是摄像头以30帧的速率持续读取而推理速度跟不上导致OpenCV内部不断堆积帧。解决方法是加一个帧率控制器让读取和推理同步到固定的帧率比如20帧而不是全力跑。4.4 常见问题速查表问题现象可能原因解决方案模型加载报错未安装Ultralytics或PyTorch版本不兼容检查pip list升级至PyTorch 2.0摄像头打不开权限未授权或端口被占用检查系统摄像头权限尝试换索引0为1推理帧率极低没有启用GPU确认安装了CUDA版PyTorch用model.cuda()EAR一直异常眼睛关键点索引错误打印关键点坐标对照COCO 17点索引表误报严重阈值设置不合理或未平滑降低EAR阈值加中值滤波用动态标定界面不刷新定时器未启动或线程阻塞检查timer.start()确保update_frame没抛异常打包成exe后无法运行缺少DLL或模型路径不对使用PyInstaller的--add-data参数打包模型和依赖4.5 训练自己数据集的注意事项标准的YOLOv8 pose模型官方在COCO数据集上训练过在驾驶员俯视摄像头视角下效果会有一定偏差。如果你需要的场景是监控驾驶室上方45度角最好用自采数据做微调。标注工具推荐用LabelMe标注姿态关键点时只需要标注眼睛、嘴角、手腕这些关键部位就够了不需要把全身所有点都标出来。数据集规模至少2000张不同光线条件下的驾驶员图片训练命令相比目标检测模型多了pose分支的参数设置。yolo pose train datacustom.yaml modelyolov8n-pose.pt epochs100 imgsz640我自己实测下来用自采数据微调50个epoch后在特定视角下检测稳定性提升非常明显尤其是暗光环境下的眼睛关键点召回率提高了不少。5. 项目扩展思路做完基础版之后这套系统还有几个明显的延展方向我建议可以根据实际需求加功能。一是多目标监测。当前代码只处理单个人如果副驾或后排也有人可以将检测结果遍历改为循环处理。不过要注意疲劳判断逻辑只对驾驶员有效需要通过目标框位置来判断谁是驾驶员一般是画面中心区域最大的那一个目标。二是报警联动。可以把疲劳报警信息写到MQTT或WebSocket上推送到远程监控平台这样车队管理者在后台就能实时看到每位驾驶员的疲劳趋势。我在实现时是写了一个简单的报警回调接口方便接入外部业务系统。三是取证数据存储。把触发报警前10秒的视频片段保存下来这在商用车事故溯源中非常重要。实现方式是用一个环形缓冲区循环覆盖存储最近的视频帧触发报警时把缓冲区的帧写入本地视频文件。四是用更轻量的模型替换YOLOv8比如在树莓派或Jetson Nano这种嵌入式设备上部署可以考虑使用NCNN或TensorRT把模型进一步压缩。做这个项目最大的体会是驾驶员状态监测真正的难点不在模型精度而在于把检测结果转化为稳定、可解释的业务指标。YOLOv8给了我们一个非常扎实的姿态估计底座但眼睛睁闭的判断阈值、哈欠的持续时间、手部靠近耳朵的判定距离都是需要在实际场景中反复打磨的细节。我写这套代码时每一步都在刻意保持模块之间的解耦等你有自己的数据集和场景需求时只需要替换detector或者调整FatigueDetector里的规则就能快速迁移到别的项目上。代码和UI界面的完整文件我已经整理好放在项目目录里照着环境搭建的部分从零跑通一次基本就能上手改自己的功能了。本文还有配套的精品资源点击获取
返回列表