EEG+眼动追踪融合:认知负荷监测的系统性综述与车内应用启示

论文信息

项目 内容
标题 Combining EEG and eye-tracking for cognitive and physiological states monitoring: a systematic review
发表期刊 Frontiers in Neuroergonomics
发表时间 2025年12月
DOI 10.3389/fnrgo.2025.1736672
综述范围 47篇研究,覆盖2010-2025

核心发现

关键结论

EEG与眼动追踪融合的分类模型,准确率显著高于任一单模态方案。 认知分心在眼动指标中表现早于驾驶性能下降,EEG提供了”看不到的认知负荷”信号,眼动提供了”可观测的行为代理”信号。

融合 vs 单模态对比

方案 平均准确率 侵入性 实时性 车内可行性
仅EEG 78-85% 高(接触式) 是 ❌
仅眼动 65-72% 低(非接触) 是 ⚠️
EEG+眼动 85-92% 中 是 ⚠️ 需简化EEG

系统性发现

1. 认知负荷的EEG特征

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
import numpy as np
from scipy.signal import welch

class EEGCognitiveLoadFeatures:
"""
EEG认知负荷特征提取

基于系统性综述总结的关键频段特征:
- Alpha频段(8-13Hz): 认知负荷增加时alpha功率下降
- Theta频段(4-8Hz): 认知负荷增加时theta功率上升
- Theta/Alpha比: 最稳定的认知负荷指数
"""

# 关键频段定义
BANDS = {
'delta': (1, 4), # 慢波睡眠/深放松
'theta': (4, 8), # 认知负荷↑ → 功率↑
'alpha': (8, 13), # 认知负荷↑ → 功率↓
'beta': (13, 30), # 活跃思维
'gamma': (30, 45), # 高级认知
}

def __init__(self, fs: float = 250.0):
self.fs = fs

def extract_band_powers(self, eeg_signal: np.ndarray) -> dict:
"""
提取各频段功率

Args:
eeg_signal: (num_channels, num_samples) EEG信号

Returns:
band_powers: dict of band -> (num_channels,) power
"""
powers = {}
for band_name, (fmin, fmax) in self.BANDS.items():
freqs, psd = welch(eeg_signal, fs=self.fs,
nperseg=min(256, eeg_signal.shape[-1]))
band_mask = (freqs >= fmin) & (freqs <= fmax)
band_power = np.sum(psd[:, band_mask], axis=1)
powers[band_name] = band_power

return powers

def cognitive_load_index(self, eeg_signal: np.ndarray) -> float:
"""
计算认知负荷指数(CLI)

CLI = Theta power / Alpha power

高CLI = 高认知负荷
低CLI = 低认知负荷

论文综述结论:Theta/Alpha比是最稳定的
跨被试认知负荷指标
"""
powers = self.extract_band_powers(eeg_signal)

theta_power = powers['theta'].mean()
alpha_power = powers['alpha'].mean()

if alpha_power > 0:
cli = theta_power / alpha_power
else:
cli = 0.0

return float(cli)

def frontal_alpha_asymmetry(self, eeg_signal: np.ndarray,
channel_map: dict) -> float:
"""
前额alpha不对称性

反映情绪效价和认知接近/回避动机
左侧alpha功率低 → 接近动机
右侧alpha功率低 → 回避动机

Args:
eeg_signal: (num_channels, num_samples)
channel_map: {'F3': idx, 'F4': idx, ...}
"""
powers = self.extract_band_powers(eeg_signal)
alpha = powers['alpha']

left_alpha = alpha[channel_map['F3']]
right_alpha = alpha[channel_map['F4']]

# 不对称指数
asymmetry = np.log(right_alpha) - np.log(left_alpha)
return float(asymmetry)

def engagement_index(self, eeg_signal: np.ndarray) -> float:
"""
参与度指数

EI = Beta / (Alpha + Theta)

高EI = 高度警觉和参与
低EI = 疲劳或分心

综述发现:EI在驾驶分心检测中
被使用频率仅次于Theta/Alpha比
"""
powers = self.extract_band_powers(eeg_signal)
beta = powers['beta'].mean()
alpha = powers['alpha'].mean()
theta = powers['theta'].mean()

if (alpha + theta) > 0:
ei = beta / (alpha + theta)
else:
ei = 0.0

return float(ei)

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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
import numpy as np
from scipy.stats import entropy

