本仓库基于 Google Research 的
language-table 深度改装。
底层的 PyBullet LanguageTable 仿真环境保留,但原有的数据集、训练、教程
流程都已替换为一套新的任务体系:把桌面抽象成 8 格外圈离散位置 的拼图
任务,并提供以下能力:
- 一个 VLM 风格的交互式 benchmark (benchmark/language_interactive_env.py): 在仿真器外封装了 8 格符号化布局、edge 边缘状态模型、单步 JSON 动作以及 目标判定。
- 一个多模式 VLM agent 运行器
(benchmark/script_agent.py):支持
one_shot、multi_step和multi_gen(图像生成回环)三种策略, 对接 DashScope 兼容的对话模型,以及火山引擎 Ark / OmniGen2 图像模型。 - 一个脚本化的 I+T→I 数据采集器
(benchmark/collect_i_t_to_i_data.py):
按照 benchmark 的离散动力学生成
(当前图像, 文本, 下一图像)三元组。 - 几个图像模型的最小验证脚本:Ark Doubao Seedream 与 OmniGen2 LoRA 推理(benchmark/test.py、 benchmark/minimal_ark_image_test.py、 benchmark/test_omnigen2_minimal.sh)。
原版的 language_table/train/、language_table/eval/、
language_table/examples/ 以及 Google Cloud 数据集引用 已不再属于当前
受支持的工作流,只有 language_table/environments/ 下的仿真器还在被
benchmark 代码使用。
language-table/
├── language_table/
│ └── environments/ # PyBullet 仿真器(保留自上游)
│ ├── blocks.py # FIXED_8_COMBINATION 等
│ ├── language_table.py
│ ├── constants.py
│ ├── oracles/ rewards/ utils/ assets/
│ └── ...
├── benchmark/
│ ├── language_interactive_env.py # VLMOuterEdgeBenchmark 与离散布局模型
│ ├── script_agent.py # one_shot / multi_step / multi_gen agent
│ ├── collect_i_t_to_i_data.py # 脚本化 I+T->I 数据集生成
│ ├── python_env_bootstrap.py # sys.path / typing_extensions 兜底
│ ├── test.py # Ark Seedream 单步最小测试
│ ├── minimal_ark_image_test.py # Ark smoke test,附像素差异诊断
│ ├── test_omnigen2_minimal.sh # OmniGen2 inference.py 包装脚本
│ ├── runs/ # 每次 agent 运行的输出(已 gitignore)
│ └── dataset/ # 采集得到的 I+T->I 数据集(已 gitignore)
├── requirements.txt
├── requirements_static.txt
└── setup.py
仿真器使用 blocks.LanguageTableBlockVariants.BLOCK_8,固定 8 个物体:
red moon, red pentagon, blue moon, blue cube,
green cube, green star, yellow star, yellow moon
桌面被离散化为 8 个 base 位置,每个 base 位置外侧再对应一个 edge 边缘状态:
base 位置: top left, top center, top right,
center left, center right,
bottom left, bottom center, bottom right
edge 状态: 上述每个位置对应一个 "<位置> edge"
内部: center
场景的离散状态由"每个 base 位置上是什么物体"以及"最多一个物体处在某个 edge 状态"共同描述。
benchmark 接受两种单步自然语言动作:
move <物体> to <物体>:把源物体推到目标物体附近。目标物体保持不动, 源物体进入目标物体所在位置的 edge 状态,而不是占据目标的 base 位置。move <物体> to <位置>:把源物体推到指定的 base 位置;该位置必须为空, 否则动作无效。
诸如 upper left、lower right、top middle 之类的别名都可接受,详见
benchmark/language_interactive_env.py
中的 EDGE_SLOT_ALIASES。
目标布局把 8 个物体一一放到 8 个 base 位置上(一一对应)。当每个物体的 XY 坐标都落在对应 base 位置的容差范围内时,视为达成目标。
benchmark 继承了上游的 Python 依赖(PyBullet、TF/TFDS、gym、tf_agents
等),再加上 VLM 与 Ark 调用所需的 OpenAI Python SDK:
python3 -m venv ./ltvenv
source ./ltvenv/bin/activate
pip install -r ./requirements.txt
pip install openai
export PYTHONPATH=${PWD}:$PYTHONPATHrequirements_static.txt(上游训练用的钉版依赖)仅作参考保留,benchmark
与数据采集器都 不需要 它。
需要使用的 API 凭据:
DASHSCOPE_API_KEY:benchmark/script_agent.py 中 VLM 策略所需。ARK_API_KEY:火山引擎 Ark Doubao Seedream 图像模型所需 (benchmark/test.py、 benchmark/minimal_ark_image_test.py 以及script_agent.py的multi_gen模式)。
benchmark/script_agent.py 与 benchmark/test_omnigen2_minimal.sh 里的 OmniGen2 路径默认指向本地 NAS / conda 环境;在其他机器上运行时请 通过 CLI 参数或环境变量覆盖。
不调用任何 VLM,跑一个确定性 benchmark:
python benchmark/language_interactive_env.py --seed 0 --layout_only这会构造 VLMOuterEdgeBenchmark,用 seed=0 采样初始布局与目标布局,
渲染图像并打印离散状态。--layout_only 表示跳过 PyBullet 物理 rollout,
仅根据维护的离散布局判定动作 —— 适合快速调 prompt、调策略,不消耗 GPU
或物理仿真时间。
export DASHSCOPE_API_KEY=...
python benchmark/script_agent.py \
--mode one_shot \
--seed 0 --num_problems 5 \
--model qwen3.6-plus \
--max_actions 12 \
--layout_only三种模式:
one_shot:VLM 同时看到初始图与目标图,一次性输出完整动作序列{"actions": [...]},benchmark 再依次执行。multi_step:每一步把"当前图 + 目标图"喂给 VLM,VLM 返回下一步单个 动作;直到达成目标或用完--max_actions。multi_gen:与multi_step类似,但反馈给 VLM 的"当前图"由图像模型 根据上一帧加动作文本生成(默认 Ark Seedream,亦可通过--omnigen_*参数切换到 OmniGen2)。
输出落到 benchmark/runs/<时间戳>/problemN/<mode>/,包含:
initial/、goal/:渲染图与 JSON 布局。prompt.txt、input_manifest.json、vlm_request.json、vlm_response.json、model_response_raw.txt、reasoning_summary.txt。steps/step_XXX/:每步的请求/响应、动作前后的渲染图,以及result.json(含成功标志与离散布局快照)。execution_summary.json:最终布局、已执行动作以及goal_reached。
加 --dry_run 可以只生成 prompt 与输入素材,不实际调用 VLM。
--layout_only 适合快速迭代。把它去掉就会让每一个高层动作都驱动真实
的 PyBullet 控制器:
python benchmark/script_agent.py --mode multi_step --seed 0 \
--low_level_step_limit 350底层用的是上游的 oracle pushing 控制器
(benchmark/language_interactive_env.py
中的 GroundTruthLanguageOracle)。如果控制器在
--low_level_step_limit 仿真步内完不成动作,会把该动作判为失败。
python benchmark/collect_i_t_to_i_data.py \
--num_samples 1000 \
--seed 0--num_samples 必须是 rollout 长度(5)的整数倍。每个 rollout:
- 恰好包含 5 个动作。
- 任意时刻最多只有一个物体处在 edge 状态。
- 在某个物体处在 edge 时,对应 base 位置上的物体被锁定不能动;下一步 要么把另一个物体移到那个"唯一空的 base 位置",要么把 edge 上的物体 移回该空位。
输出结构:
benchmark/dataset/<时间戳>/
├── dataset_index.json # 全局清单:逐样本路径、文本、动作、布局
└── rollout_XXXXXX/
├── rollout.json
└── samples/
└── sample_XXXXXX/
├── input.png # 动作前的场景
├── output.png # 动作后的场景
├── text.txt # 喂给 I+T->I 模型的完整文本提示
└── sample.json # 动作、当前布局(含 edge 状态)、元数据
text.txt 同时包含完整的任务规则与本步要执行的动作,所以每个样本都是
一个自包含的 (图像, 文本) -> 图像 训练样例。
- benchmark/test.py:给定初始图、目标图、当前图与 一句动作文本,调用一次 Ark Doubao Seedream。适合调 prompt。
- benchmark/minimal_ark_image_test.py: 更精简的 Ark 调用,会记录输入/输出的 sha256 与像素差异 —— 一旦模型 原样回吐输入,就能立刻发现。
- benchmark/test_omnigen2_minimal.sh:
调用 OmniGen2 自带的
inference.py,使用multi_gen模式所用的 LoRA。 可通过环境变量OMNIGEN2_DIR、OMNIGEN2_PYTHON、MODEL_PATH、LORA_PATH、INPUT_IMAGE、OUTPUT_DIR覆盖默认路径。
benchmark/runs/、benchmark/dataset/以及历史快照20260513_121527_848707/都已 gitignore。language_table/train/、language_table/eval/、language_table/examples/沿用自上游,当前没有任何入口在使用, 仅作为参考代码保留。- 上一版 README 里提到的 GCS 数据集路径与 BC+ResNet 检查点都与本仓库的 现有工作流无关。
Apache 2.0(继承自上游 google-research/language-table),见
LICENSE。