恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLO驾驶员疲劳检测数据集构建与训练避坑指南
首页
资讯中心
/
YOLO驾驶员疲劳检测数据集构建与训练避坑指南
YOLO驾驶员疲劳检测数据集构建与训练避坑指南
发布时间:2026/10/13 23:22:35
简介这份资源是面向计算机视觉学习者与智能驾驶方向研究者的YOLO驾驶员疲劳检测数据集可用于训练和验证闭眼、打哈欠等疲劳行为的实时识别模型适合具备一定目标检测基础、希望落地驾驶安全项目的读者。压缩包共8744个文件约256.88MB包含2915个jpg图像、2915个txt标注和2914个xml标注图像为驾驶员面部及行为样本txt提供简洁的边界框坐标与类别信息xml则保留更完整的结构化标注两种格式可灵活适配不同训练框架。目前已有2931人学习下载。数据集覆盖多种疲劳程度与行为表现读者可据此完成从数据解析、模型训练到疲劳预警验证的完整流程并借助YOLO单阶段检测的高效特性体会多目标并行识别在闭眼与打哈欠同步检测中的实用价值为智能交通与驾驶辅助系统研究提供扎实的数据支撑。1. 驾驶员疲劳检测为什么总在真实车舱里翻车聊到 yolo算法驾驶员疲劳检测数据集很多刚接触这个方向的朋友第一反应是去搜一个现成的压缩包解压、训练、看 mAP 涨到 0.9 就以为大功告成。但真正在车舱里跑过一轮的人都知道实验室里漂亮的指标和实车表现之间隔着一道鸿沟。白天正脸、光照均匀、眼睛睁得大大的样本模型当然学得又快又好可一旦到了夜间红外补光、驾驶员戴墨镜、侧脸低头、打哈欠到眼睛只剩一条缝检测框就开始飘闭眼和睁眼的分类置信度几乎贴在一起这就是典型的“数据集没覆盖到的长尾把模型带沟里了”。这个标题背后其实包含三件事一是用 YOLO 系列做目标检测框出人脸、眼睛、嘴巴这些关键区域二是靠这些区域的形态判断疲劳状态比如闭眼时长、打哈欠频率、点头幅度三是整套流程高度依赖数据集的质量和标注口径。它适合两类人想快速搭一个疲劳检测 Demo 的算法工程师以及需要给座舱监控产品做预研的技术负责人。下面我按自己踩过的路子把数据集怎么选、怎么标、怎么训、怎么验讲清楚能直接抄作业的地方我都给到命令和参数。2. 数据集怎么选从标注口径到采集场景的取舍2.1 先想清楚检测目标再决定标注什么很多人一上来就问“有没有现成的疲劳数据集”但更该先问的是你要检测的到底是人脸框、眼睛状态还是直接端到端出疲劳等级这三种目标对应的标注方式完全不同。常见做法是分两级第一级用 YOLO 检测人脸和眼睛、嘴巴区域第二级再用一个轻量分类头或者规则判断疲劳。这样做的好处是标注成本可控因为人脸和眼睛的框比“疲劳/不疲劳”这种主观标签稳定得多。我一般会把标注类别定为四类face、eye_open、eye_closed、mouth_open。注意这里没有单独标“打哈欠”因为打哈欠本质是嘴巴张开持续一段时间用 mouth_open 的时序统计就能推出来单独标一类反而增加标注歧义。标注格式用 YOLO 的 txt每行class_id x_center y_center width height坐标全部归一化到 0 到 1。# 目录结构建议images 和 labels 一一对应 dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml这个结构是 YOLO 训练的标准输入data.yaml里写清楚 train、val 路径和类别名。逻辑上就是把图像和标注文件按同名对应训练脚本会自动去 labels 目录找同名 txt。参数上唯一要注意的是路径别用中文和空格否则某些版本的 dataloader 会静默读不到文件这个坑我后面还会提。2.2 采集场景的覆盖度比数量更重要一个两万张但全是白天正脸的数据集效果往往不如八千张但覆盖了夜间、侧脸、戴眼镜、戴口罩、不同肤色的数据集。疲劳检测的难点从来不是“人脸在哪”而是“极端状态下眼睛和嘴巴还认不认得出”。我一般会按下面几个维度去凑数据维度建议覆盖说明光照白天自然光、夜间红外、隧道逆光红外下瞳孔反光会干扰眼睛状态姿态正脸、左右侧脸、低头、抬头侧脸时单眼可能被遮挡遮挡无、普通眼镜、墨镜、口罩墨镜是闭眼检测的噩梦状态睁眼、闭眼、半闭、打哈欠半闭眼最难标容易和闭眼混如果自有数据不够公开数据集可以拿来预训练但要注意它们的标注口径和你自己的不一定一致。常见做法是先用公开数据训一个基础模型再用自有数据做微调微调时把学习率调小比如从 0.01 降到 0.001避免把预训练学到的通用特征冲掉。2.3 标注质量的自检脚本标注这事人工难免手抖框偏了、类别标反了、漏标了训练时全变成噪声。我习惯在训练前跑一个自检脚本把明显有问题的样本挑出来。import os from pathlib import Path from PIL import Image def check_labels(img_dir, lbl_dir, class_num4): issues [] for lbl_path in Path(lbl_dir).glob(*.txt): img_path Path(img_dir) / (lbl_path.stem .jpg) if not img_path.exists(): issues.append(f缺图: {lbl_path.name}) continue w, h Image.open(img_path).size with open(lbl_path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: issues.append(f{lbl_path.name} 第{i}行字段数不对) continue cid, x, y, bw, bh map(float, parts) if cid class_num: issues.append(f{lbl_path.name} 类别越界: {cid}) # 归一化坐标必须在 0~1且框不能超出图像 if not (0 x 1 and 0 y 1 and 0 bw 1 and 0 bh 1): issues.append(f{lbl_path.name} 第{i}行坐标异常) return issues if __name__ __main__: for msg in check_labels(dataset/images/train, dataset/labels/train): print(msg)这段脚本做三件事检查图片和标注是否一一对应、检查每行字段数和类别 id 是否合法、检查归一化坐标是否越界。参数class_num按你实际类别数改。跑完如果输出一堆“缺图”大概率是图片扩展名不统一比如标注对应的是 png 但实际存的是 jpg这种问题在训练时表现为 loss 不降排查起来很费时间提前查掉最省事。3. 用 YOLO 训练疲劳检测模型配置、命令与调参3.1 环境准备和最小可跑命令训练环境我一般用 Python 3.9 以上加 PyTorchYOLO 选 v8 或 v11 都行接口基本一致。装依赖就一条命令别去折腾各种魔改版本官方版最稳。pip install ultralytics装完之后先别急着训用官方预训练权重跑一张图确认环境没问题yolo predict modelyolov8n.pt sourcedataset/images/val/0001.jpg能正常输出检测框和置信度说明环境通了。这一步看着多余但能帮你排除掉 CUDA 版本不匹配、显存不够这些环境问题比训到一半报错强。3.2 data.yaml 和训练参数怎么设data.yaml是训练的入口配置写错一个字段训练直接起不来。path: /home/user/dataset train: images/train val: images/val nc: 4 names: [face, eye_open, eye_closed, mouth_open]path是数据集根目录train和val是相对路径。nc是类别数必须和 names 长度一致否则训练时类别索引会错位。names 的顺序要和标注里的 class_id 严格对应标 0 的必须是 face不能乱。训练命令我一般这么写yolo detect train \ modelyolov8s.pt \ datadataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0逐个说参数model选 s 而不是 n是因为疲劳检测里眼睛区域很小n 的感受野和特征表达能力偏弱s 在速度和精度之间更平衡imgsz640是常规选择如果眼睛区域在图中占比很小可以提到 960但显存和速度要权衡batch16按显存调显存不够就降到 8 并开ampTrue混合精度lr0初始学习率 0.01 是 YOLO 的常用值微调预训练模型时降到 0.001patience20表示 20 轮指标不涨就早停省时间。3.3 小目标眼睛检测的针对性调参眼睛在整张图里往往只占几十个像素属于典型小目标。默认锚框和损失权重对小目标不友好我一般做两处调整。一是把输入尺寸提上去640 提到 960 或 1280让眼睛有更多像素二是调损失里分类和定位的权重YOLOv8 默认box7.5, cls0.5小目标可以适当提高 box 权重到 8.5 左右让模型更关注框的精度。yolo detect train \ modelyolov8s.pt \ datadataset/data.yaml \ epochs120 \ imgsz960 \ batch8 \ box8.5 \ cls0.6 \ mosaic0.8 \ close_mosaic15mosaic0.8是马赛克增强概率能提升小目标泛化但开太满会让小目标在拼接图里更小所以配合close_mosaic15在最后 15 轮关掉增强让模型在真实分布上收敛。这套组合我在几个座舱项目里试过眼睛类别的召回能比默认配置高几个点。3.4 训练过程看什么指标训练日志里重点盯三个metrics/mAP50、metrics/mAP50-95和每个类别的precision/recall。疲劳检测里 eye_closed 的召回比精度更重要因为漏检一个闭眼可能意味着错过一次疲劳告警而误检一个闭眼只是多一次提醒。如果发现 eye_closed 召回偏低先别急着加数据去看混淆矩阵大概率是半闭眼被标成了 eye_open这种标注边界问题靠调参解决不了。# 训练完在 val 集上评估输出混淆矩阵和 PR 曲线 yolo detect val modelruns/detect/train/weights/best.pt datadataset/data.yaml评估结果会存到 runs 目录下混淆矩阵能直观看到哪两类在互相混。我见过最多的情况就是 eye_open 和 eye_closed 混根因往往是标注时对“半闭”的定义不统一这时候要回去统一标注规范而不是继续训。4. 避坑与排查那些让模型白训的细节4.1 现象loss 一直不降mAP 卡在 0.1 以下原因通常是标注路径或格式有问题。YOLO 读标注是静默失败的找不到 txt 就当负样本处理模型自然学不到东西。解决方法是先跑 2.3 节的自检脚本再确认data.yaml里的路径是绝对路径还是相对路径、相对谁。我一般直接写绝对路径省得猜。4.2 现象训练集指标很高验证集一塌糊涂这是过拟合但疲劳检测里更常见的原因是训练集和验证集场景分布不一致。比如训练集全是白天验证集混了夜间模型没见过红外图像自然崩。解决办法是按场景分层划分数据集保证每个场景在 train 和 val 里都有。划分脚本可以按文件名前缀分组别用随机划分。4.3 现象眼睛框频繁抖动同一帧忽大忽小原因可能是标注框松紧不一致有人框到眼角有人框到眉毛。这种噪声会让回归损失震荡。解决方法是定一个标注规范比如眼睛框统一包含上下眼睑、不含眉毛然后抽查一批标注做一致性校验。另一个可能是 NMS 阈值太低导致相邻框被误抑制可以适当调高iou阈值到 0.6 试试。4.4 现象夜间红外图像上闭眼全检成睁眼红外图像里瞳孔反光和眼睑对比度低闭眼时上下眼睑贴合纹理信息很少。这不是调参能根治的得从数据和模型两头下手。数据上补充红外闭眼样本模型上可以加一个红外增强训练时随机把图像转灰度并加噪声让模型对低对比度更鲁棒。# 训练时自定义增强随机灰度化模拟红外 import random import cv2 import numpy as np def ir_augment(img): if random.random() 0.3: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR) noise np.random.normal(0, 8, img.shape).astype(np.uint8) img cv2.add(img, noise) return img这段增强逻辑是在 dataloader 里插入的概率 0.3 表示三成样本会被灰度化并加高斯噪声。参数8是噪声标准差太大图像会糊太小起不到作用按实际红外图像质量调。4.5 现象模型在视频上单帧还行连续帧状态跳变疲劳判断本质是时序问题单帧检测只给了瞬时状态。如果直接拿单帧结果做告警会出现睁眼闭眼反复横跳。解决办法是加一个滑动窗口做时序平滑比如统计最近 30 帧里 eye_closed 的占比超过阈值才判定疲劳。这部分逻辑放在后处理不要塞进检测模型里。5. 从检测到疲劳判定时序逻辑与验证方法检测模型输出的是每帧的框和类别真正要回答“驾驶员是否疲劳”还得靠时序统计。我一般用一个固定长度的环形缓冲区存最近 N 帧的眼睛状态然后算两个指标闭眼持续帧数和打哈欠频率。闭眼持续超过 1.5 秒按 30fps 算约 45 帧触发一级告警打哈欠在最近 60 秒内超过 3 次触发二级告警。阈值不是拍脑袋定的要拿真实驾驶视频去标定不同帧率、不同摄像头安装角度都会影响。from collections import deque class FatigueJudge: def __init__(self, fps30, eye_close_sec1.5, yawn_window60, yawn_thresh3): self.fps fps self.eye_close_frames int(eye_close_sec * fps) self.buf deque(maxlenint(yawn_window * fps)) self.yawn_thresh yawn_thresh def update(self, eye_closed, mouth_open): self.buf.append((eye_closed, mouth_open)) # 连续闭眼帧数 close_run 0 for ec, _ in reversed(self.buf): if ec: close_run 1 else: break # 打哈欠次数嘴巴张开持续超过 0.5 秒算一次 yawn_count 0 run 0 for _, mo in self.buf: if mo: run 1 else: if run self.fps * 0.5: yawn_count 1 run 0 level 0 if close_run self.eye_close_frames: level 1 if yawn_count self.yawn_thresh: level max(level, 2) return level这个类的逻辑是update每帧调用一次传入当前帧的眼睛闭合和嘴巴张开状态close_run统计从当前帧往前连续闭眼的帧数超过阈值判一级打哈欠按嘴巴张开持续超过 0.5 秒计一次窗口内次数超阈值判二级。参数eye_close_sec和yawn_thresh要按实际场景标定别照搬。验证方法上别只看 mAP。我习惯做两件事一是拿一段带标注的连续视频跑完整流程后统计告警的准确率和漏报率二是做消融把时序平滑去掉看告警抖动有多严重这样能直观看出后处理的价值。如果条件允许找几个不同光照、不同驾驶员的视频片段做交叉验证比在单一验证集上刷分有意义得多。最后说个我自己的习惯每次训完模型我都会把验证集里所有误检和漏检的样本单独存一个文件夹过一遍看有没有共性。十次里有八次能发现是某类场景数据缺失而不是模型本身不行。数据集这个事永远是疲劳检测里最值得花时间的地方模型结构反而是次要的。希望帮到你。本文还有配套的精品资源点击获取