# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
import logging
import os
import pathlib
import warnings
from isaaclab.utils.version import has_kit
from .asset_converter_base import AssetConverterBase
from .urdf_converter_cfg import UrdfConverterCfg
logger = logging.getLogger(__name__)
[docs]
class UrdfConverter(AssetConverterBase):
"""Converter for a URDF description file to a USD file.
This class wraps around the `isaacsim.asset.importer.urdf`_ API to provide a lazy
implementation for URDF to USD conversion. When the full Isaac Sim runtime is available,
the Isaac Sim URDF importer extension is enabled and used; otherwise, the API is loaded
from the standalone ``isaacsim-asset-isolated`` package.
The heavy lifting (URDF parsing, fixed-joint merging, fix-base insertion, joint-drive
configuration, density override, asset transformer profile) is delegated to Isaac Sim's
:class:`~isaacsim.asset.importer.urdf.URDFImporter` together with
:class:`~isaacsim.asset.importer.urdf.URDFImporterConfig`. IsaacLab only translates its
user-friendly :class:`UrdfConverterCfg` into the flat importer config.
.. caution::
The current lazy conversion implementation does not automatically trigger USD generation if
only the mesh files used by the URDF are modified. To force generation, either set
:obj:`AssetConverterBaseCfg.force_usd_conversion` to True or delete the output directory.
.. note::
From Isaac Sim 4.5 onwards, the extension name changed from ``omni.importer.urdf`` to
``isaacsim.asset.importer.urdf``.
.. note::
In the URDF importer 3.0, the conversion pipeline uses the ``urdf-usd-converter`` library
and the ``isaacsim.asset.transformer.rules`` extension to produce structured USD output.
Features such as ``convert_mimic_joints_to_normal_joints`` and
``replace_cylinders_with_capsules`` are no longer natively supported by the importer and
will emit warnings if enabled.
.. _isaacsim.asset.importer.urdf: https://docs.isaacsim.omniverse.nvidia.com/latest/importer_exporter/ext_isaacsim_asset_importer_urdf.html
"""
cfg: UrdfConverterCfg
"""The configuration instance for URDF to USD conversion."""
[docs]
def __init__(self, cfg: UrdfConverterCfg):
"""Initializes the class.
Args:
cfg: The configuration instance for URDF to USD conversion.
"""
# set `usd_file_name` to match the importer's output path structure:
# the importer generates `{usd_path}/{robot_name}/{robot_name}.usda`
robot_name = pathlib.PurePath(cfg.asset_path).stem
cfg.usd_file_name = os.path.join(robot_name, f"{robot_name}.usda")
super().__init__(cfg=cfg)
def _convert_asset(self, cfg: UrdfConverterCfg):
"""Run the Isaac Sim URDF importer pipeline.
Translates :class:`UrdfConverterCfg` into a flat
:class:`~isaacsim.asset.importer.urdf.URDFImporterConfig` and invokes
:meth:`~isaacsim.asset.importer.urdf.URDFImporter.import_urdf`. The importer handles
fixed-joint merging, fix-base insertion, joint-drive configuration, link density
overrides, and the asset transformer profile internally.
Args:
cfg: The URDF conversion configuration.
"""
# Inside Kit the importer ships as an extension and must be enabled before it can be
# imported; kitlessly the same module resolves from the standalone importer wheel.
if has_kit():
from isaaclab.sim.utils import enable_extension # noqa: PLC0415
enable_extension("isaacsim.asset.importer.urdf")
from isaacsim.asset.importer.urdf import URDFImporter, URDFImporterConfig # noqa: PLC0415
# log warnings for features no longer supported by the URDF importer 3.0
self._warn_unsupported_features(cfg)
# translate nested `JointDriveCfg` into flat importer fields
drive_type, target_type, stiffness, damping = self._unpack_joint_drive(cfg.joint_drive)
import_config = URDFImporterConfig(
urdf_path=os.path.normpath(cfg.asset_path),
usd_path=os.path.normpath(self.usd_dir),
merge_fixed_joints=cfg.merge_fixed_joints,
merge_mesh=cfg.merge_mesh,
collision_from_visuals=cfg.collision_from_visuals,
collision_type=cfg.collision_type,
allow_self_collision=cfg.self_collision,
ros_package_paths=list(cfg.ros_package_paths),
robot_type=cfg.robot_type,
fix_base=cfg.fix_base,
link_density=cfg.link_density if cfg.link_density > 0.0 else None,
joint_drive_type=drive_type,
joint_target_type=target_type,
override_joint_stiffness=stiffness,
override_joint_damping=damping,
run_asset_transformer=cfg.run_asset_transformer,
run_multi_physics_conversion=cfg.run_multi_physics_conversion,
debug_mode=cfg.debug_mode,
)
generated_usd_path = URDFImporter(import_config).import_urdf()
if generated_usd_path:
generated_usd_path = os.path.normpath(generated_usd_path)
self._usd_file_name = os.path.relpath(generated_usd_path, self.usd_dir)
@staticmethod
def _warn_unsupported_features(cfg: UrdfConverterCfg):
"""Log warnings for configuration options no longer supported by the URDF importer 3.0.
Args:
cfg: The URDF conversion configuration.
"""
if cfg.convert_mimic_joints_to_normal_joints:
logger.warning(
"UrdfConverter: 'convert_mimic_joints_to_normal_joints' is no longer supported"
" by the URDF importer 3.0."
)
if cfg.replace_cylinders_with_capsules:
logger.warning(
"UrdfConverter: 'replace_cylinders_with_capsules' is no longer supported by the URDF importer 3.0."
)
if cfg.root_link_name:
logger.warning("UrdfConverter: 'root_link_name' is no longer supported by the URDF importer 3.0.")
if cfg.joint_drive and isinstance(
cfg.joint_drive.gains,
UrdfConverterCfg.JointDriveCfg.NaturalFrequencyGainsCfg,
):
warnings.warn(
"UrdfConverter: 'NaturalFrequencyGainsCfg' is deprecated and no longer supported by the"
" URDF importer 3.0. The `compute_natural_stiffness` function has been removed."
" Joint drive gains will be left at the values produced by the URDF importer."
" Please use 'PDGainsCfg' instead.",
DeprecationWarning,
stacklevel=2,
)
@staticmethod
def _unpack_joint_drive(joint_drive: UrdfConverterCfg.JointDriveCfg | None) -> tuple:
"""Translate an IsaacLab :class:`UrdfConverterCfg.JointDriveCfg` into flat importer fields.
Args:
joint_drive: The nested IsaacLab joint-drive configuration, or ``None``.
Returns:
Tuple ``(drive_type, target_type, stiffness, damping)`` suitable for
:class:`~isaacsim.asset.importer.urdf.URDFImporterConfig`. Entries are ``None`` when
the user did not request an override.
"""
if joint_drive is None:
return None, None, None, None
gains = joint_drive.gains
if isinstance(gains, UrdfConverterCfg.JointDriveCfg.PDGainsCfg):
stiffness = gains.stiffness
damping = gains.damping
else:
# `NaturalFrequencyGainsCfg` is deprecated; leave gains unchanged.
stiffness = None
damping = None
return joint_drive.drive_type, joint_drive.target_type, stiffness, damping