Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/src/manual/linmpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ We need to construct a new [`LinModel`](@ref) that includes the measured disturb
``\mathbf{d} = u_l`` and the operating point ``\mathbf{d_{op}} = 20``:

```@example 1
model_d = setop!(LinModel([G G[1:2, 2]], Ts, i_d=[3]), uop=[20, 20], yop=[50, 30], dop=[20])
model_d = LinModel([G G[1:2, 2]], Ts, i_d=[3])
model_d = setop!(model_d, uop=[20, 20], yop=[50, 30], dop=[20])
```

A [`LinMPC`](@ref) controller is constructed on this model:
Expand Down
14 changes: 7 additions & 7 deletions docs/src/manual/mtk.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ function generate_f_h(model, inputs, outputs)
f_ip(ẋ, x, u, p, nothing)
catch err
if err isa MethodError
error("NonLinModel does not support a time argument t in the f function, "*
"see the constructor docstring for a workaround.")
error("NonLinModel does not support a time argument t in the f "*
"function, see the constructor docstring for a workaround.")
else
rethrow()
end
Expand All @@ -71,14 +71,14 @@ function generate_f_h(model, inputs, outputs)
u_nothing = fill(nothing, nu)
function h!(y, x, _ , p)
try
# MTK.jl supports a `u` argument in `h_ip` function but not this package. We set
# `u` as a vector of nothing and `h_ip` function will presumably throw an
# MethodError if this argument is used inside the function
# MTK.jl supports a `u` argument in `h_ip` function but not this package. We
# set `u` as a vector of nothing and `h_ip` function will presumably throw
# an MethodError if this argument is used inside the function
h_ip(y, x, u_nothing, p, nothing)
catch err
if err isa MethodError
error("NonLinModel only support strictly proper systems (no manipulated "*
"input argument u in the output function h)")
error("NonLinModel only support strictly proper systems (no "*
"manipulated input argument u in the output function h)")
else
rethrow()
end
Expand Down
31 changes: 18 additions & 13 deletions docs/src/manual/nonlinmpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ The option `Cwt=Inf` disables the slack variable `ϵ` for constraint softening.

```@example man_nonlin
using JuMP; unset_time_limit_sec(nmpc.optim) # hide
res_ry = sim!(nmpc, N, [180.0], plant=plant, x_0=[0, 0], x̂_0=[0, 0, 0])
ry = [180.0]
res_ry = sim!(nmpc, N, ry, plant=plant, x_0=[0, 0], x̂_0=[0, 0, 0])
plot(res_ry)
savefig("plot3_NonLinMPC.svg"); nothing # hide
```
Expand All @@ -142,7 +143,7 @@ inverted position, the closed-loop response to a step disturbances of 10° is al
satisfactory:

