Smart Eye视线追踪+CBTA飞行训练验证:从训练评估到座舱设计反馈的闭环

航空训练验证新范式 — 当眼动数据不仅评估飞行员,还评估模拟器本身是否设计了正确的注意力需求

背景

EASA于2026年6月引入了基于胜任力的训练与评估(CBTA)标准,用于空中交通管制员的初始培训。APATS 2026(亚太航空训练研讨会)设立了”超越合规的CBTA”专题。I/ITSEC 2026征集中关于CBTA、训练系统评估和生命周期有效性的研究。

CBTA的核心转变:从”任务完成”到”胜任力展示“。两个飞行员可能都成功完成了任务,但一个通过主动监测发现问题,另一个只在告警后才响应。结果相同,胜任力不同。

CBTA + 眼动追踪的闭环

核心案例:VR飞行训练研究

研究发现

维度 经验丰富飞行员 经验较少飞行员 差异显著?
注视次数 更少 更多 ✅
驻留时长 更长 更短 ✅
扫视路径 结构化、非随机 随机性更强 ✅
训练器评分 — — ❌ 无关联

关键发现: 飞行训练器的性能评分与飞行员真实经验无关,但眼动指标能清晰区分经验水平。说明:

  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
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
"""
CBTA + 眼动追踪四阶段实施框架
基于Smart Eye + EyeTracking PilotReady方案

四阶段:
1. 定义行为 → 2. 映射场景 → 3. 同步证据 → 4. 对比改进

硬件配置:
- Smart Eye远程眼动仪(座舱内嵌入,无需佩戴)
- PilotReady软件集成
- 模拟器事件同步
"""

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

class CompetencyType(Enum):
"""ICAO CBTA胜任力类型"""
SITUATION_AWARENESS = "situation_awareness"
INFORMATION_PROCESSING = "information_processing"
WORKLOAD_MANAGEMENT = "workload_management"
PROBLEM_SOLVING = "problem_solving"
COMMUNICATION = "communication"
LEADERSHIP = "leadership"
AUTOMATION_MANAGEMENT = "automation_management"

@dataclass
class GazeEvent:
"""眼动事件"""
timestamp: float
gaze_x: float # 注视点x
gaze_y: float # 注视点y
instrument_id: str # 注视的仪表
fixation_duration: float # 注视持续时间(ms)
is_saccade: bool # 是否扫视
saccade_velocity: float # 扫视速度

@dataclass
class SimulatorEvent:
"""模拟器事件"""
timestamp: float
event_type: str # 'alert', 'failure', 'task_start', 'task_end'
instrument_affected: str
severity: str # 'info', 'warning', 'critical'

class CBTAEyeTrackingValidator:
"""
CBTA + 眼动追踪验证器

功能:
1. 评估飞行员胜任力展示
2. 评估模拟器场景设计
"""

# 仪表区域定义(座舱坐标系)
INSTRUMENT_AREAS = {
'ASI': {'x': (0.2, 0.4), 'y': (0.1, 0.3)}, # 空速表
'ALT': {'x': (0.4, 0.6), 'y': (0.1, 0.3)}, # 高度表
'HSI': {'x': (0.6, 0.8), 'y': (0.1, 0.3)}, # 水平状态指示器
'VVI': {'x': (0.2, 0.4), 'y': (0.3, 0.5)}, # 垂直速度
'AI': {'x': (0.4, 0.6), 'y': (0.3, 0.5)}, # 姿态指示器
'NAV': {'x': (0.6, 0.8), 'y': (0.3, 0.5)}, # 导航
'ENG': {'x': (0.2, 0.4), 'y': (0.5, 0.7)}, # 发动机
'COM': {'x': (0.4, 0.6), 'y': (0.5, 0.7)}, # 通信
'OUT': {'x': (0.0, 1.0), 'y': (0.7, 1.0)}, # 窗外
}

def __init__(self):
self.gaze_events: List[GazeEvent] = []
self.sim_events: List[SimulatorEvent] = []

def add_gaze_data(self, events: List[GazeEvent]):
self.gaze_events.extend(events)

def add_sim_events(self, events: List[SimulatorEvent]):
self.sim_events.extend(events)

def analyze_competency(
self,
competency: CompetencyType,
time_window: Tuple[float, float]
) -> dict:
"""
分析特定胜任力在时间窗口内的展示

Returns:
dict: 胜任力评估结果
"""
# 筛选时间窗口内的眼动事件
window_gaze = [
g for g in self.gaze_events
if time_window[0] <= g.timestamp <= time_window[1]
]

# 计算仪表注视分布
instrument_dwell = self._compute_dwell_time(window_gaze)

# 计算扫视模式
scan_pattern = self._analyze_scan_pattern(window_gaze)

# 计算事件反应时延
reaction_delays = self._compute_reaction_delays(
window_gaze, time_window
)

# 根据胜任力类型评估
if competency == CompetencyType.SITUATION_AWARENESS:
return self._eval_situation_awareness(
instrument_dwell, scan_pattern, reaction_delays
)
elif competency == CompetencyType.INFORMATION_PROCESSING:
return self._eval_info_processing(
instrument_dwell, scan_pattern
)
else:
return {'score': 0.5, 'evidence': 'Not implemented'}

