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 tox0and the derivative path starts from zero state, i.e., the stored previous sample of the setpoint-weighted error is zero. The defaultx0 = 0is 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 equalsy0. This provides bumpless transfer from an actuator already held aty0. Ify0lies outside[y_min, y_max], the first output is the saturated value. Withwith_I = falsethe controller has no integrator state and the requested output is not realizable; only the derivative path is affected.InitialCondition.SteadyState: not supported forwith_I = true, since an integrator has no unique steady state. Withwith_I = falsethe derivative path starts at rest, which is its steady state.- The array variants
InitialStateArrayandInitialOutputArrayare 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:
| Name | Description | Units | Default value |
|---|---|---|---|
Imethod | Discretization method for the integrator | – | Discretizat...d.Forward() |
Dmethod | Discretization method for the derivative | – | Discretizat....Backward() |
with_I | Whether or not to include the integral part | – | true |
with_D | Whether or not to include the derivative part | – | true |
with_ff | Whether or not to include the feed-forward input | – | false |
Ts | – | SampleTime() | |
initialization | Initial-condition specification | – | DiscreteCom...e(; x0=0.0) |
K | Proportional gain | – | 1 |
Ti | Integral time constant (only active if with_I = true) | – | 1 |
Td | Derivative time (only active if with_D = true) | – | 1 |
Ni | Ni*Ti is the time constant of the anti-windup compensation (only active if with_I = true) | – | 0.9 |
Nd | Maximum derivative gain (only active if with_D = true). Typically set to 10-100. | – | 10 |
y_max | Maximum output | – | Inf |
y_min | Minimum output | – | ifelse(yma...max, -Inf) |
wp | Set-point weighting in the proportional part. Set to 0 to prevent step changes in the output due to step changes in the reference. | – | 1 |
wd | 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. | – | 1 |
k_ff | Gain 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
| Name | Description | Units |
|---|---|---|
I | State of Integrator | – |
D | State of filtered derivative | – |
wde | Setpoint-weighted error for derivative | – |
uff | Feed-forward contribution to the control signal | – |
v | Un-saturated output of the controller | – |
eI | Error signal input to integrator including anti-windup tracking signal | – |
e | Error 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) #hideSource
"""
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"}}
}
endTest Cases
No test cases defined.
Related
- Examples
- Experiments
- Analyses