疲劳检测与微睡预测:从PERCLOS到CNN-LSTM时序建模

引言:疲劳是隐形的杀手

统计数据

  • 疲劳驾驶占交通事故的20-30%
  • 微睡(Microsleep)持续1-4秒,足以导致致命事故
  • Euro NCAP 2026要求实时疲劳检测并在2秒内报警

挑战

  • 疲劳是渐进过程,难以早期识别
  • 个体差异大(有的人疲劳时眨眼频繁,有的人反而减少)
  • 环境干扰(夜间、隧道、逆光)

一、PERCLOS:疲劳检测的黄金标准

1.1 PERCLOS定义

PERCLOS = Percentage of Eyelid Closure

1
PERCLOS = (眼睑闭合时间 / 总时间) × 100%

阈值

PERCLOS值 状态 说明
<15% 清醒 正常驾驶
15-25% 轻度疲劳 需要警惕
25-40% 中度疲劳 需要休息
>40% 重度疲劳 立即报警

1.2 计算方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import numpy as np
import cv2

class PERCLOSCalculator:
def __init__(self, window_size=60, threshold=0.7):
"""
window_size: 时间窗口(帧数)
threshold: 眼睑闭合阈值(Eye Aspect Ratio < threshold 视为闭合)
"""
self.window_size = window_size
self.threshold = threshold
self.history = []

def update(self, eye_aspect_ratio):
"""
更新PERCLOS计算
"""
# 判断眼睑是否闭合
is_closed = eye_aspect_ratio < self.threshold
self.history.append(is_closed)

# 保持窗口大小
if len(self.history) > self.window_size:
self.history.pop(0)

# 计算PERCLOS
if len(self.history) == self.window_size:
perclos = sum(self.history) / self.window_size * 100
else:
perclos = 0

return perclos

def get_fatigue_level(self, perclos):
"""
根据PERCLOS判断疲劳等级
"""
if perclos < 15:
return "alert", 0
elif perclos < 25:
return "mild", 1
elif perclos < 40:
return "moderate", 2
else:
return "severe", 3

# 使用示例
calculator = PERCLOSCalculator(window_size=60, threshold=0.25)

for frame in video_stream:
# 检测眼部
ear = detect_eye_aspect_ratio(frame)

# 计算PERCLOS
perclos = calculator.update(ear)

# 判断疲劳等级
level, severity = calculator.get_fatigue_level(perclos)

if severity >= 2:
alert_driver("疲劳驾驶警告!")

1.3 PERCLOS的局限性

局限 原因 解决方案
个体差异 每个人眼睛大小不同 个性化校准
光照敏感 阴影影响检测 IR照明
眨眼干扰 正常眨眼被误判 过滤短眨眼(<100ms)
单一指标 无法全面评估 多指标融合

二、微睡预测

2.1 微睡特征

定义:持续1-4秒的无意识眼睑闭合

特征模式

特征 正常眨眼 微睡
时长 100-400ms 1000-4000ms
频率 15-20次/分 不规律
可逆性 自主控制 无意识
伴随症状 头部下垂、身体倾斜

2.2 微睡检测算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class MicrosleepDetector:
def __init__(self, min_duration=1.0, max_duration=4.0):
self.min_duration = min_duration
self.max_duration = max_duration
self.closure_start = None

def detect(self, is_eye_closed, timestamp):
"""
检测微睡事件
"""
if is_eye_closed:
if self.closure_start is None:
# 眼睑开始闭合
self.closure_start = timestamp

else:
if self.closure_start is not None:
# 眼睑张开,计算闭合时长
closure_duration = timestamp - self.closure_start

# 判断是否为微睡
if self.min_duration <= closure_duration <= self.max_duration:
microsleep_event = {
'start': self.closure_start,
'end': timestamp,
'duration': closure_duration
}

# 重置
self.closure_start = None

return microsleep_event

# 重置
self.closure_start = None

return None

