Skip to content

Reaching Laws

Reaching laws dictate how the system state approaches the sliding surface. They represent the desired dynamics of the sliding variable \(s(e, t)\), often in the form of \(\dot{s} = f(s)\).

Usage Example

from opensmc.reaching import SuperTwistingLaw

# Create a Super-Twisting reaching law for chattering reduction
reaching = SuperTwistingLaw(k1=1.5, k2=1.1)

# Compute the required reaching rate for a given surface value s
s_dot_req = reaching.compute(s=0.2)

Available Reaching Laws

reaching

OpenSMC Reaching Laws — 5 reaching law implementations.

Classes

ConstantRate

Bases: ReachingLaw

Constant-rate reaching law: u_r = -k * sign(s).

Parameters

k : float Switching gain (must exceed disturbance bound for robustness).

Source code in opensmc/reaching/constant.py
class ConstantRate(ReachingLaw):
    """Constant-rate reaching law: u_r = -k * sign(s).

    Parameters
    ----------
    k : float
        Switching gain (must exceed disturbance bound for robustness).
    """

    def __init__(self, *, k=1.0):
        self.k = k

    def compute(self, s, t=0.0, **kwargs):
        """Compute reaching control signal.

        Parameters
        ----------
        s : float
            Sliding variable.
        t : float, optional
            Time (unused).

        Returns
        -------
        u_r : float
            Reaching control: -k * sign(s).
        """
        return -self.k * float(np.sign(s))
Functions
compute(s, t=0.0, **kwargs)

Compute reaching control signal.

Parameters

s : float Sliding variable. t : float, optional Time (unused).

Returns

u_r : float Reaching control: -k * sign(s).

Source code in opensmc/reaching/constant.py
def compute(self, s, t=0.0, **kwargs):
    """Compute reaching control signal.

    Parameters
    ----------
    s : float
        Sliding variable.
    t : float, optional
        Time (unused).

    Returns
    -------
    u_r : float
        Reaching control: -k * sign(s).
    """
    return -self.k * float(np.sign(s))

ExponentialRate

Bases: ReachingLaw

Exponential reaching law: u_r = -k * sign(s) - q * s.

Parameters

k : float Switching gain (must exceed disturbance bound). q : float Exponential/linear gain (controls convergence rate far from surface).

Source code in opensmc/reaching/exponential.py
class ExponentialRate(ReachingLaw):
    """Exponential reaching law: u_r = -k * sign(s) - q * s.

    Parameters
    ----------
    k : float
        Switching gain (must exceed disturbance bound).
    q : float
        Exponential/linear gain (controls convergence rate far from surface).
    """

    def __init__(self, *, k=1.0, q=10.0):
        self.k = k
        self.q = q

    def compute(self, s, t=0.0, **kwargs):
        """Compute reaching control signal.

        Parameters
        ----------
        s : float
            Sliding variable.
        t : float, optional
            Time (unused).

        Returns
        -------
        u_r : float
            Reaching control: -k * sign(s) - q * s.
        """
        return -self.k * float(np.sign(s)) - self.q * s
Functions
compute(s, t=0.0, **kwargs)

Compute reaching control signal.

Parameters

s : float Sliding variable. t : float, optional Time (unused).

Returns

u_r : float Reaching control: -k * sign(s) - q * s.

Source code in opensmc/reaching/exponential.py
def compute(self, s, t=0.0, **kwargs):
    """Compute reaching control signal.

    Parameters
    ----------
    s : float
        Sliding variable.
    t : float, optional
        Time (unused).

    Returns
    -------
    u_r : float
        Reaching control: -k * sign(s) - q * s.
    """
    return -self.k * float(np.sign(s)) - self.q * s

PowerRate

Bases: ReachingLaw

Power-rate reaching law: u_r = -k * |s|^alpha * sign(s).

Parameters

k : float Gain. alpha : float Power exponent, typically 0 < alpha < 1 for finite-time convergence.

Source code in opensmc/reaching/power.py
class PowerRate(ReachingLaw):
    """Power-rate reaching law: u_r = -k * |s|^alpha * sign(s).

    Parameters
    ----------
    k : float
        Gain.
    alpha : float
        Power exponent, typically 0 < alpha < 1 for finite-time convergence.
    """

    def __init__(self, *, k=10.0, alpha=0.5):
        self.k = k
        self.alpha = alpha

    def compute(self, s, t=0.0, **kwargs):
        """Compute reaching control signal.

        Parameters
        ----------
        s : float
            Sliding variable.
        t : float, optional
            Time (unused).

        Returns
        -------
        u_r : float
            Reaching control: -k * |s|^alpha * sign(s).
        """
        return -self.k * float(np.abs(s)) ** self.alpha * float(np.sign(s))
Functions
compute(s, t=0.0, **kwargs)

Compute reaching control signal.

Parameters

s : float Sliding variable. t : float, optional Time (unused).

Returns

