Architecture & Design Philosophy¶
OpenSMC is built on a modular, object-oriented architecture that mirrors the mathematical structure of Sliding Mode Control. This design philosophy enables seamless experimentation and allows researchers to swap out individual components without rewriting the entire control loop.
The Modular Components¶
At the heart of OpenSMC are five primary base classes defined in opensmc.core:
SlidingSurface: Defines the sliding manifold \(s(e, t) = 0\). Examples include Linear, Terminal, and Integral surfaces.ReachingLaw: Dictates the dynamics of the sliding variable \(s\) during the reaching phase, typically aiming for \(\dot{s} = f(s)\).Plant: The mathematical model of the system to be controlled, representing its state-space dynamics \(\dot{x} = f(x, u, d)\).Controller: The higher-level logic that uses the Surface and Reaching Law to compute the final control input \(u\).Estimator: Optional components like observers or differentiators that provide estimates of unmeasured states or disturbances.
Simulator Loop¶
The Simulator class manages the interaction between these components. A typical simulation step follows this flow:
┌──────────────────────────────────────────────────────────┐
│ SIMULATION STEP (RK4) │
├──────────────────────────────────────────────────────────┤
│ │
│ 1. Get Current State (x) from Plant │
│ │ │
│ 2. Pass State to Estimator (Optional) │
│ │ └─> Get State/Disturbance Estimates (x̂) │
│ │ │
│ 3. Calculate Error (e = x_ref - x̂) │
│ │ │
│ 4. Pass Error to Controller │
│ │ │
│ ├─> Surface calculates s(e) │
│ ├─> Reaching Law calculates required ṡ │
│ └─> Controller computes Control Input (u) │
│ │ │
│ 5. Inject Control Input (u) into Plant │
│ │ │
│ 6. Plant updates state via ODE solver │
│ │
└──────────────────────────────────────────────────────────┘
Composing Custom Controllers¶
To create a custom SMC variant, you can inherit from the base classes in opensmc.core. For example, a custom surface would only need to implement the calculate_surface and calculate_derivative methods:
from opensmc.core import SlidingSurface
class MyCustomSurface(SlidingSurface):
def __init__(self, my_param):
self.my_param = my_param
def calculate_surface(self, error):
# Implementation of s(e)
pass
def calculate_derivative(self, error):
# Implementation of partial s / partial e
pass
This modularity ensures that any new surface you create is immediately compatible with all existing reaching laws and controllers.