mp_yam_code / scripts /robotwin_urdf_convert.py
yqi19's picture
YAM bimanual task suite: env, solvers, tasks, converters
7399b6f verified
Raw
History Blame Contribute Delete
3.81 kB
"""Convert an ARTICULATED RoboTwin object (SAPIEN-style URDF) to USD.
Most RoboTwin categories ship a single fused GLB, but some also ship articulated variants under
numbered sub-directories with a `mobility.urdf` and a `semantics.txt`. `060_kitchenpot/100015`,
for instance, is `link_0 = lid` on a PRISMATIC joint plus `link_1 = pot_body` -- i.e. a pot whose
lid genuinely lifts off, which the fused GLB cannot do.
ROBOTWIN_SRC=... ROBOTWIN_USD=... \
python scripts/robotwin_urdf_convert.py --headless --jobs 060_kitchenpot/100015
"""
import argparse, os, sys
from isaaclab.app import AppLauncher
p = argparse.ArgumentParser()
p.add_argument("--jobs", default="060_kitchenpot/100015",
help="comma-separated <category>/<instance> dirs holding a mobility.urdf")
p.add_argument("--fix_base", action="store_true", help="pin the base in place (a static fixture)")
AppLauncher.add_app_launcher_args(p)
a = p.parse_args(); a.headless = True
app = AppLauncher(a).app
from isaaclab.sim.converters import UrdfConverter, UrdfConverterCfg
SRC = os.environ.get("ROBOTWIN_SRC", "/home/yu/internship_yu/robotwin_object/objects")
OUT = os.environ.get("ROBOTWIN_USD", "/home/yu/internship_yu/robotwin_usd")
os.makedirs(OUT, exist_ok=True)
for job in [j for j in a.jobs.split(",") if j]:
cat, inst = job.split("/")
urdf = f"{SRC}/{cat}/{inst}/mobility.urdf"
if not os.path.exists(urdf):
print(f"[urdf] MISSING {urdf}", flush=True); continue
sem = f"{SRC}/{cat}/{inst}/semantics.txt"
if os.path.exists(sem):
print(f"[urdf] {cat}/{inst} parts: {open(sem).read().strip().splitlines()}", flush=True)
# `fix_base` and the joint drive's PD gains are required fields in this Isaac Lab version --
# a lid on a prismatic joint needs a soft drive so the arm can pull it open rather than
# fighting a stiff actuator.
cfg = UrdfConverterCfg(
asset_path=urdf, usd_dir=f"{OUT}/{cat}", usd_file_name=f"{inst}.usd",
force_usd_conversion=True, fix_base=a.fix_base,
# THE default here is "convex_hull", and it is wrong for every one of these fixtures: it
# collapses a whole microwave into a single box, swallowing the door, and the door's
# collider then interpenetrates the body's so the hinge cannot move at all. It also makes
# the asset RENDER as a featureless cube. Decomposition keeps the parts apart.
collider_type="convex_decomposition",
joint_drive=UrdfConverterCfg.JointDriveCfg(
target_type="none",
gains=UrdfConverterCfg.JointDriveCfg.PDGainsCfg(stiffness=0.0, damping=2.0),
),
)
try:
conv = UrdfConverter(cfg)
# The importer swallows its own failures: a hyphen in a mesh filename makes it die with
# "Used null prim", write a 492-byte stub, and return normally. Size-check the result so
# a broken conversion is caught here instead of as a missing ArticulationRootAPI later.
size = os.path.getsize(conv.usd_path) if os.path.exists(conv.usd_path) else 0
payload = sum(os.path.getsize(os.path.join(r, f))
for r, _d, fs in os.walk(os.path.dirname(conv.usd_path)) for f in fs)
if payload < 12_000:
print(f"[urdf] FAIL {cat}/{inst}: importer produced an EMPTY asset "
f"({size} B stub, {payload} B total). Almost always a USD-illegal character in "
f"a mesh/link name -- run scripts/sanitize_urdf_asset.py {cat}/{inst} first.",
flush=True)
else:
print(f"[urdf] OK {cat}/{inst} -> {conv.usd_path} ({payload//1024} KB)", flush=True)
except Exception as e:
print(f"[urdf] FAIL {cat}/{inst}: {type(e).__name__}: {e}", flush=True)
app.close(); print("URDF_CONVERT_DONE", flush=True)