Skip to content

Refactor articulation actuator ownership - #6839

Draft
AntoineRichard wants to merge 33 commits into
isaac-sim:developfrom
AntoineRichard:antoiner/actuators-collection-split-6248
Draft

Refactor articulation actuator ownership#6839
AntoineRichard wants to merge 33 commits into
isaac-sim:developfrom
AntoineRichard:antoiner/actuators-collection-split-6248

Conversation

@AntoineRichard

@AntoineRichard AntoineRichard commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR gives actuator behavior one backend-neutral runtime owner, ActuatorCollection, and makes that ownership boundary useful for performance. It is based on develop after the articulation-ordering work in #6784 merged.

Previously, each backend articulation simultaneously owned simulated state, actuator groups, raw targets, processed targets, telemetry, gain resolution, actuator execution, ordering conversion, and backend submission. The refactor now separates those responsibilities:

  • ActuatorCollection owns named actuator groups, input commands, processed joint commands, telemetry, resolved parameters, and model lifecycle.
  • ActuatorControl defines the narrow backend contract.
  • PhysxActuatorControl, NewtonActuatorControl, and OvPhysxActuatorControl own backend ordering, staging, native-actuator integration, property writes, and submission.
  • Articulation remains responsible for simulated multibody state, topology, and lifecycle orchestration.

Public API and terminology

The API distinguishes the two sides of an actuator model:

# Input received by the actuator model.
robot.actuators.command.position
robot.actuators.command.velocity
robot.actuators.command.effort
robot.actuators.command.set_position_index(value=position_command)

# Processed command produced for the simulated joints.
robot.actuators.joint_command.position
robot.actuators.joint_command.velocity
robot.actuators.joint_command.effort

# Telemetry and resolved actuator parameters.
robot.actuators.computed_torque
robot.actuators.applied_torque
robot.actuators.actuator_stiffness
robot.actuators.actuator_damping
robot.actuators.gear_ratio

All public arrays remain expressed on the simulated joint side and indexed in articulation public joint order. “Actuator command” names the receiving component; it does not imply motor-shaft indexing.

Existing articulation target/gain methods and ArticulationData command/torque properties remain available as deprecated forwarding aliases. This PR does not remove a previously released public API.

Logical groups and execution aggregation

Named actuator groups remain the configuration and access surface. Execution batches are an internal optimization and may combine disjoint stateless groups of the same exact actuator class even when their gains, limits, or gear ratios differ.

  • Per-group parameter tensors are concatenated into one executor and exposed back as stable group views.
  • Exact ImplicitActuator batches compute and publish processed commands and telemetry in one fused Warp launch.
  • Exact IdealPDActuator and DCMotor batches use preallocated pointer-stable staging, in-place Torch compute, and cached Warp gather/scatter launches.
  • Stateful, neural, overlapping, subclass-specialized, and non-opted-in models retain their model-specific paths.

This removes redundant compute/launch work without changing configuration syntax, group lookup, per-group parameter writes, or output accessors.

Backend behavior

  • PhysX: supports both the standard Isaac Lab path and optional Newton-native actuators through PhysxActuatorWrapper. Mixed implicit/explicit groups retain solver-drive commands where required.
  • Newton: supports both the standard Lab compute loop and manager-owned native actuators, including ordering-aware global DOF binding, resets, gain updates, and post-actuator telemetry.
  • OVPhysX: preserves eager partial-write semantics and keeps processed effort separate from persistent raw-command staging.
  • Ordering: collection buffers stay in public order; conversion occurs only at backend binding/submission boundaries.

Newton actuators on PhysX CUDA graphs

For graphable Newton actuators, PhysX attempts to capture the complete native sequence—effort staging, all actuator models, and torque telemetry—and replays it automatically.

Two alternating graphs preserve state ping-pong for delayed/stateful controllers: graph A reads state A and writes B; graph B reads B and writes A. Capture failure falls back to eager execution. Stateful native actuators are rejected inside a caller-owned outer CUDA capture because Python-side state ownership cannot advance on replay; callers should let the PhysX adapter own their alternating graphs. Stateless native execution remains composable with an outer capture.

Neural actuator checkpoints