def _compute_dwell_time(
self, events: List[GazeEvent]
) -> Dict[str, float]:
"""计算各仪表驻留时间占比"""
total_time = sum(e.fixation_duration for e in events if not e.is_saccade)
dwell = {}
for inst_id in self.INSTRUMENT_AREAS:
inst_time = sum(
e.fixation_duration for e in events
if e.instrument_id == inst_id and not e.is_saccade
)
dwell[inst_id] = inst_time / max(total_time, 1)
return dwell

def _analyze_scan_pattern(
self, events: List[GazeEvent]
) -> dict:
"""分析扫视模式"""
fixations = [e for e in events if not e.is_saccade]
saccades = [e for e in events if e.is_saccade]

if not fixations:
return {'fixation_count': 0, 'avg_duration': 0, 'scan_randomness': 0}

# 平均注视持续时间
avg_duration = np.mean([f.fixation_duration for f in fixations])

# 扫视路径随机性(转移矩阵熵)
transitions = []
for i in range(len(fixations) - 1):
transitions.append((fixations[i].instrument_id,
fixations[i+1].instrument_id))

# 计算转移熵
unique_instruments = list(set(f.instrument_id for f in fixations))
trans_matrix = np.zeros((len(unique_instruments),
len(unique_instruments)))
inst_idx = {inst: i for i, inst in enumerate(unique_instruments)}

for src, dst in transitions:
if src in inst_idx and dst in inst_idx:
trans_matrix[inst_idx[src], inst_idx[dst]] += 1

# 行归一化
row_sums = trans_matrix.sum(axis=1, keepdims=True)
trans_prob = np.divide(trans_matrix, row_sums,
where=row_sums > 0)
# Shannon熵
entropy = -np.sum(trans_prob * np.log2(trans_prob + 1e-10), axis=1)
avg_entropy = np.mean(entropy)

return {
'fixation_count': len(fixations),
'avg_duration_ms': avg_duration,
'saccade_count': len(saccades),
'scan_entropy': avg_entropy,
'scan_randomness': avg_entropy / np.log2(max(len(unique_instruments), 2))
}

def _compute_reaction_delays(
self,
gaze_events: List[GazeEvent],
time_window: Tuple[float, float]
) -> List[float]:
"""计算事件→注视反应时延"""
delays = []
for sim_ev in self.sim_events:
if not (time_window[0] <= sim_ev.timestamp <= time_window[1]):
continue
# 找到事件后第一次注视到受影响仪表的时间
for g in gaze_events:
if g.timestamp < sim_ev.timestamp:
continue
if g.instrument_id == sim_ev.instrument_affected:
delay = g.timestamp - sim_ev.timestamp
if delay < 10: # 10秒内
delays.append(delay)
break
return delays

def _eval_situation_awareness(
self, dwell: dict, scan: dict, delays: list
) -> dict:
"""评估情境感知胜任力"""
# 指标1: 关键仪表覆盖率
key_insts = ['ASI', 'ALT', 'HSI', 'AI']
coverage = sum(dwell.get(i, 0) for i in key_insts)

# 指标2: 扫视频率(经验飞行员更少更有结构)
scan_quality = 1.0 - min(1.0, scan.get('scan_randomness', 0.5))

# 指标3: 事件反应时延
if delays:
avg_delay = np.mean(delays)
delay_score = max(0, 1.0 - avg_delay / 5.0) # 5秒内得正分
else:
delay_score = 0.5 # 无事件

# 综合评分
score = 0.4 * coverage + 0.3 * scan_quality + 0.3 * delay_score

return {
'score': min(1.0, score),
'instrument_coverage': coverage,
'scan_quality': scan_quality,
'reaction_delay_score': delay_score,
'avg_reaction_delay_s': np.mean(delays) if delays else None,
'evidence': {
'dwell_time': dwell,
'scan_metrics': scan,
'reaction_delays': delays[:5], # 前5个样本
}
}

def _eval_info_processing(
self, dwell: dict, scan: dict
) -> dict:
"""评估信息处理胜任力"""
# 指标: 注视持续时间(太长=处理困难)
avg_dur = scan.get('avg_duration_ms', 300)
duration_score = 1.0 - min(1.0, max(0, (avg_dur - 200) / 800))

# 指标: 仪表分布均匀性
dwell_values = list(dwell.values())
if dwell_values:
# 基尼系数衡量分布不均匀性
uniformity = 1.0 - min(1.0, np.std(dwell_values) * 2)
else:
uniformity = 0.5

score = 0.5 * duration_score + 0.5 * uniformity

return {
'score': min(1.0, score),
'avg_fixation_ms': avg_dur,
'distribution_uniformity': uniformity,
'evidence': scan
}


# ==================== 测试场景 ====================
if __name__ == "__main__":
validator = CBTAEyeTrackingValidator()

# 模拟一个系统失效场景
sim_events = [
SimulatorEvent(t, 'failure', 'ENG', 'warning')
for t in [10.0] # 10秒时发动机失效
]
sim_events.append(SimulatorEvent(10.5, 'alert', 'ASI', 'critical'))
validator.add_sim_events(sim_events)

# 模拟经验飞行员的扫视数据
np.random.seed(42)
gaze_data = []

# 正常扫描阶段 (0-10秒)
instruments = ['ASI', 'ALT', 'HSI', 'AI', 'OUT', 'ENG']
for i in range(300): # 30秒 @ 30fps的注视点
t = i / 30.0
inst = instruments[i % len(instruments)]
gaze_data.append(GazeEvent(
timestamp=t,
gaze_x=np.random.uniform(0.2, 0.8),
gaze_y=np.random.uniform(0.1, 0.7),
instrument_id=inst,
fixation_duration=np.random.uniform(200, 400),
is_saccade=False
))
# 扫视
if i > 0:
gaze_data.append(GazeEvent(
timestamp=t + 0.01,
gaze_x=0, gaze_y=0,
instrument_id='',
fixation_duration=0,
is_saccade=True,
saccade_velocity=np.random.uniform(100, 300)
))

# 事件后阶段 (10-15秒): 飞行员应在1-2秒内关注ENG
for i in range(150):
t = 10.0 + i / 30.0
if i < 30: # 前1秒: 仍在扫视,但开始关注ENG
inst = 'ENG' if i % 3 == 0 else instruments[i % len(instruments)]
else: # 后续: 重点关注ENG和AI
inst = 'ENG' if i % 2 == 0 else 'AI'
gaze_data.append(GazeEvent(
timestamp=t, gaze_x=0, gaze_y=0,
instrument_id=inst,
fixation_duration=np.random.uniform(300, 500),
is_saccade=False
))

validator.add_gaze_data(gaze_data)

# 评估情境感知
result = validator.analyze_competency(
CompetencyType.SITUATION_AWARENESS,
time_window=(0, 15)
)

print("=== CBTA + 眼动追踪 评估结果 ===")
print(f"情境感知评分: {result['score']:.2f}")
print(f"仪表覆盖率: {result['instrument_coverage']:.2f}")
print(f"扫视质量: {result['scan_quality']:.2f}")
print(f"反应时延评分: {result['reaction_delay_score']:.2f}")
if result.get('avg_reaction_delay_s'):
print(f"平均反应时延: {result['avg_reaction_delay_s']:.2f}s")
print(f"\n仪表驻留时间分布:")
for inst, dwell in sorted(result['evidence']['dwell_time'].items()):
print(f" {inst}: {dwell*100:.1f}%")

Smart Eye硬件方案

参数 Smart Eye远程方案
安装方式 座舱内嵌入,无需佩戴
摄像头 多台远程摄像机
精度 <0.5°注视角度
采样率 60-120Hz
标定 快速3点标定
集成 PilotReady(EyeTracking)软件
映射 3D座舱模型→仪表ID

跨领域:航空→汽车的映射

航空CBTA概念 汽车DMS/IMS映射
胜任力评估 驾驶能力评分
模拟器心理保真度 DMS测试场景有效性
仪表注视分布 视线落点分布
扫视随机性 驾驶员注意力分散指标
事件反应时延 检测→响应延迟
经验差异 驾龄/疲劳/分心差异

对IMS的启示

1. DMS评估应超越”结果”到”过程”

当前DMS评估:是否检测到疲劳/分心 → 触发告警

CBTA启示:还应评估驾驶员如何检测——是主动扫视发现的,还是被动等告警才响应?

评估维度 当前DMS CBTA启发
检测触发 ✅ 有 ✅ 有
检测方式 ❌ 无 主动扫视 vs 被动响应
扫视质量 ❌ 无 结构化 vs 随机性
反应时延 ⚠️ 部分 事件→注视→响应全链路

2. 测试场景验证

CBTA的核心洞察——“模拟器是否创造了正确的注意力需求”——直接适用于DMS测试:

DMS测试场景 需要验证的注意力需求
分心检测(D-02) 驾驶员是否会主动扫视道路
疲劳检测(F-01) PERCLOS阈值是否在正确时机触发
OOP检测 乘员是否处于需要约束调整的姿态

3. 眼动数据作为”场景设计验证工具”

结论

Smart Eye + CBTA的方案展示了眼动追踪在训练评估中的双重价值:

  1. 评估飞行员:是否展示了胜任力(主动监控 vs 被动响应)
  2. 评估模拟器:场景是否创造了正确的注意力需求

对IMS的核心启示:

  • DMS评估应从”结果导向”扩展到”过程导向”
  • 眼动数据可验证测试场景有效性
  • 航空CBTA框架可直接迁移到驾驶员评估

参考来源:

  • Smart Eye: Validating CBTA with Eye Tracking (smarteye.se, 2026.08)
  • EASA ED Decision 2026/004/R: CBTA for ATC training
  • ICAO Doc 10056: CBTA Framework
  • VR Flight Simulation Study: Taylor & Francis, 2023