Skip to content

Commit

Permalink
Merge pull request #93 from JuliaControl/doc_correction
Browse files Browse the repository at this point in the history
added: doc correction and reduce allocations for all Kalman filters
  • Loading branch information
franckgaga authored Aug 13, 2024
2 parents d10a731 + e8b3784 commit 5bdfd14
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 107 deletions.
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 = "0.23.0"
version = "0.23.1"

[deps]
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ for more detailed examples.
- [x] manipulated inputs
- [x] measured outputs
- [x] bumpless manual to automatic transfer for control with a proper initial estimate
- [ ] estimators in two possible forms:
- [x] estimators in two possible forms:
- [x] filter (or current) form to improve accuracy and robustness
- [x] predictor (or delayed) form to reduce computational load
- [ ] filter (or current) form to improve accuracy and robustness
- [x] moving horizon estimator in two formulations:
- [x] linear plant models (quadratic optimization)
- [x] nonlinear plant models (nonlinear optimization)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/manual/nonlinmpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ nothing # hide

The [`setmodel!`](@ref) method must be called after solving the optimization problem with
[`moveinput!`](@ref) and before updating the state estimate with [`updatestate!`](@ref),
that is, when both ``\mathbf{u}(k)`` and ``\mathbf{x̂}_{k-1}(k)`` are available as the new
that is, when both ``\mathbf{u}(k)`` and ``\mathbf{x̂}_{k}(k)`` are available as the new
operating point. The [`SimResult`](@ref) object is for plotting purposes only. The adaptive
[`LinMPC`](@ref) performances are similar to the nonlinear MPC, both for the 180° setpoint:

Expand Down
8 changes: 4 additions & 4 deletions src/controller/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SteadyKalmanFil
Terminal constraints provide closed-loop stability guarantees on the nominal plant model.
They can render an unfeasible problem however. In practice, a sufficiently large
prediction horizon ``H_p`` without terminal constraints is typically enough for
stability. If `mpc.estim.direct==true`, the state is estimated at ``i = k`` (the current
time step), otherwise at ``i = k - 1``. Note that terminal constraints are applied on the
augmented state vector ``\mathbf{x̂}`` (see [`SteadyKalmanFilter`](@ref) for details on
augmentation).
stability. If `mpc.estim.direct==true`, the estimator computes the states at ``i = k``
(the current time step), otherwise at ``i = k - 1``. Note that terminal constraints are
applied on the augmented state vector ``\mathbf{x̂}`` (see [`SteadyKalmanFilter`](@ref)
for details on augmentation).
For variable constraints, the bounds can be modified after calling [`moveinput!`](@ref),
that is, at runtime, but not the softness parameters ``\mathbf{c}``. It is not possible
Expand Down
6 changes: 3 additions & 3 deletions src/estimator/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,13 @@ end
Return empty matrices, and `x̂op` & `f̂op` vectors, if `model` is not a [`LinModel`](@ref).
"""
function augment_model(model::SimModel{NT}, As, args... ; verify_obsv=false) where NT<:Real
nu, nx, nd = model.nu, model.nx, model.nd
nu, nx, nd, ny = model.nu, model.nx, model.nd, model.ny
nxs = size(As, 1)
= zeros(NT, 0, nx+nxs)
B̂u = zeros(NT, 0, nu)
= zeros(NT, 0, nx+nxs)
= zeros(NT, ny, 0)
B̂d = zeros(NT, 0, nd)
D̂d = zeros(NT, 0, nd)
D̂d = zeros(NT, ny, 0)
x̂op, f̂op = [model.xop; zeros(nxs)], [model.fop; zeros(nxs)]
return Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op
end
Expand Down
6 changes: 4 additions & 2 deletions src/estimator/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ in which ``\mathbf{Ĉ^m, D̂_d^m}`` are the rows of `estim.Ĉ, estim.D̂d` th
measured outputs ``\mathbf{y^m}``.
"""
function init_estimate!(estim::StateEstimator, ::LinModel, y0m, d0, u0)
Â, B̂u, Ĉ, B̂d, D̂d = estim.Â, estim.B̂u, estim.Ĉ, estim.B̂d, estim.D̂d
Ĉm, D̂dm = @views Ĉ[estim.i_ym, :], D̂d[estim.i_ym, :] # measured outputs ym only
Â, B̂u, B̂d = estim.Â, estim.B̂u, estim.B̂d
Ĉm, D̂dm = estim.Ĉm, estim.D̂dm
# TODO: use estim.buffer.x̂ to reduce allocations
estim.x̂0 .= [I - Â; Ĉm]\[B̂u*u0 + B̂d*d0 + estim.f̂op - estim.x̂op; y0m - D̂dm*d0]
return nothing
Expand Down Expand Up @@ -390,6 +390,8 @@ function setmodel_estimator!(estim::StateEstimator, model, _ , _ , _ , Q̂, R̂)
estim.Ĉ .=
estim.B̂d .= B̂d
estim.D̂d .= D̂d
estim.Ĉm .= @views Ĉ[estim.i_ym, :]
estim.D̂dm .= @views D̂d[estim.i_ym, :]
# --- update state estimate and its operating points ---
estim.x̂0 .+= estim.x̂op # convert x̂0 to x̂ with the old operating point
estim.x̂op .= x̂op
Expand Down
21 changes: 13 additions & 8 deletions src/estimator/internal_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ struct InternalModel{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
Bs::Matrix{NT}
Cs::Matrix{NT}
Ds::Matrix{NT}
::Matrix{NT}
B̂u::Matrix{NT}
::Matrix{NT}
B̂d::Matrix{NT}
D̂d::Matrix{NT}
::Matrix{NT}
B̂u ::Matrix{NT}
::Matrix{NT}
B̂d ::Matrix{NT}
D̂d ::Matrix{NT}
Ĉm ::Matrix{NT}
D̂dm ::Matrix{NT}
Âs::Matrix{NT}
B̂s::Matrix{NT}
direct::Bool
Expand All @@ -36,6 +38,7 @@ struct InternalModel{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
nxs = size(As,1)
nx̂ = model.nx
Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op = matrices_internalmodel(model)
Ĉm, D̂dm = Ĉ[i_ym,:], D̂d[i_ym,:]
Âs, B̂s = init_internalmodel(As, Bs, Cs, Ds)
lastu0 = zeros(NT, nu)
# x̂0 and x̂d are same object (updating x̂d will update x̂0):
Expand All @@ -50,7 +53,7 @@ struct InternalModel{NT<:Real, SM<:SimModel} <: StateEstimator{NT}
lastu0, x̂op, f̂op, x̂0, x̂d, x̂s, ŷs,
i_ym, nx̂, nym, nyu, nxs,
As, Bs, Cs, Ds,
Â, B̂u, Ĉ, B̂d, D̂d,
Â, B̂u, Ĉ, B̂d, D̂d, Ĉm, D̂dm,
Âs, B̂s,
direct, corrected,
buffer
Expand Down Expand Up @@ -156,8 +159,8 @@ function matrices_internalmodel(model::LinModel)
end
"Return empty matrices, and `x̂op` & `f̂op` vectors, if `model` is not a [`LinModel`](@ref)."
function matrices_internalmodel(model::SimModel{NT}) where NT<:Real
nu, nx, nd = model.nu, model.nx, model.nd
Â, B̂u, Ĉ, B̂d, D̂d = zeros(NT,0,nx), zeros(NT,0,nu), zeros(NT,0,nx), zeros(NT,0,nd), zeros(NT,0,nd)
nu, nx, nd, ny = model.nu, model.nx, model.nd, model.ny
Â, B̂u, Ĉ, B̂d, D̂d = zeros(NT,0,nx), zeros(NT,0,nu), zeros(NT,ny,0), zeros(NT,0,nd), zeros(NT,ny,0)
x̂op, f̂op = copy(model.xop), copy(model.fop)
return Â, B̂u, Ĉ, B̂d, D̂d, x̂op, f̂op
end
Expand Down Expand Up @@ -225,6 +228,8 @@ function setmodel_estimator!(estim::InternalModel, model, _ , _ , _ , _ , _ )
estim.Ĉ .=
estim.B̂d .= B̂d
estim.D̂d .= D̂d
estim.Ĉm .= @views Ĉ[estim.i_ym,:]
estim.D̂dm .= @views D̂d[estim.i_ym,:]
# --- update state estimate and its operating points ---
estim.x̂0 .+= estim.x̂op # convert x̂0 to x̂ with the old operating point
estim.x̂op .= x̂op
Expand Down
Loading

2 comments on commit 5bdfd14

@franckgaga
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

  • Luenberger, SteadyKalmanFilter, KalmanFilter and UnscentedKalmanFilter now produce 0 allocation with preparestate! and updatestate! calls
  • doc: minor corrections

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/113081

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.23.1 -m "<description of version>" 5bdfd14445b2a091e7cc613f0d1c7e65f9405117
git push origin v0.23.1

Please sign in to comment.