Newton MLP/LSTM checkpoints are resolved through Isaac Lab's shared retrieve_file_path() cache before PyTorch loads and re-saves them with Newton metadata. This enables remote HTTP/Nucleus-style checkpoint paths without adding a dependency while retaining local TorchScript and dictionary checkpoint support.

Documentation

  • Reworked the actuator concept page around the command pipeline, ownership, explicit/implicit behavior, limits, delay, armature, friction, and the runtime collection API.
  • Documented logical groups versus execution aggregation, pointer-stable staging, and PhysX-hosted Newton graph behavior/fallback.
  • Updated migration guides and tutorials to use actuators.command.
  • Added light/dark pipeline and parameter-reference media.
  • Moved the procedural comparison utility to tools/actuator_parameters.py.

Performance

Franka Reach end-to-end runtime

Protocol: PhysX Isaac-Reach-Franka, 4096 environments, seed 42, no visualizer, 100 warm-up steps, 1000 measured steps, three runs per variant. Franka has three logical implicit-actuator groups, so the final branch exercises automatic aggregation and the fused implicit Warp path.

Variant Mean step (ms) Median step (ms) Mean FPS Median FPS
develop base 14.8770 14.9165 275,769 274,595
PR before final staging/graph pass 13.3700 12.9113 307,123 317,242
Final branch 13.6355 14.0797 301,516 290,914

Relative to develop, the final branch reduced mean step time by 8.35% and increased mean throughput by 9.34%. Median step time improved by 5.61%, with median throughput up 5.94%.

The raw final run means were 14.0797, 14.3350, and 12.4917 ms, so variance is material. The final pointer-stability/graph pass did not resolve as an additional task-level gain over the already-aggregated PR snapshot. The performance claim is therefore the complete develop-to-final result; automatic merging is the primary optimization, while graph replay is structurally cheaper but below this benchmark's noise floor.

Go2 graph/native-path isolation

The same 4096-environment, 100-warm-up/1000-measured-step protocol was run on Go2, which contains one DC motor group and therefore does not exercise merging.

Actuator path on PhysX Mean step (ms) Median step (ms) Mean FPS Median FPS
Isaac Lab DC motor 14.9350 15.3569 278,239 266,720
Newton actuator 15.0980 14.7923 271,539 276,901

The mean favors Lab by 1.09%, while the median favors Newton by 3.68% in step time. This disagreement is run-to-run noise, not evidence of a graph speedup.

Training evidence

Long-form runs used PhysX physics, 4096 environments, RSL-RL, seed 42, and 500 learning iterations. “PhysX + Newton actuators” changes actuator execution, not the physics backend.

Run Final reward Best reward Final episode length Mean FPS
Go2, develop, regular PhysX actuators 35.432 35.645 1000.00 212,888
Go2, PR, PhysX + Newton actuators 35.676 35.961 1000.00 208,391
ANYmal-D flat, PhysX + Newton actuators 21.341 21.579 987.90 173,677
Spot flat, PhysX + Newton actuators 234.708 263.764 882.28 88,257
A1 flat (contrib), PhysX + Newton actuators 31.414 31.414 1000.00 219,286
Go1 flat (contrib), PhysX + Newton actuators 36.304 36.351 1000.00 205,034

The controlled Go2 training pair reached comparable reward and episode length. The other robots establish that diverse explicit actuator configurations complete meaningful training, not matched per-robot performance parity.

Validation

  • ./isaaclab.sh -p -m pytest source/isaaclab/test/actuators -q448 passed
  • Focused PhysX/Newton graph, fallback, graphable and non-graphable stateful outer-capture rejection, delayed equivalence, and reset suite — 9 passed
  • ./isaaclab.sh -d — Sphinx warning-as-error build succeeded
  • ./isaaclab.sh -f — all repository-wide hooks passed before commit and push
  • Independent final review — no Critical or Important findings

The fused GPU implicit telemetry equation differs from the previous Torch expression by at most one float32 ULP (4.768e-7) for the exercised values because of arithmetic ordering. Processed position, velocity, and feed-forward effort commands sent to PhysX remain exact.

One existing reversed-public-joint-ordering test triggers a PhysX tensor-setter CUDA illegal-memory-access on this host. It reproduces at the untouched pre-optimization revision with every Isaac Lab package import pinned to that checkout. Because the fault poisons the CUDA process, the graph/state suite was run in fresh focused processes; the ordering failure is not caused by this performance pass.

Type of change

  • New feature: backend-neutral actuator collection and Newton-native actuator paths
  • Performance: automatic execution aggregation, fused/cached Warp staging, and CUDA-graph replay
  • Bug fix: remote Newton neural checkpoints and backend command-staging edge cases
  • Documentation update

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh -f
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove the fix or feature is effective
  • I have added a changelog fragment for every touched package
  • My name already exists in CONTRIBUTORS.md

Add ActuatorCollection as the backend-neutral owner for actuator state and command APIs. Route legacy articulation target and gain setters through the collection with deprecation warnings.

Move common articulation actuator-control forwarding into a shared helper and keep backend adapters focused on command submission, friction writes, and native actuator paths for PhysX, OVPhysX, and Newton.

Add changelog fragments and focused ActuatorCollection tests.
Describe how Newton actuator authoring will reuse the shared asset cache to resolve remote neural-network checkpoints before PyTorch loads them.
Define the scoped implementation and real-task verification steps for cached remote actuator-network checkpoints.
Treat generated actuator plots as opaque image assets so Git and GitHub do not report their XML serialization as reviewable line churn. Mark them as generated so GitHub collapses the files by default.
Resolve actuator-network paths through the shared asset cache before PyTorch adds Newton metadata. This lets remote MLP and LSTM checkpoints load through the Newton actuator adapter.
Keep temporary workflow documents out of the Sphinx source tree so strict documentation builds do not report orphan warnings.
Separate actuator-model inputs from processed joint commands so the public API uses precise terminology across physics backends. Update the migration guide, tutorials, pipeline diagrams, and actuator parameter tool to match.
@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team infrastructure labels Aug 1, 2026
Aggregate disjoint stateless actuator groups without coupling their
logical configuration or parameter access. Keep batch staging
pointer-stable, fuse implicit execution in Warp, and reuse cached gather
and scatter launches.

Capture graphable Newton actuators on the PhysX path with alternating
state graphs, eager fallback, and protection against unsafe nested
stateful capture.
Comment on lines +253 to +277
class JointCommand:
"""Processed commands produced for the simulated joints."""

def __init__(self, collection: ActuatorCollection) -> None:
"""Initialize the joint command view.

Args:
collection: Owning actuator collection.
"""
self._collection = collection

@property
def position(self) -> ProxyArray:
"""Processed position commands [m or rad, depending on joint type]."""
return self._collection._joint_pos_target_sim_ta

@property
def velocity(self) -> ProxyArray:
"""Processed velocity commands [m/s or rad/s, depending on joint type]."""
return self._collection._joint_vel_target_sim_ta

@property
def effort(self) -> ProxyArray:
"""Processed effort commands [N or N·m, depending on joint type]."""
return self._collection._joint_effort_target_sim_ta

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Why next the class definition here?

Comment on lines +64 to +90
class Command:
"""Commands received by the actuator models.

Position and velocity commands use joint-side coordinates. All command
arrays are indexed by articulation joint, not by motor shaft.
"""

def __init__(self, collection: ActuatorCollection) -> None:
"""Initialize the command view.

Args:
collection: Owning actuator collection.
"""
self._collection = collection

@property
def position(self) -> ProxyArray:
"""Desired positions [m or rad, depending on joint type]."""
return self._collection._joint_pos_target_ta

@property
def velocity(self) -> ProxyArray:
"""Desired velocities [m/s or rad/s, depending on joint type]."""
return self._collection._joint_vel_target_ta

@property
def effort(self) -> ProxyArray:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Why nest class definition this class?

Comment on lines +492 to +493
self._soft_joint_vel_limits = wp.zeros(shape, dtype=wp.float32, device=self.device)
self._gear_ratio = wp.ones(shape, dtype=wp.float32, device=self.device)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Why do we define this here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Is it just for compatiblity?

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

Labels

documentation Improvements or additions to documentation infrastructure isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant