PID (z) (standard) DiscretePIDStandard DiscretePIDStandard Icon

DiscretePIDStandard

Discrete-time PID controller on standard (ideal) form with anti-windup and set-point weighting.

The controller is implemented on standard form

Simplified:

\[u = K \left( e + \int \frac{1}{T_i} e dt + T_d \dfrac{de}{dt} \right)\]

Detailed:

\[u = K \left( (w_p r - y) + \dfrac{1}{T_i} \int \big( (r - y) + \dfrac{e_s}{K N_i} \big ) dt + T_d \dfrac{d}{dt}(w_d r - y) \right) + k_{ff} u_{ff}\]

where e_s = u - v is the saturated error signal, v is the unsaturated control signal and u is the saturated control signal.

An optional feed-forward signal $u_{ff}$ can be added to the control signal by setting with_ff = true and connecting the u_ff input. Its contribution $k_{ff} u_{ff}$ is added to the unsaturated control signal v before saturation, so it participates in the anti-windup tracking.

The derivative is filtered to allow a maximum gain of $N_d$.

The integrator is discretized using the method specified by Imethod, options include

  • Imethod = DiscretizationMethod.Forward (default): Corresponding to the transfer function $T_s / (z - 1)$
  • Imethod = DiscretizationMethod.Backward: Corresponding to the transfer function $T_s z / (z - 1)$
  • Imethod = DiscretizationMethod.Trapezoidal: Corresponding to the transfer function $(T_s / 2) (z + 1) / (z - 1)$

The derivative is discretized using the method specified by Dmethod, options include

  • Dmethod = DiscretizationMethod.Forward: Corresponding to the transfer function $\dfrac{N (z-1)}{z - \dfrac{T_d-N T_s}{T_d}}$.
  • Dmethod = DiscretizationMethod.Backward (default): Corresponding to the transfer function $\dfrac{\dfrac{NT_d}{T_d + N T_s}(z-1)}{z - \dfrac{T_d}{T_d + N T_s}}$
  • Dmethod = DiscretizationMethod.Trapezoidal: Corresponding to the transfer function $\dfrac{\dfrac{2NT_d}{2T_d + N T_s}(z-1)}{z - \dfrac{2T_d - N T_s}{2T_d + N T_s}}$

Anti windup is realized by back-calculation (tracking) of the error signal $e_s$ when the output is saturated, with tracking time constant $T_t = N_i T_i$. This is the same $N_i$ convention as BlockComponents.Continuous.LimPID, so switching between the two components without changing Ni preserves the anti-windup behavior.

The initial condition is selected through the initialization enum. The state of the controller is the integrator state $I$ and, when with_D = true, the filtered derivative $D$ together with the previous sample of the setpoint-weighted error $w_d r - y$:

  • InitialCondition.InitialState(x0) (default): the integrator state is set to x0 and the derivative path starts from zero state, i.e., the stored previous sample of the setpoint-weighted error is zero. The default x0 = 0 is zero-state initialization.
  • InitialCondition.InitialOutput(y0): the derivative path starts at rest, so the filtered derivative contributes nothing at the first sample, and the integrator state is chosen such that the first unsaturated control signal equals y0. This provides bumpless transfer from an actuator already held at y0. If y0 lies outside [y_min, y_max], the first output is the saturated value. With with_I = false the controller has no integrator state and the requested output is not realizable; only the derivative path is affected.
  • InitialCondition.SteadyState: not supported for with_I = true, since an integrator has no unique steady state. With with_I = false the derivative path starts at rest, which is its steady state.
  • The array variants InitialStateArray and InitialOutputArray are not supported.

To use the controller in 1DOF mode, i.e., with only the control error as input, connect the error signal to the u_s connector, connect a Constant(; k = 0) to the u_m connector and set wp = wd = 1.

Usage

DiscreteComponents.DiscretePIDStandard(wde_init_w=_pid_wde_init_weight(initialization), K=1, Ti=1, Td=1, Ni=0.9, Nd=10, y_max=Inf, y_min=ifelse(y_max > 0, -y_max, -Inf), wp=1, wd=1, k_ff=1)

Parameters:

