Skip to content

Durable articulation ordering replay contract: checkpoint metadata and a from_checkpoint resolution mode #6485

Description

@AntoineRichard

Motivation

The articulation ordering stack (#6335#6341) lets the public joint/body order differ from the backend's native order, with symbolic conventions (physx, mjwarp, robot schema) resolved at env creation. Benchmarking and review of the stack surfaced three reasons the replay contract should not rest on symbolic inference alone:

  1. Fragility: cross-backend convention resolution emulates backend traversal (temporary Newton ModelBuilder builds, BFS/DFS assumptions on library defaults). A future backend default change would silently produce a wrong permutation.
  2. Startup cost: convention discovery adds ~9.3% to environment creation (~1.3–1.6 s on PhysX) even when the resolved ordering is identity, because discovery itself is the cost.
  3. No durable record: a raw framework checkpoint (e.g. rsl_rl .pt) carries no record of the joint/body order the policy was trained against; replay currently depends on the user knowing the training backend and requesting the right convention.

Approved design (summary)

Write once per run: training dumps params/ordering.yaml next to the existing params/env.yaml/agent.yaml, from the shared dump_train_configs site (all RL frameworks, one hook; ordering is immutable after articulation init, so no per-checkpoint work). Per scene articulation: the verbatim configured input, the resolved public joint/body name tuples, backend names, active backend, and a sha256 digest over the ordering-semantic fields (excluding paths/timestamps so copied runs stay valid). Entries are written for every articulation including identity — a missing entry is an error, never treated as identity.

Load: a new resolution mode joint_ordering="from_checkpoint" / body_ordering="from_checkpoint" on ArticulationCfg, plus ArticulationCfg.ordering_source_path auto-injected by the play entrypoints from --checkpoint (recursive cfg walk; works for direct and manager-based tasks; Hydra-overridable; the loader also probes the checkpoint's own directory so model.pt + ordering.yaml copied together works). Stored tuples feed the existing explicit-permutation machinery; identity normalizes to None (no staging, no kernels). Convention discovery is skipped entirely, removing the startup cost. Symbolic aliases stay as optional convenience resolvers.

Failure policy — hard errors only: digest mismatch, missing file/entry, unknown schema version, or stored names not a permutation of the live backend names (reported with exact missing/extra sets). Asset drift is caught structurally by the permutation check, which is stronger than any hash. All error text references ArticulationCfg fields and proposes explicit alternatives.

Bonus property: runs trained with ordering=None still record the training backend's public order, so from_checkpoint on a different backend reconstructs the correct permutation without the user knowing the training backend's convention — every future checkpoint becomes sim-to-sim-ready by default.

One known implementation subtlety: loaded tuples must route through the backend-spelling normalization (_match_backend_name_spellings) like convention results do — Newton spells multi-DoF joints finger:0, PhysX finger_0; the plain explicit-tuple path skips this today.

Estimated scope: ~580 production + ~510 test/doc lines, all in isaaclab core + scripts/reinforcement_learning; no backend-package changes.

LEAPP relationship — needs review by the LEAPP team

LEAPP-exported artifacts already embed per-tensor element_names (public joint names in policy order, resolved from the articulation data properties at export), and leapp_deployment_env reconnects them by name at deployment (find_joints(..., preserve_order=True)). To avoid multiplying sources of truth:

  • Single-producer rule: both LEAPP element_names resolution and ordering.yaml are write-once projections of the same runtime state (data.joint_names/data.body_names); they cannot disagree at write time. This relationship should be documented in the ordering docs.
  • Disjoint consumers: LEAPP covers exported-policy deployment (per-tensor semantics); from_checkpoint covers the raw-framework-checkpoint train→play path where no LEAPP artifact exists.
  • Composition, not competition: the LEAPP deployment env currently handles partial joint selection but not element reordering — replaying an exported policy on a backend with different native order therefore depends on asset-level ordering aligning the public order first, which is exactly what from_checkpoint provides; LEAPP's name matching then resolves to identity.
  • Follow-up option: teach the from_checkpoint loader to also accept a LEAPP artifact as its ordering source (read the articulation tensor's element_names), so LEAPP-exported policies need no sidecar. Deliberately out of v1 to avoid coupling the ordering resolver to LEAPP parsing.

Open questions

  1. Remote/Nucleus checkpoints: retrieve_file_path downloads a lone checkpoint to temp, so the sidecar probe misses — add best-effort retrieval of a sibling ordering.yaml? Should published pretrained checkpoint bundles ship one?
  2. Wire the deprecated per-framework train.py/play.py scripts (~20 lines) or skip if their removal lands in the same window?
  3. Injection scope: fill ordering_source_path on all articulation cfgs where unset (inert unless the sentinel is used) vs. sentinel-gated injection.

Status

Design complete and maintainer-approved in shape; implementation deliberately deferred until the ordering stack (#6335#6341) lands, and split into its own PR so the LEAPP team can review the replay-contract surface without the stack's noise.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions