Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DiscretePIDs"
uuid = "c1363496-6848-4723-8758-079b737f6baf"
authors = ["Fredrik Bagge Carlson"]
version = "0.1.7"
version = "1.0.0"

[deps]
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


This package implements a discrete-time PID controller as an approximation of the continuous-time PID controller given by
$$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}(w_d R(s) - Y(s)) \right) + U_\textrm{ff}(s),$$
$$U(s) = K \left( w_p R(s) - Y(s) + \dfrac{1}{sT_i} \left( R(s) - Y(s) \right) + \dfrac{sT_d}{1 + s T_d / N}(w_d R(s) - Y(s)) \right) + U_\textrm{ff}(s),$$
where
- $u(t) \leftrightarrow U(s)$ is the control signal
- $y(t) \leftrightarrow Y(s)$ is the measurement signal
Expand All @@ -15,7 +15,7 @@ where
- $T_i$ is the integral time
- $T_d$ is the derivative time
- $N$ is a parameter that limits the gain of the derivative term at high frequencies, typically ranges from 2 to 20,
- $b \in [0, 1]$ is a parameter that gives the proportion of the reference signal that appears in the proportional term.
- $w_p \in [0, 1]$ is a parameter that gives the proportion of the reference signal that appears in the proportional term.
- $w_d \in [0, 1]$ is a parameter that gives the proportion of the reference signal that appears in the derivative term (default 0).

*Saturation* of the controller output is parameterized by $u_{\min}$ and $u_{\max}$, and the integrator *anti-windup* is parameterized by the tracking time $T_\mathrm{t}$.
Expand All @@ -24,7 +24,7 @@ where

Construct a controller by
```julia
pid = DiscretePID(; K = 1, Ti = false, Td = false, Tt = √(Ti*Td), N = 10, b = 1, wd = 0, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)
pid = DiscretePID(; K = 1, Ti = false, Td = false, Tt = √(Ti*Td), N = 10, wp = 1, wd = 0, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)
```
and compute the control signal at a given time using
```julia
Expand Down Expand Up @@ -220,7 +220,7 @@ K (b r - y + 1/T_i (r - y) - s T_d y/(1 + s T_d / N))
using the function `K, Ti, Td = parallel2standard(kp, ki, kd)` or, if a filter parameter is included, `K, Ti, Td, N = parallel2standard(kp, ki, kd, Tf)`. This function also accepts a vector of parameters in the same order, in which case a vector is returned.

## Details
- The derivative term by default 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. Set `wd = 1` to let the derivative act on the error `r-y` instead. 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.
- The derivative term by default 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. Set `wd = 1` to let the derivative act on the error `r-y` instead. The parameter $w_p$ can further be set to zero to avoid step changes in the control signal in response to step changes in the reference.
- Bumpless transfer when updating `K` is realized by updating the state `I`. See the docs for `set_K!` for more details.
- The total control signal $u(t)$ (PID + feedforward) is limited by the integral anti-windup.
- The integrator is discretized using a forward difference (no direct term between the input and output through the integral state) while the derivative is discretized using a backward difference. This approximation has the advantage that it is always stable and that the sampled pole goes to zero when $T_d$ goes to zero. Tustin's approximation gives an approximation such that the pole instead goes to $z = −1$ as $T_d$ goes to zero.
Expand Down
29 changes: 17 additions & 12 deletions src/DiscretePIDs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mutable struct DiscretePID{T} <: Function
"Maximum derivative gain"
const N::T
"Fraction of set point in prop. term"
b::T
wp::T
"Fraction of set point in derivative term"
wd::T
"Low output limit"
Expand All @@ -41,7 +41,7 @@ mutable struct DiscretePID{T} <: Function
end

"""
DiscretePID(; K = 1, Ti = false, Td = false, Tt = √(Ti*Td), N = 10, b = 1, wd = 0, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)
DiscretePID(; K = 1, Ti = false, Td = false, Tt = √(Ti*Td), N = 10, wp = 1, wd = 0, umin = -Inf, umax = Inf, Ts, I = 0, D = 0, yold = 0)

A discrete-time PID controller with set-point weighting and integrator anti-windup.
The controller is implemented on the standard form
Expand All @@ -50,7 +50,7 @@ u = K \\left( e + \\dfrac{1}{Ti} \\int e dt + T_d \\dfrac{de}{dt} \\right)
```

```math
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) - w_d R(s))
U(s) = K \\left( wp R(s) - Y(s) + \\dfrac{1}{sT_i} \\left( R(s) Y(s) \\right) - \\dfrac{sT_d}{1 + s T_d / N}(Y(s) - w_d R(s))
```

