LabUtopia EBench

Teaching Note · USD Physics · DryingBox

为什么一个门会让 open_door 变难

这篇页面给产品经理和评测侧解释:USD articulation 不是普通模型导入,它同时包含层级、刚体、关节、驱动、材质和运行期状态。我们遇到的 DryingBox 问题,本质是“一个看起来像门的资产,进入 Isaac runtime 后没有成为一个稳定、可读、可评的门”。

Articulation Root: obj_obj_DryingBox_01
Joint: RevoluteJoint
Handle path: /handle

一句话结论

Before

旧门不是稳定任务对象

旧图只看到黑箱角,门板、把手、铰链和动作点不可读;运行期还出现过 joint position ~= 1.573e13 这类异常状态。

Root Cause

USD 层级和 PhysX articulation 不干净

门是 articulated object,不是普通 mesh。ArticulationRootAPIRigidBodyAPIJointbody0/body1scalehandle 层级都要同时正确。

P1 Baseline

用 sanitized surrogate 先锁定链路

P1 先用稳定的 DryingBox sanitized_surrogate 做对照基线:固定底座、只保留一个 RevoluteJoint、把门回放到 [0.0]、把手保留在内部 /handle 路径。这证明 EBench 读图和关节读数链路能跑,但还不等于原生复杂资产已经调通。

硬约束:最终必须调通 LabUtopia 原生复杂 DryingBox

这次不是继续把 cube-based surrogate 打磨得更像,而是让 LabUtopia native complex DryingBox_01 本体进入 EBench:保留原生 mesh、材质、层级身份和内部 /handle,只在必要时用 additive physics override 补齐 Isaac runtime 需要的稳定物理属性。2026-06-24 P2 已完成 Franka POC gate:native visual/hierarchy/handle 保留,wrapper-local Looks 和 native material:binding 已重连,EBench readback 中蓝色门、白色侧面、把手、观察窗和控制面板可见,native_complex_dryingbox_ready=true

层级 必须保留 允许覆盖 不允许
Native visual 原生 DryingBox 的门板、框架、把手、真实 mesh 和 material identity。 在 EBench wrapper 下补 wrapper-local Looks,并把原生 mesh 的 material:binding 重新指向本地 material path。 不把干燥箱替换成 Cube 拼出来的假箱体作为最终资产。
Native hierarchy /World/DryingBox_01 的内部树和 nested /handle 语义。 在 EBench wrapper 下增加诊断 metadata、object map 和 bbox contract。 不把 handle 拆成 top-level duplicate object。
Additive physics override 原生资产仍然是任务对象和视觉对象。 mass/inertia、修 body0/body1、固定 base、校正 reset target、隔离非任务 DOF。 不把物理稳定性问题通过换简化几何体绕过去。
Eval camera 使用 evaluator readback 作为证据路径。 任务相机、任务级隐藏和对比度增强可调整。 不把展示图、viewer 截图或漂亮图说成 baseline 成绩。
PM 版一句话:不是“换个假箱子”,而是“把原箱子搬进 EBench,再给它补上评测需要的稳定合页、复位规则和状态读回”。
Current Evidence
native_complex_dryingbox_ready=true
P2 native readback
Native Gate
task_render_accepted=true
Franka POC passed
Still False
official_baseline_evaluable=false
Lift2 gate pending
Native evidence Path SHA256
asset audit saved/diagnostics/native_dryingbox_audit_20260624_091136/audit.json e6eab4a6fc6a6b3ddddbabc2717a674c606c83255467db8b97bfbdac085aad4d
native-only smoke saved/diagnostics/native_dryingbox_smoke_20260624_091152/smoke.json fdab719564440d8528623785b55662acb38b74cf607d249dce963885082664a4
EBench readback saved/diagnostics/native_dryingbox_visual_retake_final_20260624_0002/diagnostics.json d93069572347c6a30260bc856de126193c531633be3167f4ecc7fb76ce8d7bf6

为什么 LabUtopia 原生没问题,进 EBench 后会出问题

