Skip to content

Commit 5f7e051

Browse files
committed
revert selected changes
1 parent 62f574f commit 5f7e051

File tree

1 file changed

+73
-71
lines changed

1 file changed

+73
-71
lines changed

README.md

Lines changed: 73 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
This package implements a discrete-time PID controller as an approximation of the continuous-time PID controller given by
88
$$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
where
10-
- $U(s)$ is the Laplace transform of the *control variable* (also called *manipulated variable*) $u(t)$,
11-
- $Y(s)$ is the Laplace transform of the *measured output variable* (also called *process variable*) $y(t)$,
12-
- $R(s)$ is the Laplace transform of the *reference variable* (also called *set point*) $r(t)$,
13-
- $U_\textrm{ff}(s)$ is the Laplace transform of the *feedforward* contribution to the control variable $u_\textrm{ff}(t)$,
14-
- $K$ is the *proportional gain*,
15-
- $T_\mathrm{i}$ is the *integral time*,
16-
- $T_\mathrm{d}$ is the *derivative time*,
10+
- $u(t) \leftrightarrow U(s)$ is the control signal
11+
- $y(t) \leftrightarrow Y(s)$ is the measurement signal
12+
- $r(t) \leftrightarrow R(s)$ is the reference / set point
13+
- $u_\textrm{ff}(t) \leftrightarrow U_\textrm{ff}(s)$ is the feed-forward contribution
14+
- $K$ is the proportional gain
15+
- $T_i$ is the integral time
16+
- $T_d$ is the derivative time
1717
- $N$ is a parameter that limits the gain of the derivative term at high frequencies, typically ranges from 2 to 20,
1818
- $b \in [0, 1]$ is a parameter that gives the proportion of the reference signal that appears in the proportional term.
1919

@@ -25,7 +25,7 @@ Construct a controller by
2525
```julia
2626
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)
2727
```
28-
and compute the control (the controller output) at a given time using
28+
and compute the control signal at a given time using
2929
```julia
3030
u = pid(r, y, uff)
3131
```
@@ -34,11 +34,11 @@ or
3434
u = calculate_control!(pid, r, y, uff)
3535
```
3636

37-
The parameters $K$, $T_\mathrm{i}$, and $T_\mathrm{d}$ may be updated using the functions `set_K!`, `set_Ti!`, and `set_Td!`, respectively.
37+
The parameters $K$, $T_i$, and $T_d$ may be updated using the functions `set_K!`, `set_Ti!`, and `set_Td!`, respectively.
3838

3939
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.
4040

41-
The *internal state* of the controller can be reset to zero using the function `reset_state!(pid)`. If repeated simulations using the same controller object are performed, the state should be reset between simulations.
41+
The **internal state** of the controller can be reset to zero using the function `reset_state!(pid)`. If repeated simulations using the same controller object are performed, the state should likely be reset between simulations.
4242

4343
## Examples
4444

@@ -48,24 +48,24 @@ The following example simulates a feedback control system containing a PID contr
4848

4949
```julia
5050
using DiscretePIDs, ControlSystemsBase, Plots
51-
Tf = 15 # Simulation time
52-
K = 1 # Proportional gain
53-
Ti = 1 # Integral time
54-
Td = 1 # Derivative time
55-
Ts = 0.01 # Sample time
51+
Tf = 15 # Simulation time
52+
K = 1 # Proportional gain
53+
Ti = 1 # Integral time
54+
Td = 1 # Derivative time
55+
Ts = 0.01 # sample time
5656

57-
P = c2d(ss(tf(1, [1, 1])), Ts) # Plant to be controlled, discretized using zero-order hold
57+
P = c2d(ss(tf(1, [1, 1])), Ts) # Process to be controlled, discretized using zero-order hold
5858
pid = DiscretePID(; K, Ts, Ti, Td)
5959

6060
ctrl = function(x,t)
61-
y = (P.C*x)[] # Measured output
62-
d = 1 # Disturbance
63-
r = 0 # Reference
64-
u = pid(r, y) # Control
65-
u + d # Plant input is control + disturbance
61+
y = (P.C*x)[] # measurement
62+
d = 1 # disturbance
63+
r = 0 # reference
64+
u = pid(r, y) # control signal
65+
u + d # Plant input is control signal + disturbance
6666
end
6767

68-
res = lsim(P, ctrl, Tf) # Simulate the closed-loop system
68+
res = lsim(P, ctrl, Tf)
6969

7070
plot(res, plotu=true); ylabel!("u + d", sp=2)
7171
```
@@ -79,52 +79,51 @@ This example is identical to the one above except for using [DifferentialEquatio
7979

8080
There are several different ways one could go about including a discrete-time controller in a continuous-time simulation, in particular, we must choose a way to store the computed control variable. Two common approaches are
8181

82-
1. We use a global variable into which we write the control variable at each discrete time step.
82+
1. We use a global variable into which we write the control signal at each discrete time step.
8383
2. We add an extra state variable to the system, and use it to store the control variable.
8484

8585
In this example we choose the latter approach, since it has the added benefit of adding the computed control variable to the solution object.
8686

87-
We use `DiffEqCallbacks.PeriodicCallback`, with which we perform the PID-controller update, and store the computed control variable in the extra state variable.
87+
We use `DiffEqCallbacks.PeriodicCallback`, in which we perform the PID-controller update, and store the computed control signal in the extra state variable.
8888

8989
```julia
9090
using DiscretePIDs, ControlSystemsBase, OrdinaryDiffEq, DiffEqCallbacks, Plots
9191

92-
Tf = 15 # Simulation time
93-
K = 1 # Proportional gain
94-
Ti = 1 # Integral time
95-
Td = 1 # Derivative time
96-
Ts = 0.01 # Sample time
92+
Tf = 15 # Simulation time
93+
K = 1 # Proportional gain
94+
Ti = 1 # Integral time
95+
Td = 1 # Derivative time
96+
Ts = 0.01 # sample time
9797

98-
P = ss(tf(1, [1, 1])) # Plant to be controlled in continuous time
99-
A, B, C, D = ssdata(P) # Extract the system matrices
98+
P = ss(tf(1, [1, 1])) # Process to be controlled in continuous time
99+
A, B, C, D = ssdata(P) # Extract the system matrices
100100
pid = DiscretePID(; K, Ts, Ti, Td)
101101

102102
function dynamics!(dxu, xu, p, t)
103-
A, B, C, r, d = p # Store the reference and disturbance in the parameter object
104-
x = xu[1:P.nx] # Extract the state
105-
u = xu[P.nx+1:end] # Extract the control variable
106-
dxu[1:P.nx] .= A*x .+ B*(u .+ d)# Plant input is control variable + disturbance
107-
dxu[P.nx+1:end] .= 0 # Control variable has no dynamics, it's updated by the callback
103+
A, B, C, r, d = p # We store the reference and disturbance in the parameter object
104+
x = xu[1:P.nx] # Extract the state
105+
u = xu[P.nx+1:end] # Extract the control signal
106+
dxu[1:P.nx] .= A*x .+ B*(u .+ d) # Plant input is control signal + disturbance
107+
dxu[P.nx+1:end] .= 0 # The control signal has no dynamics, it's updated by the callback
108108
end
109109

110110
cb = PeriodicCallback(Ts) do integrator
111-
p = integrator.p # Extract the parameter object from the integrator
112-
(; C, r, d) = p # Extract the reference and disturbance from the parameter object
113-
x = integrator.u[1:P.nx] # Extract the state (the integrator uses `u` to refer to the state, in control theory we typically name it `x`)
114-
y = (C*x)[] # Simulated measurement
115-
u = pid(r, y) # Compute the control variable
116-
integrator.u[P.nx+1:end] .= u # Update the control-signal state variable
111+
p = integrator.p # Extract the parameter object from the integrator
112+
(; C, r, d) = p # Extract the reference and disturbance from the parameter object
113+
x = integrator.u[1:P.nx] # Extract the state (the integrator uses the variable name `u` to refer to the state, in control theory we typically use the variable name `x`)
114+
y = (C*x)[] # Simulated measurement
115+
u = pid(r, y) # Compute the control signal
116+
integrator.u[P.nx+1:end] .= u # Update the control-signal state variable
117117
end
118118

