Download scripts/yam_mesh_export.py from yqi19/mp_yam_code: direct link, hf CLI and curl.
- Browser
- Download file 4.73 kB
-
https://huggingface.co/yqi19/mp_yam_code/resolve/main/scripts/yam_mesh_export.py
- Command line
-
hf download hf://yqi19/mp_yam_code/scripts/yam_mesh_export.py
-
curl -L -o yam_mesh_export.py https://huggingface.co/yqi19/mp_yam_code/resolve/main/scripts/yam_mesh_export.py
4.73 kB
| """Export YAM right-arm per-link meshes (in link-local frame) to OBJ for the web viewer.""" | |
| import argparse, sys, os | |
| from isaaclab.app import AppLauncher | |
| p=argparse.ArgumentParser(); AppLauncher.add_app_launcher_args(p); a=p.parse_args(); a.headless=True; a.enable_cameras=False | |
| app=AppLauncher(a).app | |
| import numpy as np, gymnasium as gym | |
| REPO=os.path.dirname(os.path.dirname(os.path.abspath(__file__))); sys.path.insert(0,os.path.join(REPO,"source")) | |
| import bimanual.tasks.manager_based.yam # noqa | |
| from isaaclab_tasks.utils import parse_env_cfg | |
| TASK="Template-YAM-Play-v0" | |
| env=gym.make(TASK, cfg=parse_env_cfg(TASK, device="cuda:0", num_envs=1)); u=env.unwrapped; env.reset() | |
| R=u.scene["right_robot"] | |
| import omni.usd | |
| from pxr import Usd, UsdGeom, Gf | |
| stage=omni.usd.get_context().get_stage() | |
| root_path=R.root_physx_view.prim_paths[0] | |
| print("[m] root:", root_path, flush=True) | |
| xf=UsdGeom.XformCache(Usd.TimeCode.Default()) | |
| OUT="/home/horde/project/xiaotong/outputs/yam_web/mesh" | |
| os.makedirs(OUT, exist_ok=True) | |
| def find_link_prim(name): | |
| for pr in Usd.PrimRange(stage.GetPrimAtPath(root_path)): | |
| if pr.GetName()==name: | |
| return pr | |
| return None | |
| def gfmat_to_np(m): | |
| return np.array([[m[i][j] for j in range(4)] for i in range(4)]) | |
| LINKS=["arm","link_1","link_2","link_3","link_4","link_5","link_6","left_finger","right_finger"] | |
| linkpath={}; Lwinv={} | |
| for n in LINKS: | |
| lp=find_link_prim(n) | |
| if lp is None: continue | |
| linkpath[lp.GetPath().pathString]=n | |
| Lwinv[n]=np.linalg.inv(gfmat_to_np(xf.GetLocalToWorldTransform(lp))) | |
| # fingers are rigidly (approx) under link_6; export them in link_6's frame so they | |
| # mount onto link_6's group in the viewer (their small prismatic slide is ignored for viz). | |
| FRAME={n:n for n in LINKS} | |
| FRAME["left_finger"]="link_6"; FRAME["right_finger"]="link_6" | |
| def owning_link(prim): | |
| p=prim.GetPath() | |
| while not p.isEmpty and p.pathString!="/": | |
| if p.pathString in linkpath: return linkpath[p.pathString] | |
| p=p.GetParentPath() | |
| return None | |
| buck={n:{"V":[],"F":[]} for n in LINKS} | |
| for pr in Usd.PrimRange(stage.GetPrimAtPath(root_path)): | |
| if pr.GetTypeName()!="Mesh": continue | |
| ln=owning_link(pr) | |
| if ln is None: continue | |
| mesh=UsdGeom.Mesh(pr); pts=mesh.GetPointsAttr().Get() | |
| if not pts: continue | |
| counts=mesh.GetFaceVertexCountsAttr().Get(); idx=mesh.GetFaceVertexIndicesAttr().Get() | |
| Mw=gfmat_to_np(xf.GetLocalToWorldTransform(pr)) | |
| M=Mw@Lwinv[FRAME[ln]] | |
| V=buck[ln]["V"]; F=buck[ln]["F"]; base=len(V) | |
| P=np.array([[p[0],p[1],p[2],1.0] for p in pts]); Pl=P@M | |
| for q in Pl: V.append((q[0],q[1],q[2])) | |
| o=0 | |
| for c in counts: | |
| for k in range(1,c-1): F.append((base+idx[o]+1, base+idx[o+k]+1, base+idx[o+k+1]+1)) | |
| o+=c | |
| manifest={} | |
| for ln in LINKS: | |
| V=buck[ln]["V"]; F=buck[ln]["F"] | |
| if not V: print(f"[m] {ln}: no mesh", flush=True); continue | |
| fn=os.path.join(OUT, ln+".obj") | |
| with open(fn,"w") as f: | |
| for v in V: f.write(f"v {v[0]:.5f} {v[1]:.5f} {v[2]:.5f}\n") | |
| for t in F: f.write(f"f {t[0]} {t[1]} {t[2]}\n") | |
| manifest[ln]={"verts":len(V),"faces":len(F)} | |
| print(f"[m] {ln}: {len(V)} verts {len(F)} tris -> {fn}", flush=True) | |
| import json; json.dump(manifest, open(os.path.join(OUT,"manifest.json"),"w")) | |
| # ---- authoritative chain: per-body world pose at HOME (from articulation) + joint axes ---- | |
| import numpy as _np | |
| origin_env=u.scene.env_origins[0].cpu().numpy() | |
| bn=list(R.data.body_names) | |
| def body_pose(name): | |
| i=bn.index(name) | |
| p=R.data.body_pos_w[0,i].cpu().numpy()-origin_env | |
| q=R.data.body_quat_w[0,i].cpu().numpy() # wxyz | |
| return [float(x) for x in p],[float(x) for x in q] | |
| # joint axes from USD physics revolute joints (axis token X/Y/Z in the joint's child frame) | |
| from pxr import UsdPhysics | |
| axis_map={} | |
| for pr in Usd.PrimRange(stage.GetPrimAtPath(root_path)): | |
| if pr.IsA(UsdPhysics.RevoluteJoint): | |
| j=UsdPhysics.RevoluteJoint(pr) | |
| ax=j.GetAxisAttr().Get() | |
| rel=j.GetBody1Rel().GetTargets() | |
| child=rel[0].name if rel else pr.GetName() | |
| axis_map[child]={"axis":str(ax)} | |
| CHAIN=["arm","link_1","link_2","link_3","link_4","link_5","link_6"] | |
| pose={} | |
| for n in CHAIN: | |
| try: pose[n]={"pos":body_pose(n)[0],"quat":body_pose(n)[1]} | |
| except Exception as e: print("[m] pose err",n,e,flush=True) | |
| out={"pose":pose,"axis":axis_map, | |
| "home":[0.0,1.5708,0.7854,0.7854,0.0,0.0], | |
| "joint_child":["link_1","link_2","link_3","link_4","link_5","link_6"]} | |
| json.dump(out, open(os.path.join(OUT,"chain_truth.json"),"w")) | |
| print("[m] pose:", {n:[round(v,4) for v in pose[n]["pos"]] for n in pose}, flush=True) | |
| print("[m] axis_map:", axis_map, flush=True) | |
| env.close(); app.close(); print("MESH_OK", flush=True) | |