class EyeTrackingCognitiveFeatures:
"""
眼动追踪认知分心特征提取

综述总结的关键眼动指标:
1. 注视分布熵 - 反映注意力分散程度
2. 扫视频率 - 反映信息搜索速率
3. 注视持续时间 - 反映信息处理深度
4. 瞳孔直径变化 - 反映认知负荷
5. 扫视速度 - 反映注意力切换速率
"""

def __init__(self, fps: float = 30.0):
self.fps = fps

def gaze_distribution_entropy(self, gaze_points: np.ndarray,
grid_size: int = 8) -> float:
"""
注视分布熵

高熵 = 注意力分散(认知分心)
低熵 = 注意力集中

Args:
gaze_points: (N, 2) 注视点序列 (x, y)
grid_size: 空间网格大小

Returns:
entropy_val: 注视分布的香农熵
"""
# 将注视点映射到网格
H, W = grid_size, grid_size
grid = np.zeros((H, W))

for x, y in gaze_points:
gx = min(int(x * W), W - 1)
gy = min(int(y * H), H - 1)
grid[gy, gx] += 1

# 归一化为概率分布
total = grid.sum()
if total > 0:
grid = grid / total
# 计算熵
grid_flat = grid.flatten()
grid_flat = grid_flat[grid_flat > 0]
entropy_val = entropy(grid_flat)
else:
entropy_val = 0.0

return float(entropy_val)

def saccade_frequency(self, gaze_points: np.ndarray,
velocity_threshold: float = 0.3) -> float:
"""
扫视频率

Args:
gaze_points: (N, 2) 归一化注视点
velocity_threshold: 扫视速度阈值

Returns:
freq: 扫视/秒
"""
if len(gaze_points) < 2:
return 0.0

# 计算速度
velocities = np.linalg.norm(
np.diff(gaze_points, axis=0), axis=1
) * self.fps

# 统计扫视次数
saccades = np.sum(velocities > velocity_threshold)
duration_sec = len(gaze_points) / self.fps

return float(saccades / duration_sec)

def fixation_duration_stats(self, gaze_points: np.ndarray,
velocity_threshold: float = 0.05) -> dict:
"""
注视持续时间统计

认知分心时:
- 平均注视时间缩短(快速切换)
- 注视时间方差增大(不规则)

Args:
gaze_points: (N, 2)

Returns:
stats: dict with mean, std, max fixation duration
"""
if len(gaze_points) < 2:
return {'mean': 0, 'std': 0, 'max': 0}

velocities = np.linalg.norm(
np.diff(gaze_points, axis=0), axis=1
)

# 注视段检测
is_fixation = velocities < velocity_threshold
durations = []
current_dur = 1

for i in range(1, len(is_fixation)):
if is_fixation[i]:
current_dur += 1
else:
if current_dur > 3: # 最少4帧
durations.append(current_dur / self.fps)
current_dur = 1

if current_dur > 3:
durations.append(current_dur / self.fps)

if not durations:
return {'mean': 0, 'std': 0, 'max': 0}

return {
'mean': float(np.mean(durations)),
'std': float(np.std(durations)),
'max': float(np.max(durations)),
'count': len(durations)
}

def pupil_diameter_variability(self, pupil_diameters: np.ndarray) -> dict:
"""
瞳孔直径变异性

认知负荷↑ → 瞳孔直径↑ + 变异性↑

注意:需去除光反射影响

Args:
pupil_diameters: (N,) 瞳孔直径序列(mm)

Returns:
stats: dict
"""
# 去趋势(去除光照变化)
from scipy.signal import detrend
detrended = detrend(pupil_diameters)

return {
'mean': float(np.mean(pupil_diameters)),
'std': float(np.std(detrended)),
'range': float(np.max(pupil_diameters) - np.min(pupil_diameters)),
'trend': float(np.polyfit(np.arange(len(pupil_diameters)),
pupil_diameters, 1)[0])
}

def composite_distraction_score(self, gaze_points: np.ndarray,
pupil_diameters: np.ndarray = None) -> float:
"""
综合分心评分

融合多个眼动指标的加权评分

Args:
gaze_points: (N, 2)
pupil_diameters: (N,) optional

Returns:
score: 0-1, 1=高度分心
"""
# 各指标
entropy_val = self.gaze_distribution_entropy(gaze_points)
sacc_freq = self.saccade_frequency(gaze_points)
fix_stats = self.fixation_duration_stats(gaze_points)