NameDescriptionUnitsDefault value
ImethodDiscretization method for the integrator–Discretizat...d.Forward()
DmethodDiscretization method for the derivative–Discretizat....Backward()
with_IWhether or not to include the integral part–true
with_DWhether or not to include the derivative part–true
with_ffWhether or not to include the feed-forward input–false
Ts–SampleTime()
initializationInitial-condition specification–DiscreteCom...e(; x0=0.0)
KProportional gain–1
TiIntegral time constant (only active if with_I = true)–1
TdDerivative time (only active if with_D = true)–1
NiNi*Ti is the time constant of the anti-windup compensation (only active if with_I = true)–0.9
NdMaximum derivative gain (only active if with_D = true). Typically set to 10-100.–10
y_maxMaximum output–Inf
y_minMinimum output–ifelse(yma...max, -Inf)
wpSet-point weighting in the proportional part. Set to 0 to prevent step changes in the output due to step changes in the reference.–1
wdSet-point weighting in the derivative part. Set to 0 to prevent very large impulsive changes in the output due to step changes in the reference.–1
k_ffGain of the feed-forward input (only active if with_ff = true)–1

Connectors

  • u_s - This connector represents a real signal as an input to a component (RealInput)
  • u_m - This connector represents a real signal as an input to a component (RealInput)
  • u_ff - This connector represents a real signal as an input to a component (RealInput)
  • y - This connector represents a real signal as an output from a component (RealOutput)

Variables

NameDescriptionUnits
IState of Integrator–
DState of filtered derivative–
wdeSetpoint-weighted error for derivative–
uffFeed-forward contribution to the control signal–
vUn-saturated output of the controller–
eIError signal input to integrator including anti-windup tracking signal–
eError signal–

Behavior

using DiscreteComponents #hide
using ModelingToolkit #hide
@variables K #hide
@variables Ti #hide
@variables Td #hide
@variables Ni #hide
@variables Nd #hide
@variables y_max #hide
@variables y_min #hide
@variables wp #hide
@variables wd #hide
@variables k_ff #hide
@named sys = DiscreteComponents.DiscretePIDStandard(K=K, Ti=Ti, Td=Td, Ni=Ni, Nd=Nd, y_max=y_max, y_min=y_min, wp=wp, wd=wd, k_ff=k_ff) #hide
full_equations(sys) #hide

Source

"""
Discrete-time PID controller on standard (ideal) form with anti-windup and set-point weighting.

The controller is implemented on standard form

Simplified:
```math
u = K \left( e + \int \frac{1}{T_i} e  dt + T_d \dfrac{de}{dt} \right)
```

Detailed:
```math
u = K \left( (w_p r - y) + \dfrac{1}{T_i} \int \big( (r - y) + \dfrac{e_s}{K N_i} \big ) dt + T_d \dfrac{d}{dt}(w_d r - y) \right) + k_{ff} u_{ff}
```

where `e_s = u - v` is the saturated error signal, `v` is the unsaturated control signal and `u` is the saturated control signal.

An optional feed-forward signal ``u_{ff}`` can be added to the control signal by setting `with_ff = true` and connecting the `u_ff` input. Its contribution ``k_{ff} u_{ff}`` is added to the unsaturated control signal `v` before saturation, so it participates in the anti-windup tracking.

The derivative is filtered to allow a maximum gain of ``N_d``.

The integrator is discretized using the method specified by `Imethod`, options include
- `Imethod = DiscretizationMethod.Forward` (default): Corresponding to the transfer function ``T_s / (z - 1)``
- `Imethod = DiscretizationMethod.Backward`: Corresponding to the transfer function ``T_s z / (z - 1)``
- `Imethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``(T_s / 2) (z + 1) / (z - 1)``

The derivative is discretized using the method specified by `Dmethod`, options include
- `Dmethod = DiscretizationMethod.Forward`: Corresponding to the transfer function ``\dfrac{N (z-1)}{z - \dfrac{T_d-N T_s}{T_d}}``.
- `Dmethod = DiscretizationMethod.Backward` (default): Corresponding to the transfer function ``\dfrac{\dfrac{NT_d}{T_d + N T_s}(z-1)}{z - \dfrac{T_d}{T_d + N T_s}}``
- `Dmethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``\dfrac{\dfrac{2NT_d}{2T_d + N T_s}(z-1)}{z - \dfrac{2T_d - N T_s}{2T_d + N T_s}}``

Anti windup is realized by back-calculation (tracking) of the error signal ``e_s`` when the output is saturated, with tracking time constant ``T_t = N_i T_i``. This is the same ``N_i`` convention as `BlockComponents.Continuous.LimPID`, so switching between the two components without changing `Ni` preserves the anti-windup behavior.

The initial condition is selected through the `initialization` enum. The state of the controller is the integrator state ``I`` and, when `with_D = true`, the filtered derivative ``D`` together with the previous sample of the setpoint-weighted error ``w_d r - y``:
- `InitialCondition.InitialState(x0)` (default): the integrator state is set to `x0` and the derivative path starts from zero state, i.e., the stored previous sample of the setpoint-weighted error is zero. The default `x0 = 0` is zero-state initialization.
- `InitialCondition.InitialOutput(y0)`: the derivative path starts at rest, so the filtered derivative contributes nothing at the first sample, and the integrator state is chosen such that the first unsaturated control signal equals `y0`. This provides bumpless transfer from an actuator already held at `y0`. If `y0` lies outside `[y_min, y_max]`, the first output is the saturated value. With `with_I = false` the controller has no integrator state and the requested output is not realizable; only the derivative path is affected.
- `InitialCondition.SteadyState`: not supported for `with_I = true`, since an integrator has no unique steady state. With `with_I = false` the derivative path starts at rest, which is its steady state.
- The array variants `InitialStateArray` and `InitialOutputArray` are not supported.

To use the controller in 1DOF mode, i.e., with only the control error as input, connect the error signal to the `u_s` connector, connect a `Constant(; k = 0)` to the `u_m` connector and set `wp = wd = 1`.
"""
component DiscretePIDStandard@[input clk extends Discrete]
  "The reference signal to the controller (or the error signal if used in 1DOF mode)"
  u_s = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 170, "x2": 0, "y2": 270, "rot": 0}
      },
      "tags": []
    }
  }
  "The measurement feedback"
  u_m = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 690, "x2": 0, "y2": 790, "rot": 0}
      },
      "tags": []
    }
  }
  "The feed-forward signal (only active if `with_ff = true`)"
  u_ff = RealInput@[clk]() if with_ff {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 430, "x2": 0, "y2": 530, "rot": 0}
      },
      "tags": []
    }
  }
  "The control signal output"
  y = RealOutput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Discretization method for the integrator"
  structural parameter Imethod::DiscretizationMethod = DiscretizationMethod.Forward()
  "Discretization method for the derivative"
  structural parameter Dmethod::DiscretizationMethod = DiscretizationMethod.Backward()
  "Whether or not to include the integral part"
  structural parameter with_I::Boolean = true
  "Whether or not to include the derivative part"
  structural parameter with_D::Boolean = true
  "Whether or not to include the feed-forward input"
  structural parameter with_ff::Boolean = false
  structural parameter Ts::Real = SampleTime()
  "Initial-condition specification"
  structural parameter initialization::InitialCondition = DiscreteComponents.InitialCondition.InitialState(x0 = 0.0)
  "Weight of the first setpoint-weighted error sample in the stored previous sample of the derivative path; 1 starts the derivative path at rest (only used if `with_D = true`)"
  final parameter wde_init_w::Real = _pid_wde_init_weight(initialization)
  "Proportional gain"
  parameter K::Real = 1
  "Integral time constant (only active if `with_I = true`)"
  parameter Ti::Real = 1
  "Derivative time (only active if `with_D = true`)"
  parameter Td::Real = 1
  "`Ni*Ti` is the time constant of the anti-windup compensation (only active if `with_I = true`)"
  parameter Ni::Real = 0.9
  "Maximum derivative gain (only active if `with_D = true`). Typically set to 10-100."
  parameter Nd::Real = 10
  "Maximum output"
  parameter y_max::Real = Inf
  "Minimum output"
  parameter y_min::Real = ifelse(y_max > 0, -y_max, -Inf)
  "Set-point weighting in the proportional part. Set to `0` to prevent step changes in the output due to step changes in the reference."
  parameter wp::Real = 1
  "Set-point weighting in the derivative part. Set to `0` to prevent very large impulsive changes in the output due to step changes in the reference."
  parameter wd::Real = 1
  "Gain of the feed-forward input (only active if `with_ff = true`)"
  parameter k_ff::Real = 1
  "State of Integrator"
  variable I::Real if with_I
  "State of filtered derivative"
  variable D::Real if with_D
  "Setpoint-weighted error for derivative"
  variable wde::Real if with_D
  "Feed-forward contribution to the control signal"
  variable uff::Real
  "Un-saturated output of the controller"
  variable v::Real
  "Error signal input to integrator including anti-windup tracking signal"
  variable eI::Real if with_I
  "Error signal"
  variable e::Real
