File size: 23,974 Bytes
42f5508
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
# SceneSmith 完整技术文档

> 语言:中文  
> 对应代码:[nepfaff/scenesmith](https://github.com/nepfaff/scenesmith)  
> 论文:[arXiv 2602.09153](https://arxiv.org/abs/2602.09153)  
> 本次运行环境:NVIDIA L40S GPU,SLURM 集群  

---

## 目录

1. [系统总览](#1-系统总览)
2. [输入输出格式](#2-输入输出格式)
3. [核心流程详解](#3-核心流程详解)
4. [代码结构解析](#4-代码结构解析)
5. [Robot Eval 模块](#5-robot-eval-模块)
6. [SceneSmith 能做什么 / 不能做什么](#6-scenesmith-能做什么--不能做什么)
7. [本次运行结果](#7-本次运行结果)
8. [Demo采集说明](#8-demo采集说明)
9. [常见问题与局限性](#9-常见问题与局限性)
10. [复现指南](#10-复现指南)

---

## 1. 系统总览

SceneSmith 是一个**全自动、文本驱动的室内场景生成系统**,专为机器人仿真设计。

给一段自然语言描述(如"一个有书桌、床和衣柜的卧室"),SceneSmith 会输出一个**物理上可用的完整室内场景**,包含:
- 精确的 6D 物体位姿
- 每个物体的碰撞几何体(SDF 格式)
- 物理属性(质量、摩擦系数、惯性矩阵)
- 可以直接导入 Drake / MuJoCo / Isaac Sim 的格式

系统的核心是用**多个 LLM Agent 协作**代替人工设计:每个 Agent 负责场景生成的一个阶段,用视觉反馈迭代优化,直到满足质量标准。

```
文字描述


┌─────────────────────────────────────────────────────────┐
│                     SceneSmith Pipeline                   │
│                                                           │
│  Floor Plan Agent → Furniture Agent → Wall Agent          │
│        ↓                 ↓               ↓               │
│  生成平面图          摆放大件家具      墙面装饰            │
│                          ↓               ↓               │
│                    Ceiling Agent → Manipuland Agent       │
│                          ↓               ↓               │
│                    天花板灯具         小件可操作物体       │
│                                                           │
│  每个阶段:LLM 规划 → 3D资产生成 → 物理验证 → 视觉评分    │
└─────────────────────────────────────────────────────────┘


house.blend (Blender 渲染) + house.dmd.yaml (Drake 仿真) + house_state.json
```

---

## 2. 输入输出格式

### 输入

| 输入项 | 格式 | 说明 |
|--------|------|------|
| 场景描述 | 自然语言字符串 | 例:`"A cozy bedroom with a queen bed..."` |
| CSV 提示文件(可选) | `prompts.csv` | 批量生成多个场景时使用 |
| 配置文件 | Hydra YAML | 控制使用的模型、后端、参数 |

**`prompts.csv` 格式**```csv
scene_index,prompt
0,"A modern kitchen with..."
1,"A cozy living room with..."
```

**关键配置参数**```yaml
openai:
  model: "gpt-5.2"              # LLM 模型名
experiment:
  num_scenes: 5                 # 生成场景数量
  scene_prompt: "..."           # 单场景描述
furniture_agent:
  asset_manager:
    backend: "hunyuan3d"        # 3D资产生成后端: sam3d | hunyuan3d | hssd
```

### 输出

每次运行生成如下目录结构:

```
outputs/YYYY-MM-DD/HH-MM-SS/
└── scene_000/
    ├── combined_house/
    │   ├── house.blend               ← Blender 完整场景 (~200MB)
    │   ├── house.dmd.yaml            ← Drake Directives(仿真可用)
    │   ├── house_furniture_welded.dmd.yaml  ← 家具焊接版(稳定性更好)
    │   ├── house_state.json          ← 所有物体的位姿+元数据
    │   └── sceneeval_state.json      ← 场景评分数据
    ├── room_bedroom/
    │   ├── generated_assets/         ← 每个物体的 SDF + 纹理
    │   │   ├── desk_0/
    │   │   │   ├── model.sdf
    │   │   │   ├── model.obj
    │   │   │   └── texture.png
    │   │   └── ...
    │   ├── scene_states/             ← 各阶段保存的中间状态
    │   │   ├── scene_after_furniture/scene.blend
    │   │   ├── scene_after_wall_objects/scene.blend
    │   │   ├── scene_after_ceiling_objects/scene.blend
    │   │   └── final_scene/
    │   │       ├── scene.blend
    │   │       └── scene_state.json
    │   └── scene_renders/            ← 各阶段的渲染图像 (PNG)
    └── floor_plans/
        └── final_floor_plan/
            ├── floor_plan.blend
            └── floor_plan.dmd.yaml
```

**`house_state.json` 结构**(关键字段):
```json
{
  "rooms": {
    "bedroom": {
      "objects": {
        "desk_0": {
          "object_type": "furniture",
          "name": "desk",
          "description": "A modern wooden desk",
          "sdf_path": "generated_assets/desk_0/model.sdf",
          "transform": {
            "translation": [0.79, 1.58, 0.0],
            "rotation_wxyz": [1.0, 0.0, 0.0, 0.0]
          },
          "bbox_min": [-0.6, -0.3, 0.0],
          "bbox_max": [0.6, 0.3, 0.75],
          "mass": 15.0,
          "mu_static": 0.6
        }
      }
    }
  }
}
```

**`house.dmd.yaml` 格式**(Drake Directives):
```yaml
directives:
- add_model:
    name: bedroom_desk_0
    file: package://scene/room_bedroom/generated_assets/desk_0/model.sdf
- add_weld:
    parent: world
    child: bedroom_desk_0::base_link
    X_PC:
      translation: [0.79, 1.58, 0.0]
      rotation: !Rpy { deg: [0, 0, 0] }
```

---

## 3. 核心流程详解

### Stage 1:Floor Plan Agent(平面图规划)

**输入**:场景描述文字  
**输出**:房间尺寸、门窗位置、墙体布局(`floor_plan.dmd.yaml`)

工作方式:
1. LLM 解析场景描述,确定所需房间类型(卧室、厨房等)
2. 生成候选平面图(房间长宽、门的位置)
3. 用 Blender 渲染俯视图,LLM 评分(0-1分)
4. 迭代优化,选择得分最高的方案

评分标准:空间合理性、比例、门窗位置是否符合常识。

---

### Stage 2:Furniture Agent(家具摆放)

**输入**:平面图、场景描述  
**输出**:家具的精确 6D 位姿 + 每件家具的 SDF 模型

这是最复杂的阶段,分三个子步骤:

#### 2a. 家具规划
LLM 决定放什么家具、大概的位置,生成一个结构化的家具列表。

#### 2b. 3D 资产生成
对每件家具,走以下路由(Asset Router):

```
家具描述文字


Asset Router (LLM决策)
    ├── "generated" → Hunyuan3D-2 / SAM3D
    │       ├── 生成参考图 (GPT-Image-2)
    │       └── 图片 → 3D mesh (扩散模型)
    │               ↓
    │       纹理烘焙 + SDF生成

    ├── "artvip" → 从ArtVIP库检索关节物体(开门的衣柜等)

    └── "hssd" → 从HSSD数据集检索(更快,质量稳定)
```

#### 2c. 迭代摆放
1. 放置家具到初始位置
2. Drake 物理仿真:检测碰撞、检查是否可站立
3. Blender 渲染多视角图
4. LLM 用视觉反馈打分(美观度、布局合理性、可达性)
5. 如果分数不满足阈值,调整位置重试(最多 N 轮)

---

### Stage 3:Wall Agent(墙面装饰)

**输入**:已摆好家具的场景  
**输出**:墙面装饰物(画、镜子、时钟、架子等)的位姿

同样走 asset router → 3D 生成 → Drake 验证 → 视觉评分的流程,但专注于墙面的高度、朝向、装饰品间距。

---

### Stage 4:Ceiling Agent(天花板)

**输入**:已完成墙面的场景  
**输出**:天花板灯具、风扇等物体

主要验证灯具位置是否在房间中央、是否与墙面冲突。

---

### Stage 5:Manipuland Agent(可操作小物件)

**输入**:已完成天花板的场景、每件家具的支撑面信息  
**输出**:桌面上的书、杯子、水果等小物件

特点:
- 自动分析每件家具的"支撑面"(桌面、架子面)
- 在支撑面上随机采样放置位置,Drake 验证不碰撞
- 可生成组合(stack/pile/filled_container):叠起来的书、装苹果的碗等

---

### Stage 6:物理投影 + 最终导出

所有物体放好后:
1. Drake 运行物理仿真,让物体自然落下(消除浮空、轻微穿插)
2. 输出最终 `house.dmd.yaml`(固定位姿,weld 到 world)
3. Blender 渲染最终场景,保存 `house.blend`

---

## 4. 代码结构解析

```
scenesmith/
├── main.py                    ← 入口,Hydra 配置,调度各 Agent

├── scenesmith/
│   ├── experiments/
│   │   └── indoor_scene_generation.py  ← 主实验类,串联所有 Agent
│   │
│   ├── floor_plan_agents/
│   │   ├── stateful_floor_plan_agent.py  ← Floor Plan Agent 实现
│   │   └── tools/                        ← 平面图相关工具
│   │
│   ├── furniture_agents/
│   │   ├── stateful_furniture_agent.py   ← Furniture Agent 主类
│   │   └── tools/
│   │       ├── furniture_tools.py        ← 家具放置、物理检查工具
│   │       ├── vision_tools.py           ← Blender 渲染观察工具
│   │       └── scene_tools.py            ← 场景状态查询
│   │
│   ├── wall_agents/           ← Wall Agent(同结构)
│   ├── ceiling_agents/        ← Ceiling Agent(同结构)
│   ├── manipuland_agents/     ← Manipuland Agent(同结构)
│   │
│   ├── agent_utils/
│   │   ├── asset_manager.py       ← 资产获取总入口
│   │   ├── asset_router/          ← LLM决策走哪个资产后端
│   │   ├── geometry_generation_server/  ← 3D生成服务(Hunyuan3D工作进程)
│   │   │   ├── server_manager.py
│   │   │   ├── worker_pool.py     ← GPU工作进程池
│   │   │   └── gpu_worker.py      ← 单个GPU的3D生成进程
│   │   ├── drake_utils.py         ← Drake 仿真工具
│   │   ├── rendering.py           ← 场景渲染工具
│   │   ├── physics_validation.py  ← 物理碰撞检测
│   │   ├── reachability.py        ← 机器人可达性分析
│   │   ├── image_generation.py    ← 参考图生成(GPT-Image-2)
│   │   ├── vlm_service.py         ← VLM评分服务
│   │   └── blender/               ← Blender 进程通信
│   │       ├── server_manager.py  ← Blender 服务器管理
│   │       └── renderer.py        ← 渲染接口
│   │
│   └── robot_eval/
│       ├── dmd_scene.py              ← Drake 场景加载
│       ├── policy_interface/
│       │   ├── policy_agent.py       ← 任务 → 物体绑定 Agent
│       │   └── predicate_resolver.py ← 绑定 → 精确位姿
│       ├── success_validation/
│       │   └── validator_agent.py    ← 任务成功验证 Agent
│       └── task_generation/
│           └── scene_prompt_generator.py ← 任务 → 场景描述

└── scripts/
    ├── robot_eval/
    │   ├── generate_prompts.py   ← Stage 1: task → prompts
    │   ├── policy_interface.py   ← Stage 3: scene + task → robot poses
    │   └── validate.py           ← Stage 4: 验证任务完成
    ├── collect_demo.py           ← Demo采集与可视化(本次添加)
    ├── render_flythrough.py      ← 生成场景飞行视频
    └── export_scene_to_mujoco.py ← 导出到 MuJoCo
```

---

### 关键类说明

#### `StatefulFurnitureAgent`
继承自 `BaseStatefulAgent`,用 `openai-agents` SDK 运行 LLM agent loop。

Agent 的 tools 包括:
- `place_furniture(obj_id, x, y, yaw)` — 放置家具
- `observe_scene()` — 渲染当前场景,返回多视角图给 LLM 看
- `get_physics_check()` — 运行 Drake 碰撞检测
- `generate_furniture_assets(descriptions)` — 批量生成 3D 资产
- `score_scene()` — VLM 打分

每轮 LLM 调用 tools → 观察反馈 → 调整 → 直到评分达标或超过最大轮数。

#### `AssetManager`
统一的资产获取接口,内部调用 `AssetRouter` 决策:
```python
asset = await asset_manager.get_asset(
    description="A wooden desk with drawers",
    object_type="furniture",
    strategy="generated"  # or "hssd", "artvip"
)
# 返回 Asset(sdf_path, obj_path, texture_path, dimensions, ...)
```

#### `DMDScene` + `PredicateResolver`
用于 robot eval:
```python
scene = load_scene_for_validation(
    scene_state_path=Path("house_state.json"),
    dmd_path=Path("house.dmd.yaml"),
)
scene.finalize()  # 建立 Drake plant,同步位姿

resolver = PredicateResolver(scene=scene, cfg=cfg)
result = resolver.resolve("Pick the apple and place it on the plate")
# result.poses[0].target_position → [x, y, z] 目标位姿
# result.poses[0].placement_bounds_min/max → 有效放置区域 AABB
```

---

## 5. Robot Eval 模块

这是 SceneSmith 的机器人评测框架,共 4 个阶段:

### 阶段 1:生成场景提示词
```bash
python scripts/robot_eval/generate_prompts.py \
    --task "Pick a fruit from the bowl and place it on the plate" \
    --output-dir outputs/eval_run \
    --num-prompts 5
```
LLM 分析 task,提取:
- 必须存在的物体(水果、碗、盘子)
- 初始状态约束(水果不能已经在盘子上)
- 可变的风格维度(厨房风格、其他装饰物)

输出 `prompts.csv` 供 `main.py` 生成多个不同风格的场景。

### 阶段 2:生成场景
```bash
python main.py +name=eval experiment.csv_path=outputs/eval_run/prompts.csv
```

### 阶段 3:Policy Interface(任务 → 机器人位姿)
```bash
python scripts/robot_eval/policy_interface.py \
    --scene-state outputs/.../house_state.json \
    --dmd outputs/.../house.dmd.yaml \
    --task "Pick a fruit from the bowl and place it on the plate" \
    --output-json robot_commands.json
```

输出格式:
```json
{
  "task": "Pick a fruit...",
  "robot_start_xy": [1.2, -0.5],
  "world_bounds": {"min": [-3, -3, 0], "max": [3, 3, 2.7]},
  "commands": [{
    "action": "pick_and_place",
    "rank": 1,
    "confidence": 0.92,
    "drake_model_name": "bedroom_apple_0",
    "target_position": [0.5, 0.3, 0.85],
    "placement_bounds_min": [0.2, 0.1, 0.85],
    "placement_bounds_max": [0.8, 0.5, 1.0],
    "reasoning": "Apple found on nightstand (precondition met), plate on desk is valid goal"
  }]
}
```

**注意**:SceneSmith 不包含 robot URDF 和 policy。`placement_bounds_min/max` 是给你的 policy 用的采样区域。

### 阶段 4:验证
Robot 执行完任务后,将修改过的 `house.dmd.yaml`(更新了物体位姿)传入验证器:
```bash
python scripts/robot_eval/validate.py \
    --scene-state outputs/.../house_state.json \
    --dmd outputs/.../modified_house.dmd.yaml \
    --task "Pick a fruit from the bowl and place it on the plate"
```

验证器用 Drake 物理查询(签名距离、接触检测)+ VLM 视觉判断,输出每个子要求的得分。

---

## 6. SceneSmith 能做什么 / 不能做什么

### ✅ 能做的

| 场景类型 | 说明 |
|---------|------|
| 卧室 | 床、衣柜、书桌、台灯、挂画、小件(书、闹钟等) |
| 厨房 | 橱柜、餐桌、厨具、水果、杯子等 |
| 办公室 | 桌子、椅子、电脑、文具等 |
| 客厅 | 沙发、茶几、书架、摆件等 |
| 关节物体 | 可开关的柜子、抽屉(来自 ArtVIP 数据集) |
| 桌面操作场景 | 通过 manipuland agent 生成多个小物件,支持 pick-and-place |
| 组合物体 | 叠起来的书(stack)、装了东西的碗(filled_container)、散乱的一堆(pile) |

### ❌ 不能做的

| 类别 | 原因 |
|------|------|
| **柔性物体**(布料、毛绒玩具) | SceneSmith 只生成刚体 SDF。柔性物体需要 FEM/粒子仿真,Drake 支持有限,SceneSmith 不建模 |
| **流体**(倒水、液体) | 同上,流体需要 SPH/粒子系统,不在 SceneSmith 范围内。`house.dmd.yaml` 是刚体 Directives |
| **人体/角色动画** | 无 URDF/骨骼,无动画系统 |
| **室外场景** | 专为室内设计,无地形、植被等 |
| **Robot URDF** | SceneSmith 只生成环境,不包含机器人本体 |
| **Policy / Controller** | 不包含任何 policy,只提供场景 + eval harness |

### 关于柔性物体和流体的正确路径

如果需要:
- **布料**:用 MuJoCo 的 `composite` 元素或 Isaac Sim 的 FEM,把 SceneSmith 生成的刚体场景作为背景
- **流体**:FluidLab、Taichi,SceneSmith 生成杯子/瓶子的 SDF 作为容器几何体
- 可以用 `scripts/export_scene_to_mujoco.py` 先把 SceneSmith 场景转成 MuJoCo XML,再在 MuJoCo 里加入柔性/流体模拟

---

## 7. 本次运行结果

### 场景 1:卧室(Bedroom)

**提示词**:
> A cozy bedroom with a queen bed against the wall, two nightstands with lamps, a wardrobe in the corner, and a small desk with a chair near the window.

**运行时间**:约 1 小时 57 分钟(L40S GPU)

**生成物体**:
| 物体 | 类型 | 位置 (x, y, z) |
|------|------|---------------|
| desk_0 | furniture | (0.79, 1.58, 0.0) |
| nightstand_0 | furniture | (-0.55, -1.73, 0.0) |
| nightstand_1 | furniture | (0.62, -1.73, 0.0) |
| rug_0 | furniture | (-0.01, -0.39, 0.0) |
| wardrobe_0 | furniture | (2.06, -1.53, 0.0) |

**注意**:本次运行中 Hunyuan3D worker 因 CUDA fork 问题未能生成 manipuland(小件物体),house_state.json 只有家具级别的物体。

**输出文件**:
- `combined_house/house.blend` — 201MB Blender 场景
- `combined_house/flythrough.mp4` — 120帧 1280×720 飞行视频
- `combined_house/house.dmd.yaml` — Drake 仿真文件

---

## 8. Demo采集说明

### 什么是 Demo

在这里,"demo" 是指一个 **pick-and-place 任务的完整轨迹**,包含:
- 初始场景状态(物体位姿)
- 末端执行器的完整轨迹(6D位姿序列)
- 夹爪宽度序列
- 任务成功与否

### 本次 Demo 类型

由于 SceneSmith 不含真实 robot,我们采用 **scripted demo**(脚本化轨迹),按照标准 pick-and-place 轨迹规划:

```
Home → Approach(接近物体上方)→ Pre-grasp(下降)→ Grasp(夹爪闭合)
→ Lift(提升)→ Transport(平移)→ Place(放下)→ Release(张开夹爪)
```

### 与真实采集的区别

| 项目 | 本次(脚本化) | 真实采集 |
|------|--------------|---------|
| 轨迹来源 | 几何规划 | robot controller / teleop |
| 物理真实性 | 运动学插值 | 力矩控制 |
| 碰撞避免 | 简单直线轨迹 | 运动规划(RRT等) |
| 接触建模 | 无 | Drake/MuJoCo 物理仿真 |
| 用途 | 演示、可视化 | 训练 policy |

### Demo JSON 格式

```json
{
  "task": "Pick an object from the nightstand and place it on the desk",
  "scene_id": "bedroom_nightstand_to_desk",
  "target_object": "bedroom_nightstand_0",
  "initial_pos": [-0.55, -1.73, 0.5],
  "goal_pos": [0.79, 1.58, 0.85],
  "goal_reference": "bedroom_desk_0",
  "success": true,
  "total_duration_s": 7.5,
  "num_steps": 150,
  "trajectory": [
    {
      "step_id": 0,
      "phase": "approach",
      "end_effector_pos": [0.0, 0.0, 0.8],
      "end_effector_quat_wxyz": [1, 0, 0, 0],
      "gripper_width": 1.0,
      "target_object_pos": [-0.55, -1.73, 0.5],
      "timestamp": 0.0
    },
    ...
  ]
}
```

### 可视化说明

`demo_*_trajectory.png` 包含 4 个子图:
1. **3D 轨迹图**:不同颜色表示不同阶段(接近/抓取/搬运/放置)
2. **俯视图**(XY平面):路径规划可视化
3. **高度-时间曲线**:末端执行器与物体的 Z 轴变化
4. **夹爪宽度-时间曲线**:抓取动作可视化

---

## 9. 常见问题与局限性

### Q: Hunyuan3D-2 生成质量很差怎么办?
**A**: 换 SAM3D 后端(需要 A100/H100,无 gated 访问限制)。Hunyuan3D 官方说明仅作 proof-of-concept,质量明显劣于 SAM3D。

### Q: manipuland 物体没有生成?
**A**: Hunyuan3D worker 在 fork 后 CUDA 重初始化失败。症状:`Worker shutting down. Stats: 279 total, 0 completed, 279 failed`。解决:换用 `strategy: "hssd"` 检索模式,不依赖 CUDA generation。

### Q: LLM 返回 `rs_*` 错误?
**A**: LiteLLM 代理是无状态的,不支持 server-side reasoning 续接。需要设置 `reasoning_effort: none` 并使用自定义 `NvidiaInferenceProvider`(已在本次配置中修复)。

### Q: 一次运行需要多久?
**A**: 单房间约 1-2 小时(L40S, Hunyuan3D)。SAM3D 质量更好但更慢。多 GPU 可以并行生成多个房间。

### Q: 能生成多房间场景吗?
**A**: 可以,在 `scene_prompt` 中描述多个房间,`experiment.num_scenes` 控制变体数量。

### Q: 如何接入自己的 robot?
**A**: 
1. 加载 `house.dmd.yaml` 到 Drake/MuJoCo
2. 用 `policy_interface.py` 获取 task 的目标位姿
3. 用你自己的 robot controller 执行
4. 将结果写回修改后的 `.dmd.yaml`
5. 用 `validate.py` 验证成功率

---

## 10. 复现指南

### 环境要求

- Python 3.11
- CUDA 12.4(编译 custom_rasterizer)
- Blender 5.1(headless,EEVEE 渲染)
- SLURM 集群,L40S GPU(48GB)

### 安装步骤

```bash
# 1. 克隆仓库
git clone https://github.com/nepfaff/scenesmith.git
cd scenesmith
git submodule update --init --recursive

# 2. 创建虚拟环境
uv sync --no-dev

# 3. 安装 Hunyuan3D-2
bash scripts/install_hunyuan3d.sh

# 4. 下载 ArtVIP 数据
huggingface-cli download nepfaff/scenesmith-preprocessed-data \
    artvip/artvip_vhacd.tar.gz --repo-type dataset --local-dir .
mkdir -p data/artvip_sdf
tar xzf artvip/artvip_vhacd.tar.gz -C data/artvip_sdf

# 5. 下载材质
python scripts/download_ambientcg.py --output data/materials
# 下载材质嵌入(从 HF)

# 6. 编译 CUDA 扩展(需要 CUDA toolkit)
# 见 cuda_home/ 目录的说明
```

### 运行

```bash
# 设置环境变量
export OPENAI_API_KEY=<your_key>
export OPENAI_BASE_URL=https://inference-api.nvidia.com/v1
export HF_TOKEN=<your_hf_token>

# 生成场景
python main.py \
    "+name=my_scene" \
    "experiment.scene_prompt=A cozy bedroom with a bed and desk" \
    "openai.model=azure/openai/gpt-5.2" \
    "furniture_agent.asset_manager.backend=hunyuan3d" \
    "furniture_agent.reasoning_effort.generation=none"

# 渲染视频
python scripts/render_flythrough.py \
    --blend outputs/.../combined_house/house.blend \
    --output flythrough.mp4 \
    --blender /path/to/blender

# 采集 Demo
python scripts/collect_demo.py \
    --scene-state outputs/.../house_state.json \
    --dmd outputs/.../house.dmd.yaml \
    --task "Pick the book and place it on the desk" \
    --output-dir demos/
```

### 本次运行的关键 Patch

本次在 NVIDIA 内部集群运行时,在原始代码基础上做了以下修改:

1. **`nvidia_inference_provider.py`**(新增):绕过 `openai-agents` SDK 对 `azure/openai/gpt-5.2` 的前缀解析,直接路由到 `inference-api.nvidia.com`
2. **`base_stateful_agent.py`**:注入自定义 Provider
3. **所有 YAML 配置**:`reasoning_effort: none`,防止 stateful reasoning items
4. **`worker_pool.py`**:限制 CUDA re-init 重试次数(max 3次)
5. **`render_flythrough.py`**(新增):适配 Blender 5.1 新 API(`action.layers[].strips[].channelbags[].fcurves`)