铁路驾驶员多传感器疲劳监测:从模拟器到真实运营的首次全传感器验证

论文信息

项目 内容
标题 Multisensor Measurement of Train Driver Mental Fatigue: From Simulation to Reality
作者 Esther Bosch, Rebecca Kruschka, David Schackmann, Stephanie Hoyer, Wolfgang Kilian, Stefan Schwanitz, Anneke Hamann
机构 德国航空航天中心(DLR)交通系统研究所 + 开姆尼茨工业大学
发表 Applied Ergonomics (arXiv:2608.23361)
链接 https://arxiv.org/abs/2608.23361
被试 模拟器14人 + 真实运营6人
关键词 train drivers, mental fatigue, HRV, EEG, EDA, eye-tracking, ATO, GoA2

核心创新

  1. 首次在真实铁路运营中部署全传感器矩阵:此前从未在真实列车驾驶中同时部署EEG+ECG+EDA+眼动+呼吸+行为的全传感器矩阵
  2. 模拟器→真实运营对照验证:14人高保真模拟器 + 6人真实铁路运营,验证各生理指标的生态效度
  3. 核心发现:HRV和呼吸率是最鲁棒的疲劳指标:EEG theta/alpha、EDA、眨眼在真实环境中失效

问题定义

铁路自动化的悖论

GoA等级 自动化程度 驾驶员角色 疲劳风险
GoA0 无自动化 完全手动 低(活跃参与)
GoA1 辅助 主要手动+辅助监控
GoA2 半自动 监控+干预 高(监控单调)
GoA3 有条件自动 应急干预 极高(极少干预)
GoA4 全自动 无驾驶员 N/A

Bainbridge悖论:自动化程度越高,驾驶员越容易疲劳(监控任务单调→认知欠载→任务诱导疲劳),但驾驶员仍需在紧急时刻保持高效。

现有铁路疲劳检测的局限

指标 模拟器验证 真实运营验证 生态效度
PERCLOS/眨眼 ✅ 有效 ⚠️ 光照干扰
EEG (theta/alpha) ✅ 有效 ❌ 振动噪声
ECG/HRV ✅ 有效 ⚠️ 部分验证
EDA ✅ 有效 ❌ 未验证
呼吸率 ✅ 有效 ❌ 未验证 未知

方法详解

1. 实验设计

flowchart TD
    A[被试: 模拟器14人/真实运营6人] --> B[基线驾驶 10分钟]
    B --> C[1小时听觉n-back任务<br/>疲劳诱导]
    C --> D[第二次驾驶 10分钟]
    B --> E[传感器数据采集]
    D --> E
    E --> E1[EEG 32通道]
    E --> E2[ECG 12导联]
    E --> E3[EDA 皮肤电导]
    E --> E4[眼动追踪]
    E --> E5[呼吸带]
    E --> E6[行为表现]
    E --> E7[主观量表 KSS]

2. 传感器矩阵

传感器 采样率 采集位置 指标
EEG 500 Hz 32通道全帽 theta(4-8Hz), alpha(8-13Hz), beta(13-30Hz)
ECG 1000 Hz 胸部3导联 HRV, LF/HF, RMSSD
EDA 100 Hz 非惯用手手指 SCR, SCL
眼动 60 Hz 头戴式眼动仪 眨眼时长, PERCLOS,瞳孔直径
呼吸 25 Hz 胸部呼吸带 呼吸率, 潮气量
行为 - n-back任务 反应时, 准确率
主观 - KSS量表 自评疲劳等级

3. 疲劳诱导协议

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
import numpy as np

class NBackFatigueProtocol:
"""
听觉n-back疲劳诱导协议

论文方法:1小时2-back听觉任务
目的:在监控场景中诱导任务性精神疲劳
"""

def __init__(self, duration_min: int = 60,
n_back: int = 2,
isi_sec: float = 3.0):
"""
Args:
duration_min: 任务总时长(分钟)
n_back: n-back等级(1/2/3)
isi_sec: 刺激间隔(秒)
"""
self.n_trials = int(duration_min * 60 / isi_sec)
self.n_back = n_back
self.isi = isi_sec
self.stimuli = self._generate_stimuli()

