File size: 8,129 Bytes
7399b6f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | """YAM LONG-HORIZON task: a primitive box (built from Cuboids, license-clean) sits in the
middle; the right arm picks each object and drops it into the box, one after another, until
ALL target objects are in the box. Each pick = PRM approach + REAL friction grasp (no attach).
Renders one continuous Isaac Sim video."""
import argparse, sys, os
from isaaclab.app import AppLauncher
parser=argparse.ArgumentParser()
parser.add_argument("--video", default="outputs/yam_longhorizon.mp4")
parser.add_argument("--objects", default="can,can2,grape")
AppLauncher.add_app_launcher_args(parser)
args=parser.parse_args(); args.headless=True; args.enable_cameras=True
app=AppLauncher(args).app
import numpy as np, torch, gymnasium as gym, json as _json
import imageio.v2 as imageio
REPO=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,os.path.join(REPO,"source")); sys.path.insert(0,os.path.dirname(os.path.abspath(__file__)))
import yam_prm
import bimanual.tasks.manager_based.yam # noqa
from isaaclab_tasks.utils import parse_env_cfg
TASK="Template-YAM-Play-v0"; dev="cuda:0"
_cfg=parse_env_cfg(TASK, device=dev, num_envs=1)
# These demos script one long manipulation sequence; the 12 s task episode length would
# auto-reset the env mid-run and snap the arm back to its home joints (a visible pose jump).
_cfg.episode_length_s = 1.0e6
try:
_cfg.terminations.time_out = None
except Exception as _e:
print('[cfg] time_out disable failed:', _e)
try: _cfg.viewer.eye=(1.0,-1.0,1.2); _cfg.viewer.lookat=(0.05,0.0,0.5); _cfg.viewer.resolution=(720,540)
except Exception as _e: print("viewer cfg:",_e)
env=gym.make(TASK, cfg=_cfg, render_mode="rgb_array"); u=env.unwrapped; env.reset()
def Rq(q):
w,x,y,z=q; return np.array([[1-2*(y*y+z*z),2*(x*y-z*w),2*(x*z+y*w)],[2*(x*y+z*w),1-2*(x*x+z*z),2*(y*z-x*w)],[2*(x*z-y*w),2*(y*z+x*w),1-2*(x*x+y*y)]])
def qR(m):
t=m[0,0]+m[1,1]+m[2,2]
if t>0: s=np.sqrt(t+1)*2; w=.25*s; x=(m[2,1]-m[1,2])/s; y=(m[0,2]-m[2,0])/s; z=(m[1,0]-m[0,1])/s
elif m[0,0]>m[1,1] and m[0,0]>m[2,2]: s=np.sqrt(1+m[0,0]-m[1,1]-m[2,2])*2; w=(m[2,1]-m[1,2])/s; x=.25*s; y=(m[0,1]+m[1,0])/s; z=(m[0,2]+m[2,0])/s
elif m[1,1]>m[2,2]: s=np.sqrt(1+m[1,1]-m[0,0]-m[2,2])*2; w=(m[0,2]-m[2,0])/s; x=(m[0,1]+m[1,0])/s; y=.25*s; z=(m[1,2]+m[2,1])/s
else: s=np.sqrt(1+m[2,2]-m[0,0]-m[1,1])*2; w=(m[1,0]-m[0,1])/s; x=(m[0,2]+m[2,0])/s; y=(m[1,2]+m[2,1])/s; z=.25*s
q=np.array([w,x,y,z]); q/=np.linalg.norm(q)+1e-9; return q if q[0]>=0 else -q
origin=u.scene.env_origins[0].cpu().numpy()
R=u.scene["right_robot"]; Rbn=list(R.data.body_names)
L=u.scene["left_robot"]; Lbn=list(L.data.body_names)
def root_of(a): return a.data.root_pos_w[0].cpu().numpy()-origin, a.data.root_quat_w[0].cpu().numpy()
rroot,rrootq=root_of(R); lroot,lrootq=root_of(L)
OFF=np.array([0,0,0.13])
# ---- central primitive box (Cuboids only, license-clean) ----
BASKET=np.array([0.10,0.0,0.45],np.float32) # env-local, table height, reachable center
import isaaclab.sim as sim_utils
S,H,T=0.16,0.10,0.010
def _cub(name,size,off,color=(0.55,0.38,0.22)):
c=sim_utils.CuboidCfg(size=tuple(size),visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=color),
collision_props=sim_utils.CollisionPropertiesCfg())
c.func(f"/World/envs/env_0/basket_{name}",c,translation=tuple((origin+BASKET+np.array(off,np.float32)).astype(float).tolist()))
_cub("floor",(S,S,T),(0,0,T/2))
_cub("xp",(T,S,H),(S/2,0,H/2)); _cub("xn",(T,S,H),(-S/2,0,H/2))
_cub("yp",(S,T,H),(0,S/2,H/2)); _cub("yn",(S,T,H),(0,-S/2,H/2))
print(f"[lh] built central box at world={np.round(origin+BASKET,3)} span={S} h={H}", flush=True)
def eef_root(a,bn,root,rootq):
i=bn.index("link_6"); p=a.data.body_pos_w[0,i].cpu().numpy()-origin; q=a.data.body_quat_w[0,i].cpu().numpy()
return Rq(rootq).T@((p+Rq(q)@OFF)-root), q
lp0,lq0=eef_root(L,Lbn,lroot,lrootq); rp0,rq0=eef_root(R,Rbn,rroot,rrootq)
OPEN,CLOSE=1.0,-1.0
def act(rp,rq,rg): return torch.tensor(np.concatenate([lp0,lq0,[1.0],rp,rq,[rg]]),dtype=torch.float32,device=dev).view(1,-1)
for _ in range(60): env.step(act(rp0,rq0,OPEN))
lhome_q=L.data.joint_pos[0].clone(); _lz=torch.zeros((1,lhome_q.shape[0]),device=dev)
def freeze_left(): L.write_joint_state_to_sim(lhome_q.view(1,-1),_lz)
def boost(view,s=1.6,d=1.4):
try:
m=view.get_material_properties().clone(); m[...,0]=s; m[...,1]=d
view.set_material_properties(m,torch.arange(m.shape[0],dtype=torch.int32,device=m.device))
except Exception as e: print("[lh] fric fail",e)
boost(R.root_physx_view)
for o in args.objects.split(","): boost(u.scene.rigid_objects[o].root_physx_view)
Rg=np.stack([np.array([0.,1.,0.]),np.array([1.,0.,0.]),np.array([0.,0.,-1.])],axis=1); gq=qR(Rg)
frames=[]
def snap():
img=env.render()
if img is not None: frames.append(np.asarray(img)[...,:3])
def r_eef(): p,_=eef_root(R,Rbn,rroot,rrootq); return p
def rsep():
jn=list(R.data.joint_names); return (R.data.joint_pos[0,jn.index("left_finger")].item()+R.data.joint_pos[0,jn.index("right_finger")].item())/2
def objw(name): return u.scene.rigid_objects[name].data.root_pos_w[0].cpu().numpy()-origin
def obj_root(name): return Rq(rrootq).T@(objw(name)-rroot)
def go(tgt,g,n,tol=None):
for k in range(n):
env.step(act(tgt.astype(np.float32),gq,g)); freeze_left()
if k%6==0: snap()
if tol and np.linalg.norm(r_eef()-tgt)<tol: break
def close_stall(tgt,n=140):
prev=rsep(); st=0
for k in range(n):
env.step(act(tgt.astype(np.float32),gq,CLOSE)); freeze_left()
if k%6==0: snap()
cur=rsep()
if abs(cur-prev)<0.0002: st+=1
else: st=0
prev=cur
if st>=8 and cur<-0.002: break
BASKET_root=(Rq(rrootq).T@(BASKET-rroot)).astype(np.float32)
DROP=BASKET_root+np.array([0,0,0.18],np.float32) # release point, above the box walls
DROP_HI=DROP+np.array([0,0,0.10],np.float32)
def pick_place(name, idx):
o=obj_root(name)
pre=o+np.array([0,0,0.12],np.float32); grasp=o+np.array([0,0,-0.05],np.float32); lift=grasp+np.array([0,0,0.24],np.float32)
print(f"[lh] --- object {idx}: {name} obj_root={np.round(o,3)} ---", flush=True)
# PRM approach from current eef to pre
prm=yam_prm.PRM(bounds_lo=np.array([-0.05,-0.35,-0.03]),bounds_hi=np.array([0.55,0.45,0.4]),obstacles=[],clearance=0.03,num_samples=150,k=10,seed=1)
p=prm.plan(r_eef().astype(np.float64),pre.astype(np.float64))
if p is None: p=np.stack([r_eef(),pre]).astype(np.float32)
wps=yam_prm.resample_polyline(yam_prm.shortcut(p,[],0.03,iters=80,seed=2),10).astype(np.float32)
for i in range(1,len(wps)): go(wps[i],OPEN, 70 if i==len(wps)-1 else 18, tol=(0.02 if i==len(wps)-1 else None))
go(grasp,OPEN,80) # descend
print(f"[lh] descend err={np.linalg.norm(r_eef()-grasp):.3f}", flush=True)
close_stall(grasp) # clamp
zg0=objw(name)[2]
go(lift,CLOSE,70) # lift
zg1=objw(name)[2]
print(f"[lh] grasp lift dz={zg1-zg0:.3f} lifted={zg1-zg0>0.05}", flush=True)
go(DROP_HI,CLOSE,90); go(DROP,CLOSE,45) # carry over the box
go(DROP,OPEN,45) # release into box
go(DROP_HI,OPEN,30) # retreat up
return zg1-zg0>0.05
order=args.objects.split(",")
results={}
for i,name in enumerate(order,1):
results[name]=pick_place(name,i)
# return home-ish
go(rp0,OPEN,50)
# ---- success check: how many objects ended up inside the box footprint ----
inb=0
for name in order:
w=objw(name); dx=abs(w[0]-BASKET[0]); dy=abs(w[1]-BASKET[1])
inside = dx<S/2+0.02 and dy<S/2+0.02 and w[2]<BASKET[2]+0.14
inb += int(inside)
print(f"[lh] {name}: final world=({w[0]:.3f},{w[1]:.3f},{w[2]:.3f}) in_box={inside}", flush=True)
os.makedirs(os.path.dirname(args.video),exist_ok=True)
if frames: imageio.mimsave(args.video, frames, fps=7)
print(f"[lh] DONE: {inb}/{len(order)} objects in the box -> {args.video} ({len(frames)} frames)", flush=True)
env.close(); app.close(); print("YAM_LONGHORIZON_OK", flush=True)
|