最容易误解的一点是:两边看起来都叫 USD,也都在 Isaac 里渲染,为什么 LabUtopia 原生 viewer 正常,进 EBench 后旧图却像倒了?答案不是“原生复杂资产坏了”,而是加载方式变了。LabUtopia 原生场景是在自己的 /World 里完整展开;EBench POC 为了让 evaluator 能按任务识别对象,会把 DryingBox_01 放进任务 wrapper,例如 /World/labutopia_level1_poc/obj_obj_DryingBox_01,再用 payload 把原始资产挂进来。

通俗地说:LabUtopia 原生模式像“箱子还在原包装里”,标签、颜色、零件都在原来的位置;EBench wrapper 像“把箱子搬进一个新房间并重新贴任务编号”。几何形状搬过来了,但旧的颜色标签还指向老房间,Isaac 找不到或不接受这个越界引用,就会退回灰白 fallback material。
原生 LabUtopia

原包装里一切都对得上

原始 DryingBox_01 的 mesh、material 和路径都在同一个 /World 舞台里。门板要找蓝色 material、箱体要找白色 material,都能沿着原来的 material:binding 找到,所以蓝门、白侧面、控制面板显示正常。

EBench wrapper

搬进新房间后标签没跟上

进入 EBench 后,mesh 被挂到 wrapper 下,但旧的 material:binding 还在指原场景里的 material path。这个关系跨出了当前 payload / wrapper 的可见范围,Isaac 会忽略它,于是蓝门和白箱体材质丢失,只剩灰白 fallback,看起来就像箱子姿态不对。

修复后

把颜色标签也搬进新房间

我们在 obj_obj_DryingBox_01 下面创建 wrapper-local Looks,把原始 material 挂进来,再把 door、body、handle、panel 等 mesh/subset 的 material:binding 重新指到 wrapper-local material。这样 EBench 既能识别任务对象,Isaac 也能正确恢复原生材质。

最终结论:旧图“像倒了”不是 native DryingBox 的 physics pose 真的倒置,而是两个证据问题叠加。第一,旧 evidence camera 更偏机器验证,不是给 PM 一眼识别的展示视角;第二,native material:binding 没在 EBench wrapper 内闭环,导致颜色丢失。修复后,同一个 native complex DryingBox_01 能以 upright 状态显示蓝门、白侧面、把手、观察窗和控制面板。
修复项 具体做法 为什么必要
wrapper-local Looks /World/labutopia_level1_poc/obj_obj_DryingBox_01/Looks 下 payload 原始 /World/Looks 让 material prim 和 DryingBox wrapper 在同一个可引用 scope 内。
material rebinding 把 door、handle、panel、body 等 native mesh/subset 的 rel material:binding 指向 wrapper-local material。 避免 Isaac/USD 忽略越界 material target,恢复蓝色门和白色箱体。
retake camera 新增 configs/cameras/labutopia_franka_poc_open_door_native_retake.yml 作为证据相机 override。 让 PM 能一眼看到 upright 箱体、蓝色门、白色侧面、handle、window 和 control panel。
native handle evidence 验证器用 handle 的 scene readback + parent DryingBox native material mask,而不是要求 native handle 必须是 surrogate 的橙色。 真实复杂资产的 handle link frame 不一定等于肉眼看到的把手几何中心,不能照搬 cube-based surrogate 的颜色规则。

先把基础讲清楚:什么是 USD articulation

USD 不是单个模型文件

USD 更像一个“可组合的场景数据库”。一个最终场景通常由很多 layerreferencepayload 和 override 合成。产品上看到的是一个门,工程上看到的是一棵 prim hierarchy 加很多属性。

这也是为什么“资产加载成功”不等于“评测可用”:mesh 可能出现了,但坐标、材质、物理、关节、相机读取任一环节不对,任务图就可能不可读。

Articulation 是带关节的刚体系统

UsdPhysics / PhysX 里,articulation 由多个 rigid links 和 joints 组成。joint position 是每个 DOF 的状态;如果是旋转轴,它就是弧度角。

门最小可用结构可以理解成:箱体是固定 base,门板是一个 link,门板和箱体之间有一个 RevoluteJoint,把手应该挂在门板上。

