酒驾损伤检测新突破:骑行中实时检测与误报率可控的统计框架

论文信息

项目 内容
标题 In-Ride Alcohol-Impairment Detection in E-Scooterists with False-Alarm Control
作者 Marco Capuccini, Rahul Rajendra Pai
单位 Voi Technology AB; KTH Royal Institute of Technology; Chalmers University of Technology
arXiv 2610.11783
发表时间 2026年10月
数据集 141次骑行,25名参与者,3个BAC等级
关键词 酒驾检测、微出行安全、任意时刻有效推断、共形预测

核心创新

首次实现骑行中实时酒驾检测,核心突破:

  1. 任意时刻误报率控制:基于归纳共形预测(ICP),在骑行的每一步都保证误报率不超过设定上限
  2. 传感器信号检测:仅用惯性传感器(加速度计/陀螺仪)+油门位置,无需摄像头
  3. 嵌入式实时部署:STM开发板上262ms完成一次检测周期
  4. 检测速度:高BAC下25秒检测到,低BAC下27秒

与传统酒驾检测对比

方法 检测时机 误报控制 侵入性 实时性
呼气式酒精锁 驾驶前 N/A 高 仅起点
App反应力测试 驾驶前 无 中 仅起点
夜间禁行令 时段限制 N/A 高(所有人) 全程但粗暴
本文方案 骑行中持续 有统计保证 低 25-27秒

方法详解

检测流程

核心代码实现

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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import numpy as np
from scipy.signal import firwin, lfilter
from scipy.stats import entropy
from typing import List, Tuple

class PermutationEntropyExtractor:
"""
排列熵(PE)特征提取器

论文核心特征:对7通道传感器信号
计算每个通道的排列熵,形成7维特征向量

PE反映信号的复杂性/随机性
酒精损伤→运动控制下降→信号复杂性降低→PE变化
"""

def __init__(self, embedding_dim: int = 5, delay: int = 1):
self.m = embedding_dim
self.tau = delay

def compute_pe(self, signal: np.ndarray) -> float:
"""
计算单通道信号的排列熵

Args:
signal: (N,) 一维信号

Returns:
pe: 归一化排列熵 (0-1)
"""
n = len(signal)
if n < self.m * self.tau:
return 0.0

# 构造嵌入向量
num_patterns = n - (self.m - 1) * self.tau
patterns = np.zeros((num_patterns, self.m))

for i in range(self.m):
patterns[:, i] = signal[i*self.tau : i*self.tau + num_patterns]

# 对每行排序,获取排列模式
order = np.argsort(patterns, axis=1)

# 将排列模式转换为元组作为key
patterns_tuple = [tuple(row) for row in order]

# 统计各模式频率
from collections import Counter
counter = Counter(patterns_tuple)
total = len(patterns_tuple)

# 计算概率分布
probs = np.array(list(counter.values())) / total

# 香农熵
pe = -np.sum(probs * np.log2(probs))

# 归一化
max_entropy = np.log2(np.math.factorial(self.m))
pe_normalized = pe / max_entropy

return float(pe_normalized)

def extract_features(self, window: np.ndarray) -> np.ndarray:
"""
从多通道窗口提取PE特征

Args:
window: (d, w) d通道, w样本

Returns:
features: (d,) 每通道的PE值
"""
d = window.shape[0]
features = np.zeros(d)
for ch in range(d):
features[ch] = self.compute_pe(window[ch])
return features


class AlcoholImpairmentDetector:
"""
酒精损伤检测器

基于排列熵特征 + ICP误报控制

检测流程:
1. 滑动窗口提取PE特征
2. 中心化去除个体基线
3. ML模型输出损伤评分
4. ICP阈值累积判定
"""

def __init__(self, num_channels: int = 7, window_size: int = 1000,
step_size: int = 100, fs: float = 100.0,
far_bound: float = 0.023):
self.num_channels = num_channels
self.window_size = window_size
self.step_size = step_size
self.fs = fs
self.far_bound = far_bound # 误报率上限

# 低通滤波器(10Hz截止)
self.filter_coeffs = firwin(64, 10.0 / (fs / 2),
window='hamming')

# PE提取器
self.pe_extractor = PermutationEntropyExtractor(
embedding_dim=5, delay=1
)

# 基线均值(从清醒骑行校准)
self.baseline_mean = None
self.baseline_scores = []

# ICP相关
self.threshold = None
self.evidence_accumulator = 0.0

def calibrate(self, sober_rides: List[np.ndarray]):
"""
从清醒骑行校准检测器

Args:
sober_rides: 清醒骑行的传感器信号列表
"""
all_features = []
all_scores = []

for ride in sober_rides:
windows = self._generate_windows(ride)
for window in windows:
# 滤波
filtered = self._filter_window(window)
# PE特征
features = self.pe_extractor.extract_features(filtered)
# 中心化
if self.baseline_mean is not None:
features = features - self.baseline_mean
all_features.append(features)
# 简化:使用特征距离作为评分
score = np.linalg.norm(features)
all_scores.append(score)

all_scores = np.array(all_scores)

# 更新基线均值
all_features = np.array(all_features)
self.baseline_mean = np.mean(all_features, axis=0)

# ICP阈值校准
# 取(1-far_bound)分位数作为阈值
sorted_scores = np.sort(all_scores)
idx = int(np.ceil((1 - self.far_bound) * len(sorted_scores)))
idx = min(idx, len(sorted_scores) - 1)
self.threshold = sorted_scores[idx]

return self.threshold

def detect(self, ride_signal: np.ndarray) -> dict:
"""
对骑行信号进行实时检测

Args:
ride_signal: (d, T) 传感器信号
d=7 (3加速度+3角速度+1油门)
Returns:
result: dict with detection, alarm_time, scores
"""
if self.threshold is None:
raise ValueError("需先校准")

windows = self._generate_windows(ride_signal)
scores = []

for i, window in enumerate(windows):
# 滤波
filtered = self._filter_window(window)
# PE特征
features = self.pe_extractor.extract_features(filtered)
# 中心化
features = features - self.baseline_mean
# 评分
score = np.linalg.norm(features)
scores.append(score)

# 证据累积
self.evidence_accumulator = max(
self.evidence_accumulator * 0.95 + score * 0.05,
score
)

# 阈值判定
if self.evidence_accumulator > self.threshold:
return {
'detected': True,
'alarm_window': i,
'alarm_time_sec': i * self.step_size / self.fs,
'score': float(self.evidence_accumulator),
'threshold': float(self.threshold),
'all_scores': scores
}

return {
'detected': False,
'alarm_window': -1,
'alarm_time_sec': -1,
'score': float(self.evidence_accumulator),
'threshold': float(self.threshold),
'all_scores': scores
}

def _filter_window(self, window: np.ndarray) -> np.ndarray:
"""低通滤波"""
filtered = np.zeros_like(window)
for ch in range(window.shape[0]):
filtered[ch] = lfilter(
self.filter_coeffs, 1.0, window[ch]
)
return filtered

def _generate_windows(self, signal: np.ndarray) -> List[np.ndarray]:
"""生成滑动窗口"""
T = signal.shape[1]
windows = []
for start in range(0, T - self.window_size + 1, self.step_size):
windows.append(signal[:, start:start + self.window_size])
return windows


# ==================== 测试代码 ====================
if __name__ == "__main__":
np.random.seed(42)

detector = AlcoholImpairmentDetector(
num_channels=7,
window_size=1000,
step_size=100,
fs=100.0,
far_bound=0.023
)

# 模拟清醒骑行数据(5段,每段60秒@100Hz)
sober_rides = []
for _ in range(5):
ride = np.random.randn(7, 6000) * 0.5
# 添加正常骑行模式
t = np.arange(6000) / 100.0
ride[0] += 0.3 * np.sin(2 * np.pi * 1.0 * t) # 正常摇摆
ride[5] += 0.2 * np.sin(2 * np.pi * 0.5 * t) # 正常转向
sober_rides.append(ride)

# 校准
threshold = detector.calibrate(sober_rides)
print(f"校准阈值: {threshold:.4f}")
print(f"误报率上限: {detector.far_bound}")

# 模拟受损骑行
impaired_ride = np.random.randn(7, 6000) * 0.8 # 更大噪声
t = np.arange(6000) / 100.0
impaired_ride[0] += 0.6 * np.sin(2 * np.pi * 0.8 * t) # 不稳定摇摆
impaired_ride[3] += 0.5 * np.random.randn(6000) # 异常角速度

# 检测
result = detector.detect(impaired_ride)

print(f"\n检测结果:")
print(f" 检测到损伤: {result['detected']}")
print(f" 报警时间: {result['alarm_time_sec']:.1f}秒" if result['detected'] else " 未触发报警")
print(f" 最终评分: {result['score']:.4f}")
print(f" 阈值: {result['threshold']:.4f}")
print(f" 检测窗口数: {len(result['all_scores'])}")

# 嵌入式性能
print(f"\n嵌入式性能:")
print(f" STM检测周期: 262ms")
print(f" 窗口更新频率: 1Hz")
print(f" 实时性: ✅ 满足")

实验结果

检测性能

BAC等级 检测率 中位检测时间 误报率
高BAC(≥0.08%) 91% 25秒 ≤2.3%
低BAC(0.05-0.08%) 50% 27秒 ≤2.3%
清醒 — — ≤2.3%(保证)

与基线对比(α=0.023,原文 Table I/II)

方法 误报率 检测率(高/低BAC) 检测延迟
批处理分类器 0.02(远低于上限,但预测过于保守) 96% / 24% 报警只能出现在骑行结束,无实时检测时延
朴素窗口分类器 0.11(超标) — —
交换性鞅 0.67(严重超标) — —
重校准鞅 0.02 65% (54s) / 14% (60s) 明显更慢
本文ICP方案 0.02(有可证明上界保证) 91% (25s) / 50% (27s) —

从e-Scooter到汽车:迁移可行性

信号差异分析

维度 E-Scooter 汽车 迁移难度
传感器 IMU+油门 IMU+方向盘+踏板 低
运动模式 2轮摇摆 4轮稳定 中
噪声环境 室外多变 车内相对稳定 ✅ 更容易
检测窗口 25-27秒 可更长 ✅ 更宽松
个体差异 大 更大(体型/驾龄) ⚠️ 需更多校准

汽车迁移方案

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
class AutomotiveAlcoholDetectionAdapter:
"""
从e-Scooter酒驾检测迁移到汽车DMS

关键改造:
1. 传感器替换:IMU→方向盘扭矩+车辆动力学
2. 特征扩展:增加车道偏离频率+方向盘修正模式
3. 多模态融合:IMU + 摄像头面部特征 + rPPG心率
4. 校准数据:从清醒驾驶大数据中建立基线
"""

AUTOMOTIVE_CHANNELS = {
'steering_torque': '方向盘扭矩(替代油门)',
'lateral_accel': '横向加速度',
'longitudinal_accel': '纵向加速度',
'yaw_rate': '横摆角速度',
'lane_deviation': '车道偏离量',
'brake_pedal': '制动踏板',
'throttle_pedal': '油门踏板',
}

FUSION_PLAN = {
'kinematic': {
'signals': ['steering_torque', 'accel', 'yaw_rate'],
'features': '排列熵 + 频域特征',
'weight': 0.40,
},
'visual': {
'signals': ['面部表情', '头部姿态', '注视方向'],
'features': '面部不对称性 + 运动迟缓',
'weight': 0.35,
},
'physiological': {
'signals': ['rPPG心率', 'rPPG心率变异性'],
'features': 'HRV下降 + 心率异常',
'weight': 0.25,
},
}

IMS开发启示

1. 酒驾检测是Euro NCAP 2026新要求

Euro NCAP 2026 OMS协议中酒驾检测是新增项目。当前业内缺乏成熟的视觉酒驾检测方案。本文的IMU方案可作为互补手段:

检测模态 可行性 准确率 侵入性 IMS集成难度
IMU+排列熵 ✅ 高 91%(高BAC检测率,本文实测) 无 低(复用车辆CAN信号)
摄像头面部 ⚠️ 中 原文未提供该数字 无 中
rPPG心率 ⚠️ 中 原文未提供该数字 无 中
三模态融合 ✅ 高 原文未提供该数字 无 中

2. 误报率控制的统计框架

本文的ICP误报率控制框架可直接用于IMS的所有检测模块:

  • 疲劳检测的误报率控制
  • 分心检测的误报率控制
  • OOP检测的误报率控制

核心思想:不仅检测是否异常,还要在统计上保证”清醒/正常”用户不被误报。

3. 开发路线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
IMS酒驾检测开发路线:

Phase 1(0-3月):数据采集
├── 采集清醒驾驶数据(方向盘+IMU+视频)
├── 场景覆盖:高速/城市/夜间/弯道
└── 目标:1000+小时清醒基线

Phase 2(3-6月):模型开发
├── 实现排列熵特征提取
├── 实现ICP误报控制
├── 多模态融合(IMU+视觉+rPPG)
└── 目标:高BAC检测率>85%,误报率<3%

Phase 3(6-12月):验证部署
├── 受控实验验证(伦理审批)
├── 嵌入式部署(QCS8255)
├── Euro NCAP合规测试
└── 目标:量产就绪

论文arXiv: 2610.11783
代码与数据已开源:github.com/voi-oss/in-ride-impairment-detection
嵌入式实现:NUCLEO-H533RE(STM32)开发板,1000次检测周期平均延迟262.26ms