Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Development] Add convert glb to usd objects. Issac Lab repo installation required. #2113

Open
wants to merge 61 commits into
base: isaac_sim_dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c722108
Add convert glb to usd objects. Issac Lab repo installation required.
danieltmeta Nov 27, 2024
176c7c9
Add urdf_conversion.py. This file converts .urdf to .usd.
danieltmeta Dec 6, 2024
8b3af05
Add clean_urdf_xml.py. Written by Eric Undersander
danieltmeta Dec 10, 2024
ef1bb47
Add bulk_usd_conversion.py.
danieltmeta Dec 10, 2024
fc9b2e4
Add glb decompression file. Add note and terminal command example. Wr…
danieltmeta Dec 10, 2024
52c6b2e
Remove asyncio library. Add MeshConverter Cfg and remove all asyncio …
danieltmeta Dec 10, 2024
75aee50
Remove remote_content folder. Move associated scripts to parent direc…
danieltmeta Dec 10, 2024
c5f4c51
Remove ssh_conversion.py. SAFEKEEPING.
danieltmeta Dec 10, 2024
2639996
Remove private funciton in urdf_conversion.py. Add UrdfConverterCfg i…
danieltmeta Dec 10, 2024
51b2e3c
Move AppLauncher class into if/main statement
danieltmeta Dec 10, 2024
9dfde68
Move Applauncher class into if/main statement.
danieltmeta Dec 10, 2024
33b51df
Fix syntax and variable errors in converter.py.
danieltmeta Dec 11, 2024
7b1a6b6
Remove and archive older versions of conversion .py
danieltmeta Jan 4, 2025
fc2e667
Draft. Add urdf to usd converter.
danieltmeta Jan 4, 2025
61287f9
Add scene_instance_json_to_usd.py. Draft stage.
danieltmeta Jan 5, 2025
c5bf778
Minor formatting
danieltmeta Jan 5, 2025
ec5d0e6
Black formatting
danieltmeta Jan 6, 2025
511d8da
Remove temporary archive folder.
danieltmeta Jan 6, 2025
ec6e720
Fix github actions issues and formatting issues.
danieltmeta Jan 6, 2025
dc683e2
Fix github actions and formatting issues.
danieltmeta Jan 6, 2025
f1c1342
Save black formatting changes.
danieltmeta Jan 6, 2025
c32fb4e
Fix black formatting.
danieltmeta Jan 6, 2025
06fcc52
Fix black formatting
danieltmeta Jan 6, 2025
365cbb2
Fix black formatting.
danieltmeta Jan 6, 2025
6f05bb7
Fix black formatting
danieltmeta Jan 6, 2025
eaacc63
FIx black formatting
danieltmeta Jan 6, 2025
21453bc
Fix black formatting.
danieltmeta Jan 6, 2025
c71926e
Fix black formatting.
danieltmeta Jan 6, 2025
0e146c7
Fix black formatting
danieltmeta Jan 6, 2025
3d4eed0
Fix isort formatting
danieltmeta Jan 6, 2025
465397d
Fix black formatting.
danieltmeta Jan 6, 2025
ab74bee
Fix isort formatting.
danieltmeta Jan 6, 2025
80b46a0
Fixing various formatting issues.
danieltmeta Jan 6, 2025
16bb555
Fix formatting.
danieltmeta Jan 6, 2025
2ee9d87
Fix formatting.
danieltmeta Jan 6, 2025
7d7a01e
Fix formatting.
danieltmeta Jan 6, 2025
41020e4
Remove unused libraries for github actions.
danieltmeta Jan 6, 2025
d180400
Minor docstring changes as suggested on github.
danieltmeta Jan 9, 2025
fd4b5d6
Update docstring for scene_instance_json_to_usd.py
danieltmeta Jan 9, 2025
5b2c63d
Add CLI for usd conversion.
danieltmeta Jan 10, 2025
1b9c9d5
Draft, add bulk scene instance json coversion
danieltmeta Jan 10, 2025
a6010f3
Draft, add CLI
danieltmeta Jan 10, 2025
d1d2fa2
precommit formatting.
danieltmeta Jan 12, 2025
14d4ee9
Remove bulk scene conversion logic. Will be transferred to unit tests.
danieltmeta Jan 12, 2025
d2db102
Update docstrings, minor check, and conversion function into if/main
danieltmeta Jan 12, 2025
1c2b1bc
precommit formatting.
danieltmeta Jan 12, 2025
fabe34c
Remove redundant imports
danieltmeta Jan 12, 2025
b096e3b
Updated docstrings and type annotations for scene_instance_json_to_us…
danieltmeta Jan 13, 2025
709c288
Updated doctring and minor formatting for urdf_to_usd.py
danieltmeta Jan 13, 2025
eec904e
Migrate draft of unit test and example data from PR #2123
danieltmeta Jan 13, 2025
9401213
Save progress unit test usd conversion.
danieltmeta Jan 15, 2025
8d047dc
Draft of unit test for usd conversion.
danieltmeta Jan 15, 2025
cee0591
Add files for unit test conversion
danieltmeta Jan 15, 2025
589aefb
minor formatting
danieltmeta Jan 15, 2025
175dcce
Formatting, end of file fix
danieltmeta Jan 15, 2025
4d428a3
Make some minor changes as described in the above comments.
danieltmeta Jan 23, 2025
71d3d06
Remove hssd comments.
danieltmeta Jan 23, 2025
0219137
Remove files generated by unit test.
danieltmeta Jan 23, 2025
a35fd09
Update pathing and import issues.
danieltmeta Jan 23, 2025
bdca713
Remove more necessary files for unit testing isaac lab conversion
danieltmeta Jan 23, 2025
2fe9535
Move meshColored folder and remove more unncessary unit test data files.
danieltmeta Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
602 changes: 602 additions & 0 deletions habitat-lab/habitat/isaacsim/scene_instance_json_to_usd.py

