Skip to content

Drive fragment schema writers by prim path expressions#6640

Draft
vidurv-nvidia wants to merge 16 commits into
isaac-sim:developfrom
vidurv-nvidia:vidurv/schema-frag-regex-targeting
Draft

Drive fragment schema writers by prim path expressions#6640
vidurv-nvidia wants to merge 16 commits into
isaac-sim:developfrom
vidurv-nvidia:vidurv/schema-frag-regex-targeting

Conversation

@vidurv-nvidia

@vidurv-nvidia vidurv-nvidia commented Jul 21, 2026

Copy link
Copy Markdown

Description

Reworks the fragment schema writers from implicit subtree-traversal targeting to explicit prim-path-expression targeting. Supersedes #6635 (closed): this branch contains its nested-hierarchy fix and regression tests, so nested URDF-importer assets are handled here. The fragment path is experimental with no external consumers yet, hence the outright breaking change.

Motivation

The fragment writers inferred their targets by traversing the spawn prim's subtree with family-specific policies (stop-at-carrier vs. descend, implicit fresh-API anchoring on bare prims). Every recent writer bug (#6501, the #6377 divergence, #6635) came from that inference. This PR removes the guessing: the caller states which prims to author on, and the writer matches, filters, and writes.

Changes

  • Expression grammar: find_matching_prims gains a trailing ** token — a prim and all of its descendants at any depth, traversing instance proxies. ** anywhere else raises.
  • Writers (apply_rigid_body/collision/mass/articulation_root/joint_drive/fixed_tendon/spatial_tendon_properties): first argument is now a prim path expression; a shared matcher splits matches into targets (carry the family's defining API / are joints / carry tendon instances), creation candidates, and skipped instanced prims. Empty fragment list stays a no-op; zero targets warns and returns False.
  • Explicit creation: create_if_missing replaces implicit fresh-API anchoring. Family rules: rigid and articulation root error on more than one candidate; mass creates on matched rigid bodies (unconditionally for a single exact-prim match — the shape/mesh spawner case); collision creates on every candidate; joint drive applies the axis-appropriate DriveAPI (distinct from ensure_drives_exist, which keeps its activation semantics); tendons are modify-only.
  • Articulation root: nested roots now raise instead of being silently pruned; sibling roots are all processed.
  • Tendon backend funcs (PhysX _tune_multi_instance_tendon, Newton apply_mujoco_fixed_tendon) no longer descend subtrees — the core writer owns targeting; funcs tune the given prim only.
  • Spawner configs: per-family *_props_prim_path fields (relative to the spawn prim, None = previous behavior, "" = the spawn prim itself) plus mass/articulation/joint_drive create flags, on RigidObjectSpawnerCfg and FileCfg (shared by the USD/URDF/MJCF paths).

Breaking changes and migration

Fragment-path only; the legacy modify_*/define_* writers are untouched. Direct writer callers: pass f"{prim_path}/**" to recover subtree behavior; add create_if_missing=True where a fresh API on a bare prim was expected; expect a warning + False instead of ValueError for paths that match nothing. Spawner-config users are unaffected — the None defaults reproduce the previous placement exactly (locked by a fragment-vs-legacy parity test on flat and nested assets).

Test coverage

Every semantic change was written test-first and shown to fail against the previous writers. 219 tests across the schema/spawner/converter suites pass; the only red in the sweep is test_newton_material_fragment_authors_all_six_newton_attrs, which fails identically on the base commit (pre-existing resolver drift, unrelated).

Type of change

  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

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 added my name to the CONTRIBUTORS.md or my name already exists there

The fragment target resolver stopped descending at the first
schema-bearing prim on each branch, so on assets with nested rigid-body
hierarchies (child links authored under their parent link prims, as
produced by the URDF importer in Isaac Sim 6.0 and later) only the
outermost link received rigid-body and mass fragment properties. The
legacy writers gained full-subtree traversal for these families in
isaac-sim#6377, so the fragment writers diverged from legacy parity.

Add a stop_at_carrier flag to the resolver, mirroring apply_nested's
stop_on_success: rigid-body and mass writers now collect every carrier
in the subtree, while collision keeps the legacy stop-at-carrier
traversal. Extend the nested-target regression tests with a nested
child link so the flat-asset, parity, and direct-writer tests all cover
the nested topology.
The rigid-body fragment writer now resolves its targets from a prim
path expression via find_matching_prims instead of inferring them by
subtree traversal: existing carriers matched by the expression (a
trailing ** token reaches the whole subtree) are modified in place.
Applying a fresh RigidBodyAPI is now explicit through the new
create_if_missing flag, which anchors a single matched non-carrier
prim and raises when the expression matches several. Zero matched
targets warn and return False instead of silently anchoring, since
inventing a body the asset's joints never reference changes its
dynamics. The USD-file spawner passes an explicit subtree expression,
while the shape and mesh spawners opt into creation on their bare
container prims.
The converter authors a fresh RigidBodyAPI on the exact xform prim it
creates, so it must opt into creation now that the fragment writer only
modifies matched carriers by default.
The mass writer now matches targets with a prim path expression (a
trailing ** token selects a prim and its whole subtree) instead of
traversing the subtree of a single prim. Creation of UsdPhysics.MassAPI
is explicit via create_if_missing: with multiple matches it only lands
on prims that already carry a rigid body (non-bodies are skipped with a
warning), while a single exact-prim match is anchored unconditionally
since the shape and mesh spawners author mass before the rigid body.
Unmatched expressions warn and return False instead of raising.
The writer now resolves its targets with find_matching_prims from an
explicit prim path expression instead of inferring roots by subtree
traversal; callers select the subtree with a trailing '**' token.

Nested articulation roots now raise a ValueError instead of being
silently pruned to the outermost root, and a fresh root anchor is only
applied when requested via the new create_if_missing flag rather than
implicitly when no root is found.
Target joints for apply_joint_drive_properties with an explicit prim
path expression instead of a subtree traversal from a root prim; the
spawner call site now passes a trailing-** expression. A new
create_if_missing flag applies the axis-appropriate UsdPhysics.DriveAPI
(angular for revolute, linear for prismatic) on matched joints lacking
it, distinct from ensure_drives_exist which seeds a minimal stiffness
on fully-passive drives. Zero matches now warn instead of raising.
The collision family writer now matches its targets with a prim path
expression (trailing ** selects a prim and its whole subtree) instead
of inferring them by subtree traversal. Creating the CollisionAPI
anchor is opt-in via create_if_missing and, unlike rigid bodies, is
allowed on multiple matched prims to cover the bare-prim shape and
mesh spawner case. Mesh-collision fragments are dispatched only to
geometry prims, since cooking attributes and the approximation token
are meaningless elsewhere.
The core apply_fixed_tendon_properties and apply_spatial_tendon_properties
writers now resolve their targets from a prim path expression (with a
trailing ** token selecting a whole subtree) instead of relying on the
backend fragment funcs to descend. The PhysX multi-instance tendon tuner
and the MuJoCo MjcTendon tuner are strictly per-prim now; a fragment
succeeds when it tunes at least one matched target, so mixed-backend
target sets compose. The from-files spawner appends /** to keep tuning
tendon schemas authored on descendant joint prims.
All fragment writers now resolve targets through prim path expressions,
so the stop-at-carrier traversal helper has no callers left. Also accept
path-like objects (e.g. Sdf.Path) in the expression matcher and fix a
test passing the stage positionally into the create_if_missing slot.
Spawner cfgs gain per-family *_prim_path fields that select which prims
of the spawned asset receive schema fragments. Patterns are relative to
the spawn prim: each /-separated token is a per-level regex and a
trailing ** matches a prim and all its descendants. None keeps the
spawner default (whole subtree for USD assets, the exact authored prim
for shapes/meshes) and an empty string targets the spawn prim itself.
New create-if-missing flags are threaded through for the mass,
articulation-root, and joint-drive writers.
@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label Jul 21, 2026
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