```@example man_nonlin
res_yd = sim!(nmpc, N, [180.0], plant=plant, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10])
res_yd = sim!(nmpc, N, ry, plant=plant, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10])
plot(res_yd)
savefig("plot4_NonLinMPC.svg"); nothing # hide
```
Expand Down Expand Up @@ -185,8 +186,10 @@ Kalman Filter similar to the previous one (``\mathbf{y^m} = θ`` and ``\mathbf{y
```@example man_nonlin
h2(x, _ , _ ) = [180/π*x[1], x[2]]
nu, nx, ny = 1, 2, 2
model2 = setname!(NonLinModel(f, h2, Ts, nu, nx, ny; p=p_model), u=vu, x=vx, y=[vy; vx[2]])
plant2 = setname!(NonLinModel(f, h2, Ts, nu, nx, ny; p=p_plant), u=vu, x=vx, y=[vy; vx[2]])
model2 = NonLinModel(f, h2, Ts, nu, nx, ny; p=p_model)
model2 = setname!(model2, u=vu, x=vx, y=[vy; vx[2]])
plant2 = NonLinModel(f, h2, Ts, nu, nx, ny; p=p_plant)
plant2 = setname!(plant2, u=vu, x=vx, y=[vy; vx[2]])
estim2 = UnscentedKalmanFilter(model2; σQ, σR, nint_u, σQint_u, i_ym=[1])
```

Expand All @@ -212,7 +215,8 @@ setpoint is similar:

```@example man_nonlin
unset_time_limit_sec(empc.optim) # hide
res2_ry = sim!(empc, N, [180, 0], plant=plant2, x_0=[0, 0], x̂_0=[0, 0, 0])
ry = [180.0, 0]
res2_ry = sim!(empc, N, ry, plant=plant2, x_0=[0, 0], x̂_0=[0, 0, 0])
plot(res2_ry, ploty=[1])
savefig("plot5_NonLinMPC.svg"); nothing # hide
```
Expand All @@ -232,7 +236,7 @@ Dict(:W_nmpc => calcW(res_ry), :W_empc => calcW(res2_ry))
Also, for a 10° step disturbance:

```@example man_nonlin
res2_yd = sim!(empc, N, [180; 0]; plant=plant2, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10, 0])
res2_yd = sim!(empc, N, ry; plant=plant2, x_0=[π, 0], x̂_0=[π, 0, 0], y_step=[10, 0])
plot(res2_yd, ploty=[1])
savefig("plot6_NonLinMPC.svg"); nothing # hide
```
Expand Down Expand Up @@ -304,7 +308,7 @@ In addition to the 180° setpoint response:
```@example man_nonlin
using Logging # hide
res3_ry = with_logger(ConsoleLogger(stderr, Error)) do # hide
res3_ry = sim!(nmpc2, N, [180; 0]; plant=plant2, x_0=[0, 0], x̂_0=[0, 0, 0]);
res3_ry = sim!(nmpc2, N, ry; plant=plant2, x_0=[0, 0], x̂_0=[0, 0, 0]);
end # hide
nothing # hide
```
Expand All @@ -316,7 +320,7 @@ function plotWithPower(res, Pmax)
t, τ, ω = res.T_data, res.U_data[1, :], res.X_data[2, :]
P, Pmax = τ.*ω, fill(Pmax, size(t))
plt1 = plot(res, ploty=[1])
plt2 = plot(t, P, label=raw"$P$", ylabel=raw"$P$ (W)", xlabel="Time (s)", legend=:right)
plt2 = plot(t, P, label="\$P\$", ylabel="\$P\$ (W)", xlabel="Time (s)", legend=:right)
plot!(plt2, t, Pmax, label=raw"$P_\mathrm{max}$", linestyle=:dot, linewidth=1.5)
return plot(plt1, plt2, layout=(2,1))
end
Expand Down Expand Up @@ -353,7 +357,8 @@ mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])
The linear controller satisfactorily rejects the 10° step disturbance:

```@example man_nonlin
res_lin = sim!(mpc, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
ry = [180.0]
res_lin = sim!(mpc, N, ry; plant, x_0=[π, 0], y_step=[10])
plot(res_lin)
savefig("plot9_NonLinMPC.svg"); nothing # hide
```
Expand Down Expand Up @@ -392,7 +397,7 @@ mpc2 = setconstraint!(mpc2; umin, umax)
does slightly improve the rejection of the step disturbance:

```@example man_nonlin
res_lin2 = sim!(mpc2, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
res_lin2 = sim!(mpc2, N, ry; plant, x_0=[π, 0], y_step=[10])
plot(res_lin2)
savefig("plot10_NonLinMPC.svg"); nothing # hide
```
Expand All @@ -412,7 +417,7 @@ Superimposing the previous disturbance rejection to the newer one gives almost i
results:

```@example man_nonlin
res_ms = sim!(mpc_ms, N, [180.0]; plant, x_0=[π, 0], y_step=[10])
res_ms = sim!(mpc_ms, N, ry; plant, x_0=[π, 0], y_step=[10])
plot!(res_ms)
savefig("plot10b_NonLinMPC.svg"); nothing # hide
```
Expand All @@ -426,7 +431,7 @@ For example, the 180° setpoint response from 0° is unsatisfactory since the pr
poor in the first quadrant:

