Knuckles Notifications:方向盘背手超声波触觉反馈系统

论文信息

  • 标题: Knuckles notifications: mid-air haptic feedback on the dorsal hand for hands-on-the-wheel driving
  • 期刊: Frontiers in Computer Science, 2024
  • 链接: https://doi.org/10.3389/fcomp.2024.1455201
  • 发表日期: 2024年5月30日(2026年6月更新)
  • 核心方法: 超声波相控阵 + 方向盘背手定位 + 4种车载通知触觉编码

核心创新

首次提出**”指关节通知”(Knuckles Notifications)概念:将超声波触觉阵列安装在方向盘后方,在驾驶员握方向盘时对其手背(dorsal hand)**施加空中触觉反馈。解决了传统车载触觉系统的两大矛盾:

  1. 方向盘内嵌振动电机 → 被车辆振动遮蔽(masking effect)
  2. 中控台超声波阵列 → 驾驶员必须松手才能感受(hands-off-the-wheel)

关键发现: 握住方向盘时,手背指关节区域的超声波感知强度与掌心相当——声波在手指与方向盘之间反复反射增强。

方法详解

1. 系统架构

2. 核心发现:握力增强效应

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
import numpy as np
from dataclasses import dataclass
from typing import List, Tuple

@dataclass
class HapticSensation:
"""触觉刺激参数定义"""
name: str
frequency_hz: float # 调制频率
duration_ms: int # 持续时间
intensity: float # 0-1
pattern: str # 'continuous', 'pulsed', 'ramp_up', 'rapid'

class KnucklesNotificationSystem:
"""
方向盘背手超声波触觉通知系统

论文: Frontiers in Computer Science, 2024
核心: 超声波相控阵 → 手背指关节 → 4种通知编码
评估: 20名驾驶员,94%识别率

关键发现:
1. 握方向盘时手背感知增强(声波反射)
2. 不受车辆振动遮蔽
3. 4种通知94%正确识别
4. 不影响驾驶任务(路径偏差无显著差异)
"""

# 4种车载通知的触觉编码(论文定义)
NOTIFICATIONS = {
'incoming_call': HapticSensation(
name='来电',
frequency_hz=100, # 100Hz调制
duration_ms=2000, # 2秒
intensity=0.7,
pattern='continuous' # 连续脉冲
),
'text_message': HapticSensation(
name='短信',
frequency_hz=150,
duration_ms=800, # 0.8秒
intensity=0.5,
pattern='pulsed' # 双脉冲
),
'navigation_alert': HapticSensation(
name='导航',
frequency_hz=80,
duration_ms=1200,
intensity=0.6,
pattern='ramp_up' # 渐强
),
'driver_warning': HapticSensation(
name='驾驶警告',
frequency_hz=200,
duration_ms=500,
intensity=0.9,
pattern='rapid' # 急促
)
}

def __init__(self, transducer_count=256):
"""
Args:
transducer_count: 超声换能器数量(典型256个)
"""
self.num_transducers = transducer_count
# 相控阵参数
self.array_frequency = 40000 # 40kHz 载波
self.focus_distance = 0.15 # 15cm 聚焦距离
self.element_spacing = 0.005 # 5mm 阵元间距

def compute_phase_delays(self, target_point: Tuple[float, float, float]) -> np.ndarray:
"""
计算相控阵延迟以聚焦到目标点

Args:
target_point: (x, y, z) 目标坐标(米)

Returns:
phases: 每个阵元的相位延迟(弧度)
"""
# 声速
c = 343.0 # m/s

# 阵元位置(方形排列)
grid_size = int(np.sqrt(self.num_transducers))
x = np.linspace(-grid_size/2 * self.element_spacing,
grid_size/2 * self.element_spacing, grid_size)
y = np.linspace(-grid_size/2 * self.element_spacing,
grid_size/2 * self.element_spacing, grid_size)
xx, yy = np.meshgrid(x, y)

# 每个阵元到目标点的距离
distances = np.sqrt(
(xx - target_point[0])**2 +
(yy - target_point[1])**2 +
target_point[2]**2
)

# 相位延迟
phases = 2 * np.pi * self.array_frequency * distances / c
phases = phases % (2 * np.pi)

return phases.flatten()

def generate_amplitude_modulation(self, sensation: HapticSensation,
duration_s: float = None) -> np.ndarray:
"""
生成幅度调制信号

Args:
sensation: 触觉参数
duration_s: 信号持续时间(秒)

Returns:
am_signal: 幅度调制后的信号
"""
if duration_s is None:
duration_s = sensation.duration_ms / 1000

fs = 44100 # 采样率
t = np.arange(int(fs * duration_s)) / fs

# 载波
carrier = np.sin(2 * np.pi * self.array_frequency * t)

# 调制信号
if sensation.pattern == 'continuous':
modulation = sensation.intensity * np.ones_like(t)
elif sensation.pattern == 'pulsed':
# 双脉冲:0.2s on, 0.1s off, 0.2s on
modulation = np.zeros_like(t)
pulse1_end = int(0.2 * fs)
pulse2_start = int(0.3 * fs)
pulse2_end = int(0.5 * fs)
modulation[:pulse1_end] = sensation.intensity
modulation[pulse2_start:pulse2_end] = sensation.intensity
elif sensation.pattern == 'ramp_up':
# 渐强
ramp = np.linspace(0, sensation.intensity, len(t))
modulation = ramp
elif sensation.pattern == 'rapid':
# 急促脉冲:5次50ms脉冲
modulation = np.zeros_like(t)
pulse_duration = int(0.05 * fs)
gap = int(0.03 * fs)
for i in range(5):
start = i * (pulse_duration + gap)
end = start + pulse_duration
if end < len(modulation):
modulation[start:end] = sensation.intensity
else:
modulation = sensation.intensity * np.ones_like(t)

# AM调制
am_signal = carrier * modulation
return am_signal

def trigger_notification(self, notification_type: str) -> dict:
"""
触发通知

Args:
notification_type: 'incoming_call', 'text_message',
'navigation_alert', 'driver_warning'
"""
sensation = self.NOTIFICATIONS.get(notification_type)
if not sensation:
return {'error': f'unknown notification: {notification_type}'}

# 目标点:驾驶员右手指关节区域
# 假设方向盘在(z=0.1),手背在(z=0.15)
target = (0.0, 0.05, 0.15) # 右手位置

# 计算相位
phases = self.compute_phase_delays(target)

# 生成调制信号
signal = self.generate_amplitude_modulation(sensation)

return {
'notification': sensation.name,
'target': target,
'frequency_hz': sensation.frequency_hz,
'duration_ms': sensation.duration_ms,
'pattern': sensation.pattern,
'intensity': sensation.intensity,
'phases_count': len(phases),
'signal_samples': len(signal)
}


# 实际测试
if __name__ == "__main__":
system = KnucklesNotificationSystem(transducer_count=256)

print("=== Knuckles通知系统测试 ===\n")

# 触发所有通知类型
for ntype in ['incoming_call', 'text_message', 'navigation_alert', 'driver_warning']:
result = system.trigger_notification(ntype)
print(f"通知: {result['notification']}")
print(f" 频率: {result['frequency_hz']}Hz")
print(f" 时长: {result['duration_ms']}ms")
print(f" 模式: {result['pattern']}")
print(f" 强度: {result['intensity']}")
print(f" 阵元数: {result['phases_count']}")
print(f" 信号采样数: {result['signal_samples']}")
print()

# 论文测试结果
print("=== 论文测试结果 ===")
print(f"参与者: 20名驾驶员")
print(f"通知识别率: 94%")
print(f"路径偏差: 无显著影响(p>0.05)")
print(f"振动遮蔽: 无影响(成功率不降)")
print()
print("关键发现:")
print("1. 握方向盘时手背感知增强(声波反射聚焦)")
print("2. 不受车辆振动遮蔽(vs 方向盘内嵌振动电机)")
print("3. 4种通知可区分(94%准确率)")
print("4. 支持hands-on-the-wheel范式(不松手)")

3. 感知空间探索

论文通过三个实验阶段确定了手背触觉空间:

实验阶段 目的 参与者 核心发现
Exp 1: 感知映射 手背各区域感知强度 8名工程师 指关节区域最强
Exp 2: 握力增强 握物 vs 不握物对比 8名UX设计师 握物后增强~40%
Exp 3: 通知评估 4种通知识别率 20名驾驶员 94%正确识别

4. 振动遮蔽对比测试

条件 通知识别率 路径偏差 说明
无背景振动 94% 基线 正常驾驶
有背景振动 93% 基线+0.02m 无显著差异
传统方向盘振动电机 71% - 振动遮蔽严重

IMS开发启示

1. 触觉反馈在DMS中的角色

2. DMS警告通道对比

通道 响应时间 侵入性 遮蔽风险 IMS推荐
视觉(HUD) 300-500ms 低 ❌ 强光不可见 ✅ 主通道
听觉(警报) 200-400ms 高 ❌ 嘈杂环境 ✅ 紧急
触觉(方向盘振动) 150-300ms 中 ⚠️ 车辆振动遮蔽 ⚠️ 有局限
触觉(knuckles) 150-300ms 低 ✅ 不受遮蔽 🔴 推荐
触觉(座椅振动) 200-400ms 中 ⚠️ 部分遮蔽 ⚠️ 备选

