arXiv 2024 睡眠检测综述解读:PERCLOS技术演进与多场景应用全景

arXiv 2024 睡眠检测综述解读:PERCLOS技术演进与多场景应用全景

论文信息

项目 内容
标题 A Survey on Drowsiness Detection – Modern Applications and Methods
作者 arXiv 2024
年份 2024
链接 https://arxiv.org/html/2408.12990v1
会议/期刊 arXiv综述

核心创新

首个跨领域睡眠检测综述

  • 不局限于驾驶场景
  • 覆盖:职场、医疗、航空、铁路、公共交通
  • 系统梳理:生理信号(EEG/ECG)vs 视觉方法
  • PERCLOS仍是核心指标,但深度学习正在革新

1. 研究背景与动机

1.1 睡眠检测的重要性

应用场景 典型案例 事故后果
驾驶 疲劳驾驶 占20%死亡事故(NHTSA 2016)
铁路 高铁司机疲劳 2011温州动车事故(40死192伤)
航空 飞行员疲劳 多起航空事故
医疗 医护人员轮班 患者安全风险
工业 机械操作员疲劳 工伤事故
办公 长时间工作 生产力下降

1.2 传统方法的局限

方法类型 局限 适用场景
生理信号(EEG/ECG) 需电极接触,用户接受度低 医疗研究
行为观察 主观性强,需人工判断 实验室
PERCLOS单指标 晚期疲劳检测,预警不足 驾驶辅助
传统CNN 计算量大,实时性差 研究原型

1.3 综述核心贡献

贡献项 描述
跨领域视野 首个覆盖驾驶+铁路+航空+医疗+工业的综述
方法分类 生理信号 vs 视觉方法,清晰分类
挑战识别 实时检测、数据传输、系统偏见三大挑战
解决方案 合成数据、模型压缩、多模态融合

2. 方法分类体系

2.1 两大类别

graph TD
    A[睡眠检测方法] --> B[生理信号]
    A --> C[视觉方法]
    
    B --> D[EEG脑电]
    B --> E[ECG心电]
    
    D --> F[频带分析]
    D --> G[波形特征]
    
    E --> H[HRV心率变异性]
    E --> I[PRV脉率变异性]
    
    C --> J[静态单帧]
    C --> K[动态多帧]
    
    J --> L[面部特征]
    J --> M[眼睛特征]
    
    K --> N[PERCLOS序列]
    K --> O[头部运动序列]
    
    F --> P[应用:医疗/研究]
    H --> P
    L --> Q[应用:实时监测]
    N --> Q

2.2 生理信号方法

EEG(脑电图)

特征 检测原理 疲劳表现
Alpha波(8-12Hz) 清醒放松 疲劳时增加
Beta波(13-30Hz) 活跃思维 疲劳时减少
Theta波(4-7Hz) 困倦状态 疲劳时显著增加
Delta波(0.5-3Hz) 深度睡眠 极度疲劳出现

ECG(心电图)

特征 检测原理 疲劳表现
HRV(心率变异性) RR间期变化 疲劳时HRV下降
LF/HF比值 低频/高频功率比 疲劳时比值变化
QRS形态 心跳波形 疲劳时微小变化

2.3 视觉方法

PERCLOS详解

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""
PERCLOS(Percentage of Eye Closure)计算
睡眠检测核心指标

参考:arXiv 2024 Survey
Euro NCAP 2026 DSM Protocol

定义:眼睛≥80%闭合的时间百分比
窗口:通常60秒滑动窗口
阈值:PERCLOS ≥ 30% 触发疲劳警告
"""

import numpy as np
from typing import Tuple

def calculate_perclos(
eye_openness_sequence: np.ndarray,
threshold_ratio: float = 0.2,
window_seconds: int = 60,
fps: int = 30
) -> Tuple[np.ndarray, float]:
"""
计算PERCLOS

