Control Utilities¶
This page documents utility classes and components used by controllers in SoRoMoX.
PID Control¶
The PIDControl class provides a flexible PID control implementation that can be used as the error-based feedback term in model-based controllers.
Features¶
- Flexible gain specification: Scalar, diagonal vector, or full matrix gains
- Anti-windup: Optional saturation functions for integral error
- JAX-compatible: Fully jittable for high-performance execution
Control Law¶
The PID control law is:
The integral error dynamics include optional saturation for anti-windup:
For the built-in hyperbolic-tangent saturation, gamma is an inverse error
scale. The componentwise scalar/vector form is
This keeps the saturation argument dimensionless, preserves the units of the
integrated error, and has unit slope at the origin. A vector gamma can assign
different physical error scales to heterogeneous coordinates. Scalar and
vector values of gamma must be strictly positive; a matrix gamma must be
symmetric positive definite.
PIDControl¶
soromox.control.PIDControl
¶
PIDControl(Kp: float | Array, Ki: float | Array, Kd: float | Array, saturation_fn: SaturationFnName | Callable[[Array], Array] | None = None, gamma: float | Array = 1.0)
Bases: Module
flowchart TD
soromox.control.PIDControl[PIDControl]
click soromox.control.PIDControl href "" "soromox.control.PIDControl"
PID controller with configurable gains and optional integral error saturation.
This class implements a standard PID control law
u = Kp @ e + Ki @ integral_error + Kd @ ed
where
- e is the tracking error
- ed is the error derivative
- integral_error is the accumulated integral of the (possibly saturated) error
The integral error dynamics are
integral_error_dot = sat(e)
where sat() is a saturation function that can be used for anti-windup.
Attributes:
| Name | Type | Description |
|---|---|---|
Kp |
Array
|
Proportional gain (scalar, diagonal vector, or matrix). |
Ki |
Array
|
Integral gain (scalar, diagonal vector, or matrix). |
Kd |
Array
|
Derivative gain (scalar, diagonal vector, or matrix). |
gamma |
Array
|
Inverse error scale for built-in saturation functions (e.g.,
|
References
Pustina, P., Borja, P., Della Santina, C., & De Luca, A. (2022). P-satI-D shape regulation of soft robots. IEEE Robotics and Automation Letters, 8(1), 1-8.
Initialize the PID controller.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
Kp
|
float | Array
|
Proportional gain. Can be: - A scalar (float or 0-d array): applied uniformly to all error components. - A 1-d array (diagonal): element-wise multiplication with error. - A 2-d array (matrix): full matrix multiplication with error. |
required |
Ki
|
float | Array
|
Integral gain. Same format options as Kp. |
required |
Kd
|
float | Array
|
Derivative gain. Same format options as Kp. |
required |
saturation_fn
|
SaturationFnName | Callable[[Array], Array] | None
|
Optional saturation function for anti-windup. Can be:
- None or "identity": No saturation (default).
- "tanh": Uses |
None
|
gamma
|
float | Array
|
Inverse error scale for built-in saturation functions. Can be
a positive scalar, positive diagonal vector, or symmetric
positive-definite matrix. For a scalar or vector, |
1.0
|
__call__
¶
Compute the PID control output and integral error derivative.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e
|
Array
|
Tracking error vector, shape (n,). |
required |
ed
|
Array
|
Error derivative vector, shape (n,). |
required |
integral_error
|
Array
|
Current integral of error, shape (n,). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
u |
Array
|
Control output, shape (n,) or (m,) depending on gain shapes. |
integral_error_dot |
Array
|
Time derivative of integral error, shape (n,). This is the (possibly saturated) error that should be integrated. |
update_gains
¶
update_gains(gains: dict[str, Array]) -> PIDControl
This function updates the gains of the PIDControl object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gains
|
dict[str, Array]
|
proportional, integral, and derivative gains |
required |
Returns:
| Name | Type | Description |
|---|---|---|
updated_self |
PIDControl
|
self object with updated parameters |
PIDControllerState¶
Container for PID controller state (integral error).
soromox.control.PIDControllerState
dataclass
¶
State container for the PID controller.
This dataclass holds all internal state variables that evolve over time during closed-loop control. It is registered as a JAX pytree to enable use with JAX transformations and ODE solvers.
Attributes:
| Name | Type | Description |
|---|---|---|
integral_error |
Array
|
Accumulated integral of the (possibly saturated) error. Shape: (num_dofs,) |
tree_unflatten
classmethod
¶
Unflatten the state from JAX pytree operations.
zero
classmethod
¶
zero(num_dofs: int) -> PIDControllerState
Create a zero-initialized controller state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_dofs
|
int
|
Number of degrees of freedom. |
required |
Returns:
| Type | Description |
|---|---|
PIDControllerState
|
PIDControllerState with zero integral error. |
Usage Example¶
import jax.numpy as jnp
from soromox.control import PIDControl, PIDControllerState
# Create PID controller with diagonal gains
pid = PIDControl(
Kp=jnp.array([100.0, 100.0]), # Proportional gains
Ki=jnp.array([10.0, 10.0]), # Integral gains
Kd=jnp.array([20.0, 20.0]), # Derivative gains
saturation_fn="tanh", # Anti-windup saturation
gamma=1.0, # Inverse saturation-error scale
)
# Initialize controller state
control_state = PIDControllerState.zero(num_dofs=2)
# Compute control output
e = q_des - q # Position error
ed = qd_des - qd # Velocity error
u, integral_error_dot = pid(e, ed, control_state.integral_error)
Reference Trajectory¶
The ReferenceTrajectory class provides a flexible way to specify desired trajectories for controllers. It supports both discrete time-series data and continuous functions, automatically deriving velocities and accelerations when not provided.
Features¶
- Dual representation: Discrete time series and continuous functions
- Auto-derivation: Automatically computes velocity and acceleration using JAX autodiff
- Linear interpolation: Creates smooth continuous trajectories from discrete data
- JAX PyTree: Compatible with JAX transformations and ODE solvers
ReferenceTrajectory¶
soromox.control.ReferenceTrajectory
dataclass
¶
ReferenceTrajectory(ts: Array, x_des_ts: Array | None = None, x_des_fn: Callable[[Array], Array] | None = None, xd_des_ts: Array | None = None, xd_des_fn: Callable[[Array], Array] | None = None, xdd_des_ts: Array | None = None, xdd_des_fn: Callable[[Array], Array] | None = None, rotation_representation: RotationRepresentation | None = None, n_points: int = 1, is_planar: bool = False)
Container for reference trajectory with both discrete and continuous representations.
This class provides flexible specification of reference trajectories for controllers. Users can provide either discrete time-series data or continuous functions, and the class will automatically generate the other representation.
When working with poses that include orientation (for 3D robots), you can specify
a rotation_representation to enable proper velocity derivation. This ensures that
angular velocities are computed correctly from orientation changes (not just naive
differentiation).
Parameters¶
ts : Array Time steps array, shape (T,). x_des_ts : Optional[Array] Desired poses at discrete time steps, shape (T, dim). If not provided, will be computed from x_des_fn. x_des_fn : Optional[Callable[[Array], Array]] Function that returns desired pose at any time t. If not provided, will be created via linear interpolation of x_des_ts. xd_des_ts : Optional[Array] Desired velocities (twists) at discrete time steps, shape (T, dim). If not provided, will be computed from xd_des_fn or derived from x_des_fn. xd_des_fn : Optional[Callable[[Array], Array]] Function that returns desired velocity at any time t. If not provided, will be created via interpolation or derived from x_des_fn. xdd_des_ts : Optional[Array] Desired accelerations at discrete time steps, shape (T, dim). If not provided, will be computed from xdd_des_fn or derived from xd_des_fn. xdd_des_fn : Optional[Callable[[Array], Array]] Function that returns desired acceleration at any time t. If not provided, will be created via interpolation or derived from xd_des_fn. rotation_representation : Optional[RotationRepresentation] The rotation representation used in the pose. If provided, velocity derivation will properly convert orientation derivatives to angular velocities. - ROTATION_VECTOR: 3D rotation vector (axis-angle) - QUATERNION: 4D unit quaternion - ROTATION_MATRIX_6D: 6D continuous representation If None, simple differentiation is used (appropriate for position-only trajectories or planar robots). n_points : int Number of points in the pose (default 1). Used with rotation_representation for proper velocity derivation. is_planar : bool Whether the trajectory is for a planar robot. If True, velocity derivation uses simple differentiation even if rotation_representation is set.
Examples¶
From discrete time series (position-only):
ts = jnp.linspace(0, 1, 100)
x_des_ts = jnp.sin(ts)[:, None] # shape (100, 1)
reference = ReferenceTrajectory(ts=ts, x_des_ts=x_des_ts)
reference.x_des_fn(0.5)
From a continuous function with proper twist derivation:
Usage Examples¶
From Discrete Time Series¶
import jax.numpy as jnp
from soromox.control import ReferenceTrajectory
# Time array
ts = jnp.linspace(0, 10, 1000)
# Desired positions at each time step
x_des_ts = jnp.column_stack([
jnp.sin(ts), # First DOF
jnp.cos(ts), # Second DOF
])
# Create trajectory - velocities and accelerations auto-derived
ref_traj = ReferenceTrajectory(ts=ts, x_des_ts=x_des_ts)
# Access continuous function (interpolated)
x_at_5 = ref_traj.x_des_fn(5.0)
xd_at_5 = ref_traj.xd_des_fn(5.0)
xdd_at_5 = ref_traj.xdd_des_fn(5.0)
From Continuous Function¶
import jax.numpy as jnp
from soromox.control import ReferenceTrajectory
# Define trajectory as a function
def x_des_fn(t):
return jnp.array([jnp.sin(t), jnp.cos(t)])
# Time array for discrete evaluation
ts = jnp.linspace(0, 10, 1000)
# Create trajectory - discrete arrays and derivatives auto-computed
ref_traj = ReferenceTrajectory(ts=ts, x_des_fn=x_des_fn)
# Access discrete arrays
x_des_ts = ref_traj.x_des_ts # Shape: (1000, 2)
xd_des_ts = ref_traj.xd_des_ts # Auto-derived via JAX autodiff
Constant Setpoint (Regulation)¶
import jax.numpy as jnp
from soromox.control import ReferenceTrajectory
# For regulation, create a constant trajectory
q_setpoint = jnp.array([0.5, 0.3])
ts = jnp.linspace(0, 10, 100)
x_des_ts = jnp.tile(q_setpoint, (len(ts), 1))
ref_traj = ReferenceTrajectory(ts=ts, x_des_ts=x_des_ts)
# xd_des and xdd_des will be (approximately) zero