Large diffs are not rendered by default.

245 changes: 245 additions & 0 deletions habitat-lab/habitat/isaacsim/urdf_to_usd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
"""This module converts urdf files into usda files."""
danieltmeta marked this conversation as resolved.
Show resolved Hide resolved

import argparse
import os

from lxml import etree as ET # Using lxml for better XML handling


def clean_urdf(input_file: str, output_file: str, remove_visual=False) -> None:
"""
Cleans a URDF file:
1. Optionally removes <visual> elements.
2. Fixes invalid use of '.' in <link> and <joint> names while preserving references.

:param input_file: Path to the input URDF file.
:param output_file: Path to the output cleaned URDF file.
:param remove_visual: If True, removes all <visual> elements.
"""
tree = ET.parse(input_file)
root = tree.getroot()

name_map = {} # Maps old names to new names for links and joints

# Helper function to sanitize names
def sanitize_name(name):
if "." in name:
new_name = name.replace(".", "_")
name_map[name] = new_name
return new_name
return name

# Update <link> and <joint> names
for element in root.xpath("//*[@name]"):
original_name = element.get("name")
sanitized_name = sanitize_name(original_name)
element.set("name", sanitized_name)

# Update references to <parent link> and <child link>
for parent in root.xpath("//parent[@link]"):
original_link = parent.get("link")
parent.set("link", name_map.get(original_link, original_link))

for child in root.xpath("//child[@link]"):
original_link = child.get("link")
child.set("link", name_map.get(original_link, original_link))

# Optionally remove <visual> elements
if remove_visual:
for visual in root.xpath("//visual"):
visual_parent = visual.getparent()
visual_parent.remove(visual)

# Write the cleaned URDF to the output file
with open(output_file, "wb") as f:
f.write(
ET.tostring(
root, pretty_print=True, xml_declaration=True, encoding="UTF-8"
)
)
print(f"Cleaned URDF written to: {output_file}")


def convert_urdf(urdf_filepath: str, out_usd_filepath: str) -> None:
"""Convert urdf file to usda file

:param urdf_filepath: The filepath of urdf file
:param out_usd_filepath: The desired output path of usda file
"""

from omni.isaac.lab.sim.converters import UrdfConverter, UrdfConverterCfg

out_dir, out_filename = os.path.split(out_usd_filepath)

# Define the configuration for the URDF conversion
config = UrdfConverterCfg(
asset_path=urdf_filepath, # Path to the input URDF file
usd_dir=out_dir, # Directory to save the converted USD file
usd_file_name=out_filename, # Name of the output USD file
force_usd_conversion=True, # Force conversion even if USD already exists
make_instanceable=False, # Make the USD asset instanceable
link_density=1000.0, # Default density for links
import_inertia_tensor=True, # Import inertia tensor from URDF
convex_decompose_mesh=False, # Decompose convex meshes
fix_base=False, # Fix the base link
merge_fixed_joints=False, # Merge links connected by fixed joints
self_collision=False, # Enable self-collision
default_drive_type="position", # Default drive type for joints
override_joint_dynamics=False, # Override joint dynamics
)

# Create the UrdfConverter with the specified configuration
converter = UrdfConverter(config)

# Get the path to the generated USD file
usd_path = converter.usd_path
print(f"USD file generated at: {usd_path}")


def add_habitat_visual_metadata_for_articulation(
usd_filepath: str,
reference_urdf_filepath: str,
out_usd_filepath: str,
project_root_folder: str,
) -> None:
"""Add habitat visual metadata into usda file.

:param usd_filepath: Usd fileapath to add visual metadata
:param reference_urdf_filepath: Filepath of reference urdf
:param out_usd_filepath: Desired output usd path
:param project_root_folder: Directory path of habitat-lab
"""

# Parse the URDF file
urdf_tree = ET.parse(reference_urdf_filepath)
urdf_root = urdf_tree.getroot()

