航空座舱眼动追踪:7大指标提升飞行员训练与安全(附 IMS 跨域启示)

发布时间: 2026-09-05
分类: 航空座舱 / 眼动追踪 / 跨域分析
标签: pilot eye tracking, cockpit, SOMAREALITY, Smart Eye, aviation safety, gaze metrics


1. 行业背景

1.1 航空安全痛点

统计 数据
航空事故中涉及驾驶舱监控不足 84%(NTSB 数据)
近50%航空事故与飞行员扫视不当相关 FAA 报告
飞行员疲劳相关事故占比 ~20%
高 workload 导致注意力隧道效应 主要事故诱因之一

1.2 眼动追踪在航空领域的应用现状

应用 厂商 状态
飞行员训练评估 SOMAREALITY, Smart Eye 2026年商用
疲劳与分心检测 Smart Eye 已部署
座舱界面优化 Hinfact + Smart Eye 研究中
自适应界面 多家研究机构 2026年原型
事故调查分析 NTSB + 眼动数据 已应用

2. 7大眼动指标详解

2.1 指标体系

2.2 各指标详细分析

指标1:仪表注视时长(Time Spent on Instruments)

飞行经验 注视模式 特征
40-70小时(新手) 长时间注视 信息处理慢
1,500-2,150小时 短时频繁扫视 高效信息提取
3,000-10,300小时 均衡分配 最高态势感知
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
import numpy as np
from typing import List, Tuple

class InstrumentGazeAnalyzer:
"""
仪表注视时长分析器

评估飞行员对各仪表的注视分配效率
"""

def __init__(self, fps: int = 60):
self.fps = fps

# 标准仪表定义
self.instruments = {
'ASI': '空速表',
'ATTI': '姿态指示器',
'ALT': '高度表',
'VSI': '升降速度表',
'HSI': '航向指示器',
'ECAM': '发动机/告警显示',
'PFD': '主飞行显示',
'ND': '导航显示'
}

def analyze_gaze_distribution(
self,
gaze_data: List[Tuple[float, str, float]],
total_time_sec: float
) -> dict:
"""
分析仪表注视分布

Args:
gaze_data: [(timestamp, instrument_id, duration_sec), ...]
total_time_sec: 总飞行时间(秒)

Returns:
analysis: 注视分析结果
"""
# 统计各仪表注视时间
instrument_times = {inst: 0.0 for inst in self.instruments}

for ts, inst, dur in gaze_data:
if inst in instrument_times:
instrument_times[inst] += dur

total_gaze = sum(instrument_times.values())

# 计算注视比例
ratios = {k: v/total_gaze for k, v in instrument_times.items() if total_gaze > 0}

# 评估经验水平
avg_fixation = np.mean([dur for _, _, dur in gaze_data])

if avg_fixation > 0.5: # >500ms
level = 'NOVICE'
elif avg_fixation < 0.2: # <200ms
level = 'EXPERT'
else:
level = 'INTERMEDIATE'

# 关键仪表覆盖
critical = ['ATTI', 'ASI', 'ALT', 'HSI']
critical_coverage = sum(ratios.get(c, 0) for c in critical)

return {
'level': level,
'avg_fixation_sec': avg_fixation,
'instrument_ratios': ratios,
'critical_coverage': critical_coverage,
'total_gaze_sec': total_gaze,
'outside_gaze_ratio': 1.0 - total_gaze / total_time_sec if total_time_sec > 0 else 0,
'overload_flag': any(r > 0.35 for r in ratios.values())
}

指标2-7:完整指标代码

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
class CockpitEyeTrackingSuite:
"""
航空座舱眼动追踪完整指标套件

7大指标计算与综合评估
"""

def __init__(self, fps: int = 60):
self.fps = fps
self.gaze_analyzer = InstrumentGazeAnalyzer(fps)

def compute_all_metrics(
self,
gaze_x: np.ndarray,
gaze_y: np.ndarray,
timestamps: np.ndarray,
instrument_labels: np.ndarray,
pupil_diameter: np.ndarray,
blink_events: np.ndarray
) -> dict:
"""
计算全部7个指标

Args:
gaze_x, gaze_y: 注视点坐标序列
timestamps: 时间戳序列
instrument_labels: 各帧注视的仪表标签
pupil_diameter: 瞳孔直径序列
blink_events: 眨眼事件序列

Returns:
metrics: 7大指标结果
"""
# 指标1: 仪表注视时长
m1 = self._metric_instrument_time(gaze_x, gaze_y, instrument_labels, timestamps)

# 指标2: 眼动距离
m2 = self._metric_eye_movement_distance(gaze_x, gaze_y)

# 指标3: 视觉搜索模式
m3 = self._metric_visual_search_pattern(gaze_x, gaze_y, instrument_labels)

# 指标4: 瞳孔直径变化
m4 = self._metric_pupil_changes(pupil_diameter, timestamps)

# 指标5: 眨眼模式
m5 = self._metric_blink_patterns(blink_events, timestamps)

# 指标6: 注意力分配
m6 = self._metric_attention_distribution(instrument_labels)

# 指标7: 扫视频率
m7 = self._metric_scan_speed(gaze_x, gaze_y, timestamps)

return {
'instrument_time': m1,
'eye_movement_distance': m2,
'visual_search_pattern': m3,
'pupil_changes': m4,
'blink_patterns': m5,
'attention_distribution': m6,
'scan_speed': m7
}

def _metric_eye_movement_distance(self, x: np.ndarray, y: np.ndarray) -> dict:
"""指标2: 眼动距离"""
distances = np.sqrt(np.diff(x)**2 + np.diff(y)**2)
total_distance = np.sum(distances)
avg_distance = np.mean(distances)

# 短距离 = 高效(熟悉座舱)
efficiency = 1.0 - min(avg_distance / 0.3, 1.0)

return {
'total_distance': total_distance,
'avg_distance_per_saccade': avg_distance,
'efficiency_score': efficiency,
'interpretation': '高效' if efficiency > 0.7 else '需改进'
}

def _metric_visual_search_pattern(self, x, y, labels) -> dict:
"""指标3: 视觉搜索模式"""
# 四种标准模式
patterns = {
'speed': 0, # 速度-姿态交替
'vertical': 0, # 高度-姿态交替
'lateral': 0, # 航向-姿态交替
'general': 0 # 通用扫描
}

transitions = {}
for i in range(1, len(labels)):
pair = (labels[i-1], labels[i])
transitions[pair] = transitions.get(pair, 0) + 1

# ATTI-ASI 交替 = 速度模式
patterns['speed'] = transitions.get(('ATTI', 'ASI'), 0) + transitions.get(('ASI', 'ATTI'), 0)
patterns['vertical'] = transitions.get(('ATTI', 'ALT'), 0) + transitions.get(('ALT', 'ATTI'), 0)
patterns['lateral'] = transitions.get(('ATTI', 'HSI'), 0) + transitions.get(('HSI', 'ATTI'), 0)

dominant = max(patterns, key=patterns.get)

return {
'patterns': patterns,
'dominant_pattern': dominant,
'structure_score': max(patterns.values()) / max(sum(patterns.values()), 1),
'is_structured': max(patterns.values()) > 10
}

def _metric_pupil_changes(self, pupil: np.ndarray, ts: np.ndarray) -> dict:
"""指标4: 瞳孔直径变化"""
# 去除光照影响(线性回归)
from scipy.signal import detrend
pupil_detrended = detrend(pupil)

# 认知负荷指数
load = np.std(pupil_detrended) / (np.mean(pupil) + 1e-8)

return {
'mean_diameter': np.mean(pupil),
'std_diameter': np.std(pupil),
'cognitive_load_index': load,
'high_load_flag': load > 0.15,
'max_dilation': np.max(pupil) - np.min(pupil)
}

def _metric_blink_patterns(self, blinks: np.ndarray, ts: np.ndarray) -> dict:
"""指标5: 眨眼模式"""
total_sec = (ts[-1] - ts[0]) if len(ts) > 1 else 1
blink_rate = np.sum(blinks) / (total_sec / 60) # 次/分

return {
'blink_rate_per_min': blink_rate,
'total_blinks': int(np.sum(blinks)),
'fatigue_flag': blink_rate > 25,
'strain_flag': blink_rate < 10
}

def _metric_attention_distribution(self, labels: np.ndarray) -> dict:
"""指标6: 注意力分配"""
unique, counts = np.unique(labels, return_counts=True)
dist = dict(zip(unique, counts / len(labels)))

# 香农熵
entropy = -sum(p * np.log2(p + 1e-10) for p in dist.values())
max_entropy = np.log2(len(unique)) if len(unique) > 0 else 1

return {
'distribution': dist,
'entropy': entropy,
'normalized_entropy': entropy / max_entropy if max_entropy > 0 else 0,
'tunnel_vision_flag': entropy / max_entropy < 0.5 if max_entropy > 0 else False
}

def _metric_scan_speed(self, x: np.ndarray, y: np.ndarray, ts: np.ndarray) -> dict:
"""指标7: 扫视频率"""
velocities = np.sqrt(np.diff(x)**2 + np.diff(y)**2) / np.diff(ts + 1e-10)
return {
'avg_scan_speed': np.mean(velocities),
'max_scan_speed': np.max(velocities),
'consistency': 1.0 - np.std(velocities) / (np.mean(velocities) + 1e-8)
}


# ============ 测试 ============

if __name__ == "__main__":
np.random.seed(42)
suite = CockpitEyeTrackingSuite(fps=60)

# 模拟数据(60秒飞行)
n = 3600 # 60秒 @ 60fps
ts = np.linspace(0, 60, n)

# 模拟注视点(在标准仪表位置间跳转)
instrument_positions = {
'ASI': (0.3, 0.4), 'ATTI': (0.5, 0.4),
'ALT': (0.7, 0.4), 'HSI': (0.5, 0.2),
'ECAM': (0.5, 0.7), 'PFD': (0.3, 0.5)
}

labels = np.random.choice(list(instrument_positions.keys()), n)
gaze_x = np.array([instrument_positions[l][0] + np.random.normal(0, 0.02) for l in labels])
gaze_y = np.array([instrument_positions[l][1] + np.random.normal(0, 0.02) for l in labels])
pupil = 4.0 + 0.5 * np.sin(2 * np.pi * 0.1 * ts) + np.random.normal(0, 0.1, n)
blinks = (np.random.rand(n) < 0.003).astype(float)

metrics = suite.compute_all_metrics(gaze_x, gaze_y, ts, labels, pupil, blinks)

print("=== 航空座舱眼动追踪 7大指标 ===\n")
for name, result in metrics.items():
print(f"指标: {name}")
for k, v in result.items():
if isinstance(v, (int, float, bool, str)):
print(f" {k}: {v}")
print()

3. 四种标准扫视模式

模式 仪表交替 场景
速度模式 空速 ↔ 姿态指示器 维持空速
垂直偏差模式 高度/升降率 ↔ 姿态指示器 控制高度
横向偏差模式 航向 ↔ 姿态指示器 保持航向
通用姿态模式 空速→姿态→ECAM→航向→高度 系统扫视

专家 vs 新手扫视差异

维度 专家(3000h+) 新手(40-70h)
AOI注视时长 167ms 274ms
扫视模式 结构化、多样 固定、单调
仪表覆盖 全面 集中少数
外部注视 高(态势感知) 低(过度依赖仪表)
眼动距离 短(高效) 长(搜索式)

4. 典型事故案例分析

4.1 Turkish Airlines 1951(2009)

要素 详情
原因 机组忽视速度和俯仰角异常
眼动问题 扫视不充分,未覆盖关键仪表
教训 结构化扫视模式的重要性

4.2 ATR 72-600 基隆河坠机(2015)

要素 详情
推动 促成座舱眼动追踪系统开发
应用 事故调查中眼动模式分析
遗产 今日眼动训练系统的技术基础

5. 航空 → 汽车跨域启示

5.1 可直接迁移的指标

航空指标 汽车DMS对应 迁移难度
仪表注视时长 仪表台/中控注视 ⭐
眼动距离 扫视范围 ⭐
视觉搜索模式 道路扫描模式 ⭐⭐
瞳孔直径变化 认知负荷评估 ⭐
眨眼模式 疲劳检测(PERCLOS) ⭐
注意力分配 道路/仪表/手机分配 ⭐⭐
扫视频率 多任务能力 ⭐⭐

5.2 航空特有的挑战

挑战 航空 汽车差异
仪表数量多 8+ 仪表 3-5 区域
高速运动 500+ km/h 60-120 km/h
三维空间 俯仰/滚转/偏航 主要水平
自动驾驶等级 高(A/P常用) L2-L3(渐进)
训练强度 1500h+ 驾照即可

5.3 IMS 可落地的航空方案

方案 来源 优先级 周期
结构化扫视模式评估 NTSB 84%数据 🔴 高 4周
瞳孔认知负荷监测 SMU+CAE研究 🟡 中 8周
注意力隧道效应检测 航空事故分析 🔴 高 4周
眼动距离效率评分 Hinfact方案 🟡 中 6周
飞行经验水平估计 注视时长分布 🟡 中 8周

6. 主要厂商

厂商 航空方案 汽车方案 协同
Smart Eye 驾驶舱眼动追踪 AIS DMS 航空+汽车双线
SOMAREALITY 飞行训练眼动 - 航空专精
Seeing Machines Guardian 航空版 Guardian DMS 跨域技术
Hinfact 训练系统(用Smart Eye) - 航空训练

7. 参考资源


航空座舱眼动追踪是 IMS 技术的重要参考源。 7大指标体系可直接迁移到汽车 DMS,特别是结构化扫视模式评估和注意力隧道效应检测——这两个在汽车场景同样致命但尚未充分覆盖。