# 归一化
entropy_norm = min(entropy_val / 3.0, 1.0) # 最大熵约ln(64)≈4.16
sacc_norm = min(sacc_freq / 3.0, 1.0) # 正常约0.5-1.5次/秒
fix_norm = 1.0 - min(fix_stats['mean'] / 1.0, 1.0) # 正常注视~0.3-0.5s

# 加权融合
score = 0.4 * entropy_norm + 0.3 * sacc_norm + 0.3 * fix_norm

if pupil_diameters is not None:
pupil_stats = self.pupil_diameter_variability(pupil_diameters)
pupil_norm = min(pupil_stats['std'] / 0.5, 1.0)
score = 0.7 * score + 0.3 * pupil_norm

return float(np.clip(score, 0, 1))


# ==================== 融合模型 ====================
class EEGEyeFusionModel:
"""
EEG + 眼动追踪融合模型

基于系统性综述的融合策略:
1. 特征级融合(最常用,47篇中32篇)
2. 决策级融合(较少)
3. 深度学习端到端融合(新兴趋势)
"""

def __init__(self):
self.eeg_extractor = EEGCognitiveLoadFeatures(fs=250.0)
self.eye_extractor = EyeTrackingCognitiveFeatures(fps=30.0)

def extract_features(self, eeg_signal: np.ndarray,
gaze_points: np.ndarray,
pupil_diameters: np.ndarray = None) -> dict:
"""
提取EEG+眼动融合特征

Args:
eeg_signal: (num_channels, num_samples)
gaze_points: (N, 2)
pupil_diameters: (N,)

Returns:
features: dict containing all features
"""
# EEG特征
cli = self.eeg_extractor.cognitive_load_index(eeg_signal)
ei = self.eeg_extractor.engagement_index(eeg_signal)

# 眼动特征
gaze_entropy = self.eye_extractor.gaze_distribution_entropy(gaze_points)
sacc_freq = self.eye_extractor.saccade_frequency(gaze_points)
fix_stats = self.eye_extractor.fixation_duration_stats(gaze_points)
distraction_score = self.eye_extractor.composite_distraction_score(
gaze_points, pupil_diameters
)

# 融合特征向量
feature_vector = np.array([
cli, # EEG认知负荷指数
ei, # EEG参与度指数
gaze_entropy, # 注视分布熵
sacc_freq, # 扫视频率
fix_stats['mean'], # 平均注视时间
fix_stats['std'], # 注视时间方差
distraction_score, # 综合分心评分
])

return {
'feature_vector': feature_vector,
'eeg_cli': cli,
'eeg_ei': ei,
'eye_entropy': gaze_entropy,
'eye_saccade_freq': sacc_freq,
'eye_fixation_mean': fix_stats['mean'],
'eye_distraction_score': distraction_score,
}

def classify_state(self, features: dict) -> dict:
"""
简化的认知状态分类

综述发现:融合方案的决策边界通常为:
- CLI > 0.8 + distraction > 0.6 → 认知分心
- EI < 0.3 + fixation_mean < 0.2 → 疲劳
- 正常范围 → 正常驾驶
"""
cli = features['eeg_cli']
ei = features['eeg_ei']
distraction = features['eye_distraction_score']
fix_mean = features['eye_fixation_mean']

# 简化决策逻辑
if cli > 0.8 and distraction > 0.6:
state = 'cognitive_distraction'
confidence = min((cli - 0.8) / 0.5 + (distraction - 0.6) / 0.4, 1.0)
elif ei < 0.3 and fix_mean < 0.2:
state = 'fatigue'
confidence = min((0.3 - ei) / 0.3 + (0.2 - fix_mean) / 0.2, 1.0)
else:
state = 'normal'
confidence = 1.0 - max(cli - 0.5, 0) - max(0.3 - ei, 0)

return {
'state': state,
'confidence': float(np.clip(confidence, 0, 1)),
'features': features
}


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

# 模拟EEG信号 (14通道, 5秒@250Hz)
eeg = np.random.randn(14, 1250) * 10
# 添加alpha和theta成分
t = np.arange(1250) / 250.0
for ch in range(14):
eeg[ch] += 5 * np.sin(2 * np.pi * 10 * t) # alpha 10Hz
eeg[ch] += 3 * np.sin(2 * np.pi * 6 * t) # theta 6Hz

# 模拟眼动数据 (150个采样点@30fps, 5秒)
gaze = np.random.rand(150, 2) * 0.3 + np.array([0.4, 0.3])
pupil = 4 + np.random.randn(150) * 0.3

