HuLiGen:从参数化人体模型生成LiDAR点云

arXiv 2610.10196 论文解读与座舱感知数据合成应用

论文信息

  • 标题: HuLiGen: Human LiDAR Generation from Parametric Body Models
  • 作者: Salma Galaaoui, Nermin Samet, David Picard
  • 机构: Valeo AI (valeoai)
  • 链接: arXiv:2610.10196
  • 代码: GitHub: valeoai/HuLiGen

核心创新

HuLiGen 使用 Point Transformer + Flow Matching 目标,从 SMPL 参数化人体模型生成真实感 LiDAR 点云。生成的点云比传统 raycasting 更接近真实采集分布,可用于合成数据预训练,在低标注/低数据场景下将 MPJPE 降低 50%。

一句话总结: 用生成模型替代物理 raycasting 来合成人体 LiDAR 数据,生成结果更接近真实采集分布,显著提升下游姿态估计性能。

问题背景

数据来源 真实感 成本 标注 数据量
真实LiDAR采集 最高 极高 极贵 稀缺
仿真raycasting 低 低 免费 无限
HuLiGen生成 接近真实 低 自动 无限

方法详解

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

class HuLiGenModel(nn.Module):
"""
HuLiGen: 从SMPL参数生成LiDAR点云

核心架构:
1. 输入:SMPL参数 (pose, shape, translation)
2. 生成:Point Transformer + Flow Matching
3. 输出:人体LiDAR点云 (N, 3) + 强度

与raycasting的区别:
- Raycasting:从传感器射光线,与3D网格求交 → 理想化,缺少噪声/遮挡
- HuLiGen:学习真实LiDAR的分布特征 → 生成更真实的点云
"""

def __init__(self, n_points=4096, hidden_dim=256,
n_layers=6, n_heads=4):
super().__init__()
self.n_points = n_points

# SMPL参数编码器
self.smpl_encoder = SMPLParameterEncoder(
pose_dim=72, shape_dim=10, trans_dim=3,
hidden_dim=hidden_dim
)

# Point Transformer(核心生成器)
self.point_transformer = PointTransformerGenerator(
n_points=n_points,
hidden_dim=hidden_dim,
n_layers=n_layers,
n_heads=n_heads
)

# Flow Matching目标
self.flow_matching = FlowMatchingObjective()

def forward(self, smpl_params: dict) -> dict:
"""
Args:
smpl_params: {
'pose': (B, 72),
'shape': (B, 10),
'translation': (B, 3)
}
Returns:
{'points': (B, N, 3), 'intensity': (B, N, 1)}
"""
# 编码SMPL参数
smpl_feat = self.smpl_encoder(smpl_params)

# 生成点云
generated = self.point_transformer(smpl_feat)

return generated

def generate_lidar(self, smpl_params: dict,
n_steps: int = 50) -> torch.Tensor:
"""
Flow Matching生成过程

从噪声逐步流向目标分布
"""
# 初始噪声
B = smpl_params['pose'].shape[0]
x_t = torch.randn(B, self.n_points, 3)

# Flow Matching去噪
smpl_feat = self.smpl_encoder(smpl_params)

for t in range(n_steps, 0, -1):
t_tensor = torch.full((B,), t/n_steps, device=x_t.device)
v = self.point_transformer.predict_velocity(
x_t, smpl_feat, t_tensor
)
x_t = x_t - v / n_steps

return x_t # (B, N, 3) 生成的人体LiDAR点云


class SMPLParameterEncoder(nn.Module):
"""SMPL参数编码器"""

def __init__(self, pose_dim=72, shape_dim=10, trans_dim=3,
hidden_dim=256):
super().__init__()
self.pose_encoder = nn.Sequential(
nn.Linear(pose_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim)
)
self.shape_encoder = nn.Sequential(
nn.Linear(shape_dim, hidden_dim // 2),
nn.ReLU(),
nn.Linear(hidden_dim // 2, hidden_dim)
)
self.trans_encoder = nn.Sequential(
nn.Linear(trans_dim, hidden_dim // 4),
nn.ReLU(),
nn.Linear(hidden_dim // 4, hidden_dim)
)

self.fusion = nn.Sequential(
nn.Linear(hidden_dim * 3, hidden_dim),
nn.LayerNorm(hidden_dim),
nn.GELU()
)

def forward(self, params):
pose_feat = self.pose_encoder(params['pose'])
shape_feat = self.shape_encoder(params['shape'])
trans_feat = self.trans_encoder(params['translation'])

concat = torch.cat([pose_feat, shape_feat, trans_feat], dim=-1)
return self.fusion(concat)


class PointTransformerGenerator(nn.Module):
"""Point Transformer生成器"""

def __init__(self, n_points=4096, hidden_dim=256,
n_layers=6, n_heads=4):
super().__init__()
self.n_points = n_points
self.hidden_dim = hidden_dim

# 输入投影
self.input_proj = nn.Linear(3, hidden_dim)

# Transformer层
self.layers = nn.ModuleList([
PointTransformerLayer(hidden_dim, n_heads)
for _ in range(n_layers)
])

# 输出投影
self.output_proj = nn.Linear(hidden_dim, 3) # 位置
self.intensity_proj = nn.Linear(hidden_dim, 1) # 强度

# 速度预测(Flow Matching)
self.velocity_proj = nn.Linear(hidden_dim, 3)

def forward(self, condition: torch.Tensor) -> dict:
"""
Args:
condition: (B, hidden_dim) SMPL条件特征
"""
B = condition.shape[0]

# 初始随机点
x = torch.randn(B, self.n_points, 3, device=condition.device)

# 投影
h = self.input_proj(x) # (B, N, hidden)

# 条件广播
h = h + condition.unsqueeze(1)

# Transformer层
for layer in self.layers:
h = layer(h)

# 输出
points = self.output_proj(h) + x # 残差
intensity = torch.sigmoid(self.intensity_proj(h))

return {'points': points, 'intensity': intensity}

def predict_velocity(self, x, condition, t):
"""预测Flow Matching速度"""
h = self.input_proj(x) + condition.unsqueeze(1)
h = h + self.time_embed(t).unsqueeze(1)

for layer in self.layers:
h = layer(h)

return self.velocity_proj(h)


class PointTransformerLayer(nn.Module):
"""Point Transformer层"""

def __init__(self, dim, num_heads):
super().__init__()
self.attn = nn.MultiheadAttention(
dim, num_heads, batch_first=True
)
self.norm1 = nn.LayerNorm(dim)
self.norm2 = nn.LayerNorm(dim)
self.ffn = nn.Sequential(
nn.Linear(dim, dim * 2),
nn.GELU(),
nn.Linear(dim * 2, dim)
)

def forward(self, x):
# 自注意力
attn_out, _ = self.attn(x, x, x)
x = self.norm1(x + attn_out)
# FFN
x = self.norm2(x + self.ffn(x))
return x


class FlowMatchingObjective:
"""
Flow Matching训练目标

与扩散模型的区别:
- 扩散:反向SDE去噪
- Flow Matching:学习从先验到数据的向量场

优势:训练更稳定,采样更快
"""

def compute_loss(self, model, x_0, condition):
"""
Args:
x_0: 真实LiDAR点云 (B, N, 3)
condition: SMPL条件
"""
B = x_0.shape[0]

# 随机时间步
t = torch.rand(B, device=x_0.device)

# 噪声
noise = torch.randn_like(x_0)

# 插值
x_t = (1 - t.view(-1, 1, 1)) * x_0 + t.view(-1, 1, 1) * noise

# 目标速度
target_v = noise - x_0

# 预测速度
pred_v = model.predict_velocity(x_t, condition, t)

# MSE损失
loss = F.mse_loss(pred_v, target_v)
return loss


import torch.nn.functional as F

# 测试
if __name__ == "__main__":
model = HuLiGenModel(n_points=2048, hidden_dim=128, n_layers=4)

# 模拟SMPL参数
smpl_params = {
'pose': torch.randn(2, 72),
'shape': torch.randn(2, 10),
'translation': torch.randn(2, 3),
}

# 生成
output = model(smpl_params)

print("=== HuLiGen 生成 ===")
print(f"输入: pose={smpl_params['pose'].shape}, shape={smpl_params['shape'].shape}")
print(f"生成点云: {output['points'].shape}")
print(f"强度: {output['intensity'].shape}")

total_params = sum(p.numel() for p in model.parameters())
print(f"参数量: {total_params:,}")

在座舱mmWave/雷达数据合成中的应用

HuLiGen 虽然面向 LiDAR 点云生成,其架构可直接迁移到 mmWave 雷达数据合成:

HuLiGen 原始用途 座舱迁移用途
SMPL → LiDAR点云 SMPL → mmWave RAD张量
人体Mesh → 3D点 人体姿态 → 雷达回波仿真
合成预训练 → HPE 合成预训练 → OOP检测

实验结果

指标 raycasting HuLiGen 提升
点云真实感 低 高 显著
下游HPE MPJPE 基线 -50% 低数据场景
训练效率 - 少量标注 低标注需求

参考资源


总结: HuLiGen 开辟了”生成式”合成LiDAR数据的新范式,比传统raycasting更真实。对IMS座舱感知,这套方法可迁移到mmWave雷达数据合成——用SMPL姿态参数生成对应的RAD张量,解决mmWave训练数据稀缺问题。与Cosmos-Drive-Dreams 形成互补:Cosmos生成驾驶场景,HuLiGen生成人体传感器数据。