2.3 微睡预测模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import torch
import torch.nn as nn

class MicrosleepPredictor(nn.Module):
"""
预测未来5秒内的微睡概率
"""
def __init__(self, input_dim=10, hidden_dim=64, num_layers=2):
super().__init__()

# 特征编码器
self.feature_encoder = nn.Sequential(
nn.Linear(input_dim, 32),
nn.ReLU(),
nn.Dropout(0.3)
)

# LSTM时序建模
self.lstm = nn.LSTM(
input_size=32,
hidden_size=hidden_dim,
num_layers=num_layers,
batch_first=True,
dropout=0.3
)

# 预测头
self.predictor = nn.Sequential(
nn.Linear(hidden_dim, 32),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(32, 1),
nn.Sigmoid()
)

def forward(self, x):
"""
x: [B, T, D] 时序特征
返回: [B, 1] 微睡概率
"""
# 特征编码
h = self.feature_encoder(x) # [B, T, 32]

# LSTM
lstm_out, _ = self.lstm(h) # [B, T, 64]

# 取最后时刻的输出
last_hidden = lstm_out[:, -1, :] # [B, 64]

# 预测
prob = self.predictor(last_hidden) # [B, 1]

return prob

# 使用示例
model = MicrosleepPredictor(input_dim=10) # 10个特征

# 输入特征:[EAR, PERCLOS, 眨眼频率, 头部位姿, ...]
features = extract_features(video_stream) # [B, 60, 10]
prob = model(features)

if prob > 0.7:
print("⚠️ 微睡风险高!建议休息")

三、CNN-LSTM融合架构

3.1 架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输入视频帧序列

┌─────────────────────────────────┐
│ CNN特征提取(MobileNet v2) │
- 每帧提取特征 │
- 输出: [B, T, 1280] │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ LSTM时序建模 │
- 捕捉时序依赖 │
- 输出: [B, T, 256] │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│ 疲劳分类头 │
- Alert / Mild / Moderate / Severe│
└─────────────────────────────────┘

3.2 完整实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import torchvision.models as models

class CNNLSTM_FatigueDetector(nn.Module):
def __init__(self, num_classes=4):
super().__init__()

# CNN骨干(预训练MobileNet v2)
mobilenet = models.mobilenet_v2(pretrained=True)
self.cnn = nn.Sequential(*list(mobilenet.features.children()))

# 特征降维
self.fc1 = nn.Linear(1280, 256)

# LSTM
self.lstm = nn.LSTM(
input_size=256,
hidden_size=128,
num_layers=2,
batch_first=True,
dropout=0.3
)

# 分类头
self.classifier = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(64, num_classes)
)

def forward(self, x):
"""
x: [B, T, 3, H, W] 视频序列
"""
B, T, C, H, W = x.size()

# 1. CNN特征提取(逐帧)
x = x.view(B * T, C, H, W)
cnn_features = self.cnn(x) # [B*T, 1280, 1, 1]
cnn_features = cnn_features.view(B * T, -1) # [B*T, 1280]

# 2. 特征降维
cnn_features = self.fc1(cnn_features) # [B*T, 256]
cnn_features = cnn_features.view(B, T, -1) # [B, T, 256]

# 3. LSTM时序建模
lstm_out, _ = self.lstm(cnn_features) # [B, T, 128]

# 4. 取最后时刻
last_hidden = lstm_out[:, -1, :] # [B, 128]

# 5. 分类
logits = self.classifier(last_hidden) # [B, num_classes]

return logits

# 使用示例
model = CNNLSTM_FatigueDetector(num_classes=4)

# 输入:30帧视频序列
video_clip = load_video_clip() # [1, 30, 3, 224, 224]
logits = model(video_clip)

# 解码
fatigue_level = torch.argmax(logits, dim=1)
print(f"疲劳等级: {['清醒', '轻度', '中度', '重度'][fatigue_level]}")

3.3 性能对比

