Skip to content

Commit 1edfa8f

Browse files
committed
added: covestim arg. in both MovingHorizonEstimator constructors
1 parent f8de776 commit 1edfa8f

2 files changed

Lines changed: 39 additions & 28 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ModelPredictiveControl"
22
uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c"
3-
version = "2.4.2"
3+
version = "2.4.3"
44
authors = ["Francis Gagnon"]
55

66
[deps]

src/estimator/mhe/construct.jl

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ transcription for now.
313313
- `hessian=false` : an `AbstractADType` backend for the Hessian of the Lagrangian, see
314314
`gradient` above for the options. The default `false` skip it and use the quasi-Newton
315315
method of `optim` (see Extended Help).
316+
- `covestim=nothing`: a [`StateEstimator`](@ref) object for the arrival covariance estimation
317+
``\mathbf{P̂}_{k-N_k}(k-N_k+p)``, `nothing` means the default choice (see Extended Help).
316318
- `direct=true`: construct with a direct transmission from ``\mathbf{y^m}`` (a.k.a. current
317319
estimator, in opposition to the delayed/predictor form).
318320
@@ -449,7 +451,7 @@ MovingHorizonEstimator estimator with a sample time Ts = 10.0 s:
449451
[`JuMP` documentation](@extref JuMP Common-mistakes-when-writing-a-user-defined-operator)
450452
for common mistakes when writing these functions. Also, an [`UnscentedKalmanFilter`](@ref)
451453
estimates the arrival covariance by default.
452-
454+
453455
One exception about AD: the selected backend for the Hessian of the Lagrangian function
454456
with `hessian=true` options is sparse:
455457
```julia
@@ -469,7 +471,14 @@ MovingHorizonEstimator estimator with a sample time Ts = 10.0 s:
469471
)
470472
)
471473
```
472-
that is, it will test many coloring orders at preparation and keep the best.
474+
that is, it will test many coloring orders at preparation and keep the best. The
475+
argument `covestim` customizes the arrival covariance estimator. The supported types are
476+
[`SteadyKalmanFilter`](@ref), [`KalmanFilter`](@ref), [`UnscentedKalmanFilter`](@ref)
477+
and [`ExtendedKalmanFilter`](@ref). A constant arrival covariance is supported with
478+
[`SteadyKalmanFilter`](@ref), and by setting the `P̂` argument of [`setstate!`](@ref)
479+
method at the desired value. For [`NonLinModel`](@ref), construct a
480+
[`SteadyKalmanFilter`](@ref) with an arbitrary [`LinModel`](@ref), as long as the number
481+
of estimated states `nx̂` matches the MHE.
473482
474483
Note that if `Cwt≠Inf`, the attribute `nlp_scaling_max_gradient` of `Ipopt` is set to
475484
`10/Cwt` (if not already set), to scale the small values of ``ε``. Use the second
@@ -497,6 +506,7 @@ function MovingHorizonEstimator(
497506
gradient::AbstractADType = DEFAULT_NONLINMHE_GRADIENT,
498507
jacobian::AbstractADType = DEFAULT_NONLINMHE_JACOBIAN,
499508
hessian::Union{AbstractADType, Bool, Nothing} = false,
509+
covestim::Union{StateEstimator, Nothing} = nothing,
500510
direct = true,
501511
σP_0 = sigmaP_0,
502512
σQ = sigmaQ,
@@ -513,19 +523,10 @@ function MovingHorizonEstimator(
513523
isnothing(He) && throw(ArgumentError("Estimation horizon He must be explicitly specified"))
514524
return MovingHorizonEstimator(
515525
model, He, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂, Cwt;
516-
gc, gc!, nc, p, direct, optim, gradient, jacobian, hessian
526+
gc, gc!, nc, p, optim, gradient, jacobian, hessian, covestim, direct
517527
)
518528
end
519529

520-
"Default optimizer for MHE, depending on the model and the number of custom NL constraints."
521-
function default_optim_mhe(model::SimModel, nc)
522-
if model isa LinModel && iszero(nc)
523-
return JuMP.Model(DEFAULT_LINMHE_OPTIMIZER, add_bridges=false)
524-
else
525-
return JuMP.Model(DEFAULT_NONLINMHE_OPTIMIZER, add_bridges=false)
526-
end
527-
end
528-
529530
@doc raw"""
530531
MovingHorizonEstimator(
531532
model, He, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂, Cwt=Inf;
@@ -536,20 +537,14 @@ end
536537
gradient=AutoForwardDiff(),
537538
jacobian=AutoForwardDiff(),
538539
hessian=false,
540+
covestim=nothing,
539541
direct=true,
540-
covestim=default_covestim_mhe(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
541542
)
542543
543544
Construct the estimator from the augmented covariance matrices `P̂_0`, `Q̂` and `R̂`.
544545
545546
This syntax allows nonzero off-diagonal elements in ``\mathbf{P̂_i}, \mathbf{Q̂, R̂}``,
546-
where ``\mathbf{P̂_i}`` is the initial estimation covariance, provided by `P̂_0` argument. The
547-
keyword argument `covestim` also allows specifying a custom [`StateEstimator`](@ref) object
548-
for the estimation of covariance at the arrival ``\mathbf{P̂}_{k-N_k}(k-N_k+p)``. The
549-
supported types are [`SteadyKalmanFilter`](@ref), [`KalmanFilter`](@ref),
550-
[`UnscentedKalmanFilter`](@ref) and [`ExtendedKalmanFilter`](@ref). A constant arrival
551-
covariance is supported with [`SteadyKalmanFilter`](@ref), and by setting the `P̂` argument
552-
of [`setstate!`](@ref) at the desired value.
547+
where ``\mathbf{P̂_i}`` is the initial estimation covariance, provided by `P̂_0` argument.
553548
"""
554549
function MovingHorizonEstimator(
555550
model::SM, He, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂, Cwt=Inf;
@@ -561,13 +556,16 @@ function MovingHorizonEstimator(
561556
gradient::AbstractADType = DEFAULT_NONLINMHE_GRADIENT,
562557
jacobian::AbstractADType = DEFAULT_NONLINMHE_JACOBIAN,
563558
hessian::Union{AbstractADType, Bool, Nothing} = false,
559+
covestim::Union{StateEstimator, Nothing} = nothing,
564560
direct = true,
565-
covestim::CE = default_covestim_mhe(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
566-
) where {NT<:Real, SM<:SimModel{NT}, JM<:JuMP.GenericModel, CE<:StateEstimator{NT}}
561+
) where {NT<:Real, SM<:SimModel{NT}, JM<:JuMP.GenericModel}
567562
P̂_0, Q̂, R̂ = to_mat(P̂_0), to_mat(Q̂), to_mat(R̂)
568563
cov = KalmanCovariances(model, i_ym, nint_u, nint_ym, Q̂, R̂, P̂_0, He)
569564
gc! = get_mutating_gc_mhe(NT, gc)
570565
hessian = validate_hessian(hessian, gradient, DEFAULT_NONLINMHE_HESSIAN)
566+
if isnothing(covestim)
567+
covestim = default_covestim_mhe(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
568+
end
571569
validate_covestim(cov, covestim)
572570
return MovingHorizonEstimator{NT}(
573571
model,
@@ -578,17 +576,30 @@ function MovingHorizonEstimator(
578576
)
579577
end
580578

581-
function default_covestim_mhe(model::LinModel, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
582-
return KalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
579+
"Default optimizer for MHE, depending on the model and the number of custom NL constraints."
580+
function default_optim_mhe(model::SimModel, nc)
581+
if model isa LinModel && iszero(nc)
582+
return JuMP.Model(DEFAULT_LINMHE_OPTIMIZER, add_bridges=false)
583+
else
584+
return JuMP.Model(DEFAULT_NONLINMHE_OPTIMIZER, add_bridges=false)
585+
end
583586
end
587+
588+
"Default arrival covariance estimator for MHE, depending on the model type only."
584589
function default_covestim_mhe(model::SimModel, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
585-
return UnscentedKalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
590+
if model isa LinModel
591+
return KalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
592+
else
593+
return UnscentedKalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct)
594+
end
586595
end
587596

588597
"Validate covestim type and dimensions."
589598
function validate_covestim(cov::KalmanCovariances, covestim::KalmanEstimator)
590-
if size(cov.P̂) != size(covestim.cov.P̂)
591-
throw(ArgumentError("estimation covariance covestim.cov.P̂ size does not match the MHE"))
599+
invP̄, P̂ = cov.invP̄, covestim.cov.
600+
nx̂ = size(invP̄, 1)
601+
if size(invP̄) != size(P̂)
602+
throw(ArgumentError("P̂ covariance size $(size(P̂)) of covestim does match nx̂=$nx̂"))
592603
end
593604
return nothing
594605
end

0 commit comments

Comments
 (0)