ae28d55f81
Source: RGB-D -> Dyn-HaMR -> L20 retargeting -> FoundationPose -> reference repair -> SPIDER, documented in docs/PIPELINE_LATEST.md and docs/SETUP_AND_WEIGHTS.md. Adds FoundationPose and nvdiffrast upstream snapshots, requirements/pipeline_venv.txt and the FoundationPose weight manifest/downloader. Assets (Git LFS): weights/ (WiLoR detector, HandFlow denoiser, UniDepth-L), FoundationPose checkpoints, HaMeR checkpoint, Dyn-HaMR HMP model and BMC constraints, L20 URDF/meshes, the 20260915_171525 D405 recording and the two box CADs. MANO models are not redistributed (third_party/hamer/_DATA/data/mano/README.txt). Environments, caches and run outputs excluded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
61 lines
3.6 KiB
Python
61 lines
3.6 KiB
Python
"""Interactive wall-clock playback of saved SPIDER states (no re-simulation)."""
|
|
import argparse,os,time,json
|
|
from pathlib import Path
|
|
p=argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument('--directory',type=Path,default=Path(__file__).resolve().parents[1]/'output/spider_dynamics_fix_20260915')
|
|
p.add_argument('--mode',choices=['physics','reference'],default='physics')
|
|
p.add_argument('--check',action='store_true');a=p.parse_args()
|
|
os.environ['MUJOCO_GL']='osmesa' if a.check else 'glfw'
|
|
import numpy as np,mujoco
|
|
root=a.directory.resolve();task=root/'datasets/processed/current/l20/bimanual/boxes'
|
|
m=mujoco.MjModel.from_xml_path(str(task/'scene_act.xml'));d=mujoco.MjData(m)
|
|
source=root/('reference_video_rate.npz' if a.mode=='reference' else 'physics_motion.npz')
|
|
mode='collision-corrected kinematic reference (middle video panel)' if a.mode=='reference' else 'saved physical state playback, no re-simulation'
|
|
with np.load(source) as z:
|
|
q=z['qpos'];t=z['time']
|
|
if a.mode=='physics':u=z['ctrl']
|
|
if a.mode=='reference':
|
|
v=np.zeros((len(q),m.nv));u=q[:,m.jnt_qposadr[m.actuator_trnid[:,0]]]
|
|
else:
|
|
with np.load(task/'0/trajectory_mjwp_act.npz') as z:v=z['qvel'].reshape(len(q),-1)
|
|
assert q.shape==(len(t),m.nq) and np.isfinite(q).all() and np.all(np.diff(t)>0)
|
|
if a.check:
|
|
for i in [0,len(q)//2,len(q)-1]:
|
|
d.qpos[:]=q[i];d.qvel[:]=v[i];d.ctrl[:]=u[i];mujoco.mj_forward(m,d)
|
|
print(json.dumps({'steps':len(q),'duration_s':float(t[-1]),'nq':m.nq,'finite':True,'mode':mode}));raise SystemExit
|
|
import mujoco.viewer
|
|
state={'paused':False,'time':0.,'speed':1.,'reset_camera':False}
|
|
def key(k):
|
|
if k==32:state['paused']=not state['paused']
|
|
elif k in [82,114]:state['time']=0.
|
|
elif k in [262,263]:state['time']=float(np.clip(state['time']+(1 if k==262 else -1)/30,0,t[-1]));state['paused']=True
|
|
elif k==265:state['speed']=min(4.,state['speed']*2)
|
|
elif k==264:state['speed']=max(.125,state['speed']/2)
|
|
elif k in [67,99]:state['reset_camera']=True
|
|
m.vis.headlight.ambient[:]=.7
|
|
d.qpos[:]=q[0];mujoco.mj_forward(m,d)
|
|
center=np.mean([d.site_xpos[m.site(s+'_object_track').id] for s in ['right','left']],axis=0)
|
|
cid=m.camera('source_camera').id;offset=d.cam_xpos[cid]-center
|
|
camera={'distance':float(np.linalg.norm(offset)),'azimuth':float(np.degrees(np.arctan2(offset[1],offset[0]))),'elevation':float(-np.degrees(np.arctan2(offset[2],np.linalg.norm(offset[:2]))))}
|
|
status=root/'interactive_playback_status.json'
|
|
print(mode+' | SPACE pause | R restart | LEFT/RIGHT video-frame step | UP/DOWN speed | C camera reset | mouse orbit/zoom',flush=True)
|
|
with mujoco.viewer.launch_passive(m,d,key_callback=key) as viewer:
|
|
def reset_camera():
|
|
viewer.cam.type=mujoco.mjtCamera.mjCAMERA_FREE;viewer.cam.lookat[:]=center
|
|
for k,x in camera.items():setattr(viewer.cam,k,x)
|
|
with viewer.lock():
|
|
reset_camera();viewer.opt.sitegroup[:]=0;viewer.opt.geomgroup[3:]=0
|
|
last=time.perf_counter();last_status=0
|
|
while viewer.is_running():
|
|
now=time.perf_counter();elapsed=now-last;last=now
|
|
if not state['paused']:state['time']=(state['time']+elapsed*state['speed'])%float(t[-1])
|
|
i=int(np.clip(np.searchsorted(t,state['time'],side='right')-1,0,len(t)-1))
|
|
with viewer.lock():
|
|
if state['reset_camera']:reset_camera();state['reset_camera']=False
|
|
d.qpos[:]=q[i];d.qvel[:]=v[i];d.ctrl[:]=u[i];d.time=float(t[i]);mujoco.mj_forward(m,d)
|
|
viewer.sync()
|
|
if now-last_status>1:
|
|
status.write_text(json.dumps({'running':True,'pid':os.getpid(),'mode':mode,'source':str(source),'step':i,'time_s':float(t[i]),'speed':state['speed'],'paused':state['paused'],'updated_unix':time.time()},indent=2));last_status=now
|
|
time.sleep(.008)
|
|
status.write_text(json.dumps({'running':False,'pid':os.getpid(),'updated_unix':time.time()}))
|