方法 准确率 推理时间 参数量
PERCLOS 75% 5ms 0
CNN单帧 82% 15ms 3.5M
CNN-LSTM 91% 25ms 5.2M
Transformer 90% 35ms 12M

四、Euro NCAP 2026疲劳检测要求

4.1 测试场景

场景 要求
长时间驾驶 2小时以上,检测疲劳累积
夜间驾驶 低光照环境下的检测能力
单调路况 高速公路,容易疲劳
城市拥堵 频繁启停,注意力下降

4.2 性能指标

指标 要求
检测延迟 ≤2秒
真阳性率 >90%
误报率 <5%
唤醒延迟 ≤1秒(微睡后)

4.3 报警机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class FatigueWarningSystem:
def __init__(self):
self.perclos_calculator = PERCLOSCalculator()
self.microsleep_detector = MicrosleepDetector()
self.cnn_lstm_model = CNNLSTM_FatigueDetector()

self.warning_count = 0
self.last_warning_time = 0

def monitor(self, frame, timestamp):
"""
实时监控
"""
# 1. PERCLOS计算
ear = detect_eye_aspect_ratio(frame)
perclos = self.perclos_calculator.update(ear)

# 2. 微睡检测
is_closed = ear < 0.25
microsleep = self.microsleep_detector.detect(is_closed, timestamp)

# 3. CNN-LSTM预测
fatigue_level = self.cnn_lstm_model(frame)

# 4. 综合判断
if microsleep:
self.trigger_warning("微睡检测!立即休息!", severity=3)

elif fatigue_level >= 2:
self.trigger_warning("疲劳驾驶警告!", severity=2)

elif perclos > 25:
self.trigger_warning("轻度疲劳,建议休息", severity=1)

def trigger_warning(self, message, severity):
"""
触发警告
"""
current_time = time.time()

# 避免频繁报警
if current_time - self.last_warning_time > 30:
print(f"[{severity}] {message}")
self.last_warning_time = current_time

# 集成实际报警接口
if severity == 3:
emergency_stop() # 紧急停车
elif severity == 2:
audio_alert(message) # 声音报警
else:
visual_alert(message) # 视觉提示

五、边缘优化

5.1 模型量化

1
2
3
4
5
6
7
8
9
10
11
12
import torch.quantization as quant

# 动态量化
model_quantized = quant.quantize_dynamic(
model,
{nn.LSTM, nn.Linear},
dtype=torch.qint8
)

# 性能对比
# FP32: 25ms, 5.2M params
# INT8: 15ms, 1.3M params

5.2 TensorRT优化

1
2
3
4
5
6
7
8
9
10
11
# 转换为ONNX
torch.onnx.export(
model,
torch.randn(1, 30, 3, 224, 224),
'fatigue_detector.onnx'
)

# TensorRT优化
# FP32: 25ms
# FP16: 15ms
# INT8: 10ms

六、总结

6.1 核心结论

技术点 关键发现
PERCLOS 简单有效,但需结合其他指标
微睡检测 闭合时长是关键特征
CNN-LSTM 时序建模提升准确率
边缘优化 INT8量化满足实时性

6.2 实施建议

  1. 短期:使用PERCLOS + 微睡检测
  2. 中期:引入CNN-LSTM融合架构
  3. 长期:开发个性化疲劳模型

参考文献

  1. Dinges, D. F., & Grace, R. “PERCLOS: A Valid Psychophysiological Measure of Alertness.” 1998.
  2. “Edge-Optimized Deep Learning for Early Driver Fatigue Detection.” 2025.
  3. Euro NCAP. “Driver Monitoring Test Protocol.” 2026.

本文是IMS疲劳检测系列文章之一


疲劳检测与微睡预测:从PERCLOS到CNN-LSTM时序建模
https://dapalm.com/2026/03/13/2026-03-13-疲劳检测与微睡预测-从PERCLOS到CNN-LSTM时序建模/
作者
Mars
发布于
2026年3月13日
许可协议