Metadata-Version: 2.4
Name: newton-actuators
Version: 0.1.1
Summary: DEPRECATED: actuators are now part of the 'newton' package as 'newton.actuators'. This standalone package is no longer maintained.
Project-URL: Homepage, https://github.com/newton-physics/newton
Project-URL: Documentation, https://newton-physics.github.io/newton/latest/api/newton_actuators.html
Project-URL: Repository, https://github.com/newton-physics/newton-actuators
Project-URL: Migration Guide, https://newton-physics.github.io/newton/latest/concepts/actuators.html
Author-email: Newton Developers <warp-python@nvidia.com>
License-Expression: Apache-2.0
License-File: LICENSE.md
Classifier: Development Status :: 7 - Inactive
Classifier: Environment :: GPU :: NVIDIA CUDA
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Requires-Dist: warp-lang>=1.11.0.dev20251205
Provides-Extra: torch-cu12
Requires-Dist: torch>=2.7.0; extra == 'torch-cu12'
Provides-Extra: torch-cu13
Requires-Dist: torch>=2.10.0; extra == 'torch-cu13'
Description-Content-Type: text/markdown

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/newton-physics/newton-actuators/main)

> **⚠️ Deprecation Notice:** Starting with [Newton](https://github.com/newton-physics/newton) 1.3, actuators are created in and used exclusively from Newton's built-in `newton.actuators` module. This standalone `newton-actuators` package will no longer be maintained. Please migrate:
>
> - Concepts guide: https://newton-physics.github.io/newton/latest/concepts/actuators.html
> - API reference: https://newton-physics.github.io/newton/latest/api/newton_actuators.html

# Newton Actuators

GPU-accelerated actuator library for physics simulations.

This library provides a collection of actuator implementations that integrate with physics simulation pipelines. Actuators read from simulation state arrays and write computed forces/torques back to control arrays.

## Installation

```bash
pip install newton-actuators
```

Or install from source:

```bash
cd newton-actuators
pip install -e .
```

### With PyTorch (for neural network actuators)

The `ActuatorNetMLP` and `ActuatorNetLSTM` actuators require PyTorch. Install
the extra matching your CUDA version:

**Using uv** (index routing is automatic):

```bash
uv pip install "newton-actuators[torch-cu12]"   # CUDA 12.x
uv pip install "newton-actuators[torch-cu13]"   # CUDA 13.x
```

**Using pip** (requires manual `--extra-index-url`):

```bash
pip install "newton-actuators[torch-cu12]" --extra-index-url https://download.pytorch.org/whl/cu128
pip install "newton-actuators[torch-cu13]" --extra-index-url https://download.pytorch.org/whl/cu130
```

## API Reference

### Actuator Classes

| Actuator | Description | Stateful | Transmission |
|----------|-------------|----------|--------------|
| `ActuatorPD` | Stateless PD controller | No | No |
| `ActuatorPID` | PID controller with integral term | Yes | No |
| `ActuatorDelayedPD` | PD controller with input delay | Yes | No |
| `ActuatorDCMotor` | PD with DC motor velocity-dependent saturation | No | No |
| `ActuatorRemotizedPD` | Delayed PD with angle-dependent torque limits | Yes | No |
| `ActuatorNetMLP` | MLP network actuator with position/velocity history | Yes | No |
| `ActuatorNetLSTM` | LSTM network actuator with recurrent hidden state | Yes | No |

#### Control Laws

- **ActuatorPD**: `τ = clamp(constant + act + Kp·(target_pos - q) + Kd·(target_vel - v), ±max_force)`
- **ActuatorPID**: `τ = clamp(constant + act + Kp·(target_pos - q) + Ki·∫e·dt + Kd·(target_vel - v), ±max_force)`
- **ActuatorDelayedPD**: Same as PD but with delayed targets (circular buffer)
- **ActuatorDCMotor**: Same PD force computation, but torque is clamped to velocity-dependent bounds from the motor torque-speed curve: `τ_max(v) = clamp(τ_sat·(1 - v/v_max), 0, effort_limit)`, `τ_min(v) = clamp(τ_sat·(-1 - v/v_max), -effort_limit, 0)`, `τ = clamp(τ, τ_min(v), τ_max(v))`
- **ActuatorRemotizedPD**: Same as DelayedPD, but torque limits are interpolated from an angle-dependent lookup table: `τ_limit = interp(q, lookup_table)`
- **ActuatorNetMLP**: `τ = clamp(network(cat(pos_error_history * pos_scale, vel_history * vel_scale)) * torque_scale, ±max_force)` — history is maintained internally
- **ActuatorNetLSTM**: `τ = clamp(network(input, (h, c)), ±max_force)` — hidden and cell state maintained internally

### Base Class Methods

All actuators inherit from `Actuator` and provide these methods:

- `resolve_arguments(args) -> dict`: (classmethod) Resolve user-provided arguments with defaults
- `is_stateful() -> bool`: Returns True if the actuator maintains internal state
- `is_graphable() -> bool`: Returns True if `step()` can be captured in a CUDA graph (False for torch-based NN actuators)
- `has_transmission() -> bool`: Returns True if the actuator has a transmission phase
- `state() -> State | None`: Returns a new state instance (None for stateless actuators)
- `step(sim_state, sim_control, current_state, next_state, dt)`: Execute one control step

### State Classes

Stateful actuators use nested State classes:

- `ActuatorPID.State` - Contains the integral term for PID control
- `ActuatorDelayedPD.State` - Contains circular buffers for delayed targets
- `ActuatorRemotizedPD.State` - Inherits `ActuatorDelayedPD.State` (same delay buffers)

- `ActuatorNetMLP.State` - Contains position error and velocity history buffers
- `ActuatorNetLSTM.State` - Contains LSTM hidden and cell state tensors

## Workflow

1. **Create actuators** with appropriate parameters
2. **Check statefulness**: Call `actuator.is_stateful()` to determine if state management is needed
3. **Initialize states**: For stateful actuators, create double-buffered states with `actuator.state()`
4. **Simulation loop**: Call `actuator.step()` to compute forces
5. **Swap buffers**: For stateful actuators, swap state buffers after each step
6. **Reset between episodes**: Call `state.reset()` on any stateful actuator's state to zero internal buffers without reallocating

## Examples

### Stateless Actuator (ActuatorPD)

```python
import warp as wp
from newton_actuators import ActuatorPD

# Create a PD actuator for 3 DOFs
indices = wp.array([0, 1, 2], dtype=wp.uint32)
pd_actuator = ActuatorPD(
    input_indices=indices,
    output_indices=indices,
    kp=wp.array([100.0, 100.0, 100.0], dtype=wp.float32),
    kd=wp.array([10.0, 10.0, 10.0], dtype=wp.float32),
    max_force=wp.array([50.0, 50.0, 50.0], dtype=wp.float32),
    constant_force=wp.array([0.0, 0.0, 0.0], dtype=wp.float32),
)

# In simulation loop - stateless actuators don't need state management
pd_actuator.step(sim_state, sim_control, None, None, dt=0.01)
```

### Stateful Actuator (ActuatorPID)

```python
import warp as wp
from newton_actuators import ActuatorPID

indices = wp.array([0, 1], dtype=wp.uint32)
pid_actuator = ActuatorPID(
    input_indices=indices,
    output_indices=indices,
    kp=wp.array([100.0, 100.0], dtype=wp.float32),
    ki=wp.array([10.0, 10.0], dtype=wp.float32),
    kd=wp.array([5.0, 5.0], dtype=wp.float32),
    max_force=wp.array([50.0, 50.0], dtype=wp.float32),
    integral_max=wp.array([10.0, 10.0], dtype=wp.float32),
    constant_force=wp.array([0.0, 0.0], dtype=wp.float32),
)

# Check if actuator needs state management
if pid_actuator.is_stateful():
    # Create double-buffered states
    state_a = pid_actuator.state()
    state_b = pid_actuator.state()

# Simulation loop with state swapping
current_state, next_state = state_a, state_b
for step in range(num_steps):
    pid_actuator.step(sim_state, sim_control, current_state, next_state, dt=0.01)
    current_state, next_state = next_state, current_state  # Swap buffers
```

### Non-Graphable Stateful Actuator (ActuatorNetLSTM)

Network actuators (`ActuatorNetMLP`, `ActuatorNetLSTM`) are stateful but not
CUDA-graphable due to Warp-PyTorch interop. Because their `step()` cannot be
captured in a CUDA graph, double-buffering is not strictly required — you can
pass the **same state object** as both `current_state` and `next_state`:

```python
# Simple: single state object (fine when not using CUDA graphs)
state = lstm_actuator.state()
for step in range(num_steps):
    lstm_actuator.step(sim_state, sim_control, state, state, dt=0.01)
```

## USD Parsing

The library includes utilities for parsing actuator definitions from USD files:

```python
from newton_actuators import parse_actuator_prim

# Parse a USD prim with actuator attributes
result = parse_actuator_prim(prim)
if result is not None:
    actuator_class = result.actuator_class  # e.g., ActuatorPD
    target_paths = result.target_paths      # e.g., ["/World/Robot/Joint1"]
    kwargs = result.kwargs                   # e.g., {"kp": 100.0, "kd": 10.0}
```

## License

Apache-2.0