# 测试融合模型
model = EEGEyeFusionModel()
features = model.extract_features(eeg, gaze, pupil)
result = model.classify_state(features)

print("=" * 60)
print("EEG + 眼动追踪融合检测结果")
print("=" * 60)
print(f"\n状态: {result['state']}")
print(f"置信度: {result['confidence']:.2%}")
print(f"\n特征值:")
print(f" EEG认知负荷指数(CLI): {result['features']['eeg_cli']:.4f}")
print(f" EEG参与度指数(EI): {result['features']['eeg_ei']:.4f}")
print(f" 注视分布熵: {result['features']['eye_entropy']:.4f}")
print(f" 扫视频率(次/秒): {result['features']['eye_saccade_freq']:.2f}")
print(f" 平均注视时间(秒): {result['features']['eye_fixation_mean']:.3f}")
print(f" 综合分心评分: {result['features']['eye_distraction_score']:.4f}")

综述关键发现

EEG在认知检测中的角色

EEG指标 认知负荷方向 效应量 稳定性
Alpha功率 负荷↑ → 功率↓ 中等 高
Theta功率 负荷↑ → 功率↑ 中等 高
Theta/Alpha比 负荷↑ → 比值↑ 大 最高
Beta功率 负荷↑ → 功率↑ 小 中
前额不对称 情绪相关 小 中

眼动在认知检测中的角色

眼动指标 认知负荷方向 效应量 可检测性
注视分布熵 负荷↑ → 熵↑ 大 高
扫视频率 负荷↑ → 频率↑ 中 高
注视时间 负荷↑ → 时间↓ 中 高
瞳孔直径 负荷↑ → 直径↑ 大 中(受光照影响)
扫视速度 负荷↑ → 速度↑ 小 中

融合策略对比

策略 论文数量 准确率 实时性 复杂度
特征级融合 32/47 85-92% 中 低
决策级融合 8/47 82-88% 高 低
深度学习端到端 7/47 88-95% 低 高

车内应用可行性分析

耳道EEG:突破传统EEG瓶颈

维度 传统EEG 耳道EEG(cEEGrid)
电极数 14-64 1-5
佩戴方式 头皮贴片 入耳式
信号质量 高 中(可接受)
侵入性 高 低
车内可行 ❌ ✅ 可能
驾驶干扰 高 极低

IMS融合方案建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
IMS认知负荷监测三阶方案:

阶段1(当前):仅眼动追踪
├── 注视分布熵 + 扫视频率
├── 准确率 ~70%
└── 已部署

阶段2(1-2年):眼动+耳道EEG
├── cEEGrid入耳式EEG
├── 融合Theta/Alpha比 + 眼动指标
├── 准确率 ~85%
└── 需硬件合作

阶段3(3-5年):全脑机接口
├── 非侵入式干电极EEG头枕
├── 深度学习端到端融合
├── 准确率 >90%
└── 技术成熟后

量产可行性

指标 阶段1 阶段2 阶段3
准确率 70% 85% >90%
硬件成本 $0(复用DMS) +$15(cEEGrid) +$30(EEG头枕)
用户接受度 100% 85% 70%
法规合规 无障碍 医疗级可能 医疗级
部署时间 已完成 12个月 36个月

开发启示

1. 短期可落地:增强眼动指标

当前IMS的眼动模块已输出视线方向,可直接增加:

  • 注视分布熵:5行代码,空间网格化+熵计算
  • 扫视频率统计:速度阈值+计数
  • 注视持续时间分析:滑动窗口统计

2. 中期方向:耳道EEG集成

  • 与TWS耳机厂商合作(如苹果AirPods Pro已具备生物传感潜力)
  • cEEGrid入耳式电极,无需额外佩戴
  • 重点提取Theta/Alpha比作为认知负荷核心指标

3. 与EyeCue框架的互补

EyeCue(本文另一篇解读)提供”注视+场景”的认知分心检测,本综述提供”EEG+眼动”的融合方案。两者可互补:

1
2
3
4
5
6
7
8
EyeCue(软件层):
注视点 + 场景视频 → 认知分心概率 74%

EEG+眼动(硬件层):
脑电特征 + 眼动统计 → 认知负荷指数 85%

融合决策层:
EyeCue + EEG → 综合认知状态 90%+

论文DOI: 10.3389/fnrgo.2025.1736672