RadarGen:从相机图像生成雷达点云——ECCV 2026 跨模态数据合成新范式

标注雷达数据极其昂贵——一个场景需要专用雷达设备采集。RadarGen 用扩散模型从多视角相机图像直接生成雷达点云,包含RCS和速度信息。这意味着可以用廉价摄像头数据大规模合成雷达训练数据——对座舱mmWave感知数据匮乏有直接价值。

论文信息

项目 内容
标题 RadarGen: Automotive Radar Point Cloud Generation from Cameras
会议 ECCV 2026
arXiv 2512.17897
项目页 radargen.github.io
关键词 扩散模型, 跨模态生成, 雷达点云, BEV

核心创新

跨模态生成管道

1
2
3
多视角相机图像 → 预训练基础模型提取(深度/语义/运动)
→ BEV对齐条件输入 → 图像-潜空间扩散模型 →
BEV雷达地图(含RCS+速度)→ 点云重建

与现有方案对比

方案 数据来源 模态 标注需求 物理一致性
真实采集 专用雷达 雷达 高成本 ✅
仿真 游戏引擎 虚拟 低 中
RadarGen 相机图像 相机→雷达 零(用现有数据) 高(条件引导)

方法详解

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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
"""
RadarGen: 从相机图像生成雷达点云

论文核心:
1. 将雷达点云表示为BEV地图(编码空间结构+RCS+速度)
2. 用图像-潜空间扩散模型生成BEV雷达地图
3. 用预训练基础模型提取深度/语义/运动作为条件
4. 从BEV地图重建点云
"""

import torch
import torch.nn as nn
import numpy as np
from typing import Dict, Optional

class BEVRadarRepresentation:
"""
BEV雷达点云表示

将3D雷达点云编码为2D BEV地图:
- 通道1: 占用 (0/1)
- 通道2: RCS (雷达截面积)
- 通道3: 径向速度 (Doppler)
- 通道4: 位置x
- 通道5: 位置y
"""

def __init__(
self,
bev_h: int = 200, # 前向距离格数
bev_w: int = 200, # 侧向距离格数
range_max: float = 50.0, # 最大距离 (m)
resolution: float = 0.5 # 格分辨率 (m)
):
self.H = bev_h
self.W = bev_w
self.range_max = range_max
self.res = resolution

def pointcloud_to_bev(
self,
points: np.ndarray # (N, 5): x, y, z, rcs, velocity
) -> np.ndarray:
"""点云 → BEV地图 (H, W, 5)"""
bev = np.zeros((self.H, self.W, 5))

for p in points:
x, y, z, rcs, vel = p

# 坐标到BEV格
xi = int((x + self.range_max) / self.res)
yi = int((y + self.range_max) / self.res)

if 0 <= xi < self.W and 0 <= yi < self.H:
bev[yi, xi, 0] = 1.0 # 占用
bev[yi, xi, 1] = rcs # RCS
bev[yi, xi, 2] = vel # 速度
bev[yi, xi, 3] = x # x
bev[yi, xi, 4] = y # y

return bev

def bev_to_pointcloud(self, bev: np.ndarray) -> np.ndarray:
"""BEV地图 → 点云"""
points = []
for yi in range(self.H):
for xi in range(self.W):
if bev[yi, xi, 0] > 0.5: # 占用
rcs = bev[yi, xi, 1]
vel = bev[yi, xi, 2]
x = bev[yi, xi, 3]
y = bev[yi, xi, 4]
z = 0 # BEV不编码z
points.append([x, y, z, rcs, vel])
return np.array(points)


class LatentDiffusionRadarGen(nn.Module):
"""
图像-潜空间扩散模型生成雷达BEV

1. VAE编码器:BEV雷达地图 → 潜空间
2. 条件提取:相机图像 → 深度/语义/运动特征
3. 扩散过程:潜空间去噪
4. VAE解码器:潜空间 → BEV雷达地图
"""

def __init__(
self,
bev_channels: int = 5,
bev_size: int = 200,
latent_dim: int = 4,
latent_size: int = 25,
cond_dim: int = 256
):
super().__init__()

# VAE编码器
self.bev_encoder = nn.Sequential(
nn.Conv2d(bev_channels, 32, 3, 2, 1),
nn.GroupNorm(8, 32),
nn.SiLU(),
nn.Conv2d(32, 64, 3, 2, 1),
nn.GroupNorm(8, 64),
nn.SiLU(),
nn.Conv2d(64, latent_dim, 3, 2, 1)
)

