TriDrive:联合驾驶员-车辆-道路建模的驾驶员监控预测框架

论文信息

项目 内容
标题 TriDrive: Joint Driver, Vehicle, and Road Modeling for Forecasting and Driver Monitoring
作者 Yuhang Wang 等
领域 Robotics (cs.RO), Computer Vision (cs.CV)
年份 2026
链接 arXiv:2609.33000
代码 HuggingFace
规模 32页(10页正文+附录),4图25表

核心创新

TriDrive 是首个统一框架,联合预测驾驶员运动学、车辆动力学和道路需求,通过自动化条件转移模型实现。它解决了以往驾驶员监控中”座舱内活动”与”交通条件下的驾驶员运动”孤立建模的问题。

三大突破:

  1. 模态特定编码器:驾驶员锚定运动学表示 + 因果CAN总线动力学 + 冻结V-JEPA 2道路潜在表示
  2. 有向残差连接:驾驶员和道路上下文通过有向连接精化车辆预测
  3. 实时部署:蒸馏道路编码器后在 comma four + 8GB GPU 上运行,5Hz更新,p95延迟177ms

方法详解

1. 整体架构

2. 模态特定编码器

2.1 驾驶员锚定运动学表示

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
import torch
import torch.nn as nn
from typing import Tuple

class AnchoredKinematicEncoder(nn.Module):
"""
TriDrive 驾驶员运动学编码器

论文 Section 3.1: 将驾驶员关键点序列编码为锚定表示
以驾驶员静止姿态为锚点,编码相对偏移量

Args:
num_keypoints: 关键点数量(如面部106点+上半身17点)
input_dim: 每个关键点的维度 (x, y, confidence)
hidden_dim: 隐藏层维度
num_layers: Transformer层数
"""

def __init__(
self,
num_keypoints: int = 123,
input_dim: int = 3,
hidden_dim: int = 256,
num_layers: int = 4,
num_heads: int = 8
):
super().__init__()
self.num_keypoints = num_keypoints
self.hidden_dim = hidden_dim

# 关键点嵌入
self.keypoint_embed = nn.Linear(input_dim, hidden_dim)

# 位置编码
self.pos_encoding = self._create_pos_encoding(num_keypoints, hidden_dim)

# 因果 Transformer 编码器
encoder_layer = nn.TransformerEncoderLayer(
d_model=hidden_dim,
nhead=num_heads,
dim_feedforward=hidden_dim * 4,
dropout=0.1,
activation='gelu',
batch_first=True
)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)

# 锚点归一化层
self.anchor_norm = nn.LayerNorm(hidden_dim)

def _create_pos_encoding(self, num_kp: int, dim: int) -> torch.Tensor:
"""创建关键点位置编码"""
pos = torch.arange(num_kp).float().unsqueeze(1)
div_term = torch.exp(torch.arange(0, dim, 2).float() *
(-torch.log(torch.tensor(10000.0)) / dim))
pe = torch.zeros(num_kp, dim)
pe[:, 0::2] = torch.sin(pos * div_term)
pe[:, 1::2] = torch.cos(pos * div_term)
return pe.unsqueeze(0) # (1, num_kp, dim)

