Skip to content

update urdf mjcf importer to use the latest isaac sim importer#5394

Open
stevfeng wants to merge 2 commits intoisaac-sim:developfrom
stevfeng:dev/stevfeng/update_importers
Open

update urdf mjcf importer to use the latest isaac sim importer#5394
stevfeng wants to merge 2 commits intoisaac-sim:developfrom
stevfeng:dev/stevfeng/update_importers

Conversation

@stevfeng
Copy link
Copy Markdown

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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (existing functionality will not work without user modification)
  • Documentation update

Screenshots

Please attach before and after screenshots of the change if applicable.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • 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
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label Apr 24, 2026
Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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_visuals will see a type annotation change (was missing, now bool)
  • 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_base creates FixedJoint
  • merge_fixed_joints XML-level and full pipeline
  • Deprecated NaturalFrequencyGainsCfg warning
  • 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_assetAssetConverterBase.__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-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR refactors the URDF and MJCF converters to delegate the full conversion pipeline to the latest Isaac Sim importers (URDFImporter / MJCFImporter), removing ~300 lines of duplicated IsaacLab-side USD post-processing and adding new config fields that mirror the expanded URDFImporterConfig / MJCFImporterConfig options.

  • P1 — silent breakage of per-joint drive configs: JointDriveCfg.drive_type, target_type, gains.stiffness, and gains.damping all accept dict[str, ...] for per-joint regex overrides (a documented, previously-working feature). _unpack_joint_drive passes those dicts verbatim to URDFImporterConfig fields that expect scalar values, with no warning emitted — existing users relying on per-joint patterns will get type errors or silent no-ops.

Confidence Score: 4/5

Mostly 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

Filename Overview
source/isaaclab/isaaclab/sim/converters/urdf_converter.py Major refactor: multi-step pipeline replaced with a thin wrapper around URDFImporter.import_urdf(). Per-joint dict support for drive_type/target_type/stiffness/damping is silently broken — those values are forwarded verbatim to scalar-expecting importer fields with no warning.
source/isaaclab/isaaclab/sim/converters/urdf_converter_cfg.py New config fields added (ros_package_paths, robot_type, run_asset_transformer, run_multi_physics_conversion, debug_mode); collision_type Literal extended; deprecated fields annotated. Well-documented.
source/isaaclab/isaaclab/sim/converters/mjcf_converter.py New MJCFImporterConfig fields forwarded cleanly. Shutil import moved to top-level. Straightforward and clean.
source/isaaclab/isaaclab/sim/converters/mjcf_converter_cfg.py Well-documented addition of fix_base, link_density, robot_type, actuator gain override fields, and pipeline control flags mirroring the new MJCFImporterConfig options.
source/isaaclab/isaaclab/sim/converters/urdf_utils.py Replaced with a re-export shim. Top-level import will fail at load time if isaacsim is not present rather than deferring the error as other Isaac Sim imports do.
source/isaaclab/test/sim/test_urdf_converter.py Minor comment update only. No functional test changes.

Sequence Diagram

sequenceDiagram
    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
Loading

Reviews (1): Last reviewed commit: "update urdf mjcf importer to use the lat..." | Re-trigger Greptile

Comment on lines 155 to +179
@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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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``.
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Comment on lines +113 to +121
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."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant