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
| """ L2自动驾驶瞳孔认知负荷监测系统 基于: Pupillary dynamics during hands-off L2 driving (2026)
核心: 持续监测基线瞳孔直径,而非等待任务事件 依赖:opencv, numpy """
import numpy as np from typing import Tuple, List from dataclasses import dataclass
@dataclass class PupilMetrics: """瞳孔指标数据类""" baseline_diameter: float current_diameter: float tepr: float cognitive_load_index: float arousal_level: float load_trend: str
class PupillaryCognitiveMonitor: """ 瞳孔认知负荷监测器 论文核心结论: - 基线瞳孔直径均值是认知负荷的可靠指标 - 基线越高 → TEPR越小 (负荷饱和效应) - 持续监测比事件触发更实用 部署: DMS摄像头 + IR补光 → 瞳孔检测 → 认知负荷评估 """ def __init__(self, baseline_window: int = 30, sampling_rate: int = 60, plr_correction: bool = True, load_threshold: float = 0.7): self.baseline_window = baseline_window self.sampling_rate = sampling_rate self.plr_correction = plr_correction self.load_threshold = load_threshold self.pupil_history: List[float] = [] self.luminance_history: List[float] = [] self.max_history = baseline_window * sampling_rate self.resting_baseline = 4.0 def update(self, pupil_diameter: float, luminance: float) -> PupilMetrics: """ 更新瞳孔数据并计算指标 Args: pupil_diameter: 当前瞳孔直径 (mm) luminance: 当前环境照度 (lux) Returns: PupilMetrics: 瞳孔指标 """ if self.plr_correction: corrected_diameter = self._correct_plr(pupil_diameter, luminance) else: corrected_diameter = pupil_diameter self.pupil_history.append(corrected_diameter) self.luminance_history.append(luminance) if len(self.pupil_history) > self.max_history: self.pupil_history.pop(0) self.luminance_history.pop(0) baseline = np.mean(self.pupil_history) if len(self.pupil_history) >= 10 else corrected_diameter tepr = corrected_diameter - baseline cognitive_index = self._compute_load_index(baseline) arousal = self._compute_arousal(baseline) trend = self._compute_trend() return PupilMetrics( baseline_diameter=baseline, current_diameter=corrected_diameter, tepr=tepr, cognitive_load_index=cognitive_index, arousal_level=arousal, load_trend=trend ) def _correct_plr(self, diameter: float, luminance: float) -> float: """ 瞳孔光反射(PLR)校正 瞳孔大小同时受认知负荷和光照影响 需要去除光照引起的瞳孔变化 PLR模型: D = D_cognitive * f(luminance) f(l) = 4.0 + 1.5 * exp(-l / 100) # 简化模型 """ plr_predicted = 4.0 + 1.5 * np.exp(-luminance / 100) plr_ratio = diameter / (plr_predicted + 1e-6) corrected = self.resting_baseline * plr_ratio return np.clip(corrected, 2.0, 8.0) def _compute_load_index(self, baseline: float) -> float: """ 计算认知负荷指数 (0-1) 基线瞳孔直径越大 → 负荷越高 归一化到静息基线 """ load = (baseline - self.resting_baseline) / (self.resting_baseline * 0.5) return np.clip(load, 0.0, 1.0) def _compute_arousal(self, baseline: float) -> float: """ 计算唤醒水平 (LC-NE系统代理) 蓝斑核-去甲肾上腺素系统活动 与瞳孔基线直接相关 """ arousal = 1.0 / (1.0 + np.exp(-(baseline - self.resting_baseline) / 0.3)) return float(arousal) def _compute_trend(self) -> str: """计算负荷趋势""" if len(self.pupil_history) < 30: return "stable" recent = np.mean(self.pupil_history[-30:]) older = np.mean(self.pupil_history[-60:-30]) if len(self.pupil_history) >= 60 else recent diff = recent - older if diff > 0.1: return "rising" elif diff < -0.1: return "falling" return "stable" def should_alert(self, metrics: PupilMetrics) -> Tuple[bool, str]: """是否需要发出认知负荷警告""" if metrics.cognitive_load_index > self.load_threshold: if metrics.load_trend == "rising": return True, "认知负荷持续上升,建议接管" elif metrics.cognitive_load_index > 0.85: return True, "认知负荷过高,存在分心风险" return False, ""
if __name__ == "__main__": monitor = PupiliaryCognitiveMonitor( baseline_window=30, sampling_rate=60, plr_correction=True ) np.random.seed(42) print("=== 场景1: 正常L2驾驶 ===") for t in range(60): pupil = 4.2 + np.random.randn() * 0.1 lux = 500 + np.random.randn() * 20 m = monitor.update(pupil, lux) print(f"基线瞳孔: {m.baseline_diameter:.2f}mm") print(f"认知负荷: {m.cognitive_load_index:.2f}") print(f"唤醒水平: {m.arousal_level:.2f}") alert, msg = monitor.should_alert(m) print(f"警告: {alert} {msg}") print("\n=== 场景2: 高认知负荷 ===") for t in range(120): pupil = 5.0 + np.random.randn() * 0.15 lux = 480 + np.random.randn() * 20 m = monitor.update(pupil, lux) print(f"基线瞳孔: {m.baseline_diameter:.2f}mm") print(f"认知负荷: {m.cognitive_load_index:.2f}") print(f"唤醒水平: {m.arousal_level:.2f}") print(f"趋势: {m.load_trend}") alert, msg = monitor.should_alert(m) print(f"警告: {alert} {msg}") print("\n=== 场景3: 极端认知负荷 ===") for t in range(120): pupil = 5.8 + np.random.randn() * 0.2 lux = 460 + np.random.randn() * 20 m = monitor.update(pupil, lux) print(f"基线瞳孔: {m.baseline_diameter:.2f}mm") print(f"认知负荷: {m.cognitive_load_index:.2f}") alert, msg = monitor.should_alert(m) print(f"警告: {alert} {msg}")
|