TAB 1. Stage 1 hidden reference scene generation, explained
写给 Yi Ge:在把 LLM generation harness 架到 Viet 的 stage1_reference 上之前,先把它的每一步、每个文件、每个物理量看清楚。所有数字都带来源;代码里没有的东西标 "not in the code"。
路径缩写:S1 = code/private/stage1_reference(= 分支 step_1_hidden_reference_scene_generation @ e5dcd30,S1/ARCHIVED_FROM.txt);KD = S1/kitchen_drawer_pull;PKG = code/github-code/src/simgenprobe(main);HO = code/private/_asset/kitchen_drawer_pull_handoff_extracted/kitchen_drawer_pull_handoff;USDA = HO/scene/scene.usda(Viet 交付的已 build 好的 kitchen 场景,2627 行);template = code/github-code/data/development_scenes/development_00/scene.json;RUNS = code/runs/stage1(我们的集群 上的 Stage 1 输出);ENV62 = $ENVS/simgenprobe(Isaac Sim 6.2.0.0)。seed 848 的值来自本机用 conda-envs/848x 跑 S1/sample.py 两次得到的 ground_truth.json(两次字节相同:scene.json sha256 d6d0586b…,scene_physics.usda b7499786…);seed 2611 的值用 KD/truth.py 重算并与 USDA 逐行核对;我们的集群 实测来自 smoke job 3506650(RUNS/logs/stage1-smoke-3506650.log,<node>,L40S,driver 615.71.09,2026-11-04 18:56:42 到 19:10:09 ET,COMPLETED)。
19:3x ET 更新(主会话):第 9 节的 WebRTC 插件问题已修并复查通过;第 6、10 节 OptiX 的原因查明、
verify_views已 PASS;交互 viewer 的 我们的集群 版 job 脚本已写好(code/private/tools/jobs/stage1_live_viewer.sbatch)。
1. 一句话 + 总 pipeline 图
Stage 1 把一个 seed 变成一个 θ(s) 完全已知、在 Isaac Sim 6.2 里 build 过、validate 过、render 过的 hidden reference scene,然后只把 public 的那一层(几何、颜色、材料名、两张 view、相机内参)交给 agent;θ(s)、ground_truth.json、所有 USD 永远留在 hidden 目录(S1/README.md:20-39,KD/README.md:63-69)。
两个 family 的流程并排如下。每个 node 有 id、短 label、sublabel(输入/输出)、lane(CPU 或 GPU)、zone(hidden / bundle / agent);optional: true 的 node 画成浅色;edge 带动词;zone 变化处就是 hidden/public 边界(画成虚线)。
{
"lanes": ["CPU", "GPU"],
"zones": {
"hidden": "Stage 1 only: ground truth, theta, USD, validation, renders",
"bundle": "evaluation only, contains theta (not agent-safe)",
"agent": "agent-safe observation package"
},
"rows": [
{"family": "tabletop held_out_00 (seed 848)", "nodes": [
{"id": "t_in", "label": "config + seed", "sublabel": "S1/config.yaml (seed 848), template development_00/scene.json, PKG/data/materials/materials.json", "lane": "CPU", "zone": "hidden"},
{"id": "t_sample", "label": "sample", "sublabel": "out: scene.json, scene_physics.usda, ground_truth.json", "lane": "CPU", "zone": "hidden"},
{"id": "t_build", "label": "build_scene V1-V9", "sublabel": "URDF import, compose scene.usda, reload, validate | out: robot/panda.usd, scene.usda, validation.json, ground_truth.json(+robot, isaac)", "lane": "GPU", "zone": "hidden"},
{"id": "t_tools", "label": "validate_existing / inspect_stage", "sublabel": "optional: V1-V9 again on a copy; read-only inventory of a saved scene.usda | for comparing Isaac versions", "lane": "GPU", "zone": "hidden", "optional": true},
{"id": "t_render", "label": "isaac_render", "sublabel": "PathTracing 64 spp x 8 subframes, warmup 64 (CLI defaults) | out: renders/front_right.png, top_down.png, *_instances.npz, render.json", "lane": "GPU", "zone": "hidden"},
{"id": "t_verify", "label": "verify_views", "sublabel": "exposure, noise, cube/table/robot/room masks | out: renders/verify.json", "lane": "CPU", "zone": "hidden"},
{"id": "t_finalize", "label": "finalize", "sublabel": "stage_checks (62 items), theta read-back, Isaac validation passed, verified views, ReferenceStore contract, separation, MuJoCo settle | out: finalize.json", "lane": "CPU", "zone": "hidden"},
{"id": "t_bundle", "label": "held_out/held_out_00/", "sublabel": "scene.json (theta inside), manifest.json, views/front_right.png, views/top_down.png", "lane": "CPU", "zone": "bundle"},
{"id": "t_observe", "label": "simgenprobe observe", "sublabel": "S1/observe_held_out.yaml: verify_lock, ReferenceStore load, public_projection, write_base_text, disclose, leak_scan | out (harness only, outside package/): <out_dir>/record.json", "lane": "CPU", "zone": "bundle"},
{"id": "t_obs", "label": "observation package/", "sublabel": "base_text.md, disclosed_parameters.json, cameras.json, manifest.json, views/", "lane": "CPU", "zone": "agent"}
]},
{"family": "kitchen kitchen_drawer_pull_00 (seed 2611)", "nodes": [
{"id": "k_simready", "label": "simready", "sublabel": "download 4 SimReady packages, sha256 vs BOM | out: simready_cache/, simready_assets.json", "lane": "CPU", "zone": "hidden"},
{"id": "k_in", "label": "config + seed + screenshot", "sublabel": "KD/config.yaml (seed 2611), references/target_scene.png, landmarks.json", "lane": "CPU", "zone": "hidden"},
{"id": "k_camera", "label": "camera", "sublabel": "optional: fit eye, angles, focal, cabinet size, tile from landmarks.json (72 starts) | stdout only, must equal config.yaml", "lane": "CPU", "zone": "hidden", "optional": true},
{"id": "k_sample", "label": "sample", "sublabel": "theta, layout, pull plan | out: scene_spec.json, ground_truth.json, scene_physics.usda", "lane": "CPU", "zone": "hidden"},
{"id": "k_build", "label": "build K1-K17", "sublabel": "URDF import, compose, home pose, validate, rollout | out: robot/panda.usd, scene.usda, validation.json, demo/pull_rollout.npz, demo/states.json", "lane": "GPU", "zone": "hidden"},
{"id": "k_render", "label": "render", "sublabel": "states A, B x views reference_match, workspace | out: renders/{benchmark,demo}/*.png, *_instances.npz, render.json", "lane": "GPU", "zone": "hidden"},
{"id": "k_compare", "label": "compare", "sublabel": "optional: state B render vs target_scene.png, chamfer | out: comparison/iter_NN/ (record only)", "lane": "CPU", "zone": "hidden", "optional": true},
{"id": "k_verify", "label": "verify_views", "sublabel": "provenance, staleness, exposure, noise, visibility, landmarks | out: renders/verify.json", "lane": "CPU", "zone": "hidden"},
{"id": "k_finalize", "label": "finalize", "sublabel": "gates validation, renders, files, stage; leak_scan | out: finalize.json", "lane": "CPU", "zone": "hidden"},
{"id": "k_bundle", "label": "bundle/kitchen_drawer_pull_00/", "sublabel": "scene_spec.json (theta inside), manifest.json, views/ (state A), demo/reference_match.png (state B), ATTRIBUTION.md", "lane": "CPU", "zone": "bundle"},
{"id": "k_obs", "label": "observation/kitchen_drawer_pull_00/", "sublabel": "base_text.md, cameras.json, views/", "lane": "CPU", "zone": "agent"}
]}
],
"edges": [
{"from": "t_in", "to": "t_sample", "label": "seeds PCG64(848), fills the template"},
{"from": "t_sample", "to": "t_build", "label": "reads ground_truth.json + scene_physics.usda"},
{"from": "t_build", "to": "t_tools", "label": "optional: re-validate a copy, inventory the stage", "optional": true},
{"from": "t_build", "to": "t_render", "label": "opens scene.usda, poses the settled cube in the session layer"},
{"from": "t_render", "to": "t_verify", "label": "checks renders against ground_truth.json"},
{"from": "t_verify", "to": "t_finalize", "label": "verify.json must pass, hashes pinned"},
{"from": "t_finalize", "to": "t_bundle", "label": "copies scene.json, re-encodes verified views, writes manifest.json"},
{"from": "t_bundle", "to": "t_observe", "label": "ReferenceStore(held_out_root) loads, contract G-B1 to G-B10"},
{"from": "t_observe", "to": "t_obs", "label": "writes package/, leak scan fails closed"},
{"from": "k_simready", "to": "k_sample", "label": "cache required even to sample"},
{"from": "k_in", "to": "k_camera", "label": "optional: landmarks.json -> camera fit", "optional": true},
{"from": "k_in", "to": "k_sample", "label": "seeds PCG64(2611); screenshot sha256 must match"},
{"from": "k_sample", "to": "k_build", "label": "reads ground_truth.json + scene_physics.usda"},
{"from": "k_build", "to": "k_render", "label": "link poses of states A and B into the session layer"},
{"from": "k_render", "to": "k_compare", "label": "optional: state B render vs screenshot", "optional": true},
{"from": "k_render", "to": "k_verify", "label": "4 input hashes pinned in render.json"},
{"from": "k_verify", "to": "k_finalize", "label": "verify.json must pass and not be stale"},
{"from": "k_finalize", "to": "k_bundle", "label": "atomic swap after all gates"},
{"from": "k_finalize", "to": "k_obs", "label": "handwritten base_text + cameras.json, leak_scan"},
{"from": "k_simready", "to": "k_build", "label": "scene_physics.usda references ../simready_cache/... (written at sample time); scene.usda is its Export copy, so the cache must stay beside the scene"}
],
"boundary": {"between_zones": ["hidden", "bundle", "agent"], "note": "the only files an agent may ever receive are the agent-zone nodes; record.json stays with the harness"}
}
2. 输入与输出
Tabletop(held_out_00):hidden 文件在 <output_root>/stage1/held_out_00/,bundle 在 <output_root>/held_out/held_out_00/,output_root = ../runs/stage1(S1/config.yaml:4,S1/config.py:106-118,相对 stage1_reference/ 的父目录)。
| 步骤 | 设备 / 时间 | 读 | 写(格式) | zone |
|---|---|---|---|---|
python -m stage1_reference.sample (S1/sample.py:270-298) |
CPU, 秒级 (README.md:50) |
config.yaml,template scene.json,materials.json (version "0.1") |
scene.json(SceneSpecification schema 0.1,canonical,6 位有效数字);scene_physics.usda(ASCII USD,PKG 的 write_usd,无 robot,usd_file.py:548 robot_included: False);ground_truth.json(sorted keys,S1/ground_truth.py:281-288) |
hidden |
isaac/build_scene.py |
GPU 节点,Kit isaaclab.python.headless.kit,CPU PhysX,约 2 min (README.md:52);我们的集群 实测 job 3506650 V1 到 V9 全 PASS,STAGE1_BUILD passed=True steps=2761(log :100-109) |
ground_truth.json,scene_physics.usda,Isaac exts 里的 URDF (config.yaml:43) |
robot/panda.usd(usda 文本);scene.usda;validation.json(V1 到 V9);ground_truth.json 补上 robot.links/joints、isaac record、文件 hash (build_scene.py:104-146) |
hidden |
render/isaac_render.py |
GPU RTX,约 3 min (README.md:54);我们的集群 实测 render exit 0,render.json passed: true(log :1557),但 OptiX denoiser 没起来(第 6 节) |
scene.usda,validation.json 的 settled cube pose |
renders/<view>.png,renders/<view>_instances.npz(instance id 到 prim path),renders/render.json |
hidden |
render/verify_views.py |
CPU | renders,ground_truth.json,validation.json |
renders/verify.json(记录 render.json 与每张 png 的 sha256) |
hidden |
finalize.py |
CPU,需要 MuJoCo (README.md:59 MUJOCO_GL=egl) |
以上全部 | bundle:scene.json(含 θ)、manifest.json、views/front_right.png、views/top_down.png(write_png 重编码);finalize.json |
bundle |
simgenprobe observe --config S1/observe_held_out.yaml |
CPU;view 缺失时需要 engine (PKG/input_conditions/build.py:143-148) |
bundle(经 ReferenceStore,held_out_root) |
package/:base_text.md、disclosed_parameters.json、cameras.json、manifest.json(scene_id、input_combination、lock_hash、renderer、files 的 sha256、labels、described_objects、fixtures、units、observation_sha256,build.py:217-230)、views/;package/ 之外的 <out_dir>/record.json(Disclosure、label 到 reference name 的映射,harness 侧,build.py:252;文件名 observation.py:41-47) |
agent(package/);harness(record.json) |
Kitchen(kitchen_drawer_pull_00):output_root = ../runs/stage1/kitchen_drawer_pull(KD/config.yaml:5);hidden 在 <root>/kitchen_drawer_pull_00/,bundle 在 <root>/bundle/<scene>/,observation 在 <root>/observation/<scene>/,cache 在 <root>/simready_cache/(KD/config.py:254-272)。
| 步骤 | 设备 / 时间 | 读 | 写 | zone |
|---|---|---|---|---|
kitchen_drawer_pull.simready (KD/simready.py) |
CPU + 网络 | config.yaml:120-126 的 4 个 package |
simready_cache/<path>/…(跳过 .thumbs/、.simready/(package.json 单独取)、除 .mdl 外的 source_assets/,以及不在 BOM 里的已发布文件(LICENSE.txt 除外),simready.py:39-47, 99-106, 121-124),simready_assets.json;每个文件 sha256 对 BOM,不符即 raise (:133-136);约 0.8 GB (README.md:36) |
hidden |
kitchen_drawer_pull.camera(可选) |
CPU | references/landmarks.json |
stdout(fit 结果;test 保证与 config.yaml 一致,tests/test_kitchen_drawer_pull.py:56-69) |
无 |
kitchen_drawer_pull.sample (KD/sample.py:209-247) |
CPU, 秒级 | config.yaml,target_scene.png(sha256 必须等于 config.yaml:7,sample.py:216-218),SimReady cache |
scene_spec.json(family schema kitchen_drawer_pull/0.1,sample.py:39),ground_truth.json(θ、fixed、parameter table、mass properties、plan、camera fit、SimReady provenance、软件版本、hash),scene_physics.usda |
hidden |
isaac/build.py |
GPU,约 2 min (README.md:45),CPU PhysX (session.py:69) |
ground_truth.json,scene_physics.usda,URDF |
robot/panda.usd,scene.usda,validation.json(K1 到 K17),demo/pull_rollout.npz(每步 time、phase、q、targets、slide、slide_speed、tcp,加 robot_dofs,KD/isaac/validate.py:91-99, 773),demo/states.json(state A、B 的 link poses);ground_truth.json 补 robot、isaac、hash |
hidden |
isaac/render.py |
GPU RTX,约 1.5 min (README.md:47) |
scene.usda,demo/states.json |
renders/benchmark/{reference_match,workspace}.png(state A),renders/demo/…(state B),*_instances.npz,render.json(pin 4 个输入文件的 sha256,render.py:36,43-45) |
hidden |
kitchen_drawer_pull.verify_views |
CPU | renders,ground_truth.json |
renders/verify.json |
hidden |
kitchen_drawer_pull.compare(可选) |
CPU | state B 的 render,screenshot | comparison/iter_NN/,只记录 |
hidden |
kitchen_drawer_pull.finalize |
CPU | 以上全部 | bundle/<scene>/(scene_spec.json、manifest.json、views/、demo/reference_match.png、ATTRIBUTION.md),observation/<scene>/(base_text.md、cameras.json、views/),finalize.json;先在临时目录建好再原子换入 (finalize.py:295-302, 413-414) |
bundle + agent |
三点提醒:(1) HO/scene/scene.usda 自己就带全部物理值(HANDOFF.md:102-104),所以 HO/scene 算 hidden。(2) archive 不能原地跑,REPOSITORY_ROOT 是 stage1_reference/ 的父目录(S1/config.py:13);code/private/data 现在是 symlink → ../github-code/data(2026-11-04 18:53 ET 建),所以 S1/config.yaml:5 的 data/development_scenes/development_00/scene.json 直接能解析,不用再覆盖 template_scene。(3) 我们的集群 上的 Isaac 步骤走 S1/isaac_python_cluster.sh(本地文件,不在 e5dcd30 里;:11 指向 ENV62,:21 把 github-code/src 和 code/private 放进 PYTHONPATH),提交入口 code/private/tools/jobs/stage1_smoke.sbatch(scavenger,gpu:nvidia_l40s:1,:8-10)。
3. 两个场景族
Tabletop:table、一个 cube、桌下 drawer、Panda、visual-only 的房间;几何全部来自 template,只有 cube 和 θ 是抽的。
| 物体 | 几何(m) | 物理角色 | 来源 |
|---|---|---|---|
| table | top 0.8 x 0.6 x 0.04,中心 (0, 0, 0.38),上表面 z = 0.40;4 条腿 0.05 x 0.05 x 0.36,inset 0.04 | static collider,table material (friction = θ,restitution 0.0) | template:17-43 |
| drawer | box 0.3 x 0.25 x 0.08,closed center (0.25, 0, 0.315);prismatic 沿 +x,range [0, 0.25];handle 0.02 x 0.1 x 0.015 在前脸中央(link 中心 +x 0.16 处,usd_file.py:443) |
articulation:base 1 kg / inertia 0.01 FixedJoint 到 world,link mass = θ;handle 没有 CollisionAPI(usd_file.py:438-445,只有传了 material 才加 collider,:300-302) |
template:45-91;PKG/constants.py:238-239 |
cube item_0 |
a x a x a,a ∈ [0.045, 0.07];中心 x ∈ [-0.20, 0.25],y ∈ [-0.10, 0.10];yaw ∈ [-30, 30] deg;放在 z = 0.40 + a/2 + 0.001(spawn gap constants.py:71) |
rigid body,mass/friction/restitution = θ | S1/config.yaml:8-13 |
| robot | Panda,base (0.40, -0.45, 0.2),yaw 90 deg,站在 0.20 x 0.20 x 0.2 的 pedestal 上;fingers 闭合(finger_opening_m: 0.0,用指外侧推) |
11 rigid links(asset 质量,共 18.00 kg),pedestal 只是 collider | template:94-110;constants.py:78-80;S1/config.yaml:48 |
| ground | 20 x 20 x 0.1 slab,上表面 z = 0 | friction 0.5,restitution 0.0 | constants.py:61-62, 82-83 |
| room | 两面墙 x = -2.225 和 y = -2.225(5.4 x 0.05 x 2.6);5 个 prop:cabinet、bin、plant_pot、2 个 box_stack,落在离原点 0.9 到 2.0 m 的环上 | 纯 visual:无 collision、无 rigid body、无 mass(stage_checks.py:291-308 断言) |
S1/environment.py:117-141;config.yaml:22-40 |
| cameras | front_right eye (0.976765, 0.926765, 1.16772) 看 (0.05, 0, 0.25);top_down eye (0, 0, 1.45) 看 (0, 0, 0.4);1280 x 720,fovy 40 deg,fx = fy = 989.09 px,cx = 640,cy = 360 |
另两台 front_left、side 也写进 USD 但不 render |
template:189-254;seed 848 ground_truth.json cameras |
Kitchen:一个 procedural 的白色 island、一个带 bar handle 的 drawer、站在地上的 Panda、4 件 SimReady 背景家具。世界系:island 前脸是 x = 0,柜体占 x ∈ [-0.669, 0],y 居中,地面 z = 0(KD/layout.py:3-6)。
| 物体 | 几何(m) | 物理角色 | 来源 |
|---|---|---|---|
| island carcass | 宽 0.930(y)x 深 0.669(x)x 高 0.86,板厚 0.018;countertop 0.04 厚,上表面 z = 0.90,x ∈ [-0.669, +0.025](前脸 overhang 0.025,后面没有),y 两侧各 overhang 0.025 | 10 个 static collider:side_left/right、back、bottom、divider、top_rail、mid_rail、countertop、door_left/right;无 rigid body | KD/config.yaml:12-31;layout.py:149-192(countertop :170-180);finalize.py:186 "overhangs the front and the sides";USDA island colliders |
| drawer opening | z ∈ [0.66, 0.85](高 0.19),top rail 0.010,mid rail 0.06;门板 z ∈ [0.012, 0.597](plinth 0.012;z_doors_top 0.60 减 door_gap 0.003) |
layout.py:146-148, 183-184(算出);config.yaml:23-24 |
|
| drawer link | link 原点 = 前脸外表面中心,闭合时 (0, 0, 0.755);front 0.019 x 0.888 x 0.184;box 深 0.50、宽 0.870、高 0.12、壁 0.012;box 后壁在 x = -0.519 | rigid body,8 个 box collider(front、box_bottom/left/right/back 用 drawer material;handle_bar、两个 post 用 handle material) | layout.py:232-301;usd_scene.py:191-213 |
| handle | bar 0.014 x 0.16 x 0.014,中心离前脸 0.035;两个 post 0.028 x 0.012 x 0.012 在 y = ±0.068;闭合时 bar 中心 world (0.035, 0, 0.755) | collider(handle friction = θ) | config.yaml:42-47 |
| drawer_slide | prismatic +x,limits [0, 0.40];闭合时最小间隙 3 mm(front 对 side_left),全开 6 mm(box_bottom 对 divider);全开时 box 还有 0.119 留在柜内 | force drive,stiffness 0,damping = θ;joint-axis friction effort = θ | config.yaml:41;layout.py:331, 341;USDA:1258-1277 |
| robot | base (0.655, -0.026, 0),yaw 180 deg,直接站在地上;home q [0, -0.6, 0, -2.6, 0, 2.0, 0.7854] rad,TCP 在 (0.2938, -0.026, 0.3452);fingers 开 0.04 | 同 tabletop 的 asset 与 gains | config.yaml:65-75;USDA:2450-2456 |
| floor | 12 x 12 x 0.1 slab,上表面 z = 0;tile 0.576 pitch,grout 0.024,42 条 grout 条 | floor material 0.8 / 0 | usd_scene.py:327-339;config.yaml:85-91 |
| cameras | reference_match eye (1.93489, -1.63035, 1.52610),forward (-0.658076, 0.616647, -0.432068),roll 4.0697 deg,fovy 29.2663,1424 x 1124,fx = fy = 2152.4 px(focal 31.6745 mm);workspace eye (1.45, 1.35, 1.55) 看 (-0.119, 0.006, 0.169),fovy 38.4,1280 x 960,fx = fy = 1378.4 px |
config.yaml:93-107;USDA:1284-1309 |
|
| lights | dome 220;distant key 1600,angle 12 deg,方向 (-0.35, 0.45, -0.82);rect fill 3000,1.6 x 1.2 在 (0.4, -0.2, 2.6) | config.yaml:109-117;USDA:1317-1335 |
|
| background | back_counter Counter_A01 在 (-1.20, 0.20, 0);right_cabinet Cabinet_B01 (-0.357, 2.165, 0) yaw -76;wall_cabinet Cabinet_D01 (-0.44, 2.495, 1.30) yaw -76;dishwasher Dishwasher_A01 (0.31, 2.33, 0) yaw -76 | 物理全关(见第 6 节) | config.yaml:120-126;USDA:1345-2448 |
4. 物理属性 θ(s)
Tabletop 的 θ(s) 就是我们 PKG/scene_specification/physical_parameters.py:47-60 定义的那 7 项,一项不多一项不少;评分方式 log(mass、damping、friction)或 absolute(contact_friction、restitution)(:24-29)。
| 参数 | θ key (owner, quantity) | 单位 | 分布 | seed 848 值 | 抽样代码 | 区间出处 | 我们的 θ 覆盖 |
|---|---|---|---|---|---|---|---|
| 桌面摩擦 | (table, contact_friction) | 1 | U[0.2, 0.6] | 0.419825 | S1/sampler.py:198-199 |
materials.json:214-220 |
是 (:58) |
| 抽屉质量 | (drawer, mass) | kg | U[0.4, 1.2] | 0.671937 | :201 |
:221-227 |
是 (:59) |
| 滑轨阻尼 | (drawer_slide, joint_damping) | N s/m | U[2.0, 8.0] | 2.22435 | :202-204 |
:228-234 |
是 (:56) |
| 滑轨摩擦 | (drawer_slide, joint_friction) | N | U[0.3, 1.2] | 0.421894 | :205-207 |
:235-241 |
是 (:57) |
| cube 质量 | (item_0, mass) | kg | density x a^3,density U[class range] | 0.253765 = 1023.23 x 0.0628279^3 | :213, 228 |
class row | 是 (:51) |
| cube 摩擦 | (item_0, contact_friction) | 1 | U[class range](abs_plastic [0.2, 0.5]) | 0.339249 | :229-231 |
materials.json:12-17 |
是 (:52) |
| cube 弹性 | (item_0, restitution) | 1 | U[class low, min(class high, 0.8)](abs_plastic [0.4, 0.7]) | 0.64654 | :232-234;cap constants.py:69 |
:18-23 |
是 (:53) |
五个 cube 材料类(config.yaml:13;glass、rubber、steel 在材料表里但不在 cube 类里);质量范围是 a ∈ [0.045, 0.07] 推出的(我的算术)。0.8 的 restitution cap 永远不起作用(最高上界 0.7)。
class (materials.json 行) |
density kg/m^3 | contact friction | restitution | 隐含 mass kg |
|---|---|---|---|---|
| wood (186-211) | 400 到 900 | 0.25 到 0.6 | 0.3 到 0.6 | 0.0365 到 0.309 |
| abs_plastic (4-29) | 1020 到 1080 | 0.2 到 0.5 | 0.4 到 0.7 | 0.0929 到 0.370 |
| cardboard (56-81) | 100 到 700 | 0.3 到 0.6 | 0.1 到 0.3 | 0.00911 到 0.240 |
| aluminum (30-55) | 2640 到 2810 | 0.25 到 0.6 | 0.4 到 0.7 | 0.241 到 0.964 |
| foam (82-107) | 30 到 120 | 0.5 到 1.0 | 0.1 到 0.4 | 0.00273 到 0.0412 |
Tabletop 里不进 θ 的其它量:
| 类别 | 量 | seed 848 值 | agent 可见 | 来源 |
|---|---|---|---|---|
| 抽样 | material class | abs_plastic(5 类等概率) | public | sampler.py:210 |
| 抽样 | edge a | 0.0628279 | public | config.yaml:9;sampler.py:212 |
| 抽样 | 中心 xy,yaw | (-0.0469382, -0.0764218),-26.7965 deg | public | config.yaml:10-12;sampler.py:220-224 |
| 抽样 | density | 1023.23 kg/m^3(canonicalize 重算为 mass/volume,canonical.py:33-41) |
hidden | sampler.py:213 |
| 固定 | table restitution | 0.0 | hidden | template:43 |
| 固定 | drawer contact_friction / restitution | 0.5 / 0.0 | hidden | template:63-64 |
| 固定 | ground friction / restitution | 0.5 / 0.0 | hidden | constants.py:61-62 |
| 固定 | robot 接触材料 | friction 0.5,restitution 0.0 | hidden | S1/isaac/compose.py:32-35 |
| 固定 | arm drive;finger drive;gravity | 400 N m/rad,80 N m s/rad;2000 N/m,100 N s/m;disable_gravity: true |
hidden | S1/config.yaml:44-49 |
| asset 自带 | Panda link 质量 | link0 2.8142028、link1 2.3599997、link2 2.3795187、link3 2.6498823、link4 2.6948018、link5 2.9812818、link6 1.1285807、link7 0.40529126、hand 0.5583305、每指 0.014055223 kg,合计 18.00 kg | hidden | HO/scene/robot/panda.usd:782, 1102, 1138, 1174, 1297, 1420, 1518, 1966, 2196, 2344, 2444(同一 URDF 导出;USDA 本身只有 drawer base 的 1 kg :1095 和 drawer 的 2.1217 :1120) |
| asset 自带 | drive maxForce;maxJointVelocity | joint1 到 4 为 87,joint5 到 7 为 12,fingers 20;124.61832 deg/s(joint1 到 4)、149.54198 deg/s(joint5 到 7)、0.2 m/s(fingers) | hidden | panda.usd:2801-2975(87、12)、:3016, 3041(20);:2815、:2931、:3030 |
| 派生 | cube inertia diagonal | m a^2/6 = 1.66950e-4 kg m^2 | hidden | uniform_solid_inertia;ground_truth.json |
| 派生 | drawer inertia;articulation base | box 公式;1 kg、0.01 kg m^2 | hidden | usd_file.py:423;constants.py:238-239 |
| 派生 | V8 slip force | μmg = 1.0451 N,μ = max(0.339249, 0.419825) | validate.py:516-519 |
Kitchen 的 θ(s) 有 5 项(KD/truth.py:20-47 THETA_ORDER),每项 rng.uniform 后取 6 位有效数字(:50-61);USD 里写的就是取整后的值,PhysX 必须按这个值读回来(:3-4)。
| 参数 | θ key | 单位 | 区间 (KD/config.yaml) |
seed 2611 值 | USD 属性 (USDA 行) |
我们的 θ kind (truth.py) |
|---|---|---|---|---|---|---|
| 抽屉质量 | drawer.mass | kg | [0.8, 2.5] (:52) |
2.1217 | /World/Kitchen/Drawer/drawer physics:mass (:1120) |
mass (:21);但在我们的 fixture range [0.4, 1.2] 之外(materials.json:221-227;contract.py:504-514 G-B3 会拒) |
| 滑轨阻尼 | drawer_slide.joint_damping | N s/m | [2.0, 10.0] (:53) |
2.80644 | drawer_slide drive:linear:physics:damping (:1262) |
joint_damping (:28);区间比我们的 [2.0, 8.0] 宽(materials.json:228-234),这个值在内 |
| 滑轨摩擦 | drawer_slide.joint_friction | N | [0.5, 3.0] (:54) |
2.13293 | physxJointAxis:linear:{static,dynamic}FrictionEffort (:1276-1277) |
joint_friction (:35);在我们的 [0.3, 1.2] 之外(materials.json:235-241) |
| 抽屉接触摩擦 | drawer.contact_friction | 1 | [0.3, 0.7] (:55) |
0.660176 | /World/PhysicsMaterials/drawer static = dynamic (:58, 60) |
contact_friction (:43);我们的 schema 有这个 field 但不进 θ(contract.py:238-240 从 public projection 排除) |
| 把手接触摩擦 | handle.contact_friction | 1 | [0.5, 1.0] (:56) |
0.931859 | /World/PhysicsMaterials/handle (:69, 71) |
None(:46):我们的 handle 只有 size 和 color(schema.py:256-260) |
Kitchen 不进 θ 的其它量:
| 类别 | 量 | 值 | 来源 |
|---|---|---|---|
| 固定 | restitution(drawer、handle、cabinet、floor) | 全 0 | config.yaml:58-63 |
| 固定 | cabinet friction;floor friction | 0.5;0.8 | config.yaml:60, 62;USDA:47, 49;:36, 38 |
| 固定 | travel | [0, 0.40] m | config.yaml:41;USDA:1274-1275 |
| 固定 | drive stiffness / type | 0 N/m / "force" | truth.py:241-248;USDA:1263, 1266 |
| 固定 | robot 接触材料 | 0.5 / 0 | compose.py:32-35;USDA:80-82 |
| 固定 | combine mode(4 个 kitchen 材料) | "max"(robot 材料没写) | usd_file.py:248-251;USDA 每个 material |
| 固定 | robot gains | 400 / 80 / 2000 / 100(USD angular 写成每度 6.981317 / 1.396263) | config.yaml:67-70;S1/isaac/robot.py:166-171 |
| 派生 | COM(link frame) | (-0.217877, 0, -0.0453954) m | truth.py:69-98;USDA:1118 |
| 派生 | principal inertia | (0.0812338, 0.169798, 0.241747) kg m^2 | USDA:1119 |
| 派生 | principal axes (w, x, y, z) | (0.70646, -0.03030, -0.03030, 0.70646) | USDA:1121 |
| 派生 | density;分件质量 | 197.252 kg/m^3;front 0.612359、box_bottom 1.02966、box_left/right 各 0.127819、box_back 0.21627、handle_bar 0.00618582、post 各 0.00079532 kg | truth.py:94-97 |
| asset 自带 | Panda link 质量、maxForce、速度上限 | 同上表 | HO/scene/robot/panda.usd |
一个物理含义(我的推断,不在代码里):drawer 不碰柜体(间隙 3 到 6 mm),K13/K14 又禁止机器人碰 handle 以外的东西,所以 drawer.contact_friction 在这个 task 里不作用于任何接触;手指对 bar 的摩擦按 "max" 规则等于 handle 值。
5. 怎么生成的
Tabletop 用一个 PCG64 流(S1/sample.py:277),消耗顺序固定:先 4 个 fixture 抽样(sampler.py:198-207),然后每次 cube attempt 8 次抽样(class、edge、density、x、y、yaw、friction、restitution,:210-234),最后房间(sample.py:286-288)。改 cube 的区间或 attempt 次数会把整个房间换掉;改房间配置不影响 cube。每个 cube attempt 要过三道门,500 次不过就 RuntimeError(config.yaml:6;sampler.py:245-247):
- canonicalize:6 位有效数字,density = mass/volume(
canonical.py:33-41)。 - contract 零 finding,通过一个无 view 的临时 bundle 跑
verify_reference_scene(sampler.py:130-151, 238-240)。会卡的规则:G-B10 mass ∈ [0.005, 2.0] kg(contract.py:71, 534-542,小 foam 会撞);G-B6 slip/topple margin:box 的 ratio = (w/2)/(h/2) = 1,min over μ ∈ {own, max(own, table)} 的 |μ/ratio − 1| 必须 ≥ 0.15(constants.py:127;contract.py:610-634),所以 foam 的 μ ∈ (0.85, 1.0] 被拒;G-B8 density = mass/volume 必须在 class range 内(contract.py:543-554),理论上 6 位取整能把贴边的 density 推出区间,200 seed 实测 0 次;G-B5 edge margin 0.04 m(x reach ≤ 0.36,最大只有 0.298)和中心间距 0.16 m(单物体)永远不卡。 - push plan 存在(
:241-243):沿 +y(config.yaml:16);start = center − dir·(yaw-aware half extent + 0.03);end = start + 0.10·dir;expected cube xy = center + 0.07·dir(sampler.py:76-84);expected 必须还在桌面内,每轴 |expected − top_center| ≤ top_size/2 − 该轴 half extent,否则plan_push直接返回 None(:85-92);推的高度 z = 0.40 + a/2(:78-80);waypoints:approach 在 push 高度上方 0.12 m,然后 6 个等距点 pre_push、push_1 到 push_4、post_push(:94-99;config.yaml:19-20);tool pose z 朝下、x 沿推向(kinematics.py:92-99);IK 用 Franka modified DH 链 + damped least squares,300 次迭代、damping 0.05、数值 Jacobian 步长 1e-6、裁到关节限位、上一个 waypoint 的 q 做 warm start(kinematics.py:130-155);通过条件:限位内、位置误差 < 1e-3 m、姿态误差 < 1e-2 rad(:122-127),相邻 waypoint 的 max |Δq| ≤ 0.5 rad(sampler.py:40, 106-107)。出桌面和 IK 失败都计为 "push plan 不存在",没有分开统计。seed 848 第 1 次 attempt 就通过,IK 残差 2e-6 到 8e-6 m。
本机实测(seeds 0 到 199,不在代码里):200 个场景共 216 次 attempt,被拒 11 次 G-B6(foam)、1 次 G-B10 mass(foam)、4 次 push plan、0 次 G-B8;接受的 class 分布 cardboard 50、aluminum 45、wood 42、abs_plastic 38、foam 25。
房间抽样(environment.py:109-215):两面墙各取 4 色之一、地板 4 色之一;每个 prop 尺寸逐轴均匀(cylinder 令 y = x),环半径 U[0.9, 2.0],角度 U[0, 2π],box 的 yaw U[0, 90](硬编码,:156-158);每个 prop 最多 400 次(config.yaml:40);灯光 dome U[150, 400],key U[400, 800],方位 U[20, 160] deg、仰角 U[40, 70] deg(config.yaml:36-39)。seed 848 用了 21 次 attempt。房间不进 scene.json,agent 的文本里没有它(README.md:258-259)。
| prop 被拒条件 | 阈值 | environment.py |
|---|---|---|
| 离桌面 footprint 太近 | < 0.30 m | 164-165 |
| 离 robot base(减 half footprint)太近 | < 0.45 m | 166-170 |
| 穿墙 | footprint 的最小角越过墙:center − half < −2.2(x 或 y 任一轴) | 171-172 |
| 离已放 prop 太近 | < 0.05 m(硬编码) | 173-176 |
| 在某个 view 里可能挡桌面 | image box 重叠且比桌子最远角近 | 63-73, 177-178 |
| 两个 view 都出画 | 76-86, 179-180 |
Kitchen 没有 accept/reject 循环:θ 一次抽 5 个(sample.py:93-94),layout 由 config 确定性算出(layout.py:138-343),pull plan 用同一套 IK 从 home 解 pregrasp、approach、close、pull_1 到 pull_8、release、retreat、return_home(plan.py:131-205);通过条件是 IK 全部 ok、接触段相邻 waypoint 的 max |Δq| ≤ 0.25 rad、arm(球模型半径 0.06 m)离所有 collider 和 drawer ≥ 0.02 m(plan.py:37-45, 196-200),不满足就 RuntimeError(sample.py:97-102)。seed 2611:最大关节步 0.0928 rad,最小间隙 0.0730 m(retreat 到 return_home 段对 drawer front)。抓取姿态 GRASP_ROTATION(plan.py:36):TCP 沿 world −x 接近,手指开合方向竖直,闭合后上下夹住 bar,拉力靠指垫摩擦,没有任何 attach joint(PROGRESS.md:171)。
Validation 的 rollout 从 settle 后直接走 pregrasp(KD/isaac/validate.py:171-177),没有 home 相位;viewer 的 scripted pull 多一段 home(时长 seconds.move,drawer_panel.py:97-98)。下表是 validation 的相位,合计 12.6 s = 3024 步:
| phase | TCP(world, m) | fingers(m) | 时长 + dwell(s,config.yaml:83) |
|---|---|---|---|
| pregrasp | (0.158, 0, 0.755) = grasp + 0.12 | 0.04 | 1.5 + 0.4 |
| approach | (0.038, 0, 0.755) = bar 中心 0.035 + grasp_depth 0.003 | 0.04 | 1.0 + 0.4 |
| close | 同上 | 0.0 | 0.8 + 0.4 |
| pull_1 到 pull_8 | x 每步 +0.025,到 0.238 | 0.0 | 8 x 0.375 + 0.4 |
| release | (0.238, 0, 0.755) | 0.04 | 0.6 + 0.4 |
| retreat | (0.358, 0, 0.755) | 0.04 | 1.0 + 0.4 |
| return_home | home (0.2938, -0.026, 0.3452) | 0.04 | 1.5 + 0.8 |
Kitchen 的相机不是抽的,是从截图拟合的(KD/camera.py:214-307):
| 项 | 内容 | 来源 |
|---|---|---|
| 观测 | 5 个手选柜体角点 + 8 段 RANSAC grout 线,图 712 x 562 | references/landmarks.json |
| 未知量(12) | eye 3、yaw/pitch/roll 3、焦距 1、柜宽、柜深、tile pitch、tile offset 2 | camera.py:228-234 |
| 假设 | carcass 高 0.86(定尺度)、countertop 0.04、overhang 0.025、主点在图心 | camera.py:313;PROGRESS.md:97-99 |
| 求解 | 72 个起点的 least_squares,取最小 cost |
camera.py:260-284 |
| 结果 | eye (1.935, -1.630, 1.526),fovy 29.27 deg,roll 4.07 deg,柜体 0.930 x 0.669,tile 0.576;角点 RMS 3.6 px,线 RMS 3.3 px;robot base 由像素 (468, 505) 反投影到地面得 (0.655, -0.026) | PROGRESS.md:99-101 |
| 看不见、靠假设 | rails、抽屉深度、travel、handle | PROGRESS.md:103-105, 230-231 |
| render 对截图 | chamfer 7.44 px(iter 1)、9.67 px(iter 2,加 MDL 后背景多了边),之后停 | PROGRESS.md:186-193 |
可复现性:tabletop 的 sample 不写时间戳,同 seed 字节相同(tests/test_stage1_reference.py:114-125,本机复现);但 ground_truth.json 记录 python/numpy/usd-core 版本(sample.py:181-190),换环境字节会变;manifest.json 带 created_utc(sampler.py:177)。numpy 跨版本是否保持同一随机流:not in the code。Kitchen 同理(sample.py:11, 45-50)。
6. Isaac 技术要点
| 要点 | 做法 | 代码 |
|---|---|---|
| URDF import(6.x) | URDFImporter(merge_fixed_joints=False, fix_base=True, allow_self_collision=False, run_asset_transformer=False) 导到临时目录,Flatten,把 Flattened_Prototype_N 按首次引用顺序重编号后导出为 usda 文本,使同一 robot 给出相同字节 |
S1/isaac/robot.py:74-134 |
| drives | revolute 用 angular DriveAPI,type "force",stiffness 400·π/180、damping 80·π/180(USD 角量按度),target 与 JointStateAPI 写 home(度)、速度 0;prismatic 指 2000 / 100;newton:velocityLimit 复制到 physxJoint:maxJointVelocity;每个 rigid link disableGravity = True;articulation enabledSelfCollisions = False |
robot.py:143-211 |
| compose scene.usda | Sdf.Layer.Export 把 scene_physics.usda 复制成 scene.usda(拷贝,不是 sublayer);/World/Robot reference ./robot/panda.usd 并 translate + orient 到 base pose;没有 body0 的 root joint 的 localPos0/localRot0 设成 base pose(6.x 里 body0 已指向 root Xform,这步跳过);robot 材料 0.5 / 0 以 weakerThanDescendants 绑到每个 robot collider;pedestal 是 CollisionAPI 的 Cube;房间和 lights 写入;layer customData robot_included: True |
S1/isaac/compose.py:107-215;kitchen 复用 KD/isaac/build.py:41-62 |
| PhysX scene | up Z,metersPerUnit 1,kilogramsPerUnit 1;gravity 9.81;240 steps/s;solver TGS;broadphase MBP;enableGPUDynamics False;bounceThreshold 0.1 m/s;材料 combine mode "max";body damping 0、sleep threshold 0 |
usd_file.py:325-349, 239-252, 305-322;constants.py:77, 229-234;USDA:17-27 |
| reload + read-back(Session) | open_stage,5 次 app.update(),关掉 4 个 USD write-back 设置,dt = 1/timeStepsPerSecond,给 robot 的每个 rigid body 加 PhysxContactReportAPI(threshold 0),tabletop 再加 cube(S1/isaac/validate.py:133-135),kitchen 再加 drawer link(KD/isaac/session.py:63-66);overwrite_gpu_setting(0)(CPU PhysX),attach_stage,start_simulation,每步手动 simulate + fetch_results 并收集 contact report;再建 numpy tensor view(create_simulation_view("numpy"),subspace root "/"),用 get_masses、get_material_properties、get_dof_dampings、get_dof_friction_properties、get_dof_stiffnesses、get_link_transforms 读 PhysX 实际值 |
S1/isaac/validate.py:104-173;KD/isaac/session.py:22-133 |
| home 姿态写进文件 | 用 PhysX 给出的 link 世界位姿写回每个 link 的 translate/orient,再把内存里的 ContactReportAPI 去掉后保存,文件打开就是摆好姿势的 | validate.py:275-314;session.py:205-234 |
| path tracing,不用 DLSS | SimulationApp({"headless": True, "renderer": "PathTracing", "samples_per_pixel_per_frame": 64, "denoiser": True});/rtx/pathtracing/totalSpp = clampSpp = 64 x 8 = 512;warmup 64 次 update;Replicator render_product + rgb + instance_id_segmentation(colorize False),rep.orchestrator.step(rt_subframes=8);OptiX denoiser(:103,没有关闭开关);real-time 模式靠 DLSS,image 里没 NGX 所以不用(README.md:152-153)。64 spp、8 subframes、warmup 64 都是 isaac_render.py:64-70 的 CLI 默认值;S1/config.yaml:53-54 的 warmup_updates: 32、rt_subframes: 64 只有旧的 isaac/render_views.py:81-83 读,isaac_render.py 不读,改 config 不改 render |
S1/render/isaac_render.py:64-70, 96-161;KD/isaac/render.py:89-187;KD/config.yaml:131-134 |
| OptiX denoiser 在 我们的集群 上 | <node> 实测 optixDenoiserCreate 失败:Unable to load denoiser weights: Could not open optix denoiser weights file /usr/share/nvidia/nvoptix.bin(节点上没有这个文件;tabletop render 段 353 次,viewer robot 测试完整 log 6170 次);render 仍 exit 0、render.json passed: true,两张 view 是没去噪的 512 spp path tracing;verify_views 19:1x ET 跑过:噪声门 PASS,全部检查 PASS,verified=True。同名文件在我们的 driver user-space 拷贝里有:conda-envs/848x-isaac/nvidia-userspace/615.71.09/share/nvidia/nvoptix.bin,但 OptiX 只认 /usr/share/nvidia/nvoptix.bin,没有环境变量可改(NVIDIA 论坛的办法只有 root 放文件或容器 bind)。verify_views 19:1x ET 在这两张图上跑过:噪声门 PASS,verified=True |
RUNS/logs/stage1-smoke-3506650.log:1545-1557;RUNS/kitchen_drawer_pull/view-test-3506650/test-robot.log;RUNS/stage1/held_out_00/renders/render.json |
| session layer 摆姿势 | render 时把 settled cube pose(tabletop)或 state A/B 的 link poses(kitchen)写进 stage.GetSessionLayer(),scene.usda 不动;每换一个 state 就 session_layer.Clear() |
isaac_render.py:32-42;render.py:131-139 |
| 先写失败记录 | render.json 在 Kit 启动前先写 passed: False 和 "Kit ended before a frame was rendered",RTX 把进程带崩也有记录 |
isaac_render.py:86-94;render.py:78-87 |
| 确定性测试 | tabletop V7:snapshot/restore 后重跑 480 步,cube pose 差 < 1e-6;kitchen K17:两次重新 load 文件再 settle + rollout,q/slide/tcp 差 < 1e-6,因为 snapshot/restore 不重置 PhysX contact cache(第一次 build 差 6.1e-6 失败) | validate.py:500-514;KD/isaac/validate.py:730-769;PROGRESS.md:171-174 |
| SimReady 背景去物理 | 三个 variant set PhysX/MuJoCo/Newton 置 "Disabled" 还不够;再把 instance 解开,rigid body、collider 全 disable,joint SetActive(False),ArticulationRootAPI 移除。四件合计 24 个 rigidBodyEnabled = 0、24 个 collisionEnabled = 0、20 个 joint active = false、4 处 delete apiSchemas = ["PhysicsArticulationRootAPI"];分件 back_counter 9/9/8、right_cabinet 3/3/2、wall_cabinet 6/6/5、dishwasher 6/6/5;finalize 要求背景 live physics 为 0 |
KD/usd_scene.py:244-307;PROGRESS.md:78-79;USDA:1346, 1760, 1898, 2123;KD/finalize.py:124-140 |
| 退出码 | 6.x 的 app.close() 默认以 0 退出,close_app(app, status) 传 exit_code |
S1/isaac/launch.py:48-53 |
| 两个 experience | build/validate 用 isaaclab.python.headless.kit(不启 RTX),rendering 用 isaaclab.python.headless.rendering.kit(5.1 路径);launch.py 在 isaacsim/apps 和 isaaclab/apps 找,找不到就 FileNotFoundError。ENV62(isaacsim 6.2.0.0 + isaaclab 3.0.0 --no-deps)里两份 kit 都在 isaaclab/apps/,build_scene 在它上面跑通;848x-isaac(6.0.0.1)没装 isaaclab,apps/ 只有 isaacsim.exp.*.kit,所以 build_scene / validate_existing 在它上面起不来;isaac_render.py 不用这些 experience |
launch.py:19-38;README.md:122-124 |
7. 校验
Tabletop V1 到 V9(S1/isaac/validate.py:340-652),settle 480 步 = 2 s(config.yaml:55),robot 一直 hold home。我们的集群 实测(job 3506650,ENV62,<node>):九项全 PASS,STAGE1_BUILD passed=True steps=2761(log :100-109;RUNS/stage1/held_out_00/validation.json)。
| V | 通过条件 | 行 |
|---|---|---|
| V1 | stage 打开、PhysX attach;settle 后 cube pose 与 robot q 全有限 | 351-371 |
| V2 | cube:|z − (0.40 + a/2)| < 2e-3 m,xy 漂移 < 1e-3 m,倾斜 < 0.5 deg,速度 < 1e-3 m/s | 372-390 |
| V3 | max 关节误差 < 0.01 rad;panda_link0 位置误差 < 1e-3 m;panda_hand 对 DH 链 < 5e-3 m 且 < 1.0 deg |
391-423 |
| V4 | settle 期间 robot 与非 robot collider 零接触(pedestal 碰 link0 除外) | 253-268, 424-433 |
| V5 | cube mass / static / dynamic friction / restitution、drawer mass、damping、joint friction、7 个 arm stiffness(对 400 每 rad)、link 质量,相对误差 ≤ 1e-5;注意 table friction 读的是 USD binding 不是 PhysX(_table_material,317-327) |
435-488 |
| V6 | |slide| < 1e-3 m | 489-498 |
| V7 | restore 初始 snapshot 重跑 480 步,max |cube pose 差| < 1e-6 | 500-514 |
| V8 | F* = μmg,μ = max(cube, table),0.5 s:0.5F* 下位移 < 1e-3 m;1.5F* 下位移对 ½(1.5F*/m − μg)t² 相对误差 < 0.15 | 516-550 |
| V9 | 按 waypoints 关节空间插值(approach 1.5 s,pre_push 1.0 s,push 2.0 s,dwell 0.75 s,hold 0.5 s);TCP 误差 < 0.01 m(approach、pre_push、post_push);cube 沿推向位移 ∈ [0.035, 0.105] m(计划 0.07);横向 < 0.03 m;倾斜 < 10 deg;z > 桌面;碰到了 cube;没碰别的 | 38, 552-636 |
Kitchen K1 到 K17(KD/isaac/validate.py:236-795),settle 480 步,K11 到 K17 从重新 load 的文件开始,rollout 12.6 s = 3024 步。括号里是 Viet 的实测(PROGRESS.md:151-169,job 8138527,Isaac Sim 6.2.0.0,Kit 110.4)。
| K | 通过条件(实测) | 行 |
|---|---|---|
| K1 | 2 个 articulation root,状态有限(dt 1/240) | 252-263, 289-291 |
| K2 | robot 9 DOF 名字对、drawer 1 个 prismatic drawer_slide |
266-284 |
| K3 | island 下无 rigid body;drawer base 位姿变化 < 1e-6(0) | 298-306 |
| K4 | settle 后 |slide| 和 |v| < 1e-4(4.8e-13 m) | 308-316 |
| K5 | (f + 5 N) 推 0.5 s:沿 x > 0.005、横向 < 1e-4、转动 < 0.01 deg;反向推后 > −1e-3(0.241 m,1.2e-10 m,0 deg) | 319-354 |
| K6 | ±40 N 各 3 s:到达上下限误差 < 2e-3,过冲 < 3e-3(0.40000 m) | 357-388 |
| K7 | mass 相对误差 ≤ 1e-5,COM < 1e-5 m,principal inertia 相对 < 1e-4(2.3e-8,6.8e-10 m,1.8e-7) | 391-429 |
| K8 | damping、static/dynamic friction 读回 ≤ 1e-5;0.5f 推 1 s |x| < 5e-4;F = f + 0.05c 推 4 s 稳态速度与 0.05 m/s 相对误差 < 0.10 | 432-483 |
| K9 | base 位置 < 1e-3 m,yaw < 0.1 deg(2.7e-5 m,0.002 deg) | 486-513 |
| K10 | 关节 < 0.01,hand 对 DH 链 < 5e-3 m、< 1 deg(4.6e-5,2.5e-5 m) | 514-540 |
| K11 | plan reachable;pregrasp、approach 的 TCP 误差 < 5e-3(4.5 / 2.4 mm) | 561-582 |
| K12 | 闭指前 robot 零接触(link0 碰地除外) | 583-592 |
| K13 | 两指都碰 bar,每指位置在 0.007 ± 0.003 m,无其它接触(6.88 mm) | 593-630 |
| K14 | final slide ∈ [0.9 x 0.20, 0.40],slip < 0.01,TCP tracking < 0.01,无其它接触(0.1980 m,slip 4.8 μm,2.0 mm) | 631-671 |
| K15 | 最小 separation > −0.005 m,off-axis < 1e-4,slide 在 limits ± 1e-3(−0.05 mm) | 672-701 |
| K16 | retreat 后零接触,slide 变化 < 2e-3,arm 离 home < 0.01 rad(0.1986 m,2.6e-3 rad) | 702-729 |
| K17 | 两次 fresh load 的 rollout 差 < 1e-6(0.0,3024 samples) | 730-769 |
Render 之后和 finalize 的检查:
| 检查 | tabletop (S1/render/verify_views.py:45-53, 139-321) |
kitchen (KD/verify_views.py:35-42, 151-224) |
|---|---|---|
| 来源 / 新鲜度 | render.json passed;verify 记录 render.json 和 png 的 sha256 |
另要求 engine 为 Isaac Sim 6.2.0.0,4 个输入 hash 未变 |
| 尺寸、曝光 | 相机尺寸;mean ∈ [40, 220],clipped < 0.05 | mean ∈ [30, 225],clipped ≤ 0.05 |
| 噪声 | 桌面 mask(腐蚀 6 px)高频 std ≤ 6.0 | 柜体平面 mask(腐蚀 3 px)MAD ≤ 6.0 |
| 几何 | cube mask 质心离 settled 中心投影 ≤ 8 px;桌面 ≥ 1000 px 且质心在投影轮廓 ± 5 px 内 | robot base 投影落在 robot 上;3 个柜体 landmark(front_left、front_right、countertop_front)每个都要落在 cabinet mask 上或被 robot mask 整窗遮住,且至少 2 个落在 cabinet 上(:186-197) |
| 可见性 | robot ≥ 1000 px;每个投影面积 ≥ 5000 px² 的墙/prop ≥ 200 px;至少一个 view 有房间 | robot ≥ 2000 px,drawer_front ≥ 1500,handle ≥ 60(demo/reference_match 免) |
| 两 view 不同 | 平均绝对差 ≥ 10 | 同 |
| finalize 静态比对 | S1/stage_checks.py(usd-core,无 GPU):units、gravity、cube pose/size/mass/material、table material 和 top_z、drawer mass/damping/friction、robot base/anchor/articulation/joint limits/home targets/link masses、房间无物理、相机 fovy 和位置、灯光;容差 1e-6 x max(1, |expected|)(:18-24),6.x robot 共 62 项(README.md:102-103);另加 θ read-back ≤ 1e-6、"Isaac Sim validation of the reloaded scene passed"、"views: verified renders of scene.usda, room included"、ReferenceStore contract、separation、MuJoCo settle(finalize.py:139-276,其中 :156-176) |
KD/finalize.py:51-162:θ 和 fixed 对 USD、joint 类型/轴/limits、articulation roots = 2、背景 live physics = 0、decor/floor 无物理、相机 focal 和 eye;gates validation / renders / files / stage 全过才发布 |
8. agent 看到什么,看不到什么
Tabletop 走我们自己的 observe(S1/observe_held_out.yaml,与 configs/sample.yaml 只差四处:visual_condition: text with two complementary static views、held_out_root: ../runs/stage1/held_out、split: held-out scenes、scene_ids: [held_out_00];仍是 engines: [MuJoCo],:8-20)。ReferenceStore.load 跑全部 contract;build_observation 从 bundle 拷贝已 ship 的 view 并记录 manifest 的 renderer,只 render 没 ship 的 view(build.py:141-160, 192-210)。
| 文件 | 内容 | 来源 |
|---|---|---|
base_text.md |
public projection 的文字化:table、drawer(几何、关节轴和行程、handle 尺寸、"closed")、robot(pedestal 高、base 位置、yaw)、objects(label、形状、尺寸、颜色词、材料名、位置、yaw) | PKG/input_conditions/base_text.py:175;contract.py:234-278 |
disclosed_parameters.json |
NONE 为空;PARTIAL 按 one_value_per_relationship:每个 object 三选一、每个 joint 二选一,table friction 和 drawer mass 永远 withheld,3 位有效数字;ALL 给全部 7 个 |
partial_information.py:173-242;constants.py:225-226 |
views/front_right.png、views/top_down.png |
Isaac Sim RTX 的 render(房间在图里但不在文字里) | finalize.py:122-136 |
cameras.json |
两台相机的 fx、fy、cx、cy 和 OpenCV camera_to_world | views.py:86-96 |
manifest.json |
scene_id、input_combination、lock_hash、renderer、每个文件的 sha256、labels、described_objects、fixtures block、units、observation_sha256 | build.py:217-230 |
seed 848 的 base_text.md(用 write_base_text 对 sampled scene.json 生成;object label 由 object_labels 的 seeded permutation 给出,单物体时 permutation 长度 1,任何 seed 都得到 object_1,base_text.py:41-45):
A tan wood table that does not move. Its top is a slab of 80.0 x 60.0 x 4.0 cm (x, y, z) centered on the origin with its upper face at z = 0.0 cm. … A purple wood drawer, a closed box of 30.0 x 25.0 x 8.0 cm (x, y, z), hangs under the table top and moves along +x on a prismatic joint. Closed, its center is at x = 25.0 cm, y = 0.0 cm, z = -8.5 cm; it can move by up to 25.0 cm from the closed position. A dark gray handle of 2.0 x 10.0 x 1.5 cm sits in the middle of its front face. The drawer is closed. … A robot arm (franka emika panda) with a panda hand is mounted on a pedestal 20.0 cm tall; its base stands at x = 40.0 cm, y = -45.0 cm, rotated by 90.0 degrees about the vertical axis. … object_1: a red ABS plastic box of 6.3 x 6.3 x 6.3 cm (x, y, z). Its center is at x = -4.7 cm, y = -7.6 cm; its top is at z = 6.3 cm. It is rotated by -26.8 degrees about the vertical axis.
Kitchen 的 observation 是 finalize 手写的(KD/finalize.py:176-236),只有一个 level "no supplied physical parameter values"(:257-259),没有 disclosed_parameters.json 和 PackageManifest,所以我们的 load_observation 读不了它,9 个 input condition 一个都没产出。base_text.md 原文:
A kitchen. In the middle stands a white island cabinet on a dark tiled floor with pale grout lines. The island is 93 cm wide, 67 cm deep and 90 cm tall to the top of its thick white countertop, which overhangs the front and the sides slightly. Under the countertop is one drawer across the full width of the island, with a metal bar handle in the middle of its front; the drawer slides straight out of the front of the island. Below the drawer are two panelled doors with round knobs next to the gap between them. A Franka Emika Panda arm with its two-finger hand stands on the floor in front of the island, facing the drawer, its base about 66 cm in front of the island's front face, centred on it. Further white kitchen furniture stands in the background. Frame: z up, the island's front face is the plane x = 0, the drawer opens towards +x, the island is centred on y = 0 and stands on the floor at z = 0. The views show the initial state: the drawer closed, the arm at its home pose with the hand open. The robot's task in this scene is to grasp the drawer handle and pull the drawer open. Background furniture: NVIDIA SimReady assets, CC-BY-4.0.
cameras.json 给 reference_match(1424 x 1124,fovy 29.2663,fx = fy = 2152.445,cx 712,cy 562)和 workspace(1280 x 960,fovy 38.4,fx = fy = 1378.372,cx 640,cy 480)的内参和 OpenCV camera_to_world(:207-236)。
| 看不到的 | tabletop | kitchen |
|---|---|---|
| θ(s) | 7 个值(hidden_values() = 0.253765, 0.339249, 0.64654, 2.22435, 0.421894, 0.419825, 0.671937) |
5 个值 |
| 其它物理量 | density、table/drawer restitution、drawer contact friction、robot gains、PhysX 设置 | 固定值、派生值、combine mode、robot gains、PhysX 设置 |
| 几何 | 房间的任何描述(只在图里) | travel 0.40、box 深 0.50、高 0.12、rails、opening 0.19;handle 0.16 bar、14 mm、35 mm standoff |
| robot | 无(base pose、pedestal 在文本里) | 精确 base pose 和 home q(只能看图) |
| 其它 | seed、ground truth、USD、record.json |
plan、state B 图、截图、SimReady 选件和位置、seed、ground truth、USD |
| 防泄漏 | 规则 | 代码 |
|---|---|---|
| finalize 文本/文件名 | 不得含 seed、ground_truth、.usd、validation |
S1/finalize.py:47, 210-235 |
| finalize 数值 | %.6g 格式的任何 θ 值不得出现在 public projection |
:222-224 |
| test | regex (?<![0-9a-f])848(?![0-9a-f]):禁的是不与 hex 字符相邻的独立 848(0.848 也禁,因为 . 不是 hex);1848、8480、sha256 里的 848 都允许 |
tests/test_stage1_reference.py:163-165 |
observe leak_scan |
扫 package 里的 withheld 值和派生值(density 用 4 种 scale,e² 用 3 种),3 位有效数字;marker 词 seed、secret、private、held_out、hidden、scene.json |
partial_information.py:265-299;leak_scan.py:120-128 |
kitchen leak_scan |
needles:θ、非零固定值、principal inertia、0.4、400/80/2000/100;有 finding 就不把临时目录换入 observation/<scene>/,finalize 失败;已发布的旧 observation 不会被删 |
KD/finalize.py:239-274, 399-414 |
9. 交互查看
S1/isaac/stream_scene.py 用 isaacsim.exp.full.streaming.kit 把整个 Isaac Sim UI headless 渲染后经 WebRTC 推流(signalling TCP 49100,media UDP 47998,:3-6, 81-82),客户端是同版本(6.2)的 Isaac Sim WebRTC Streaming Client(README.md:181-183)。--public-ip 是客户端发 media 的目标:直连填节点地址,ssh 转发填 127.0.0.1(:7-8)。ssh -L 不转 UDP、socat 会合并 datagram 导致黑屏,所以 udp_tcp_relay.py 给每个 datagram 加 (channel, length) 头走一条 TCP(udp_tcp_relay.py:2-14, 26)。启动约 1 min,打印 STAGE1_STREAM ready(README.md:192;stream_scene.py:174-180);关掉客户端 session 就结束。渲染模式 RENDER_MODES = ("default", "RealTimePathTracing", "PathTracing")(:40):default 是 RTX Real-Time(靠 DLSS,无 NGX 会 grainy),PathTracing 是 OptiX(代码设 4 spp/帧,静止时累积到 256,:59-67, 86-95);--capture-dir + --capture-every 把 viewport 存成 PNG(:99-102, 223-230)。
| 面板 | 控件 | 作用 | 代码 |
|---|---|---|---|
| Panda Control | Play、Pause、Reset robot、Reset simulation | 只改 live PhysX 状态;状态行显示 PhysX 步数计的 sim time | robot_panel.py:404-415, 265-291 |
| Panda Control | Camera:orbit(步长 5 到 45 deg)、pan 0.1、zoom 0.8、Focus selected、Reset view | 只动 viewport 相机 | :294-365, 416-441 |
| Panda Control | 7 个 arm joint 滑条(度) | 裁到 PhysX 限位后下发 drive target | :226-232, 442-452 |
| Panda Control | Gripper Open(0.04 m/指)、Close、0 到 40 mm 滑条 | finger drive target | :234-239, 453-466 |
| Panda Control | End effector x/y/z/roll/pitch/yaw,Go (IK),Use current pose | 只在 IK 收敛且在限位内才下发,否则 "nothing commanded" | :241-263, 467-479 |
| Panda Control | Feedback(约 10 Hz) | actual/target q、tracking error、PhysX TCP、DH 链与 PhysX 的差(mm) | :537-571 |
Kitchen Drawer(--drawer-panel) |
Scripted pull | 按 plan_pull 的 waypoints 和 config.pull.seconds 在 sim time 里插值(比 validation 多一段 home);成功判据 0.9 x 0.20 ≤ final ≤ upper + 1e-3;plan 来自 config.yaml,不来自 stage,UI 里改 θ 不影响它 |
drawer_panel.py:89-152 |
| Kitchen Drawer | Reset drawer | slide 和速度归 0 | :80-86 |
| Kitchen Drawer | Travel limits | session layer 里两块半透明板标闭合和全开位置 | :177-203 |
| Kitchen Drawer | 标签 | slide、速度、limits、phase、status | :205-223 |
| headless 自检 | --robot-test、--drawer-test |
Viet 实测 33/33,pull 到 0.1988 m 成功 | PROGRESS.md:235-249 |
我们的集群 实测(job 3506650,code/private/tools/jobs/stage1_smoke.sbatch,<node> L40S,ENV62,--render-mode PathTracing --drawer-panel,对 HO 场景的副本):--robot-test summary passed=True 33/33(RUNS/kitchen_drawer_pull/view-test-3506650/test-robot.log:63745);--drawer-test pull PASS final 0.1988 m planned 0.200 m, best 0.1988 m, 1539 frames, sim 17.14 s,summary passed=True(test-drawer.log:13206, 13424);启动 254.2 s(第一次)和 88.2 s(第二次)(job log :7;test-drawer.log:1811);副本没被改写、hidden 文件 hash 不变(job log :91-92)。两个问题:(1) WebRTC 插件没加载:[Plugin: libomni.kit.livestream.webrtc.plugin.so] Could not load the dynamic library ... Error: libatomic.so.1: cannot open shared object file(job log :5,两次测试各一次),headless 自检不需要它,但 Mac 客户端连不上。已修(19:13 ET):env 的 conda libgcc 自带 lib/libatomic.so.1,加了 lib/extra/libatomic.so.1 符号链接并写进 hook 的 LD_LIBRARY_PATH(simgenprobe.sh:29, 35);复查 job 3507469(<node>,2 min,default 渲染模式)插件错误 0 条,omni.kit.livestream.webrtc-10.7.3 startup,STAGE1_STREAM ready ... startup=70.6s,DRAWER_TEST summary passed=True;(2) PathTracing 模式下 ready 行报 /rtx/pathtracing/spp=64 /rtx/pathtracing/totalSpp=64(job log :6),不是代码设的 4 / 256,设置没生效,原因 not in the code;OptiX denoiser 同样缺 weights(第 6 节)。
永远打开一个 copy:UI 里 File > Save 写的是打开的那个文件(README.md:187-189);kitchen 的 copy 必须和 simready_cache 平级(scene.usda 用 ../simready_cache/... 引用,USDA:1347, 1761, 1899, 2124),robot 文件夹放旁边(KD/README.md:77-82)。Viet 的 sbatch 假设 队友的集群:account/partition/qos 都是 scavenger(HO/jobs/kitchen_webrtc.sbatch:6-8)、--gres=gpu:<their-gpu>:1(:9)、Apptainer 镜像 STAGE1_ISAAC_SIF(:14)、NGX bind(:23)、.<their-domain> 主机名(:34);我们的集群 上这些都要换(smoke sbatch 已经换成 scavenger + gpu:nvidia_l40s:1,无 Apptainer)。
10. 需要你拍板的几处
- Isaac 版本:Stage 1 已定在 6.2,harness 还没定。
ENV62(conda-envs/simgenprobe,23 GB,code/private/tools/create_simgenprobe_env.sh建,log 末行 "2026-11-04 18:39:39 EST done";isaacsim 6.2.0.0 + isaaclab 3.0.0--no-deps,isaaclab/apps/isaaclab.python.headless*.kit都在)已经存在,S1/isaac_python_cluster.sh:11指向它,smoke job 3506650 把 viewer 自检、build V1 到 V9、render 全跑通(第 7、9 节)。没定的是我们的 package:PKG/constants.py:228写死ISAAC_SIM_VERSION = "6.0.0.1",Isaac engine 的 worker handshake 版本不等就EngineUnavailable(scene_compilation/isaac_sim/isaac_sim_engine.py:251-256),version()也回这个常量(:868-870);constants_sha256()把 constants 模块里每个大写常量 hash 进 lock(evaluation_protocol/lock.py:124-127,data/protocol/protocol.lock.json:44),verify_lock在 observe / evaluate / cli 开头重算,不等就ProtocolLockMismatch(lock.py:257-271;steps/observe.py:57,steps/evaluate.py:88,cli.py:141)。所以ENV62能跑 Stage 1,但不能当 observe / generate / evaluate 的engines.isaac_sim用。(a) 把constants.py:228改成 6.2.0.0 并重新生成 lock:一套 Isaac 跑 Stage 1 和 harness,代价是lock_hash变,已有的 observation / record 带旧 hash;(b) harness 留 6.0.0.1(848x-isaac),Stage 1 用 6.2:两套 Isaac,跨版本一致性只能靠validate_existing.py/inspect_stage.py比(README.md:109-112),没验证过。推荐 (a),"一律用 Isaac" 应当是一个版本,Stage 1 的 V1 到 V9 和 K1 到 K17 都只在 6.2 上验证过。附带一个矛盾:建 env 的 log 18:39:34 EST 列出 usd-core 26.8(create_simgenprobe_env.log:394, 414),19:1x EST 在ENV62里pip list没有它,顶层pxr来自 usd-exchange 3.0.1(usd_exchange-3.0.1.dist-info/RECORD),是主会话 18:52 ET 用uv pip uninstall usd-core特意拿掉的:Isaac 自带 pxr,site-packages 里的 usd-core 会被先 import 到(Viet 分两个 venv 就是为了这个);smoke 的 build 和 render 都过了。 - finalize 里的 MuJoCo。 tabletop
finalize.py:237-262用 MuJoCo settle 做交叉检查,observe_held_out.yaml:19-20的 evaluation engine 也是 MuJoCo;kitchen 不碰 MuJoCo。(a) 保留为独立检查;(b) 删掉或换成 Isaac settle。推荐 (b),与"一律用 Isaac"一致,代价是少一道独立 cross-check。 - demo 用哪个 family。 (a) kitchen:pull 已验证(K11 到 K17),但不在我们的
SceneSpecification里(14 个 schema error,PROGRESS.md:214),没有 9 个 condition;θ 对我们的协议:2 个值超出 fixture 区间(drawer.mass 2.1217 vs [0.4, 1.2],materials.json:221-227;joint_friction 2.13293 vs [0.3, 1.2],:235-241),1 个区间更宽但值在内(damping [2, 10] vs [2, 8],:228-234),1 项 schema 有但不进 θ(drawer.contact_friction),1 项无对应(handle.contact_friction,truth.py:46);(b) tabletopheld_out_00:完全在协议里,但 task 只有 push(ground_truth.py:188),handle 没 collider。推荐先 (a) 做 policy demo、(b) 跑 harness 的 9 个 condition,两条线分开;要合并就得做 schema 0.2(cabinet fixture、floor-standing robot、带 friction 的 handle),周级工作。 - viewer 连接。 (a) ssh 隧道到计算节点 loopback(
PUBLIC_IP=127.0.0.1+ relay);(b) VPN 直连(需要节点的 49100/tcp 和 47998/udp 可达,firewalld 未验证)。插件加载问题已修(第 9 节,复查 job 3507469)。推荐先 (a),Mac 已能直接 ssh 到计算节点;节点侧的 job 脚本已备好:code/private/tools/jobs/stage1_live_viewer.sbatch(提交后 log 里打印 Mac 端要敲的两条命令)。 - 渲染去噪(已有结论)。 <node> 没有
/usr/share/nvidia/nvoptix.bin,OptiX denoiser 建不起来;这个路径是写死的,没有环境变量能改,只能 root 放文件或容器 bind(我们的拷贝在nvidia-userspace/615.71.09/share/nvidia/,指不过去)。实测影响:tabletop 两张 view 是未去噪的 512 spp path tracing,verify_views19:1x ET 跑过,噪声门和全部检查 PASS,verified=True。结论:reference 渲染不动;交互 viewer 走 default 模式(RTX Real-Time + DLSS,dlss/supported=True,复查 job 3507469 就是这个模式)。