Sliding Surfaces¶
Sliding surfaces define the manifold in the error state-space where the system should ideally reside. OpenSMC provides 11 different sliding surface implementations, ranging from classical linear surfaces to advanced predefined-time and hierarchical manifolds.
Usage Example¶
from opensmc.surfaces import NonsingularTerminalSurface
# Create a nonsingular terminal sliding surface
surface = NonsingularTerminalSurface(alpha=2.0, beta=1.5, p=5, q=3)
# Compute surface value for a given error vector
s = surface.compute(error=[0.5, 0.1])
Available Surfaces¶
surfaces
¶
OpenSMC Surfaces — 11 sliding surface implementations + RL-discovered.
Classes¶
FastTerminalSurface
¶
Bases: SlidingSurface
Fast terminal sliding surface (Liu & Wang, 2012).
.. math:: s = \dot{e} + \alpha\,e + \beta\,|e|^{q/p}\,\mathrm{sign}(e)
Combines a linear term (alpha * e) for global convergence with a terminal term (beta * |e|^{q/p} * sign(e)) for finite-time convergence near the origin. This ensures global finite-time stability.
.. note:: The exponent here is q/p > 1 (not p/q < 1 as in the standard terminal surface), following Liu & Wang (2012) Ch 7.3.
Parameters¶
alpha : float Linear gain (positive). beta : float Terminal gain (positive). p : int Numerator (odd positive integer, p < q). q : int Denominator (odd positive integer).
Source code in opensmc/surfaces/terminal.py
GlobalSurface
¶
Bases: SlidingSurface
Global sliding surface with exponential correction.
.. math:: s(t) = \dot{e} + c\,e - \bigl(\dot{e}(0) + c\,e(0)\bigr)\,\exp(-\alpha\,t)
Key property: s(0) = 0 by construction, regardless of initial conditions. The exponential correction decays with rate alpha, and as t -> infinity the surface converges to the standard linear surface s = edot + c * e.
The transition time from global to linear behavior is approximately:
.. math:: t_\epsilon = -\frac{\ln(\epsilon / |s_0|)}{\alpha}
Parameters¶
c : float Base surface slope (positive). alpha : float Exponential decay rate for the correction term (positive). Larger alpha means faster transition to the standard surface.
Source code in opensmc/surfaces/global_surface.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute the global sliding variable.
On first call, stores e(0) and edot(0) to compute the correction term s_0 = edot(0) + c * e(0).
Parameters¶
e : float Tracking error. edot : float Error derivative. t : float Current time (required for the exponential decay).
Returns¶
s : float Sliding variable (exactly zero at t = 0).
Source code in opensmc/surfaces/global_surface.py
HierarchicalSurface
¶
Bases: SlidingSurface
Hierarchical sliding surface for multi-DOF underactuated systems.
.. math:: s_1 &= \dot{e}_a + c_1\,e_a \qquad \text{(actuated subsystem)} \ s_2 &= \dot{e}_u + c_2\,e_u \qquad \text{(unactuated subsystem)} \ S &= s_1 + \lambda\,s_2 \qquad \text{(hierarchical combination)}
A single control input stabilizes both subsystems through the dynamic coupling. The weight lambda controls the relative priority:
- lambda = 0: only the actuated DOF is controlled.
- lambda >> 1: the controller prioritizes the unactuated DOF.
Parameters¶
c1 : float Actuated subsystem surface slope (positive). c2 : float Unactuated subsystem surface slope (positive). lam : float Coupling weight (non-negative).
Source code in opensmc/surfaces/hierarchical.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute the hierarchical sliding variable S = s1 + lambda * s2.
For underactuated systems, the errors must be provided as keyword arguments:
Parameters¶
e : float
Actuated subsystem error (e_a). Ignored if e_a is in kwargs.
edot : float
Actuated subsystem error derivative. Ignored if edot_a is in kwargs.
e_a : float, optional
Actuated subsystem error (alternative to positional e).
edot_a : float, optional
Actuated error derivative (alternative to positional edot).
e_u : float
Unactuated subsystem error (required in kwargs).
edot_u : float
Unactuated error derivative (required in kwargs).
Returns¶
S : float Hierarchical sliding variable.
Source code in opensmc/surfaces/hierarchical.py
IntegralSlidingSurface
¶
Bases: SlidingSurface
Integral sliding surface (Utkin, 1996).
Utkin formulation:
.. math:: s(t) = G\bigl[x(t) - x(0)\bigr] - \int_0^t G\bigl(A\,x(\tau) + B\,u_{\mathrm{nom}}(\tau) \bigr)\,d\tau
Simplified scalar formulation (used here):
.. math:: s = c\,(e - e_0) - z, \quad \dot{z} = c\,\dot{e}_{\mathrm{nom}}
where e_0 is the initial error and z accumulates the nominal dynamics.
Key property: s(0) = 0 by construction -- no reaching phase.
For the common case of a second-order plant with nominal feedback u_nom = -K*x, the integral compensator evolves as:
.. math:: s = C \cdot (x_{\mathrm{err}} - E), \quad \dot{E} = (A - BK)\,x_{\mathrm{err}}, \quad E(0) = 0
Parameters¶
c : float Surface slope (positive). dt : float Integration time step for accumulating the integral.
Source code in opensmc/surfaces/integral_sliding.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute the integral sliding variable.
On first call, stores e(0) and edot(0) so that s(0) = 0. Subsequent calls accumulate the nominal dynamics integral.
.. math:: s(t) = (\dot{e} + c\,e) - (\dot{e}_0 + c\,e_0)\, \exp(-\alpha\,t)
This implementation uses the Utkin approach: s(0) = 0 exactly.
Parameters¶
e : float Tracking error. edot : float Error derivative. t : float Current time.
Returns¶
s : float Sliding variable (zero at t = 0).
Source code in opensmc/surfaces/integral_sliding.py
IntegralTerminalSurface
¶
Bases: SlidingSurface
Integral terminal sliding surface.
.. math:: s = \dot{e} + c_1\,e + c_2 \int_0^t |e(\tau)|^{p/q} \,\mathrm{sign}(e(\tau))\,d\tau
Combines three mechanisms: - Linear term (c1 * e): fast convergence far from origin. - Terminal integral term: finite-time convergence via fractional power p/q < 1. - Integral action: eliminates steady-state error under constant disturbance.
Parameters¶
c1 : float Linear gain (positive). c2 : float Integral terminal gain (positive). p : int Numerator of fractional exponent (odd positive integer, p < q). q : int Denominator of fractional exponent (odd positive integer). dt : float Integration time step for accumulating the integral term.
Source code in opensmc/surfaces/integral_terminal.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute s = edot + c1e + c2integral(|e|^{p/q}*sign(e) dt).
The integral is accumulated internally using Euler integration at each call.
Source code in opensmc/surfaces/integral_terminal.py
LinearSurface
¶
Bases: SlidingSurface
Classical linear sliding surface.
.. math:: s = \dot{e} + c \, e
Parameters¶
c : float Surface slope (must be positive). Controls the convergence rate on the sliding manifold: e(t) = e(t_r) * exp(-c*(t - t_r)).
Source code in opensmc/surfaces/linear.py
NonlinearDampingSurface
¶
Bases: SlidingSurface
Nonlinear damping sliding surface.
.. math:: s = \dot{e} + \bigl(c + \Psi(y)\bigr)\,e
The nonlinear function Psi(y) adapts the effective slope c_eff = c + Psi(y) based on the output y, reducing overshoot compared to a fixed linear surface.
Two Psi types are supported:
Gaussian (Eq. 2.8):
.. math:: \Psi(y) = -\beta\,\exp(-\tilde{k}\,y^2)
- At y = 0 (far from setpoint): Psi = -beta, c_eff = c - beta (gentle start).
- At y = y_ref: Psi ~ 0, c_eff ~ c (full slope, fast final convergence).
Exponential (Eq. 2.7, adapted for tracking):
.. math:: \Psi(y) = \frac{-\beta}{1 - e^{-1}} \left(\exp!\bigl(-(1 - r^2)\bigr) - e^{-1}\right), \quad r = y / y_{\mathrm{ref}}
- At y = 0: Psi ~ 0, c_eff = c (fast approach).
- At y = y_ref: Psi = -beta, c_eff = c - beta (gentle arrival).
Parameters¶
c : float
Base surface slope (positive, must satisfy c > beta for c_eff > 0).
beta : float
Damping amplitude (positive, beta < c).
psi_type : str
Either 'gaussian' or 'exponential'.
k_tilde : float
Width parameter for Gaussian Psi (positive).
y_ref : float
Reference setpoint for exponential Psi.
Source code in opensmc/surfaces/nonlinear_damping.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute s = edot + (c + Psi(y)) * e.
Parameters¶
e : float Tracking error. edot : float Error derivative. y : float, optional Current output value for computing Psi. If not provided, defaults to 0.0 (which gives Psi at the initial condition).
Returns¶
s : float Sliding variable.
Source code in opensmc/surfaces/nonlinear_damping.py
NonsingularTerminalSurface
¶
Bases: SlidingSurface
Nonsingular terminal sliding surface (Yu & Zhihong, 2002).
.. math:: s = e + \frac{1}{\beta}\,|\dot{e}|^{q/p}\,\mathrm{sign}(\dot{e})
Avoids the singularity of the standard terminal surface by placing the fractional power on edot instead of e. Note q/p > 1 so the exponent is greater than one.
Parameters¶
beta : float Gain (positive). p : int Numerator (odd positive integer, p < q). q : int Denominator (odd positive integer).
Source code in opensmc/surfaces/terminal.py
PIDSurface
¶
Bases: SlidingSurface
PID-type sliding surface.
.. math:: s = \alpha\,\dot{e} + \beta\,e + \gamma \int_0^t e(\tau)\,d\tau
Three-term surface combining derivative, proportional, and integral action:
- alpha (derivative): provides damping.
- beta (proportional): controls convergence rate.
- gamma (integral): eliminates steady-state error under constant disturbance.
When gamma = 0, this reduces to the classical linear surface s = alpha * edot + beta * e.
Designed for second-order SMC where the controller computes udot (control derivative), making the actual control u continuous (no chattering).
Parameters¶
alpha : float Derivative coefficient (positive). beta : float Proportional coefficient (positive). gamma : float Integral coefficient (non-negative). Set to 0 to recover the classical linear surface. dt : float Integration time step for the integral term.
Source code in opensmc/surfaces/pid.py
PredefinedTimeSurface
¶
Bases: SlidingSurface
Predefined-time sliding surface.
For t < Tc:
.. math:: c(t) = \frac{\pi}{2\,T_c} \cdot \frac{1}{\cos!\left(\frac{\pi}{2}\,\frac{t}{T_c}\right)}
.. math:: s(t) = \dot{e} + c(t)\,e
For t >= Tc:
.. math:: s(t) = \dot{e} + c_\infty\,e \quad \text{(standard linear surface)}
Key property: The time-varying gain c(t) grows to infinity as t -> Tc, forcing e(t) -> 0 before the user-specified deadline Tc, regardless of initial conditions. After Tc, the surface reverts to a standard linear surface for regulation.
The gain derivative (needed for the controller) is:
.. math:: \dot{c}(t) = \frac{\pi^2}{4\,T_c^2} \cdot \frac{\sin!\left(\frac{\pi}{2}\,\frac{t}{T_c}\right)} {\cos^2!\left(\frac{\pi}{2}\,\frac{t}{T_c}\right)}
Parameters¶
Tc : float Predefined convergence time (positive). Error will reach zero before this time. c_inf : float Post-convergence surface slope (positive). Used for t >= Tc.
Source code in opensmc/surfaces/predefined_time.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute s = edot + c(t) * e.
Parameters¶
e : float Tracking error. edot : float Error derivative. t : float Current time (required for the time-varying gain).
Returns¶
s : float Sliding variable.
Source code in opensmc/surfaces/predefined_time.py
gain(t)
¶
Compute the time-varying gain c(t).
Returns c(t) for t < Tc, or c_inf for t >= Tc. Clamps t/Tc to 0.999 to avoid division by zero at t = Tc.
Source code in opensmc/surfaces/predefined_time.py
gain_dot(t)
¶
Compute the time derivative of c(t).
Returns dc/dt for t < Tc, or 0.0 for t >= Tc.
Source code in opensmc/surfaces/predefined_time.py
RLDiscoveredSurface
¶
Bases: SlidingSurface
Sliding surface learned by a reinforcement learning agent.
Wraps a trained Stable-Baselines3 model (or any callable) as a SlidingSurface compatible with all OpenSMC controllers.
Parameters¶
model : str or callable If str: path to a saved SB3 model (.zip). Loaded with PPO.load(). If callable: function(obs) -> action that computes the surface value. obs_fn : callable or None Optional function to map (e, edot, t) -> observation vector. Default: np.array([e, edot]). scale : float Scale factor applied to the RL output. Default 1.0.
Source code in opensmc/rl/rl_surface.py
Functions¶
compute(e, edot, t=0.0, **kwargs)
¶
Compute sliding variable using the RL policy.
Parameters¶
e : float or ndarray — tracking error edot : float or ndarray — error derivative t : float — time
Returns¶
s : float or ndarray — RL-generated sliding variable
Source code in opensmc/rl/rl_surface.py
TerminalSurface
¶
Bases: SlidingSurface
Terminal sliding surface (Zak, 1988).
.. math:: s = \dot{e} + \beta\,|e|^{p/q}\,\mathrm{sign}(e)
Provides finite-time convergence on the sliding manifold with convergence time:
.. math:: T_f = \frac{q}{\beta\,(q - p)}\,|e(0)|^{(q-p)/q}
.. warning::
This surface has a singularity at e = 0 in the equivalent control
(the coefficient beta * (p/q) * |e|^{p/q - 1} diverges). Use
:class:NonsingularTerminalSurface to avoid this issue.
Parameters¶
beta : float Gain (positive). p : int Numerator of the fractional exponent (odd positive integer, p < q). q : int Denominator of the fractional exponent (odd positive integer).