119-
parameters = (; A, B, C, r=0, d=1) # Reference = 0, disturbance = 1
120-
xu0 = zeros(P.nx + P.nu) # Initial state of the system + control variables
121-
prob = ODEProblem(dynamics!, xu0, (0, Tf), parameters, callback=cb) # Reference = 0, disturbance = 1
119+
parameters = (; A, B, C, r=0, d=1) # reference = 0, disturbance = 1
120+
xu0 = zeros(P.nx + P.nu) # Initial state of the system + control signals
121+
prob = ODEProblem(dynamics!, xu0, (0, Tf), parameters, callback=cb) # reference = 0, disturbance = 1
122122
sol = solve(prob, Tsit5(), saveat=Ts)
123123

124124
plot(sol, layout=(2, 1), ylabel=["x" "u"], lab="")
125125
```
126-
127-
The figure should look more or less identical to the previous one, except that we plot the control variable $u$ instead of the combined input $u + d$ like we did above. Due to the fast sample rate $T_\mathrm{s}$, the control variable looks continuous, however, increase $T_s$ and you'll notice the zero-order-hold nature of $u$.
126+
The figure should look more or less identical to the one above, except that we plot the control signal $u$ instead of the combined input $u + d$ like we did above. Due to the fast sample rate $T_s$, the control signal looks continuous, however, increase $T_s$ and you'll notice the zero-order-hold nature of $u$.
128127

129128
### Example using SeeToDee.jl
130129

@@ -137,59 +136,58 @@ considers the continuous-time dynamical system modelled by
137136
```math
138137
\dot x(t) = f(x(t), u(t), p(t), t)
139138
```
140-
and at a given state $x$ and time $t$ and for a given control $u$, it computes an approximation $x^+$ to the state $x(t+T_\mathrm{s})$ at the next time step $t+T_\mathrm{s}$
139+
and at a given state $x$ and time $t$ and for a given control $u$, it computes an approximation $x^+$ to the state $x(t+T_s)$ at the next time step $t+T_s$
141140

142141
```math
143-
x(t+T_\mathrm{s}) \approx x^+ = \phi(x(t), u(t), p(t), t,T_\mathrm{s}).
142+
x(t+T_s) \approx x^+ = \phi(x(t), u(t), p(t), t,T_s).
144143
```
145144

146145
```julia
147146
using DiscretePIDs, ControlSystemsBase, SeeToDee, Plots
148-
149-
Tf = 15 # Simulation time
150-
K = 1 # Proportional gain
151-
Ti = 1 # Integral time
152-
Td = 1 # Derivative time
153-
Ts = 0.01 # Sample time
154-
P = ss(tf(1, [1, 1])) # Plant to be controlled in continuous time
155-
A,B,C = ssdata(P) # Extract the system matrices
156-
p = (; A, B, C, r=0, d=1) # Reference = 0, disturbance = 1
147+
Tf = 15 # Simulation time
148+
K = 1 # Proportional gain
149+
Ti = 1 # Integral time
150+
Td = 1 # Derivative time
151+
Ts = 0.01 # sample time
152+
P = ss(tf(1, [1, 1])) # Process to be controlled, in continuous time
153+
A,B,C = ssdata(P) # Extract the system matrices
154+
p = (; A, B, C, r=0, d=1) # reference = 0, disturbance = 1
157155

158156
pid = DiscretePID(; K, Ts, Ti, Td)
159157

160158
ctrl = function(x,p,t)
161-
y = (p.C*x)[] # Measurement output of the plant
159+
y = (p.C*x)[] # measurement
162160
pid(r, y)
163161
end
164162

165-
function dynamics(x, u, p, t) # This time we define the dynamics as a function of the state and the control
166-
A, B, C, r, d = p # Store the reference and disturbance in the parameter object
167-
A*x .+ B*(u .+ d) # Plant input is control variable + disturbance
163+
function dynamics(x, u, p, t) # This time we define the dynamics as a function of the state and control signal
164+
A, B, C, r, d = p # We store the reference and disturbance in the parameter object
165+
A*x .+ B*(u .+ d) # Plant input is control signal + disturbance
168166
end
169167
discrete_dynamics = SeeToDee.Rk4(dynamics, Ts) # Create a discrete-time dynamics function
170168

