Skip to content
TUTORIAL

Sliding-Mode Control ​

This example demonstrates how to model a system with a discrete-time sliding-mode controller (SMC) in Dyad. The system consists of a second-order plant with a disturbance and a SuperTwistingSMC controller. The controller is implemented as a discrete-time system, and the plant is modeled as a continuous-time system.

Background ​

When designing an SMC controller, we must choose a switching function that produces the sliding variable  . The sliding surface   must be chosen such that the sliding variable exhibits desirable properties, i.e., converges to the desired state with stable dynamics. The control law is chosen to drive the system from any state    to the sliding surface  . The sliding surface is commonly chosen as an asymptotically stable system with order equal to  , where is the dimension of the state in the system to be controlled, and is the number of inputs.

Since the dynamics in this example is of relative degree  , we will choose a switching surface corresponding to a stable first-order system (  ). We will choose the system

which yields the switching variable   , where    is the tracking error.

Implementation ​

The sliding variable is computed from the sampled signals using block diagram components. The plant position is sampled (with AD effects for realism), filtered through an ExponentialFilter, and differentiated using a DiscreteDerivative to estimate velocity. The position and velocity errors relative to the reference signals are summed to form the sliding variable     , which is the input of the SuperTwistingSMC controller. Download as a Dyad projectSMC.zipOpen in Dyad Studio

dyad
"""
Test component that validates the SuperTwistingSMC sliding mode controller.

A second-order plant tracks a sinusoidal reference `qr` under a disturbance `d` that
consists of a constant and two sinusoids. The plant position is sampled and differentiated
to estimate velocity. The sliding variable `s = (xd - qdr) + (x - qr)` is computed from the
sampled signals and is the input of the super-twisting controller. The controller output is held by a ZOH
and summed with the disturbance before driving the plant.
"""
test component TestSlidingModeControl
  "Plant"
  secondorder = BlockComponents.Continuous.SecondOrder() {^secondorder}
  "Velocity estimation via discrete derivative of sampled position"
  dd_xd = DiscreteComponents.DiscreteDerivative(initialization = DiscreteComponents.InitialCondition.InitialOutput(y0 = 0.01)) {^dd_xd}
  "Position reference qr"
  sine_ref = BlockComponents.Sources.Sine(amplitude = 1, frequency = 2 / (2 * pi)) {^sine_ref}
  "Velocity reference qdr, the time derivative of qr"
  cosine_refd = BlockComponents.Sources.Cosine(amplitude = 2, frequency = 2 / (2 * pi)) {^cosine_refd}
  ps_qr = DiscreteComponents.Sampler() {^ps_qr}
  ps_qdr = DiscreteComponents.Sampler() {^ps_qdr}
  zoh = DiscreteComponents.ZeroOrderHold() {^zoh}
  "Constant component of the disturbance"
  constant_dist = BlockComponents.Sources.Constant(k = 2) {^constant_dist}
  "First sinusoidal component of the disturbance"
  sine_dist1 = BlockComponents.Sources.Sine(amplitude = 2, frequency = 3 / (2 * pi)) {^sine_dist1}
  "Second sinusoidal component of the disturbance"
  sine_dist2 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 5 / (2 * pi)) {^sine_dist2}
  "Position error x - qr"
  err_pos = BlockComponents.Math.Add(k2 = -1) {^err_pos}
  "Velocity error xd_est - qdr"
  err_vel = BlockComponents.Math.Add(k2 = -1) {^err_vel}
  "Sliding variable s, the sum of the position and velocity errors"
  sliding_surface = BlockComponents.Math.Add() {^sliding_surface}
  "Control + disturbance → plant"
  add_ctrl = BlockComponents.Math.Add() {^add_ctrl}
  add3 = BlockComponents.Math.Add3() {^add3}
  smc = DiscreteComponents.SuperTwistingSMC(k = 50) {^smc}
  periodicclock = DiscreteComponents.PeriodicClock(dt = 0.01) {^periodicclock}
  samplewithadeffects = DiscreteComponents.SampleWithADEffects(y_min = -3, y_max = 3, bits = 12, sigma = 0.01) {^samplewithadeffects}
  exponentialfilter = DiscreteComponents.ExponentialFilter(a = 0.2, initialization = DiscreteComponents.InitialCondition.InitialOutput(y0 = -1)) {^exponentialfilter}
  variable disturbance::Real
relations
  initial secondorder.x = -1
  initial secondorder.xd = 0
  # Sample references
  connect(sine_ref.y, ps_qr.u) {^id20}
  connect(cosine_refd.y, ps_qdr.u) {^id21}
  connect(zoh.y, add_ctrl.u1) {^id22}
  connect(add_ctrl.y, secondorder.u) {^id23}
  connect(dd_xd.y, err_vel.u1) {^id24}
  connect(ps_qdr.y, err_vel.u2) {^id25}
  connect(err_pos.y, sliding_surface.u1) {^id26}
  connect(ps_qr.y, err_pos.u2) {^id27}
  connect(sine_dist1.y, add3.u2) {^id28}
  connect(add3.y, add_ctrl.u2) {^id29}
  connect(err_vel.y, sliding_surface.u2) {^id30}
  connect(smc.s, sliding_surface.y) {^id31}
  connect(smc.y, zoh.u) {^id32}
  connect(constant_dist.y, add3.u1) {^id33}
  connect(sine_dist2.y, add3.u3) {^id34}
  connect(samplewithadeffects.u, secondorder.y) {^id35}
  connect(samplewithadeffects.y, periodicclock.y, exponentialfilter.u) {^id36}
  connect(exponentialfilter.y, dd_xd.u, err_pos.u1) {^id37}
metadata {
  "Dyad": {
    "tests": {
      "case1": {"stop": 6, "expect": {"signals": ["secondorder.y", "smc.y"]}}
    },
    "doc": {"behavior": false}
  },
  "_links": {
    "secondorder": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 1020, "y1": 660, "x2": 1120, "y2": 760, "rot": 0}
        },
        "tags": []
      }
    },
    "dd_xd": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 140, "y1": 850, "x2": 40, "y2": 950, "rot": 90}
        },
        "tags": []
      }
    },
    "sine_ref": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": -200, "y1": 480, "x2": -100, "y2": 580, "rot": 0}
        },
        "tags": []
      }
    },
    "cosine_refd": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": -200, "y1": 690, "x2": -100, "y2": 790, "rot": 0}
        },
        "tags": []
      }
    },
    "ps_qr": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": -70, "y1": 480, "x2": 30, "y2": 580, "rot": 0}
        },
        "tags": []
      }
    },
    "ps_qdr": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": -70, "y1": 690, "x2": 30, "y2": 790, "rot": 0}
        },
        "tags": []
      }
    },
    "zoh": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 690, "y1": 690, "x2": 790, "y2": 790, "rot": 0}
        },
        "tags": []
      }
    },
    "constant_dist": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 520, "y1": 280, "x2": 620, "y2": 380, "rot": 0}
        },
        "tags": []
      }
    },
    "sine_dist1": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 520, "y1": 410, "x2": 620, "y2": 510, "rot": 0}
        },
        "tags": []
      }
    },
    "sine_dist2": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 520, "y1": 540, "x2": 620, "y2": 640, "rot": 0}
        },
        "tags": []
      }
    },
    "err_pos": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 150, "y1": 660, "x2": 250, "y2": 560, "rot": 0}
        },
        "tags": []
      }
    },
    "err_vel": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 150, "y1": 820, "x2": 250, "y2": 720, "rot": 0}
        },
        "tags": []
      }
    },
    "sliding_surface": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 360, "y1": 690, "x2": 460, "y2": 790, "rot": 0}
        },
        "tags": []
      }
    },
    "add_ctrl": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 880, "y1": 760, "x2": 980, "y2": 660, "rot": 0}
        },
        "tags": []
      }
    },
    "add3": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 690, "y1": 410, "x2": 790, "y2": 510, "rot": 0}
        },
        "tags": []
      }
    },
    "smc": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 520, "y1": 690, "x2": 620, "y2": 790, "rot": 0}
        },
        "tags": []
      }
    },
    "periodicclock": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 880, "y1": 800, "x2": 980, "y2": 900, "rot": 0}
        },
        "tags": []
      }
    },
    "samplewithadeffects": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 980, "y1": 940, "x2": 880, "y2": 1040, "rot": 0}
        },
        "tags": []
      }
    },
    "exponentialfilter": {
      "Dyad": {
        "placement": {
          "diagram": {"iconName": "default", "x1": 530, "y1": 940, "x2": 430, "y2": 1040, "rot": 0}
        },
        "tags": []
      }
    },
    "id20": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id21": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id22": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id23": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id24": {
      "Dyad": {
        "edges": [{"S": 1, "M": [{"x": 90, "y": 800}], "E": 2}],
        "renderStyle": "standard"
      }
    },
    "id25": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id26": {
      "Dyad": {
        "edges": [{"S": 1, "M": [{"x": 280, "y": 610}, {"x": 280, "y": 710}], "E": 2}],
        "renderStyle": "standard"
      }
    },
    "id27": {
      "Dyad": {
        "edges": [{"S": 1, "M": [{"x": 68, "y": 530}, {"x": 68, "y": 580}], "E": 2}],
        "renderStyle": "standard"
      }
    },
    "id28": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id29": {
      "Dyad": {
        "edges": [{"S": 1, "M": [{"x": 830, "y": 460}, {"x": 830, "y": 680}], "E": 2}],
        "renderStyle": "standard"
      }
    },
    "id30": {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}},
    "id31": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id32": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}},
    "id33": {
      "Dyad": {
        "renderStyle": "standard",
        "edges": [{"S": 1, "M": [{"x": 657.5, "y": 330}, {"x": 657.5, "y": 430}], "E": 2}]
      }
    },
    "id34": {
      "Dyad": {
        "renderStyle": "standard",
        "edges": [{"S": 1, "M": [{"x": 657.5, "y": 590}, {"x": 657.5, "y": 490}], "E": 2}]
      }
    },
    "id35": {
      "Dyad": {
        "edges": [{"S": 1, "M": [{"x": 1160, "y": 990}, {"x": 1160, "y": 710}], "E": 2}],
        "renderStyle": "standard"
      }
    },
    "id36": {
      "Dyad": {
        "edges": [
          {"S": 1, "M": [], "E": -1},
          {"S": -1, "M": [{"x": 800, "y": 990}, {"x": 800, "y": 850}], "E": 2},
          {"S": 3, "M": [], "E": -1}
        ],
        "junctions": [{"x": 800, "y": 990}],
        "renderStyle": "standard"
      }
    },
    "id37": {
      "Dyad": {
        "edges": [
          {"S": 1, "M": [], "E": -1},
          {"S": -1, "M": [{"x": 90, "y": 990}], "E": 2},
          {"S": 3, "M": [{"x": -250, "y": 640}, {"x": -250, "y": 990}], "E": -1}
        ],
        "junctions": [{"x": 90, "y": 990}],
        "renderStyle": "standard"
      }
    }
  }
}
end

analysis SlidingModeControlDemoAnalysis
  extends TransientAnalysis(stop = 10.0)
  model = TestSlidingModeControl()
end
julia
using Plots
result = SlidingModeControlDemoAnalysis()
using DyadInterface
model = artifacts(result, :SimplifiedSystem)
figy = plot(result; idxs = model.secondorder.y, label = "Plant position")
plot!(result; idxs = model.sine_ref.y, label = "Reference")
figu = plot(result; idxs = model.smc.y, label = "Control signal")
plot!(result; idxs = model.add3.y, label = "Disturbance")
plot(figy, figu, layout = (2, 1))

The simulation indicates that the controller is able to track the reference signal despite the presence of the disturbance. The control signal exhibits high-frequency chattering, a common characteristic of sliding-mode controllers. The use of SampleWithADEffects and ExponentialFilter adds realistic measurement imperfections: 12-bit quantization, small Gaussian noise, and low-pass filtering of the position signal.