Skip to content

Commit 77163a7

Browse files
authored
Merge pull request #244 from JuliaControl/prettier_print
added: clearer pretty-print with trees + print differentiation backends
2 parents f2f5b2e + ff3289d commit 77163a7

18 files changed

+419
-311
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
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 = "1.9.1"
4+
version = "1.9.2"
55

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

src/controller/construct.jl

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,20 @@ constraints are all soft by default. See Extended Help for time-varying constrai
221221
julia> mpc = LinMPC(setop!(LinModel(tf(3, [30, 1]), 4), uop=[50], yop=[25]));
222222
223223
julia> mpc = setconstraint!(mpc, umin=[0], umax=[100], Δumin=[-10], Δumax=[+10])
224-
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, SteadyKalmanFilter estimator and:
225-
10 prediction steps Hp
226-
2 control steps Hc
227-
1 slack variable ϵ (control constraints)
228-
1 manipulated inputs u (0 integrating states)
229-
2 estimated states x̂
230-
1 measured outputs ym (1 integrating states)
231-
0 unmeasured outputs yu
232-
0 measured disturbances d
224+
LinMPC controller with a sample time Ts = 4.0 s:
225+
├ estimator: SteadyKalmanFilter
226+
├ model: LinModel
227+
├ optimizer: OSQP
228+
├ transcription: SingleShooting
229+
└ dimensions:
230+
├ 10 prediction steps Hp
231+
├ 2 control steps Hc
232+
├ 1 slack variable ϵ (control constraints)
233+
├ 1 manipulated inputs u (0 integrating states)
234+
├ 2 estimated states x̂
235+
├ 1 measured outputs ym (1 integrating states)
236+
├ 0 unmeasured outputs yu
237+
└ 0 measured disturbances d
233238
```
234239
235240
# Extended Help

src/controller/explicitmpc.jl

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,15 @@ arguments.
117117
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4);
118118
119119
julia> mpc = ExplicitMPC(model, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
120-
ExplicitMPC controller with a sample time Ts = 4.0 s, SteadyKalmanFilter estimator and:
121-
30 prediction steps Hp
122-
1 control steps Hc
123-
1 manipulated inputs u (0 integrating states)
124-
4 estimated states x̂
125-
2 measured outputs ym (2 integrating states)
126-
0 unmeasured outputs yu
127-
0 measured disturbances d
120+
ExplicitMPC controller with a sample time Ts = 4.0 s:
121+
├ estimator: SteadyKalmanFilter
122+
├ model: LinModel
123+
└ dimensions:
124+
├ 1 manipulated inputs u (0 integrating states)
125+
├ 4 estimated states x̂
126+
├ 2 measured outputs ym (2 integrating states)
127+
├ 0 unmeasured outputs yu
128+
└ 0 measured disturbances d
128129
```
129130
130131
"""
@@ -177,15 +178,15 @@ end
177178
setconstraint!(::ExplicitMPC; kwargs...) = error("ExplicitMPC does not support constraints.")
178179

179180
function Base.show(io::IO, mpc::ExplicitMPC)
180-
Hp, Hc = mpc.Hp, mpc.Hc
181-
nu, nd = mpc.estim.model.nu, mpc.estim.model.nd
182-
nx̂, nym, nyu = mpc.estim.nx̂, mpc.estim.nym, mpc.estim.nyu
181+
estim, model = mpc.estim, mpc.estim.model
182+
Hp, Hc, nϵ = mpc.Hp, mpc.Hc, mpc.
183+
nu, nd = model.nu, model.nd
184+
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
183185
n = maximum(ndigits.((Hp, Hc, nu, nx̂, nym, nyu, nd))) + 1
184-
println(io, "$(nameof(typeof(mpc))) controller with a sample time Ts = "*
185-
"$(mpc.estim.model.Ts) s, "*
186-
"$(nameof(typeof(mpc.estim))) estimator and:")
187-
println(io, "$(lpad(Hp, n)) prediction steps Hp")
188-
println(io, "$(lpad(Hc, n)) control steps Hc")
186+
println(io, "$(nameof(typeof(mpc))) controller with a sample time Ts = $(model.Ts) s:")
187+
println(io, "├ estimator: $(nameof(typeof(mpc.estim)))")
188+
println(io, "├ model: $(nameof(typeof(model)))")
189+
println(io, "└ dimensions:")
189190
print_estim_dim(io, mpc.estim, n)
190191
end
191192

src/controller/linmpc.jl

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,20 @@ arguments. This controller allocates memory at each time step for the optimizati
168168
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4);
169169
170170
julia> mpc = LinMPC(model, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
171-
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, SteadyKalmanFilter estimator and:
172-
30 prediction steps Hp
173-
1 control steps Hc
174-
1 slack variable ϵ (control constraints)
175-
1 manipulated inputs u (0 integrating states)
176-
4 estimated states x̂
177-
2 measured outputs ym (2 integrating states)
178-
0 unmeasured outputs yu
179-
0 measured disturbances d
171+
LinMPC controller with a sample time Ts = 4.0 s:
172+
├ estimator: SteadyKalmanFilter
173+
├ model: LinModel
174+
├ optimizer: OSQP
175+
├ transcription: SingleShooting
176+
└ dimensions:
177+
├ 30 prediction steps Hp
178+
├ 1 control steps Hc
179+
├ 1 slack variable ϵ (control constraints)
180+
├ 1 manipulated inputs u (0 integrating states)
181+
├ 4 estimated states x̂
182+
├ 2 measured outputs ym (2 integrating states)
183+
├ 0 unmeasured outputs yu
184+
└ 0 measured disturbances d
180185
```
181186
182187
# Extended Help
@@ -238,15 +243,20 @@ Use custom state estimator `estim` to construct `LinMPC`.
238243
julia> estim = KalmanFilter(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4), i_ym=[2]);
239244
240245
julia> mpc = LinMPC(estim, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
241-
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, KalmanFilter estimator and:
242-
30 prediction steps Hp
243-
1 control steps Hc
244-
1 slack variable ϵ (control constraints)
245-
1 manipulated inputs u (0 integrating states)
246-
3 estimated states x̂
247-
1 measured outputs ym (1 integrating states)
248-
1 unmeasured outputs yu
249-
0 measured disturbances d
246+
LinMPC controller with a sample time Ts = 4.0 s:
247+
├ estimator: KalmanFilter
248+
├ model: LinModel
249+
├ optimizer: OSQP
250+
├ transcription: SingleShooting
251+
└ dimensions:
252+
├ 30 prediction steps Hp
253+
├ 1 control steps Hc
254+
├ 1 slack variable ϵ (control constraints)
255+
├ 1 manipulated inputs u (0 integrating states)
256+
├ 3 estimated states x̂
257+
├ 1 measured outputs ym (1 integrating states)
258+
├ 1 unmeasured outputs yu
259+
└ 0 measured disturbances d
250260
```
251261
"""
252262
function LinMPC(

src/controller/nonlinmpc.jl

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -223,16 +223,23 @@ This controller allocates memory at each time step for the optimization.
223223
```jldoctest
224224
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
225225
226-
julia> mpc = NonLinMPC(model, Hp=20, Hc=1, Cwt=1e6)
227-
NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, SingleShooting transcription, UnscentedKalmanFilter estimator and:
228-
20 prediction steps Hp
229-
1 control steps Hc
230-
1 slack variable ϵ (control constraints)
231-
1 manipulated inputs u (0 integrating states)
232-
2 estimated states x̂
233-
1 measured outputs ym (1 integrating states)
234-
0 unmeasured outputs yu
235-
0 measured disturbances d
226+
julia> mpc = NonLinMPC(model, Hp=20, Hc=10, transcription=MultipleShooting())
227+
NonLinMPC controller with a sample time Ts = 10.0 s:
228+
├ estimator: UnscentedKalmanFilter
229+
├ model: NonLinModel
230+
├ optimizer: Ipopt
231+
├ transcription: MultipleShooting
232+
├ gradient: AutoForwardDiff
233+
├ jacobian: AutoSparse (AutoForwardDiff, TracerSparsityDetector, GreedyColoringAlgorithm)
234+
└ dimensions:
235+
├ 20 prediction steps Hp
236+
├ 10 control steps Hc
237+
├ 1 slack variable ϵ (control constraints)
238+
├ 1 manipulated inputs u (0 integrating states)
239+
├ 2 estimated states x̂
240+
├ 1 measured outputs ym (1 integrating states)
241+
├ 0 unmeasured outputs yu
242+
└ 0 measured disturbances d
236243
```
237244
238245
# Extended Help
@@ -327,16 +334,23 @@ julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver
327334
328335
julia> estim = UnscentedKalmanFilter(model, σQint_ym=[0.05]);
329336
330-
julia> mpc = NonLinMPC(estim, Hp=20, Hc=1, Cwt=1e6)
331-
NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, SingleShooting transcription, UnscentedKalmanFilter estimator and:
332-
20 prediction steps Hp
333-
1 control steps Hc
334-
1 slack variable ϵ (control constraints)
335-
1 manipulated inputs u (0 integrating states)
336-
2 estimated states x̂
337-
1 measured outputs ym (1 integrating states)
338-
0 unmeasured outputs yu
339-
0 measured disturbances d
337+
julia> mpc = NonLinMPC(estim, Hp=20, Cwt=1e6)
338+
NonLinMPC controller with a sample time Ts = 10.0 s:
339+
├ estimator: UnscentedKalmanFilter
340+
├ model: NonLinModel
341+
├ optimizer: Ipopt
342+
├ transcription: SingleShooting
343+
├ gradient: AutoForwardDiff
344+
├ jacobian: AutoForwardDiff
345+
└ dimensions:
346+
├ 20 prediction steps Hp
347+
├ 2 control steps Hc
348+
├ 1 slack variable ϵ (control constraints)
349+
├ 1 manipulated inputs u (0 integrating states)
350+
├ 2 estimated states x̂
351+
├ 1 measured outputs ym (1 integrating states)
352+
├ 0 unmeasured outputs yu
353+
└ 0 measured disturbances d
340354
```
341355
"""
342356
function NonLinMPC(
@@ -738,8 +752,14 @@ end
738752

739753
"Evaluate the economic term `E*JE` of the objective function for [`NonLinMPC`](@ref)."
740754
function obj_econ(
741-
mpc::NonLinMPC, model::SimModel, Ue, Ŷe::AbstractVector{NT}
755+
mpc::NonLinMPC, ::SimModel, Ue, Ŷe::AbstractVector{NT}
742756
) where NT<:Real
743757
E_JE = mpc.weights.iszero_E ? zero(NT) : mpc.weights.E*mpc.JE(Ue, Ŷe, mpc.D̂e, mpc.p)
744758
return E_JE
745-
end
759+
end
760+
761+
"Print the differentiation backends of a [`NonLinMPC`](@ref) controller."
762+
function print_backends(io::IO, mpc::NonLinMPC)
763+
println(io, "├ gradient: $(backend_str(mpc.gradient))")
764+
println(io, "├ jacobian: $(backend_str(mpc.jacobian))")
765+
end

src/estimator/internal_model.jl

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ estimator is allocation-free if `model` simulations do not allocate.
8080
# Examples
8181
```jldoctest
8282
julia> estim = InternalModel(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5), i_ym=[2])
83-
InternalModel estimator with a sample time Ts = 0.5 s, LinModel and:
84-
1 manipulated inputs u
85-
2 estimated states x̂
86-
1 measured outputs ym
87-
1 unmeasured outputs yu
88-
0 measured disturbances d
83+
InternalModel estimator with a sample time Ts = 0.5 s:
84+
├ model: LinModel
85+
└ dimensions:
86+
├ 1 manipulated inputs u
87+
├ 2 estimated states x̂
88+
├ 1 measured outputs ym
89+
├ 1 unmeasured outputs yu
90+
└ 0 measured disturbances d
8991
```
9092
9193
# Extended Help
@@ -355,10 +357,10 @@ end
355357
function print_estim_dim(io::IO, estim::InternalModel, n)
356358
nu, nd = estim.model.nu, estim.model.nd
357359
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
358-
println(io, "$(lpad(nu, n)) manipulated inputs u")
359-
println(io, "$(lpad(nx̂, n)) estimated states x̂")
360-
println(io, "$(lpad(nym, n)) measured outputs ym")
361-
println(io, "$(lpad(nyu, n)) unmeasured outputs yu")
362-
print(io, "$(lpad(nd, n)) measured disturbances d")
360+
println(io, "$(lpad(nu, n)) manipulated inputs u")
361+
println(io, "$(lpad(nx̂, n)) estimated states x̂")
362+
println(io, "$(lpad(nym, n)) measured outputs ym")
363+
println(io, "$(lpad(nyu, n)) unmeasured outputs yu")
364+
print(io, "$(lpad(nd, n)) measured disturbances d")
363365
end
364366

src/estimator/kalman.jl

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,14 @@ state of the next time step ``\mathbf{x̂}_k(k+1)``. This estimator is allocatio
130130
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
131131
132132
julia> estim = SteadyKalmanFilter(model, i_ym=[2], σR=[1], σQint_ym=[0.01])
133-
SteadyKalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
134-
1 manipulated inputs u (0 integrating states)
135-
3 estimated states x̂
136-
1 measured outputs ym (1 integrating states)
137-
1 unmeasured outputs yu
138-
0 measured disturbances d
133+
SteadyKalmanFilter estimator with a sample time Ts = 0.5 s:
134+
├ model: LinModel
135+
└ dimensions:
136+
├ 1 manipulated inputs u (0 integrating states)
137+
├ 3 estimated states x̂
138+
├ 1 measured outputs ym (1 integrating states)
139+
├ 1 unmeasured outputs yu
140+
└ 0 measured disturbances d
139141
```
140142
141143
# Extended Help
@@ -395,12 +397,14 @@ This estimator is allocation-free.
395397
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
396398
397399
julia> estim = KalmanFilter(model, i_ym=[2], σR=[1], σP_0=[100, 100], σQint_ym=[0.01])
398-
KalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
399-
1 manipulated inputs u (0 integrating states)
400-
3 estimated states x̂
401-
1 measured outputs ym (1 integrating states)
402-
1 unmeasured outputs yu
403-
0 measured disturbances d
400+
KalmanFilter estimator with a sample time Ts = 0.5 s:
401+
├ model: LinModel
402+
└ dimensions:
403+
├ 1 manipulated inputs u (0 integrating states)
404+
├ 3 estimated states x̂
405+
├ 1 measured outputs ym (1 integrating states)
406+
├ 1 unmeasured outputs yu
407+
└ 0 measured disturbances d
404408
```
405409
"""
406410
function KalmanFilter(
@@ -639,12 +643,14 @@ This estimator is allocation-free if `model` simulations do not allocate.
639643
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
640644
641645
julia> estim = UnscentedKalmanFilter(model, σR=[1], nint_ym=[2], σPint_ym_0=[1, 1])
642-
UnscentedKalmanFilter estimator with a sample time Ts = 10.0 s, NonLinModel and:
643-
1 manipulated inputs u (0 integrating states)
644-
3 estimated states x̂
645-
1 measured outputs ym (2 integrating states)
646-
0 unmeasured outputs yu
647-
0 measured disturbances d
646+
UnscentedKalmanFilter estimator with a sample time Ts = 10.0 s:
647+
├ model: NonLinModel
648+
└ dimensions:
649+
├ 1 manipulated inputs u (0 integrating states)
650+
├ 3 estimated states x̂
651+
├ 1 measured outputs ym (2 integrating states)
652+
├ 0 unmeasured outputs yu
653+
└ 0 measured disturbances d
648654
```
649655
650656
# Extended Help
@@ -1002,12 +1008,15 @@ differentiation. This estimator is allocation-free if `model` simulations do not
10021008
julia> model = NonLinModel((x,u,_,_)->0.2x+u, (x,_,_)->-3x, 5.0, 1, 1, 1, solver=nothing);
10031009
10041010
julia> estim = ExtendedKalmanFilter(model, σQ=[2], σQint_ym=[2], σP_0=[0.1], σPint_ym_0=[0.1])
1005-
ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s, NonLinModel and:
1006-
1 manipulated inputs u (0 integrating states)
1007-
2 estimated states x̂
1008-
1 measured outputs ym (1 integrating states)
1009-
0 unmeasured outputs yu
1010-
0 measured disturbances d
1011+
ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s:
1012+
├ model: NonLinModel
1013+
├ jacobian: AutoForwardDiff
1014+
└ dimensions:
1015+
├ 1 manipulated inputs u (0 integrating states)
1016+
├ 2 estimated states x̂
1017+
├ 1 measured outputs ym (1 integrating states)
1018+
├ 0 unmeasured outputs yu
1019+
└ 0 measured disturbances d
10111020
```
10121021
"""
10131022
function ExtendedKalmanFilter(
@@ -1169,6 +1178,10 @@ function update_estimate!(estim::ExtendedKalmanFilter{NT}, y0m, d0, u0) where NT
11691178
return predict_estimate_kf!(estim, u0, d0, F̂)
11701179
end
11711180

1181+
function print_details(io::IO, estim::ExtendedKalmanFilter)
1182+
println(io, "├ jacobian: $(backend_str(estim.jacobian))")
1183+
end
1184+
11721185
"Set `estim.cov.P̂` to `estim.cov.P̂_0` for the time-varying Kalman Filters."
11731186
function init_estimate_cov!(
11741187
estim::Union{KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFilter}, _ , _ , _

src/estimator/luenberger.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,14 @@ is allocation-free.
8383
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);
8484
8585
julia> estim = Luenberger(model, nint_ym=[1, 1], poles=[0.61, 0.62, 0.63, 0.64])
86-
Luenberger estimator with a sample time Ts = 0.5 s, LinModel and:
87-
1 manipulated inputs u (0 integrating states)
88-
4 estimated states x̂
89-
2 measured outputs ym (2 integrating states)
90-
0 unmeasured outputs yu
91-
0 measured disturbances d
86+
Luenberger estimator with a sample time Ts = 0.5 s:
87+
├ model: LinModel
88+
└ dimensions:
89+
├ 1 manipulated inputs u (0 integrating states)
90+
├ 4 estimated states x̂
91+
├ 2 measured outputs ym (2 integrating states)
92+
├ 0 unmeasured outputs yu
93+
└ 0 measured disturbances d
9294
```
9395
"""
9496
function Luenberger(

0 commit comments

Comments
 (0)