Skip to content
Merged
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ModelPredictiveControl"
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
authors = ["Francis Gagnon"]
version = "1.9.1"
version = "1.9.2"

[deps]
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
Expand Down
23 changes: 14 additions & 9 deletions src/controller/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,20 @@ constraints are all soft by default. See Extended Help for time-varying constrai
julia> mpc = LinMPC(setop!(LinModel(tf(3, [30, 1]), 4), uop=[50], yop=[25]));

julia> mpc = setconstraint!(mpc, umin=[0], umax=[100], Δumin=[-10], Δumax=[+10])
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, SteadyKalmanFilter estimator and:
10 prediction steps Hp
2 control steps Hc
1 slack variable ϵ (control constraints)
1 manipulated inputs u (0 integrating states)
2 estimated states x̂
1 measured outputs ym (1 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
LinMPC controller with a sample time Ts = 4.0 s:
├ estimator: SteadyKalmanFilter
├ model: LinModel
├ optimizer: OSQP
├ transcription: SingleShooting
└ dimensions:
├ 10 prediction steps Hp
├ 2 control steps Hc
├ 1 slack variable ϵ (control constraints)
├ 1 manipulated inputs u (0 integrating states)
├ 2 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down
33 changes: 17 additions & 16 deletions src/controller/explicitmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ arguments.
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4);

