fix(assets): stabilize L20 fixed-base PhysX preview

原因:保存 L20 资产、固定基座、地面光照、相邻碰撞过滤和拇指受限驱动,
修正过短时间轴;USD 依赖通过 Git LFS 管理,转换元数据移除个人路径。
包含 GUI 有限步数拇指回归入口与验证记录。

验证:用户 Isaac Sim 6.0.1 GUI 中等价回归代码完成 3840 物理步,PASS。
提交前 pxr Stage.Open/ComputeAllDependencies:10 层、0 未解析依赖、0 组合错误。
Python AST 语法、git diff --cached --check、LFS 指针哈希与 git lfs fsck 均 PASS。
pre-commit run --all-files:BLOCKED,命令未安装;仓库许可证模板也缺失。
Isaac Lab 任务发现/训练 E2E NOT_RUN:当前资产尚未接入任务,不启动额外仿真。

兼容性:包版本维持 0.1.0,现有 Cartpole 配置未改变;需要 Git LFS。
armature 为未标定数值参数;mimic 耦合、其他手指大动作、reset/replay 和
长时间 RTX 渲染资源耗尽仍未验收。本提交不表示训练或发布就绪。
This commit is contained in:
2026-09-10 16:44:45 +08:00
parent 35621dde47
commit 071a1bdff5
14 changed files with 208 additions and 1 deletions
@@ -0,0 +1,61 @@
"""Run inside Isaac Sim to test L20 thumb slider jumps; pauses on completion.
Results: builtins._l20_drive_result. This changes the slider target temporarily.
Requires a clean initial scene and a nonlooping timeline long enough for 16 seconds.
"""
import asyncio, builtins
async def drive_test():
import asyncio, time, builtins, numpy as np, omni.usd, omni.timeline, omni.kit.app, omni.physx
from isaacsim.core.simulation_manager import SimulationManager
from pxr import Usd
s = omni.usd.get_context().get_stage()
tl = omni.timeline.get_timeline_interface()
joint = s.GetPrimAtPath('/tn__linkerhand_g20_lefturdf_cZ0/Physics/thumb_cmc_roll')
target = joint.GetAttribute('drive:angular:physics:targetPosition')
r = {'status': 'RUNNING', 'steps': 0, 'segments': [], 'max_speed': 0.0}
builtins._l20_drive_result = r
def step(dt):
r['steps'] += 1
sub = omni.physx.get_physx_interface().subscribe_physics_step_events(step)
def arr(x):
return x.numpy() if hasattr(x, 'numpy') else np.asarray(x)
try:
tl.play()
a = None
deadline = time.monotonic() + 60
for goal in [0, 30, 60, 80, 0, -20, 100, 0]:
with Usd.EditContext(s, s.GetRootLayer()):
target.Set(float(goal))
start = r['steps']
while r['steps'] - start < 480:
await asyncio.wait_for(omni.kit.app.get_app().next_update_async(), 10)
assert time.monotonic() < deadline, 'Wall timeout'
if r['steps'] < 4:
continue
if a is None:
a = SimulationManager.get_physics_simulation_view().create_articulation_view('/tn__linkerhand_g20_lefturdf_cZ0/Physics/fixed_base_joint')
names = list(a.shared_metatype.dof_names)
idx = names.index('thumb_cmc_roll')
q = arr(a.get_dof_positions())
v = arr(a.get_dof_velocities())
links = arr(a.get_link_transforms())
assert all((np.isfinite(x).all() for x in [q, v, links])), 'Nonfinite state'
r['max_speed'] = max(r['max_speed'], float(np.abs(v).max()))
assert np.abs(v).max() < 10, 'Excessive speed'
assert np.abs(links[..., :3]).max() < 0.5, 'Escaped link'
assert q[0, idx] > -0.05 and q[0, idx] < 1.45, 'Roll limit violation'
actual = float(np.rad2deg(q[0, idx]))
r['segments'].append({'target_deg': goal, 'actual_deg': actual})
assert abs(actual - min(80.214, max(0, goal))) < 3, 'Tracking error ' + str(actual)
r['status'] = 'PASS'
except Exception as e:
r['status'] = 'FAIL'
r['error'] = str(e)
finally:
tl.pause()
sub = None
builtins._l20_drive_task = asyncio.ensure_future(drive_test())
print('Started bounded thumb target-jump test')