YOLO驾驶员疲劳检测实战:从数据标注到TensorRT部署

发布时间:2026/10/1 13:40:50
YOLO驾驶员疲劳检测实战:从数据标注到TensorRT部署 简介这套资源面向自动驾驶、安全监控等领域的开发者和研究者围绕驾驶员疲劳检测提供YOLO算法模型与配套数据集可识别闭眼、打哈欠等典型疲劳行为适合作为预警系统开发、算法学习或毕业设计的参考方案。压缩包内共含2000个文件以1984个txt标注文件为主另有13个md说明文档、2个pdf和1个yaml配置文件整体大小约306MB。数据集标签同时提供txt与xml两种格式分别存放于不同文件夹既可用于YOLO训练也方便配合LabelImg进行查看。目前已有1139人学习下载。资源中除模型权重与标注数据外还保留了完整的目录结构和说明文档能够帮助使用者快速理解数据组织方式、训练参数与配置方法免去手动标注和整理数据的重复劳动是快速上手驾驶员疲劳检测的实用资料。1. 用YOLO做驾驶员疲劳检测把“框住眼睛”变成“判断状态”的技术路线把 yolo算法、驾驶员疲劳检测模型和数据集这三件事拼在一起得到的不是一份PPT而是一条从课题验证到真机部署的完整技术路线。疲劳驾驶是事故率最高的诱因之一方向盘转角传感器和车道偏移告警都只能事后补救真正能提前几秒发现困倦的信号还是来自驾驶员面部本身——闭眼时长、打哈欠频率、低头角度。这个项目解决的就是“在行车画面中稳定地框出这些状态”而不是传统意义上的“检测驾驶员这个人”。任务拆开看本质是两段式先用目标检测模型在画面里定位眼睛和嘴部区域再根据这些区域的状态判断是否疲劳。YOLO负责前一段后一段靠PERCLOS这类时序指标完成。这套方案对刚接触目标检测的工程师很友好数据集标注成本可控模型大小也压得住车载边缘设备。适合正在做课题的学生、准备把视觉检测落到车机上的算法工程师以及想要快速验证“视觉疲劳监测是否可行”的项目负责人。2. 算法选型为什么目标检测方案比关键点方案更适合疲劳状态识别2.1 疲劳检测的任务本质状态框检测与PERCLOS的互补关系疲劳检测行业里公认的量化指标是PERCLOS即单位时间内眼睛闭合帧数占比。闭眼时间比例超过阈值就判定为疲劳状态。这个指标本身不关心模型怎么找到眼睛只关心“每一帧里眼睛是不是闭着的”。所以后端算法不玄学就是一个时序统计问题真正决定成败的是前端能不能稳定地拿到“眼睛状态”这个输入。YOLO方案在其中的角色是直接输出“眼睛闭合”“打哈欠”“低头”这类状态框。模型不看整张脸而是把局部区域的视觉特征映射成状态类别。相比之下关键点方案比如人脸网格、MediaPipe先定位眼睑、嘴角等几十个关键点再用几何指标判断开闭。这个方案在实验室环境里效果不差但到了真实驾驶场景就容易翻车半遮挡时关键点直接漂移逆光时眼睑轮廓丢失嵌入式平台上还得同时跑人脸检测和关键点回归两个模型帧率压力大得多。实践中还有一种混合做法用YOLO先框出眼睛和嘴巴区域再在框内用EAR眼睛纵横比做状态判断相当于让YOLO只负责“找得准”让几何算法负责“判得稳”。这种方案的好处是YOLO不需要去学“闭眼”和“睁眼”这种细微差异减少模型分类负担眼部框得够稳EAR的阈值调节也更可控。但纯YOLO方案的优势在于端到端一个模型推理一次就拿到所有状态类别管线更短。2.2 小目标检测为什么难眼部区域在画面中的像素占比低驾驶员监控摄像头通常装在后视镜或仪表盘上方画面里人脸区域大概占1/3而一只眼睛可能只有30×20像素甚至更小。这个尺寸在目标检测里属于典型的小目标.。YOLOv8在检测头设计上做了不少适配anchor-free的分布式焦点回归让边界框定位更稳但小目标的特征在深层特征图里被压缩得厉害所以训练时要把输入分辨率提上去。imgsz设到640是底线眼部区域占比低的时候建议直接上960代价是推理耗时增加。车载设备上一般会给两个档位白天高分辨率检测夜间降分辨率换帧率。模型的选择上我在Jetson这类边缘设备上常用的是YOLOv8n和YOLOv5s两个档位。二者对比如下模型输入分辨率单帧推理耗时Jetson Orin NX小目标召回表现适用场景YOLOv8n640约15ms眼部小目标误检偏多快速验证、低功耗设备YOLOv8m640约35ms小目标召回率明显提高精度优先的项目YOLOv5s640约10ms眼部区域召回尚可老设备兼容部署成熟如果只是跑通流程YOLOv8n够用但做疲劳检测这种对漏检容忍度低的场景我一般会先用YOLOv8m验证精度天花板确认性能满足要求后再考虑蒸馏或量化到轻量模型。2.3 公开数据集与自采数据的取舍先跑通再增量公开数据集方面NTHU-DDD驾驶员分心检测视频集和YawDD打哈欠视频集是两类经常被用到的起步材料。它们的优点是标注类别和疲劳场景相关拿来就能验证训练管线局限也很明显机位固定、光照场景单一、类别分布不均衡。比如YawDD里打哈欠样本集中在少数几个人的录制视频里直接用这个训练出来的模型换到真实车机上泛化能力撑不住。所以常见的做法是“公开数据集预训练 少量自采数据微调”。先用自己的摄像头在办公环境录几段模拟驾驶视频覆盖闭眼、打哈欠、低头几个典型状态标注几百帧做微调。自采数据的关键是传感器位置要和最终部署机位一致。摄像头装在后视镜和装在A柱上看到的眼部角度完全不同装错了位置采集再多数据也白费。3. 构建疲劳检测数据集标注决策、类别平衡与隐私边界3.1 类别体系设计闭眼、打哈欠之外还要不要细分数据集是工程落地的地基。类别设计上我建议一开始不要太细。闭眼、打哈欠、低头、正常驾驶四个类别就能覆盖大部分疲劳检测需求。左眼右眼分开标注是很多人容易掉的坑两边眼睑状态在大多数帧里是一致的分开反而增加标注成本还容易让模型学到“左右不对称才正常”的错误先验。半睁眼是个边界情况。疲劳早期的表现往往不是闭眼而是眼皮下垂盖住一半瞳孔。这种情况归到睁眼类还是闭眼类直接决定了PERCLOS判定是否提前报警。我的经验是早期数据量不够时把半睁眼归入闭眼类等闭眼样本足够多了再单独拆一个“半睁眼”类出来。否则模型会对“瞳孔可见”这个特征过度敏感把半睁眼划进正常类。嘴部状态同理。“打哈欠”定义成“嘴巴张开且能看到咽部或舌腭”才标注打哈欠的预备动作轻微张口不标。类别定义越明确后期返工越少。3.2 从视频帧提取与清洗抽帧、清晰度过滤与无效帧剔除疲劳检测的数据大多来自视频而不是图片所以第一步是抽帧。视频相邻帧高度相似训练集不需要每帧都保留固定间隔抽帧能大幅降低标注压力。抽完帧之后运动模糊和过暗过曝帧必须剔除。这类帧连人眼都分不清眼睑状态标进去只会给模型送噪声。下面这个脚本做两件事按固定间隔抽帧并用拉普拉斯方差判断清晰度过滤掉模糊和过暗的帧。import cv2 import numpy as np from pathlib import Path video_path Path(driver_video.mp4) out_dir Path(frames) out_dir.mkdir(exist_okTrue) cap cv2.VideoCapture(str(video_path)) interval 5 # 每 5 帧取 1 帧避免相邻帧重复 min_focus 80.0 # 低于该阈值的帧视为模糊帧丢弃 frame_idx 0 saved_idx 0 while True: ret, frame cap.read() if not ret: break if frame_idx % interval ! 0: frame_idx 1 continue gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) focus_score cv2.Laplacian(gray, cv2.CV_64F).var() if focus_score min_focus: frame_idx 1 continue if np.mean(gray) 40 or np.mean(gray) 215: frame_idx 1 continue out_path str(out_dir / fframe_{saved_idx:06d}.jpg) cv2.imwrite(out_path, frame) saved_idx 1 frame_idx 1 cap.release() print(fsaved {saved_idx} frames)这段脚本里interval和min_focus是决定数据集质量的两个关键参数。interval太小相似帧太多不仅增加标注量还会让模型对特定角度过拟合interval太大眨眼这类短时动作可能整段被跳过。按25帧的视频来算5帧取1帧意味着每秒钟保留5帧闭眼动作通常持续15帧以上不会丢。min_focus的阈值需要根据摄像头分辨率调整720P下80左右是经验值分辨率越高阈值可以适当上调黑暗场景下平均灰度检查先于清晰度检查执行能省掉大量纯黑帧的拉普拉斯计算。3.3 数据增强与类别平衡墨镜、口罩、夜间红外场景的兜底手段YOLOv8自带的增强已经很强mosaic、mixup、HSV扰动默认开启。但疲劳检测场景需要额外叠加两类增强遮挡模拟和亮度突变模拟。驾驶员画面里方向盘、雨刮器会周期性挡脸用随机矩形遮挡模拟这些情况模型就不会因为局部特征消失就彻底丢失目标。隧道出入口是另一个典型场景画面亮度在几百毫秒内跳变增强时对图像做大幅度的曝光压暗或提亮能显著提高模型在真实路测中的鲁棒性。类别平衡问题在疲劳数据集里几乎必然存在。正常驾驶帧随手就能录几千张打哈欠样本可能只有一两百张。常见做法是先统计各类别数量挑出最少类别数量的上限其余类别随机过采样到同一量级。不要用重复帧直接复制那只会让模型记住特定画面更好的方式是复制后做HSV扰动和轻微几何变换。比较隐蔽的问题是标注一致性。多人协作标注时不同标注员对“半睁眼”“打哈欠”的理解可能会存在偏差一个疲劳状态可能出现多种标注结果这会造成训练数据内部打架。项目开始前先让所有标注员标同一批10张图算一下一致率低于80%就说明类别定义还需要再明确。3.4 隐私与伦理边界疲劳检测数据不能随意采集和发布说到这里必须停下来强调一点驾驶员疲劳检测的数据集涉及人脸生物特征不是自己随便录几段就能对外发布的。真实驾驶场景里驾驶员有合理的安全预期采集前必须告知用途、征得书面同意并且保证数据不会被挪作他用。训练和测试阶段尽量使用脱敏数据人脸区域做了模糊再入模型或者干脆用合成数据验证管线。公开发布数据集时尤其要确认其中没有可追溯到具体个人的身份信息这是整个项目能顺利推进的前提条件。4. 用YOLOv8训练自己的数据集yaml配置、命令参数与训练结果验证4.1 环境准备与数据集目录结构训练之前先把目录结构准备好。Ultralytics YOLO的标签格式是txt文本每行“class x_center y_center width height”坐标是归一化到0到1之间的相对值。LabelImg或X-AnyLabeling标注完直接导出会得到这个格式不需要自己手工转换。dataset/ ├── images/ │ ├── train/ # 全部训练图像 │ └── val/ # 验证图像 ├── labels/ │ ├── train/ # 与图像同名的 txt 标签 │ └── val/ └── data.yaml环境依赖不多PyTorch加上ultralytics这一个包就够了。版本上我用的是ultralytics 8.x系列YOLOv8n的权重文件可以指定网络自动下载。数据集目录和代码目录分开存训练脚本在项目根目录数据集放在独立磁盘路径下便于后面增量更新数据时不影响代码版本管理。4.2 数据yaml与训练命令参数详解data.yaml是整个训练流程的核心配置指定类别名称和训练、验证数据路径。在标注好数据集之后这是第一步要改的文件。path: /data/driver_fatigue # 数据集根目录 train: images/train val: images/val names: 0: closed_eye 1: yawn 2: looking_down 3: normal这里类别索引从0开始必须和标注txt里的class id严格对应ng标签一旦错位训练过程不会报错但模型输出的类别语义会全部错乱——这是最容易出现且最难排查的训练问题。训练命令本身不复杂但几个参数直接决定模型质量yolo detect train \ data/data/driver_fatigue/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ lr00.001 \ optimizerSGD参数的选择有讲究。imgsz训练分辨率决定了模型对眼部小目标的敏感度640可以在速度和精度之间取得平衡如果闭眼漏检严重再试960。epochs设100配合patience15的意思是连续15个epoch验证集指标没有提升就早停训练集过拟合严重时不会白等。优化器上我偏好SGD而不是默认的AdamSGD在小数据集上收敛更稳不易震荡到退化的局部最优点。batch size是根据显存来的12GB显存跑YOLOv8n可以放到32显存不够就降到8梯度累积的补偿效果一般不如直接缩小模型。训练集和验证集的划分要在训练前一次性做完不要用ultralytics自动划分。疲劳数据按视频文件而非帧来划分同一个视频的帧不能同时出现在训练集和验证集中否则模型记住了驾驶员的面孔验证指标虚高但真实路测毫无用处。4.3 训练结果验证混淆矩阵、PR曲线与类别错报分析训练结束后先看跑完的命令输出预测结果和指标曲线。第一个要看的是混淆矩阵重点观察closed_eye被误判成normal的比例。疲劳检测的告警逻辑是宁可多报不可漏报因此这个比例应控制在极低水平。如果误报高说明闭眼和正常睁眼在视觉特征上没有拉开距离要么回到第3章检查半睁眼的标注归属要么在数据增强里加大闭眼正样本的扰动强度。第二个指标是每个类别的平均精度。打哈欠类别的AP通常是最低的因为打哈欠动作的持续时间短、嘴部形态多样性大。AP低于70%不是致命问题后端有PERCLOS时序统计兜底偶尔漏检几帧不会触发告警但closed_eye的AP低于90%就要警惕说明模型对闭眼的判别力不够。每轮epoch结束后生成的results.csv保存了所有训练指标可以用pandas直接读取绘图也可以只看ultralytics输出的可视化和验证曲线。如果验证损失在训练后期不降反升而训练损失还在下降那就是过拟合了减小epochs、加大增强强度或补充数据都是可行的方向。5. 疲劳检测项目常见的翻车现场框不准、半睁眼误判、夜间失效的排查5.1 半睁眼被识别成正常睁眼困倦初期就失守现象模型对完全闭合的眼睛判断很准但驾驶员眼皮耷拉、瞳孔只露出一半时被模型归类为normalPERCLOS始终不报警等到真正闭眼时往往已经快睡着了。原因标注阶段把半睁眼全归到了normal类。模型学到的是“能看到瞳孔就是睁眼”半睁眼这种中间态在特征空间里和正常睁眼距离更近自然被分到一起。解决回数据集把半睁眼帧单独挑出来先全部改成closed_eye训练一版看效果数据量够的话直接增加一个drowsy_eye类。后处理侧同步把判定阈值往敏感方向调比如闭眼帧占比8秒内超过40%就触发告警早期预警比精确判断更重要。5.2 夜间红外画面精度骤降模型被可见光样本“驯化”了现象白天场景闭眼检测准确率很高切换到红外夜视摄像头后mAP掉了二十个百分点闭眼框乱飘。原因数据集全是RGB可见光图像夜间红外摄像头输出的是单通道灰度图且红外光源会造成瞳孔区域过曝、眼睑纹理丢失。模型练了一身“看颜色辨状态”的本事到了红外画面上完全使不出来。解决在训练增强里强制加入灰度化分支以一定概率把图像转成单通道再训练有条件的团队直接采集红外场景数据做微调。这条没有捷径数据源的光谱特性和部署传感器不一致精度损失是必然的。5.3 连续误报导致告警疲劳单帧判断的时序抖动现象车辆经过颠簸路段时模型在1秒内闭眼、睁眼来回跳变告警频繁触发然后又立刻消失最后司机直接把告警功能关了。原因模型输出的是单帧检测结果没有做时序平滑。行车颠簸导致面部抖动连续几帧里闭眼特征时有时无单帧误检被直接当成了真实状态。解决后端加滑动窗口连续5帧中至少4帧判定闭眼才算一次闭环事件告警后锁定状态至少3秒不重复触发。这个逻辑写在第6章属于纯工程手段但往往是整个项目中最值钱的部分。5.4 佩戴墨镜和口罩后漏检率飙升现象白天逆光下驾驶员戴墨镜眼部区域直接检测不到冬季戴口罩打哈欠类别全部失效。原因训练集里没有遮挡样本。墨镜把眼部特征整体遮盖口罩把嘴部特征遮盖模型找不到判据就直接输出低置信度框被过滤掉。解决数据增强里加随机黑色矩形遮住眼部或嘴部区域模拟墨镜和口罩场景。但坦率说重度遮挡下视觉方案确实有天花板实际工程中我会同时接入方向盘转角传感器——视觉失效时转向行为特征还能撑住告警逻辑这是多传感器互补的现实选择。5.5 换了个机位性能骤降摄像头安装位置的“视角陷阱”现象数据集是在仪表盘位置采集的部署时摄像头装在后视镜侧面结果闭眼检测的漏检率明显上升。原因机位变化改变了眼部的几何形状和遮挡关系。仪表盘位置能正面看到眼睑开合而A柱机位只能看到侧面眼睑轮廓特征完全不同。解决采集阶段就用最终部署的机位录制数据如果机位还没定至少准备两个角度的数据混合训练。摄像头安装高度、俯仰角度、焦距一旦确定就写进部署文档固定下来别指望部署现场再自由发挥。视觉模型对机位变化十分敏感这是疲劳检测项目里最容易被低估的变数。6. 落地优化用PERCLOS做状态判决与TensorRT量化部署6.1 从单帧检测到疲劳判定滑动窗口后处理模型输出的是每一帧的检测结果但驾驶员的疲劳状态是连续过程单帧判断不具备参考意义。我一般会写一个滑动窗口统计器维护最近N帧的闭眼标记序列计算窗口中闭眼帧的占比超过阈值才触发一次闭眼事件。这个后处理逻辑虽然简单却是整个系统是否“可信”的关键所在。from collections import deque window deque(maxlen30) # 30帧窗口约1秒 30fps closed_threshold 0.5 # 窗口内闭眼帧占比超过50%触发闭眼 event_count 0 def on_frame(detections): global event_count # detections: list of (class_id, confidence) eye_closed any(cls 0 for cls, conf in detections if conf 0.5) window.append(1 if eye_closed else 0) if len(window) window.maxlen and sum(window) / window.maxlen closed_threshold: event_count 1 window.clear() # 清空窗口避免同一次闭眼重复触发 return True return Falsewindow长度和closed_threshold是整套判定逻辑的核心参数。窗口设1秒比较合适帧率波动时统计仍稳定closed_threshold设0.5意味着闭眼持续超过0.5秒才上报眨眼约0.1到0.2秒和视觉抖动不会触发告警。确认收到告警后会清空窗口避免同一次闭眼事件重复触发导致司机被频繁提示。要想做PERCLOS指标可以把window替换成固定时间段的帧序列计算闭眼帧占比后按时间维度输出统计量。6.2 模型压缩TensorRT量化与设备部署边缘设备上跑PyTorch模型效率不理想通常做法是先导出ONNX再转TensorRT。命令也不复杂yolo export model/data/driver_fatigue/best.pt formatonnx imgsz640 trtexec --onnxbest.onnx --saveEnginebest.engine --fp16导出时imgsz要和训练时一致否则输入尺寸变化会导致精度下降FP16量化对YOLOv8n的影响通常在1到2个百分点闭眼检测这类语义相对清晰的类别一般感知不到差异。真机部署时先跑一遍离线视频流验证帧率和CPU占用再用硬编码的输入尺寸接摄像头流避免运行时动态shape带来的不稳定。我第一次把小模型直接丢到老设备上闭眼检测在全分辨率下只有8帧被迫把输入分辨率降到416才跑起来之后的教训就是部署前先量算力量化后再谈精度顺序不能反过来。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询