julia> mpc = ExplicitMPC(model, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
ExplicitMPC controller with a sample time Ts = 4.0 s, SteadyKalmanFilter estimator and:
30 prediction steps Hp
1 control steps Hc
1 manipulated inputs u (0 integrating states)
4 estimated states x̂
2 measured outputs ym (2 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
ExplicitMPC controller with a sample time Ts = 4.0 s:
├ estimator: SteadyKalmanFilter
├ model: LinModel
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 4 estimated states x̂
├ 2 measured outputs ym (2 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```

"""
Expand Down Expand Up @@ -177,15 +178,15 @@ end
setconstraint!(::ExplicitMPC; kwargs...) = error("ExplicitMPC does not support constraints.")

function Base.show(io::IO, mpc::ExplicitMPC)
Hp, Hc = mpc.Hp, mpc.Hc
nu, nd = mpc.estim.model.nu, mpc.estim.model.nd
nx̂, nym, nyu = mpc.estim.nx̂, mpc.estim.nym, mpc.estim.nyu
estim, model = mpc.estim, mpc.estim.model
Hp, Hc, nϵ = mpc.Hp, mpc.Hc, mpc.nϵ
nu, nd = model.nu, model.nd
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
n = maximum(ndigits.((Hp, Hc, nu, nx̂, nym, nyu, nd))) + 1
println(io, "$(nameof(typeof(mpc))) controller with a sample time Ts = "*
"$(mpc.estim.model.Ts) s, "*
"$(nameof(typeof(mpc.estim))) estimator and:")
println(io, "$(lpad(Hp, n)) prediction steps Hp")
println(io, "$(lpad(Hc, n)) control steps Hc")
println(io, "$(nameof(typeof(mpc))) controller with a sample time Ts = $(model.Ts) s:")
println(io, "├ estimator: $(nameof(typeof(mpc.estim)))")
println(io, "├ model: $(nameof(typeof(model)))")
println(io, "└ dimensions:")
print_estim_dim(io, mpc.estim, n)
end

Expand Down
46 changes: 28 additions & 18 deletions src/controller/linmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,20 @@ arguments. This controller allocates memory at each time step for the optimizati
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4);

julia> mpc = LinMPC(model, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, SteadyKalmanFilter estimator and:
30 prediction steps Hp
1 control steps Hc
1 slack variable ϵ (control constraints)
1 manipulated inputs u (0 integrating states)
4 estimated states x̂
2 measured outputs ym (2 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
LinMPC controller with a sample time Ts = 4.0 s:
├ estimator: SteadyKalmanFilter
├ model: LinModel
├ optimizer: OSQP
├ transcription: SingleShooting
└ dimensions:
├ 30 prediction steps Hp
├ 1 control steps Hc
├ 1 slack variable ϵ (control constraints)
├ 1 manipulated inputs u (0 integrating states)
├ 4 estimated states x̂
├ 2 measured outputs ym (2 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down Expand Up @@ -238,15 +243,20 @@ Use custom state estimator `estim` to construct `LinMPC`.
julia> estim = KalmanFilter(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4), i_ym=[2]);

julia> mpc = LinMPC(estim, Mwt=[0, 1], Nwt=[0.5], Hp=30, Hc=1)
LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SingleShooting transcription, KalmanFilter estimator and:
30 prediction steps Hp
1 control steps Hc
1 slack variable ϵ (control constraints)
1 manipulated inputs u (0 integrating states)
3 estimated states x̂
1 measured outputs ym (1 integrating states)
1 unmeasured outputs yu
0 measured disturbances d
LinMPC controller with a sample time Ts = 4.0 s:
├ estimator: KalmanFilter
├ model: LinModel
├ optimizer: OSQP
├ transcription: SingleShooting
└ dimensions:
├ 30 prediction steps Hp
├ 1 control steps Hc
├ 1 slack variable ϵ (control constraints)
├ 1 manipulated inputs u (0 integrating states)
├ 3 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 1 unmeasured outputs yu
└ 0 measured disturbances d
```
"""
function LinMPC(
Expand Down
64 changes: 42 additions & 22 deletions src/controller/nonlinmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,23 @@ This controller allocates memory at each time step for the optimization.
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);

julia> mpc = NonLinMPC(model, Hp=20, Hc=1, Cwt=1e6)
NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, SingleShooting transcription, UnscentedKalmanFilter estimator and:
20 prediction steps Hp
1 control steps Hc
1 slack variable ϵ (control constraints)
1 manipulated inputs u (0 integrating states)
2 estimated states x̂
1 measured outputs ym (1 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
julia> mpc = NonLinMPC(model, Hp=20, Hc=10, transcription=MultipleShooting())
NonLinMPC controller with a sample time Ts = 10.0 s:
├ estimator: UnscentedKalmanFilter
├ model: NonLinModel
├ optimizer: Ipopt
├ transcription: MultipleShooting
├ gradient: AutoForwardDiff
├ jacobian: AutoSparse (AutoForwardDiff, TracerSparsityDetector, GreedyColoringAlgorithm)
└ dimensions:
├ 20 prediction steps Hp
├ 10 control steps Hc
├ 1 slack variable ϵ (control constraints)
├ 1 manipulated inputs u (0 integrating states)
├ 2 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down Expand Up @@ -327,16 +334,23 @@ julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver

julia> estim = UnscentedKalmanFilter(model, σQint_ym=[0.05]);

julia> mpc = NonLinMPC(estim, Hp=20, Hc=1, Cwt=1e6)
NonLinMPC controller with a sample time Ts = 10.0 s, Ipopt optimizer, SingleShooting transcription, UnscentedKalmanFilter estimator and:
20 prediction steps Hp
1 control steps Hc
1 slack variable ϵ (control constraints)
1 manipulated inputs u (0 integrating states)
2 estimated states x̂
1 measured outputs ym (1 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
julia> mpc = NonLinMPC(estim, Hp=20, Cwt=1e6)
NonLinMPC controller with a sample time Ts = 10.0 s:
├ estimator: UnscentedKalmanFilter
├ model: NonLinModel
├ optimizer: Ipopt
├ transcription: SingleShooting
├ gradient: AutoForwardDiff
├ jacobian: AutoForwardDiff
└ dimensions:
├ 20 prediction steps Hp
├ 2 control steps Hc
├ 1 slack variable ϵ (control constraints)
├ 1 manipulated inputs u (0 integrating states)
├ 2 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```
"""
function NonLinMPC(
Expand Down Expand Up @@ -738,8 +752,14 @@ end

"Evaluate the economic term `E*JE` of the objective function for [`NonLinMPC`](@ref)."
function obj_econ(
mpc::NonLinMPC, model::SimModel, Ue, Ŷe::AbstractVector{NT}
mpc::NonLinMPC, ::SimModel, Ue, Ŷe::AbstractVector{NT}
) where NT<:Real
E_JE = mpc.weights.iszero_E ? zero(NT) : mpc.weights.E*mpc.JE(Ue, Ŷe, mpc.D̂e, mpc.p)
return E_JE
end
end

"Print the differentiation backends of a [`NonLinMPC`](@ref) controller."
function print_backends(io::IO, mpc::NonLinMPC)
println(io, "├ gradient: $(backend_str(mpc.gradient))")
println(io, "├ jacobian: $(backend_str(mpc.jacobian))")
end
24 changes: 13 additions & 11 deletions src/estimator/internal_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ estimator is allocation-free if `model` simulations do not allocate.
# Examples
```jldoctest
julia> estim = InternalModel(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5), i_ym=[2])
InternalModel estimator with a sample time Ts = 0.5 s, LinModel and:
1 manipulated inputs u
2 estimated states x̂
1 measured outputs ym
1 unmeasured outputs yu
0 measured disturbances d
InternalModel estimator with a sample time Ts = 0.5 s:
├ model: LinModel
└ dimensions:
├ 1 manipulated inputs u
├ 2 estimated states x̂
├ 1 measured outputs ym
├ 1 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down Expand Up @@ -355,10 +357,10 @@ end
function print_estim_dim(io::IO, estim::InternalModel, n)
nu, nd = estim.model.nu, estim.model.nd
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
println(io, "$(lpad(nu, n)) manipulated inputs u")
println(io, "$(lpad(nx̂, n)) estimated states x̂")
println(io, "$(lpad(nym, n)) measured outputs ym")
println(io, "$(lpad(nyu, n)) unmeasured outputs yu")
print(io, "$(lpad(nd, n)) measured disturbances d")
println(io, "$(lpad(nu, n)) manipulated inputs u")
println(io, "$(lpad(nx̂, n)) estimated states x̂")
println(io, "$(lpad(nym, n)) measured outputs ym")
println(io, "$(lpad(nyu, n)) unmeasured outputs yu")
print(io, "$(lpad(nd, n)) measured disturbances d")
end

61 changes: 37 additions & 24 deletions src/estimator/kalman.jl
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ state of the next time step ``\mathbf{x̂}_k(k+1)``. This estimator is allocatio
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);

julia> estim = SteadyKalmanFilter(model, i_ym=[2], σR=[1], σQint_ym=[0.01])
SteadyKalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
1 manipulated inputs u (0 integrating states)
3 estimated states x̂
1 measured outputs ym (1 integrating states)
1 unmeasured outputs yu
0 measured disturbances d
SteadyKalmanFilter estimator with a sample time Ts = 0.5 s:
├ model: LinModel
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 3 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 1 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down Expand Up @@ -395,12 +397,14 @@ This estimator is allocation-free.
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);

julia> estim = KalmanFilter(model, i_ym=[2], σR=[1], σP_0=[100, 100], σQint_ym=[0.01])
KalmanFilter estimator with a sample time Ts = 0.5 s, LinModel and:
1 manipulated inputs u (0 integrating states)
3 estimated states x̂
1 measured outputs ym (1 integrating states)
1 unmeasured outputs yu
0 measured disturbances d
KalmanFilter estimator with a sample time Ts = 0.5 s:
├ model: LinModel
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 3 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 1 unmeasured outputs yu
└ 0 measured disturbances d
```
"""
function KalmanFilter(
Expand Down Expand Up @@ -639,12 +643,14 @@ This estimator is allocation-free if `model` simulations do not allocate.
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);

julia> estim = UnscentedKalmanFilter(model, σR=[1], nint_ym=[2], σPint_ym_0=[1, 1])
UnscentedKalmanFilter estimator with a sample time Ts = 10.0 s, NonLinModel and:
1 manipulated inputs u (0 integrating states)
3 estimated states x̂
1 measured outputs ym (2 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
UnscentedKalmanFilter estimator with a sample time Ts = 10.0 s:
├ model: NonLinModel
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 3 estimated states x̂
├ 1 measured outputs ym (2 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```

# Extended Help
Expand Down Expand Up @@ -1002,12 +1008,15 @@ differentiation. This estimator is allocation-free if `model` simulations do not
julia> model = NonLinModel((x,u,_,_)->0.2x+u, (x,_,_)->-3x, 5.0, 1, 1, 1, solver=nothing);

julia> estim = ExtendedKalmanFilter(model, σQ=[2], σQint_ym=[2], σP_0=[0.1], σPint_ym_0=[0.1])
ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s, NonLinModel and:
1 manipulated inputs u (0 integrating states)
2 estimated states x̂
1 measured outputs ym (1 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s:
├ model: NonLinModel
├ jacobian: AutoForwardDiff
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 2 estimated states x̂
├ 1 measured outputs ym (1 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```
"""
function ExtendedKalmanFilter(
Expand Down Expand Up @@ -1169,6 +1178,10 @@ function update_estimate!(estim::ExtendedKalmanFilter{NT}, y0m, d0, u0) where NT
return predict_estimate_kf!(estim, u0, d0, F̂)
end

function print_details(io::IO, estim::ExtendedKalmanFilter)
println(io, "├ jacobian: $(backend_str(estim.jacobian))")
end

"Set `estim.cov.P̂` to `estim.cov.P̂_0` for the time-varying Kalman Filters."
function init_estimate_cov!(
estim::Union{KalmanFilter, UnscentedKalmanFilter, ExtendedKalmanFilter}, _ , _ , _
Expand Down
14 changes: 8 additions & 6 deletions src/estimator/luenberger.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ is allocation-free.
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);

julia> estim = Luenberger(model, nint_ym=[1, 1], poles=[0.61, 0.62, 0.63, 0.64])
Luenberger estimator with a sample time Ts = 0.5 s, LinModel and:
1 manipulated inputs u (0 integrating states)
4 estimated states x̂
2 measured outputs ym (2 integrating states)
0 unmeasured outputs yu
0 measured disturbances d
Luenberger estimator with a sample time Ts = 0.5 s:
├ model: LinModel
└ dimensions:
├ 1 manipulated inputs u (0 integrating states)
├ 4 estimated states x̂
├ 2 measured outputs ym (2 integrating states)
├ 0 unmeasured outputs yu
└ 0 measured disturbances d
```
"""
function Luenberger(
Expand Down
Loading
Loading