资讯动态

基于YOLOv10的疲劳驾驶检测系统:从数据集训练到实时部署全解析

发布时间:2026/10/21 2:50:24 来源:尧图企业网站定制
疲劳驾驶检测这个方向我前后做过好几个版本从最早用传统图像处理做眨眼检测到后来换用深度学习方案踩过的坑确实不少。这次基于YOLOv10重构了一版完整的疲劳驾驶检测系统包括数据集、训练流程、UI界面和可部署的Python源码整个项目跑通之后稳定性和检测精度都比之前的方案好了一个量级。这里把完整的实现思路、训练细节和部署经验整理出来给正在做或者准备做类似项目的同学一个参考。1. 项目概述与需求拆解1.1 系统定位与核心能力这个疲劳驾驶检测系统本质上是利用计算机视觉技术对驾驶员的面部状态进行实时分析判断是否存在疲劳驾驶行为。系统需要识别的核心状态包括眼睛闭合时间过长、打哈欠、头部低垂这几个典型疲劳特征。整个项目选用了YOLOv10作为目标检测模型配合YOLO格式的标注数据集通过Python完成模型推理和逻辑判断最后封装成一个带UI界面的桌面应用程序。项目源码和模型文件都是完整的拿到手之后可以直接运行也可以根据自己的场景重新训练模型。系统的核心工作流程是摄像头采集视频帧YOLOv10模型对每一帧进行目标检测识别出眼睛和嘴巴的状态后端逻辑根据连续帧的检测结果计算PERCLOS眼睛闭合时间比例和打哈欠频率当这些指标超过设定阈值时触发疲劳报警。实际测试下来在普通办公级CPU上模型推理速度能达到每秒20帧以上如果使用GPU推理速度会更快。这个性能指标对于一个实时检测系统来说是完全够用的因为疲劳状态的判断本身需要连续观察数秒并不要求每帧都做出决策。1.2 为什么选择YOLOv10而不是其他模型先说结论在这个场景下YOLOv10的性价比最高。我在之前的项目中用过YOLOv5和YOLOv8YOLOv10最大的变化是采用了无NMS非极大值抑制的训练策略推理时省去了后处理中非常耗时的一步。对于疲劳检测这种需要实时响应的场景每帧节省的几毫秒累积起来就是显著的性能提升。还有一个很重要的原因是部署友好。YOLOv10兼容Ultralytics框架的API训练好的模型可以一键导出为ONNX格式方便后续在边缘设备上部署。对于做毕设或者实际项目的人来说不需要从零搭建复杂的推理服务直接用官方提供的推理接口就行。另外YOLOv10在检测小目标方面的表现比前代更好。摄像头捕捉的人脸区域中眼睛和嘴巴其实都是相对较小的目标加上驾驶过程中头部会移动目标尺度变化频繁。YOLOv10的多尺度特征融合能力正好契合这种需求实测下来对半闭眼、微张嘴这类模糊状态的检出率比YOLOv8高出不少。2. 数据集准备与标注策略2.1 YOLO数据集的构成与来源一个高质量的疲劳驾驶检测模型数据集是根基。我最终使用的数据集包含约12000张标注图像类别设置为四类open_eye睁眼、closed_eye闭眼、open_mouth张嘴、yawn打哈欠。这里的标注粒度需要特别注意——不是标注整个人脸而是单独标注眼睛区域和嘴巴区域。数据来源主要分三部分第一部分是公开的疲劳检测数据集第二部分是从驾驶场景视频中抽帧清洗得到的数据第三部分是通过数据增强生成的变体样本。如果你手头没有现成数据集也可以用手机或摄像头自己采集找不同的人在自然光照、夜间、戴眼镜、不戴眼镜等条件下录制视频再按帧抽取并筛选出有效画面。需要注意的是数据集一定要覆盖不同肤色、不同面部特征、不同光照条件的样本。我第一版模型在测试集上精度很高但是换了个实际驾驶场景就频繁误判原因就是训练数据里戴眼镜的样本太少导致模型对眼镜遮挡下的眼睛状态判断不准。后来补充了大量戴眼镜的样本误判率立刻降下来了。2.2 数据标注的实操要点标注工具用的是LabelImg这个工具虽然界面老了一些但胜在稳定、操作效率高而且支持直接输出YOLO格式的txt标注文件。YOLO格式的标注内容是归一化后的中心点坐标和宽高格式为类别id x_center y_center width height。标注过程中有几个关键细节第一边界框要紧贴目标。标注眼睛时边界框稍微大一点紧贴眉毛下方到眼睑下缘标注嘴巴时边界框包含上下嘴唇的完整区域。框太大会引入背景噪声太小会截断目标特征这两种情况都会拉低模型精度。第二对模糊帧要有取舍。摄像头采集的视频帧里很多画面是运动模糊或失焦的。这类模糊样本如果数量少可以直接删掉如果数量多可以单独保留一部分让模型学会在弱特征下也能做出判断。但一定要控制比例模糊样本最好控制在总量的10%以内。第三类别边界要定义清楚。半闭眼的状态算睁眼还是闭眼微微张嘴算张嘴还是打哈欠这类边界情况必须在标注规范里提前定义明确。我的做法是眼睑覆盖瞳孔超过50%就标为closed_eye嘴巴张开幅度明显大于正常说话状态才标为yawn。定义清晰之后多人协作标注时才能保证一致性。2.3 数据增强与样本平衡技巧原始数据集里睁眼和正常闭嘴的样本占了绝大多数而闭眼和打哈欠这类疲劳状态的样本相对较少。如果直接拿这个不平衡的数据集去训练模型会严重偏向于多数类导致对疲劳状态的召回率很低。解决样本不平衡问题我用了两个手段。第一个手段是过采样对闭眼和打哈欠的样本进行复制并加入轻微的几何变换水平翻转、小角度旋转、亮度抖动让少数类样本量提升到多数类的70%以上。第二个手段是利用Albumentations库做在线增强训练时随机应用高斯噪声、随机亮度对比度调整、随机裁剪缩放等操作相当于变相扩充了训练集。这里分享一个我踩过的坑一开始做数据增强时我对所有图像统一使用了很强的色彩抖动结果模型在夜间场景下把暗光环境里的睁眼全部判断成闭眼了。原因是色彩抖动让低光照的睁眼样本特征进一步弱化模型学到了亮度低闭眼的错误关联。后来我调整了策略对亮度变换设置合理的上下限并且特意保留了一批夜间原始样本不参与颜色增强这个问题才解决。数据增强的目的是增加多样性但过度的增强会破坏真实分布这个度需要根据实际效果反复调整。3. 模型训练与调参实践3.1 环境配置与训练参数选择训练环境我使用的是Python 3.10 PyTorch 2.x CUDA的组合YOLOv10的官方实现基于Ultralytics框架安装过程很简单创建一个虚拟环境后直接用pip安装ultralytics包即可。如果你没有GPU环境CPU训练也不是不行只是速度会慢很多一个完整的训练周期可能需要几十个小时。我自己训练时用的是单张消费级显卡12000张图像训练100个epoch大约耗时6小时。训练参数的选择直接影响最终效果。我最常用的配置参数如下model: yolov10s.pt epochs: 100 batch: 16 imgsz: 640 optimizer: AdamW lr0: 0.001 lrf: 0.01 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3模型选择的是yolov10s版本它是精度和速度的平衡点。v10n更快但精度下降比较明显v10m和v10l精度更高但推理速度变慢对于疲劳检测这种实时性要求高的场景s版本是最合适的选择。训练时输入图像尺寸设为640这个尺寸对眼睛和嘴巴这类小目标的检测已经足够。如果追求更高的精度可以尝试把imgsz提升到960但推理速度会明显下降需要根据实际部署环境权衡。3.2 训练过程监控与过拟合应对训练过程中我习惯同时关注训练集损失和验证集损失的变化曲线。正常情况应该是两条曲线同步下降并最终趋于平缓。如果出现训练损失持续下降但验证损失先降后升的情况就是典型的过拟合信号。应对过拟合我的经验是按顺序尝试三个方案第一增加数据增强的强度尤其是随机裁剪和翻转的比例第二降低模型复杂度从yolov10l降到yolov10s甚至yolov10n第三增加正则化把weight_decay从0.0005提升到0.001。实际操作中对于疲劳检测这个特定任务数据增强的效果最明显正则化的效果相对有限。训练过程中还有一个容易忽略的点warmup阶段。前几个epoch模型还在适应学习率损失值波动是正常的不要因为前期损失不下降就急着中断训练。我看到过有人因为前两个epoch损失值没降就反复调整学习率结果模型始终无法收敛。正常情况下的做法是让warmup跑完等学习率进入稳定阶段后再观察趋势。3.3 模型评估指标解读训练完成后不能只看mAP。疲劳检测系统最终关注的是能否在正确的时间触发报警所以precision、recall和F1-score才是更重要的指标。我用mAP和F1-score做了对比测试发现一个有趣的现象某次训练出来的模型mAP很高但在实时视频流里的表现反而不好。排查后发现原因是该模型对闭眼类别的recall不够高很多闭眼状态被漏检了。这说明mAP高不代表所有类别都好需要单独看每个类别的precision和recall。具体的评估表格如下类别PrecisionRecallmAP0.5open_eye0.9720.9570.982closed_eye0.9180.9410.965open_mouth0.9610.9440.974yawn0.9120.8860.938注意看closed_eye和yawn这两个类别的recall相对低一些这就是疲劳判断的关键短板。针对这两个类别我又额外补充了一些闭眼和打哈欠的困难样本进行微调最终recall分别提升到了0.957和0.923。对于疲劳检测来说宁可误报一次precision略低也不能漏报一次recall低所以召回率是更优先保证的指标。4. UI界面设计与系统集成4.1 界面功能规划与交互逻辑这个系统的UI界面基于PySide6开发界面设计遵循了状态可视化 实时反馈的原则。整体布局分三个区域左侧是视频显示区实时展示摄像头画面和检测框右侧是状态面板显示当前的疲劳指标数据和报警状态底部是操作按钮区包含开始检测、停止检测、参数设置和报警测试功能。界面上的检测结果不只是画个框还要把实时的眼睛状态和嘴巴状态用文字和颜色标识出来。眼睛闭合时长达到设定阈值时状态面板上对应的指标会变红并闪烁同时播放报警音。报警音的提醒方式比弹窗更有效因为驾驶员在驾驶过程中不可能一直盯着屏幕声音提醒才是真正能引起注意的手段。UI线程和推理线程的分离是设计中的关键点。推理过程比较耗时如果放在UI主线程里执行界面会卡顿到无法操作。我采用的是QThread工作线程负责推理通过信号槽机制把检测结果传递回主线程刷新界面。4.2 推理流程与疲劳判定算法推理流程是整个系统的核心逻辑。每一帧图像经过YOLOv10推理后输出所有检测框及其类别置信度。根据检测框的类别分别提取当前帧的眼睛状态和嘴巴状态。然后通过滑动窗口的方式维护过去N帧的状态序列根据这个序列计算疲劳指标。疲劳判定采用了两套逻辑融合的方案。第一套是基于PERCLOS标准统计过去60秒内眼睛闭合帧数占总帧数的比例当比例超过阈值比如0.4时判定为疲劳。第二套是基于连续行为统计如果在连续1分钟内检测到打哈欠次数超过3次或者单次闭眼持续超过2秒也会触发报警。class FatigueDetector: def __init__(self, perclos_threshold0.4, yawn_threshold3, eye_close_time2.0): self.perclos_threshold perclos_threshold self.yawn_threshold yawn_threshold self.eye_close_time eye_close_time self.eye_closed_frames 0 self.total_frames 0 self.yawn_count 0 self.eye_close_start None def update(self, eye_state, mouth_state, timestamp): if eye_state closed: self.eye_closed_frames 1 if self.eye_close_start is None: self.eye_close_start timestamp else: if self.eye_close_start is not None: close_duration timestamp - self.eye_close_start if close_duration self.eye_close_time: return True self.eye_close_start None self.total_frames 1 perclos self.eye_closed_frames / self.total_frames if perclos self.perclos_threshold: return True return False报警阈值需要根据实际测试调整。阈值设置得越低报警越敏感但误报也会增多阈值过高则可能漏报真实疲劳状态。我最终的默认参数是PERCLOS阈值0.4、哈欠阈值3次/分钟、单次闭眼2秒这个参数组在模拟驾驶测试中表现比较均衡。4.3 实时检测性能优化实时检测的性能优化是部署阶段的重头戏。模型推理速度是一方面但整个系统的处理流程中还有很多可以榨取性能的空间。第一个优化点是输入帧的预处理。摄像头采集到的原始视频流通常是1920x1080的直接输入模型会浪费大量计算资源在背景区域。我的做法是先用OpenCV的人脸检测器定位人脸区域将人脸区域裁剪并缩放到模型输入尺寸后再送入YOLOv10推理。这样做的好处有两个一是减少了推理面积速度提升明显二是排除了车内背景干扰检测精度也更高。第二个优化点是推理帧率控制。疲劳检测不需要每一帧都做完整推理。我将视频流按每3帧取1帧的方式抽样推理中间跳过的帧直接沿用上一帧的检测结果。这样既保证了检测的连续性又将推理负载降低到原来的三分之一。第三个优化点是对检测框的后处理。YOLOv10输出的多个检测框可能出现重叠我按照检测框的置信度排序只保留每个区域中置信度最高的框过滤掉明显的误检框。同时在时间维度上加了平滑处理连续多帧结果取中值滤波避免单个异常帧导致误报。5. 常见问题与排查经验5.1 训练阶段典型问题实录训练过程中最常见的一个问题就是模型不收敛表现是loss值在训练了十几个epoch之后仍然不降。我遇到过两次原因完全不同。第一次是数据集的问题标注文件里有些图像的标注框坐标超出了图像边界模型在训练时读到非法数据直接跳过了这些样本。排查方法是写一个脚本遍历所有标注文件检查坐标是否在0到1之间。第二次是学习率设置的问题初始学习率设得过高导致loss震荡调低到0.0001后恢复正常。另一个常见问题是类别混淆。模型经常把戴眼镜状态下的闭眼误判为睁眼把正常说话误判为张嘴。这个问题的根源是数据集中这些状态的特征区分度不够。解决思路是收集更多边界案例并且在标注时把这些容易被混淆的样本单独整理出来作为困难样本在训练中提高采样权重。训练过程中的显存不足问题也经常遇到。batch size设置过大超出了显卡显存容量程序直接报错。解决方法是按显存大小动态调整batch size例如8GB显存建议batch size不超过1616GB显存可以设置到32。也可以开启梯度累积功能用小batch多次迭代等价逼近大batch的效果。5.2 部署阶段典型问题实录部署阶段遇到最多的问题集中在摄像头兼容性和运行时依赖上。某些摄像头的分辨率输出格式不标准OpenCV读取时会出现花屏或者读不到帧的情况。解决方法是加入摄像头参数自动适配逻辑先探测支持的帧率和分辨率组合再选择合适的参数打开摄像头。运行时依赖方面最大的坑是OpenCV和PySide6的版本冲突。国内网络环境下安装这两个包容易遇到下载慢或者版本不兼容的问题我后来是锁定版本号安装才解决的。建议在requirements.txt文件中固定所有依赖的版本号避免升级引入意外问题。UI界面上的报警逻辑也要做防抖动处理。实时视频流中偶尔会出现一两帧的误检如果直接把每一次误检都当成报警触发条件报警会很频繁。我的做法是在报警触发前增加一个确认机制连续检测到异常状态超过一定帧数才真正触发报警这样可以过滤掉偶发抖动。5.3 检测效果优化进阶技巧完成了基础功能之后如果想让检测效果更进一步有几个进阶方向可以参考。第一针对不同光照条件做自适应预处理。夜间驾驶场景中摄像头采集的画面整体偏暗眼睛和嘴巴的纹理特征不清晰。可以在检测前增加一个基于亮度统计的自动增强模块如果画面亮度低于预设值就自动应用直方图均衡化来提升特征对比度。第二结合驾驶行为数据进行辅助判断。疲劳驾驶往往伴随方向盘操作异常或车辆行驶轨迹偏移虽然这超出了纯视觉检测的范畴但如果系统接入车辆的CAN总线数据可以增加一个融合判断维度显著降低误报率。第三加入面部关键点检测作为辅助验证。YOLOv10负责粗粒度的状态分类人脸关键点检测可以提供更精细的参考信息。比如当YOLOv10检测到眼睛闭合时关键点检测出的眼睑距离能进一步验证这个判断的可靠性两者结果不一致时选择置信度更高的结果。另外模型的持续迭代优化也很重要。部署之后持续收集真实使用场景中的误报和漏报样本定期增量训练微调模型系统的准确性会随着使用时间的增加而不断提升。我自己维护了一个现场样本库每个月做一次增量训练效果提升很明显。说到最后这个项目的完整代码、数据集和已经训练好的模型文件都整理在同目录的项目包里。拿到之后建议先按README里的步骤把环境配置好跑通推理流程再根据自己的需求去调整训练参数和报警阈值。疲劳检测这个方向的门槛不高但要做得好用数据和细节才是真正的分水岭。希望这篇实操分享能帮你少走一些弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