-
Notifications
You must be signed in to change notification settings - Fork 468
Move modular design of newton-actuators to Newton repo #2449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
adenzler-nvidia
merged 43 commits into
newton-physics:main
from
jvonmuralt:actuators-move
Apr 23, 2026
Merged
Changes from 41 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
4f8f617
new lib design
jvonmuralt 2d8eefd
formatting and types
jvonmuralt e23b3ed
renames
jvonmuralt 9738c81
cleanup
jvonmuralt ca82871
cleanup
jvonmuralt 79cc869
cleanup
jvonmuralt 36437b5
comments
jvonmuralt 08541f0
add documentation and minor restructuring
jvonmuralt 48810f5
improve documentation
jvonmuralt 74112db
cleanup, refactor of position based clamping
jvonmuralt 2b56eef
cleanup
jvonmuralt 997900c
refactor delay to allow for randomization of delay
jvonmuralt 387ef13
delay refactor
jvonmuralt 875284f
add pos and frc optional indices
jvonmuralt cce0824
bring scales to the mlp
jvonmuralt d2a885d
cleanup
jvonmuralt a067fe4
restructure tests
jvonmuralt 206f624
comments
jvonmuralt 2f32b38
refactor
jvonmuralt 3ef0178
fix dc motor
jvonmuralt f3d81ae
rename, improve docs and errors
jvonmuralt 9a55c51
fix
jvonmuralt 1af4b3e
Merge remote-tracking branch 'origin/main' into actuators-move
jvonmuralt 1f5efb3
fix naming
jvonmuralt 081a5a0
cleanup
jvonmuralt 39b28aa
cleanup
jvonmuralt 5359884
add schema, address comments
jvonmuralt 3deb9c2
Merge remote-tracking branch 'origin/main' into actuators-move
jvonmuralt ce2f3cb
remining comments and docs fixes
jvonmuralt 9dc2c8c
fix year
jvonmuralt 7978ad8
minor
jvonmuralt 3a7dc02
minor
jvonmuralt 096c598
minor
jvonmuralt 4d2c21a
minor
jvonmuralt ccc409d
comments
jvonmuralt 08d0f95
comments
jvonmuralt 74d4084
raise on unsupported target joints
jvonmuralt 42fd7d8
add a 1-DOF guard in import_usd.py
jvonmuralt 381c53c
remove box
jvonmuralt c4e9d2e
remove box
jvonmuralt 03fe947
fix
jvonmuralt 0ba0da3
comments
jvonmuralt 974b6c6
add NotImplementedError
jvonmuralt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| .. SPDX-FileCopyrightText: Copyright (c) 2025 The Newton Developers | ||
| .. SPDX-License-Identifier: CC-BY-4.0 | ||
|
|
||
| newton.actuators | ||
| ================ | ||
|
|
||
| GPU-accelerated actuator models for physics simulations. | ||
|
|
||
| This module provides a modular library of actuator components — controllers, | ||
| clamping, and delay — that compute joint effort from simulation state and | ||
| control targets. Components are composed into an :class:`Actuator` instance | ||
| and registered with :meth:`~newton.ModelBuilder.add_actuator` during model | ||
| construction. | ||
|
|
||
| .. py:module:: newton.actuators | ||
| .. currentmodule:: newton.actuators | ||
|
|
||
| .. rubric:: Classes | ||
|
|
||
| .. autosummary:: | ||
| :toctree: _generated | ||
| :nosignatures: | ||
|
|
||
| Actuator | ||
| ActuatorParsed | ||
| Clamping | ||
| ClampingDCMotor | ||
| ClampingMaxEffort | ||
| ClampingPositionBased | ||
| ComponentKind | ||
| Controller | ||
| ControllerNeuralLSTM | ||
| ControllerNeuralMLP | ||
| ControllerPD | ||
| ControllerPID | ||
| Delay | ||
|
|
||
| .. rubric:: Functions | ||
|
|
||
| .. autosummary:: | ||
| :toctree: _generated | ||
| :signatures: long | ||
|
|
||
| parse_actuator_prim | ||
| register_actuator_component |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| .. SPDX-FileCopyrightText: Copyright (c) 2026 The Newton Developers | ||
| .. SPDX-License-Identifier: CC-BY-4.0 | ||
|
|
||
| .. currentmodule:: newton.actuators | ||
|
|
||
| Actuators | ||
| ========= | ||
|
|
||
| .. warning:: | ||
|
|
||
| The actuator API is **experimental** and may change in future releases. | ||
| Feedback is welcome — please file issues or discussion threads. | ||
|
|
||
| Actuators provide composable implementations that read physics simulation | ||
| state, compute effort, and **accumulate** (scatter-add) the effort into | ||
| control arrays for application to the simulation. The caller must zero the | ||
| output array before stepping actuators each frame. The simulator does not | ||
| need to be part of Newton: actuators are designed to be reusable anywhere the | ||
| caller can provide state arrays and consume effort. | ||
|
|
||
| Each :class:`Actuator` instance is **vectorized**: a single actuator object | ||
| operates on a batch of DOF indices in global state and control arrays, allowing | ||
| efficient integration into RL workflows with many parallel environments. | ||
|
|
||
| The goal is to provide canonical actuator models with support for | ||
| **differentiability** and **graphable execution** where the underlying | ||
| controller implementation supports it. Actuators are designed to be easy to | ||
| customize and extend for specific actuator models. | ||
|
|
||
| Architecture | ||
| ------------ | ||
|
|
||
| An actuator is composed from three building blocks, applied in this order: | ||
|
|
||
| .. code-block:: text | ||
|
|
||
| Actuator | ||
| ├── Delay (optional: delays command inputs by N actuator timesteps) | ||
| ├── Controller (control law that computes raw effort) | ||
| └── Clamping[] (clamps raw effort based on motor-limit modeling) | ||
| ├── ClampingMaxEffort (±max_effort symmetric clamp) | ||
| ├── ClampingDCMotor (velocity-dependent saturation) | ||
| └── ClampingPositionBased (position-dependent lookup table) | ||
|
|
||
| **Delay** | ||
| Optionally delays command inputs (control targets and feedforward terms) | ||
| by *N* actuator timesteps before they reach the controller, modeling | ||
| communication or processing latency. The delay always produces output; | ||
| when the buffer is empty or a DOF has ``delay_steps == 0``, the current | ||
| command inputs are used directly. When underfilled, the lag is clamped | ||
| to the available history so the oldest available entry is returned. | ||
|
|
||
| **Controller** | ||
| Computes raw actuator effort [N or N·m] from the current simulator state | ||
| and control targets. This is the actuator's control law — for example PD, | ||
| PID, or neural-network-based control. See the individual controller class | ||
| documentation for the control-law equations. | ||
|
|
||
| **Clamping** | ||
| Clamps raw effort based on motor-limit modeling. This applies | ||
| post-controller output limits to the computed effort to model motor limits | ||
| such as saturation, back-EMF losses, performance envelopes, or | ||
| position-dependent effort limits. Multiple clamping stages can be combined | ||
| on a single actuator. | ||
|
|
||
| The per-step pipeline is: | ||
|
|
||
| .. code-block:: text | ||
|
|
||
|
jvonmuralt marked this conversation as resolved.
|
||
| Delay read → Controller → Clamping → Scatter-add → State updates (controller + delay write) | ||
|
|
||
| Controllers and clamping objects are pluggable: implement the | ||
| :class:`Controller` or :class:`Clamping` base class to add new models. | ||
|
|
||
| .. note:: | ||
|
preist-nvidia marked this conversation as resolved.
|
||
|
|
||
| **Current limitations:** the first version does not include a transmission | ||
| model (gear ratios / linkage transforms), supports only single-input | ||
| single-output (SISO) actuators (one DOF per actuator), and does not model | ||
| actuator dynamics (inertia, friction, thermal effects). | ||
|
|
||
| Usage | ||
| ----- | ||
|
|
||
| Actuators are registered during model construction with | ||
| :meth:`~newton.ModelBuilder.add_actuator` and are instantiated automatically | ||
| when the model is finalized: | ||
|
|
||
| .. testsetup:: actuator-usage | ||
|
|
||
| import warp as wp | ||
| import newton | ||
| from newton.actuators import ( | ||
| Actuator, ClampingMaxEffort, ControllerPD, Delay, | ||
| ) | ||
|
|
||
| builder = newton.ModelBuilder() | ||
| link = builder.add_link() | ||
| joint = builder.add_joint_revolute(parent=-1, child=link, axis=newton.Axis.Z) | ||
| builder.add_articulation([joint]) | ||
| dof_index = builder.joint_qd_start[joint] | ||
|
|
||
| .. testcode:: actuator-usage | ||
|
|
||
| builder.add_actuator( | ||
| ControllerPD, | ||
| index=dof_index, | ||
| kp=100.0, | ||
| kd=10.0, | ||
| delay_steps=5, | ||
| clamping=[(ClampingMaxEffort, {"max_effort": 50.0})], | ||
| ) | ||
|
jvonmuralt marked this conversation as resolved.
|
||
|
|
||
| model = builder.finalize() | ||
|
|
||
| For manual construction (outside of :class:`~newton.ModelBuilder`), compose the | ||
| components directly: | ||
|
|
||
| .. testcode:: actuator-usage | ||
|
|
||
| indices = wp.array([0], dtype=wp.uint32) | ||
| kp = wp.array([100.0], dtype=wp.float32) | ||
| kd = wp.array([10.0], dtype=wp.float32) | ||
| max_e = wp.array([50.0], dtype=wp.float32) | ||
|
|
||
| actuator = Actuator( | ||
| indices, | ||
| controller=ControllerPD(kp=kp, kd=kd), | ||
| delay=Delay(delay_steps=wp.array([5], dtype=wp.int32), max_delay=5), | ||
| clamping=[ClampingMaxEffort(max_effort=max_e)], | ||
| ) | ||
|
|
||
|
|
||
| Stateful Actuators | ||
| ------------------ | ||
|
|
||
| Controllers that maintain internal state (e.g. :class:`ControllerPID` with an | ||
| integral accumulator, or :class:`ControllerNeuralLSTM` with hidden/cell state) and | ||
| actuators with a :class:`Delay` require explicit double-buffered state | ||
| management. Create two state objects with :meth:`Actuator.state` and swap them | ||
| after each step: | ||
|
|
||
| .. testcode:: actuator-usage | ||
|
|
||
| state_0 = model.actuators[0].state() | ||
| state_1 = model.actuators[0].state() | ||
| state = model.state() | ||
| control = model.control() | ||
|
|
||
| for step in range(3): | ||
| control.joint_f.zero_() # zero output before stepping actuators | ||
| model.actuators[0].step(state, control, state_0, state_1, dt=0.01) | ||
| state_0, state_1 = state_1, state_0 | ||
|
|
||
| Stateless actuators (e.g. a plain PD controller without delay) do not require | ||
| state objects — simply omit them: | ||
|
|
||
| .. testcode:: actuator-usage | ||
|
|
||
| # Build a stateless actuator (no delay, stateless controller) | ||
| b2 = newton.ModelBuilder() | ||
| lk = b2.add_link() | ||
| jt = b2.add_joint_revolute(parent=-1, child=lk, axis=newton.Axis.Z) | ||
| b2.add_articulation([jt]) | ||
| b2.add_actuator(ControllerPD, index=b2.joint_qd_start[jt], kp=50.0) | ||
| m2 = b2.finalize() | ||
|
|
||
| m2.actuators[0].step(m2.state(), m2.control()) | ||
|
|
||
| Differentiability and Graph Capture | ||
| ----------------------------------- | ||
|
|
||
| Whether an actuator supports differentiability and CUDA graph capture depends on | ||
| its controller. :class:`ControllerPD` and :class:`ControllerPID` are fully | ||
| graphable. Neural-network controllers (:class:`ControllerNeuralMLP`, | ||
| :class:`ControllerNeuralLSTM`) require PyTorch and are not graphable due to | ||
| framework interop overhead. | ||
|
|
||
| :meth:`Actuator.is_graphable` returns ``True`` when all components can be | ||
| captured in a CUDA graph. | ||
|
|
||
| Available Components | ||
| -------------------- | ||
|
|
||
| Delay | ||
| ^^^^^ | ||
|
|
||
| * :class:`Delay` — circular-buffer delay for control targets (stateful). | ||
|
|
||
| Controllers | ||
| ^^^^^^^^^^^ | ||
|
|
||
| * :class:`ControllerPD` — proportional-derivative control law (stateless). | ||
| * :class:`ControllerPID` — proportional-integral-derivative control law | ||
| (stateful: integral accumulator with anti-windup clamp). | ||
| * :class:`ControllerNeuralMLP` — MLP neural-network controller (requires | ||
| PyTorch, stateful: position/velocity history buffers). | ||
| * :class:`ControllerNeuralLSTM` — LSTM neural-network controller (requires | ||
| PyTorch, stateful: hidden/cell state). | ||
|
|
||
| See the API documentation for each controller's control-law equations. | ||
|
|
||
| Clamping | ||
| ^^^^^^^^ | ||
|
|
||
| * :class:`ClampingMaxEffort` — symmetric clamp to ±max_effort per actuator. | ||
| * :class:`ClampingDCMotor` — velocity-dependent effort saturation using the DC | ||
| motor effort-speed characteristic. | ||
| * :class:`ClampingPositionBased` — position-dependent effort limits via | ||
| interpolated lookup table (e.g. for linkage-driven joints). | ||
|
|
||
| Multiple clamping objects can be stacked on a single actuator; they are applied | ||
|
preist-nvidia marked this conversation as resolved.
|
||
| in sequence. | ||
|
|
||
| Customization | ||
| ------------- | ||
|
|
||
| Any actuator can be assembled from the existing building blocks — mix and | ||
| match controllers, clamping stages, and delay to fit a specific use case. | ||
| When the built-in components are not sufficient, implement new ones by | ||
| subclassing :class:`Controller` or :class:`Clamping`. | ||
|
|
||
| For example, a custom controller needs to implement | ||
| :meth:`~Controller.compute` and :meth:`~Controller.resolve_arguments`: | ||
|
|
||
| .. code-block:: python | ||
| :caption: Skeleton — the ``compute`` body is omitted; see existing | ||
| controllers for complete examples. | ||
|
|
||
| import warp as wp | ||
| from newton.actuators import Controller | ||
|
|
||
| class MyController(Controller): | ||
| @classmethod | ||
| def resolve_arguments(cls, args): | ||
| return {"gain": args.get("gain", 1.0)} | ||
|
|
||
| def __init__(self, gain: wp.array): | ||
| self.gain = gain | ||
|
|
||
| def compute(self, positions, velocities, target_pos, target_vel, | ||
|
jvonmuralt marked this conversation as resolved.
|
||
| feedforward, pos_indices, vel_indices, | ||
| target_pos_indices, target_vel_indices, | ||
| forces, state, dt, device=None): | ||
| # Launch a Warp kernel that writes effort into `forces` | ||
| ... | ||
|
|
||
| ``resolve_arguments`` maps user-provided keyword arguments (from | ||
| :meth:`~newton.ModelBuilder.add_actuator` or USD schemas) to constructor | ||
| parameters, filling in defaults where needed. | ||
|
|
||
| Similarly, a custom clamping stage subclasses :class:`Clamping` and implements | ||
| :meth:`~Clamping.modify_forces` (which reads effort from a source buffer and writes bounded effort to a destination buffer). | ||
|
|
||
| See Also | ||
| -------- | ||
|
|
||
| * :mod:`newton.actuators` — full API reference | ||
| * :meth:`newton.ModelBuilder.add_actuator` — registering actuators during | ||
| model construction | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.