Skip to content
LIBRARY
DiscretePIDParallel.md

DiscretePIDParallel ​

Discrete-time PID controller on parallel form with anti-windup and set-point weighting.

The controller is implemented on parallel form:

Simplified:

Detailed:

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 can be added to the control signal by setting with_ff = true and connecting the u_ff input. Its contribution 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 .

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

  • Imethod = DiscretizationMethod.Forward (default): Corresponding to the transfer function  

  • Imethod = DiscretizationMethod.Backward: Corresponding to the transfer function  

  • Imethod = DiscretizationMethod.Trapezoidal: Corresponding to the transfer function   

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

  • Dmethod = DiscretizationMethod.Forward: Corresponding to the transfer function     .

  • Dmethod = DiscretizationMethod.Backward (default): Corresponding to the transfer function       

  • Dmethod = DiscretizationMethod.Trapezoidal: Corresponding to the transfer function       

Anti windup is realized by tracking using the gain on the error signal when the output is saturated.

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.DiscretePIDParallel(kp=1, ki=1, kd=1, Ni=sqrt(max(kd * ki, 1e-6)), Nd=10 * kp, 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()
kpProportional gain–1
kiIntegral gain (only active if with_I = true)–1
kdDerivative gain (only active if with_D = true)–1
NiAnti-windup gain (only active if with_I = true)–sqrt(max(kd * ki, 1e-6))
NdMaximum derivative gain (only active if with_D = true). Typically set to 10-100 times the proportional gain.–10 * kp
y_maxMaximum output–Inf
y_minMinimum output–ifelse(y_ma..._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 ​

Source ​

dyad
"""
Discrete-time PID controller on parallel form with anti-windup and set-point weighting.

The controller is implemented on parallel form:

Simplified:
```math
u = k_p e + \\int k_i e dt + k_d \\dfrac{de}{dt}
```

Detailed:
```math
u = k_p(w_p r - y) + \\int \\big( k_i (r - y) + N_i e_s \\big ) dt + k_d \\dfrac{d}{dt}(w_d r - y) + 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{k_d-N T_s}{k_d}}``.
- `Dmethod = DiscretizationMethod.Backward` (default): Corresponding to the transfer function ``\\dfrac{\\dfrac{Nk_d}{k_d + N T_s}(z-1)}{z - \\dfrac{k_d}{k_d + N T_s}}``
- `Dmethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``\\dfrac{\\dfrac{2Nk_d}{2k_d + N T_s}(z-1)}{z - \\dfrac{2k_d - N T_s}{2k_d + N T_s}}``

Anti windup is realized by tracking using the gain ``N_i`` on the error signal ``e_s`` when the output is saturated.

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 DiscretePIDParallel@[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": 180, "x2": 0, "y2": 280, "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()
  "Proportional gain"
  parameter kp::Real = 1
  "Integral gain (only active if `with_I = true`)"
  parameter ki::Real = 1
  "Derivative gain (only active if `with_D = true`)"
  parameter kd::Real = 1
  "Anti-windup gain (only active if `with_I = true`)"
  parameter Ni::Real = sqrt(max(kd * ki, 1e-6))
  "Maximum derivative gain (only active if `with_D = true`). Typically set to 10-100 times the proportional gain."
  parameter Nd::Real = 10 * kp
  "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
    initial I@(clk-1) = 0
    switch Imethod
      case Forward
        initial eI@(clk-1) = 0
        eI = e + Ni * (y - v) # Add anti-windup tracking signal to error before integration
        I@clk = I@(clk-1) + Ts * ki * eI@(clk-1)
      case Backward
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        # Use previous-sample u and v to break the algebraic loop v -> u -> eI -> I -> v through clamp
        eI = e + Ni * (y@(clk-1) - v@(clk-1))
        I@clk = I@(clk-1) + Ts * ki * eI@clk
      case Trapezoidal
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        initial eI@(clk-1) = 0
        # Use previous-sample u and v to break the algebraic loop v -> u -> eI -> I -> v through clamp
        eI = e + Ni * (y@(clk-1) - v@(clk-1))
        I@clk = I@(clk-1) + Ts * ki * (eI@clk + eI@(clk-1)) / 2
    end
  end
  if with_D
    initial D@(clk-1) = 0
    initial wde@(clk-1) = 0
    wde = wd * u_s - u_m
    switch Dmethod
      case Forward
        D@clk = (kd - Nd * Ts) / kd * D@(clk-1) + Nd * (wde@clk - wde@(clk-1))
      case Backward
        D@clk = kd / (kd + Nd * Ts) * D@(clk-1) + Nd * kd / (kd + Nd * Ts) * (wde@clk - wde@(clk-1))
      case Trapezoidal
        D@clk = (2 * kd - Nd * Ts) / (2 * kd + Nd * Ts) * D@(clk-1) + 2 * Nd * kd / (2 * kd + Nd * Ts) * (wde@clk - wde@(clk-1))
    end
  end
  if with_I and with_D
    v = kp * (wp * u_s - u_m) + I + D + uff # Unsaturated control signal
  elseif with_I
    v = kp * (wp * u_s - u_m) + I + uff # Unsaturated control signal
  elseif with_D
    v = kp * (wp * u_s - u_m) + D + uff # Unsaturated control signal
  else
    v = kp * (wp * u_s - u_m) + uff # Unsaturated control signal (pure proportional)
  end
metadata {
  "Dyad": {"icons": {"default": "dyad://DiscreteComponents/DiscretePIDParallel.svg"}}
}
end
Flattened Source
dyad
"""
Discrete-time PID controller on parallel form with anti-windup and set-point weighting.

The controller is implemented on parallel form:

Simplified:
```math
u = k_p e + \\int k_i e dt + k_d \\dfrac{de}{dt}
```

Detailed:
```math
u = k_p(w_p r - y) + \\int \\big( k_i (r - y) + N_i e_s \\big ) dt + k_d \\dfrac{d}{dt}(w_d r - y) + 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{k_d-N T_s}{k_d}}``.
- `Dmethod = DiscretizationMethod.Backward` (default): Corresponding to the transfer function ``\\dfrac{\\dfrac{Nk_d}{k_d + N T_s}(z-1)}{z - \\dfrac{k_d}{k_d + N T_s}}``
- `Dmethod = DiscretizationMethod.Trapezoidal`: Corresponding to the transfer function ``\\dfrac{\\dfrac{2Nk_d}{2k_d + N T_s}(z-1)}{z - \\dfrac{2k_d - N T_s}{2k_d + N T_s}}``

Anti windup is realized by tracking using the gain ``N_i`` on the error signal ``e_s`` when the output is saturated.

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 DiscretePIDParallel
  "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": 180, "x2": 0, "y2": 280, "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()
  "Proportional gain"
  parameter kp::Real = 1
  "Integral gain (only active if `with_I = true`)"
  parameter ki::Real = 1
  "Derivative gain (only active if `with_D = true`)"
  parameter kd::Real = 1
  "Anti-windup gain (only active if `with_I = true`)"
  parameter Ni::Real = sqrt(max(kd * ki, 1e-6))
  "Maximum derivative gain (only active if `with_D = true`). Typically set to 10-100 times the proportional gain."
  parameter Nd::Real = 10 * kp
  "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
    initial I@(clk-1) = 0
    switch Imethod
      case Forward
        initial eI@(clk-1) = 0
        eI = e + Ni * (y - v) # Add anti-windup tracking signal to error before integration
        I@clk = I@(clk-1) + Ts * ki * eI@(clk-1)
      case Backward
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        # Use previous-sample u and v to break the algebraic loop v -> u -> eI -> I -> v through clamp
        eI = e + Ni * (y@(clk-1) - v@(clk-1))
        I@clk = I@(clk-1) + Ts * ki * eI@clk
      case Trapezoidal
        initial y@(clk-1) = 0
        initial v@(clk-1) = 0
        initial eI@(clk-1) = 0
        # Use previous-sample u and v to break the algebraic loop v -> u -> eI -> I -> v through clamp
        eI = e + Ni * (y@(clk-1) - v@(clk-1))
        I@clk = I@(clk-1) + Ts * ki * (eI@clk + eI@(clk-1)) / 2
    end
  end
  if with_D
    initial D@(clk-1) = 0
    initial wde@(clk-1) = 0
    wde = wd * u_s - u_m
    switch Dmethod
      case Forward
        D@clk = (kd - Nd * Ts) / kd * D@(clk-1) + Nd * (wde@clk - wde@(clk-1))
      case Backward
        D@clk = kd / (kd + Nd * Ts) * D@(clk-1) + Nd * kd / (kd + Nd * Ts) * (wde@clk - wde@(clk-1))
      case Trapezoidal
        D@clk = (2 * kd - Nd * Ts) / (2 * kd + Nd * Ts) * D@(clk-1) + 2 * Nd * kd / (2 * kd + Nd * Ts) * (wde@clk - wde@(clk-1))
    end
  end
  if with_I and with_D
    v = kp * (wp * u_s - u_m) + I + D + uff # Unsaturated control signal
  elseif with_I
    v = kp * (wp * u_s - u_m) + I + uff # Unsaturated control signal
  elseif with_D
    v = kp * (wp * u_s - u_m) + D + uff # Unsaturated control signal
  else
    v = kp * (wp * u_s - u_m) + uff # Unsaturated control signal (pure proportional)
  end
metadata {
  "Dyad": {"icons": {"default": "dyad://DiscreteComponents/DiscretePIDParallel.svg"}}
}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses