| """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) |
| |
| |
| |
| 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, |
| |
| |
| |
| |
| 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) |
| |
| |
| |
| 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) |
|
|