Args:
eye_openness_sequence: 眼睛开度序列 (0-1)
0 = 完全闭合
1 = 完全张开
threshold_ratio: 闭合阈值
开度 < threshold 视为闭合
默认0.2(即≥80%闭合)
window_seconds: 滑动窗口秒数
fps: 帧率

Returns:
perclos_series: PERCLOS时间序列
current_perclos: 当前PERCLOS值

Euro NCAP 2026判定:
- PERCLOS ≥ 30%: 二级疲劳警告
- PERCLOS ≥ 15%: 一级疲劳预警
- PERCLOS < 15%: 正常
"""
window_frames = window_seconds * fps
N = len(eye_openness_sequence)

perclos_series = []

for i in range(N - window_frames):
window = eye_openness_sequence[i:i+window_frames]

# 计算闭合帧数
closed_frames = np.sum(window < threshold_ratio)

# PERCLOS百分比
perclos = closed_frames / window_frames * 100
perclos_series.append(perclos)

current_perclos = perclos_series[-1] if perclos_series else 0

return np.array(perclos_series), current_perclos


def detect_microsleep(
eye_openness_sequence: np.ndarray,
min_closed_duration_ms: int = 500,
fps: int = 30
) -> Tuple[int, list]:
"""
检测微睡眠

Args:
eye_openness_sequence: 眼睛开度序列
min_closed_duration_ms: 微睡眠定义时长(>500ms)
fps: 帧率

Returns:
microsleep_count: 微睡眠次数
microsleep_intervals: 微睡眠时间段列表

微睡眠定义:
- 眼睛闭合时长 > 500ms(典型1.5-2秒)
- Euro NCAP 2026:闭眼≥1.5秒触发微睡眠警告
"""
threshold = 0.2 # ≥80%闭合
min_frames = int(min_closed_duration_ms / 1000 * fps)

microsleep_count = 0
microsleep_intervals = []

closed_start = None

for i, openness in enumerate(eye_openness_sequence):
if openness < threshold:
if closed_start is None:
closed_start = i
else:
if closed_start is not None:
closed_duration = i - closed_start

if closed_duration > min_frames:
microsleep_count += 1
start_time = closed_start / fps
end_time = i / fps
microsleep_intervals.append((start_time, end_time))

closed_start = None

return microsleep_count, microsleep_intervals


# 测试
if __name__ == "__main__":
np.random.seed(42)

# 模拟眼睛开度序列(疲劳驾驶)
# 正常:开度0.8-1.0
# 疲劳:开度下降,出现闭眼事件

N = 1800 # 60秒 @ 30fps

# 前30秒正常
normal = np.random.uniform(0.8, 1.0, 900)

# 后30秒疲劳(PERCLOS增加)
fatigue = np.random.uniform(0.1, 0.7, 900)

# 模拟微睡眠事件(闭眼1.5-2秒)
for i in range(3):
start = 1000 + i * 200
end = start + 45 + np.random.randint(0, 15) # 1.5-2秒
fatigue[start:end] = np.random.uniform(0.05, 0.15, end - start)

eye_sequence = np.concatenate([normal, fatigue])

# 计算PERCLOS
perclos_series, current = calculate_perclos(eye_sequence)

print(f"当前PERCLOS: {current:.1f}%")
print(f"PERCLOS峰值: {np.max(perclos_series):.1f}%")

# 检测微睡眠
count, intervals = detect_microsleep(eye_sequence)

print(f"\n微睡眠次数: {count}")
for i, (start, end) in enumerate(intervals):
print(f" 微睡眠{i+1}: {start:.1f}s - {end:.1f}s ({(end-start):.2f}s)")

其他视觉特征

特征 计算方法 疲劳表现
眨眼频率 单位时间眨眼次数 疲劳时频率变化(增加或减少)
眨眼时长 单次眨眼持续时间 疲劳时眨眼时长增加
头部姿态 Pitch/Roll/Yaw 疲劳时头部下垂
嘴部张开(打哈欠) MAR(Mouth Aspect Ratio) 疲劳时哈欠频率增加
面部表情 EmoNet情绪识别 疲劳时表情识别变化

3. 深度学习方法进展

3.1 CNN-LSTM模型

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
CNN-LSTM睡眠检测模型
参考:arXiv 2024 Survey
ResearchGate CNN-LSTM + PERCLOS + Yawning

架构:
1. CNN提取面部特征
2. LSTM建模时序依赖
3. 多特征融合(PERCLOS + 哈欠频率)
"""

