DiscretePIDScheduled Icon
DiscretePIDScheduled
Discrete-time PID controller on standard (ideal) form whose gains are input signals, intended for gain scheduling.
The control law is that of DiscretePIDStandard,
\[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. The gain $K$, the integral time $T_i$ and the derivative time $T_d$ are clocked input signals rather than parameters. The component contains no scheduling logic; the gains are computed by an external component from the scheduling variables, e.g., as a function of the operating point of the plant, and connected to the inputs K, Ti and Td.
The integrator state $I$ and the filtered derivative $D$ are expressed in the unit of the control signal, i.e., the gain $K$ multiplies the increments of $I$ and $D$ rather than the state variables themselves:
\[v = K (w_p r - y) + I + D + k_{ff} u_{ff}, \qquad I_{k+1} = I_k + T_s \left( \dfrac{K}{T_i} e + \dfrac{e_s}{T_i N_i} \right)\]
shown here for the forward discretization of the integrator. For constant gain inputs the controller is identical to DiscretePIDStandard with the same gains. When the gains change, the accumulated integral and derivative contributions to the control signal remain unchanged, so a change of the gains does not by itself cause a step in the control signal. A change of $K$ only alters the proportional contribution $K (w_p r - y)$ immediately; the anti-windup term does not divide by $K$, so $K = 0$ is admissible. With $w_p < 1$ the proportional contribution contains the term $-(1 - w_p) K y$, which varies with $K$ also when the control error is zero; while $K$ varies continuously, the integral part compensates this variation at the expense of a small control error.
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 coefficients of the derivative filter are evaluated with the current value of the input $T_d$ at each sample.
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$.
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. Since $I$ is expressed in the unit of the control signal,x0is the initial integral contribution to the control signal, which differs from the convention ofDiscretePIDStandardby the factor $K$.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, evaluated with the gain inputs at the first sample. 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.DiscretePIDScheduled(wde_init_w=_pid_wde_init_weight(initialization), 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) |
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)K- This connector represents a real signal as an input to a component (RealInput)Ti- This connector represents a real signal as an input to a component (RealInput)Td- 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 | Integral contribution to the control signal | – |
D | Filtered derivative contribution to the control signal | – |
wde | Setpoint-weighted error for derivative | – |
uff | Feed-forward contribution to the control signal | – |
v | Un-saturated output of the controller | – |
eI | Rate of change of the integral contribution, including the anti-windup tracking signal | – |
e | Error signal | – |
Behavior
using DiscreteComponents #hide
using ModelingToolkit #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.DiscretePIDScheduled(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 whose gains are input signals, intended for gain scheduling.
The control law is that of `DiscretePIDStandard`,
```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. The gain ``K``, the integral time ``T_i`` and the derivative time ``T_d`` are clocked input signals rather than parameters. The component contains no scheduling logic; the gains are computed by an external component from the scheduling variables, e.g., as a function of the operating point of the plant, and connected to the inputs `K`, `Ti` and `Td`.
The integrator state ``I`` and the filtered derivative ``D`` are expressed in the unit of the control signal, i.e., the gain ``K`` multiplies the increments of ``I`` and ``D`` rather than the state variables themselves:
```math
v = K (w_p r - y) + I + D + k_{ff} u_{ff}, \qquad I_{k+1} = I_k + T_s \left( \dfrac{K}{T_i} e + \dfrac{e_s}{T_i N_i} \right)
```
shown here for the forward discretization of the integrator. For constant gain inputs the controller is identical to `DiscretePIDStandard` with the same gains. When the gains change, the accumulated integral and derivative contributions to the control signal remain unchanged, so a change of the gains does not by itself cause a step in the control signal. A change of ``K`` only alters the proportional contribution ``K (w_p r - y)`` immediately; the anti-windup term does not divide by ``K``, so ``K = 0`` is admissible. With ``w_p < 1`` the proportional contribution contains the term ``-(1 - w_p) K y``, which varies with ``K`` also when the control error is zero; while ``K`` varies continuously, the integral part compensates this variation at the expense of a small control error.
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 coefficients of the derivative filter are evaluated with the current value of the input ``T_d`` at each sample.
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``.
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. Since ``I`` is expressed in the unit of the control signal, `x0` is the initial integral contribution to the control signal, which differs from the convention of `DiscretePIDStandard` by the factor ``K``.
- `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`, evaluated with the gain inputs at the first sample. 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 DiscretePIDScheduled@[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": []
}
}
"Proportional gain"
K = RealInput@[clk]() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 200, "y1": -100, "x2": 300, "y2": 0, "rot": 90}
},
"tags": []
}
}
"Integral time constant (only active if `with_I = true`)"
Ti = RealInput@[clk]() if with_I {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 450, "y1": -100, "x2": 550, "y2": 0, "rot": 90}
},
"tags": []
}
}
"Derivative time (only active if `with_D = true`)"
Td = RealInput@[clk]() if with_D {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 700, "y1": -100, "x2": 800, "y2": 0, "rot": 90}
},
"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)
"`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
"Integral contribution to the control signal"
variable I::Real if with_I
"Filtered derivative contribution to the control signal"
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
"Rate of change of the integral contribution, including the 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 = K / Ti * e + (y - v) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * eI@(clk-1)
case Backward
initial y@(clk-1) = 0
initial v@(clk-1) = 0
eI = K / Ti * e + (y@(clk-1) - v@(clk-1)) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * eI@clk
case Trapezoidal
initial y@(clk-1) = 0
initial v@(clk-1) = 0
initial eI@(clk-1) = 0
eI = K / Ti * e + (y@(clk-1) - v@(clk-1)) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * (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 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 as in DiscretePIDStandard.
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 * K / Ti * e
case Trapezoidal
initial I@(clk-1) = initialization.y0 - uff - K * (wp * u_s - u_m) - Ts * K / Ti * e / 2
end
case SteadyState
initial I@(clk-1) = error("SteadyState initial condition is not supported for DiscretePIDScheduled 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) + K * Nd * (wde@clk - wde@(clk-1))
case Backward
D@clk = Td / (Td + Nd * Ts) * D@(clk-1) + K * 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 * K * 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/DiscretePIDScheduled.svg"}}
}
end
Flattened Source
"""
Discrete-time PID controller on standard (ideal) form whose gains are input signals, intended for gain scheduling.
The control law is that of `DiscretePIDStandard`,
```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. The gain ``K``, the integral time ``T_i`` and the derivative time ``T_d`` are clocked input signals rather than parameters. The component contains no scheduling logic; the gains are computed by an external component from the scheduling variables, e.g., as a function of the operating point of the plant, and connected to the inputs `K`, `Ti` and `Td`.
The integrator state ``I`` and the filtered derivative ``D`` are expressed in the unit of the control signal, i.e., the gain ``K`` multiplies the increments of ``I`` and ``D`` rather than the state variables themselves:
```math
v = K (w_p r - y) + I + D + k_{ff} u_{ff}, \qquad I_{k+1} = I_k + T_s \left( \dfrac{K}{T_i} e + \dfrac{e_s}{T_i N_i} \right)
```
shown here for the forward discretization of the integrator. For constant gain inputs the controller is identical to `DiscretePIDStandard` with the same gains. When the gains change, the accumulated integral and derivative contributions to the control signal remain unchanged, so a change of the gains does not by itself cause a step in the control signal. A change of ``K`` only alters the proportional contribution ``K (w_p r - y)`` immediately; the anti-windup term does not divide by ``K``, so ``K = 0`` is admissible. With ``w_p < 1`` the proportional contribution contains the term ``-(1 - w_p) K y``, which varies with ``K`` also when the control error is zero; while ``K`` varies continuously, the integral part compensates this variation at the expense of a small control error.
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 coefficients of the derivative filter are evaluated with the current value of the input ``T_d`` at each sample.
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``.
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. Since ``I`` is expressed in the unit of the control signal, `x0` is the initial integral contribution to the control signal, which differs from the convention of `DiscretePIDStandard` by the factor ``K``.
- `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`, evaluated with the gain inputs at the first sample. 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 DiscretePIDScheduled
"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": []
}
}
"Proportional gain"
K = RealInput@[clk]() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 200, "y1": -100, "x2": 300, "y2": 0, "rot": 90}
},
"tags": []
}
}
"Integral time constant (only active if `with_I = true`)"
Ti = RealInput@[clk]() if with_I {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 450, "y1": -100, "x2": 550, "y2": 0, "rot": 90}
},
"tags": []
}
}
"Derivative time (only active if `with_D = true`)"
Td = RealInput@[clk]() if with_D {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 700, "y1": -100, "x2": 800, "y2": 0, "rot": 90}
},
"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)
"`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
"Integral contribution to the control signal"
variable I::Real if with_I
"Filtered derivative contribution to the control signal"
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
"Rate of change of the integral contribution, including the 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 = K / Ti * e + (y - v) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * eI@(clk-1)
case Backward
initial y@(clk-1) = 0
initial v@(clk-1) = 0
eI = K / Ti * e + (y@(clk-1) - v@(clk-1)) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * eI@clk
case Trapezoidal
initial y@(clk-1) = 0
initial v@(clk-1) = 0
initial eI@(clk-1) = 0
eI = K / Ti * e + (y@(clk-1) - v@(clk-1)) / (Ti * Ni)
I@clk = I@(clk-1) + Ts * (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 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 as in DiscretePIDStandard.
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 * K / Ti * e
case Trapezoidal
initial I@(clk-1) = initialization.y0 - uff - K * (wp * u_s - u_m) - Ts * K / Ti * e / 2
end
case SteadyState
initial I@(clk-1) = error("SteadyState initial condition is not supported for DiscretePIDScheduled 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) + K * Nd * (wde@clk - wde@(clk-1))
case Backward
D@clk = Td / (Td + Nd * Ts) * D@(clk-1) + K * 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 * K * 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/DiscretePIDScheduled.svg"}}
}
endTest Cases
No test cases defined.
Related
- Examples
- Experiments
- Analyses