# Get the robot name from the URDF
robot_name = urdf_root.get("name")

# Extract visual metadata from the URDF
visual_metadata = {}
urdf_dir = os.path.dirname(os.path.abspath(reference_urdf_filepath))
# usd_dir = os.path.dirname(os.path.abspath(usd_filepath)) # NOTE: Not used in this function
for link in urdf_root.findall("link"):
link_name = link.get("name")
visual = link.find("visual")
if visual is not None:
geometry = visual.find("geometry")
if geometry is not None:
mesh = geometry.find("mesh")
if mesh is not None:
filename = mesh.get("filename")
asset_path = os.path.relpath(
os.path.abspath(os.path.join(urdf_dir, filename)),
start=project_root_folder,
)
scale = (1.0, 1.0, 1.0) # Default scale

# Check for scale in the <mesh> element
scale_element = mesh.find("scale")
if scale_element is not None:
scale = tuple(
map(float, scale_element.text.split())
) # type: ignore # noqa: ALL

# Replace periods with underscores for USD-safe names
# todo: use a standard get_sanitized_usd_name function here
safe_link_name = link_name.replace(".", "_")
visual_metadata[safe_link_name] = {
"assetPath": asset_path,
"assetScale": scale,
}

# Open the USD file
stage = Usd.Stage.Open(usd_filepath)
if not stage:
raise ValueError(f"Could not open USD file: {usd_filepath}")

# Add the habitatVisual metadata to each relevant prim
for link_name, metadata in visual_metadata.items():
prim_path = f"/{robot_name}/{link_name}"
prim = stage.GetPrimAtPath(prim_path)
if prim:
# Add assetPath
asset_path_attr = prim.CreateAttribute(
"habitatVisual:assetPath", Sdf.ValueTypeNames.String
)
asset_path_attr.Set(metadata["assetPath"])

# Add assetScale
asset_scale_attr = prim.CreateAttribute(
"habitatVisual:assetScale", Sdf.ValueTypeNames.Float3
)
asset_scale_attr.Set(Gf.Vec3f(*metadata["assetScale"]))
else:
print(f"Warning: Prim not found for link: {link_name}")

# Save the updated USD to the output file
stage.GetRootLayer().Export(out_usd_filepath)
print(f"Updated USD file written to: {out_usd_filepath}")


if __name__ == "__main__":
danieltmeta marked this conversation as resolved.
Show resolved Hide resolved
"""This function converts urdf files to usda."""
# Build parser and subparser
parser = argparse.ArgumentParser(
description="Create an empty Issac Sim stage."
)
subparsers = parser.add_subparsers(dest="command")

# Parser for clean_urdf
parser_clean_urdf = subparsers.add_parser(
"clean_urdf", help="Run clean_urdf function"
)
parser_clean_urdf.add_argument("input_file")
parser_clean_urdf.add_argument("output_file")
parser_clean_urdf.add_argument("--remove_visual")
parser_clean_urdf.set_defaults(func=clean_urdf)

# Parser for convert_urdf function
parser_convert_urdf = subparsers.add_parser(
"convert_urdf", help="Run convert_urdf function"
)
parser_convert_urdf.add_argument("urdf_filepath")
parser_convert_urdf.add_argument("out_usd_filepath")
parser_convert_urdf.set_defaults(func=convert_urdf)

# Parser for add_habitat_visual_metadata_for_articulation function
parser_add_hab_visual_metadata = subparsers.add_parser(
"add_habitat_visual_metadata_for_articulation",
help="add_habitat_visual_metadata_for_articulation",
)
parser_add_hab_visual_metadata.add_argument("usd_filepath")
parser_add_hab_visual_metadata.add_argument("reference_urdf_filepath")
parser_add_hab_visual_metadata.add_argument("out_usd_filepath")
parser_add_hab_visual_metadata.add_argument("project_root_folder")
parser_add_hab_visual_metadata.set_defaults(
func=add_habitat_visual_metadata_for_articulation
)

# Launch Issac Lab Applauncher and load libraries
from omni.isaac.lab.app import AppLauncher

AppLauncher.add_app_launcher_args(parser)
args = parser.parse_args()
args.headless = True
app_launcher = AppLauncher(args)
simulation_app = app_launcher.app
from pxr import Gf, Sdf, Usd

# Argparse function selection
if args.command:
if args.command == "clean_urdf":
args.func(args.input_file, args.output_file, args.remove_visual)
elif args.command == "convert_urdf":
args.func(args.urdf_filepath, args.out_usd_filepath)
elif args.command == "add_habitat_visual_metadata_for_articulation":
args.func(
args.usd_filepath,
args.reference_urdf_filepath,
args.out_usd_filepath,
args.project_root_folder,
)
else:
parser.print_help()
1 change: 1 addition & 0 deletions test/data/usd_conversion_data/.asset_hash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24f1b688baf20921994b95e47f42b7ee
danieltmeta marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

Loading
Loading