# VAE解码器
self.bev_decoder = nn.Sequential(
nn.ConvTranspose2d(latent_dim, 64, 3, 2, 1, output_padding=1),
nn.GroupNorm(8, 64),
nn.SiLU(),
nn.ConvTranspose2d(64, 32, 3, 2, 1, output_padding=1),
nn.GroupNorm(8, 32),
nn.SiLU(),
nn.ConvTranspose2d(32, bev_channels, 3, 2, 1, output_padding=1)
)

# 条件编码器(深度/语义/运动)
self.cond_encoder = nn.Sequential(
nn.Conv2d(3, 64, 3, 1, 1), # 3通道: 深度+语义+运动
nn.GroupNorm(8, 64),
nn.SiLU(),
nn.Conv2d(64, 128, 3, 2, 1),
nn.GroupNorm(8, 128),
nn.SiLU(),
nn.Conv2d(128, cond_dim, 3, 2, 1)
)

# U-Net 去噪网络(简化版)
self.denoiser = UNetDiffusion(
in_channels=latent_dim,
cond_channels=cond_dim,
model_channels=128,
num_res_blocks=2
)

def forward(
self,
bev: torch.Tensor,
conditions: torch.Tensor,
t: torch.Tensor
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
训练前向

Args:
bev: (B, 5, H, W) 真实BEV雷达
conditions: (B, 3, H, W) 深度+语义+运动
t: (B,) 扩散时间步
"""
# VAE编码
latents = self.bev_encoder(bev)

# 条件编码
cond = self.cond_encoder(conditions)

# 加噪
noise = torch.randn_like(latents)
noisy = latents + noise * t.view(-1, 1, 1, 1)

# 去噪预测
noise_pred = self.denoiser(noisy, t, cond)

# VAE解码
recon = self.bev_decoder(latents)

return noise_pred, recon

@torch.no_grad()
def generate(
self,
conditions: torch.Tensor,
num_steps: int = 50
) -> torch.Tensor:
"""生成雷达BEV地图"""
B = conditions.shape[0]

# 条件编码
cond = self.cond_encoder(conditions)

# 从纯噪声开始
latents = torch.randn(B, 4, 25, 25)

# DDPM采样
for i in reversed(range(num_steps)):
t = torch.full((B,), i / num_steps)
noise_pred = self.denoiser(latents, t, cond)
latents = latents - noise_pred * (1.0 / num_steps)

# VAE解码
bev = self.bev_decoder(latents)
return bev


class UNetDiffusion(nn.Module):
"""简化版U-Net去噪网络"""

def __init__(
self,
in_channels: int = 4,
cond_channels: int = 256,
model_channels: int = 128,
num_res_blocks: int = 2
):
super().__init__()

# 时间嵌入
self.time_embed = nn.Sequential(
nn.Linear(1, model_channels),
nn.SiLU(),
nn.Linear(model_channels, model_channels)
)

# 条件投影
self.cond_proj = nn.Conv2d(cond_channels, model_channels, 1)

# 下采样
self.down1 = ResBlock(in_channels, model_channels)
self.down2 = ResBlock(model_channels, model_channels * 2)

# 中间块
self.mid = ResBlock(model_channels * 2, model_channels * 2)

# 上采样
self.up1 = ResBlock(model_channels * 2, model_channels)
self.up2 = ResBlock(model_channels, in_channels)

def forward(
self,
x: torch.Tensor,
t: torch.Tensor,
cond: torch.Tensor
) -> torch.Tensor:
# 时间嵌入
t_emb = self.time_embed(t.view(-1, 1))
t_emb = t_emb.view(-1, t_emb.shape[-1], 1, 1)

# 条件
cond = self.cond_proj(cond)

# U-Net
h1 = self.down1(x)
h2 = self.down2(h1)
h = self.mid(h2 + cond + t_emb)
h = self.up1(h + h2)
h = self.up2(h + h1)

return h


class ResBlock(nn.Module):
"""残差块"""
def __init__(self, in_ch, out_ch):
super().__init__()
self.conv1 = nn.Conv2d(in_ch, out_ch, 3, 1, 1)
self.conv2 = nn.Conv2d(out_ch, out_ch, 3, 1, 1)
self.skip = nn.Conv2d(in_ch, out_ch, 1) if in_ch != out_ch else nn.Identity()
self.norm1 = nn.GroupNorm(8, out_ch)
self.norm2 = nn.GroupNorm(8, out_ch)

def forward(self, x):
h = self.norm1(self.conv1(x))
h = nn.functional.silu(h)
h = self.norm2(self.conv2(h))
return h + self.skip(x)


# ============ 测试 ============
if __name__ == "__main__":
torch.manual_seed(42)

model = LatentDiffusionRadarGen()

# 模拟输入
bev = torch.randn(2, 5, 200, 200) # BEV雷达
conditions = torch.randn(2, 3, 200, 200) # 深度+语义+运动
t = torch.rand(2)

# 训练前向
noise_pred, recon = model(bev, conditions, t)

print("RadarGen 测试:")
print(f" BEV输入: {bev.shape}")
print(f" 条件输入: {conditions.shape}")
print(f" 噪声预测: {noise_pred.shape}")
print(f" VAE重建: {recon.shape}")

# 生成
generated = model.generate(conditions, num_steps=10)
print(f" 生成BEV: {generated.shape}")

# BEV → 点云
bev_rep = BEVRadarRepresentation()
bev_np = generated[0].numpy()
points = bev_rep.bev_to_pointcloud(bev_np)
print(f" 生成点云: {points.shape} 点")

params = sum(p.numel() for p in model.parameters())
print(f"\n模型参数: {params:,} ({params/1e6:.1f}M)")

实验结果

生成质量

指标 RadarGen 随机基线 仿真基线
FID ↓ 28.3 85.2 42.1
点数误差 ±15% ±80% ±30%
RCS分布 0.87 0.12 0.65
速度分布 0.82 0.20 0.58
空间分布 0.91 0.30 0.75

下游感知任务

训练数据 检测AP ↑ 说明
仅真实雷达 68.2 基线
真实+仿真 70.1 +1.9
真实+RadarGen 72.5 +4.3
仅RadarGen 58.3 可用但不如真实

IMS 开发启示

1. 座舱雷达数据合成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 车内雷达数据合成方案
cabin_radar_synthesis = {
"problem": "座舱mmWave雷达训练数据稀缺,场景有限",
"solution": "用座舱摄像头数据合成雷达点云",
"pipeline": [
"1. 座舱RGB/IR摄像头 → 图像",
"2. 预训练模型 → 深度/语义/运动",
"3. RadarGen扩散模型 → BEV雷达地图",
"4. BEV → 点云",
"5. 合成雷达训练数据"
],
"advantage": "摄像头数据已有,无需额外采集",
"volume": "1小时视频 → 数万帧合成雷达",
"cost": "零额外采集成本"
}

2. 跨模态数据增强架构

3. NVIDIA Isaac Sim 集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Isaac Sim + RadarGen 联合方案
isaac_radar_pipeline = {
"isaac_sim": {
"role": "物理仿真+传感器模拟",
"outputs": ["RGB图像", "深度图", "语义分割", "LiDAR"],
"advantage": "物理精确的光照/材质/运动"
},
"radargen": {
"role": "跨模态雷达合成",
"input": "Isaac Sim的RGB+深度+语义",
"output": "合成mmWave雷达点云",
"advantage": "比仿真雷达更真实"
},
"combined": "Isaac Sim提供场景 → RadarGen生成雷达 → 完整多模态数据集"
}

4. 数据合成优先级

阶段 方法 目标 数据量
V1 仿真+RadarGen CPD存在检测 10K帧
V2 真实+RadarGen OOP姿态分类 50K帧
V3 真实+RadarGen+Isaac Sim 行为理解 100K帧
V4 多模态融合 全座舱感知 500K帧

参考文献

  1. RadarGen: arXiv:2512.17897, ECCV 2026
  2. 项目页: radargen.github.io
  3. 潜空间扩散: Rombach et al. (LDM)
  4. BEV表示: Bird’s-eye-view
  5. NVIDIA Isaac Sim: Omniverse仿真
  6. DepthAnything/MiDaS: 单目深度估计
  7. SAM: Segment Anything Model

发布时间: 2026-08-25 05:40
标签: RadarGen, ECCV 2026, 扩散模型, 跨模态生成, 雷达点云, BEV, 数据合成, 座舱感知