import torch
import torch.nn as nn
import numpy as np

class CNNLSTM_Drowsiness(nn.Module):
"""
CNN-LSTM睡眠检测模型

结构:
- CNN特征提取:ResNet/MobileNet
- LSTM时序建模:捕捉眨眼/哈欠序列
- 多特征融合:PERCLOS + Yawning + Head Pose
"""

def __init__(
self,
cnn_feature_dim: int = 512,
lstm_hidden_dim: int = 256,
lstm_layers: int = 2,
additional_features: int = 10, # PERCLOS + Yawning + etc
num_classes: int = 3, # Normal / Drowsy / Sleep
dropout: float = 0.3
):
super().__init__()

# CNN特征提取(简化版)
self.cnn = nn.Sequential(
nn.Conv2d(3, 64, 7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(3, stride=2, padding=1),

nn.Conv2d(64, 128, 3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.MaxPool2d(2),

nn.Conv2d(128, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.MaxPool2d(2),

nn.Conv2d(256, cnn_feature_dim, 3, padding=1),
nn.BatchNorm2d(cnn_feature_dim),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1)
)

# LSTM时序建模
self.lstm = nn.LSTM(
input_size=cnn_feature_dim + additional_features,
hidden_size=lstm_hidden_dim,
num_layers=lstm_layers,
batch_first=True,
dropout=dropout,
bidirectional=True
)

# 分类头
self.classifier = nn.Sequential(
nn.Linear(lstm_hidden_dim * 2, lstm_hidden_dim),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(lstm_hidden_dim, num_classes)
)

def forward(
self,
frame_sequence: torch.Tensor,
additional_features: torch.Tensor
) -> torch.Tensor:
"""
前向传播

Args:
frame_sequence: 视频帧序列, shape=(B, T, C, H, W)
additional_features: PERCLOS/Yawning等特征, shape=(B, T, F)

Returns:
logits: 分类输出, shape=(B, num_classes)
"""
B, T, C, H, W = frame_sequence.shape

# CNN特征提取(逐帧)
cnn_features = []
for t in range(T):
frame = frame_sequence[:, t] # (B, C, H, W)
feat = self.cnn(frame) # (B, cnn_feature_dim, 1, 1)
feat = feat.view(B, -1) # (B, cnn_feature_dim)
cnn_features.append(feat)

cnn_features = torch.stack(cnn_features, dim=1) # (B, T, cnn_feature_dim)

# 特征融合
combined_features = torch.cat([
cnn_features, additional_features
], dim=-1) # (B, T, cnn_feature_dim + F)

# LSTM建模
lstm_out, (h_n, c_n) = self.lstm(combined_features)

# 取最后时刻的双向输出
final_hidden = lstm_out[:, -1, :] # (B, lstm_hidden_dim * 2)

# 分类
logits = self.classifier(final_hidden)

return logits


# 测试
if __name__ == "__main__":
model = CNNLSTM_Drowsiness(
cnn_feature_dim=512,
lstm_hidden_dim=256,
additional_features=10, # PERCLOS, yawning, head pose
num_classes=3
)

# 模拟输入
frames = torch.randn(4, 30, 3, 112, 112) # 30帧序列
features = torch.randn(4, 30, 10) # PERCLOS + yawning + etc

logits = model(frames, features)
probs = torch.softmax(logits, dim=-1)

print(f"输出shape: {logits.shape}")
print(f"预测类别分布:")
classes = ['正常', '疲劳', '睡眠']
for i in range(4):
for j, cls in enumerate(classes):
print(f" 样本{i+1} {cls}: {probs[i, j].item():.2%}")

3.2 性能对比

方法 准确率 延迟 模型大小 适用平台
PERCLOS单指标 75-85% <10ms N/A 任何嵌入式
CNN-LSTM 92-97% 50-100ms 50-200MB 高性能SoC
Transformer 95-98% 100-200ms 100-500MB GPU
轻量化模型 90-95% 20-40ms 5-20MB 移动端/边缘

4. 应用场景详解

4.1 驾驶场景

车辆类型 检测重点 Euro NCAP要求
轿车 PERCLOS + 微睡眠 2026强制
卡车 长时疲劳监测 强制安装
高铁 集成生理信号 国标要求
飞机 多模态监测 FAA规范

4.2 铁路场景

关键点 检测方法 事故案例
高铁司机 PERCLOS + EEG辅助 温州动车事故
信号员 行为监测 安全隐患
检修工 作业疲劳 工伤事故

4.3 医疗场景

应用 检测对象 方法
睡眠诊断 睡眠障碍患者 EEG + PERCLOS
ICU监测 重症患者 多生理信号
护士轮班 医护人员 PERCLOS + 行为

4.4 工业场景

工种 风险 检测方法
起重机操作 重物坠落 PERCLOS + 头部姿态
石油化工 爆炸风险 多模态监测
生产线 产品缺陷 PERCLOS + 行为

5. IMS开发启示

5.1 算法选择建议

IMS模块 推荐算法 原因
疲劳检测核心 PERCLOS + CNN-LSTM 平衡精度与实时性
微睡眠检测 PERCLOS阈值+闭眼时长 Euro NCAP明确要求
哈欠检测 MAR(Mouth Aspect Ratio) 补充信号
头部姿态 Head Pose估计 疲劳辅助信号

5.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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
IMS多模态疲劳检测融合策略
参考:arXiv 2024 Survey建议

融合信号:
1. PERCLOS(主信号)
2. 眨眼时长(辅助)
3. 头部姿态(辅助)
4. 哈欠频率(补充)
"""

import numpy as np
from typing import Dict, List

class DrowsinessFusion:
"""
多模态疲劳检测融合

融合策略:
- PERCLOS权重:0.4
- 眨眼时长权重:0.2
- 头部姿态权重:0.2
- 哈欠频率权重:0.2

Euro NCAP 2026:
- 综合分数 ≥ 70% → 二级警告
- 综合分数 ≥ 50% → 一级预警
"""

def __init__(self):
self.weights = {
'perclos': 0.4,
'blink_duration': 0.2,
'head_pose': 0.2,
'yawn_frequency': 0.2
}

self.thresholds = {
'perclos_high': 30, # PERCLOS ≥ 30% → 高风险
'perclos_mid': 15, # PERCLOS ≥ 15% → 中风险
'blink_long': 0.3, # 眨眼时长 > 0.3s → 疲劳
'head_down': 15, # 头部Pitch > 15° → 下垂
'yawn_high': 3 # 5分钟内哈欠 ≥ 3次 → 疲劳
}

def compute_risk_score(
self,
features: Dict[str, float]
) -> float:
"""
计算综合风险分数

Args:
features: 各特征值
- perclos: PERCLOS百分比
- blink_duration: 平均眨眼时长(秒)
- head_pitch: 头部俯仰角(度)
- yawn_count: 哈欠次数

Returns:
risk_score: 综合风险分数 (0-100)
"""
# PERCLOS风险
perclos = features['perclos']
if perclos >= self.thresholds['perclos_high']:
perclos_risk = 100
elif perclos >= self.thresholds['perclos_mid']:
perclos_risk = 50 + (perclos - 15) / 15 * 50
else:
perclos_risk = perclos / 15 * 50

# 眨眼时长风险
blink_dur = features['blink_duration']
if blink_dur >= self.thresholds['blink_long']:
blink_risk = 80
else:
blink_risk = blink_dur / 0.3 * 80

# 头部姿态风险
head_pitch = features['head_pitch']
if head_pitch >= self.thresholds['head_down']:
head_risk = 70
else:
head_risk = head_pitch / 15 * 70

# 哈欠风险
yawn_count = features['yawn_count']
if yawn_count >= self.thresholds['yawn_high']:
yawn_risk = 90
else:
yawn_risk = yawn_count / 3 * 90

# 加权融合
risk_score = (
perclos_risk * self.weights['perclos'] +
blink_risk * self.weights['blink_duration'] +
head_risk * self.weights['head_pose'] +
yawn_risk * self.weights['yawn_frequency']
)

return risk_score

def classify_state(
self,
risk_score: float
) -> str:
"""
分类疲劳状态

Args:
risk_score: 综合风险分数

Returns:
state: '正常' / '一级预警' / '二级警告'
"""
if risk_score >= 70:
return '二级警告'
elif risk_score >= 50:
return '一级预警'
else:
return '正常'


# 测试
if __name__ == "__main__":
fusion = DrowsinessFusion()

# 场景1:轻度疲劳
features_light = {
'perclos': 18,
'blink_duration': 0.25,
'head_pitch': 10,
'yawn_count': 1
}

score1 = fusion.compute_risk_score(features_light)
state1 = fusion.classify_state(score1)

print(f"轻度疲劳场景:")
print(f" PERCLOS: {features_light['perclos']}%")
print(f" 综合分数: {score1:.1f}")
print(f" 状态: {state1}")

# 场景2:重度疲劳
features_heavy = {
'perclos': 35,
'blink_duration': 0.35,
'head_pitch': 18,
'yawn_count': 4
}

score2 = fusion.compute_risk_score(features_heavy)
state2 = fusion.classify_state(score2)

print(f"\n重度疲劳场景:")
print(f" PERCLOS: {features_heavy['perclos']}%")
print(f" 综合分数: {score2:.1f}")
print(f" 状态: {state2}")

5.3 开发优先级

优先级 开发项 技术路线 Euro NCAP得分
🔴 P0 PERCLOS计算 滑动窗口+阈值 核心指标
🔴 P0 微睡眠检测 闭眼时长>1.5秒 2分
🔴 P0 CNN-LSTM模型 ResNet+LSTM 精度提升
🟡 P1 哈欠检测 MAR计算 补充信号
🟡 P1 多模态融合 加权融合 精度+5%
🟢 P2 KSS映射 自评分数对应 研究参考

6. 总结

arXiv 2024睡眠检测综述的核心贡献:

跨领域视野

  1. 首个全景综述:驾驶+铁路+航空+医疗+工业
  2. 方法分类清晰:生理信号 vs 视觉方法
  3. 挑战识别:实时性、数据传输、系统偏见

PERCLOS仍是核心

  1. 标准指标:Euro NCAP、ISO标准采用
  2. 晚期检测局限:需结合其他信号早期预警
  3. 深度学习革新:CNN-LSTM达到92-97%准确率

IMS落地要点

  • PERCLOS必须实现:Euro NCAP核心指标
  • 微睡眠检测必须精确:闭眼≥1.5秒触发警告
  • 多模态融合提升精度:PERCLOS + 眨眼 + 头部 + 哈欠
  • 轻量化模型边缘部署:CNN-LSTM压缩至5-20MB

参考链接:


arXiv 2024 睡眠检测综述解读:PERCLOS技术演进与多场景应用全景
https://dapalm.com/2026/07/05/2026-07-05-drowsiness-detection-survey-perclos-arxiv-2024-zh/
作者
Mars
发布于
2026年7月5日
许可协议