Skip to content

Commit 383ce4b

Browse files
authored
Merge pull request #39 from JuliaControl/doc_mistake
Minor doc correction and code cleanup
2 parents 90369ca + 96bed0e commit 383ce4b

File tree

11 files changed

+71
-75
lines changed

11 files changed

+71
-75
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
33
authors = ["Francis Gagnon"]
4-
version = "0.19.0"
4+
version = "0.19.1"
55

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"

docs/src/manual/nonlinmpc.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ The plant model is nonlinear:
3535

3636
in which ``g`` is the gravitational acceleration in m/s², ``L``, the pendulum length in m,
3737
``K``, the friction coefficient at the pivot point in kg/s, and ``m``, the mass attached at
38-
the end of the pendulum in kg. Here, a [fourth order Runge-Kutta method](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods)
39-
solves the differential equations to construct a [`NonLinModel`](@ref):
38+
the end of the pendulum in kg. The [`NonLinModel`](@ref) constructor assumes by default
39+
that the state function `f` is continuous in time, that is, an ordinary differential
40+
equation system (like here):
4041

4142
```@example 1
4243
using ModelPredictiveControl

src/controller/execute.jl

+15-17
Original file line numberDiff line numberDiff line change
@@ -160,26 +160,26 @@ function initpred!(mpc::PredictiveController, model::LinModel, d, ym, D̂, R̂y,
160160
ŷ, F, q̃, p = mpc.ŷ, mpc.F, mpc.q̃, mpc.p
161161
ŷ .= evalŷ(mpc.estim, ym, d)
162162
predictstoch!(mpc, mpc.estim, d, ym) # init mpc.Ŷop for InternalModel
163-
F_LHS = similar(F)
164-
F .= mul!(F_LHS, mpc.K, mpc.estim.x̂)
165-
F .+= mul!(F_LHS, mpc.V, mpc.estim.lastu0)
166-
F .+= mpc.Ŷop
163+
F .= mpc.Ŷop
164+
mul!(F, mpc.K, mpc.estim.x̂, 1, 1)
165+
mul!(F, mpc.V, mpc.estim.lastu0, 1, 1)
167166
if model.nd 0
168167
mpc.d0 .= d .- model.dop
169168
mpc.D̂0 .=.- mpc.Dop
170169
mpc.D̂E[1:model.nd] .= mpc.d0
171170
mpc.D̂E[model.nd+1:end] .= mpc.D̂0
172-
F .+= mul!(F_LHS, mpc.G, mpc.d0)
173-
F .+= mul!(F_LHS, mpc.J, mpc.D̂0)
171+
mul!(F, mpc.G, mpc.d0, 1, 1)
172+
mul!(F, mpc.J, mpc.D̂0, 1, 1)
174173
end
175174
mpc.R̂y .= R̂y
176-
C_y = F_LHS
175+
C_y = similar(F)
177176
C_y .= mpc.F .- mpc.R̂y
178177
q̃ .= lmul!(2,(mpc.M_Hp*mpc.Ẽ)'*C_y)
179178
p .= dot(C_y, mpc.M_Hp, C_y)
180179
if ~mpc.noR̂u
181180
mpc.R̂u .= R̂u
182-
C_u = mpc.T_lastu .- mpc.R̂u
181+
C_u = similar(mpc.T_lastu)
182+
C_u .= mpc.T_lastu .- mpc.R̂u
183183
mpc..+= lmul!(2, (mpc.L_Hp*mpc.S̃)'*C_u)
184184
mpc.p .+= dot(C_u, mpc.L_Hp, C_u)
185185
end
@@ -232,12 +232,11 @@ function predictstoch!(
232232
ŷd .+= estim.model.yop
233233
ŷs = zeros(NT, estim.model.ny)
234234
ŷs[estim.i_ym] .= @views ym .- ŷd[estim.i_ym] # ŷs=0 for unmeasured outputs
235-
Ŷop_LHS = similar(Ŷop)
236-
Ŷop .= mul!(Ŷop_LHS, mpc.Ks, estim.x̂s)
237-
Ŷop .+= mul!(Ŷop_LHS, mpc.Ps, ŷs)
238235
for j=1:mpc.Hp
239-
Ŷop[(1 + ny*(j-1)):(ny*j)] .+= yop
236+
Ŷop[(1 + ny*(j-1)):(ny*j)] .= yop
240237
end
238+
mul!(Ŷop, mpc.Ks, estim.x̂s, 1, 1)
239+
mul!(Ŷop, mpc.Ps, ŷs, 1, 1)
241240
return nothing
242241
end
243242
"Separate stochastic predictions are not needed if `estim` is not [`InternalModel`](@ref)."
@@ -255,12 +254,11 @@ function linconstraint!(mpc::PredictiveController, model::LinModel)
255254
nU, nΔŨ, nY = length(mpc.con.Umin), length(mpc.con.ΔŨmin), length(mpc.con.Ymin)
256255
nx̂ = mpc.estim.nx̂
257256
fx̂ = mpc.con.fx̂
258-
fx̂_LHS = similar(fx̂)
259-
fx̂ .= mul!(fx̂_LHS, mpc.con.kx̂, mpc.estim.x̂)
260-
fx̂ .+= mul!(fx̂_LHS, mpc.con.vx̂, mpc.estim.lastu0)
257+
mul!(fx̂, mpc.con.kx̂, mpc.estim.x̂)
258+
mul!(fx̂, mpc.con.vx̂, mpc.estim.lastu0, 1, 1)
261259
if model.nd 0
262-
fx̂ .+= mul!(fx̂_LHS, mpc.con.gx̂, mpc.d0)
263-
fx̂ .+= mul!(fx̂_LHS, mpc.con.jx̂, mpc.D̂0)
260+
mul!(fx̂, mpc.con.gx̂, mpc.d0, 1, 1)
261+
mul!(fx̂, mpc.con.jx̂, mpc.D̂0, 1, 1)
264262
end
265263
n = 0
266264
mpc.con.b[(n+1):(n+nU)] .= @. -mpc.con.Umin + mpc.T_lastu

src/estimator/execute.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ end
4343

4444
"Use the augmented model matrices if `model` is a [`LinModel`](@ref)."
4545
function f̂!(x̂next, estim::StateEstimator, ::LinModel, x̂, u, d)
46-
x̂next .= 0
47-
mul!(x̂next, estim.Â, x̂, 1, 1)
46+
mul!(x̂next, estim.Â, x̂)
4847
mul!(x̂next, estim.B̂u, u, 1, 1)
4948
mul!(x̂next, estim.B̂d, d, 1, 1)
5049
return nothing
@@ -64,8 +63,7 @@ function ĥ!(ŷ, estim::StateEstimator, model::SimModel, x̂, d)
6463
end
6564
"Use the augmented model matrices if `model` is a [`LinModel`](@ref)."
6665
function ĥ!(ŷ, estim::StateEstimator, ::LinModel, x̂, d)
67-
ŷ .= 0
68-
mul!(ŷ, estim.Ĉ, x̂, 1, 1)
66+
mul!(ŷ, estim.Ĉ, x̂)
6967
mul!(ŷ, estim.D̂d, d, 1, 1)
7068
return nothing
7169
end

src/estimator/kalman.jl

+11-9
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,17 @@ The [`SteadyKalmanFilter`](@ref) updates it with the precomputed Kalman gain ``\
182182
function update_estimate!(estim::SteadyKalmanFilter, u, ym, d=empty(estim.x̂))
183183
Â, B̂u, B̂d, Ĉm, D̂dm = estim.Â, estim.B̂u, estim.B̂d, estim.Ĉm, estim.D̂dm
184184
x̂, K̂ = estim.x̂, estim.
185-
v̂, ŷm, x̂LHS = similar(ym), similar(ym), similar(x̂)
186-
# in-place operations to recuce allocations:
187-
ŷm .= mul!(v̂, Ĉm, x̂)
188-
ŷm .+= mul!(v̂, D̂dm, d)
189-
v̂ .= ym .- ŷm
190-
x̂ .= mul!(x̂LHS, Â, x̂)
191-
.+= mul!(x̂LHS, B̂u, u)
192-
.+= mul!(x̂LHS, B̂d, d)
193-
.+= mul!(x̂LHS, K̂, v̂)
185+
ŷm, x̂next = similar(ym), similar(x̂)
186+
# in-place operations to reduce allocations:
187+
mul!(ŷm, Ĉm, x̂)
188+
mul!(ŷm, D̂dm, d, 1, 1)
189+
= ŷm
190+
v̂ .= ym .- ŷm
191+
mul!(x̂next, Â, x̂)
192+
mul!(x̂next, B̂u, u, 1, 1)
193+
mul!(x̂next, B̂d, d, 1, 1)
194+
mul!(x̂next, K̂, v̂, 1, 1)
195+
x̂ .= x̂next
194196
return nothing
195197
end
196198

src/estimator/luenberger.jl

+11-9
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,16 @@ Same than [`update_estimate!(::SteadyKalmanFilter)`](@ref) but using [`Luenberge
108108
function update_estimate!(estim::Luenberger, u, ym, d=empty(estim.x̂))
109109
Â, B̂u, B̂d, Ĉm, D̂dm = estim.Â, estim.B̂u, estim.B̂d, estim.Ĉm, estim.D̂dm
110110
x̂, K̂ = estim.x̂, estim.
111-
v̂, ŷm, x̂LHS = similar(ym), similar(ym), similar(x̂)
112-
# in-place operations to recuce allocations:
113-
ŷm .= mul!(v̂, Ĉm, x̂)
114-
ŷm .+= mul!(v̂, D̂dm, d)
115-
v̂ .= ym .- ŷm
116-
x̂ .= mul!(x̂LHS, Â, x̂)
117-
.+= mul!(x̂LHS, B̂u, u)
118-
.+= mul!(x̂LHS, B̂d, d)
119-
.+= mul!(x̂LHS, K̂, v̂)
111+
ŷm, x̂next = similar(ym), similar(x̂)
112+
# in-place operations to reduce allocations:
113+
mul!(ŷm, Ĉm, x̂)
114+
mul!(ŷm, D̂dm, d, 1, 1)
115+
= ŷm
116+
v̂ .= ym .- ŷm
117+
mul!(x̂next, Â, x̂)
118+
mul!(x̂next, B̂u, u, 1, 1)
119+
mul!(x̂next, B̂d, d, 1, 1)
120+
mul!(x̂next, K̂, v̂, 1, 1)
121+
x̂ .= x̂next
120122
return nothing
121123
end