def _generate_stimuli(self) -> list:
"""生成n-back刺激序列(字母0-9)"""
np.random.seed(42)
letters = list('0123456789')
sequence = []

for i in range(self.n_trials):
# 30%的trial是match(当前=n-back前)
if i >= self.n_back and np.random.random() < 0.3:
# 匹配n-back前的字母
sequence.append({
'letter': sequence[i - self.n_back]['letter'],
'is_target': True
})
else:
letter = np.random.choice(letters)
# 确保不是意外匹配
while (i >= self.n_back and
letter == sequence[i - self.n_back]['letter']):
letter = np.random.choice(letters)
sequence.append({
'letter': letter,
'is_target': False
})

return sequence

def evaluate_performance(self, responses: list) -> dict:
"""
评估被试n-back表现

Args:
responses: [{'trial': i, 'responded': bool}]

Returns:
metrics: {accuracy, rt_mean, rt_std, d_prime}
"""
hits, misses, false_alarms, correct_rejections = 0, 0, 0, 0
reaction_times = []

for i, resp in enumerate(responses):
target = self.stimuli[i]['is_target']

if target and resp['responded']:
hits += 1
if 'rt' in resp:
reaction_times.append(resp['rt'])
elif target and not resp['responded']:
misses += 1
elif not target and resp['responded']:
false_alarms += 1
else:
correct_rejections += 1

n_targets = sum(1 for s in self.stimuli if s['is_target'])
n_nontargets = len(self.stimuli) - n_targets

hit_rate = hits / (hits + misses + 1e-8)
fa_rate = false_alarms / (false_alarms + correct_rejections + 1e-8)

# d-prime (信号检测灵敏度)
from scipy.stats import norm
d_prime = norm.ppf(hit_rate) - norm.ppf(fa_rate)

return {
'accuracy': (hits + correct_rejections) / len(responses),
'hit_rate': hit_rate,
'fa_rate': fa_rate,
'd_prime': d_prime,
'rt_mean': np.mean(reaction_times) if reaction_times else 0,
'rt_std': np.std(reaction_times) if reaction_times else 0
}

4. 生理指标提取

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
class FatigueIndicatorExtractor:
"""
多模态疲劳指标提取器

论文核心方法:从6种传感器提取疲劳相关指标
"""

@staticmethod
def extract_hrv_features(ecg: np.ndarray, fs: int = 1000) -> dict:
"""
ECG→HRV特征(论文发现最鲁棒的指标)
"""
# R峰检测
from scipy.signal import find_peaks
r_peaks, _ = find_peaks(
ecg, height=np.percentile(ecg, 97),
distance=fs * 0.3 # 最小心率300ms间隔
)

rr_intervals = np.diff(r_peaks) / fs * 1000 # ms

if len(rr_intervals) < 5:
return {'rmssd': 0, 'sdnn': 0, 'lf_hf': 0, 'mean_hr': 0}

# 时域特征
rmssd = np.sqrt(np.mean(np.diff(rr_intervals) ** 2))
sdnn = np.std(rr_intervals)
mean_hr = 60000 / np.mean(rr_intervals)

# 频域特征
# 重采样为均匀序列
rr_uniform = np.interp(
np.linspace(0, len(rr_intervals) - 1, 256),
np.arange(len(rr_intervals)),
rr_intervals
)

freqs = np.fft.rfftfreq(256, d=1/4) # 4Hz重采样
power = np.abs(np.fft.rfft(rr_uniform)) ** 2

lf_mask = (freqs >= 0.04) & (freqs < 0.15)
hf_mask = (freqs >= 0.15) & (freqs <= 0.4)

lf_power = np.sum(power[lf_mask])
hf_power = np.sum(power[hf_mask])

return {
'rmssd': rmssd,
'sdnn': sdnn,
'lf_hf': lf_power / (hf_power + 1e-8),
'mean_hr': mean_hr,
'lf_power': lf_power,
'hf_power': hf_power
}

@staticmethod
def extract_eeg_features(eeg: np.ndarray, fs: int = 500) -> dict:
"""
EEG→频段功率(论文发现真实环境不可靠)
"""
from scipy.signal import welch
freqs, psd = welch(eeg, fs=fs, nperseg=fs*4)

bands = {
'theta': (4, 8),
'alpha': (8, 13),
'beta': (13, 30)
}

features = {}
for name, (f_low, f_high) in bands.items():
mask = (freqs >= f_low) & (freqs < f_high)
features[f'{name}_power'] = np.sum(psd[mask])

# Theta/Alpha比值(经典认知负荷指标)
features['theta_alpha_ratio'] = (
features['theta_power'] / (features['alpha_power'] + 1e-8)
)

return features

@staticmethod
def extract_breathing_features(resp: np.ndarray, fs: int = 25) -> dict:
"""
呼吸带→呼吸率(论文发现第二鲁棒指标)
"""
from scipy.signal import find_peaks

# 带通滤波 0.1-0.5 Hz (6-30 breaths/min)
from scipy.signal import butter, filtfilt
b, a = butter(4, [0.1, 0.5], btype='band', fs=fs)
filtered = filtfilt(b, a, resp)

# 找呼吸峰
peaks, _ = find_peaks(filtered, distance=fs * 2) # 最少2秒间隔

breathing_rate = len(peaks) / (len(resp) / fs / 60) # breaths/min

# 呼吸幅度变化
amplitudes = [filtered[p] for p in peaks]

return {
'breathing_rate': breathing_rate,
'breathing_amp_mean': np.mean(amplitudes) if amplitudes else 0,
'breathing_amp_var': np.std(amplitudes) if amplitudes else 0,
'breathing_interval_var': np.std(np.diff(peaks) / fs) if len(peaks) > 2 else 0
}

实验结果

各指标疲劳检测效果

指标 模拟器效果 真实运营效果 鲁棒性 推荐度
HRV (RMSSD) ✅ p<0.01 ✅ p<0.05 ★★★★★ ✅ 首选
呼吸率 ✅ p<0.05 ✅ p<0.05 ★★★★★ ✅ 首选
EEG theta ✅ p<0.01 ❌ ns ★★☆☆☆ ❌ 不推荐
EEG alpha ✅ p<0.05 ❌ ns ★★☆☆☆ ❌ 不推荐
EDA (SCR) ⚠️ p=0.08 ❌ ns ★☆☆☆☆ ❌ 不推荐
眨眼时长 ✅ p<0.05 ❌ ns ★★☆☆☆ ⚠️ 辅助
行为(反应时) ✅ p<0.01 - ★★★☆☆ ✅ 辅助
KSS自评 ✅ p<0.001 ✅ p<0.01 ★★★★☆ ✅ 辅助

关键发现:HRV变化模式

测量阶段 RMSSD (ms) LF/HF 含义
基线驾驶 42.3 ± 8.1 2.1 正常状态
n-back后驾驶 31.5 ± 6.8 1.4 疲劳后RMSSD↓25%
变化方向 ↓25% ↓33% 副交感神经退缩

真实运营的技术挑战

挑战 描述 影响
振动干扰 列车振动传导至EEG/ECG电极 EEG信噪比↓60%
传感器连接 长时间佩戴电极松动 ECG数据丢失15%
光照变化 隧道出入口光照突变 眼动追踪短暂失效
温度 驾驶室温度波动大 EDA基线漂移
空间限制 驾驶室空间小 传感器布置受限
被试数少 真实运营仅6人 统计效力不足

IMS开发启示

1. 跨交通领域疲劳检测对比

领域 最有效指标 次有效指标 无效指标 关键差异
汽车DMS PERCLOS 眼动+头部 EEG 驾驶员活跃参与
铁路驾驶 HRV 呼吸率 EEG/EDA 监控单调→自主神经退化
航空塔台 眼动+ECG融合 PERCLOS EEG 屏幕凝视
矿区运输 PERCLOS 行为 - 颠簸环境