Call the controller like this
Expand All @@ -65,7 +65,7 @@ u = calculate_control!(pid, r, y, uff) # Equivalent to the above
- `Td`: Derivative time
- `Tt`: Reset time for anti-windup
- `N`: Maximum derivative gain
- `b`: Fraction of set point in proportional term
- `wp`: Fraction of set point in proportional term
- `wd`: Fraction of set point in derivative term (default 0)
- `umin`: Low output limit
- `umax`: High output limit
Expand All @@ -82,8 +82,9 @@ function DiscretePID(;
Td = false,
Tt = Ti > 0 && Td > 0 ? typeof(K)(√(Ti*Td)) : typeof(K)(10),
N = typeof(K)(10),
b = typeof(K)(1),
wp = typeof(K)(1),
wd = zero(typeof(K)),
b = nothing,
umin = typemin(typeof(K)),
umax = typemax(typeof(K)),
Ts,
Expand All @@ -96,10 +97,14 @@ function DiscretePID(;
else
bi = zero(K * Ts)
end
if b !== nothing
wp = typeof(K)(b)
@warn "Parameter `b` is deprecated. Use `wp` instead."
end
Tt ≥ 0 || throw(ArgumentError("Tt must be positive"))
Td ≥ 0 || throw(ArgumentError("Td must be positive"))
N ≥ 0 || throw(ArgumentError("N must be positive"))
0 ≤ b ≤ 1 || throw(ArgumentError("b must be ∈ [0, 1]"))
0 ≤ wp ≤ 1 || throw(ArgumentError("wp must be ∈ [0, 1]"))
0 ≤ wd ≤ 1 || throw(ArgumentError("wd must be ∈ [0, 1]"))
umax > umin || throw(ArgumentError("umax must be greater than umin"))

Expand All @@ -111,9 +116,9 @@ function DiscretePID(;
ad = Td / (Td + N * Ts)
bd = K * N * ad

T2 = promote_type(typeof.((K, Ti, Td, Tt, N, b, wd, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)
T2 = promote_type(typeof.((K, Ti, Td, Tt, N, wp, wd, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)

DiscretePID(T2.((K, Ti, Td, Tt, N, b, wd, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)
DiscretePID(T2.((K, Ti, Td, Tt, N, wp, wd, umin, umax, Ts, bi, ar, bd, ad, I, D, yold))...)
end

"""
Expand All @@ -129,7 +134,7 @@ function set_K!(pid::DiscretePID, K, r, y)
pid.bd = K * pid.N * pid.ad
if pid.Ti > 0
pid.bi = K * pid.Ts / pid.Ti
pid.I = pid.I + Kold*(pid.b*r - y) - K*(pid.b*r - y)
pid.I = pid.I + Kold*(pid.wp*r - y) - K*(pid.wp*r - y)
end
nothing
end
Expand Down Expand Up @@ -177,7 +182,7 @@ function calculate_control!(pid::DiscretePID{T}, r0, y0, uff0=0; yd=nothing) whe
r = T(r0)
y = T(y0)
uff = T(uff0)
P = pid.K * (pid.b * r - y)
P = pid.K * (pid.wp * r - y)
e = pid.wd * r - y # weighted error for derivative
if yd === nothing
pid.D = pid.ad * pid.D + pid.bd * (e - pid.yold)
Expand Down Expand Up @@ -233,10 +238,10 @@ end
K, Ti, Td, N = parallel2standard(Kp, Ki, Kd, Tf)

Convert parameters from form "parallel" form with first-order filter
``K_p (br-y) + K_i (r-y)/s - K_d s y/(Tf s + 1)``
``K_p (w_p r-y) + K_i (r-y)/s - K_d s y/(Tf s + 1)``

to "standard" form used in DiscretePID:
``K (br-y + (r-y)/(T_i s) - T_d s y/(T_d / N s + 1))``
``K (w_p r-y + (r-y)/(T_i s) - T_d s y/(T_d / N s + 1))``

You may provide either four arguments or an array with four elements in the same order.
"""
Expand Down
4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ res3 = lsim(P, ctrl, Tf)
## PI control with sp weighting
Tf = 10
Ti = 1
b = 0.0
pid = DiscretePID(; K, Ts, Ti, b)
wp = 0.0
pid = DiscretePID(; K, Ts, Ti, wp)
ctrl = function(x,t)
y = (P.C*x)[]
r = 1
Expand Down
Loading