```@example man_nonlin
res_lin3 = sim!(mpc2, N, [180.0]; plant, x_0=[0, 0])
res_lin3 = sim!(mpc2, N, ry; plant, x_0=[0, 0])
plot(res_lin3)
savefig("plot11_NonLinMPC.svg"); nothing # hide
```
Expand Down Expand Up @@ -483,7 +488,7 @@ operating point. The [`SimResult`](@ref) object is for plotting purposes only. T
[`LinMPC`](@ref) performances are similar to the nonlinear MPC, both for the 180° setpoint:

```@example man_nonlin
x_0 = [0, 0]; x̂_0 = [0, 0, 0]; ry = [180]
x_0 = [0, 0]; x̂_0 = [0, 0, 0];
res_slin = sim_adapt!(mpc3, model, N, ry, plant, x_0, x̂_0)
plot(res_slin)
savefig("plot12_NonLinMPC.svg"); nothing # hide
Expand Down
6 changes: 3 additions & 3 deletions ext/LinearMPCext.jl
Original file line number Diff line number Diff line change
Expand Up @@ -252,21 +252,21 @@ documentation for more details on the supported features and how to generate cod
```jldoctest
julia> import LinearMPC, JuMP, DAQP;

julia> mpc1 = LinMPC(LinModel(tf(2, [10, 1]), 1.0); optim=JuMP.Model(DAQP.Optimizer));
julia> mpc1 = LinMPC(LinModel(tf(2, [9, 1]), 1.0); optim=JuMP.Model(DAQP.Optimizer));

julia> preparestate!(mpc1, [1.0]);

julia> u = moveinput!(mpc1, [10.0]); round.(u, digits=6)
1-element Vector{Float64}:
17.577311
17.527485

julia> mpc2 = LinearMPC.MPC(mpc1);

julia> x̂ = LinearMPC.correct_state!(mpc2, [1.0]);

julia> u = LinearMPC.compute_control(mpc2, x̂, r=[10.0]); round.(u, digits=6)
1-element Vector{Float64}:
17.577311
17.527485
```
"""
LinearMPC.MPC(mpc::ModelPredictiveControl.LinMPC) = convert(LinearMPC.MPC, mpc)
Expand Down
14 changes: 10 additions & 4 deletions src/controller/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,20 @@ prediction horizon ``H_p``.

# Examples
```jldoctest
julia> mpc = LinMPC(KalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4.0)), σR=[√25]), Hp=1, Hc=1);
julia> model1 = LinModel(ss(0.1, 0.5, 1, 0, 4.0));

julia> mpc.estim.model.A[1], mpc.estim.cov.R̂[1], mpc.weights.M_Hp[1], mpc.weights.Ñ_Hc[1]
julia> mpc = LinMPC(KalmanFilter(model1, σR=[√25]), Hp=1, Hc=1);

julia> estim = mpc.estim;

julia> estim.model.A[1], estim.cov.R̂[1], mpc.weights.M_Hp[1], mpc.weights.Ñ_Hc[1]
(0.1, 25.0, 1.0, 0.1)

julia> setmodel!(mpc, LinModel(ss(0.42, 0.5, 1, 0, 4.0)); R̂=[9], M_Hp=[10], Nwt=[0.666]);
julia> model2 = LinModel(ss(0.42, 0.5, 1, 0, 4.0));

julia> setmodel!(mpc, model2; R̂=[9], M_Hp=[10], Nwt=[0.666]);

