scenesmith / README.md
yuq-nv's picture
Add bedroom scene, demos, Chinese documentation, and scene type guide
69983a1 verified
|
Raw
History Blame
24 kB
# 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`)