news 2026/10/1 13:40:47

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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_ok=True) 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 / f"frame_{saved_idx:06d}.jpg") cv2.imwrite(out_path, frame) saved_idx += 1 frame_idx += 1 cap.release() print(f"saved {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 \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=15 \ lr0=0.001 \ optimizer=SGD

参数的选择有讲究。imgsz训练分辨率决定了模型对眼部小目标的敏感度,640可以在速度和精度之间取得平衡,如果闭眼漏检严重再试960。epochs设100配合patience=15的意思是连续15个epoch验证集指标没有提升就早停,训练集过拟合严重时不会白等。优化器上我偏好SGD而不是默认的Adam,SGD在小数据集上收敛更稳,不易震荡到退化的局部最优点。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 半睁眼被识别成正常睁眼:困倦初期就失守

现象:模型对完全闭合的眼睛判断很准,但驾驶员眼皮耷拉、瞳孔只露出一半时被模型归类为normal,PERCLOS始终不报警,等到真正闭眼时往往已经快睡着了。

原因:标注阶段把半睁眼全归到了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(maxlen=30) # 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 False

window长度和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 format=onnx imgsz=640 trtexec --onnx=best.onnx --saveEngine=best.engine --fp16

导出时imgsz要和训练时一致,否则输入尺寸变化会导致精度下降;FP16量化对YOLOv8n的影响通常在1到2个百分点,闭眼检测这类语义相对清晰的类别一般感知不到差异。真机部署时先跑一遍离线视频流验证帧率和CPU占用,再用硬编码的输入尺寸接摄像头流,避免运行时动态shape带来的不稳定。我第一次把小模型直接丢到老设备上,闭眼检测在全分辨率下只有8帧,被迫把输入分辨率降到416才跑起来,之后的教训就是:部署前先量算力,量化后再谈精度,顺序不能反过来。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 13:40:41

一句话生成Node.js学习官网:Express+SQLite+ESM快速上线实战

1. 从一句话到上线&#xff1a;这个项目到底在做什么 先把话说透。这个项目的核心目标非常直接&#xff1a;用一句话描述需求&#xff0c;快速生成一个 Node.js 学习官网&#xff0c;并且立刻发布上线。听起来像是某种“魔法”&#xff0c;但拆开看&#xff0c;它其实是一条完整…

作者头像 李华
网站建设 2026/10/1 13:40:00

PanWatch HTTP 代理配置详解:海外数据源访问的三种配置方式

PanWatch HTTP 代理配置详解&#xff1a;海外数据源访问的三种配置方式 【免费下载链接】PanWatch PanWatch — AI stock monitoring for A-shares, HK & US markets, powered by TradingAgents. Portfolio insights, real-time alerts & automated reports.&#xff5…

作者头像 李华
网站建设 2026/10/1 13:39:48

Agent工程三层架构:Harness、Loop与Graph的生产实践指南

前一阵子帮团队重构了一个 Agent 项目&#xff0c;代码从最开始的两千行左右&#xff0c;膨胀到了差不多一万行。业务方倒是很高兴&#xff0c;因为多 Agent 协作、工具权限、失败重放这些需求都上了&#xff0c;但我自己清楚&#xff0c;真正让这个项目活下来的&#xff0c;不…

作者头像 李华
网站建设 2026/10/1 13:39:47

MADDPG多智能体博弈对抗实战:Python源码精读与训练调优

简介&#xff1a;面向计算机专业毕业设计、课程设计及强化学习入门者&#xff0c;这是一份基于MADDPG&#xff08;多智能体深度确定性策略梯度&#xff09;的博弈对抗算法Python完整项目。代码覆盖经验回放缓冲区、Actor-Critic网络、DDPG训练流程、环境交互与测试模块&#xf…

作者头像 李华
网站建设 2026/10/1 13:39:34

交叉编译报错 cannot find crt1.o 的原因与彻底解决方案

干过交叉编译的人&#xff0c;十有八九都撞见过这个报错&#xff1a;arm-linux-gnueabihf-gcc main.c -o main /usr/lib/gcc-cross/arm-linux-gnueabihf/9/../../../../arm-linux-gnueabihf/bin/ld: cannot find crt1.o: No such file or directory collect2: error: ld return…

作者头像 李华
网站建设 2026/10/1 13:38:41

2000张行人图像够用吗?YOLOv5小数据集落地临界点解析

简介&#xff1a;本资源是一份专为YOLOv5目标检测模型训练与评估打造的行人检测数据集&#xff0c;面向计算机视觉初学者、算法工程师及智能安防项目开发者&#xff0c;解决行人检测任务中高质量标注数据匮乏的问题。数据集包含2000张真实场景行人图像&#xff08;JPG格式&…

作者头像 李华