src/estimator/mhe/construct.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ N_k = \begin{cases}
187187
The vectors ``\mathbf{Ŵ}`` and ``\mathbf{V̂}`` encompass the estimated process noise
188188
``\mathbf{ŵ}(k-j)`` and sensor noise ``\mathbf{v̂}(k-j)`` from ``j=N_k-1`` to ``0``. The
189189
Extended Help defines the two vectors and the scalar ``ϵ``. See [`UnscentedKalmanFilter`](@ref)
190-
for details on he augmented process model and ``\mathbf{R̂}, \mathbf{Q̂}`` covariances. The
190+
for details on the augmented process model and ``\mathbf{R̂}, \mathbf{Q̂}`` covariances. The
191191
matrix ``\mathbf{P̂}_{k-N_k}(k-N_k+1)`` is estimated with an [`ExtendedKalmanFilter`](@ref).
192192
193193
!!! warning

src/estimator/mhe/execute.jl

+9-10
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,16 @@ of the time-varying ``\mathbf{P̄}`` covariance . The computed variables are:
221221
```
222222
"""
223223
function initpred!(estim::MovingHorizonEstimator, model::LinModel)
224-
C, optim = estim.C, estim.optim
224+
F, C, optim = estim.F, estim.C, estim.optim
225225
= isinf(C) ? 0 : 1
226226
nx̂, nŵ, nym, Nk = estim.nx̂, estim.nx̂, estim.nym, estim.Nk[]
227227
nYm, nŴ = nym*Nk, nŵ*Nk
228228
nZ̃ =+ nx̂ + nŴ
229229
# --- update F and fx̄ vectors for MHE predictions ---
230-
F_LHS = similar(estim.F)
231-
estim.F .= mul!(F_LHS, estim.G, estim.U)
232-
estim.F .+= estim.Ym
230+
F .= estim.Ym
231+
mul!(F, estim.G, estim.U, 1, 1)
233232
if model.nd 0
234-
estim.F .+= mul!(F_LHS, estim.J, estim.D)
233+
mul!(F, estim.J, estim.D, 1, 1)
235234
end
236235
estim.fx̄ .= estim.x̂arr_old
237236
# --- update H̃, q̃ and p vectors for quadratic optimization ---
@@ -261,10 +260,10 @@ Also init ``\mathbf{F_x̂ = G_x̂ U + J_x̂ D}`` vector for the state constraint
261260
[`init_predmat_mhe`](@ref).
262261
"""
263262
function linconstraint!(estim::MovingHorizonEstimator, model::LinModel)
264-
Fx̂_LHS = similar(estim.con.Fx̂)
265-
estim.con.Fx̂ .= mul!(Fx̂_LHS, estim.con.Gx̂, estim.U)
263+
Fx̂ = estim.con.Fx̂
264+
mul!(Fx̂, estim.con.Gx̂, estim.U)
266265
if model.nd 0
267-
estim.con.Fx̂ .+= mul!(Fx̂_LHS, estim.con.Jx̂, estim.D)
266+
mul!(Fx̂, estim.con.Jx̂, estim.D, 1, 1)
268267
end
269268
X̂min, X̂max = trunc_bounds(estim, estim.con.X̂min, estim.con.X̂max, estim.nx̂)
270269
Ŵmin, Ŵmax = trunc_bounds(estim, estim.con.Ŵmin, estim.con.Ŵmax, estim.nx̂)
@@ -276,9 +275,9 @@ function linconstraint!(estim::MovingHorizonEstimator, model::LinModel)
276275
n += nx̃
277276
estim.con.b[(n+1):(n+nx̃)] .= @. +estim.con.x̃max
278277
n += nx̃
279-
estim.con.b[(n+1):(n+nX̂)] .= @. -X̂min + estim.con.Fx̂
278+
estim.con.b[(n+1):(n+nX̂)] .= @. -X̂min + Fx̂
280279
n += nX̂
281-
estim.con.b[(n+1):(n+nX̂)] .= @. +X̂max - estim.con.Fx̂
280+
estim.con.b[(n+1):(n+nX̂)] .= @. +X̂max - Fx̂
282281
n += nX̂
283282
estim.con.b[(n+1):(n+nŴ)] .= @. -Ŵmin
284283
n += nŴ

src/model/linmodel.jl

+5-5
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,9 @@ disturbances ``\mathbf{d_0 = d - d_{op}}``. The Moore-Penrose pseudo-inverse com
229229
``\mathbf{(I - A)^{-1}}`` to support integrating `model` (integrator states will be 0).
230230
"""
231231
function steadystate!(model::LinModel, u, d)
232-
model.x .= pinv(I - model.A)*(model.Bu*(u - model.uop) + model.Bd*(d - model.dop))
232+
M = I - model.A
233+
rtol = sqrt(eps(real(float(oneunit(eltype(M)))))) # pinv docstring recommendation
234+
model.x .= pinv(M; rtol)*(model.Bu*(u - model.uop) + model.Bd*(d - model.dop))
233235
return nothing
234236
end
235237

@@ -239,8 +241,7 @@ end
239241
Evaluate `xnext = A*x + Bu*u + Bd*d` in-place when `model` is a [`LinModel`](@ref).
240242
"""
241243
function f!(xnext, model::LinModel, x, u, d)
242-
xnext .= 0
243-
mul!(xnext, model.A, x, 1, 1)
244+
mul!(xnext, model.A, x)
244245
mul!(xnext, model.Bu, u, 1, 1)
245246
mul!(xnext, model.Bd, d, 1, 1)
246247
return nothing
@@ -253,8 +254,7 @@ end
253254
Evaluate `y = C*x + Dd*d` in-place when `model` is a [`LinModel`](@ref).
254255
"""
255256
function h!(y, model::LinModel, x, d)
256-
y .= 0
257-
mul!(y, model.C, x, 1, 1)
257+
mul!(y, model.C, x)
258258
mul!(y, model.Dd, d, 1, 1)
259259
return nothing
260260
end

test/test_predictive_control.jl

+9-9
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ end
8383
r = [15]
8484
outdist = [5]
8585
mpc_im = LinMPC(InternalModel(linmodel))
86-
linmodel.x[:] .= 0
86+
linmodel.x .= 0
8787
ym, u = linmodel() - outdist, [0.0]
8888
for i=1:25
8989
ym = linmodel() - outdist
@@ -94,7 +94,7 @@ end
9494
@test u [2] atol=1e-2
9595
@test ym r atol=1e-2
9696
mpc_nint_u = LinMPC(SteadyKalmanFilter(LinModel(tf(5, [2, 1]), 3), nint_u=[1]))
97-
linmodel.x[:] .= 0
97+
linmodel.x .= 0
9898
ym, u = linmodel() - outdist, [0.0]
9999
for i=1:25
100100
ym = linmodel() - outdist
@@ -105,7 +105,7 @@ end
105105
@test u [2] atol=1e-2
106106
@test ym r atol=1e-2
107107
mpc_nint_ym = LinMPC(SteadyKalmanFilter(LinModel(tf(5, [2, 1]), 3), nint_ym=[1]))
108-
linmodel.x[:] .= 0
108+
linmodel.x .= 0
109109
ym, u = linmodel() - outdist, [0.0]
110110
for i=1:25
111111
ym = linmodel() - outdist
@@ -313,7 +313,7 @@ end
313313
r = [15]
314314
outdist = [5]
315315
mpc_im = ExplicitMPC(InternalModel(linmodel))
316-
linmodel.x[:] .= 0
316+
linmodel.x .= 0
317317
ym, u = linmodel() - outdist, [0.0]
318318
for i=1:25
319319
ym = linmodel() - outdist
@@ -324,7 +324,7 @@ end
324324
@test u [2] atol=1e-2
325325
@test ym r atol=1e-2
326326
mpc_nint_u = ExplicitMPC(SteadyKalmanFilter(LinModel(tf(5, [2, 1]), 3), nint_u=[1]))
327-
linmodel.x[:] .= 0
327+
linmodel.x .= 0
328328
ym, u = linmodel() - outdist, [0.0]
329329
for i=1:25
330330
ym = linmodel() - outdist
@@ -335,7 +335,7 @@ end
335335
@test u [2] atol=1e-2
336336
@test ym r atol=1e-2
337337
mpc_nint_ym = ExplicitMPC(SteadyKalmanFilter(LinModel(tf(5, [2, 1]), 3), nint_ym=[1]))
338-
linmodel.x[:] .= 0
338+
linmodel.x .= 0
339339
ym, u = linmodel() - outdist, [0.0]
340340
for i=1:25
341341
ym = linmodel() - outdist
@@ -475,7 +475,7 @@ end
475475
r = [15]
476476
outdist = [5]
477477
nmpc_im = NonLinMPC(InternalModel(linmodel))
478-
linmodel.x[:] .= 0
478+
linmodel.x .= 0
479479
ym, u = linmodel() - outdist, [0.0]
480480
for i=1:25
481481
ym = linmodel() - outdist
@@ -486,7 +486,7 @@ end
486486
@test u [2] atol=1e-2
487487
@test ym r atol=1e-2
488488
nmpc_nint_u = NonLinMPC(SteadyKalmanFilter(linmodel, nint_u=[1]))
489-
linmodel.x[:] .= 0
489+
linmodel.x .= 0
490490
ym, u = linmodel() - outdist, [0.0]
491491
for i=1:25
492492
ym = linmodel() - outdist
@@ -497,7 +497,7 @@ end
497497
@test u [2] atol=1e-2
498498
@test ym r atol=1e-2
499499
nmpc_nint_ym = NonLinMPC(SteadyKalmanFilter(linmodel, nint_ym=[1]))
500-
linmodel.x[:] .= 0
500+
linmodel.x .= 0
501501
ym, u = linmodel() - outdist, [0.0]
502502
for i=1:25
503503
ym = linmodel() - outdist

test/test_sim_model.jl

+4-8
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,13 @@ end
137137
@test isa(nonlinmodel3, NonLinModel{Float32})
138138

139139
function f1!(xnext, x, u, d)
140-
xnext .= 0
141-
mul!(xnext, linmodel2.A, x, 1, 1)
140+
mul!(xnext, linmodel2.A, x)
142141
mul!(xnext, linmodel2.Bu, u, 1, 1)
143142
mul!(xnext, linmodel2.Bd, d, 1, 1)
144143
return nothing
145144
end
146145
function h1!(y, x, d)
147-
y .= 0
148-
mul!(y, linmodel2.C, x, 1, 1)
146+
mul!(y, linmodel2.C, x)
149147
mul!(y, linmodel2.Dd, d, 1, 1)
150148
return nothing
151149
end
@@ -171,15 +169,13 @@ end
171169
@test y zeros(1)
172170

173171
function f2!(ẋ, x, u , d)
174-
ẋ .= 0
175-
mul!(ẋ, A, x, 1, 1)
172+
mul!(ẋ, A, x)
176173
mul!(ẋ, Bu, u, 1, 1)
177174
mul!(ẋ, Bd, d, 1, 1)
178175
return nothing
179176
end
180177
function h2!(y, x, d)
181-
y .= 0
182-
mul!(y, C, x, 1, 1)
178+
mul!(y, C, x)
183179
mul!(y, Dd, d, 1, 1)
184180
return nothing
185181
end

0 commit comments

Comments
 (0)