Source code for isaaclab.sim.converters.urdf_converter
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.# All rights reserved.## SPDX-License-Identifier: BSD-3-Clausefrom__future__importannotationsimportmathimportreimportisaacsimimportomni.kit.appimportomni.kit.commandsimportomni.usdfromisaacsim.core.utils.extensionsimportenable_extensionfrom.asset_converter_baseimportAssetConverterBasefrom.urdf_converter_cfgimportUrdfConverterCfg
[docs]classUrdfConverter(AssetConverterBase):"""Converter for a URDF description file to a USD file. This class wraps around the `isaacsim.asset.importer.urdf`_ extension to provide a lazy implementation for URDF to USD conversion. It stores the output USD file in an instanceable format since that is what is typically used in all learning related applications. .. 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``. This converter class now uses the latest extension from Isaac Sim. .. _isaacsim.asset.importer.urdf: https://docs.isaacsim.omniverse.nvidia.com/latest/robot_setup/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. """manager=omni.kit.app.get_app().get_extension_manager()ifnotmanager.is_extension_enabled("isaacsim.asset.importer.urdf"):enable_extension("isaacsim.asset.importer.urdf")fromisaacsim.asset.importer.urdf._urdfimportacquire_urdf_interfaceself._urdf_interface=acquire_urdf_interface()super().__init__(cfg=cfg)
""" Implementation specific methods. """def_convert_asset(self,cfg:UrdfConverterCfg):"""Calls underlying Omniverse command to convert URDF to USD. Args: cfg: The URDF conversion configuration. """import_config=self._get_urdf_import_config()# parse URDF fileresult,self._robot_model=omni.kit.commands.execute("URDFParseFile",urdf_path=cfg.asset_path,import_config=import_config)ifresult:ifcfg.joint_drive:# modify joint parametersself._update_joint_parameters()# set root link nameifcfg.root_link_name:self._robot_model.root_link=cfg.root_link_name# convert the model to USDomni.kit.commands.execute("URDFImportRobot",urdf_path=cfg.asset_path,urdf_robot=self._robot_model,import_config=import_config,dest_path=self.usd_path,)else:raiseValueError(f"Failed to parse URDF file: {cfg.asset_path}")""" Helper methods. """def_get_urdf_import_config(self)->isaacsim.asset.importer.urdf.ImportConfig:"""Create and fill URDF ImportConfig with desired settings Returns: The constructed ``ImportConfig`` object containing the desired settings. """# create a new import config_,import_config=omni.kit.commands.execute("URDFCreateImportConfig")# set the unit scaling factor, 1.0 means meters, 100.0 means cmimport_config.set_distance_scale(1.0)# set imported robot as default primimport_config.set_make_default_prim(True)# add a physics scene to the stage on import if none existsimport_config.set_create_physics_scene(False)# -- asset settings# default density used for links, use 0 to auto-computeimport_config.set_density(self.cfg.link_density)# mesh simplification settingsconvex_decomp=self.cfg.collider_type=="convex_decomposition"import_config.set_convex_decomp(convex_decomp)# create collision geometry from visual geometryimport_config.set_collision_from_visuals(self.cfg.collision_from_visuals)# consolidating links that are connected by fixed jointsimport_config.set_merge_fixed_joints(self.cfg.merge_fixed_joints)# -- physics settings# create fix joint for base linkimport_config.set_fix_base(self.cfg.fix_base)# self collisions between links in the articulationimport_config.set_self_collision(self.cfg.self_collision)# convert mimic joints to normal jointsimport_config.set_parse_mimic(self.cfg.convert_mimic_joints_to_normal_joints)# replace cylinder shapes with capsule shapesimport_config.set_replace_cylinders_with_capsules(self.cfg.replace_cylinders_with_capsules)returnimport_configdef_update_joint_parameters(self):"""Update the joint parameters based on the configuration."""# set the drive typeself._set_joints_drive_type()# set the drive target typeself._set_joints_drive_target_type()# set the drive gainsself._set_joint_drive_gains()def_set_joints_drive_type(self):"""Set the joint drive type for all joints in the URDF model."""fromisaacsim.asset.importer.urdf._urdfimportUrdfJointDriveTypedrive_type_mapping={"force":UrdfJointDriveType.JOINT_DRIVE_FORCE,"acceleration":UrdfJointDriveType.JOINT_DRIVE_ACCELERATION,}ifisinstance(self.cfg.joint_drive.drive_type,str):forjointinself._robot_model.joints.values():joint.drive.set_drive_type(drive_type_mapping[self.cfg.joint_drive.drive_type])elifisinstance(self.cfg.joint_drive.drive_type,dict):forjoint_name,drive_typeinself.cfg.joint_drive.drive_type.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the drive type config was not found in the URDF file. The"f" joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]joint.drive.set_drive_type(drive_type_mapping[drive_type])def_set_joints_drive_target_type(self):"""Set the joint drive target type for all joints in the URDF model."""fromisaacsim.asset.importer.urdf._urdfimportUrdfJointTargetTypetarget_type_mapping={"none":UrdfJointTargetType.JOINT_DRIVE_NONE,"position":UrdfJointTargetType.JOINT_DRIVE_POSITION,"velocity":UrdfJointTargetType.JOINT_DRIVE_VELOCITY,}ifisinstance(self.cfg.joint_drive.target_type,str):forjointinself._robot_model.joints.values():joint.drive.set_target_type(target_type_mapping[self.cfg.joint_drive.target_type])elifisinstance(self.cfg.joint_drive.target_type,dict):forjoint_name,target_typeinself.cfg.joint_drive.target_type.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the target type config was not found in the URDF file. The"f" joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]joint.drive.set_target_type(target_type_mapping[target_type])def_set_joint_drive_gains(self):"""Set the joint drive gains for all joints in the URDF model."""# set the gains directly from stiffness and damping valuesifisinstance(self.cfg.joint_drive.gains,UrdfConverterCfg.JointDriveCfg.PDGainsCfg):# stiffnessifisinstance(self.cfg.joint_drive.gains.stiffness,(float,int)):forjointinself._robot_model.joints.values():self._set_joint_drive_stiffness(joint,self.cfg.joint_drive.gains.stiffness)elifisinstance(self.cfg.joint_drive.gains.stiffness,dict):forjoint_name,stiffnessinself.cfg.joint_drive.gains.stiffness.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the drive stiffness config was not found in the URDF file."f" The joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]self._set_joint_drive_stiffness(joint,stiffness)# dampingifisinstance(self.cfg.joint_drive.gains.damping,(float,int)):forjointinself._robot_model.joints.values():self._set_joint_drive_damping(joint,self.cfg.joint_drive.gains.damping)elifisinstance(self.cfg.joint_drive.gains.damping,dict):forjoint_name,dampinginself.cfg.joint_drive.gains.damping.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the drive damping config was not found in the URDF file."f" The joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]self._set_joint_drive_damping(joint,damping)# set the gains from natural frequency and damping ratioelifisinstance(self.cfg.joint_drive.gains,UrdfConverterCfg.JointDriveCfg.NaturalFrequencyGainsCfg):# damping ratioifisinstance(self.cfg.joint_drive.gains.damping_ratio,(float,int)):forjointinself._robot_model.joints.values():joint.drive.damping_ratio=self.cfg.joint_drive.gains.damping_ratioelifisinstance(self.cfg.joint_drive.gains.damping_ratio,dict):forjoint_name,damping_ratioinself.cfg.joint_drive.gains.damping_ratio.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the damping ratio config was not found in the URDF file."f" The joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]joint.drive.damping_ratio=damping_ratio# natural frequency (this has to be done after damping ratio is set)ifisinstance(self.cfg.joint_drive.gains.natural_frequency,(float,int)):forjointinself._robot_model.joints.values():joint.drive.natural_frequency=self.cfg.joint_drive.gains.natural_frequencyself._set_joint_drive_gains_from_natural_frequency(joint)elifisinstance(self.cfg.joint_drive.gains.natural_frequency,dict):forjoint_name,natural_frequencyinself.cfg.joint_drive.gains.natural_frequency.items():# handle joint name being a regexmatches=[sforsinself._robot_model.joints.keys()ifre.search(joint_name,s)]ifnotmatches:raiseValueError(f"The joint name {joint_name} in the natural frequency config was not found in the URDF"f" file. The joint names in the URDF are {list(self._robot_model.joints.keys())}")formatchinmatches:joint=self._robot_model.joints[match]joint.drive.natural_frequency=natural_frequencyself._set_joint_drive_gains_from_natural_frequency(joint)def_set_joint_drive_stiffness(self,joint,stiffness:float):"""Set the joint drive stiffness. Args: joint: The joint from the URDF robot model. stiffness: The stiffness value. """fromisaacsim.asset.importer.urdf._urdfimportUrdfJointTypeifjoint.type==UrdfJointType.JOINT_PRISMATIC:joint.drive.set_strength(stiffness)else:# we need to convert the stiffness from radians to degreesjoint.drive.set_strength(math.pi/180*stiffness)def_set_joint_drive_damping(self,joint,damping:float):"""Set the joint drive damping. Args: joint: The joint from the URDF robot model. damping: The damping value. """fromisaacsim.asset.importer.urdf._urdfimportUrdfJointTypeifjoint.type==UrdfJointType.JOINT_PRISMATIC:joint.drive.set_damping(damping)else:# we need to convert the damping from radians to degreesjoint.drive.set_damping(math.pi/180*damping)def_set_joint_drive_gains_from_natural_frequency(self,joint):"""Compute the joint drive gains from the natural frequency and damping ratio. Args: joint: The joint from the URDF robot model. """fromisaacsim.asset.importer.urdf._urdfimportUrdfJointDriveType,UrdfJointTargetTypestrength=self._urdf_interface.compute_natural_stiffness(self._robot_model,joint.name,joint.drive.natural_frequency,)self._set_joint_drive_stiffness(joint,strength)ifjoint.drive.target_type==UrdfJointTargetType.JOINT_DRIVE_POSITION:m_eq=1.0ifjoint.drive.drive_type==UrdfJointDriveType.JOINT_DRIVE_FORCE:m_eq=joint.inertiadamping=2*m_eq*joint.drive.natural_frequency*joint.drive.damping_ratioself._set_joint_drive_damping(joint,damping)