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
| """ Cineon ELE: Empathic Learning Engine 飞行员性能评估平台
核心功能: 1. 眼动数据 → 仪表扫描模式分析 2. 面部数据 → 认知状态推断 3. 飞行操作数据 → competency评估 4. 综合报告 → 训练改进建议
关键指标: - 扫描效率: 仪表间切换频率/覆盖率 - 注视停留: 关键仪表注视时长 - 认知负荷: 瞳孔直径变化 - 疲劳指标: 眨眼频率/闭眼时长 - 情境感知: 视线分配比例 """
from dataclasses import dataclass from typing import List, Tuple, Optional import numpy as np
@dataclass class GazeData: """单帧眼动数据""" timestamp: float gaze_x: float gaze_y: float confidence: float pupil_diameter: float blink: bool
@dataclass class InstrumentRegion: """座舱仪表区域定义""" name: str bbox: Tuple[float, float, float, float] priority: int
class ScanPatternAnalyzer: """ 仪表扫描模式分析器 基于眼动数据评估飞行员的仪表扫描效率 关键指标: - 扫描路径: 仪表间切换序列 - 停留时间: 各仪表注视时长 - 覆盖率: 查看的仪表比例 - 切换频率: 仪表切换速率 - 主要仪表优先级: 是否优先查看关键仪表 """ STANDARD_INSTRUMENTS = [ InstrumentRegion("ADI", (0.4, 0.3, 0.55, 0.5), 1), InstrumentRegion("HSI", (0.55, 0.3, 0.7, 0.5), 1), InstrumentRegion("ALT", (0.7, 0.3, 0.85, 0.5), 1), InstrumentRegion("ASI", (0.25, 0.3, 0.4, 0.5), 1), InstrumentRegion("VSI", (0.85, 0.3, 1.0, 0.5), 2), InstrumentRegion("ENGINE", (0.3, 0.55, 0.7, 0.7), 2), InstrumentRegion("NAV", (0.1, 0.1, 0.3, 0.25), 3), InstrumentRegion("COM", (0.7, 0.55, 0.9, 0.7), 2), ] def __init__(self): self.current_instrument = None self.dwell_times = {inst.name: 0.0 for inst in self.STANDARD_INSTRUMENTS} self.scan_path = [] self.blink_count = 0 self.pupil_sizes = [] def process_gaze(self, gaze: GazeData, dt: float = 1/60): """处理单帧眼动数据""" instrument = self._find_instrument(gaze.gaze_x, gaze.gaze_y) if instrument: self.dwell_times[instrument.name] += dt if instrument != self.current_instrument: self.scan_path.append(instrument.name) self.current_instrument = instrument if gaze.blink: self.blink_count += 1 if gaze.confidence > 0.5: self.pupil_sizes.append(gaze.pupil_diameter) def _find_instrument(self, x: float, y: float) -> Optional[InstrumentRegion]: """根据注视点查找仪表""" for inst in self.STANDARD_INSTRUMENTS: x1, y1, x2, y2 = inst.bbox if x1 <= x <= x2 and y1 <= y <= y2: return inst return None def generate_report(self, duration_sec: float) -> dict: """生成性能评估报告""" total_gaze = sum(self.dwell_times.values()) viewed = sum(1 for v in self.dwell_times.values() if v > 0.5) coverage = viewed / len(self.STANDARD_INSTRUMENTS) primary_time = sum( self.dwell_times[inst.name] for inst in self.STANDARD_INSTRUMENTS if inst.priority == 1 ) primary_ratio = primary_time / (total_gaze + 1e-8) scan_rate = len(self.scan_path) / (duration_sec / 60) blink_rate = self.blink_count / (duration_sec / 60) if self.pupil_sizes: pupil_std = np.std(self.pupil_sizes) cognitive_load = min(pupil_std / 0.5, 1.0) else: cognitive_load = 0.0 return { 'dwell_times': self.dwell_times, 'coverage': coverage, 'primary_ratio': primary_ratio, 'scan_rate_per_min': scan_rate, 'blink_rate_per_min': blink_rate, 'cognitive_load': cognitive_load, 'scan_path': self.scan_path, 'fatigue_risk': blink_rate > 20 or primary_ratio < 0.5, 'competency_score': self._compute_competency( coverage, primary_ratio, scan_rate, blink_rate ) } def _compute_competency(self, coverage, primary_ratio, scan_rate, blink_rate) -> float: """综合competency评分 (0-100)""" score = 0 score += coverage * 25 score += primary_ratio * 25 score += min(scan_rate / 20, 1) * 25 score += (1 - min(blink_rate / 30, 1)) * 25 return round(score, 1)
if __name__ == "__main__": analyzer = ScanPatternAnalyzer() np.random.seed(42) for i in range(3600): t = i / 60 cycle = (t * 0.5) % 4 if cycle < 1: x, y = 0.47, 0.4 elif cycle < 2: x, y = 0.62, 0.4 elif cycle < 3: x, y = 0.77, 0.4 else: x, y = 0.32, 0.4 x += np.random.normal(0, 0.02) y += np.random.normal(0, 0.02) gaze = GazeData( timestamp=t, gaze_x=x, gaze_y=y, confidence=0.9, pupil_diameter=4.0 + 0.1 * np.sin(t * 2), blink=(np.random.random() < 0.005) ) analyzer.process_gaze(gaze, dt=1/60) report = analyzer.generate_report(60) print("=== Cineon ELE 飞行员性能报告 ===") print(f"仪表停留时间:") for name, time in report['dwell_times'].items(): print(f" {name}: {time:.1f}s") print(f"\n覆盖率: {report['coverage']:.1%}") print(f"主要仪表占比: {report['primary_ratio']:.1%}") print(f"扫描频率: {report['scan_rate_per_min']:.1f} 次/分") print(f"眨眼率: {report['blink_rate_per_min']:.1f} 次/分") print(f"认知负荷: {report['cognitive_load']:.2f}") print(f"疲劳风险: {'是' if report['fatigue_risk'] else '否'}") print(f"Competency评分: {report['competency_score']}/100") print(f"\n扫描路径 (前20个): {report['scan_path'][:20]}")
|