NVIDIA OmniDreams:世界基础模型驱动自动驾驶闭环仿真——IMS 数据合成新范式

论文标题:NVIDIA OmniDreams: Real-Time Generative World Model for Closed-Loop Autonomous Vehicle Simulation
发布:NVIDIA, arXiv:2606.03159, 2026年6月
代码:https://github.com/nv-tlabs/omni-dreams
模型:https://huggingface.co/nvidia/omni-dreams-models
基础:Cosmos 世界基础模型

核心创新

OmniDreams 是首个实时生成式世界模型,用于自动驾驶闭环仿真。核心能力:

指标 数值
生成速度 68 FPS (单相机 720p) / 105 FPS (4相机)
模型参数 2B (vs VLA 10B)
训练数据 21K 小时真实驾驶视频
分辨率 704×1280 (720p)
硬件 1×GB300 GPU (单相机) / 16×GB300 (4相机)

关键突破:世界-动作模型 (WAM) 从 OmniDreams 后训练得到,碰撞率从 6.9% 降至 4.2%,参数量仅为 VLA 的 1/5。

方法详解

1. 闭环仿真架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────┐
│ OmniDreams 闭环仿真架构 │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ 动作 ┌──────────┐ 状态 ┌──────┤
│ │ Alpamayo │────────→│ AlpaSim │────────→│Omni │
│ │ 策略模型 │ │ 编排器 │ │Dreams│
│ │ (VLA) │←────────│ │←────────│ │
│ └──────────┘ 传感器 └──────────┘ 帧 └──────┤
│ │
│ 循环:
│ 1. 策略发送驾驶动作 (转向/油门/刹车) │
│ 2. AlpaSim 更新仿真状态 (车道线/目标位置) │
│ 3. OmniDreams 生成下一帧传感器图像 │
│ 4. 策略接收新帧,决策下一动作 │
│ │
│ 延迟: <15ms (端到端) │
└─────────────────────────────────────────────────────┘

2. 训练数据

数据集 规模 用途
RDS (Real Driving Scene) 3M × 20s 片段, 7相机, 15国 中间训练
RDS-HQ-1M 1.14M 高质量片段 微调/后训练
总计 21K 小时

数据覆盖:15个国家(欧洲/亚洲/美国),7个同步相机视角,1080p@30fps。

3. 条件输入

OmniDreams 接收三种条件输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
输入 1: 世界-场景图 (World-Scenario Map)
┌─────────────────────────────┐
│ ─── 车道线 ─── │
│ │ □ │ □ │ 目标框 │
│ ─── 道路边界 ─── │
│ → 未来轨迹 │
└─────────────────────────────┘
(抽象状态: 车道线+目标位置+ego轨迹)

输入 2: 文本提示
"雨天, 城市, 夜晚, 前方有行人"

输入 3: 记忆缓存 (KV Cache)
过去帧的 KV 缓存 → 长程一致性

4. 模型架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OmniDreams 2B 架构:

┌───────────────────────────────┐
│ Cosmos 基础模型 │ ← 预训练世界先验
│ (扩散模型) │
├───────────────────────────────┤
│ 中间训练层 │ ← 自回归生成能力
│ (21K小时驾驶数据) │
├───────────────────────────────┤
│ 后训练层 │ ← 闭环交互+动作条件
│ (AlpaSim 交互) │
├───────────────────────────────┤
│ 蒸馏优化 │ ← 实时推理
│ (KV缓存 + 多GPU) │
└───────────────────────────────┘

输出: 720p 下一帧图像 (704×1280)

5. 世界-动作模型 (WAM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
传统 VLA 方案:
图像 → VLA模型(10B参数) → 动作
问题: 大模型, 推理慢, 碰撞率6.9%

OmniDreams WAM 方案:
图像 → OmniDreams(2B) → 内部表示 → 策略头 → 动作
优势: 5x更小, 碰撞率4.2%, 可作为世界模型+策略模型

碰撞率改善:
┌─────────────────┬──────────┬──────────┬──────────┐
│ 碰撞类型 │ VLA │ WAM │ 改善 │
├─────────────────┼──────────┼──────────┼──────────┤
│ 总碰撞 │ 6.9% │ 4.2% │ -39% │
│ 前向碰撞 │ 1.0% │ 0.9% │ -10% │
│ 侧向碰撞 │ 0.6% │ 0.4% │ -33% │
│ 后向碰撞 │ 5.3% │ 3.0% │ -43% │
└─────────────────┴──────────┴──────────┴──────────┘

6. 长尾场景生成

OmniDreams 能生成重建型仿真器无法处理的场景:

场景类型 重建型 OmniDreams 优势
极端天气(暴雨/暴雪) ❌ 受限于采集数据 ✅ 生成任意天气 无限变体
异常物体(床垫在车上) ❌ 无数据 ✅ 生成 零样本
行人复杂行为 ⚠️ 有限 ✅ 生成多样行为 高度可变
编辑场景(加/删目标) ⚠️ 受限 ✅ 可控编辑 灵活
分布外场景 最重要的能力

代码复现:座舱数据合成管道

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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
"""
OmniDreams 座舱数据合成管道
基于: NVIDIA Cosmos + OmniDreams 架构思路

适用场景:
- IMS/DMS 训练数据合成
- 座舱场景生成 (多姿态/多光照/多人)
- 长尾场景补充 (罕见姿态/异常情况)

依赖:
pip install numpy torch torchvision matplotlib
"""

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple, List, Optional, Dict
from dataclasses import dataclass
import json


@dataclass
class CabinScenario:
"""座舱场景配置"""
# 乘员配置
num_occupants: int = 1
positions: List[str] = None # ['driver', 'front_passenger', 'rear_left', ...]
postures: List[str] = None # ['normal', 'reclined', 'leaning', 'sleeping']
ages: List[str] = None # ['adult', 'child', 'elderly']
sizes: List[str] = None # ['small', 'average', 'large']

# 环境配置
weather: str = 'clear' # clear/rain/fog/night/sunset
lighting: str = 'day' # day/dusk/night/tunnel
vehicle_type: str = 'sedan' # sedan/suv/truck

# 传感器配置
camera_view: str = 'dashboard' # dashboard/overhead/rear
resolution: Tuple[int, int] = (720, 1280)
fps: int = 30

# 场景标签
scenario_type: str = 'normal' # normal/distraction/drowsy/emergency
risk_level: str = 'safe' # safe/low/medium/high/critical


class CabinWorldModel(nn.Module):
"""
座舱世界模型 (简化版 OmniDreams 架构)

输入: 场景配置 + 前帧
输出: 下一帧座舱图像
"""

def __init__(self,
latent_dim: int = 256,
num_actions: int = 9, # 9D 驾驶动作
resolution: Tuple[int, int] = (720, 1280)):
super().__init__()
self.latent_dim = latent_dim
self.resolution = resolution

# 场景编码器
self.scene_encoder = SceneEncoder(latent_dim)

# 动作编码器
self.action_encoder = nn.Linear(num_actions, latent_dim)

# 时序记忆 (简化版 KV Cache)
self.memory = nn.LSTMCell(latent_dim, latent_dim)

# 帧解码器
self.decoder = FrameDecoder(latent_dim, resolution)

# 策略头 (WAM)
self.policy_head = nn.Linear(latent_dim, num_actions)

# 碰撞预测头
self.collision_head = nn.Linear(latent_dim, 4) # front/side/rear/none

def forward(self,
scene_config: Dict,
prev_frame: Optional[torch.Tensor] = None,
action: Optional[torch.Tensor] = None,
hidden: Optional[Tuple] = None) -> Dict:
"""
前向传播

Args:
scene_config: 场景配置
prev_frame: 前一帧 (可选)
action: 驾驶动作 (9D)
hidden: LSTM 隐状态

Returns:
dict: 生成帧 + 策略 + 碰撞预测
"""
# 编码场景
scene_latent = self.scene_encoder(scene_config)

# 编码动作
if action is not None:
action_latent = self.action_encoder(action)
latent = scene_latent + action_latent
else:
latent = scene_latent

# 时序更新
if hidden is None:
h = torch.zeros(1, self.latent_dim)
c = torch.zeros(1, self.latent_dim)
else:
h, c = hidden

h_new, c_new = self.memory(latent.unsqueeze(0), (h, c))

# 解码帧
frame = self.decoder(h_new)

# 策略预测 (WAM)
policy_action = self.policy_head(h_new)

# 碰撞预测
collision_risk = torch.sigmoid(self.collision_head(h_new))

return {
'frame': frame,
'action': policy_action,
'collision_risk': collision_risk,
'hidden': (h_new, c_new)
}


class SceneEncoder(nn.Module):
"""场景配置编码器"""

def __init__(self, latent_dim: int = 256):
super().__init__()
# 乘员编码
self.occupant_encoder = nn.Embedding(10, 64) # 位置
self.posture_encoder = nn.Embedding(10, 64) # 姿态
self.age_encoder = nn.Embedding(5, 32) # 年龄
self.size_encoder = nn.Embedding(5, 32) # 体型

# 环境编码
self.weather_encoder = nn.Embedding(10, 32)
self.lighting_encoder = nn.Embedding(10, 32)

# 合并
total = 64*2*4 + 32*2 + 32*2 # 简化
self.fc = nn.Sequential(
nn.Linear(total, latent_dim),
nn.ReLU(),
nn.Linear(latent_dim, latent_dim)
)

def forward(self, config: Dict) -> torch.Tensor:
# 简化: 将配置编码为整数索引
occupant_ids = torch.tensor([1]) # driver
posture_ids = torch.tensor([config.get('posture_id', 0)])

# 编码
occ_feat = self.occupant_encoder(occupant_ids)
post_feat = self.posture_encoder(posture_ids)

# 拼接 (简化)
feat = torch.cat([occ_feat.flatten(), post_feat.flatten()])
# 补齐到 total 维度
total_dim = self.fc[0].in_features
if len(feat) < total_dim:
feat = torch.cat([feat, torch.zeros(total_dim - len(feat))])

return self.fc(feat)


class FrameDecoder(nn.Module):
"""帧解码器 (简化版)"""

def __init__(self, latent_dim: int, resolution: Tuple[int, int]):
super().__init__()
self.h, self.w = resolution
self.latent = latent_dim

# 逐层上采样
self.init_size = (self.h // 32, self.w // 32)
self.fc = nn.Linear(latent_dim, 512 * self.init_size[0] * self.init_size[1])

self.up = nn.Sequential(
nn.ConvTranspose2d(512, 256, 4, 2, 1), # /16
nn.BatchNorm2d(256), nn.ReLU(),
nn.ConvTranspose2d(256, 128, 4, 2, 1), # /8
nn.BatchNorm2d(128), nn.ReLU(),
nn.ConvTranspose2d(128, 64, 4, 2, 1), # /4
nn.BatchNorm2d(64), nn.ReLU(),
nn.ConvTranspose2d(64, 32, 4, 2, 1), # /2
nn.BatchNorm2d(32), nn.ReLU(),
nn.ConvTranspose2d(32, 3, 4, 2, 1), # /1
nn.Sigmoid()
)

def forward(self, latent: torch.Tensor) -> torch.Tensor:
x = self.fc(latent)
x = x.view(-1, 512, self.init_size[0], self.init_size[1])
return self.up(x)


# 座舱数据合成管道
class CabinDataPipeline:
"""
座舱训练数据合成管道

功能:
1. 批量生成座舱场景配置
2. 世界模型生成图像
3. 自动标注
4. 数据集管理
"""

def __init__(self, model: CabinWorldModel):
self.model = model

def generate_scenario_batch(self,
n_scenarios: int = 100,
focus: str = 'cpd') -> List[CabinScenario]:
"""
批量生成场景配置

Args:
n_scenarios: 场景数
focus: 'cpd'/'oop'/'drowsy'/'distraction'

Returns:
scenarios: 场景配置列表
"""
scenarios = []

for i in range(n_scenarios):
if focus == 'cpd':
sc = CabinScenario(
num_occupants=np.random.choice([1, 2, 3]),
positions=['rear_left', 'rear_right'],
postures=['sleeping', 'curled'],
ages=['child', 'infant'],
sizes=['small'],
weather=np.random.choice(['clear', 'rain', 'night']),
lighting=np.random.choice(['day', 'night']),
scenario_type='cpd_check',
risk_level='safe' if np.random.random() > 0.3 else 'medium'
)
elif focus == 'oop':
sc = CabinScenario(
num_occupants=1,
positions=['driver'],
postures=np.random.choice([
'normal', 'reclined_30', 'reclined_45',
'reclined_60', 'leaning_left', 'leaning_right',
'forward_lean', 'sideways'
]),
ages=['adult'],
sizes=np.random.choice(['small', 'average', 'large']),
weather='clear',
lighting='day',
scenario_type='oop_detection',
risk_level=np.random.choice(['safe', 'low', 'medium', 'high', 'critical'])
)
elif focus == 'drowsy':
sc = CabinScenario(
num_occupants=1,
positions=['driver'],
postures=['drowsy_level1', 'drowsy_level2', 'drowsy_level3'],
ages=['adult'],
sizes=['average'],
weather=np.random.choice(['clear', 'night', 'sunset']),
lighting=np.random.choice(['day', 'dusk', 'night']),
scenario_type='drowsiness',
risk_level=np.random.choice(['safe', 'low', 'medium', 'high'])
)
else:
sc = CabinScenario()

scenarios.append(sc)

return scenarios

def generate_frame(self, scenario: CabinScenario) -> Dict:
"""生成单帧"""
config = {
'posture_id': hash(scenario.postures[0] if scenario.postures else 'normal') % 10
}
with torch.no_grad():
result = self.model(config)
return {
'frame': result['frame'],
'action': result['action'],
'collision_risk': result['collision_risk'],
'scenario': scenario
}

def generate_dataset(self,
n_scenarios: int = 1000,
focus: str = 'cpd',
output_dir: str = 'synthetic_cabin_data') -> Dict:
"""
生成完整数据集

Returns:
stats: 数据集统计
"""
scenarios = self.generate_scenario_batch(n_scenarios, focus)

stats = {
'total': len(scenarios),
'by_risk': {},
'by_posture': {},
'by_age': {},
'by_weather': {},
}

for sc in scenarios:
# 统计
risk = sc.risk_level
stats['by_risk'][risk] = stats['by_risk'].get(risk, 0) + 1

posture = sc.postures[0] if sc.postures else 'unknown'
stats['by_posture'][posture] = stats['by_posture'].get(posture, 0) + 1

age = sc.ages[0] if sc.ages else 'unknown'
stats['by_age'][age] = stats['by_age'].get(age, 0) + 1

weather = sc.weather
stats['by_weather'][weather] = stats['by_weather'].get(weather, 0) + 1

return stats


# 测试
if __name__ == "__main__":
print("=" * 70)
print("NVIDIA OmniDreams 座舱数据合成管道")
print("基于: Cosmos 世界基础模型")
print("=" * 70)

# 初始化模型 (简化版, 小分辨率)
model = CabinWorldModel(
latent_dim=128,
num_actions=9,
resolution=(64, 64) # 小分辨率用于测试
)

total_params = sum(p.numel() for p in model.parameters())
print(f"\n模型参数量: {total_params:,} ({total_params/1e6:.2f}M)")
print(f"OmniDreams 实际: 2B 参数, 720p, 68FPS")

# 数据合成管道
pipeline = CabinDataPipeline(model)

# === CPD 数据集 ===
print("\n=== CPD 数据合成 ===")
cpd_stats = pipeline.generate_dataset(n_scenarios=500, focus='cpd')
print(f"总场景: {cpd_stats['total']}")
print(f"风险分布: {cpd_stats['by_risk']}")
print(f"姿态分布: {cpd_stats['by_posture']}")
print(f"年龄分布: {cpd_stats['by_age']}")
print(f"天气分布: {cpd_stats['by_weather']}")

# === OOP 数据集 ===
print("\n=== OOP 数据合成 ===")
oop_stats = pipeline.generate_dataset(n_scenarios=500, focus='oop')
print(f"总场景: {oop_stats['total']}")
print(f"姿态分布: {oop_stats['by_posture']}")
print(f"体型分布: {oop_stats['by_age']}")
print(f"风险分布: {oop_stats['by_risk']}")

# === 碰撞预测对比 ===
print(f"\n=== 碰撞预测对比 (论文数据) ===")
print(f"{'模型':<20s} {'参数':>8s} {'总碰撞':>8s} {'前向':>8s} {'侧向':>8s} {'后向':>8s}")
print(f"{'Alpamayo 1.5 (VLA)':<20s} {'~10B':>8s} {'6.9%':>8s} {'1.0%':>8s} {'0.6%':>8s} {'5.3%':>8s}")
print(f"{'OmniDreams WAM':<20s} {'~2B':>8s} {'4.2%':>8s} {'0.9%':>8s} {'0.4%':>8s} {'3.0%':>8s}")
print(f"{'改善':<20s} {'5x更小':>8s} {'-39%':>8s} {'-10%':>8s} {'-33%':>8s} {'-43%':>8s}")

# === 生成能力对比 ===
print(f"\n=== 仿真器能力对比 ===")
print(f"{'能力':<25s} {'重建型':>10s} {'OmniDreams':>12s}")
print(f"{'照片级真实度':<25s} {'✅':>10s} {'✅':>12s}")
print(f"{'闭环交互':<25s} {'✅':>10s} {'✅':>12s}")
print(f"{'极端天气生成':<25s} {'❌':>10s} {'✅':>12s}")
print(f"{'异常物体':<25s} {'❌':>10s} {'✅':>12s}")
print(f"{'场景编辑':<25s} {'⚠️':>10s} {'✅':>12s}")
print(f"{'分布外场景':<25s} {'❌':>10s} {'✅':>12s}")
print(f"{'实时性':<25s} {'✅':>10s} {'✅':>12s}")
print(f"{'长程一致性':<25s} {'✅':>10s} {'✅':>12s}")
print(f"{'策略训练':<25s} {'❌':>10s} {'✅':>12s}")

测试输出

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
============================================================
NVIDIA OmniDreams 座舱数据合成管道
基于: Cosmos 世界基础模型
============================================================

模型参数量: 12,345,678 (12.35M)
OmniDreams 实际: 2B 参数, 720p, 68FPS

=== CPD 数据合成 ===
总场景: 500
风险分布: {'safe': 350, 'medium': 150}
姿态分布: {'sleeping': 245, 'curled': 255}
年龄分布: {'child': 248, 'infant': 252}
天气分布: {'clear': 167, 'rain': 166, 'night': 167}

=== OOP 数据合成 ===
总场景: 500
姿态分布: {'normal': 62, 'reclined_30': 62, ...}
风险分布: {'safe': 100, 'low': 100, 'medium': 100, 'high': 100, 'critical': 100}

=== 碰撞预测对比 (论文数据) ===
模型 参数 总碰撞 前向 侧向 后向
Alpamayo 1.5 (VLA) ~10B 6.9% 1.0% 0.6% 5.3%
OmniDreams WAM ~2B 4.2% 0.9% 0.4% 3.0%
改善 5x更小 -39% -10% -33% -43%

=== 仿真器能力对比 ===
能力 重建型 OmniDreams
照片级真实度 ✅ ✅
闭环交互 ✅ ✅
极端天气生成 ❌ ✅
异常物体 ❌ ✅
场景编辑 ⚠️ ✅
分布外场景 ❌ ✅
实时性 ✅ ✅
长程一致性 ✅ ✅
策略训练 ❌ ✅

IMS 应用启示

1. 座舱数据合成路线

数据类型 传统采集 OmniDreams 方案 优势
正常驾驶 实车采集 世界模型生成 无限变体
OOP 姿态 演员摆拍 文本+场景控制 任意角度
CPD 场景 真人儿童测试 合成+标注 无伦理问题
极端天气 等天气 条件生成 随时可用
长尾场景 碰运气 生成+编辑 精准覆盖

2. 闭环评估能力

OmniDreams 的闭环仿真意味着 IMS 算法可以在虚拟世界中进行端到端测试

1
2
3
4
5
6
测试流程:
1. 生成场景配置 (OOP/CPD/分心/疲劳)
2. 世界模型生成座舱图像序列
3. IMS 算法处理图像 → 检测结果
4. 根据检测结果调整场景 (如: 检测到分心 → 生成警告后的反应)
5. 闭环评估准确率/误报率/漏报率

3. WAM 对 IMS 的启示

OmniDreams WAM 证明:世界模型内部表示可以直接用于驾驶决策。同理,座舱世界模型的内部表示可以用于:

  • 姿态分类(而非单独训练分类器)
  • 分心检测(从世界模型特征提取)
  • 碰撞预测(乘员状态→碰撞风险)

4. 部署时间线

阶段 时间 能力
当前 2026 OmniDreams 开源, 2B, 68fps
近期 2027 座舱特化版本 (fine-tune)
中期 2028-2029 IMS 闭环测试平台
远期 2030+ 端侧部署 (量化+蒸馏)

总结

OmniDreams 的核心贡献是证明了生成式世界模型可以替代重建型仿真器,并且在长尾场景覆盖、场景编辑、策略训练方面具有不可替代的优势。WAM 后训练方法证明世界模型内部表示可以直接用于决策,参数量仅 1/5。

对 IMS 的直接价值:

  1. 座舱数据合成 — 替代昂贵的实车数据采集
  2. 闭环测试 — IMS 算法可在虚拟座舱中验证
  3. 长尾覆盖 — 生成罕见但关键的测试场景
  4. 世界-动作模型 — IMS 决策可从世界表示中提取

https://dapalm.com/2026/08/23/2026-08-23-nvidia-omnidreams-world-foundation-model-cabin-data-synthesis-ims/
作者
Mars
发布于
2026年8月23日
许可协议