Skip to content

Commit 2fcec1e

Browse files
committed
added: MHE predict! for LinModel
1 parent ff17732 commit 2fcec1e

File tree

1 file changed

+44
-19
lines changed

1 file changed

+44
-19
lines changed

src/estimator/mhe.jl

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
const DEFAULT_LINMHE_OPTIMIZER = OSQP.MathOptInterfaceOSQP.Optimizer
22
const DEFAULT_NONLINMHE_OPTIMIZER = optimizer_with_attributes(Ipopt.Optimizer,"sb"=>"yes")
33

4-
"Include all the data for the constraints of [`MovingHorizonEstimator`](@ref)"
4+
@doc raw"""
5+
Include all the data for the constraints of [`MovingHorizonEstimator`](@ref).
6+
7+
The bounds on the estimated state at arrival ``\mathbf{x̂}_k(k-N_k+1)`` is separated from
8+
the other state constraints ``\mathbf{x̂}_k(k-N_k+2), \mathbf{x̂}_k(k-N_k+3), ...`` since
9+
the former is always a linear inequality constraint (it's a decision variable). The fields
10+
`x̂min` and `x̂max` refer to the bounds at the arrival, and `X̂min` and `X̂max`, the others.
11+
"""
512
struct EstimatorConstraint{NT<:Real}
613
Ẽx̂ ::Matrix{NT}
714
Fx̂ ::Vector{NT}
@@ -144,7 +151,7 @@ This estimator can handle constraints on the estimates, see [`setconstraint!`](@
144151
Additionally, `model` is not linearized like the [`ExtendedKalmanFilter`](@ref), and the
145152
probability distribution is not approximated like the [`UnscentedKalmanFilter`](@ref). The
146153
computational costs are drastically higher, however, since it minimizes the following
147-
nonlinear objective function at each discrete time ``k``:
154+
objective function at each discrete time ``k``:
148155
```math
149156
\min_{\mathbf{x̂}_k(k-N_k+1), \mathbf{Ŵ}} \mathbf{x̄}' \mathbf{P̄}^{-1} \mathbf{x̄}
150157
+ \mathbf{Ŵ}' \mathbf{Q̂}_{N_k}^{-1} \mathbf{Ŵ}
@@ -164,8 +171,11 @@ and the covariances are repeated ``N_k`` times:
164171
\mathbf{R̂}_{N_k} &= \text{diag}\mathbf{(R̂,R̂,...,R̂)}
165172
\end{aligned}
166173
```
167-
The estimation horizon ``H_e`` limits the window length ``N_k = \min(k+1, H_e)``. The
168-
vectors ``\mathbf{Ŵ}`` and ``\mathbf{V̂}`` encompass the estimated process noise
174+
The estimation horizon ``H_e`` limits the window length:
175+
```math
176+
N_k = \min(k+1, H_e)
177+
```
178+
The vectors ``\mathbf{Ŵ}`` and ``\mathbf{V̂}`` encompass the estimated process noise
169179
``\mathbf{ŵ}(k-j)`` and sensor noise ``\mathbf{v̂}(k-j)`` from ``j=N_k-1`` to ``0``. The
170180
Extended Help explicitly defines the two vectors. See [`SteadyKalmanFilter`](@ref) for
171181
details on ``\mathbf{R̂}, \mathbf{Q̂}`` covariances and model augmentation. The process
@@ -179,7 +189,7 @@ model is identical to the one in [`UnscentedKalmanFilter`](@ref) documentation.
179189
# Arguments
180190
- `model::SimModel` : (deterministic) model for the estimations.
181191
- `He=nothing`: estimation horizon ``H_e``, must be specified.
182-
- `optim=default_mhe_optim(model)` : quadratic or nonlinear optimizer used in the moving
192+
- `optim=default_optim_mhe(model)` : quadratic or nonlinear optimizer used in the moving
183193
horizon estimator, provided as a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
184194
(default to [`Ipopt.jl`](https://github.com/jump-dev/Ipopt.jl), or [`OSQP.jl`](https://osqp.org/docs/parsers/jump.html)
185195
if `model` is a [`LinModel`](@ref)).
@@ -191,7 +201,7 @@ model is identical to the one in [`UnscentedKalmanFilter`](@ref) documentation.
191201
julia> model = NonLinModel((x,u,_)->0.1x+u, (x,_)->2x, 10.0, 1, 1, 1);
192202
193203
julia> estim = MovingHorizonEstimator(model, He=5, σR=[1], σP0=[0.01])
194-
MovingHorizonEstimator estimator with a sample time Ts = 10.0 s, NonLinModel and:
204+
MovingHorizonEstimator estimator with a sample time Ts = 10.0 s, Ipopt optimizer, NonLinModel and:
195205
5 estimation steps He
196206
1 manipulated inputs u (0 integrating states)
197207
2 states x̂
@@ -238,7 +248,7 @@ function MovingHorizonEstimator(
238248
nint_ym ::IntVectorOrInt = default_nint(model, i_ym, nint_u),
239249
σQint_ym ::Vector = fill(1, max(sum(nint_ym), 0)),
240250
σP0int_ym::Vector = fill(1, max(sum(nint_ym), 0)),
241-
optim::JM = default_mhe_optim(model),
251+
optim::JM = default_optim_mhe(model),
242252
) where {NT<:Real, SM<:SimModel{NT}, JM<:JuMP.GenericModel}
243253
# estimated covariances matrices (variance = σ²) :
244254
P̂0 = Hermitian(diagm(NT[σP0; σP0int_u; σP0int_ym].^2), :L)
@@ -251,9 +261,9 @@ function MovingHorizonEstimator(
251261
end
252262

253263
"Return a `JuMP.Model` with OSQP optimizer if `model` is a [`LinModel`](@ref)."
254-
default_mhe_optim(::LinModel) = JuMP.Model(DEFAULT_LINMHE_OPTIMIZER, add_bridges=false)
264+
default_optim_mhe(::LinModel) = JuMP.Model(DEFAULT_LINMHE_OPTIMIZER, add_bridges=false)
255265
"Else, return it with Ipopt optimizer."
256-
default_mhe_optim(::SimModel) = JuMP.Model(DEFAULT_NONLINMHE_OPTIMIZER, add_bridges=false)
266+
default_optim_mhe(::SimModel) = JuMP.Model(DEFAULT_NONLINMHE_OPTIMIZER, add_bridges=false)
257267

258268
@doc raw"""
259269
MovingHorizonEstimator(model, He, i_ym, nint_u, nint_ym, P̂0, Q̂, R̂, optim)
@@ -618,7 +628,7 @@ end
618628
619629
Set the constraint parameters of `estim` [`MovingHorizonEstimator`](@ref).
620630
621-
The moving horizon estimator supports constraints on the estimated states ``\mathbf{x̂}``,
631+
The moving horizon estimator supports constraints on the estimated state ``\mathbf{x̂}``,
622632
process noise ``\mathbf{ŵ}`` and sensor noise ``\mathbf{v̂}``:
623633
```math
624634
\begin{alignat*}{3}
@@ -627,9 +637,9 @@ process noise ``\mathbf{ŵ}`` and sensor noise ``\mathbf{v̂}``:
627637
\mathbf{v̂_{min}} ≤&&\ \mathbf{v̂}(k-j+1) &≤ \mathbf{v̂_{max}} &&\qquad j = N_k, N_k - 1, ... , 1
628638
\end{alignat*}
629639
```
630-
Note that state and process noise constraints are applied on augmented model vectors (see
631-
the extended help of [`SteadyKalmanFilter`](@ref) for details on augmentation). Also,
632-
constraining the estimated sensor noises is equivalent to constraining the innovation term
640+
Note that the state and process noise constraints are applied on augmented model vectors
641+
(see the extended help of [`SteadyKalmanFilter`](@ref) for details on augmentation). Also,
642+
constraining the estimated sensor noises is equivalent to constraining the innovation term,
633643
since ``\mathbf{v̂}(k) = \mathbf{y^m}(k) - \mathbf{ŷ^m}(k)`` in the MHE. See Extended Help
634644
for time-varying constraints.
635645
@@ -653,7 +663,7 @@ for time-varying constraints.
653663
julia> estim = MovingHorizonEstimator(LinModel(ss(0.5,1,1,0,1)), He=3);
654664
655665
julia> estim = setconstraint!(estim, x̂min=[-50, -50], x̂max=[50, 50])
656-
MovingHorizonEstimator estimator with a sample time Ts = 1.0 s, LinModel and:
666+
MovingHorizonEstimator estimator with a sample time Ts = 1.0 s, OSQP optimizer, LinModel and:
657667
3 estimation steps He
658668
1 manipulated inputs u (0 integrating states)
659669
2 states x̂
@@ -822,6 +832,16 @@ function setnonlincon!(estim::MovingHorizonEstimator, ::NonLinModel)
822832
return nothing
823833
end
824834

835+
function Base.show(io::IO, estim::MovingHorizonEstimator)
836+
nu, nd = estim.model.nu, estim.model.nd
837+
nx̂, nym, nyu = estim.nx̂, estim.nym, estim.nyu
838+
n = maximum(ndigits.((nu, nx̂, nym, nyu, nd))) + 1
839+
println(io, "$(typeof(estim).name.name) estimator with a sample time "*
840+
"Ts = $(estim.model.Ts) s, $(solver_name(estim.optim)) optimizer, "*
841+
"$(typeof(estim.model).name.name) and:")
842+
print_estim_dim(io, estim, n)
843+
end
844+
825845
"Print the overall dimensions of the state estimator `estim` with left padding `n`."
826846
function print_estim_dim(io::IO, estim::MovingHorizonEstimator, n)
827847
nu, nd = estim.model.nu, estim.model.nd
@@ -902,9 +922,6 @@ function update_estimate!(estim::MovingHorizonEstimator{NT}, u, ym, d) where NT<
902922
isfinite(J0) || (Z̃0 = [estim.x̂arr_old; zeros(NT, nŴ)])
903923
set_start_value.(Z̃var, Z̃0)
904924
# ------- solve optimization problem --------------
905-
# at start, when time windows are not filled, some decision variables are fixed at 0:
906-
# unfix.(Z̃var[is_fixed.(Z̃var)])
907-
# fix.(Z̃var[(1 + nx̂*(Nk+1)):end], 0)
908925
try
909926
optimize!(optim)
910927
catch err
@@ -1043,14 +1060,22 @@ function obj_nonlinprog(
10431060
end
10441061

10451062
"""
1046-
predict!(V̂, X̂, estim::MovingHorizonEstimator, model::SimModel, Z̃) -> V̂, X̂
1063+
predict!(V̂, X̂, estim::MovingHorizonEstimator, model::LinModel, Z̃) -> V̂, X̂
10471064
1048-
Compute the `V̂` vector and `X̂` vectors for the `MovingHorizonEstimator`.
1065+
Compute the `V̂` vector and `X̂` vectors for the `MovingHorizonEstimator` and `LinModel`.
10491066
10501067
The method mutates `V̂` and `X̂` vector arguments. The vector `V̂` is the estimated sensor
10511068
noises `V̂` from ``k-N_k+1`` to ``k``. The `X̂` vector is estimated states from ``k-N_k+2`` to
10521069
``k+1``.
10531070
"""
1071+
function predict!(V̂, X̂, estim::MovingHorizonEstimator, ::LinModel, Z̃)
1072+
nX̂, nYm = estim.nx̂*estim.Nk[], estim.nym*estim.Nk[]
1073+
V̂[1:nYm] = estim.*+ estim.F
1074+
X̂[1:nX̂] = estim.con.Ẽx̂*+ estim.con.Fx̂
1075+
return V̂, X̂
1076+
end
1077+
1078+
"Compute the two vectors when `model` is not a `LinModel`."
10541079
function predict!(
10551080
V̂, X̂, estim::MovingHorizonEstimator, model::SimModel, Z̃::Vector{T}
10561081
) where {T<:Real}

0 commit comments

Comments
 (0)