Core ABCs¶
shinro.components
ConfigDriven¶
Mixin for components constructed from a TOML config.
Subclasses declare a frozen Config dataclass whose fields ARE the
config schema. parse_config strict-parses a raw TOML dict into it:
unknown keys, a wrong type value, and missing required fields are loud
errors naming the component, so authoring typos surface at parse time
instead of mid-simulation.
Config
class-attribute
¶
The component's strict config dataclass. Subclasses must override.
parse_config
classmethod
¶
Strict-parse a TOML config dict into cls.Config.
An already-built Config instance is passed through unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Any
|
Raw TOML dict (with optional |
required |
Returns:
| Type | Description |
|---|---|
Any
|
A |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the subclass has not declared |
ValueError
|
On unknown keys, a mismatched |
Source code in src/shinro/components.py
load_config
classmethod
¶
Strict-parse a TOML config into cls.Config, then inject plant-derived values.
With plant, dt is filled from the plant when the config omits
it and a declared dt that disagrees with the plant's is a loud
error (the plant is the source of truth). With derive_model,
A_dynamics/B_dynamics are additionally derived from the plant
when the config declares neither. See
shinro.utils.linearization.inject_plant_derived.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Raw TOML dict (with optional |
required | |
plant
|
Optional plant for derived-value injection. |
None
|
|
derive_model
|
bool
|
Also derive |
False
|
Returns:
| Type | Description |
|---|---|
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
On strict-parse failure or a |
Source code in src/shinro/components.py
PhysicsEngine¶
Bases: ABC
Abstract base class for physics engines (MuJoCo, PyBullet, Drake, etc.).
Provides name-based access to joints, bodies, and actuators. Plants use this interface instead of importing a physics engine directly.
actuator_names
abstractmethod
property
¶
List of all actuator names in the model.
has_free_joint
property
¶
Whether the model's first joint is a free (floating) joint.
get_joint_qpos
abstractmethod
¶
set_joint_qpos
abstractmethod
¶
get_joint_vel
abstractmethod
¶
set_joint_ctrl
abstractmethod
¶
get_joint_limits
abstractmethod
¶
get_body_xpos
abstractmethod
¶
get_body_xquat
abstractmethod
¶
get_body_id
abstractmethod
¶
compute_jacobian
abstractmethod
¶
Return (jacp, jacr) — 3×nv position and orientation Jacobians for a body.
compute_jacobian_for_joints
abstractmethod
¶
Return the 6×k Jacobian (position + orientation) for a body, extracting only the columns corresponding to the named joints. Returns shape (6, k).
Source code in src/shinro/components.py
forward
abstractmethod
¶
step
abstractmethod
¶
reset
abstractmethod
¶
get_sensor_data
abstractmethod
¶
pin_free_base
¶
Pin a floating planar base to (x, y) with upright orientation.
For engines whose world contains a free joint driven by a
self-integrating plant (see Plant.post_engine_step): writes the
plant's planar pose into the base, pins its orientation upright, and
zeroes the floating-base velocities. Default: no-op — engines without a
floating base (or with a contact-physics-owned base) need not override.
Source code in src/shinro/components.py
Controller¶
Bases: ConfigDriven, ABC
Abstract base class for all controllers.
A controller computes control actions (e.g., torques, voltages) from reference signals and/or state feedback. Subclasses implement specific control laws (PID, MPC, LQR, etc.).
Usage
controller = MyController(...) action = controller.compute(reference=ref, state=x) controller.reset()
compute
abstractmethod
¶
Compute the control action.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments for computation. |
()
|
**kwargs
|
Any
|
Keyword arguments for computation. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The computed control action. |
Source code in src/shinro/components.py
reset
¶
Reset the controller to its initial state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments for reset. |
()
|
**kwargs
|
Any
|
Keyword arguments for reset. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
None. |
Plant¶
Bases: ConfigDriven, ABC
Abstract base class for a system plant.
A plant represents the system to be controlled (e.g., a robot, motor, or dynamical system). It provides the current state, a model for prediction/optimization, and a step method for simulation.
Usage
plant = MyPlant(...) state = plant.get_state() model = plant.get_model() next_state = plant.step(control_input)
input_dim
class-attribute
instance-attribute
¶
Control input dimension.
Plants that support linearization via linearize_plant should set
this in __init__ so get_model() can default u0 correctly.
None means the plant has not declared its input dimension; callers
of linearize_plant must then pass u0 explicitly.
bk
instance-attribute
¶
bk: ArrayBackend
Array backend the plant computes with (numpy or torch). Concrete plants set
this in __init__.
dt
instance-attribute
¶
Integration time step in seconds. Concrete plants set this in __init__.
get_state
abstractmethod
¶
Get the current state of the plant.
Returns:
| Type | Description |
|---|---|
Any
|
The current state (e.g., joint positions, velocities). |
get_model
abstractmethod
¶
Get the mathematical model of the plant.
For linear plants, returns (A, B) state-space matrices.
For nonlinear plants, accepts optional x0 and u0 keyword
arguments to linearize the dynamics around an operating point.
Returns:
| Type | Description |
|---|---|
Any
|
The plant model (e.g., a dynamics function, matrices, or |
Any
|
a callable used by the controller for prediction). |
Source code in src/shinro/components.py
step
abstractmethod
¶
Perform a single time step simulation or execution of the plant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments (e.g., control input). |
()
|
**kwargs
|
Any
|
Keyword arguments. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The next state or result of the step. |
Source code in src/shinro/components.py
physics_engine
abstractmethod
¶
physics_engine(engine: PhysicsEngine | None, *args: Any, **kwargs: Any) -> Any
Attach a physics engine to the plant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine | None
|
A PhysicsEngine instance, or None to detach. |
required |
reset_state
¶
Zero the plant's analytic state at a run boundary.
Concrete default preserves historical behavior (RobotSim.reset zeroed
self.state whenever it was a numpy array); plants with custom reset
semantics (e.g. state derived from the engine) override this. Called by
the simulation factory's reset for every plant.
Source code in src/shinro/components.py
post_engine_step
¶
post_engine_step(engine: PhysicsEngine) -> None
Reconcile plant state with the engine after engine.step().
Called once per plant by the simulation factory after the shared engine advances the world. Plants that self-integrate their dynamics (e.g. a wheeled base with analytical kinematics under a sim-backed scenario) override this to write their state back into the engine — syncing a floating base, depositing visual joint deltas, etc. Plants whose dynamics are fully owned by the engine need not override.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine
|
The physics engine this plant is attached to. |
required |
Source code in src/shinro/components.py
dynamics
¶
Continuous-time dynamics \(\dot{x} = f(x, u)\).
Override in nonlinear plants to expose the dynamics function for linearization and for batched rollouts. Returns None by default (linear plants need not override this).
The override must accept a single state (n_x,) or a batch
(N, n_x) and return the derivative with the same rank — a single
state is a batch of one, so one implementation serves both. It must
also route every backend call through bk (which defaults to
self.bk), because tracing swaps only the traced component's
backend, never the plant's. That is what lets the same function back
the eager per-sample rollout, the finite-difference linearization, and
the lowered graph — there is no second, separately-maintained batched
formula to drift. See shinro.utils.batching for the rank
helpers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
Current state vector (n_x,) or batch (N, n_x). |
required |
control
|
Any
|
Control input (n_u,), batch (N, n_u), or scalar. |
required |
bk
|
Any | None
|
Backend to evaluate with. Defaults to |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
Time derivative with the rank of |
Any
|
implemented. |
Source code in src/shinro/components.py
measurement
¶
Measurement model \(h(x)\) (the plant's sensor), or None.
The nonlinear counterpart of a filter's measurement_fn: override in
a plant to expose its sensor, so a config/scenario-driven EKF/UKF
derives h from the plant instead of a hand-wired callable — exactly
as dynamics supplies \(f\). Return the measurement vector
(n_y,).
Contract, mirroring dynamics: route every backend call through
bk (defaults to self.bk) and accept a leading batch axis —
(n_x,) or (N, n_x) in, same rank out — because the EKF
differentiates h with a probe batch and the UKF pushes its sigma
batch through it. The default returns None (no sensor model); a
filter attached to such a plant keeps taking h from its config's
measurement_matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
State vector (n_x,) or batch (N, n_x). |
required |
bk
|
Any | None
|
Backend to evaluate with. Defaults to |
None
|
Returns:
| Type | Description |
|---|---|
Any | None
|
Measurement vector (n_y,) or batch (N, n_y), or |
Source code in src/shinro/components.py
control_matrix
¶
Control matrix \(g(x, u) = \partial f/\partial u\) at (state, control).
This is the input map a control law contracts against (SMC's
\(c^T g\)); for a control-affine plant \(\dot{x} = f(x) +
g(x)u\) it is the whole control dependence. Shape (n_x, n_u).
The default derives it from dynamics by central finite
differences — the same arithmetic linearize_plant returns as B
— so every plant with a dynamics gets a correct g with no extra
code. Override it to be exact (a closed form, u-independent for
force/torque plants) or cheaper; the default doubles as the test oracle
for such an override.
Contract, mirroring dynamics: route every backend call through
bk (defaults to self.bk), and keep a leading probe batch when
one arrives — the tracing backend probes with a (2*n_u, n_u) block
whose batch axis is shared with the state, so the state is broadcast
onto it rather than evaluated alone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
Current state vector (n_x,). |
required |
control
|
Any
|
Current control (n_u,) — the point |
required |
bk
|
Any | None
|
Backend to evaluate with. Defaults to |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The control matrix |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the plant defines no |
Source code in src/shinro/components.py
operating_control
¶
Control at the plant's operating point, or None for zero.
model_terms evaluates the control matrix \(g(x, u)\) at this
point. For a plant whose actuation is linear in u (force/torque
inputs) the default None is correct — g is u-independent, so
the drift convention f(x, 0) / g(x, 0) holds. A plant whose
actuation is nonlinear in u (e.g. rotor thrusts ~ w^2) has
g(x, 0) = 0 and must override this to return the equilibrium control
(e.g. hover rotor speeds), so the tangent model is taken about the real
operating point.
Returns:
| Type | Description |
|---|---|
Any | None
|
Operating control (n_u,), or |
Source code in src/shinro/components.py
model_terms
¶
Control-affine model pair (f_x, g_x) at the operating point.
Returns the first-order (tangent) model of dynamics about
operating_control, in the control-affine form the SMC law
assumes:
so at u = u_op it reproduces the true dynamics f(x, u_op). With
the default zero operating control this is exactly f(x, 0) and
g(x, 0) — the drift convention — so control-affine plants are
unaffected.
This is the single site the codegen (shinro.codegen.build._trace_plant_model)
and gate A evaluate, so the live and lowered models cannot drift apart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
State vector (n_x,). |
required |
bk
|
Any | None
|
Backend to evaluate with. Defaults to |
None
|
n_u
|
int | None
|
Control dimension — used to size the zero control when
|
None
|
Returns:
| Type | Description |
|---|---|
tuple[Any, Any]
|
Tuple |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the plant defines no |
Source code in src/shinro/components.py
StateEstimator¶
Bases: ConfigDriven, ABC
Abstract base class for state estimators.
A state estimator reconstructs the full system state from noisy measurements and known control inputs. Subclasses implement filters such as Kalman filters, observers, or complementary filters.
Usage
estimator = MyEstimator(...) state = estimator.estimate(measurement=y, control_input=u) estimator.reset()
estimate
abstractmethod
¶
Estimate the current state from a measurement and control input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
measurement
|
Any
|
Sensor measurement (e.g., encoder reading, IMU). |
required |
control_input
|
Any
|
Control input applied to the system. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The estimated state. |
Source code in src/shinro/components.py
measure
¶
Expected measurement y = h(x) for a true state.
The simulation synthesizes each sensor reading by driving the
ground-truth state through this model (then adding
[noise.measurement]), so the estimator and the simulated sensor stay
consistent. The default is the identity (the sensor observes the full
state); a filter with a measurement model overrides it — the linear
C for KF/Luenberger, or the nonlinear h (a config C or the
plant's shinro.components.Plant.measurement) for EKF/UKF.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
True state (n_x,). |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Expected measurement (n_y,). |
Source code in src/shinro/components.py
reset
¶
TrajectoryGenerator¶
Bases: ConfigDriven, ABC
Abstract base class for trajectory generators.
A trajectory generator produces a reference path (position, velocity, acceleration) from a start to an end configuration. Subclasses implement splines, minimum-jerk, or motion-primitive generators.
Usage
generator = MyTrajectoryGenerator(...) trajectory = generator.generate(start=start_pos, end=end_pos) generator.reset()
generate
abstractmethod
¶
Generate a trajectory from start to end position over a given duration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_position
|
Any
|
Initial configuration. |
required |
end_position
|
Any
|
Final target configuration. |
required |
duration
|
Any
|
Total time duration of the trajectory in seconds. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The generated trajectory (e.g., a sequence of waypoints or |
Any
|
a callable that returns setpoints at a given time). |