Skip to content

Commit 0f58b3e

Browse files
authored
Adds script to convert urdfs/meshes to instanceable USD in batches (#2248)
# Description Adding a script that converts URDFs and Meshes to instanceable formatted usd in batch (recursively searching dir) so that users do not have to do it one at a time ## Type of change - New feature (non-breaking change which adds functionality) ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
1 parent 505679f commit 0f58b3e

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
"""
7+
Utility to bulk convert URDFs or mesh files into instanceable USD format.
8+
9+
Unified Robot Description Format (URDF) is an XML file format used in ROS to describe all elements of
10+
a robot. For more information, see: http://wiki.ros.org/urdf
11+
12+
This script uses the URDF importer extension from Isaac Sim (``omni.isaac.urdf_importer``) to convert a
13+
URDF asset into USD format. It is designed as a convenience script for command-line use. For more
14+
information on the URDF importer, see the documentation for the extension:
15+
https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/ext_omni_isaac_urdf.html
16+
17+
18+
positional arguments:
19+
input The path to the input directory containing URDFs and Meshes.
20+
output The path to directory to store the instanceable files.
21+
22+
optional arguments:
23+
-h, --help Show this help message and exit
24+
--conversion-type Select file type to convert, urdf or mesh. (default: urdf)
25+
--merge-joints Consolidate links that are connected by fixed joints. (default: False)
26+
--fix-base Fix the base to where it is imported. (default: False)
27+
--make-instanceable Make the asset instanceable for efficient cloning. (default: False)
28+
29+
"""
30+
31+
"""Launch Isaac Sim Simulator first."""
32+
33+
import argparse
34+
35+
from isaaclab.app import AppLauncher
36+
37+
# add argparse arguments
38+
parser = argparse.ArgumentParser(description="Utility to convert a URDF or mesh into an Instanceable asset.")
39+
parser.add_argument("input", type=str, help="The path to the input directory.")
40+
parser.add_argument("output", type=str, help="The path to directory to store converted instanceable files.")
41+
parser.add_argument(
42+
"--conversion-type", type=str, default="both", help="Select file type to convert, urdf, mesh, or both."
43+
)
44+
parser.add_argument(
45+
"--merge-joints",
46+
action="store_true",
47+
default=False,
48+
help="Consolidate links that are connected by fixed joints.",
49+
)
50+
parser.add_argument("--fix-base", action="store_true", default=False, help="Fix the base to where it is imported.")
51+
parser.add_argument(
52+
"--make-instanceable",
53+
action="store_true",
54+
default=True,
55+
help="Make the asset instanceable for efficient cloning.",
56+
)
57+
parser.add_argument(
58+
"--collision-approximation",
59+
type=str,
60+
default="convexDecomposition",
61+
choices=["convexDecomposition", "convexHull", "none"],
62+
help=(
63+
'The method used for approximating collision mesh. Set to "none" '
64+
"to not add a collision mesh to the converted mesh."
65+
),
66+
)
67+
parser.add_argument(
68+
"--mass",
69+
type=float,
70+
default=None,
71+
help="The mass (in kg) to assign to the converted asset. If not provided, then no mass is added.",
72+
)
73+
74+
# append AppLauncher cli args
75+
AppLauncher.add_app_launcher_args(parser)
76+
# parse the arguments
77+
args_cli = parser.parse_args()
78+
79+
# launch omniverse app
80+
app_launcher = AppLauncher(args_cli)
81+
simulation_app = app_launcher.app
82+
83+
"""Rest everything follows."""
84+
85+
import os
86+
87+
from isaaclab.sim.converters import MeshConverter, MeshConverterCfg, UrdfConverter, UrdfConverterCfg
88+
from isaaclab.sim.schemas import schemas_cfg
89+
90+
91+
def main():
92+
93+
# Define conversion time given
94+
conversion_type = args_cli.conversion_type.lower()
95+
# Warning if conversion type input is not valid
96+
if conversion_type != "urdf" and conversion_type != "mesh" and conversion_type != "both":
97+
raise Warning("Conversion type is not valid, please select either 'urdf', 'mesh', or 'both'.")
98+
99+
if not os.path.exists(args_cli.input):
100+
print(f"Error: The directory {args_cli.input} does not exist.")
101+
102+
# For each file and subsequent sub-directory
103+
for root, dirs, files in os.walk(args_cli.input):
104+
# For each file
105+
for filename in files:
106+
# Check for URDF extensions
107+
if (conversion_type == "urdf" or conversion_type == "both") and filename.lower().endswith(".urdf"):
108+
# URDF converter call
109+
urdf_converter_cfg = UrdfConverterCfg(
110+
asset_path=f"{root}/{filename}",
111+
usd_dir=f"{args_cli.output}/{filename[:-5]}",
112+
usd_file_name=f"{filename[:-5]}.usd",
113+
fix_base=args_cli.fix_base,
114+
merge_fixed_joints=args_cli.merge_joints,
115+
force_usd_conversion=True,
116+
make_instanceable=args_cli.make_instanceable,
117+
)
118+
# Create Urdf converter and import the file
119+
urdf_converter = UrdfConverter(urdf_converter_cfg)
120+
print(f"Generated USD file: {urdf_converter.usd_path}")
121+
122+
elif (conversion_type == "mesh" or conversion_type == "both") and (
123+
filename.lower().endswith(".fbx")
124+
or filename.lower().endswith(".obj")
125+
or filename.lower().endswith(".dae")
126+
or filename.lower().endswith(".stl")
127+
):
128+
# Mass properties
129+
if args_cli.mass is not None:
130+
mass_props = schemas_cfg.MassPropertiesCfg(mass=args_cli.mass)
131+
rigid_props = schemas_cfg.RigidBodyPropertiesCfg()
132+
else:
133+
mass_props = None
134+
rigid_props = None
135+
136+
# Collision properties
137+
collision_props = schemas_cfg.CollisionPropertiesCfg(
138+
collision_enabled=args_cli.collision_approximation != "none"
139+
)
140+
# Mesh converter call
141+
mesh_converter_cfg = MeshConverterCfg(
142+
mass_props=mass_props,
143+
rigid_props=rigid_props,
144+
collision_props=collision_props,
145+
asset_path=f"{root}/{filename}",
146+
force_usd_conversion=True,
147+
usd_dir=f"{args_cli.output}/{filename[:-4]}",
148+
usd_file_name=f"{filename[:-4]}.usd",
149+
make_instanceable=args_cli.make_instanceable,
150+
collision_approximation=args_cli.collision_approximation,
151+
)
152+
# Create mesh converter and import the file
153+
mesh_converter = MeshConverter(mesh_converter_cfg)
154+
print(f"Generated USD file: {mesh_converter.usd_path}")
155+
156+
157+
if __name__ == "__main__":
158+
# run the main function
159+
main()
160+
# close sim app
161+
simulation_app.close()

0 commit comments

Comments
 (0)