Plants¶
shinro.plants
Robot plant models for simulation and control.
Provides concrete plant implementations that wrap robot kinematics and optionally attach a MuJoCo physics engine for mesh-accurate simulation.
Available plants: ArmRobot — 6-DOF serial-link arm with FK, Jacobian, IK HolonomicMobileRobot — N-wheel holonomic base with omni-wheel kinematics InvertedPendulum — 2D inverted pendulum with analytical dynamics CartPole — 4D cart-pole with coupled dynamics DoublePendulum — 4D planar double pendulum with analytical dynamics Quadrotor — 12D quadrotor (placeholder)
ArmRobot¶
Bases: Plant
6-DOF robotic arm plant with forward kinematics, Jacobian, and IK.
Models a serial-link manipulator with configurable joint offsets and rotation axes. Supports two modes:
- Standalone — uses simplified FK/IK for quick testing.
- Physics engine — attaches a PhysicsEngine for mesh-accurate Jacobian IK via MuJoCo.
The arm operates in Cartesian space: step() takes a 6D velocity
twist \([dx, dy, dz, d\text{roll}, d\text{pitch}, d\text{yaw}]\),
integrates to a target pose, and uses inverse kinematics to compute
joint angles. The controller never touches joint space.
Forward kinematics uses homogeneous transforms (4x4) chained per joint. The Jacobian is computed via the geometric method (cross product of joint axes with position vectors). Inverse kinematics uses damped pseudoinverse with step clamping and joint limit enforcement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_dof
|
int
|
Number of degrees of freedom. |
required |
dt
|
float
|
Time step in seconds. |
required |
joint_limits
|
Array of shape (num_dof, 2) with [min, max] per joint. |
required | |
joint_offsets
|
Array of shape (num_dof, 3) with link offset vectors. |
required | |
rot_axes
|
list[str]
|
List of rotation axes per joint ( |
required |
joint_names
|
list[str] | None
|
Optional list of joint name strings. |
None
|
ee_body_name
|
str | None
|
Optional name of the end-effector body in the physics engine. |
None
|
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Source code in src/shinro/plants/armrobot.py
physics_engine
¶
physics_engine(engine: PhysicsEngine | None)
Attach a physics engine.
When attached, the backend is inherited from the engine, the end-effector body name is auto-detected, and the initial state is read from the engine.
When detached, the standalone FK is used for state initialization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine | None
|
PhysicsEngine instance or None to detach. |
required |
Source code in src/shinro/plants/armrobot.py
get_model
¶
Get the discrete-time state-space model.
Returns:
| Type | Description |
|---|---|
|
Tuple of (A, B) where A = I_6 and B = dt * I_6. |
step
¶
Execute one control step.
When a physics engine is attached:
- Integrates the Cartesian velocity twist to get a target EE pose
(position + orientation, ZYX Euler rates — same math as standalone).
- Runs engine_ik() to compute joint angles.
- Sends joint angles to the engine via set_joint_ctrl.
When standalone:
- Integrates the state directly.
- Runs the standalone inverse_kinematics().
- Clips joint angles to limits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u
|
Control input (6,) — [dx, dy, dz, droll, dpitch, dyaw]. |
required |
Returns:
| Type | Description |
|---|---|
|
Joint angle vector (num_dof,). |
Source code in src/shinro/plants/armrobot.py
forward_kinematics
¶
Compute forward kinematics for a given joint configuration.
Chains the per-joint homogeneous transforms and returns the end-effector transform, joint positions, and joint axes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
joint_angles
|
Joint angle vector (num_dof,). |
required |
Returns:
| Type | Description |
|---|---|
|
Tuple of (T_ee, positions, axes): |
|
|
|
|
|
|
Source code in src/shinro/plants/armrobot.py
inverse_kinematics
¶
inverse_kinematics(target_pose, max_iters: int = 100, q_init=None, tol: float = 0.0001, max_step: float = 0.2)
Compute inverse kinematics via damped pseudoinverse.
Iteratively minimizes the pose error (position + orientation) using the geometric Jacobian. The orientation error is computed from the rotation matrix trace.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_pose
|
Target homogeneous transform (4, 4). |
required | |
max_iters
|
int
|
Maximum number of IK iterations. |
100
|
q_init
|
Initial joint angle guess. Defaults to last joint angles. |
None
|
|
tol
|
float
|
Convergence tolerance on position and orientation error. |
0.0001
|
max_step
|
float
|
Maximum joint angle change per iteration. |
0.2
|
Returns:
| Type | Description |
|---|---|
|
Joint angle vector (num_dof,) that achieves the target pose. |
Source code in src/shinro/plants/armrobot.py
engine_ik
¶
engine_ik(target_ee, target_euler=None, max_iters: int = 20, lam: float = 0.01, max_dq: float = 0.5)
Compute inverse kinematics using the physics engine's Jacobian.
Uses the engine's mesh-accurate Jacobian with damped least squares.
With target_euler given, solves the full 6D pose error (position +
orientation, world-frame log map); without it, solves position only
(the historical 3D path, byte-identical to previous behavior). Sets
joint positions in the engine and calls forward() each iteration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_ee
|
Target end-effector position (3,). |
required | |
target_euler
|
Optional target orientation as ZYX Euler [roll, pitch, yaw] (3,). When given, the 6D pose error is minimized. |
None
|
|
max_iters
|
int
|
Maximum IK iterations. |
20
|
lam
|
float
|
Damping factor for the least squares solve. |
0.01
|
max_dq
|
float
|
Maximum joint angle change per iteration. |
0.5
|
Returns:
| Type | Description |
|---|---|
|
Joint angle vector (num_dof,) that achieves the target pose. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no physics engine is attached. |
Source code in src/shinro/plants/armrobot.py
from_config
classmethod
¶
from_config(config, backend: ArrayBackend | None = None)
Create an ArmRobot from a TOML config dict or ArmRobotConfig.
Config fields
joint_group: Name of the joint group in joint_groups.
num_dof: Number of degrees of freedom.
dt: Time step.
joint_offsets: List of link offset vectors.
rot_axes: List of rotation axes.
ee_body_name: Optional end-effector body name.
Requires a runtime-injected engine (RobotSim merges it into the
config dict); standalone use without an engine is not supported.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TOML config dict (with runtime-injected |
required | |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Returns:
| Type | Description |
|---|---|
|
ArmRobot instance. |
Source code in src/shinro/plants/armrobot.py
HolonomicMobileRobot¶
Bases: Plant
Holonomic mobile robot with omni-wheel kinematics.
Models a robot with N wheels arranged symmetrically around a center. Maps world-frame velocity commands \([v_x, v_y, \omega]\) to individual wheel speeds via the kinematic matrix \(A_{\text{kin}}\).
The forward kinematics matrix \(A_{\text{kin}}\) maps body-frame velocities to wheel speeds:
where each row of \(A_{\text{kin}}\) is \([\sin(\theta_i), -\cos(\theta_i), -R]\) for wheel i at angle \(\theta_i\).
When a MuJoCo engine is attached, step() also stores wheel rotation
deltas for visual rolling in the physics simulation.
The kinematics matrix is built once in __init__ using numpy (scalar
trig from Python floats), then converted to the backend via
bk.from_numpy. All per-step operations use bk.xxx.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_wheels
|
int
|
Number of wheels (e.g., 3 for omni, 4 for mecanum). |
required |
radius_robots
|
float
|
Distance from robot center to each wheel (m). |
required |
gamma
|
float
|
Angle of the first wheel relative to the robot base (rad). |
required |
radius_wheels
|
float
|
Radius of each wheel (m). |
required |
dt
|
float
|
Simulation time step (s). |
required |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Source code in src/shinro/plants/holonomicmobilerobot.py
physics_engine
¶
Attach a physics engine.
After attachment, step() uses the engine for visual wheel rolling.
The backend is inherited from the engine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine instance or None to detach. |
required |
Source code in src/shinro/plants/holonomicmobilerobot.py
step
¶
Update robot state and compute wheel speeds.
Transforms the world-frame velocity command to body frame, computes wheel speeds via the kinematic matrix, and integrates the state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u_world
|
Desired velocity \([v_x, v_y, \omega]\) in world frame (3,). |
required |
Returns:
| Type | Description |
|---|---|
|
Wheel speed vector (n_wheels,). |
Source code in src/shinro/plants/holonomicmobilerobot.py
post_engine_step
¶
Reconcile the engine's free-joint base with the plant's integrated pose.
The plant self-integrates pose analytically; the MuJoCo base exists for
physics/rendering only. After engine.step() this writes (x, y) into
the free joint, pins the base quaternion upright and zeros the
floating-base velocities — the chassis follows the plant's kinematics,
not contact dynamics — then deposits the pending wheel-rotation deltas
onto the drive joints for visual rolling (consumed once per step).
No-op for engines without a free joint (e.g. a statically mounted fixture): plant state remains the source of truth either way.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
The physics engine stepping the world. |
required |
Source code in src/shinro/plants/holonomicmobilerobot.py
set_pose
¶
Set the robot's pose directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float
|
X position (m). |
required |
y
|
float
|
Y position (m). |
required |
theta
|
float
|
Orientation (rad). |
required |
get_state
¶
Return the current pose \([x, y, \theta]\).
Returns:
| Type | Description |
|---|---|
|
State vector (3,) — [x, y, theta]. |
get_model
¶
Get the discrete-time state-space model.
Returns:
| Type | Description |
|---|---|
|
Tuple of (A, B) where A = I_3 and B = dt * I_3. |
from_config
classmethod
¶
from_config(config, backend: ArrayBackend | None = None)
Create a HolonomicMobileRobot from a TOML config dict or HolonomicMobileRobotConfig.
Config fields
num_wheels: Number of wheels. radius_robots: Distance from center to each wheel (m). gamma: First wheel angle offset (rad). radius_wheels: Wheel radius (m). dt: Time step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TOML config dict (may carry a runtime-injected |
required | |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Returns:
| Type | Description |
|---|---|
|
HolonomicMobileRobot instance. |
Source code in src/shinro/plants/holonomicmobilerobot.py
InvertedPendulum¶
Bases: Plant
2D inverted pendulum with standalone analytical dynamics and optional MuJoCo engine.
Models a simple pendulum with a point mass at the end of a massless rod, hinged at the origin. The state is the angle from upright \([\theta, \dot{\theta}]\) and the control is torque at the pivot \([\tau]\).
Supports two modes:
- Standalone — integrates the analytical dynamics using semi-implicit Euler (velocity updated before position).
- Physics engine — attaches a MuJoCo engine for mesh-accurate simulation.
The continuous-time dynamics are:
The linearized model is computed about an operating point supplied to
get_model, defaulting to the upright equilibrium
\((\theta=0, \dot{\theta}=0)\) where the continuous-time Jacobians
are:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass
|
float
|
Mass of the pendulum bob (kg). |
0.1
|
length
|
float
|
Length of the pendulum rod (m). |
0.5
|
damping
|
float
|
Linear damping coefficient at the pivot (Nms/rad). |
0.0
|
gravity
|
float
|
Gravitational acceleration (m/s^2). |
9.81
|
dt
|
float
|
Time step in seconds. |
0.01
|
state_bounds
|
tuple | None
|
Optional (min, max) bounds for state clipping. Each is an array of shape (2,). |
None
|
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Source code in src/shinro/plants/inverted_pendulum.py
physics_engine
¶
physics_engine(engine: PhysicsEngine | None)
Attach or detach a physics engine.
When attached, the backend is inherited from the engine and the state is reset to zeros. When detached, the backend reverts to NumpyBackend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine | None
|
PhysicsEngine instance or None to detach. |
required |
Source code in src/shinro/plants/inverted_pendulum.py
get_state
¶
Get the current state \([\theta, \dot{\theta}]\).
When a physics engine is attached, reads joint position and velocity from the engine. Otherwise returns a copy of the internal state.
Returns:
| Type | Description |
|---|---|
|
State vector (2,) — [theta, theta_dot]. |
Source code in src/shinro/plants/inverted_pendulum.py
get_model
¶
Get the discrete-time state-space model around an operating point.
Linearizes the continuous-time dynamics \(f(x, u) = \dot{x}\)
around (x0, u0) using central finite differences via
shinro.utils.linearization.linearize_plant, then
Euler-discretizes at the plant's dt. When x0/u0 are
omitted, defaults to the upright equilibrium
\((\theta=0, \dot{\theta}=0)\) with zero control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
Operating point state (2,) — [theta, theta_dot]. Defaults to zeros (upright equilibrium). |
None
|
|
u0
|
Operating point control (1,) — [tau]. Defaults to zeros. |
None
|
|
eps
|
Step size for finite differences. |
1e-06
|
Returns:
| Type | Description |
|---|---|
|
Tuple of (A, B) where A = I + dt·∂f/∂x is (2, 2) and |
|
|
B = dt·∂f/∂u is (2, 1). |
Source code in src/shinro/plants/inverted_pendulum.py
dynamics
¶
Continuous-time dynamics \(\dot{x} = f(x, u)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State vector (2,) — [theta, theta_dot]. |
required | |
control
|
Control vector (1,) or scalar — [tau]. |
required |
Returns:
| Type | Description |
|---|---|
|
Time derivative of the state (2,) — [theta_dot, theta_ddot]. |
Source code in src/shinro/plants/inverted_pendulum.py
step
¶
Execute one control step.
When a physics engine is attached, sets the torque actuator and advances the engine. Otherwise integrates the analytical dynamics using semi-implicit Euler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u
|
Control input (1,) or scalar — torque at pivot (Nm). |
required |
Returns:
| Type | Description |
|---|---|
|
New state vector (2,) — [theta, theta_dot]. |
Source code in src/shinro/plants/inverted_pendulum.py
from_config
classmethod
¶
from_config(config, backend: ArrayBackend | None = None)
Create an InvertedPendulum from a TOML config dict or InvertedPendulumConfig.
Config fields
mass: Pendulum bob mass (kg).
length: Pendulum rod length (m).
damping: Linear damping coefficient.
gravity: Gravitational acceleration (m/s^2).
dt: Time step.
state_bounds: Optional dict with min and max lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TOML config dict (may carry a runtime-injected |
required | |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Returns:
| Type | Description |
|---|---|
|
InvertedPendulum instance. |
Source code in src/shinro/plants/inverted_pendulum.py
CartPole¶
Bases: Plant
4D cart-pole system with standalone analytical dynamics and optional MuJoCo engine.
Models a cart on a frictionless track with a pole hinged on top. The state is \([x, \dot{x}, \theta, \dot{\theta}]\) (cart position, cart velocity, pole angle from upright, pole angular velocity) and the control is horizontal force on the cart \([F]\).
Supports two modes:
- Standalone — integrates the coupled analytical dynamics using semi-implicit Euler (velocities updated before positions).
- Physics engine — attaches a MuJoCo engine for mesh-accurate simulation.
The equations of motion for the coupled system are:
The linearized model is computed about an operating point supplied to
get_model, defaulting to the upright equilibrium
\((x=0, \dot{x}=0, \theta=0, \dot{\theta}=0)\) where the
continuous-time Jacobians are:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cart_mass
|
float
|
Mass of the cart (kg). |
0.5
|
pole_mass
|
float
|
Mass of the pole (kg). |
0.1
|
pole_length
|
float
|
Length of the pole (m). |
0.5
|
damping
|
float
|
Linear damping coefficient at the pole hinge (Nms/rad). |
0.0
|
gravity
|
float
|
Gravitational acceleration (m/s^2). |
9.81
|
dt
|
float
|
Time step in seconds. |
0.01
|
track_limits
|
tuple | None
|
Optional (min, max) bounds for cart position (m). |
None
|
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Source code in src/shinro/plants/cartpole.py
physics_engine
¶
physics_engine(engine: PhysicsEngine | None)
Attach or detach a physics engine.
When attached, the backend is inherited from the engine and the state is reset to zeros. When detached, the backend reverts to NumpyBackend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine | None
|
PhysicsEngine instance or None to detach. |
required |
Source code in src/shinro/plants/cartpole.py
get_state
¶
Get the current state \([x, \dot{x}, \theta, \dot{\theta}]\).
When a physics engine is attached, reads joint positions and velocities from the engine. Otherwise returns a copy of the internal state.
Returns:
| Type | Description |
|---|---|
|
State vector (4,) — [x, x_dot, theta, theta_dot]. |
Source code in src/shinro/plants/cartpole.py
get_model
¶
Get the discrete-time state-space model around an operating point.
Linearizes the continuous-time dynamics \(f(x, u) = \dot{x}\)
around (x0, u0) using central finite differences via
shinro.utils.linearization.linearize_plant, then
Euler-discretizes at the plant's dt. When x0/u0 are
omitted, defaults to the upright equilibrium
\((x=0, \dot{x}=0, \theta=0, \dot{\theta}=0)\) with zero
control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
Operating point state (4,) — [x, x_dot, theta, theta_dot]. Defaults to zeros (upright equilibrium). |
None
|
|
u0
|
Operating point control (1,) — [F]. Defaults to zeros. |
None
|
|
eps
|
Step size for finite differences. |
1e-06
|
Returns:
| Type | Description |
|---|---|
|
Tuple of (A, B) where A = I + dt·∂f/∂x is (4, 4) and |
|
|
B = dt·∂f/∂u is (4, 1). |
Source code in src/shinro/plants/cartpole.py
dynamics
¶
Continuous-time dynamics \(\dot{x} = f(x, u)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State vector (4,) — [x, x_dot, theta, theta_dot]. |
required | |
control
|
Control vector (1,) or scalar — [F]. |
required |
Returns:
| Type | Description |
|---|---|
|
Time derivative of the state (4,) — [x_dot, x_ddot, theta_dot, theta_ddot]. |
Source code in src/shinro/plants/cartpole.py
step
¶
Execute one control step.
When a physics engine is attached, sets the cart force actuator and advances the engine. Otherwise integrates the coupled analytical dynamics using semi-implicit Euler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u
|
Control input (1,) or scalar — horizontal force on cart (N). |
required |
Returns:
| Type | Description |
|---|---|
|
New state vector (4,) — [x, x_dot, theta, theta_dot]. |
Source code in src/shinro/plants/cartpole.py
from_config
classmethod
¶
from_config(config, backend: ArrayBackend | None = None)
Create a CartPole from a TOML config dict or CartPoleConfig.
Config fields
cart_mass: Mass of the cart (kg). pole_mass: Mass of the pole (kg). pole_length: Length of the pole (m). damping: Linear damping coefficient. gravity: Gravitational acceleration (m/s^2). dt: Time step. track_limits: Optional list of [min, max] for cart position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TOML config dict (may carry a runtime-injected |
required | |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Returns:
| Type | Description |
|---|---|
|
CartPole instance. |
Source code in src/shinro/plants/cartpole.py
DoublePendulum¶
Bases: Plant
4D planar double pendulum with standalone analytical dynamics and optional MuJoCo engine.
Models two point masses at the ends of two massless rods, hinged in series. The state is \([\theta_1, \theta_2, \omega_1, \omega_2]\) (angle of each rod from the downward vertical and its angular velocity) and the control is joint torques \([\tau_1, \tau_2]\) applied at each hinge. The rest equilibrium \(\theta=0\) is stable (hanging down).
Supports two modes:
- Standalone — integrates the analytical dynamics using semi-implicit Euler (velocities updated before positions).
- Physics engine — attaches a MuJoCo engine for mesh-accurate
simulation (expects an MJCF model with
hinge_1/hinge_2joints andtorque_1/torque_2motor actuators).
The equations of motion are the standard double pendulum manipulator form:
where \(M\) is the mass matrix, \(C\) the Coriolis matrix, and \(G\) the gravity vector. The angular acceleration follows from \(\ddot{\theta} = M^{-1}(\tau - C\dot{\theta} - G)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass_top
|
float
|
Mass of the top pendulum bob (kg). |
0.1
|
mass_bottom
|
float
|
Mass of the bottom pendulum bob (kg). |
0.1
|
length_top
|
float
|
Length of the top rod (m). |
0.5
|
length_bottom
|
float
|
Length of the bottom rod (m). |
0.5
|
dt
|
float
|
Time step in seconds. |
0.01
|
g
|
float
|
Gravitational acceleration (m/s^2). |
9.81
|
state_bounds
|
tuple | None
|
Optional (min, max) bounds for state clipping. Each is an array of shape (4,). |
None
|
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Source code in src/shinro/plants/double_pendulum.py
physics_engine
¶
physics_engine(engine: PhysicsEngine | None)
Attach or detach a physics engine.
When attached, the backend is inherited from the engine and the state is reset to zeros. When detached, the backend reverts to NumpyBackend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
PhysicsEngine | None
|
PhysicsEngine instance or None to detach. |
required |
Source code in src/shinro/plants/double_pendulum.py
dynamics
¶
Continuous-time dynamics \(\dot{x} = f(x, u)\).
State ordering is \([\theta_1, \theta_2, \omega_1, \omega_2]\) and control is \([\tau_1, \tau_2]\). The angular acceleration is solved from the manipulator equation \(\ddot{\theta} = M^{-1}(\tau - C\dot{\theta} - G)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State vector (4,) — [theta_1, theta_2, omega_1, omega_2]. |
required | |
control
|
Control vector (2,) or scalar — [tau_1, tau_2]. |
required |
Returns:
| Type | Description |
|---|---|
|
Time derivative of the state (4,) — |
|
|
[omega_1, omega_2, theta_1_ddot, theta_2_ddot]. |
Source code in src/shinro/plants/double_pendulum.py
get_model
¶
Get the discrete-time state-space model around an operating point.
Linearizes the continuous-time dynamics \(f(x, u) = \dot{x}\)
around (x0, u0) using central finite differences via
shinro.utils.linearization.linearize_plant, then
Euler-discretizes at the plant's dt. When x0/u0 are
omitted, defaults to the rest equilibrium
\((\theta=0, \dot{\theta}=0)\) with zero control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
Operating point state (4,) — [theta_1, theta_2, omega_1, omega_2]. Defaults to zeros. |
None
|
|
u0
|
Operating point control (2,) — [tau_1, tau_2]. Defaults to zeros. |
None
|
|
eps
|
Step size for finite differences. |
1e-06
|
Returns:
| Type | Description |
|---|---|
|
Tuple of (A, B) where A = I + dt·∂f/∂x is (4, 4) and |
|
|
B = dt·∂f/∂u is (4, 2). |
Source code in src/shinro/plants/double_pendulum.py
get_state
¶
Get the current state \([\theta_1, \theta_2, \omega_1, \omega_2]\).
When a physics engine is attached, reads joint positions and velocities from the engine. Otherwise returns a copy of the internal state.
Returns:
| Type | Description |
|---|---|
|
State vector (4,) — [theta_1, theta_2, omega_1, omega_2]. |
Source code in src/shinro/plants/double_pendulum.py
step
¶
Execute one control step.
When a physics engine is attached, sets the torque actuators and advances the engine. Otherwise integrates the analytical dynamics using semi-implicit Euler (velocities updated before positions).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u
|
Control input (2,) or scalar — [tau_1, tau_2] joint torques (Nm). |
required |
Returns:
| Type | Description |
|---|---|
|
New state vector (4,) — [theta_1, theta_2, omega_1, omega_2]. |
Source code in src/shinro/plants/double_pendulum.py
from_config
classmethod
¶
from_config(config, backend: ArrayBackend | None = None)
Create a DoublePendulum from a TOML config dict or DoublePendulumConfig.
Config fields
mass_top: Top bob mass (kg).
mass_bottom: Bottom bob mass (kg).
length_top: Top rod length (m).
length_bottom: Bottom rod length (m).
dt: Time step.
g: Gravitational acceleration (m/s^2).
state_bounds: Optional dict with min and max lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TOML config dict (may carry a runtime-injected |
required | |
backend
|
ArrayBackend | None
|
Array backend. Defaults to NumpyBackend. |
None
|
Returns:
| Type | Description |
|---|---|
|
DoublePendulum instance. |
Source code in src/shinro/plants/double_pendulum.py
Quadrotor¶
Bases: Plant
Quadrotor — follows HolonomicMobileRobot pattern.
State: 12D (pose + twist) Control: 4D (thrust + body torques) — higher-level abstraction TBD
TODO: implement standalone dynamics + MuJoCo engine mode