方向盘嵌入sEMG传感器:非侵入式早期疲劳检测新方案——IMS开发启示

论文:Can Steering Wheel Detect Your Driving Fatigue?
方法:方向盘嵌入式表面肌电图(sEMG) + PU Learning + XGBoost
测试:实验室96% F1, 实路90% F1
关键词:sEMG, steering wheel, fatigue detection, non-intrusive, PU Learning

核心创新

本文提出将sEMG传感器直接嵌入方向盘,通过肌肉电信号在疲劳行为出现前检测疲劳状态。

一句话总结: 把生物传感器嵌入方向盘,驾驶员握方向盘时即可检测肌肉疲劳信号——无需额外佩戴设备,比视觉方法提前检测到疲劳。

问题定义

现有疲劳检测方法对比

方法 检测延迟 侵入性 准确率 局限
车辆行为(车道偏离) 高(疲劳已严重) 无 ~80% 需行驶数据积累
面部识别(眨眼/打哈欠) 中 无(摄像头) ~85% 光照敏感、眼镜遮挡
EEG脑电 低(最早) 高(戴帽) ~90% 不可量产
ECG心电 低 中(胸带) ~88% 不舒适
sEMG肌电(本文) 低(早期) 无(嵌入方向盘) 90-96% 手部位置依赖

核心优势

  1. 非侵入式:传感器嵌入方向盘FPC板,驾驶员自然握持即可采集
  2. 早期检测:肌肉疲劳信号先于行为表现(打哈欠/车道偏离)
  3. 实路验证:Mercedes GLA200 + Audi Q7,118km实路测试

方法详解

1. 硬件设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
方向盘sEMG传感器布局:

┌──────────────────────────┐
│ FPC板 (10cm × 5cm) │
│ ┌────┐ ┌────┐ ┌────┐ │
│ │sEMG│ │sEMG│ │sEMG│ │ ← 3个采集点
│ │ 1 │ │ 2 │ │ 3 │ │
│ └────┘ └────┘ └────┘ │
│ 贴合方向盘弧度 │
└──────────────────────────┘

采集:手掌和指尖肌肉电信号
采样率:1000 Hz
传输:蓝牙/WiFi → 处理器

2. 信号处理:Valid sEMG Selection Machine (VsESM)

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
import numpy as np
import torch
import torch.nn as nn
from sklearn.ensemble import RandomForestClassifier
import xgboost as xgb

class VsESM:
"""
Valid sEMG Selection Machine

论文核心方法:从噪声sEMG数据中筛选有效信号

问题:方向盘sEMG受机械振动、手部移动影响,大量噪声
方案:PU Learning + XGBoost半监督筛选
"""

def __init__(self, n_features=20):
self.n_features = n_features
self.feature_extractor = SEMGFeatureExtractor()
self.pu_learner = XGBClassifier(use_label_encoder=False)

def fit(self, raw_sEMG, valid_samples, unlabeled_samples):
"""
Args:
raw_sEMG: (N, T) 原始sEMG信号
valid_samples: 确认有效的样本索引(P集)
unlabeled_samples: 未标注样本(U集)
"""
# 提取特征
features = self.feature_extractor.transform(raw_sEMG)

# PU Learning: 将U集中不似P的标记为"可能的噪声"
P = features[valid_samples]
U = features[unlabeled_samples]

# 训练XGBoost区分P和U
X = np.vstack([P, U])
y = np.concatenate([np.ones(len(P)), np.zeros(len(U))])
self.pu_learner.fit(X, y)

# 从U中筛选出"似P"的样本
probas = self.pu_learner.predict_proba(U)[:, 1]
self.threshold = np.percentile(probas, 50) # 中位数阈值

def transform(self, raw_sEMG):
"""筛选有效sEMG段"""
features = self.feature_extractor.transform(raw_sEMG)
probas = self.pu_learner.predict_proba(features)[:, 1]
valid_mask = probas > self.threshold
return valid_mask


class SEMGFeatureExtractor:
"""sEMG特征提取:统计特征 + 二阶特征"""

def transform(self, sEMG_signal):
"""
Args:
sEMG_signal: (N, T) 多通道sEMG
Returns:
features: (N, n_features)
"""
features = []
for ch in range(sEMG_signal.shape[1]):
signal = sEMG_signal[:, ch]

# 一阶统计特征
mean = np.mean(signal)
std = np.std(signal)
rms = np.sqrt(np.mean(signal**2))
max_val = np.max(np.abs(signal))

# 二阶特征(本文创新)
# Slope: 信号变化斜率
slope = np.diff(signal).mean()
# Absolute distance: 信号幅度面积变化
sma = np.sum(np.abs(signal))
abs_dist = np.abs(sma - np.mean(sma))

# Lempel-Ziv复杂度斜率(关键疲劳指标)
lz_complexity = self._lemel_ziv_complexity(signal)

features.extend([mean, std, rms, max_val,
slope, abs_dist, lz_complexity])

return np.array(features)

def _lemel_ziv_complexity(self, signal, threshold=None):
"""计算Lempel-Ziv复杂度"""
if threshold is None:
threshold = np.mean(signal)
binary = (signal > threshold).astype(int)
binary_str = ''.join(map(str, binary))

# LZ复杂度计算
n = len(binary_str)
c = 1
i = 0
while i < n:
j = 1
while i + j <= n:
if binary_str[i:i+j] in binary_str[:i+j-1]:
j += 1
else:
break
c += 1
i += j - 1

# 归一化
import math
return c * np.log2(n) / n if n > 0 else 0


# 疲劳检测分类器
class SteeringWheelFatigueDetector:
"""
方向盘sEMG疲劳检测完整管道

论文实现: sEMG → VsESM → 特征提取 → Random Forest → 疲劳/清醒
"""

def __init__(self):
self.vsesm = VsESM()
self.extractor = SEMGFeatureExtractor()
self.classifier = RandomForestClassifier(
n_estimators=100, max_depth=10, random_state=42
)

def fit(self, raw_sEMG, labels, valid_indices, unlabeled_indices):
"""
Args:
raw_sEMG: (N, T, C) 原始信号
labels: (N,) 疲劳标签(0=清醒, 1=疲劳)
valid_indices: 已确认有效段
unlabeled_indices: 未标注段
"""
# 1. 训练VsESM
self.vsesm.fit(raw_sEMG, valid_indices, unlabeled_indices)

# 2. 筛选有效段
valid_mask = self.vsesm.transform(raw_sEMG)

# 3. 提取特征
features = self.extractor.transform(raw_sEMG[valid_mask])
valid_labels = labels[valid_mask]

# 4. 训练分类器
self.classifier.fit(features, valid_labels)

def predict(self, raw_sEMG):
"""预测疲劳状态"""
valid_mask = self.vsesm.transform(raw_sEMG)
if not np.any(valid_mask):
return 0 # 无有效信号,默认清醒
features = self.extractor.transform(raw_sEMG[valid_mask])
return self.classifier.predict(features)

def predict_proba(self, raw_sEMG):
"""疲劳概率"""
valid_mask = self.vsesm.transform(raw_sEMG)
if not np.any(valid_mask):
return 0.0
features = self.extractor.transform(raw_sEMG[valid_mask])
return self.classifier.predict_proba(features)[:, 1].mean()


# 测试
if __name__ == "__main__":
detector = SteeringWheelFatigueDetector()

# 模拟数据
np.random.seed(42)
# 清醒状态:高频小幅振动
alert_sEMG = np.random.normal(0.5, 0.15, (1000, 100, 3))
# 疲劳状态:低频大幅振动(肌肉代偿)
fatigued_sEMG = np.random.normal(0.3, 0.25, (1000, 100, 3))

X = np.vstack([alert_sEMG, fatigued_sEMG])
y = np.concatenate([np.zeros(1000), np.ones(1000)])

# 训练
valid_idx = np.random.choice(2000, 500, replace=False)
unlabeled_idx = np.setdiff1d(np.arange(2000), valid_idx)
detector.fit(X, y, valid_idx, unlabeled_idx)

# 测试
test_sample = np.random.normal(0.35, 0.20, (10, 100, 3)) # 偏疲劳
pred = detector.predict(test_sample)
proba = detector.predict_proba(test_sample)

print(f"预测: {'疲劳' if pred[0] == 1 else '清醒'}")
print(f"疲劳概率: {proba:.2f}")
print("✅ 方向盘sEMG疲劳检测管道验证完成")

3. 关键特征分析

特征 含义 疲劳时的变化
Slope of Lempel-Ziv Complexity 信号复杂度变化率 显著下降
Absolute distance of SMA 幅度面积变化 显著增加
RMS 信号均方根 下降
Std 标准差 增加(代偿性抖动)

核心发现: “LZ复杂度斜率”和”SMA绝对距离”在行为变化(打哈欠/车道偏离)前就显著改变——实现早期检测。

实验结果

实验环境

环境 车辆 人数 时长 场景
E1实验室 高保真模拟器 13人 90分钟/人 控制环境
E2实路 Mercedes GLA200, Audi Q7 4人 118km 晨/夜

性能对比

环境 分类器 F1 Score 准确率 说明
E1实验室 Random Forest 96% 95% 控制环境
E2实路 Random Forest 90%+ 88% 真实驾驶
基线 车道偏离 ~75% 70% 延迟检测
基线 眨眼检测 ~82% 80% 光照敏感

IMS 应用启示

1. 与现有DMS的融合架构

1
2
3
4
5
方向盘sEMG(早期生理信号)─┐
│
摄像头(面部/眼动)─────────┤──→ 多模态融合 ──→ 疲劳等级
│
车辆CAN(驾驶行为)─────────┘

2. 部署可行性评估

维度 评估 说明
硬件成本 ⚠️ 中等 sEMG FPC板 ~$20-50/车
安装复杂度 ⚠️ 中等 方向盘重新设计
用户接受度 ✅ 高 完全无感知
检测提前量 ✅ 优势 比视觉早30-60秒
手位置依赖 ❌ 挑战 需要持续握持
量产可行性 ⚠️ 中期 2-3年可量产

3. Euro NCAP关联

Euro NCAP 2026 DSM要求:

  • sEMG可作为补充信号增强疲劳检测可靠性
  • 多模态融合方案在Dossier中需声明各模态贡献
  • sEMG的早期检测可降低PERCLOS方法的延迟

4. 开发建议

优先级 行动 时间线
🟡 中 评估方向盘sEMG传感器供应商 1-2月
🟡 中 原型FPC板设计+信号采集 2-4月
🟢 低 模拟器+实路数据采集 4-8月
🟢 低 sEMG+视觉融合算法 8-12月

论文与代码

  • 论文来源: Lacuna收录
  • 关键词: sEMG, steering wheel, PU Learning, Random Forest, fatigue detection

本文为论文解读类博客,包含方法详解、代码复现、性能对比、IMS落地指导。