Skip to content

Commit 834ffa4

Browse files
committed
add support for externally sourced derivative
1 parent b86dc72 commit 834ffa4

File tree

4 files changed

+50
-4
lines changed

4 files changed

+50
-4
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DiscretePIDs"
22
uuid = "c1363496-6848-4723-8758-079b737f6baf"
33
authors = ["Fredrik Bagge Carlson"]
4-
version = "0.1.6"
4+
version = "0.1.7"
55

66
[deps]
77
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ or
3434
u = calculate_control!(pid, r, y, uff)
3535
```
3636

37+
The derivative term is by default computed by filtering the measurement $y$, but it can also be sourced externally by setting the keyword argument `yd`:
38+
```julia
39+
u = calculate_control!(pid, r, y, uff; yd)
40+
```
41+
When `yd` is provided, no filtering is applied by the PID controller, i.e., $N$ is ignored. This is useful when the derivative is computed externally, e.g., from a velocity sensor or an observer.
42+
3743
The parameters $K$, $T_i$, and $T_d$ may be updated using the functions `set_K!`, `set_Ti!`, and `set_Td!`, respectively.
3844

3945
The numeric type used by the controller (the `T` in `DiscretePID{T}`) is determined by the types of the parameters. To use a custom number type, e.g., a fixed-point number type, simply pass the parameters as that type, see example below. The controller will automatically convert measurements and references to this type before performing the control calculations.

src/DiscretePIDs.jl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,18 +160,24 @@ end
160160

161161

162162
"""
163-
u = calculate_control!(pid::DiscretePID, r, y, uff=0)
163+
u = calculate_control!(pid::DiscretePID, r, y, uff=0; yd = nothing)
164164
(pid)(r, y, uff=0) # Alternative syntax
165165
166166
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.
167167
If the type of the input arguments differ from the numeric type used by the PID controller, they will be converted before computations are performed.
168+
169+
The derivative term is by default computed by filtering the measurement `y`, but it can also be provided directly by setting the optional keyword argument `yd`. When `yd` is set, no filtering is applied by the PID controller, i.e., `N` is ignored.
168170
"""
169-
function calculate_control!(pid::DiscretePID{T}, r0, y0, uff0=0) where T
171+
function calculate_control!(pid::DiscretePID{T}, r0, y0, uff0=0; yd=nothing) where T
170172
r = T(r0)
171173
y = T(y0)
172174
uff = T(uff0)
173175
P = pid.K * (pid.b * r - y)
174-
pid.D = pid.ad * pid.D - pid.bd * (y - pid.yold)
176+
if yd === nothing
177+
pid.D = pid.ad * pid.D - pid.bd * (y - pid.yold)
178+
else
179+
pid.D = - pid.K * pid.Td * T(yd)
180+
end
175181
v = P + pid.I + pid.D + uff
176182
u = clamp(v, pid.umin, pid.umax)
177183
pid.I = pid.I + pid.bi * (r - y) + pid.ar * (u - v)

test/runtests.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,40 @@ res2 = lsim(P, ctrl, Tf)
9292
@test res.y res2.y rtol=0.02
9393
# plot([res, res2])
9494

95+
## PID control with external derivative (yd keyword)
96+
# Compare internal filtered derivative vs externally provided derivative
97+
pid_internal = DiscretePID(; K = 1.0*K, Ts, Ti, Td = 0.8*Td)
98+
pid_external = DiscretePID(; K = 1.0*K, Ts, Ti, Td = 0.8*Td)
99+
100+
yold_ext = 0.0
101+
ctrl_internal = function(x, t)
102+
y = (P.C*x)[]
103+
d = 1
104+
r = 0
105+
u = pid_internal(r, y)
106+
u + d
107+
end
108+
109+
ctrl_external = function(x, t)
110+
global yold_ext
111+
y = (P.C*x)[]
112+
d = 1
113+
r = 0
114+
yd = (y - yold_ext) / Ts # Compute raw derivative of -y
115+
yold_ext = y
116+
u = calculate_control!(pid_external, r, y; yd)
117+
u + d
118+
end
119+
120+
res_internal = lsim(P, ctrl_internal, Tf)
121+
reset_state!(pid_internal)
122+
reset_state!(pid_external)
123+
yold_ext = 0.0
124+
res_external = lsim(P, ctrl_external, Tf)
125+
126+
# External derivative is unfiltered, so allow larger tolerance
127+
@test res_internal.y res_external.y rtol=0.1
128+
95129
reset_state!(pid)
96130
res3 = lsim(P, ctrl, Tf)
97131
@test res3.y == res2.y

0 commit comments

Comments
 (0)