SAE L3 自动驾驶被动疲劳——眼动+ECG 多模态检测

论文信息

  • 标题: Multimodal Physiological Detection of Passive Fatigue in SAE Level 3 Automated Driving Using Eye-Movement and ECG Features
  • 来源: Applied Sciences (MDPI), 16(18), 9049
  • 作者: Li Jiangtian, Lan Chenghui
  • 时间: 2026年9月
  • 核心贡献: L3 自动驾驶中被动疲劳的多模态生理检测

核心创新

  1. 被动疲劳 (Passive Fatigue):L3 中驾驶员不操作但需监控,因任务单调产生疲劳
  2. 眼动 + ECG 双模态:互补检测认知衰减
  3. SAE L3 场景特化:针对监控型疲劳而非驾驶型疲劳

方法详解

1. 被动疲劳 vs 主动疲劳

维度 主动疲劳 (L0-L2) 被动疲劳 (L3)
驾驶员角色 主动操作 监控者
疲劳来源 操作负荷 任务单调
发生速度 渐进 更快
眼动特征 凝视分散 凝视固定+眨眼减少
ECG 特征 HRV 下降 HRV 更快下降
检测难度 高(无行为信号)

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
SAE L3 被动疲劳多模态检测

眼动特征 + ECG 特征 → 融合分类
"""

import numpy as np
from typing import Dict, Tuple, List
from dataclasses import dataclass

@dataclass
class L3FatigueConfig:
"""L3 被动疲劳检测配置"""
# 眼动
eye_tracker_fps: int = 60
# ECG
ecg_fs: int = 250
# 窗口
window_sec: float = 60.0 # 60 秒窗口
# 疲劳等级
n_levels: int = 3 # 清醒/轻度疲劳/深度疲劳

class EyeMovementFeatures:
"""眼动特征提取"""

@staticmethod
def extract(gaze_x: np.ndarray, gaze_y: np.ndarray,
blink_flags: np.ndarray, fps: int = 60) -> Dict[str, float]:
"""
Args:
gaze_x, gaze_y: 视线坐标, shape=(N,)
blink_flags: 眨眼标记, shape=(N,)
fps: 帧率

Returns:
features: 眼动特征字典
"""
N = len(gaze_x)
duration = N / fps # 秒

# 1. 眨眼频率
blink_rate = np.sum(blink_flags) / duration # 次/分钟

# 2. PERCLOS (80% 眼睑闭合)
eye_openness = 1 - blink_flags
perclos = np.sum(eye_openness < 0.2) / N * 100

# 3. 凝视分散度
gaze_std_x = np.std(gaze_x)
gaze_std_y = np.std(gaze_y)
gaze_dispersion = np.sqrt(gaze_std_x**2 + gaze_std_y**2)

# 4. 扫视频率
saccades = np.sum(
np.sqrt(np.diff(gaze_x)**2 + np.diff(gaze_y)**2) > 0.1
)
saccade_rate = saccades / duration

# 5. 微跳视幅度
microsaccade = np.mean(
np.sqrt(np.diff(gaze_x)**2 + np.diff(gaze_y)**2)
)

# 6. 瞳孔直径变化 (如有)
# pupil_var = np.std(pupil_diameter)

return {
'blink_rate': blink_rate,
'perclos': perclos,
'gaze_dispersion': gaze_dispersion,
'saccade_rate': saccade_rate,
'microsaccade_amplitude': microsaccade,
# L3 特有: 凝视固定时间比
'gaze_fixation_ratio': np.sum(
np.sqrt(np.diff(gaze_x)**2 + np.diff(gaze_y)**2) < 0.01
) / (N - 1)
}

class ECGFeatures:
"""ECG 特征提取"""

@staticmethod
def extract(ecg: np.ndarray, fs: int = 250) -> Dict[str, float]:
"""
Args:
ecg: ECG 信号, shape=(N,)
fs: 采样率

Returns:
features: ECG 特征字典
"""
# 简化 R 峰检测
from scipy.signal import find_peaks
r_peaks, _ = find_peaks(ecg, distance=fs*0.6, height=np.mean(ecg)*1.5)

if len(r_peaks) < 2:
return {'hr_mean': 0, 'hrv_rmssd': 0, 'hrv_sdnn': 0, 'lf_hf': 0}

# RR 间期
rr_intervals = np.diff(r_peaks) / fs # 秒

# 心率
hr_mean = 60 / np.mean(rr_intervals) if len(rr_intervals) > 0 else 0

# HRV 时域
rmssd = np.sqrt(np.mean(np.diff(rr_intervals)**2))
sdnn = np.std(rr_intervals)

# 频域 (LF/HF 比)
from numpy.fft import rfft, rfftfreq
rr_interp = np.interp(
np.arange(0, len(rr_intervals), 0.25),
np.arange(len(rr_intervals)),
rr_intervals
)
spectrum = np.abs(rfft(rr_interp))
freqs = rfftfreq(len(rr_interp), 0.25)

lf_power = np.sum(spectrum[(freqs >= 0.04) & (freqs <= 0.15)])
hf_power = np.sum(spectrum[(freqs >= 0.15) & (freqs <= 0.40)])
lf_hf = lf_power / max(hf_power, 1e-10)

return {
'hr_mean': hr_mean,
'hrv_rmssd': rmssd * 1000, # ms
'hrv_sdnn': sdnn * 1000, # ms
'lf_hf_ratio': lf_hf
}

class L3FatigueDetector:
"""L3 被动疲劳多模态检测器"""

THRESHOLDS = {
'alert': {'perclos': 15, 'hrv_rmssd': 40, 'lf_hf': 1.5},
'mild': {'perclos': 25, 'hrv_rmssd': 25, 'lf_hf': 2.0},
'severe': {'perclos': 40, 'hrv_rmssd': 15, 'lf_hf': 3.0}
}

def classify(self, eye_feat: dict, ecg_feat: dict) -> str:
"""分类疲劳等级"""
score = 0

if eye_feat['perclos'] > self.THRESHOLDS['severe']['perclos']:
score += 3
elif eye_feat['perclos'] > self.THRESHOLDS['mild']['perclos']:
score += 2
elif eye_feat['perclos'] > self.THRESHOLDS['alert']['perclos']:
score += 1

if ecg_feat['hrv_rmssd'] < self.THRESHOLDS['severe']['hrv_rmssd']:
score += 3
elif ecg_feat['hrv_rmssd'] < self.THRESHOLDS['mild']['hrv_rmssd']:
score += 2
elif ecg_feat['hrv_rmssd'] < self.THRESHOLDS['alert']['hrv_rmssd']:
score += 1

if score >= 5: return 'Severe Fatigue'
elif score >= 3: return 'Mild Fatigue'
else: return 'Alert'


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

# 模拟 60 秒数据
N_eye = 60 * 60 # 60fps
N_ecg = 60 * 250 # 250Hz

# 清醒状态
alert_gaze_x = np.random.randn(N_eye) * 0.1
alert_gaze_y = np.random.randn(N_eye) * 0.1
alert_blink = (np.random.rand(N_eye) < 0.005).astype(int)
alert_ecg = np.sin(2 * np.pi * 1.2 * np.arange(N_ecg) / 250)

# 被动疲劳
fatigue_gaze_x = np.random.randn(N_eye) * 0.02 # 凝视固定
fatigue_gaze_y = np.random.randn(N_eye) * 0.02
fatigue_blink = (np.random.rand(N_eye) < 0.002).astype(int) # 眨眼减少
fatigue_ecg = np.sin(2 * np.pi * 1.0 * np.arange(N_ecg) / 250)

eye_feat_alert = EyeMovementFeatures.extract(alert_gaze_x, alert_gaze_y, alert_blink)
eye_feat_fatigue = EyeMovementFeatures.extract(fatigue_gaze_x, fatigue_gaze_y, fatigue_blink)
ecg_feat_alert = ECGFeatures.extract(alert_ecg)
ecg_feat_fatigue = ECGFeatures.extract(fatigue_ecg)

detector = L3FatigueDetector()

print("=== L3 被动疲劳多模态检测 ===")
print(f"\n{'指标':<25} {'清醒':<15} {'疲劳':<15}")
for key in eye_feat_alert:
print(f" {key:<23} {eye_feat_alert[key]:<15.2f} {eye_feat_fatigue[key]:<15.2f}")
for key in ecg_feat_alert:
print(f" {key:<23} {ecg_feat_alert[key]:<15.2f} {ecg_feat_fatigue[key]:<15.2f}")

print(f"\n清醒 → {detector.classify(eye_feat_alert, ecg_feat_alert)}")
print(f"疲劳 → {detector.classify(eye_feat_fatigue, ecg_feat_fatigue)}")

L3 被动疲劳特征

指标 清醒 轻度疲劳 深度疲劳 来源
PERCLOS <15% 15-25% >40% 眼动
眨眼率 15-20/min 8-12/min <8/min 眼动
凝视分散 眼动
HRV RMSSD >40ms 25-40ms <15ms ECG
LF/HF 比 <1.5 1.5-2.0 >3.0 ECG
心率 70-80 65-75 <65 ECG

IMS 应用

1. L3 场景特化需求

L3 自动驾驶改变了 DMS 需求:

  • 驾驶员不操作但需准备接管
  • 疲劳来自单调监控而非操作负荷
  • 检测更难(无行为信号)
  • 需要生理信号补充

2. 眼动 + ECG 互补

检测维度 眼动 ECG 互补价值
疲劳早期 ⚠️ PERCLOS 滞后 ✅ HRV 先变 ECG 预警
疲劳确认 ✅ PERCLOS 直观 ⚠️ HRV 个体差异 眼动确认
分心检测 ✅ 凝视分散 ❌ 不敏感 眼动主导
接管准备 ✅ 扫视频率 ✅ HRV 恢复 双模态
接触方式 非接触 需接触 融合

3. 与方向盘 sEMG 三模态融合

模态 预警提前 接触方式 L3 适用
ECG HRV +5min 接触 ⚠️ 座椅集成
sEMG 方向盘 +2-5min 自然接触 ⚠️ L3 少握方向盘
眼动 PERCLOS 0s 非接触 ✅ 最适合 L3

L3 最优方案:眼动为主 + ECG 座椅集成 + sEMG 方向盘补充

开发启示

  1. L3 被动疲劳不同于 L2:单调监控比主动驾驶更易疲劳,需更早检测
  2. HRV 是最早的生理指标:比 PERCLOS 早 5-10 分钟变化
  3. 眼动最适合 L3:非接触 + 持续监控 + 凝视分散检测分心
  4. 座椅集成 ECG:座椅靠背内嵌电极,实现非接触 ECG
  5. L3 接管前需疲劳评估:系统判断驾驶员是否可安全接管

https://dapalm.com/2026/09/15/2026-09-15-l3-passive-fatigue-eye-ecg-multimodal-ims/
作者
Mars
发布于
2026年9月15日
许可协议