update urdf mjcf importer to use the latest isaac sim importer#5394
update urdf mjcf importer to use the latest isaac sim importer#5394stevfeng wants to merge 2 commits intoisaac-sim:developfrom
Conversation
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Summary
This PR refactors the URDF and MJCF converters to delegate the full conversion pipeline to Isaac Sim's native importers (URDFImporter/URDFImporterConfig and MJCFImporter/MJCFImporterConfig). The PR removes ~750 lines of duplicated IsaacLab-specific post-processing code (fix-base insertion, joint drives, link density, ArticulationRootAPI relocation) and replaces it with a thin translation layer. The urdf_utils.py module is reduced to a re-export shim forwarding to the canonical Isaac Sim implementation.
Architecture Impact
High impact, well-contained. The changes affect the asset conversion pipeline which runs offline before simulation. The public API (UrdfConverterCfg, MjcfConverterCfg, merge_fixed_joints) is preserved. However:
- Downstream callers using
UrdfConverterCfg.collision_from_visualswill see a type annotation change (was missing, nowbool) - Tests validate the new pipeline produces equivalent USD output
- No runtime simulation impact — converters only affect USD generation
Implementation Verdict
Minor fixes needed. The refactor is architecturally sound, but there are a few issues with config field types, missing type annotations, and a potential test fragility.
Test Coverage
Good coverage. The test file has been updated and covers:
- Config change detection for lazy conversion
- Drive type/gains application (scalar and per-joint dict)
fix_basecreates FixedJointmerge_fixed_jointsXML-level and full pipeline- Deprecated
NaturalFrequencyGainsCfgwarning collision_from_visuals,self_collision,link_density
Missing: No test for ros_package_paths resolution with package:// URIs. This is a new config field without coverage.
CI Status
No CI checks available yet.
Findings
🔴 Critical: urdf_converter_cfg.py:174 — ros_package_paths type mismatch with Isaac Sim API
The config declares ros_package_paths: list[dict[str, str]] = [] and the docstring says "Each entry is a dictionary with keys name and path". However, in urdf_converter.py:96, it's passed as:
ros_package_paths=list(cfg.ros_package_paths),The URDFImporterConfig.ros_package_paths in Isaac Sim may expect a different structure (likely list[tuple[str, str]] or a flat dict). Without verifying the Isaac Sim API contract, this could cause silent failures or exceptions when users actually try to resolve package:// URIs. The test suite does not exercise this path.
🟡 Warning: mjcf_converter_cfg.py:91-92 — override_gain_prm and override_bias_prm should validate length
The docstrings specify these are "10 floats" but there's no validation. If a user passes a list of wrong length, the error will surface deep in the Isaac Sim importer with a confusing message. Consider adding a validate_config hook:
def validate_config(self):
if self.override_gain_prm is not None and len(self.override_gain_prm) != 10:
raise ValueError("override_gain_prm must have exactly 10 elements")🟡 Warning: urdf_converter.py:88-89 — collision_type literal values may not match Isaac Sim enum
The UrdfConverterCfg.collision_type accepts "Bounding Sphere" and "Bounding Cube" (with spaces), and this string is passed directly to URDFImporterConfig(collision_type=...). If the Isaac Sim importer expects different casing (e.g., "BoundingSphere") or an enum, this will fail. The test test_collider_type_convex_decomposition uses collider_type (which doesn't exist in the config — should be collision_type), so the test isn't actually validating this.
🔴 Critical: test_urdf_converter.py:622 — Test uses non-existent config field collider_type
config.collider_type = "convex_decomposition" # WRONG: field is `collision_type`This line sets a non-existent attribute. The test passes because the default collision_type="Convex Hull" is used. The test name and docstring claim to verify "Convex Decomposition" but it's actually testing "Convex Hull". Additionally, the value should be "Convex Decomposition" (with capitals and space) per the Literal type.
🟡 Warning: urdf_converter_cfg.py:171 — merge_mesh added but not forwarded to importer
In urdf_converter.py:_convert_asset, the config field merge_mesh is correctly forwarded. However, looking at the diff, this was an existing field that's now properly wired up. Good.
🔵 Improvement: urdf_utils.py — Missing fallback for Isaac Sim versions without the import
The module unconditionally imports from isaacsim.asset.importer.urdf.impl.urdf_utils. If a user has an older Isaac Sim version where this module doesn't exist, they'll get an ImportError at module load time. Consider a try/except with a clear error message:
try:
from isaacsim.asset.importer.urdf.impl.urdf_utils import merge_fixed_joints
except ImportError as e:
raise ImportError(
"merge_fixed_joints requires Isaac Sim 4.5+ with the updated URDF importer"
) from e🔵 Improvement: urdf_converter.py:153 — Deprecation warning for NaturalFrequencyGainsCfg should use warnings.warn consistently
The method uses warnings.warn with stacklevel=2, which is correct for showing the user's call site. However, the warning is only triggered inside _convert_asset → _warn_unsupported_features, meaning stacklevel=2 points to _convert_asset, not the user's UrdfConverter(cfg) call. Should be stacklevel=4 to trace through _warn_unsupported_features → _convert_asset → AssetConverterBase.__init__ → user code.
🔵 Improvement: CHANGELOG.rst:4 — Date is in the future (2026-04-24)
The changelog date appears to be a placeholder. Should be updated to the actual release date.
Greptile SummaryThis PR refactors the URDF and MJCF converters to delegate the full conversion pipeline to the latest Isaac Sim importers (
Confidence Score: 4/5Mostly safe to merge, but the per-joint JointDriveCfg dict handling is a silent regression for existing users of that feature. The refactor is well-structured and the MJCF changes are clean. The one P1 finding — per-joint dict values for drive_type/target_type/stiffness/damping being forwarded to scalar-expecting importer fields without a warning — is a real regression for users of that documented API. The fix is low-effort. source/isaaclab/isaaclab/sim/converters/urdf_converter.py — specifically _unpack_joint_drive and _warn_unsupported_features. Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant UrdfConverter
participant _unpack_joint_drive
participant URDFImporterConfig
participant URDFImporter
User->>UrdfConverter: UrdfConverterCfg(fix_base, joint_drive, ...)
UrdfConverter->>_unpack_joint_drive: JointDriveCfg
_unpack_joint_drive-->>UrdfConverter: (drive_type, target_type, stiffness, damping)
UrdfConverter->>URDFImporterConfig: urdf_path, usd_path, merge_fixed_joints, collision_*, fix_base, link_density, joint_drive_type, override_joint_stiffness, run_asset_transformer, debug_mode
URDFImporterConfig-->>UrdfConverter: config
UrdfConverter->>URDFImporter: import_urdf()
URDFImporter-->>UrdfConverter: robot_name/robot_name.usda
Reviews (1): Last reviewed commit: "update urdf mjcf importer to use the lat..." | Re-trigger Greptile |
| @staticmethod | ||
| def _apply_fix_base(stage): | ||
| """Add a fixed joint from the world to the root link of the robot. | ||
|
|
||
| Args: | ||
| stage: The USD stage to modify. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| default_prim = stage.GetDefaultPrim() | ||
| if not default_prim or not default_prim.IsValid(): | ||
| carb.log_warn("UrdfConverter: Cannot apply fix_base - no default prim found.") | ||
| return | ||
|
|
||
| # find the root link: first child with `RigidBodyAPI` under the prim hierarchy | ||
| root_link = None | ||
| for prim in stage.Traverse(): | ||
| if prim.HasAPI(UsdPhysics.RigidBodyAPI): | ||
| root_link = prim | ||
| break | ||
|
|
||
| if root_link is None: | ||
| carb.log_warn("UrdfConverter: Cannot apply fix_base - no rigid body link found.") | ||
| return | ||
|
|
||
| # create a fixed joint connecting the world to the root link | ||
| default_prim_path = default_prim.GetPath() | ||
| joint_path = default_prim_path.AppendChild("fix_base_joint") | ||
|
|
||
| fixed_joint = UsdPhysics.FixedJoint.Define(stage, joint_path) | ||
| # `body0` left empty => connected to the world frame | ||
| fixed_joint.CreateBody1Rel().SetTargets([root_link.GetPath()]) | ||
|
|
||
| @staticmethod | ||
| def _fix_articulation_root_for_fixed_base(usd_path: str): | ||
| """Move ArticulationRootAPI from the root rigid body to its parent prim. | ||
|
|
||
| After the asset transformer, ArticulationRootAPI ends up on the root rigid body. | ||
| When combined with a FixedJoint on that same body (``fix_base_joint``), PhysX treats | ||
| the articulation as a floating-base + external constraint (maximal coordinate tree) | ||
| rather than a proper fixed-base reduced-coordinate articulation. | ||
|
|
||
| Moving ArticulationRootAPI to the parent of the root rigid body (a non-rigid Xform / | ||
| Scope ancestor) resolves this, matching the pattern used by ``schemas.py``'s | ||
| ``fix_root_link``. | ||
|
|
||
| Changes are authored as **local opinions in the root layer** of the stage, which are | ||
| stronger than the variant-payload-sublayer opinions written by the asset transformer. | ||
| This means the root layer's ``delete apiSchemas`` overrides the ``prepend apiSchemas`` | ||
| in the deeper sublayers without modifying those files. | ||
|
|
||
| Args: | ||
| usd_path: Absolute path to the final ``.usda`` file produced by the asset transformer. | ||
| """ | ||
| from pxr import Usd, UsdPhysics | ||
|
|
||
| stage = Usd.Stage.Open(usd_path) | ||
| if not stage: | ||
| carb.log_warn( | ||
| f"UrdfConverter: Cannot open final stage at '{usd_path}'" | ||
| " for fix_base ArticulationRootAPI post-processing." | ||
| ) | ||
| return | ||
|
|
||
| # Find the root rigid body that incorrectly has ArticulationRootAPI applied. | ||
| root_body_prim = None | ||
| for prim in stage.Traverse(): | ||
| if prim.HasAPI(UsdPhysics.ArticulationRootAPI) and prim.HasAPI(UsdPhysics.RigidBodyAPI): | ||
| root_body_prim = prim | ||
| break | ||
|
|
||
| if root_body_prim is None: | ||
| # ArticulationRootAPI is already on a non-rigid ancestor (correct) or not present. | ||
| return | ||
|
|
||
| parent_prim = root_body_prim.GetParent() | ||
| if not parent_prim or not parent_prim.IsValid(): | ||
| carb.log_warn("UrdfConverter: Root rigid body has no valid parent prim — skipping ArticulationRootAPI fix.") | ||
| return | ||
|
|
||
| # Collect all articulation-related schema names applied to the root rigid body. | ||
| articulation_api_names = [ | ||
| name | ||
| for name in root_body_prim.GetAppliedSchemas() | ||
| if "ArticulationRoot" in name or name == "PhysxArticulationAPI" | ||
| ] | ||
|
|
||
| # --- Apply ArticulationRootAPI schemas to the parent prim --- | ||
| # (edit target is the root layer by default; writes local opinions) | ||
| UsdPhysics.ArticulationRootAPI.Apply(parent_prim) | ||
| already_on_parent = set(parent_prim.GetAppliedSchemas()) | ||
| for name in articulation_api_names: | ||
| if name != "PhysicsArticulationRootAPI" and name not in already_on_parent: | ||
| parent_prim.AddAppliedSchema(name) | ||
|
|
||
| # --- Copy USD articulation attributes to the parent prim --- | ||
| usd_art_api = UsdPhysics.ArticulationRootAPI(root_body_prim) | ||
| for attr_name in usd_art_api.GetSchemaAttributeNames(): | ||
| attr = root_body_prim.GetAttribute(attr_name) | ||
| val = attr.Get() if attr else None | ||
| if val is not None: | ||
| parent_attr = parent_prim.GetAttribute(attr_name) | ||
| if not parent_attr: | ||
| parent_attr = parent_prim.CreateAttribute(attr_name, attr.GetTypeName()) | ||
| parent_attr.Set(val) | ||
|
|
||
| # --- Copy physxArticulation:* attributes to the parent prim --- | ||
| for attr in root_body_prim.GetAttributes(): | ||
| aname = attr.GetName() | ||
| if aname.startswith("physxArticulation:"): | ||
| val = attr.Get() | ||
| if val is not None: | ||
| parent_attr = parent_prim.GetAttribute(aname) | ||
| if not parent_attr: | ||
| parent_attr = parent_prim.CreateAttribute(aname, attr.GetTypeName()) | ||
| parent_attr.Set(val) | ||
|
|
||
| # --- Remove ArticulationRootAPI schemas from the root rigid body --- | ||
| # Writing "delete" list-ops in the root layer overrides "prepend" in sublayers. | ||
| root_body_prim.RemoveAppliedSchema("PhysxArticulationAPI") | ||
| root_body_prim.RemoveAPI(UsdPhysics.ArticulationRootAPI) | ||
| for name in articulation_api_names: | ||
| if name not in ("PhysicsArticulationRootAPI", "PhysxArticulationAPI"): | ||
| root_body_prim.RemoveAppliedSchema(name) | ||
|
|
||
| # Save only the root layer (sublayers produced by the asset transformer are untouched). | ||
| stage.GetRootLayer().Save() | ||
|
|
||
| @staticmethod | ||
| def _apply_link_density(stage, density: float): | ||
| """Set default density on rigid body links that have no explicit mass. | ||
|
|
||
| Args: | ||
| stage: The USD stage to modify. | ||
| density: The density value in kg/m^3. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| for prim in stage.Traverse(): | ||
| if not prim.HasAPI(UsdPhysics.MassAPI): | ||
| continue | ||
| mass_api = UsdPhysics.MassAPI(prim) | ||
| # only set density if mass is not explicitly specified (0.0 means auto-compute) | ||
| mass_attr = mass_api.GetMassAttr() | ||
| if mass_attr and mass_attr.HasValue() and mass_attr.Get() > 0.0: | ||
| continue | ||
| density_attr = mass_api.GetDensityAttr() | ||
| if not density_attr: | ||
| density_attr = mass_api.CreateDensityAttr() | ||
| density_attr.Set(density) | ||
|
|
||
| def _apply_joint_drives(self, stage, cfg: UrdfConverterCfg): | ||
| """Set joint drive properties (type, target, gains) on USD joints. | ||
| def _unpack_joint_drive(joint_drive: UrdfConverterCfg.JointDriveCfg | None) -> tuple: | ||
| """Translate an IsaacLab :class:`UrdfConverterCfg.JointDriveCfg` into flat importer fields. | ||
|
|
||
| Args: | ||
| stage: The USD stage to modify. | ||
| cfg: The URDF converter configuration containing joint drive settings. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| # collect all joints with their metadata | ||
| joints: dict[str, tuple] = {} | ||
| for prim in stage.Traverse(): | ||
| if not (prim.IsA(UsdPhysics.RevoluteJoint) or prim.IsA(UsdPhysics.PrismaticJoint)): | ||
| continue | ||
| joint_name = prim.GetName() | ||
| is_revolute = prim.IsA(UsdPhysics.RevoluteJoint) | ||
| instance_name = "angular" if is_revolute else "linear" | ||
| joints[joint_name] = (prim, is_revolute, instance_name) | ||
|
|
||
| if not joints: | ||
| return | ||
|
|
||
| drive_cfg = cfg.joint_drive | ||
|
|
||
| # apply drive type (force / acceleration) | ||
| self._set_drive_type_on_joints(joints, drive_cfg) | ||
| # apply target type (none / position / velocity) | ||
| self._set_target_type_on_joints(joints, drive_cfg) | ||
| # apply gains (stiffness / damping) | ||
| self._set_drive_gains_on_joints(joints, drive_cfg) | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| # Joint drive helpers | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| @staticmethod | ||
| def _set_drive_type_on_joints(joints: dict, drive_cfg: UrdfConverterCfg.JointDriveCfg): | ||
| """Set the drive type (force or acceleration) on joint prims. | ||
| joint_drive: The nested IsaacLab joint-drive configuration, or ``None``. | ||
|
|
||
| Args: | ||
| joints: Mapping of joint name → (prim, is_revolute, instance_name). | ||
| drive_cfg: The joint drive configuration. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| def _apply(prim, instance_name: str, drive_type: str): | ||
| drive = UsdPhysics.DriveAPI.Get(prim, instance_name) | ||
| type_attr = drive.GetTypeAttr() | ||
| if not type_attr: | ||
| type_attr = drive.CreateTypeAttr() | ||
| type_attr.Set(drive_type) | ||
|
|
||
| if isinstance(drive_cfg.drive_type, str): | ||
| for _name, (prim, _is_rev, inst) in joints.items(): | ||
| _apply(prim, inst, drive_cfg.drive_type) | ||
| elif isinstance(drive_cfg.drive_type, dict): | ||
| for pattern, drive_type in drive_cfg.drive_type.items(): | ||
| matches = [n for n in joints if re.search(pattern, n)] | ||
| if not matches: | ||
| raise ValueError( | ||
| f"Joint name pattern '{pattern}' in drive_type config matched no joints." | ||
| f" Available joints: {list(joints.keys())}" | ||
| ) | ||
| for name in matches: | ||
| prim, _, inst = joints[name] | ||
| _apply(prim, inst, drive_type) | ||
|
|
||
| @staticmethod | ||
| def _set_target_type_on_joints(joints: dict, drive_cfg: UrdfConverterCfg.JointDriveCfg): | ||
| """Set the target type (none, position, velocity) on joint prims. | ||
|
|
||
| For ``"none"``, both stiffness and damping are zeroed out. | ||
|
|
||
| Args: | ||
| joints: Mapping of joint name → (prim, is_revolute, instance_name). | ||
| drive_cfg: The joint drive configuration. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| def _apply(prim, instance_name: str, target_type: str): | ||
| drive = UsdPhysics.DriveAPI.Get(prim, instance_name) | ||
| if target_type == "none": | ||
| drive.GetStiffnessAttr().Set(0.0) | ||
| drive.GetDampingAttr().Set(0.0) | ||
|
|
||
| if isinstance(drive_cfg.target_type, str): | ||
| for _name, (prim, _is_rev, inst) in joints.items(): | ||
| _apply(prim, inst, drive_cfg.target_type) | ||
| elif isinstance(drive_cfg.target_type, dict): | ||
| for pattern, target_type in drive_cfg.target_type.items(): | ||
| matches = [n for n in joints if re.search(pattern, n)] | ||
| if not matches: | ||
| raise ValueError( | ||
| f"Joint name pattern '{pattern}' in target_type config matched no joints." | ||
| f" Available joints: {list(joints.keys())}" | ||
| ) | ||
| for name in matches: | ||
| prim, _, inst = joints[name] | ||
| _apply(prim, inst, target_type) | ||
|
|
||
| @staticmethod | ||
| def _set_drive_gains_on_joints(joints: dict, drive_cfg: UrdfConverterCfg.JointDriveCfg): | ||
| """Set stiffness and damping on joint drive APIs. | ||
|
|
||
| For revolute joints the user-facing values (Nm/rad) are converted to the USD | ||
| convention (Nm/deg) by multiplying by ``pi / 180``. | ||
|
|
||
| Args: | ||
| joints: Mapping of joint name → (prim, is_revolute, instance_name). | ||
| drive_cfg: The joint drive configuration. | ||
| 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. | ||
| """ | ||
| from pxr import UsdPhysics | ||
|
|
||
| gains = drive_cfg.gains | ||
| if not isinstance(gains, UrdfConverterCfg.JointDriveCfg.PDGainsCfg): | ||
| return | ||
|
|
||
| def _set_stiffness(prim, instance_name: str, is_revolute: bool, value: float): | ||
| drive = UsdPhysics.DriveAPI.Get(prim, instance_name) | ||
| usd_value = value * math.pi / 180.0 if is_revolute else value | ||
| stiffness_attr = drive.GetStiffnessAttr() | ||
| if not stiffness_attr: | ||
| stiffness_attr = drive.CreateStiffnessAttr() | ||
| stiffness_attr.Set(usd_value) | ||
|
|
||
| def _set_damping(prim, instance_name: str, is_revolute: bool, value: float): | ||
| drive = UsdPhysics.DriveAPI.Get(prim, instance_name) | ||
| usd_value = value * math.pi / 180.0 if is_revolute else value | ||
| damping_attr = drive.GetDampingAttr() | ||
| if not damping_attr: | ||
| damping_attr = drive.CreateDampingAttr() | ||
| damping_attr.Set(usd_value) | ||
|
|
||
| # --- stiffness --- | ||
| if isinstance(gains.stiffness, (float, int)): | ||
| for _name, (prim, is_rev, inst) in joints.items(): | ||
| _set_stiffness(prim, inst, is_rev, gains.stiffness) | ||
| elif isinstance(gains.stiffness, dict): | ||
| for pattern, stiffness in gains.stiffness.items(): | ||
| matches = [n for n in joints if re.search(pattern, n)] | ||
| if not matches: | ||
| raise ValueError( | ||
| f"Joint name pattern '{pattern}' in stiffness config matched no joints." | ||
| f" Available joints: {list(joints.keys())}" | ||
| ) | ||
| for name in matches: | ||
| prim, is_rev, inst = joints[name] | ||
| _set_stiffness(prim, inst, is_rev, stiffness) | ||
|
|
||
| # --- damping --- | ||
| if gains.damping is None: | ||
| return | ||
| if isinstance(gains.damping, (float, int)): | ||
| for _name, (prim, is_rev, inst) in joints.items(): | ||
| _set_damping(prim, inst, is_rev, gains.damping) | ||
| elif isinstance(gains.damping, dict): | ||
| for pattern, damping in gains.damping.items(): | ||
| matches = [n for n in joints if re.search(pattern, n)] | ||
| if not matches: | ||
| raise ValueError( | ||
| f"Joint name pattern '{pattern}' in damping config matched no joints." | ||
| f" Available joints: {list(joints.keys())}" | ||
| ) | ||
| for name in matches: | ||
| prim, is_rev, inst = joints[name] | ||
| _set_damping(prim, inst, is_rev, damping) | ||
| 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 |
There was a problem hiding this comment.
Per-joint dict configs silently passed to scalar-expecting importer fields
JointDriveCfg.drive_type, target_type, gains.stiffness, and gains.damping all accept dict[str, ...] for per-joint regex-pattern overrides. The old code explicitly handled this with re.search pattern matching. _unpack_joint_drive now extracts these values verbatim and forwards them to URDFImporterConfig(joint_drive_type=..., joint_target_type=..., override_joint_stiffness=..., override_joint_damping=...), which almost certainly expects scalar/string values — not dicts. Any user who relied on per-joint configs will either hit a runtime type error or have their settings silently ignored, with no warning emitted.
_warn_unsupported_features should detect dict-valued drive_type, target_type, stiffness, and damping and emit a deprecation warning, the same way it does for NaturalFrequencyGainsCfg.
| the canonical implementation to preserve the public import path | ||
| ``isaaclab.sim.converters.urdf_utils``. | ||
| """ | ||
|
|
There was a problem hiding this comment.
Re-export shim has no fallback for missing upstream module
The entire urdf_utils.py module is now a single import from isaacsim.asset.importer.urdf.impl.urdf_utils. If that internal path changes or the isaacsim package is not installed (e.g., when the module is imported in a documentation/test environment without Isaac Sim), any import of isaaclab.sim.converters.urdf_utils will raise an ImportError at module load time rather than at conversion time. Consider wrapping the import in a try/except and raising a clearer error only when merge_fixed_joints is actually called, similar to how other Isaac Sim-specific imports are deferred inside methods across the codebase.
| run_asset_transformer: bool = True | ||
| """Whether to run the asset transformer profile after conversion. Defaults to True. | ||
|
|
||
| When enabled, the importer restructures the intermediate USD into a layered, | ||
| payload-based package. Disable for a single flat USD output. | ||
| """ | ||
|
|
||
| run_multi_physics_conversion: bool = True | ||
| """Whether to run the MJCF-to-PhysX physics conversion pass. Defaults to True.""" |
There was a problem hiding this comment.
run_asset_transformer and run_multi_physics_conversion default to True — worth documenting the behavioral implications
Defaulting them to True preserves existing behaviour, which is correct. However, if a user accidentally sets run_multi_physics_conversion=False, the resulting USD will lack PhysX joint attributes and the articulation will be unusable in simulation. Consider adding a .. warning:: block to both docstrings noting that disabling these flags produces a non-simulated asset intended only for inspection/debugging.
Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
List any dependencies that are required for this change.
In Isaac Sim, we have added more capabilities to handle joint presets, fixed joints, and other properties to the importers, so we can simplify the isaac lab importer workflow.
Fixes # (issue)
Type of change
Screenshots
Please attach before and after screenshots of the change if applicable.
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there