u_r : float Reaching control: -k * |s|^alpha * sign(s).

Source code in opensmc/reaching/power.py
def compute(self, s, t=0.0, **kwargs):
    """Compute reaching control signal.

    Parameters
    ----------
    s : float
        Sliding variable.
    t : float, optional
        Time (unused).

    Returns
    -------
    u_r : float
        Reaching control: -k * |s|^alpha * sign(s).
    """
    return -self.k * float(np.abs(s)) ** self.alpha * float(np.sign(s))

Saturation

Bases: ReachingLaw

Saturation reaching law: u_r = -k * sat(s / phi).

Parameters

k : float Gain. phi : float Boundary layer thickness. Larger phi reduces chattering but increases steady-state error.

Source code in opensmc/reaching/saturation.py
class Saturation(ReachingLaw):
    """Saturation reaching law: u_r = -k * sat(s / phi).

    Parameters
    ----------
    k : float
        Gain.
    phi : float
        Boundary layer thickness. Larger phi reduces chattering but
        increases steady-state error.
    """

    def __init__(self, *, k=15.0, phi=0.1):
        self.k = k
        self.phi = phi

    def compute(self, s, t=0.0, **kwargs):
        """Compute reaching control signal.

        Parameters
        ----------
        s : float
            Sliding variable.
        t : float, optional
            Time (unused).

        Returns
        -------
        u_r : float
            Reaching control: -k * sat(s / phi).
        """
        y = s / self.phi
        sat_y = float(np.clip(y, -1.0, 1.0))
        return -self.k * sat_y
Functions
compute(s, t=0.0, **kwargs)

Compute reaching control signal.

Parameters

s : float Sliding variable. t : float, optional Time (unused).

Returns

u_r : float Reaching control: -k * sat(s / phi).

Source code in opensmc/reaching/saturation.py
def compute(self, s, t=0.0, **kwargs):
    """Compute reaching control signal.

    Parameters
    ----------
    s : float
        Sliding variable.
    t : float, optional
        Time (unused).

    Returns
    -------
    u_r : float
        Reaching control: -k * sat(s / phi).
    """
    y = s / self.phi
    sat_y = float(np.clip(y, -1.0, 1.0))
    return -self.k * sat_y

SuperTwisting

Bases: ReachingLaw

Super-twisting reaching law (Levant, 1993).

u_r = -k1 * |s|^(1/2) * sign(s) + z, zdot = -k2 * sign(s)

Parameters

k1 : float Proportional gain on |s|^(1/2) term. k2 : float Integral gain (drives zdot). dt : float Time step for Euler integration of the internal state z.

Source code in opensmc/reaching/super_twisting.py
class SuperTwisting(ReachingLaw):
    """Super-twisting reaching law (Levant, 1993).

    u_r = -k1 * |s|^(1/2) * sign(s) + z,  zdot = -k2 * sign(s)

    Parameters
    ----------
    k1 : float
        Proportional gain on |s|^(1/2) term.
    k2 : float
        Integral gain (drives zdot).
    dt : float
        Time step for Euler integration of the internal state z.
    """

    def __init__(self, *, k1=15.0, k2=10.0, dt=1e-4):
        self.k1 = k1
        self.k2 = k2
        self.dt = dt
        self.z = 0.0

    def compute(self, s, t=0.0, **kwargs):
        """Compute reaching control signal and update internal state.

        Parameters
        ----------
        s : float
            Sliding variable.
        t : float, optional
            Time (unused).

        Returns
        -------
        u_r : float
            Reaching control: -k1 * |s|^(1/2) * sign(s) + z.
        """
        sign_s = float(np.sign(s))
        u_r = -self.k1 * float(np.abs(s)) ** 0.5 * sign_s + self.z
        self.z -= self.k2 * sign_s * self.dt
        return u_r

    def reset(self):
        """Reset the integral state z to zero."""
        self.z = 0.0
Functions
compute(s, t=0.0, **kwargs)

Compute reaching control signal and update internal state.

Parameters

s : float Sliding variable. t : float, optional Time (unused).

Returns

u_r : float Reaching control: -k1 * |s|^(1/2) * sign(s) + z.

Source code in opensmc/reaching/super_twisting.py
def compute(self, s, t=0.0, **kwargs):
    """Compute reaching control signal and update internal state.

    Parameters
    ----------
    s : float
        Sliding variable.
    t : float, optional
        Time (unused).

    Returns
    -------
    u_r : float
        Reaching control: -k1 * |s|^(1/2) * sign(s) + z.
    """
    sign_s = float(np.sign(s))
    u_r = -self.k1 * float(np.abs(s)) ** 0.5 * sign_s + self.z
    self.z -= self.k2 * sign_s * self.dt
    return u_r
reset()

Reset the integral state z to zero.

Source code in opensmc/reaching/super_twisting.py
def reset(self):
    """Reset the integral state z to zero."""
    self.z = 0.0