171-
x = zeros(P.nx) # Initial condition
172-
X, U = [], [] # To store the solution
173-
t = range(0, step=Ts, stop=Tf) # Time vector
169+
x = zeros(P.nx) # Initial condition
170+
X, U = [], [] # To store the solution
171+
t = range(0, step=Ts, stop=Tf) # Time vector
174172
for t = t
175173
u = ctrl(x, p, t)
176-
push!(U, u) # Save solution for plotting
174+
push!(U, u) # Save solution for plotting
177175
push!(X, x)
178176
x = discrete_dynamics(x, u, p, t) # Advance the state one step
179177
end
180178

181-
Xm = reduce(hcat, X)' # Reduce to from vector of vectors to matrix
182-
Ym = Xm*P.C' # Compute the output (same as state in this simple case)
179+
Xm = reduce(hcat, X)' # Reduce to from vector of vectors to matrix
180+
Ym = Xm*P.C' # Compute the output (same as state in this simple case)
183181
Um = reduce(hcat, U)'
184182

185183
plot(t, [Ym Um], layout=(2,1), ylabel = ["y" "u"], legend=false)
186184
```
187185
Once again, the output looks identical and is therefore omitted here.
188186

189187
## Details
190-
- 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 variable in response to step changes in the reference.
188+
- 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.
191189
- Bumpless transfer when updating `K` is realized by updating the state `I`. See the docs for `set_K!` for more details.
192-
- The total control variable $u(t)$ (PID + feedforward) is limited by the integral anti-windup.
190+
- The total control signal $u(t)$ (PID + feedforward) is limited by the integral anti-windup.
193191
- 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.
194192
- This particular implementation of a discrete-time PID controller is detailed in Chapter 8 of [Wittenmark, Björn, Karl-Erik Årzén, and Karl Johan Åström. ‘Computer Control: An Overview’. IFAC Professional Brief. International Federation of Automatic Control, 2002](https://www.ifac-control.org/publications/list-of-professional-briefs/pb_wittenmark_etal_final.pdf/view).
195193
- When used with input arguments of standard types, such as `Float64` or `Float32`, the controller is guaranteed not to allocate any memory or contain any dynamic dispatches. This analysis is carried out in the tests, and is performed using [AllocCheck.jl](https://github.com/JuliaLang/AllocCheck.jl).
@@ -198,7 +196,7 @@ Once again, the output looks identical and is therefore omitted here.
198196
If the controller is ultimately to be implemented on a platform without floating-point hardware, we can simulate how it will behave with fixed-point arithmetics using the [FixedPointNumbers.jl](https://github.com/francescoalemanno/FixedPoint.jl) package. The following example modifies the first example above and shows how to simulate the controller using 16-bit fixed-point arithmetics with 10 bits for the fractional part:
199197
```julia
200198
using FixedPointNumbers
201-
T = Fixed{Int16, 10} # 16-bit fixed-point with 10 bits for the fractional part
199+
T = Fixed{Int16, 10} # 16-bit fixed-point with 10 bits for the fractional part
202200
pid = DiscretePID(; K = T(K), Ts = T(Ts), Ti = T(Ti), Td = T(Td))
203201
res_fp = lsim(P, ctrl, Tf)
204202
plot([res, res_fp], plotu=true, lab=["Float64" "" string(T) ""]); ylabel!("u + d", sp=2)
@@ -238,6 +236,10 @@ calculate_control! returned: 3.000000
238236
calculate_control! returned: 3.000000
239237
```
240238

239+
240+
241+
242+
241243
## See also
242244
- [TrajectoryLimiters.jl](https://github.com/baggepinnen/TrajectoryLimiters.jl) To generate dynamically feasible reference trajectories with bounded velocity and acceleration given an instantaneous reference $r(t)$ which may change abruptly.
243245
- [SymbolicControlSystems.jl](https://github.com/JuliaControl/SymbolicControlSystems.jl) For C-code generation of LTI systems.

0 commit comments

Comments
 (0)