relations
  y = clamp(v, y_min, y_max) # Saturated control signal (= controller output)
  e = u_s - u_m
  if with_ff
    uff = k_ff * u_ff # Feed-forward added before saturation
  else
    uff = 0
  end
  if with_I
    switch Imethod
      case Forward
        initial eI@(clk-1) = 0
        eI = e + (y - v) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * eI@(clk-1)
      case Backward
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        eI = e + (y@(clk-1) - v@(clk-1)) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * eI@clk
      case Trapezoidal
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        initial eI@(clk-1) = 0
        eI = e + (y@(clk-1) - v@(clk-1)) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * (eI@clk + eI@(clk-1)) / 2
    end
    switch initialization
      case InitialState
        initial I@(clk-1) = initialization.x0
      case InitialOutput
        # The derivative path starts at rest for this variant, so the filtered derivative
        # contributes nothing at the first sample and the requested output fixes the
        # integrator state after the first update to (y0 - uff)/K - (wp*u_s - u_m). The
        # stored previous-sample state is that value less the first update, which differs
        # between the discretization methods: Forward integrates the previous error sample,
        # initialized to zero, while Backward and Trapezoidal integrate the current one.
        switch Imethod
          case Forward
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m)
          case Backward
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m) - Ts / Ti * e
          case Trapezoidal
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m) - Ts / Ti * e / 2
        end
      case SteadyState
        initial I@(clk-1) = error("SteadyState initial condition is not supported for DiscretePIDStandard with with_I = true, an integrator has no unique steady state; use InitialOutput or InitialState")
    end
  end
  if with_D
    initial D@(clk-1) = 0
    initial wde@(clk-1) = wde_init_w * (wd * u_s - u_m)
    wde = wd * u_s - u_m
    switch Dmethod
      case Forward
        D@clk = (Td - Nd * Ts) / Td * D@(clk-1) + Nd * (wde@clk - wde@(clk-1))
      case Backward
        D@clk = Td / (Td + Nd * Ts) * D@(clk-1) + Nd * Td / (Td + Nd * Ts) * (wde@clk - wde@(clk-1))
      case Trapezoidal
        D@clk = (2 * Td - Nd * Ts) / (2 * Td + Nd * Ts) * D@(clk-1) + 2 * Nd * Td / (2 * Td + Nd * Ts) * (wde@clk - wde@(clk-1))
    end
  end
  if with_I and with_D
    v = K * ((wp * u_s - u_m) + I + D) + uff # Unsaturated control signal
  elseif with_I
    v = K * ((wp * u_s - u_m) + I) + uff # Unsaturated control signal
  elseif with_D
    v = K * ((wp * u_s - u_m) + D) + uff # Unsaturated control signal
  else
    v = K * (wp * u_s - u_m) + uff # Unsaturated control signal (pure proportional)
  end
