Skip to content

Commit 5fb157e

Browse files
mzaffalonMichele Zaffalonbaggepinnen
authored
Add a feed-forward term (#3)
* Add a feed-forward term The total control signal (PID + feed-forward) must be limited by the integral anti-windup. * Update src/DiscretePIDs.jl Co-authored-by: Fredrik Bagge Carlson <[email protected]> * Update README.md Co-authored-by: Fredrik Bagge Carlson <[email protected]> * Improve text format Co-authored-by: Michele Zaffalon <[email protected]> Co-authored-by: Fredrik Bagge Carlson <[email protected]>
1 parent aa80587 commit 5fb157e

File tree

2 files changed

+19
-17
lines changed

2 files changed

+19
-17
lines changed

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66

77
This package implements a discrete-time PID controller on the form
8-
$$U(s) = K \left( bR(s) - Y(s) + \dfrac{1}{sT_i} \left( R(s) - Y(s) \right) - \dfrac{sT_d}{1 + s T_d / N}Y(s) \right)$$
8+
$$U(s) = K \left( bR(s) - Y(s) + \dfrac{1}{sT_i} \left( R(s) - Y(s) \right) - \dfrac{sT_d}{1 + s T_d / N}Y(s) \right) + U_\textrm{ff}(s)$$
99

1010
where
1111
- $u(t) \leftrightarrow U(s)$ is the control signal
1212
- $y(t) \leftrightarrow Y(s)$ is the measurement signal
1313
- $r(t) \leftrightarrow R(s)$ is the reference / set point
14+
- $u_\textrm{ff}(t) \leftrightarrow u_\textrm{ff}(s)$ is the feed-forward contribution
1415
- $K$ is the proportional gain
1516
- $T_i$ is the integral time
1617
- $T_d$ is the derivative time
@@ -19,17 +20,17 @@ where
1920

2021
The controller further has output saturation controlled by `umin, umax` and integrator anti-windup controlled by the tracking time $T_t$.
2122

22-
Construct a controller using
23+
Construct a controller using
2324
```julia
2425
pid = DiscretePID(; K = 1, Ti = false, Td = false, Tt = (Ti*Td), N = 10, b = 1, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)
2526
```
26-
and compute a control signal using
27+
and compute a control signal using
2728
```julia
28-
u = pid(r, y)
29+
u = pid(r, y, uff)
2930
```
3031
or
3132
```julia
32-
u = calculate_control!(pid, r, y)
33+
u = calculate_control!(pid, r, y, uff)
3334
```
3435

3536
The parameters $K, T_i, T_d$ may be updated using the functions, `set_K!, set_Ti!, set_Td!`.
@@ -40,7 +41,7 @@ The following example simulates the PID controller using ControlSystems.jl. We w
4041
```julia
4142
using DiscretePIDs, ControlSystems, Plots
4243
Tf = 15 # Simulation time
43-
K = 1
44+
K = 1
4445
Ti = 1
4546
Td = 1
4647
Ts = 0.01 # sample time
@@ -50,7 +51,7 @@ pid = DiscretePID(; K, Ts, Ti, Td)
5051

5152
ctrl = function(x,t)
5253
y = (P.C*x)[] # measurement
53-
d = 1 # disturbance
54+
d = 1 # disturbance
5455
r = 0 # reference
5556
u = pid(r, y)
5657
u + d # Plant input is control signal + disturbance
@@ -64,4 +65,5 @@ plot(res, plotu=true); ylabel!("u + d", sp=2)
6465

6566
## Details
6667
- The derivative term only acts on the (filtered) measurement and not the command signal. It is thus safe to pass step changes in the reference to the controller. The parameter $b$ can further be set to zero to avoid step changes in the control signal in response to step changes in the reference.
67-
- Bumpless transfer when updating `K` is realized by updating the state `I`. See the docs for `set_K!` for more details.
68+
- Bumpless transfer when updating `K` is realized by updating the state `I`. See the docs for `set_K!` for more details.
69+
- The total control signal $u(t)$ (PID + feed-forward) is limited by the integral anti-windup.

src/DiscretePIDs.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ end
2424
"""
2525
DiscretePID(; K = 1, Ti = false, Td = false, Tt = √(Ti*Td), N = 10, b = 1, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)
2626
27-
A discrete-time PID controller with set-point weighting and integrator anti-windup.
27+
A discrete-time PID controller with set-point weighting and integrator anti-windup.
2828
The controller is implemented on the standard form
2929
```math
3030
u = K \\left( e + \\dfrac{1}{Ti} \\int e dt + T_d \\dfrac{de}{dt} \\right)
@@ -74,13 +74,13 @@ function DiscretePID(;
7474
N 0 || throw(ArgumentError("N must be positive"))
7575
0 b 1 || throw(ArgumentError("b must be ∈ [0, 1]"))
7676
umax > umin || throw(ArgumentError("umax must be greater than umin"))
77-
77+
7878
ar = Ts / Tt
7979
ad = Td / (Td + N * Ts)
8080
bd = K * N * ad
81-
81+
8282
T = promote_type(typeof.((K, Ti, Td, Tt, N, b, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)
83-
83+
8484
DiscretePID(T.((K, Ti, Td, Tt, N, b, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)
8585
end
8686

@@ -128,15 +128,15 @@ end
128128

129129

130130
"""
131-
u = calculate_control!(pid::DiscretePID, r, y)
132-
(pid)(r, y) # Alternative syntax
131+
u = calculate_control!(pid::DiscretePID, r, y, uff=0)
132+
(pid)(r, y, uff=0) # Alternative syntax
133133
134-
Calculate the control output from the PID controller when `r` is the reference (set point) and `y` is the latest measurement.
134+
Calculate the control output from the PID controller when `r` is the reference (set point), `y` is the latest measurement and `uff` is the feed-forward contribution.
135135
"""
136-
function calculate_control!(pid::DiscretePID, r, y)
136+
function calculate_control!(pid::DiscretePID, r, y, uff=0)
137137
P = pid.K * (pid.b * r - y)
138138
pid.D = pid.ad * pid.D - pid.bd * (y - pid.yold)
139-
v = P + pid.I + pid.D
139+
v = P + pid.I + pid.D + uff
140140
u = clamp(v, pid.umin, pid.umax)
141141
pid.I = pid.I + pid.bi * (r - y) + pid.ar * (u - v)
142142
pid.yold = y

0 commit comments

Comments
 (0)