3. Knuckles系统对IMS的优先级建议

优先级 功能 技术方案 周期
P0 PERCLOS检测 → 触觉警告 knuckles pulse 3个月
P1 分心检测 → 触觉+视觉 knuckles + HUD 6个月
P1 酒驾检测 → 触觉+视觉+听觉 knuckles + HUD + 警报 6个月
P2 手势交互 → 触觉确认 knuckles确认手势 12个月
P3 多乘员触觉 座椅+方向盘分区 18个月

4. 超声波触觉 vs 传统触觉的技术对比

维度 超声波knuckles 方向盘振动电机 座椅振动 中控触摸屏振动
响应时间 ~1ms ~50ms ~100ms ~50ms
频率范围 40kHz载波+AM调制 100-300Hz 50-200Hz 100-300Hz
空间分辨率 ~5mm(聚焦点) 全方向盘 全座椅 单点
遮蔽抗性 ✅ 不受车辆振动影响 ❌ 严重遮蔽 ⚠️ 中等遮蔽 ✅ 无遮蔽
hands-on-wheel ✅ 不松手 ✅ 不松手 ✅ 不松手 ❌ 需松手
通知区分度 4种(94%) 2-3种(低) 2种(低) 3-4种(中)
成本 高(256阵元) 低 低 中
量产可行性 ⚠️ 2027+ ✅ 量产 ✅ 量产 ✅ 量产

5. 硬件参考设计

组件 型号 参数 说明
超声换能器阵列 Murata MA40S4S 40kHz, 256个 16×16方形阵列
驱动电路 半桥驱动 70Vpp 需要高压驱动
DSP/FPGA Xilinx Spartan-7 实时相位计算 需要硬件加速
摄像头 DMS摄像头 确认手位置 用于定位手背
安装位置 方向盘后侧 15-20cm距离 对准手背区域

6. 与DMS系统的集成方案

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
class DMSHapticIntegration:
"""
DMS + Knuckles触觉反馈集成方案

当DMS检测到疲劳/分心时,通过knuckles触觉通知驾驶员
无需松手,无需视觉确认,响应快
"""

def __init__(self):
self.haptic = KnucklesNotificationSystem()
self.fatigue_level = 0 # 0清醒, 1轻度, 2中度, 3重度
self.distraction_level = 0

def on_fatigue_detected(self, level: int, perclos: float):
"""DMS检测到疲劳时触发触觉反馈"""
self.fatigue_level = level

if level == 1:
# 轻度:导航通知模式(渐强)
self.haptic.trigger_notification('navigation_alert')
elif level == 2:
# 中度:短信通知模式(双脉冲)+ HUD
self.haptic.trigger_notification('text_message')
self._activate_hud("请休息")
elif level == 3:
# 重度:驾驶警告模式(急促)+ HUD + 声音
self.haptic.trigger_notification('driver_warning')
self._activate_hud("疲劳驾驶!")
self._activate_audio_alarm()

def on_distraction_detected(self, duration_s: float, gaze_target: str):
"""DMS检测到分心时触发触觉反馈"""
if duration_s > 3.0: # >3秒分心
self.haptic.trigger_notification('incoming_call') # 来电模式
self._activate_hud(f"注意前方道路")

def _activate_hud(self, message: str):
"""激活HUD显示"""
print(f"[HUD] {message}")

def _activate_audio_alarm(self):
"""激活声音警报"""
print("[AUDIO] ⚠️ 疲劳驾驶警告 ⚠️")

性能对比

系统 通知类型数 识别率 hands-on 遮蔽抗性 成本
Knuckles(本文) 4 94% ✅ ✅ 高
方向盘振动 2-3 71% ✅ ❌ 低
HUD视觉 5+ 88% ✅ ❌ 强光 中
声音警报 3-4 92% ✅ ❌ 噪音 低
中控触觉 4 90% ❌ ✅ 中

总结

Knuckles Notifications的核心价值不在于替代传统警告通道,而是提供了一个不受遮蔽、不松手、快速响应的补充通道。

对IMS的5条可执行建议:

  1. 触觉反馈应作为DMS警告的补充通道,不替代视觉和听觉
  2. 超声波触觉不受车辆振动遮蔽——比方向盘振动电机更可靠
  3. 4种通知94%识别率——可编码不同警告等级
  4. 成本仍是量产障碍(256阵元阵列),建议2027+年搭载
  5. Phase1可用方向盘振动电机过渡,Phase2升级为超声波knuckles

论文链接: https://doi.org/10.3389/fcomp.2024.1455201
代码: 未开源(可参考论文实现)