"""Full-length contact registration; object poses are inferred targets, not physics.""" import json from pathlib import Path import numpy as np from scipy.spatial.transform import Rotation,Slerp from l20_contact import ContactRefiner from l20_calibrated import ROOT BASE=ROOT/'output/l20_2047635068';OUT=BASE/'bottle_grasp' def main(): motion=np.load(BASE/'motion.npz');spec=json.loads((OUT/'object_spec.json').read_text()) solver=ContactRefiner(spec,OUT) count=len(motion['qpos']);times=np.arange(count) rotation=Rotation.from_quat(motion['wrist_quat_wxyz'][:,[1,2,3,0]]) reference=666 # Infer a constant bottle-to-palm registration over the holding phase. rel_pos=rotation[reference].inv().apply(np.array(spec['position'])-motion['wrist_pos'][reference]) rel_rot=rotation[reference].inv()*Rotation.from_quat(np.array(spec['quat_wxyz'])[[1,2,3,0]]) bottle_pos=rotation.apply(np.tile(rel_pos,(count,1)))+motion['wrist_pos'] bottle_rot=rotation*rel_rot # Contact onset bracket comes from visual inspection: open at 100, grasped by 200. onset=160 bottle_pos[:onset]=bottle_pos[onset] bottle_quat=bottle_rot.as_quat()[:,[3,0,1,2]] bottle_quat[:onset]=bottle_quat[onset] samples=np.unique(np.r_[np.arange(onset,count,8),count-1]) fits=[];reports=[] for t in samples: active=np.array([motion['qpos'][t,list(motion['joint_names']).index(n)] for n in solver.cal.active]) x,q,report=solver.solve(active,motion['wrist_pos'][t],motion['wrist_quat_wxyz'][t],bottle_pos[t],bottle_quat[t]) fits.append(x);reports.append(report) if len(fits)%20==0:print(f'contact keyframes {len(fits)}/{len(samples)}',flush=True) fits=np.array(fits) interp=np.stack([np.interp(times,samples,fits[:,j]) for j in range(22)],axis=1) source_active=np.stack([motion['qpos'][:,list(motion['joint_names']).index(n)] for n in solver.cal.active],axis=1) # Blend acquisition only; preserve pre-contact source motion. weight=np.clip((times-(onset-30))/30,0,1);weight=weight*weight*(3-2*weight) interp[:,:16]=source_active*(1-weight[:,None])+interp[:,:16]*weight[:,None] interp[:,16:]*=weight[:,None] repairs=[] for t in range(onset,count): solver.set_pose(interp[t],motion['wrist_pos'][t],motion['wrist_quat_wxyz'][t],bottle_pos[t],bottle_quat[t]) if -solver.distances().min()>.0015: x,_,r=solver.solve(source_active[t],motion['wrist_pos'][t],motion['wrist_quat_wxyz'][t],bottle_pos[t],bottle_quat[t],initial=interp[t]) if r['max_penetration_after_mm']>1.5 and t>onset: alternative,_,retry=solver.solve(source_active[t],motion['wrist_pos'][t],motion['wrist_quat_wxyz'][t],bottle_pos[t],bottle_quat[t],initial=interp[t-1]) if retry['max_penetration_after_mm']