def forward(
self,
keypoints: torch.Tensor,
anchor_pose: torch.Tensor = None
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
前向传播

Args:
keypoints: 驾驶员关键点序列 (B, T, K, D)
B=batch, T=时间步, K=关键点数, D=维度
anchor_pose: 静止姿态锚点 (B, K, D),可选

Returns:
encoded: 编码后的运动学特征 (B, T, hidden_dim)
aux: 辅助特征用于有向残差连接
"""
B, T, K, D = keypoints.shape

# 锚定:计算相对于静止姿态的偏移
if anchor_pose is not None:
# 相对偏移编码(论文核心创新)
anchor_expanded = anchor_pose.unsqueeze(1) # (B, 1, K, D)
relative_kp = keypoints - anchor_expanded
else:
# 使用序列均值作为锚点
relative_kp = keypoints - keypoints.mean(dim=1, keepdim=True)

# 展平关键点维度
flat = relative_kp.reshape(B, T, K * D)
x = self.keypoint_embed(flat) # (B, T, hidden_dim) - 需调整维度

# 加位置编码
x = x + self.pos_encoding[:, :T, :].to(x.device)

# Transformer 编码
encoded = self.transformer(x)
encoded = self.anchor_norm(encoded)

# 辅助输出(用于有向残差连接到车辆预测)
aux = encoded.detach() # 阻断梯度回流

return encoded, aux


class CausalCANDynamicsEncoder(nn.Module):
"""
TriDrive CAN总线动力学编码器

论文 Section 3.2: 因果建模车辆动力学
处理方向盘转角、车速、加速度等CAN信号
"""

def __init__(
self,
num_signals: int = 12, # 方向盘、车速、横摆角速度等
hidden_dim: int = 128,
num_layers: int = 2
):
super().__init__()

self.input_proj = nn.Linear(num_signals, hidden_dim)

# 因果掩码(防止未来信息泄露)
self.causal_mask = None # 动态生成

encoder_layer = nn.TransformerEncoderLayer(
d_model=hidden_dim,
nhead=4,
dim_feedforward=hidden_dim * 4,
dropout=0.1,
batch_first=True
)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)

def forward(self, can_signals: torch.Tensor) -> torch.Tensor:
"""
Args:
can_signals: CAN总线信号序列 (B, T, num_signals)
包括:方向盘转角、车速、横摆角速度、
纵向加速度、横向加速度、发动机转速等
Returns:
dynamics: 车辆动力学编码 (B, T, hidden_dim)
"""
B, T, _ = can_signals.shape

x = self.input_proj(can_signals)

# 生成因果掩码
mask = torch.triu(
torch.ones(T, T, device=x.device) * float('-inf'),
diagonal=1
)

dynamics = self.transformer(x, mask=mask)
return dynamics


class RoadSceneEncoder(nn.Module):
"""
TriDrive 道路场景编码器

论文 Section 3.3: 使用冻结的 V-JEPA 2 提取道路潜在表示
V-JEPA 2 是 Meta 的视频联合嵌入预测架构
"""

def __init__(self, hidden_dim: int = 256):
super().__init__()

# V-JEPA 2 冻结参数(不参与训练)
self.vjepa = None # 实际使用时加载预训练权重
self.frozen = True

# 结构化道路边际提取
self.road_margin_proj = nn.Linear(
768, # V-JEPA 2 输出维度
hidden_dim
)

# 道路类型嵌入
self.road_type_embed = nn.Embedding(8, hidden_dim) # 高速/城市/乡村等

def forward(
self,
video_frames: torch.Tensor,
road_type: torch.Tensor = None
) -> torch.Tensor:
"""
Args:
video_frames: 前方道路视频 (B, C, T, H, W)
road_type: 道路类型索引 (B,)
Returns:
road_latent: 道路潜在表示 (B, T, hidden_dim)
"""
# V-JEPA 2 提取潜在表示(冻结)
with torch.no_grad():
if self.vjepa is not None:
vjepa_latent = self.vjepa(video_frames) # (B, T, 768)
else:
# 模拟:使用随机特征
B, T = video_frames.shape[0], video_frames.shape[2]
vjepa_latent = torch.randn(B, T, 768,
device=video_frames.device)

# 投影到模型维度
road_latent = self.road_margin_proj(vjepa_latent)

# 加入道路类型信息
if road_type is not None:
type_embed = self.road_type_embed(road_type) # (B, hidden_dim)
road_latent = road_latent + type_embed.unsqueeze(1)

return road_latent


# 测试代码
if __name__ == "__main__":
# 模拟输入数据
batch_size, seq_len = 4, 30

# 1. 驾驶员关键点
keypoints = torch.randn(batch_size, seq_len, 123, 3)
anchor_pose = torch.randn(batch_size, 123, 3)

kin_encoder = AnchoredKinematicEncoder(num_keypoints=123, hidden_dim=256)
kin_feat, kin_aux = kin_encoder(keypoints, anchor_pose)
print(f"驾驶员运动学特征: {kin_feat.shape}")
# 输出: torch.Size([4, 30, 256])

# 2. CAN信号
can_signals = torch.randn(batch_size, seq_len, 12)
can_encoder = CausalCANDynamicsEncoder(num_signals=12, hidden_dim=128)
can_feat = can_encoder(can_signals)
print(f"CAN动力学特征: {can_feat.shape}")
# 输出: torch.Size([4, 30, 128])

# 3. 道路场景
video = torch.randn(batch_size, 3, seq_len, 224, 224)
road_encoder = RoadSceneEncoder(hidden_dim=256)
road_feat = road_encoder(video)
print(f"道路场景特征: {road_feat.shape}")
# 输出: torch.Size([4, 30, 256])

print("\n✅ 三模态编码器测试通过")

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
class DirectedResidualFusion(nn.Module):
"""
TriDrive 有向残差连接融合层

论文 Section 3.4: driver → vehicle, road → vehicle 的有向连接
驾驶员和道路特征通过残差方式精化车辆动力学预测
"""

def __init__(self, driver_dim=256, road_dim=256, vehicle_dim=128):
super().__init__()

# 投影到统一空间
self.driver_proj = nn.Linear(driver_dim, vehicle_dim)
self.road_proj = nn.Linear(road_dim, vehicle_dim)

# 门控机制
self.driver_gate = nn.Sequential(
nn.Linear(vehicle_dim * 2, vehicle_dim),
nn.Sigmoid()
)
self.road_gate = nn.Sequential(
nn.Linear(vehicle_dim * 2, vehicle_dim),
nn.Sigmoid()
)

self.norm = nn.LayerNorm(vehicle_dim)

def forward(self, vehicle_feat, driver_feat, road_feat):
"""
有向残差:driver → vehicle, road → vehicle

Args:
vehicle_feat: (B, T, V)
driver_feat: (B, T, D) - 来自驾驶员编码器
road_feat: (B, T, R) - 来自道路编码器
Returns:
refined_vehicle: 精化后的车辆特征 (B, T, V)
"""
# 投影
d_proj = self.driver_proj(driver_feat)
r_proj = self.road_proj(road_feat)

# 门控融合
d_gate = self.driver_gate(torch.cat([vehicle_feat, d_proj], dim=-1))
r_gate = self.road_gate(torch.cat([vehicle_feat, r_proj], dim=-1))

# 有向残差
refined = vehicle_feat + d_gate * d_proj + r_gate * r_proj
refined = self.norm(refined)

return refined


# 测试融合层
if __name__ == "__main__":
fusion = DirectedResidualFusion(256, 256, 128)
v = torch.randn(4, 30, 128)
d = torch.randn(4, 30, 256)
r = torch.randn(4, 30, 256)
out = fusion(v, d, r)
print(f"融合后车辆特征: {out.shape}")
# 输出: torch.Size([4, 30, 128])

实验结果

1. AIDE 基准测试(公开数据集)

方法 All-MPJPE ↓ 说明
前人最佳 SOTA 71.47 孤立驾驶员运动建模
TriDrive 48.05 联合建模,降低32.7%

2. BATON 数据集(197.2小时自然驾驶)

任务 指标 无有向连接 有有向连接 提升
方向盘介入预测 PR-AUC 基线 +0.084 显著
TTC下降预测 PR-AUC 基线 +0.286 显著

3. 实时部署性能

指标 数值 说明
更新频率 5 Hz 每秒5次评估
p95延迟 177 ms 含模型推理+后处理
硬件 comma four + 8GB GPU 消费级硬件
AUROC 0.725 vs openpilot 0.563
驾驶员评分 - 适当性 +1.79 高于openpilot
驾驶员评分 - 及时性 +2.67 高于openpilot

IMS 开发启示

1. 架构启示

从孤立建模到联合建模: TriDrive 证明了驾驶员状态不能脱离车辆状态和道路场景孤立分析。对 IMS 的启示:

IMS 模块 当前做法 TriDrive 启示 优先级
疲劳检测 纯面部特征 + CAN动力学 + 道路复杂度 🔴高
分心检测 视线偏离 + 头部姿态 + 方向盘微操 + 道路需求 🔴高
危险行为 行为分类 + 车辆状态转移预测 🟡中
驾驶员警告 固定阈值 自适应道路需求条件 🔴高

2. 部署启示

维度 TriDrive 方案 IMS 可借鉴点
道路编码器 V-JEPA 2 冻结 + 蒸馏 用预训练视觉模型冻结+蒸馏降低算力
实时推理 5Hz, 177ms p95 DMS 需≥10Hz,但可分主次线程
轻量探针 独立当前状态评估 可用轻量探针做高频警告,重模型做低频分析
硬件 comma four + 8GB GPU IMS 高通平台算力更充裕

3. 数据启示

  • BATON 子集 197.2 小时自然驾驶数据 → IMS 需积累类似规模自然驾驶数据
  • AIDE 公开基准 → 可直接用于验证 IMS 算法
  • 代码开源(HuggingFace)→ 可直接复现并适配到 IMS 框架

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
# IMS 集成 TriDrive 思路伪代码
class IMSEnhancedDMS:
"""
IMS 增强型 DMS,借鉴 TriDrive 联合建模思路
"""

def __init__(self):
# 模态编码器
self.driver_encoder = AnchoredKinematicEncoder() # 面部+姿态
self.vehicle_encoder = CausalCANDynamicsEncoder() # CAN信号
# 道路编码器可选(取决于是否有前视摄像头接入)

# 融合层
self.fusion = DirectedResidualFusion()

# 下游任务头
self.fatigue_head = ... # 疲劳分类
self.distraction_head = ... # 分心分类
self.warning_head = ... # 警告触发

def process(self, frame, can_data, road_frame=None):
# 1. 编码各模态
driver_feat = self.driver_encoder(frame)
vehicle_feat = self.vehicle_encoder(can_data)

# 2. 有向残差融合
if road_frame is not None:
road_feat = self.road_encoder(road_frame)
fused = self.fusion(vehicle_feat, driver_feat, road_feat)
else:
fused = self.fusion(vehicle_feat, driver_feat,
torch.zeros_like(driver_feat))

# 3. 多任务预测
fatigue = self.fatigue_head(fused)
distraction = self.distraction_head(fused)
warning = self.warning_head(fused)

return {
'fatigue': fatigue,
'distraction': distraction,
'warning': warning
}

与现有方法对比

方法 建模范围 道路上下文 实时部署 代码开源
纯DMS(面部特征) 仅驾驶员 ❌ ✅ 部分
AIDE 基线 驾驶员+简单场景 ❌ ✅ ✅
openpilot DMS 驾驶员 简化 ✅ ✅
TriDrive 驾驶员+车辆+道路 ✅ V-JEPA 2 ✅ 5Hz/177ms ✅

总结

TriDrive 是驾驶员监控领域的重要突破:

  1. 首次联合建模驾驶员-车辆-道路三方交互
  2. 有向残差连接比简单拼接更有效地传递跨模态信息
  3. 实时可部署,在消费级硬件上实现 5Hz 推理
  4. 代码开源,可直接复现和适配

对 IMS 的核心启示:驾驶员状态监控必须脱离孤立面部特征分析,走向多模态联合建模。道路需求和车辆状态为驾驶员行为提供了关键的上下文基线,缺失这个基线是当前 DMS 误报率高、警告不及时的根源。