Python Modules
Python Modules is Shinro’s registry-based Python composition pattern for whole-body robot control — the concrete implementation of the Control Architecture described conceptually elsewhere in these docs. It is robot-agnostic by design: five abstract base classes, registry-based factories, and TOML config compose a control stack for any robot that implements a Plant. The current reference robot is LeKiwi, a holonomic-base mobile manipulator, used throughout the demos below — but nothing about the framework is LeKiwi-specific.
The code lives in shinro-python-modules.
Architecture overview
Five ABCs — Controller, Plant, StateEstimator, TrajectoryGenerator, PhysicsEngine — each with a small interface and one or more concrete implementations. Every concrete class self-registers with a decorator (@register_controller("LQR"), @register_plant("ArmRobot"), and so on), and a generic factory instantiates a class by name from a TOML config file. Adding a new controller, estimator, trajectory generator, or plant means writing the class, adding the decorator plus a from_config classmethod, and a TOML file — no factory code changes.
See Control Architecture for why this decomposition exists and how the five ABCs compose. This page covers running and extending the framework as it stands today.
Getting started
pip install numpy scipy osqp mujocoRun a demo:
# Terminal-only, no graphs or viewerpython -m demos.demo_simple
# Arm trajectory demo with live viewerpython -m demos.demo_arm_trajectory
# Base tracking with LQR + observerpython -m demos.demo_base_tracking
# Base tracking with MPC instead of LQRpython -m demos.demo_base_tracking --controller mpc
# Full pick-and-place sequencepython -m demos.demo_pick_and_placeUsage patterns
Programmatic (low-level)
Wire a plant to a physics engine and drive it directly:
from physics_engine import MuJoCoEnginefrom plants.armrobot import ArmRobot
engine = MuJoCoEngine("path/to/model.xml")arm = ArmRobot(num_dof=6, dt=0.02, ...)arm.physics_engine(engine)arm.step(np.array([0.05, 0.0, 0.0, 0.0, 0.0, 0.0]))TOML-driven (generic)
Compose a controller and trajectory from config, without touching robot-specific code:
from simulation import RobotSimfrom factories import ControllerFactory, TrajectoryFactory
sim = RobotSim("robot_config.toml")ctrl = ControllerFactory("configs/controllers/lqr_base.toml").create()schedule = TrajectoryFactory("configs/trajectories/base_straight.toml").create()
for step, target in enumerate(schedule): u = ctrl.compute(sim.base.get_state(), target) sim.base.step(u) sim.step()Auto-generating config from a robot’s MJCF model
python scripts/generate_robot_config.py lekiwi-sim/mjcf_lcmm_robot.xml > robot_config.tomlrobot_config.toml is generated from a robot’s MuJoCo XML model rather than hand-written — the intended path for pointing the framework at a new robot.
Component catalog
Controllers
| Controller | Registered as | Class | Plant | Use Case |
|---|---|---|---|---|
| PID | PID | PIDController | Arm (joint space) | Position servo — send joint angles directly |
| MPC (Δu) | MPC_DeltaU | MPC_LTI_DeltaU | Base (3D) | Trajectory optimization with Δu regularization |
| MPC (base) | MPC_LTI | MPC_LTI_Base | Base (3D) | Standard linear time-invariant MPC |
| LQR | LQR | LQR | Base (3D) | Regulation / stabilization |
State estimators
| Estimator | Use Case |
|---|---|
| KalmanFilter | Optimal state estimation with process/measurement noise |
| LuenbergerObserver | Deterministic state estimation with user-specified gain |
Trajectory generators
| Generator | Continuity | Config type |
|---|---|---|
| CubicPolynomial | Position + velocity | cubic_segments |
| QuinticPolynomial | Position + velocity + acceleration | quintic_segments |
| WaypointSchedule | Piecewise constant | waypoints |
| PhaseSchedule | Multi-signal (arm + base + jaw) | phase_list |
Extending the framework
Adding support for a new robot means implementing a new Plant — the controllers, estimators, trajectory generators, and factories are already robot-agnostic and require no changes:
@register_plant("MyRobot")class MyRobot(Plant): @classmethod def from_config(cls, config): return cls(...)Adding a new controller:
@register_controller("MyType")class MyController(Controller): @classmethod def from_config(cls, config): return cls(...)Adding a new physics engine (an alternative to MuJoCo):
from components import PhysicsEngine
class MyEngine(PhysicsEngine): def get_joint_qpos(self, name): ... def set_joint_ctrl(self, name, value): ... # ... implement all abstract methodsPlants written against the PhysicsEngine protocol accept a new engine automatically — no plant code changes.
Reference robot: LeKiwi
LeKiwi — a 3-DOF holonomic mobile base carrying a 6-DOF arm — is the robot the demos above target today. Its Plant implementations (HolonomicMobileRobot, ArmRobot) are concrete instances of the same ABCs any other robot would implement; nothing in the framework depends on LeKiwi specifically.
The arm’s step() method takes a Cartesian velocity twist [dx, dy, dz, droll, dpitch, dyaw], integrates it into a target pose, runs inverse kinematics internally, and sends joint angles to the simulated or real servos — the controller never touches joint space. This is the “nonlinearity encapsulated in the Plant” pattern from Control Architecture applied concretely.
Known simulation limitations: the MuJoCo model welds the base in place and tracks the arm’s kinematic state separately from the base’s, rather than simulating both as one articulated body — a simplification of the demo environment, not a framework constraint.
Status
This framework runs today, standalone, outside Shinro Studio — install it and run the demos above with no Shinro-specific tooling. Wiring it into Shinro Studio (Simulator mode, module composition) is on our roadmap; nothing on this page describes Studio integration as already working.