julia> mpc.estim.model.A[1], mpc.estim.cov.R̂[1], mpc.weights.M_Hp[1], mpc.weights.Ñ_Hc[1]
julia> estim.model.A[1], estim.cov.R̂[1], mpc.weights.M_Hp[1], mpc.weights.Ñ_Hc[1]
(0.42, 9.0, 10.0, 0.666)
```
"""
Expand Down
2 changes: 1 addition & 1 deletion src/controller/linmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ Use custom state estimator `estim` to construct `LinMPC`.

# Examples
```jldoctest
julia> estim = KalmanFilter(LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 4), i_ym=[2]);
julia> estim = KalmanFilter(LinModel([tf(6, [9, 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:
Expand Down
8 changes: 4 additions & 4 deletions src/controller/nonlinmpc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,10 @@ This controller allocates memory at each time step for the optimization.

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

julia> mpc = NonLinMPC(model, Hp=20, Hc=10, transcription=MultipleShooting())
NonLinMPC controller with a sample time Ts = 10.0 s:
NonLinMPC controller with a sample time Ts = 5.0 s:
├ estimator: UnscentedKalmanFilter
├ model: NonLinModel
├ optimizer: Ipopt
Expand Down Expand Up @@ -385,12 +385,12 @@ Use custom state estimator `estim` to construct `NonLinMPC`.

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->0.5x+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

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

julia> mpc = NonLinMPC(estim, Hp=20, Cwt=1e4)
NonLinMPC controller with a sample time Ts = 10.0 s:
NonLinMPC controller with a sample time Ts = 5.0 s:
├ estimator: UnscentedKalmanFilter
├ model: NonLinModel
├ optimizer: Ipopt
Expand Down
14 changes: 8 additions & 6 deletions src/estimator/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ If applicable, it also sets the error covariance `estim.cov.P̂` to `estim.cov.P

# Examples
```jldoctest
julia> estim = SteadyKalmanFilter(LinModel(tf(3, [10, 1]), 0.5), nint_ym=[2], direct=false);
julia> estim = Luenberger(LinModel(tf(3, [10, 1]), 0.5), nint_ym=[2], direct=false);

julia> u = [1]; y = [3 - 0.1]; x̂ = round.(initstate!(estim, u, y), digits=3)
3-element Vector{Float64}:
Expand Down Expand Up @@ -277,11 +277,11 @@ Calling a [`StateEstimator`](@ref) object calls this `evaloutput` method.

# Examples
```jldoctest
julia> kf = SteadyKalmanFilter(setop!(LinModel(tf(2, [10, 1]), 5), yop=[20]), direct=false);
julia> kf = KalmanFilter(setop!(LinModel(tf(2, [10, 1]), 5), yop=[6]), direct=false);

julia> ŷ = evaloutput(kf)
1-element Vector{Float64}:
20.0
6.0
```
"""
function evaloutput(estim::StateEstimator{NT}, d=estim.buffer.empty) where NT <: Real
Expand Down Expand Up @@ -318,13 +318,13 @@ delayed/predictor (2.) formulation:

# Examples
```jldoctest
julia> estim2 = SteadyKalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4)), nint_ym=0, direct=true);
julia> estim2 = KalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4)), nint_ym=0, direct=true);

julia> x̂ = round.(preparestate!(estim2, [1]), digits=2)
1-element Vector{Float64}:
0.5

julia> estim1 = SteadyKalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4)), nint_ym=0, direct=false);
julia> estim1 = KalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4)), nint_ym=0, direct=false);

julia> x̂ = preparestate!(estim1, [1])
1-element Vector{Float64}:
Expand Down Expand Up @@ -462,7 +462,9 @@ augmented model is not verified (see Extended Help for more info).

# Examples
```jldoctest
julia> kf = KalmanFilter(LinModel(ss(0.1, 0.5, 1, 0, 4.0)), σQ=[√4.0], σQint_ym=[√0.25]);
julia> model = LinModel(ss(0.1, 0.5, 1, 0, 4.0));

julia> kf = KalmanFilter(model, σQ=[√4.0], σQint_ym=[√0.25]);

julia> kf.model.A[], kf.cov.Q̂[1, 1], kf.cov.Q̂[2, 2]
(0.1, 4.0, 0.25)
Expand Down
2 changes: 1 addition & 1 deletion src/estimator/internal_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ 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])
julia> estim = InternalModel(LinModel([tf(3, [9, 1]); tf(2, [4, 1])], 0.5), i_ym=[2])
InternalModel estimator with a sample time Ts = 0.5 s:
├ model: LinModel
├ direct: true
Expand Down
8 changes: 4 additions & 4 deletions src/estimator/kalman.jl
Original file line number Diff line number Diff line change
Expand Up @@ -661,10 +661,10 @@ This estimator is allocation-free if `model` simulations do not allocate.

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->x, 5, 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:
UnscentedKalmanFilter estimator with a sample time Ts = 5.0 s:
├ model: NonLinModel
├ direct: true
└ dimensions:
Expand Down Expand Up @@ -1032,9 +1032,9 @@ differentiation. This estimator is allocation-free if `model` simulations do not

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.2x+u, (x,_,_)->-3x, 5.0, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->0.2x+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

julia> estim = ExtendedKalmanFilter(model, σQ=[2], σQint_ym=[2], σP_0=[0.1], σPint_ym_0=[0.1])
julia> estim = ExtendedKalmanFilter(model, σQ=[2], σQint_ym=[2], σP_0=[0.1])
ExtendedKalmanFilter estimator with a sample time Ts = 5.0 s:
├ model: NonLinModel
├ jacobian: AutoForwardDiff
Expand Down
14 changes: 7 additions & 7 deletions src/estimator/manual.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ examples.
```jldoctest
julia> model = LinModel([tf(3, [30, 1]); tf(-2, [5, 1])], 0.5);

julia> estim = ManualEstimator(model, nint_ym=0) # disable augmentation with integrators
julia> estim = ManualEstimator(model, nint_ym=0) # no augmentation with integrators
ManualEstimator estimator with a sample time Ts = 0.5 s:
├ model: LinModel
└ dimensions:
Expand Down Expand Up @@ -109,14 +109,14 @@ ManualEstimator estimator with a sample time Ts = 0.5 s:
initstate!(estim, [0], [0])
y_data, ŷ_data = zeros(5), zeros(5)
for i=1:5
y = model() # simulated measurement
x̂ = preparestate!(estim, y) # correct nonlinear MHE state estimate
ŷ = estim() # nonlinear MHE estimated output
setstate!(mpc, x̂) # update MPC with the MHE corrected state
y = model() # simulated measurement
x̂ = preparestate!(estim, y) # correct nonlinear MHE state estimate
ŷ = estim() # nonlinear MHE estimated output
setstate!(mpc, x̂) # update MPC with the MHE corrected state
u = moveinput!(mpc, [0])
y_data[i], ŷ_data[i] = y[1], ŷ[1]
updatestate!(estim, u, y) # update nonlinear MHE estimation
updatestate!(model, u .+ 0.5) # update simulator with load disturbance
updatestate!(estim, u, y) # update nonlinear MHE estimation
updatestate!(model, u.+0.5) # update simulator with load disturbance
end
return collect([y_data ŷ_data]')
end;
Expand Down
4 changes: 2 additions & 2 deletions src/estimator/mhe/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ at each time step for the optimization.

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->2x, 10.0, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->0.1x+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

julia> estim = MovingHorizonEstimator(model, He=5, σR=[1], σP_0=[0.01])
MovingHorizonEstimator estimator with a sample time Ts = 10.0 s:
MovingHorizonEstimator estimator with a sample time Ts = 5.0 s:
├ model: NonLinModel
├ optimizer: Ipopt
├ transcription: SingleShooting
Expand Down
4 changes: 2 additions & 2 deletions src/model/linearization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ the `jacobian` keyword argument at the construction of `model` to swap the backe

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->x.^3 + u, (x,_,_)->x, 0.1, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->x.^3+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

julia> linmodel = linearize(model, x=[10.0], u=[0.0]);

Expand Down Expand Up @@ -145,7 +145,7 @@ The keyword arguments are identical to [`linearize`](@ref). The code is allocati

# Examples
```jldoctest
julia> model = NonLinModel((x,u,_,_)->x.^3 + u, (x,_,_)->x, 0.1, 1, 1, 1, solver=nothing);
julia> model = NonLinModel((x,u,_,_)->x.^3+u, (x,_,_)->x, 5, 1, 1, 1, solver=nothing);

julia> linmodel = linearize(model, x=[10.0], u=[0.0]); linmodel.A
1×1 Matrix{Float64}:
Expand Down
Loading
Loading