Root
ArticulationRootAPI
Base
PhysicsFixedJoint
Door DOF
RevoluteJoint
Reset Target
[0.0]
对产品经理的比喻:普通 mesh 像一块静态道具;USD articulation 像一个带合页、门轴、限位、质量和物理状态的舞台机关。道具放进场景只要位置对;机关还必须能稳定转、能复位、能被相机看清。

互动:切换失败模式,看门为什么会坏

下面这个小动画不是 Isaac 渲染结果,而是用于教学的结构示意。切换按钮可以看到我们这次实际排查过的几类风险。

Root scale: [1,1,1]
Joint state: 0.0 rad
Nested handle: /World/.../obj_obj_DryingBox_01/handle
Baseline

P1 对照基线:稳定、可读、可验证

当前正式诊断里,surrogate DryingBox 只暴露 RevoluteJointjoint_positions=[0.0]runtime_physics_stable=true。门板、框架和细橙色把手都在同一张 camera2 readback 图里。这是 native 接入的对照组,不是 native 完成证明。

门角度演示 28 deg

七步走:原生复杂 DryingBox 应该怎么调通

下面是实施路线和当前进度。前五步已经有 evidence:asset audit、native-only Isaac smoke、EBench wrapper、additive physics overrideopen_door eval readback 都已通过;第六步是把证据写进教程和周报;第七步才进入 Lift2 baseline gate。不能把本地 POC PASS 当作官方 Lift2 baseline PASS。

Gate 1

Asset audit:先看清原生资产到底长什么样

只读打开 LabUtopia 原始 /World/DryingBox_01,导出 prim tree、ArticulationRootAPI、所有 RigidBodyAPI、所有 Jointbody0/body1、root scalemass/inertiacenterOfMassprincipalAxes、handle path 和 material binding。

通过标准:拿到 JSON 报告和 USD hash,能明确 native 风险点,而不是靠猜。

我们这次遇到的门问题

问题 产品侧表现 工程侧原因 风险
旧图只看到黑箱角 看不出门在哪里,也看不出要抓哪个把手。 camera2 构图、任务对象显隐、LabUtopia 坐标归一都还没收敛。 PM 无法判断任务是否成立,评测侧也无法把它当作有效视觉证据。
joint position 爆值 门状态不可解释,reset 后可能不是可评的关闭态。 原始 DryingBox 的 USD physics topology 对 Isaac runtime 不稳定,出现过 1.573e13 量级关节读数和 PhysX transform warning 策略还没开始评,场景状态已经不可信。
把手像独立物体 把手位置异常,或者中间版本变成一大片橙色面板。 把手原本应该是 DryingBox 内部子路径 /handle,但导入/封装时曾出现 top-level duplicate 或过大 display marker。 动作点不可读,视觉 QA 会把图判为 WARN 或 FAIL。
原生材质/结构不等于 eval 可读 原生视角可能更像 LabUtopia,但不一定能让任务目标清楚。 P1 目标是 eval-path evidence,所以用了任务专用 camera2、任务级隐藏和高对比 displayColor 如果混淆“展示图”和“评测图”,会误判当前 readiness。

P1 已做什么:从爆值风险到稳定 sanitized_surrogate 对照组

/World/labutopia_level1_poc/obj_obj_DryingBox_01
PhysicsArticulationRootAPI, root scale [1,1,1]
Root
body_link
fixed base, mass and inertia are finite
Base
door_link
connected by RevoluteJoint, reset target [0.0]
Door
/handle
nested under DryingBox, fixed to door_link, scale [0.045,0.075,0.25]
Handle

关键策略

  • P1 先不让不稳定的原始复杂 articulation 直接进入正式评测,而是生成 DryingBox sanitized_surrogate 做可控对照组。
  • BaseFixedJoint 把箱体固定住,避免整个干燥箱在物理里漂移。
  • 只保留一个明确的 RevoluteJoint,让门的状态可以用一个角度解释。
  • 在 reset 后回放 target_positions=[0.0],保证任务从关闭态开始。
  • handle 保持为 DryingBox 内部子部件,而不是 top-level object。
  • 给门板、门缝、把手加 displayColor 和 material binding,让 camera2 读图可解释。

代码层证据

root: /World/labutopia_level1_poc/obj_obj_DryingBox_01
joint: RevoluteJoint
target_positions: [0.0]
handle: /World/labutopia_level1_poc/obj_obj_DryingBox_01/handle
runtime_joint_positions: [0.0]
runtime_physics_stable: true
task_render_accepted: true
official_baseline_evaluable: false

可读性结果

P1 图不是为了还原 LabUtopia 原生 viewer,而是为了证明 evaluator 能看到任务:门板、框架、细橙色把手在同一张图里;render_validation 能检测到 obj_DryingBox_01obj_DryingBox_01_handle 的 bbox 尺寸达标。

P2 已用 native complex DryingBox_01 重新跑过 asset audit、Isaac smoke 和 EBench readback gate。当前边界是:native_complex_dryingbox_ready=truetask_render_accepted=true,但 official_baseline_evaluable=false

Claim Boundary:三件事不能混在一起

Claim 现在可以说 现在不能说
Task render Franka POC 三任务 eval readback 非黑,P1 surrogate baseline 中 render_validation.passed=truetask_render_accepted=true;P2 native DryingBox readback 同样通过任务证据门禁。 不能说策略已经求解成功;当前 smoke 默认动作分数仍是 0.0
Native asset 可以说 LabUtopia native complex DryingBox_01 已通过 Franka POC native gate:mesh、材质、层级和 nested handle 保留,只做必要的 additive physics override 不能说 P2 readback 就是 LabUtopia viewer 原生视角,也不能把它说成官方 Lift2 baseline 成绩。
Official Lift2 baseline 后续需要验证 Lift2 复合资产根目录和官方 runner gate。 不能说官方 Lift2 baseline 已经可评、已执行或已有官方成绩。

旧图和最终图怎么读

Old open door failed render
旧图:只看到黑色箱体角,门板、把手、铰链和动作点都不明确。
Current open door evaluator readback
P1 eval readback:正面可见 DryingBox frame、door panel 和 thin orange handle;正式诊断里 render_validation.passed=true。这张图是 surrogate baseline 证据,不是 native complex DryingBox 调通证据。
Native complex DryingBox evaluator readback
旧 P2 native 反例:已经回到 LabUtopia native complex DryingBox_01,但证据相机和 native material binding 还没闭环,蓝门/白侧面不清楚,所以会让人误以为箱子倒了。
Retake native complex DryingBox evaluator readback
P2 retake native PASS:修复 wrapper-local Looksmaterial:binding 后重拍。箱体 upright,蓝色门、白色侧面、把手、观察窗和控制面板可读;正式诊断里 native_complex_dryingbox_ready=truetask_render_accepted=true

资料来源和我们如何使用

中文解释是面向 PM 的二次整理;英文术语保留原样,便于工程和论文阅读时对齐官方文档。

OpenUSD Introduction 用于解释 USD compositionlayerreference 和 scene graph 不是单个模型文件。
UsdPhysics Schema 用于说明 UsdPhysics 是 USD 中表达 physics simulation representation 的 schema 集合。
UsdPhysicsArticulationRootAPI 用于解释 ArticulationRootAPI 标记 reduced-coordinate articulation subtree,以及 fixed-base root 的放置规则。
UsdPhysicsJoint 用于解释 Joint 如何约束两个 rigid bodies,或约束 rigid body 与 world。
Omni Physics Articulations 用于对齐 Isaac/Omniverse 对 USD hierarchy、fixed-base articulation 和 root API 的实践要求。
PhysX Articulations 用于解释 reduced-coordinate articulation 维护 joint positions / velocities / accelerations,旋转轴 joint position 是 radians。
NVIDIA OpenUSD for Developers 用于 PM 口径解释 OpenUSD 是建模、分类、组合多数据源的生态,不只是文件格式。
AOUSD Explainer: What Is OpenUSD? 用于补充 layeringcomposition 和协作式资产组织的非官方解读。

项目证据来自 saved/diagnostics/labutopia_p1_gate_open_door_formal_20260624_0002/diagnostics.jsonsaved/diagnostics/native_dryingbox_visual_retake_final_20260624_0002/diagnostics.jsonstandalone_tools/labutopia_poc/build_asset_overlay.py 和周报 evidence manifest。