核心洞察:不同交通领域的最佳疲劳指标不同!汽车DMS以眼动为主,铁路驾驶以HRV为主。

2. 汽车DMS vs 铁路DMS的差异

维度 汽车DMS 铁路DMS 对IMS的启示
自动化等级 L2-L3 GoA2 铁路先到高自动化
驾驶员角色 活跃驾驶 监控干预 监控场景疲劳更快
最佳指标 PERCLOS HRV 指标选择需匹配场景
振动水平 中等 铁路EEG更难
光照 可控(车内) 不可控(驾驶室大窗) 铁路眼动更难
法规 Euro NCAP TSI/ERA 标准不同

3. 对IMS多场景部署的指导

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
class MultiDomainFatigueConfig:
"""
多交通领域疲劳检测配置

根据场景动态选择最优指标组合
"""

configs = {
'automotive_L2': {
'primary': ['perclos', 'gaze_dispersion'],
'secondary': ['head_pose', 'rppg_hrv'],
'weight': {'perclos': 0.4, 'gaze': 0.3,
'head': 0.15, 'rppg': 0.15},
'window': '30s',
'alert_threshold': 0.6
},
'automotive_L3': {
# L3更接近铁路GoA2的监控场景
'primary': ['rppg_hrv', 'perclos'],
'secondary': ['gaze_dispersion', 'breathing_rate'],
'weight': {'rppg_hrv': 0.35, 'perclos': 0.25,
'gaze': 0.2, 'breathing': 0.2},
'window': '60s',
'alert_threshold': 0.55
},
'railway_GoA2': {
'primary': ['hrv', 'breathing_rate'],
'secondary': ['perclos', 'behavioral'],
'weight': {'hrv': 0.4, 'breathing': 0.25,
'perclos': 0.2, 'behavior': 0.15},
'window': '60s',
'alert_threshold': 0.55
},
'aviation_tower': {
'primary': ['eye_tracking', 'ecg_hrv'],
'secondary': ['behavioral', 'subjective'],
'weight': {'eye': 0.35, 'ecg': 0.35,
'behavior': 0.15, 'subjective': 0.15},
'window': '60s',
'alert_threshold': 0.6
}
}

4. 部署建议

场景 主传感器 辅传感器 放弃传感器 理由
汽车DMS 摄像头(眼动) rPPG(心率) EEG 驾驶活跃→眼动有效
铁路DMS ECG/rPPG 呼吸 EEG 监控单调→HRV更敏感
航空DMS 眼动+ECG 行为 EEG 屏幕凝视→眼动+HRV

局限性

局限 描述 缓解方向
被试数少 真实运营仅6人 扩大规模至30+
单一铁路线 未覆盖不同线路条件 多线路验证
EEG失效 振动干扰为主因 主动降噪电极
无实时检测 离线分析 实时算法移植
n-back诱导 非自然疲劳 长时间自然驾驶
季节因素 单次采集 四季重复

总结

这篇论文是铁路驾驶员疲劳检测的里程碑研究:

  1. HRV和呼吸率是最鲁棒的疲劳指标:在模拟器和真实运营中均有效,振动和光照不影响
  2. EEG在真实铁路环境中不可靠:振动噪声导致信噪比下降60%,与实验室结果不一致
  3. 监控场景与驾驶场景的最优指标不同:监控单调→自主神经退化先于行为退化
  4. 汽车DMS不能直接迁移到铁路DMS:指标选择需匹配场景特征

对IMS的启示:随着汽车自动驾驶等级提升(L3+),驾驶员角色从”活跃驾驶”转向”监控干预”,越来越接近铁路GoA2场景。因此,L3+汽车DMS应增加HRV/呼吸率权重,降低PERCLOS权重——这是从铁路研究获得的核心洞察。


https://dapalm.com/2026/09/21/2026-09-21-19-train-driver-multisensor-fatigue-hrv-breathing-ims/
作者
Mars
发布于
2026年9月21日
许可协议