首页 / 资讯中心 / 文章详情

基于YOLOv10的疲劳驾驶检测系统:从数据训练到UI部署全流程

基于YOLOv10的疲劳驾驶检测系统:从数据训练到UI部署全流程 ★ FEATURED ARTICLE
做疲劳驾驶检测这个方向最开始其实是被一个做车队管理系统的朋友拉上船的。他希望给自己的车载设备加一个驾驶员状态监测功能用来做安全预警和事后追溯。我去逛了一圈开源社区发现大部分demo还停留在检测到眼睛闭了就叫一声的水平能实际落地到界面里、跑成闭环的案例很少。正好赶上YOLOv10这套新框架出来就顺手把整条链路梳理了一遍从数据集、训练、推理到带UI的完整项目全套折腾下来觉得这条技术路径的性价比确实不错。这套系统本质上解决的是人在驾驶过程中注意力下降靠机器替人盯住状态的问题。核心玩法是用YOLOv10作为检测引擎先在数据集上训出能够识别眨眼和打哈欠等疲劳特征的模型再用Python把模型封装进一个独立的UI界面。你打开界面摄像头画面上会实时框出人脸的眼睛和嘴巴区域配上帧率、报警状态这些指标一旦检测到闭眼持续超过阈值或者哈欠频率过高系统就触发声光报警。整个过程不需要打字输入没有复杂的命令行交互打开就能巡航监控。这篇文章适合正在做毕业设计、想参加竞赛或者准备把计算机视觉项目落地成产品原型的人。哪怕你之前只跑过YOLOv5的官方例程也完全可以顺着下面的操作复现出来。我会把每一步的技术选型原因、训练过程中的实际踩坑、UI封装时容易翻车的地方都摊开讲尽量少让你们走弯路。1. 为什么用YOLOv10来干这件事而不是继续用YOLOv81.1 疲劳检测场景对检测器的真实需求想判断一个人是否疲劳视觉上最稳定的信号是眼睛和嘴巴的状态。研究者们定了一堆客观指标其中最核心的两个是PERCLOS单位时间内眼睛闭合时间占比和打哈欠频率。PERCLOS超过一定阈值基本能推断疲劳倾向打哈欠频率骤增则是另一条强信号。所以后端检测器的任务非常具体在连续的视频帧里把左右眼区域和嘴巴区域稳定地框出来并且给每个框打上状态标签——眼睛是睁还是闭嘴巴是正常还是打哈欠。注意这个任务跟通用目标检测还有点区别目标都是小尺寸器官变化幅度不大可环境光照和遮挡情况非常随机。驾驶员可能戴眼镜、墨镜可能刚进隧道光线骤变也可能侧头跟副驾说话。这些都会直接影响单帧检测的稳定性。1.2 YOLOv10相比前代的关键进步YOLOv10是在之前版本基础上的又一次大迭代。对做疲劳检测的人来说最值得关注的是它把模型的延迟又往下压了一截。检测模型在边缘设备上跑一帧推理时间直接决定了系统能跟多高的摄像头帧率组合。以前用YOLOv8s在普通CPU上跑勉强有个两三帧每秒放到实际驾驶场景就明显卡顿。YOLOv10引入了一种端到端检测的优化思路省掉了一些重复计算损耗让推理速度在同精度水平下更快量化部署也更友好。有一点需要说清楚YOLOv10在结构上往前走了一步但对训练数据的要求并没有降低。它模型更轻、收敛更快可一样得靠高质量标注数据喂着否则再好的网络结构也是巧妇难为无米之炊。我后面会花大篇幅讲数据集因为这个才是真正决定系统上限的环节。1.3 选型时对比过哪些方案在选择技术路线的时候我认真对比过三条路。第一条路是传统方案用OpenCV的人脸关键点检测拿到眼睛和嘴巴的坐标然后直接计算眼睛纵横比EAR和嘴巴纵横比MAR来判断开闭。这个方案实现简单几十行代码就能跑起来对硬件要求极低。但它的缺点非常致命对姿态变化极其敏感驾驶员稍微侧脸关键点就漂移误报率会高到没法用。另外它对光照变化的鲁棒性也很差白天和夜间的表现完全是两个系统。第二条路是把人脸检测和疲劳分类完全拆开做先用轻量人脸检测器拿框再把人脸区域送入一个分类CNN直接输出正常/困倦这种顶层结果。好处是逻辑清晰坏处是中间多了一步误差会叠加。而且分类网络给不了你眼睛和嘴巴的具体状态后续想扩展更细的行为分析比如频繁揉眼睛、点头打瞌睡会很吃力。第三条路就是我选的方案用目标检测一次到位直接框出眼睛和嘴巴并做状态分类。这种检测式做法的优势在于模型自动学习到了器官的局部特征和空间关系对姿态抖动的容忍度更佳。YOLOv10在精度和速度上很好地平衡了这个需求最终定下来用它作为主检测器。2. 数据是这套系统的地基开源数据集的搭配策略2.1 可用的开源数据构成盘点疲劳驾驶检测起步时最容易被忽略的就是数据准备。很多人习惯于下载一个现成的数据集就开训等验证集指标一出数据集本身存在的类不均衡、场景单一问题马上就变成模型精度的天花板。我训练时用的是标准的YOLO标注格式数据集核心覆盖两类器官状态眼睛的睁与闭嘴巴的张开—这里直接用打哈欠的开来简化—与闭。整体样本量我控制在两万张左右其中闭眼和打哈欠的样本占到了四成以上。之所以刻意抬高低频状态的占比是因为疲劳状态本身在驾驶过程中是偶发事件如果按真实分布去采样模型根本见不到几次闭眼和打哈欠的正样本学完就会变成永远输出正常的极端偏向模型。开源数据的组合策略上我采用了两类来源混合。一类是室内固定场景下采集的模拟疲劳状态图像这类数据结构干净器官位置标准适合做基础训练另一类是车载环境镜头的真实道路画面这类数据光照复杂框的抖动大但对模型的实际泛化能力至关重要。两边按7比3的比例混合既保证了模型先学会眼睛和嘴巴长什么样又逼着它适应驾驶室里长什么样。2.2 标注环节的细节才是关键拿到原始图像之后标注环节直接决定模型上限。很多人以为标注就是画框选个类别就够了但实际上眼睛和嘴巴这种目标有它的特殊性。首先是标签粒度问题。眼睛闭着这个状态有不同阶段从睁眼到闭眼中间有个半闭形态画框的时候最容易犯的错误是把这个过渡帧也标成闭。标注规范必须统一成眼皮闭合超过一半才标闭眼否则模型学到的边界会被这个灰色地带搅浑推理时就会出现闭眼检测置信度忽高忽低的怪现象。其次是嘴巴状态和说话状态的混淆。司机正常说话时嘴巴也在张开跟打哈欠的张开在形态上有重叠。光靠单帧图像模型确实很难区分。我的做法是标注时只选动作幅度大、持续时间足够的典型打哈欠帧把日常说话的那种微张排除在正样本之外。这等于人为降低了任务难度让模型先抓住哈欠区别于说话的核心视觉差异。2.3 数据增强策略别让模型只会看原图数据增强是疲劳检测绕不开的一环。驾驶场景中光照转变剧烈早上迎着太阳、中午进地库、晚上对面车灯晃过来每一种情况都会让图片的整体亮度、对比度和色彩偏向发生剧烈变化。如果模型是在环境单一的清晰图上训练出来的到了实际行驶中很容易因为一个逆光就断了检测。我对训练图做了一批针对性的增强操作亮度范围在-30%到30%之间随机抖动对比度按0.8到1.2的系数浮动水平翻转按50%概率随机。另外还做了一个小角度的随机旋转和缩放模拟摄像头安装误差以及车身颠簸带来的画面偏移。这些操作在训练时是实时进行的模型每看一遍图都会看到微调过的版本相当于用数据量换鲁棒性。尤其值得说的是马赛克增强。YOLOv10官方训练默认开了Mosaic把四张图拼成一张训练这能帮助模型学到更丰富的上下文信息。但在疲劳检测里目标小、人脸占比小Mosaic反而可能把人脸切成碎片。我在训练后期把Mosaic的概率降了下来避免模型在大型拼图图上学习到过于碎片化的器官特征。3. YOLOv10训练工程的完整拆解3.1 训练环境与依赖版本把代码跑起来之前先把环境锁死。这是最不性感但最重要的步骤版本不匹配会让你在排查问题的时候怀疑人生。我用的是Python 3.10搭配PyTorch 2.1的组合CUDA用到12.1。YOLOv10的官方工程已经帮你写好了依赖列表直接装就行但几个核心库的版本我建议手动卡一下opencv-python别追最新版4.8或者4.9都比较稳ultralytics这个基础库版本需要和YOLOv10的代码分支匹配我当时用的是基于某一个较新的commit整体来说比YOLOv8时代更容易装。硬件方面我在一台RTX 4060 Ti 8GB显存的机器上完成了全部训练。这个显存规模在目前的主流配置里很有代表性。如果你只有6GB显存把输入分辨率从640降到480或者把batch size缩到8问题也不大。纯CPU训练基本不现实建议有NVIDIA显卡再上手此坑。3.2 训练关键参数的逻辑推导训练参数不是一个模板抄下来就完事的参数之间都是耦合关系。我把自己在config里比较关键的一组参数列出来做个解释。img-size: 640。这个尺寸在检测精度和推理速度之间是最常用的平衡点。眼睛嘴巴属于小目标输入分辨率再低会出现细节丢失框的位置开始跳变再往上加到960训练时间变长对小目标的收益却边际递减。batch-size: 16。在8GB显存下16是相对安全的数值。batch越大梯度更新越稳定但显存不够会直接OOM。实测batch 16足够。epochs: 200。我在训练到120轮左右的时候发现验证集loss基本走平后面几十轮纯粹是在微调。如果你时间紧100轮也能出一个可用的模型但200轮会让边界框的稳定性更好。optimizer: SGD还是Adam。YOLO系列一贯偏好SGD的收敛稳定性。实际对比下来SGD训练后期定位更准Adam前期的loss下降更快但到了100轮之后往里挤精度的时候反而容易震荡。最终往长线训我还是选SGD。lr0: 0.01配合余弦退火衰减。这个学习率对YOLOv10来说是官方推荐的基线。如果你换成其他学习率一定要一起调整weight decay否则很容易出现前期loss就崩掉的局面。3.3 完整训练及验证流程整个训练流程分三步走。第一步是组织数据集目录标准YOLO格式根目录下建train和val两个文件夹每个文件夹里再拆成images和labels两个子文件夹labels里面每张图对应一个同名txt文件每行内容依次是类别ID 归一化中心x 归一化中心y 归一化宽 归一化高。第二步是写一个数据描述yaml文件定义类别数、类别名、训练集路径和验证集路径。注意这个文件里的路径前缀要指到数据集根目录的绝对路径很多人就是漏了这一步导致训练时一直找不到数据。第三步是写训练脚本。核心就十几行代码大致是加载yaml配置、指定预训练权重、设定好epoch数和batch等参数然后调用训练方法。第一次训练建议加载COCO上的预训练权重来做迁移学习相当于让模型继承见过大量目标的通用特征提取能力比自己从头学要快得多。我实测下来用预训练权重冷启动前面二十轮就能看到明显的loss下降而从头训练前五十轮基本在挣扎。训练结束后验证环节别只看总体的mAP。在疲劳检测这里你要单独拆出闭眼和打哈欠这两个类别的精确率和召回率来看。总体mAP高不代表疲劳状态检得准因为睁眼和正常嘴巴这两类占了样本的大头模型只要把它们分对整体分数就会被拉上去而真正的少数类表现如何反而被掩盖了。我精确到类别记了一个指标表类别精确率召回率说明睁眼0.9570.948头部姿态变化时偶有漏检闭眼0.9220.917戴深色墨镜时置信度会掉嘴巴正常0.9340.941与说话状有少量混淆打哈欠0.9090.886小幅度哈欠时有漏框这个指标对实际驾驶监测来说已经够用主要是闭眼和哈欠的召回率还有上升空间。后来我把闭眼的置信度阈值往下调了一点虽然带来了少量误报但换来了更灵敏的疲劳预警。4. 把模型封装成带UI界面的完整系统4.1 系统架构别把UI和检测逻辑炒在一锅里训练好模型离一个能用的系统还有很远的距离。很多人把检测代码和界面代码写在一个文件里摄像头推流、模型推理、按钮事件、报警逻辑全部纠缠在一起。这个开发阶段看起来快但一遇到卡顿、黑屏、报警失灵排查起来会非常头大。我的架构选择是把系统拆成三层采集层负责打开摄像头、读帧、把帧送到推理模块推理层加载YOLOv10模型对帧做预处理缩放、归一化执行推理解析检测结果并整理成结构化数据比如当前是否有闭眼、是否有打哈欠、置信度多少、框的位置坐标是什么表现层UI界面负责画面显示、统计信息展示、报警触发。层与层之间通过数据流衔接UI不直接接触模型模型也不管界面怎么画。这样做的好处是以后你要换模型、换摄像头、加报警方式改动都只局限于对应层里。4.2 UI框架选型为什么用PyQt而不是Web方案UI界面这一块我在PyQt5和Web方案比如Flask加网页前端之间犹豫过一段时间。Web方案的好处是展示形式灵活你可以在浏览器里看到画面甚至可以远程访问。但问题也很明显要做实时视频流推送需要处理HLS或WebRTC整套链路非常重而且延迟很难压到可感知的范围以下。对本地单机演示和实际部署来说PyQt5的本地窗口方案明显更轻、更稳。PyQt5的逻辑是一个主窗口承载摄像头画面用QLabel来显示每一帧图像。后台用一个定时器或者专用线程去读取摄像头数据、跑推理再把处理完的帧转成QPixmap格式塞给界面。这样可以把耗时的推理任务从UI线程里剥离出来避免界面卡死。4.3 界面功能设计与细节实现界面的功能布局上我坚持操作最少化的设计理念。你打开程序一个窗口窗口里就是实时摄像头画面画面上覆盖着检测框和标签画面下方一行小字显示当前帧率、检测到的疲劳状态、连续闭眼次数窗口右侧或者顶部几个按钮分别是开始检测、停止检测、保存截图和报警开关。检测结果展示这块有个细节值得讲一下框的颜色要有明确的语义。我的设计是正常状态一律用绿色框并配上Eyes Open或Normal Mouth的标签一旦检测到闭眼框立刻变成红色并显示Eyes Closed打哈欠则用橙色框。人眼对颜色的反应比对文字的反应快得多这种设计在驾驶员瞥一眼屏幕的瞬间就能掌握状态。报警模块我单独用一个线程来管理。它接收推理层的状态信号内部维护一个连续帧计数器。闭眼连续出现的帧数超过预设阈值比如超过10帧且持续约0.33秒以上判定为一次潜在疲劳信号触发蜂鸣器报警。同时界面上还会弹出一条红色的文字提示。为了防止一秒内反复报警烦人每次报警后有个2秒的静默期在这个窗口内即使再次触发条件也不会重复响。报警阈值的设定逻辑值得单独说明连续帧数不能设得太低否则司机正常眨眼也会被判成疲劳也不能设得太高否则检测到闭眼状态后反应太迟钝预警就失去意义。我这里结合摄像头帧率按约3秒的持续闭眼时间来反推帧数阈值。虽然每个人正常的眨眼时长也在100多毫秒但真正进入疲劳状态时闭眼时间显著加长且频率变密集通过连续计数能较好地区分两者。4.4 核心推理引擎的代码组织模型推理部分的核心逻辑不复杂但是有几个关键点值得写出来。加载模型时我用的是从训练好的权重目录导出ONNX格式再加载推理而不是直接在推理脚本里加载PyTorch权重。用ONNX的好处是推理速度更快且部署时不依赖训练使用的环境框架版本。导出那一步在训练完成后顺手就做了然后界面这边的推理代码只需要不到十行的ONNX会话加载逻辑。代码里的核心循环是这样组织的从摄像头读取一帧BGR图像先调整尺寸到推理输入要求做归一化然后送入模型执行推理拿到输出数组。输出经过解析过滤保留置信度高于设定阈值的检测框再用非极大值抑制去掉重叠框最后把有效框的坐标映射回原图像尺寸。整个过程在一帧内的耗时在普通桌面级GPU上控制在十几毫秒量级。这里要特别提醒一个问题UI上显示的画面和模型推理所见的画面两者分辨率不用完全一致。显示画面使用原始摄像头分辨率就行而模型推理前需要把画面压到640x640这个缩放并不会导致检测精度大幅下降。你不需要在界面上把画面也缩成640x640否则会觉得字都糊了。4.5 打包发布把项目变成普通用户也能跑的最简形式界面跑通后最后一步是把项目整理成可发布的形态。我有两个忠告第一千万别只在你自己那台配置了全套环境的电脑上跑。一定要在没有装过PyTorch的全新Windows机器上验证一遍否则你会被一堆缺失依赖的报错淹死。第二尽量提供两种形态。一种是为开发者准备的源码运行方式配一个requirements.txt安装完依赖直接运行入口脚本另一种是像我这样用打包工具把程序打包成exe可执行文件这样不熟悉Python的人也能双击启动。打包过程中最痛苦的几个点一是打包出来的exe体积巨大因为ONNX Runtime和OpenCV这些库都很重压在500MB以上很正常二是模型文件要放到外层资源目录否则打进exe内部会异常难调用。我的建议是模型文件作为外部附件放着和exe同级或放在指定子目录下别硬整合进执行体内。5. 实测环节与反复踩坑之后的调整记录5.1 第一次实测模型全绿但系统几乎不可用第一批模型训练完我信心满满地架起摄像头对着屏幕做了一分钟测试。结果非常尴尬单帧检测指标在测试集上看起来漂亮实际一连续跑起来画面上检测框疯狂跳动标签在睁眼和闭眼之间反复横跳报警声更是响得跟警报器一样。冷静下来排查后发现问题根本不在模型本身而在我之前提到过的状态持续性这个问题上。模型每一帧都在独立判断眼睛状态帧与帧之间没有时序关联。真实情况下司机眨一次眼可能持续两三帧这几帧的检测结果会连续输出闭眼可模型偶尔也会在正常睁眼状态下因为一个低头动作抢出一个低置信度的闭眼框。如果系统不加逻辑约束直接被这种噪声带着走。解决办法是引入帧间状态滤波。我给帧加了一个临时状态计数器只有连续若干帧检测到闭眼时才真正判定为进入闭眼状态只要中间有一帧检测到睁眼计数器就清零重置。这样一来单帧抖动被吸收掉了系统的输出状态变得平滑稳定。同样的逻辑也应用于哈欠检测。这个改动对公司界面上指标的影响是决定性的报警准确率肉眼可见地上升了一大截。5.2 第二次翻车光照变了就失明模型在室内固定光源下一切正常我把系统挪到窗户边阳光从侧面打进来画面里出现大量阴影检测率立刻断崖式下跌。观察了一段时间发现人脸一进入逆光区域眼睛区域直接糊成一片别说检测闭眼还是睁眼了连眼睛这个目标本身都很难被框出来。这个问题处理方法分两路。一路从硬件侧解决优先选择支持宽动态范围补偿的摄像头它能在明暗差异大的场景下尽量保留暗部细节。另一路从算法侧优化我在摄像头读取帧之后加了一个快速自适应直方图均衡化预处理压缩光照差异让眼睛区域的纹理更突出。这个处理大约消耗每帧1毫秒左右对整体速度影响很小但逆光场景的检测稳定度提高了不少。5.3 第三次教训戴眼镜的人到底怎么检疲劳检测面临的另一个特殊场景是驾驶员戴眼镜。普通近视镜还好说但太阳镜尤其是偏振墨镜会严重遮挡眼睛区域的红外和可见光特征。我在测墨镜样本时闭眼检测准确率掉得比较多界面上时不时出现睁眼误报。这事的本质是训练数据里戴墨镜的样本太少。开源数据集大多是在实验室模拟环境下采集的很少有人戴着墨镜去录数据。要解决这个问题必须去补充数据我后面专门录制了一批戴墨镜转头、点头、闭眼的视频抽帧后补充标注进训练集。这个办法朴素但有效重训一轮后墨镜场景的召回率回升明显。说白了数据增强解决不了数据本身没覆盖的问题该采集还得采集。5.4 关于报警策略的最终手感报警的灵敏度调了几版后我逐渐形成了一套自己的经验。判断疲劳不能只看单一信号最好把多个维度的信息做加权考量。纯靠闭眼超过3秒这一条规则长时间直行公路上驾驶员偶尔眯一下眼就可能误报。但如果你把连续闭眼和单位时间内打哈欠次数结合来判且两者同时超限才触发高等级报警系统的可用性会大幅提升。我最终实现的是两级预警第一级是黄色预警检测到闭眼帧数超阈值或哈欠频率偏高界面上提示疑似疲劳请注意休息第二级是红色报警两种疲劳信号同时出现且持续一段时间立刻触发声音报警并录像。这样既保留了灵敏度又相对克制不至于一路响个不停。6. 再往前一步这套系统的扩展可能项目做完后很多同学问我的第一句话是能不能把这套系统拿去做手机端或者车载嵌入式设备理论上当然可以但需要做几层手术。第一层是模型侧把YOLOv10导出成嵌入式友好的格式做量化推理精度会掉一点但换来的是能上树莓派甚至某些算力盒子。第二层是感知侧现在只用了摄像头一个传感器如果加上方向盘转角传感器、车速信号甚至驾驶员心率手环就形成多模态疲劳检测准确性会有质的飞跃。第三层是产品侧把报警事件对接进车队管理后台自动推送消息给调度员那就是一整套车队安全管理系统的雏形。从技术成长角度看这个项目覆盖的链条足够完整从数据采集标注到模型训练调参再到推理优化和GUI封装最后到部署验证。你走完一遍等于把计算机视觉落地到真实业务场景的大部分坑都踩了一遍。正是这些坑让你的代码从一个能跑的demo变成能用的工具。最后说说我个人在这套系统里最大的感受。做疲劳驾驶检测最忌讳的是把模型精度当作唯一目标真正重要的是系统在连续运行的几十分钟里能不能保持稳定的输出、合理的报警频率和让人信服的交互反馈。这些看着不起眼的工程细节远比单张图片上的mAP数字更能决定这个系统是否值得被信任。
阅读完成 · 觉得有帮助?
咨询建站