metadata {
  "Dyad": {"icons": {"default": "dyad://DiscreteComponents/DiscretePIDStandard.svg"}}
}
end
Flattened Source
"""
Discrete-time PID controller on standard (ideal) form with anti-windup and set-point weighting.

The controller is implemented on standard form

Simplified:
```math
u = K \left( e + \int \frac{1}{T_i} e  dt + T_d \dfrac{de}{dt} \right)
```

Detailed:
```math
u = K \left( (w_p r - y) + \dfrac{1}{T_i} \int \big( (r - y) + \dfrac{e_s}{K N_i} \big ) dt + T_d \dfrac{d}{dt}(w_d r - y) \right) + k_{ff} u_{ff}
```

where `e_s = u - v` is the saturated error signal, `v` is the unsaturated control signal and `u` is the saturated control signal.

An optional feed-forward signal ``u_{ff}`` can be added to the control signal by setting `with_ff = true` and connecting the `u_ff` input. Its contribution ``k_{ff} u_{ff}`` is added to the unsaturated control signal `v` before saturation, so it participates in the anti-windup tracking.

The derivative is filtered to allow a maximum gain of ``N_d``.

The integrator is discretized using the method specified by `Imethod`, options include
- `Imethod = DiscretizationMethod.Forward` (default): Corresponding to the transfer function ``T_s / (z - 1)``
- `Imethod = DiscretizationMethod.Backward`: Corresponding to the transfer function ``T_s z / (z - 1)``
- `Imethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``(T_s / 2) (z + 1) / (z - 1)``

The derivative is discretized using the method specified by `Dmethod`, options include
- `Dmethod = DiscretizationMethod.Forward`: Corresponding to the transfer function ``\dfrac{N (z-1)}{z - \dfrac{T_d-N T_s}{T_d}}``.
- `Dmethod = DiscretizationMethod.Backward` (default): Corresponding to the transfer function ``\dfrac{\dfrac{NT_d}{T_d + N T_s}(z-1)}{z - \dfrac{T_d}{T_d + N T_s}}``
- `Dmethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``\dfrac{\dfrac{2NT_d}{2T_d + N T_s}(z-1)}{z - \dfrac{2T_d - N T_s}{2T_d + N T_s}}``

Anti windup is realized by back-calculation (tracking) of the error signal ``e_s`` when the output is saturated, with tracking time constant ``T_t = N_i T_i``. This is the same ``N_i`` convention as `BlockComponents.Continuous.LimPID`, so switching between the two components without changing `Ni` preserves the anti-windup behavior.

The initial condition is selected through the `initialization` enum. The state of the controller is the integrator state ``I`` and, when `with_D = true`, the filtered derivative ``D`` together with the previous sample of the setpoint-weighted error ``w_d r - y``:
- `InitialCondition.InitialState(x0)` (default): the integrator state is set to `x0` and the derivative path starts from zero state, i.e., the stored previous sample of the setpoint-weighted error is zero. The default `x0 = 0` is zero-state initialization.
- `InitialCondition.InitialOutput(y0)`: the derivative path starts at rest, so the filtered derivative contributes nothing at the first sample, and the integrator state is chosen such that the first unsaturated control signal equals `y0`. This provides bumpless transfer from an actuator already held at `y0`. If `y0` lies outside `[y_min, y_max]`, the first output is the saturated value. With `with_I = false` the controller has no integrator state and the requested output is not realizable; only the derivative path is affected.
- `InitialCondition.SteadyState`: not supported for `with_I = true`, since an integrator has no unique steady state. With `with_I = false` the derivative path starts at rest, which is its steady state.
- The array variants `InitialStateArray` and `InitialOutputArray` are not supported.

To use the controller in 1DOF mode, i.e., with only the control error as input, connect the error signal to the `u_s` connector, connect a `Constant(; k = 0)` to the `u_m` connector and set `wp = wd = 1`.
"""
component DiscretePIDStandard
  "The reference signal to the controller (or the error signal if used in 1DOF mode)"
  u_s = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 170, "x2": 0, "y2": 270, "rot": 0}
      },
      "tags": []
    }
  }
  "The measurement feedback"
  u_m = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 690, "x2": 0, "y2": 790, "rot": 0}
      },
      "tags": []
    }
  }
  "The feed-forward signal (only active if `with_ff = true`)"
  u_ff = RealInput@[clk]() if with_ff {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 430, "x2": 0, "y2": 530, "rot": 0}
      },
      "tags": []
    }
  }
  "The control signal output"
  y = RealOutput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Discretization method for the integrator"
  structural parameter Imethod::DiscretizationMethod = DiscretizationMethod.Forward()
  "Discretization method for the derivative"
  structural parameter Dmethod::DiscretizationMethod = DiscretizationMethod.Backward()
  "Whether or not to include the integral part"
  structural parameter with_I::Boolean = true
  "Whether or not to include the derivative part"
  structural parameter with_D::Boolean = true
  "Whether or not to include the feed-forward input"
  structural parameter with_ff::Boolean = false
  structural parameter Ts::Real = SampleTime()
  "Initial-condition specification"
  structural parameter initialization::InitialCondition = DiscreteComponents.InitialCondition.InitialState(x0 = 0.0)
  "Weight of the first setpoint-weighted error sample in the stored previous sample of the derivative path; 1 starts the derivative path at rest (only used if `with_D = true`)"
  final parameter wde_init_w::Real = _pid_wde_init_weight(initialization)
  "Proportional gain"
  parameter K::Real = 1
  "Integral time constant (only active if `with_I = true`)"
  parameter Ti::Real = 1
  "Derivative time (only active if `with_D = true`)"
  parameter Td::Real = 1
  "`Ni*Ti` is the time constant of the anti-windup compensation (only active if `with_I = true`)"
  parameter Ni::Real = 0.9
  "Maximum derivative gain (only active if `with_D = true`). Typically set to 10-100."
  parameter Nd::Real = 10
  "Maximum output"
  parameter y_max::Real = Inf
  "Minimum output"
  parameter y_min::Real = ifelse(y_max > 0, -y_max, -Inf)
  "Set-point weighting in the proportional part. Set to `0` to prevent step changes in the output due to step changes in the reference."
  parameter wp::Real = 1
  "Set-point weighting in the derivative part. Set to `0` to prevent very large impulsive changes in the output due to step changes in the reference."
  parameter wd::Real = 1
  "Gain of the feed-forward input (only active if `with_ff = true`)"
  parameter k_ff::Real = 1
  "State of Integrator"
  variable I::Real if with_I
  "State of filtered derivative"
  variable D::Real if with_D
  "Setpoint-weighted error for derivative"
  variable wde::Real if with_D
  "Feed-forward contribution to the control signal"
  variable uff::Real
  "Un-saturated output of the controller"
  variable v::Real
  "Error signal input to integrator including anti-windup tracking signal"
  variable eI::Real if with_I
  "Error signal"
  variable e::Real
relations
  y = clamp(v, y_min, y_max) # Saturated control signal (= controller output)
  e = u_s - u_m
  if with_ff
    uff = k_ff * u_ff # Feed-forward added before saturation
  else
    uff = 0
  end
  if with_I
    switch Imethod
      case Forward
        initial eI@(clk-1) = 0
        eI = e + (y - v) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * eI@(clk-1)
      case Backward
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        eI = e + (y@(clk-1) - v@(clk-1)) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * eI@clk
      case Trapezoidal
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        initial eI@(clk-1) = 0
        eI = e + (y@(clk-1) - v@(clk-1)) / (K * Ni)
        I@clk = I@(clk-1) + Ts / Ti * (eI@clk + eI@(clk-1)) / 2
    end
    switch initialization
      case InitialState
        initial I@(clk-1) = initialization.x0
      case InitialOutput
        # The derivative path starts at rest for this variant, so the filtered derivative
        # contributes nothing at the first sample and the requested output fixes the
        # integrator state after the first update to (y0 - uff)/K - (wp*u_s - u_m). The
        # stored previous-sample state is that value less the first update, which differs
        # between the discretization methods: Forward integrates the previous error sample,
        # initialized to zero, while Backward and Trapezoidal integrate the current one.
        switch Imethod
          case Forward
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m)
          case Backward
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m) - Ts / Ti * e
          case Trapezoidal
            initial I@(clk-1) = (initialization.y0 - uff) / K - (wp * u_s - u_m) - Ts / Ti * e / 2
        end
      case SteadyState
        initial I@(clk-1) = error("SteadyState initial condition is not supported for DiscretePIDStandard with with_I = true, an integrator has no unique steady state; use InitialOutput or InitialState")
    end
  end
  if with_D
    initial D@(clk-1) = 0
    initial wde@(clk-1) = wde_init_w * (wd * u_s - u_m)
    wde = wd * u_s - u_m
    switch Dmethod
      case Forward
        D@clk = (Td - Nd * Ts) / Td * D@(clk-1) + Nd * (wde@clk - wde@(clk-1))
      case Backward
        D@clk = Td / (Td + Nd * Ts) * D@(clk-1) + Nd * Td / (Td + Nd * Ts) * (wde@clk - wde@(clk-1))
      case Trapezoidal
        D@clk = (2 * Td - Nd * Ts) / (2 * Td + Nd * Ts) * D@(clk-1) + 2 * Nd * Td / (2 * Td + Nd * Ts) * (wde@clk - wde@(clk-1))
    end
  end
  if with_I and with_D
    v = K * ((wp * u_s - u_m) + I + D) + uff # Unsaturated control signal
  elseif with_I
    v = K * ((wp * u_s - u_m) + I) + uff # Unsaturated control signal
  elseif with_D
    v = K * ((wp * u_s - u_m) + D) + uff # Unsaturated control signal
  else
    v = K * (wp * u_s - u_m) + uff # Unsaturated control signal (pure proportional)
  end
metadata {
  "Dyad": {"icons": {"default": "dyad://DiscreteComponents/DiscretePIDStandard.svg"}}
}
end


Test Cases

No test cases defined.

  • Examples
  • Experiments
  • Analyses