@@ -66,7 +66,8 @@ struct MovingHorizonEstimator{
6666 JM<: JuMP.GenericModel ,
6767 GB<: AbstractADType ,
6868 JB<: AbstractADType ,
69- HB<: Union{AbstractADType, Nothing} ,
69+ HB<: Union{AbstractADType, Nothing} ,
70+ PT<: Any ,
7071 GCfunc<: Function ,
7172 CE<: KalmanEstimator ,
7273} <: StateEstimator{NT}
@@ -92,6 +93,7 @@ struct MovingHorizonEstimator{
9293 nym:: Int
9394 nyu:: Int
9495 nxs:: Int
96+ p:: PT
9597 As :: Matrix{NT}
9698 Cs_u:: Matrix{NT}
9799 Cs_y:: Matrix{NT}
@@ -133,7 +135,7 @@ struct MovingHorizonEstimator{
133135 function MovingHorizonEstimator {NT} (
134136 model:: SM ,
135137 He, i_ym, nint_u, nint_ym, cov:: KC , Cwt,
136- gc!:: GCfunc , nc, p,
138+ gc!:: GCfunc , nc, p:: PT ,
137139 optim:: JM , gradient:: GB , jacobian:: JB , hessian:: HB , covestim:: CE ;
138140 direct= true
139141 ) where {
@@ -144,6 +146,7 @@ struct MovingHorizonEstimator{
144146 GB<: AbstractADType ,
145147 JB<: AbstractADType ,
146148 HB<: Union{AbstractADType, Nothing} ,
149+ PT<: Any ,
147150 GCfunc<: Function ,
148151 CE<: KalmanEstimator{NT}
149152 }
@@ -181,7 +184,7 @@ struct MovingHorizonEstimator{
181184 Nk = [0 ]
182185 corrected = [false ]
183186 buffer = StateEstimatorBuffer {NT} (nu, nx̂, nym, ny, nd, nk, He, nε)
184- estim = new {NT, SM, KC, JM, GB, JB, HB, GCfunc, CE} (
187+ estim = new {NT, SM, KC, JM, GB, JB, HB, PT, GCfunc, CE} (
185188 model,
186189 cov,
187190 optim, con,
@@ -190,6 +193,7 @@ struct MovingHorizonEstimator{
190193 Z̃, lastu0, x̂op, f̂op, x̂0,
191194 He, nε,
192195 i_ym, nx̂, nym, nyu, nxs,
196+ p,
193197 As, Cs_u, Cs_y, nint_u, nint_ym,
194198 Â, B̂u, Ĉ, B̂d, D̂d, Ĉm, D̂dm,
195199 Ẽ, F, G, J, B, ẽx̄, fx̄,
@@ -386,26 +390,26 @@ MovingHorizonEstimator estimator with a sample time Ts = 10.0 s:
386390 unavailable e.g.: ``\m athbf{u}(k)`` with ``p=0``. They are filled with `NaN` values.
387391 The exact time steps of the `NaN`s are detailed in the last column below.
388392
389- | ARGUMENT | SIZE | FIRST SAMPLE | LAST SAMPLE | MISSING SAMPLES (NAN) |
390- | :--------------- | :-------------- | :-------------- | :-------------- | :------------------------- |
391- | ``\m athbf{X̂_e}`` | `((Nk+1)*nx̂,)` | ``k - N_k + p`` | ``k + p`` | — |
392- | ``\m athbf{V̂_e}`` | `((Nk+1)*nym,)` | ``k - N_k + p`` | ``k + p`` | ``k - N_k, k + 1`` |
393- | ``\m athbf{Ŵ_e}`` | `((Nk+1)*nx̂,)` | ``k - N_k + p`` | ``k + p`` | ``k - N_k + p - 1, k + p`` |
394- | ``\m athbf{U_e}`` | `((Nk+1)*nu,)` | ``k - N_k + p`` | ``k + p`` | ``k + p`` |
395- | ``\m athbf{Y_e^m}`` | `((Nk+1)*nym,)` | ``k - N_k + p`` | ``k + p`` | ``k + 1`` |
396- | ``\m athbf{D_e}`` | `((Nk+1)*nd,)` | ``k - N_k + p`` | ``k + p`` | ``k + 1`` |
397- | ``\m athbf{P̄}`` | `(nx̂, nx̂)` | ``k - N_k + p`` | ``k - N_k + p`` | — |
398- | ``\m athbf{x̄}`` | `(nx̂,)` | ``k - N_k + p`` | ``k - N_k + p`` | — |
399- | ``\m athbf{p}`` | var. | — | — | — |
400- | ``ε`` | `()` | — | — | — |
393+ | ARGUMENT | SIZE | FIRST SAMPLE | LAST SAMPLE | MISSING SAMPLES |
394+ | :--------------- | :-------------- | :-------------- | :-------------- | :----------------- |
395+ | ``\m athbf{X̂_e}`` | `((Nk+1)*nx̂,)` | ``k - N_k + p`` | ``k + p`` | — |
396+ | ``\m athbf{V̂_e}`` | `((Nk+1)*nym,)` | ``k - N_k + p`` | ``k + p`` | ``k - N_k, k + 1`` |
397+ | ``\m athbf{Ŵ_e}`` | `((Nk+1)*nx̂,)` | ``k - N_k + p`` | ``k + p`` | ``k + p`` |
398+ | ``\m athbf{U_e}`` | `((Nk+1)*nu,)` | ``k - N_k + p`` | ``k + p`` | ``k + p`` |
399+ | ``\m athbf{Y_e^m}`` | `((Nk+1)*nym,)` | ``k - N_k + p`` | ``k + p`` | ``k + 1`` |
400+ | ``\m athbf{D_e}`` | `((Nk+1)*nd,)` | ``k - N_k + p`` | ``k + p`` | ``k + 1`` |
401+ | ``\m athbf{P̄}`` | `(nx̂, nx̂)` | ``k - N_k + p`` | ``k - N_k + p`` | — |
402+ | ``\m athbf{x̄}`` | `(nx̂,)` | ``k - N_k + p`` | ``k - N_k + p`` | — |
403+ | ``\m athbf{p}`` | var. | — | — | — |
404+ | ``ε`` | `()` | — | — | — |
401405
402406 If `LHS` represents the result of the left-hand side in the inequality
403407 ``\m athbf{g_c}(\m athbf{X̂_e, V̂_e, Ŵ_e, U_e, Y_e^m, D_e, P̄, x̄, p}, ε) ≤ \m athbf{0}``,
404408 the function `gc` can be implemented in two possible ways:
405409
406410 1. **Non-mutating function** (out-of-place): define it as `gc(X̂e, V̂e, Ŵe, Ue, Yem, De,
407411 P̄, x̄, p, ε) -> LHS`. This syntax is simple and intuitive but it allocates more memory.
408- 2. **Mutating function** (in-place): define it as `gc!(LHS, X̂e, V̂e, Ŵe, Ue, Yme , De, P̄,
412+ 2. **Mutating function** (in-place): define it as `gc!(LHS, X̂e, V̂e, Ŵe, Ue, Yem , De, P̄,
409413 x̄, p, ε) -> nothing`. This syntax reduces the allocations and potentially the
410414 computational burden as well.
411415
@@ -921,7 +925,7 @@ function setconstraint!(
921925 return estim
922926end
923927
924- " By default, no nonlinear constraints, return nothing."
928+ " By default, no nonlinear constraints or only custom ones, do and return nothing."
925929reset_nonlincon! (:: MovingHorizonEstimator , :: SimModel ) = nothing
926930
927931"""
@@ -931,7 +935,7 @@ Re-construct nonlinear constraints and add them to `estim.optim`.
931935"""
932936function reset_nonlincon! (estim:: MovingHorizonEstimator , model:: NonLinModel )
933937 g_oracle = get_nonlincon_oracle (estim, estim. optim)
934- set_nonlincon! (estim, model, estim. optim, g_oracle)
938+ set_nonlincon! (estim, estim. optim, g_oracle)
935939end
936940
937941@doc raw """
@@ -1462,15 +1466,21 @@ Init the quadratic optimization of [`MovingHorizonEstimator`](@ref).
14621466function init_optimization! (
14631467 estim:: MovingHorizonEstimator , model:: LinModel , optim:: JuMP.GenericModel ,
14641468)
1469+ C, con = estim. C, estim. con
14651470 nZ̃ = length (estim. Z̃)
14661471 JuMP. num_variables (optim) == 0 || JuMP. empty! (optim)
14671472 JuMP. set_silent (optim)
14681473 limit_solve_time (optim, model. Ts)
14691474 @variable (optim, Z̃var[1 : nZ̃])
1470- A = estim . con. A[estim . con. i_b, :]
1471- b = estim . con. b[estim . con. i_b]
1475+ A = con. A[con. i_b, :]
1476+ b = con. b[con. i_b]
14721477 @constraint (optim, linconstraint, A* Z̃var .≤ b)
14731478 @objective (optim, Min, obj_quadprog (Z̃var, estim. H̃, estim. q̃))
1479+ if con. nc > 0
1480+ set_scaling_gradient! (optim, C)
1481+ g_oracle = get_nonlincon_oracle (estim, optim)
1482+ set_nonlincon! (estim, optim, g_oracle)
1483+ end
14741484 return nothing
14751485end
14761486
@@ -1491,23 +1501,16 @@ function init_optimization!(
14911501 JuMP. set_silent (optim)
14921502 limit_solve_time (optim, model. Ts)
14931503 @variable (optim, Z̃var[1 : nZ̃])
1494- A = estim . con. A[con. i_b, :]
1495- b = estim . con. b[con. i_b]
1504+ A = con. A[con. i_b, :]
1505+ b = con. b[con. i_b]
14961506 @constraint (optim, linconstraint, A* Z̃var .≤ b)
14971507 # --- nonlinear optimization init ---
1498- if ! isinf (C) && JuMP. solver_name (optim) == " Ipopt"
1499- try
1500- JuMP. get_attribute (optim, " nlp_scaling_max_gradient" )
1501- catch
1502- # default "nlp_scaling_max_gradient" to `10.0/C` if not already set:
1503- JuMP. set_attribute (optim, " nlp_scaling_max_gradient" , 10.0 / C)
1504- end
1505- end
1508+ set_scaling_gradient! (optim, C)
15061509 # constraints with vector nonlinear oracle, objective function with splatting:
15071510 J_op = get_nonlinobj_op (estim, optim)
15081511 g_oracle = get_nonlincon_oracle (estim, optim)
15091512 @objective (optim, Min, J_op (Z̃var... ))
1510- set_nonlincon! (estim, model, optim, g_oracle)
1513+ set_nonlincon! (estim, optim, g_oracle)
15111514 return nothing
15121515end
15131516
@@ -1534,23 +1537,28 @@ function get_nonlinobj_op(
15341537 He = estim. He
15351538 ng = length (con. i_g)
15361539 nŴ, nV̂, nX̂, ng, nZ̃ = He* nx̂, He* nym, He* nx̂, length (con. i_g), length (estim. Z̃)
1540+ nŴe, nX̂e, nV̂e = (He+ 1 )* nx̂, (He+ 1 )* nx̂, (He+ 1 )* nym
15371541 strict = Val (true )
15381542 myNaN = convert (JNT, NaN )
15391543 J:: Vector{JNT} = zeros (JNT, 1 )
1540- x̂0arr:: Vector{JNT} , x̄:: Vector{JNT} = zeros (JNT, nx̂), zeros (JNT, nx̂)
1544+ x̂0arr:: Vector{JNT} , x̄:: Vector{JNT} = zeros (JNT, nx̂), zeros (JNT, nx̂)
15411545 Ŵ:: Vector{JNT} = zeros (JNT, nŴ)
1542- V̂:: Vector{JNT} , X̂0:: Vector{JNT} = zeros (JNT, nV̂), zeros (JNT, nX̂)
1546+ V̂:: Vector{JNT} , X̂0:: Vector{JNT} = zeros (JNT, nV̂), zeros (JNT, nX̂)
1547+ Ŵe:: Vector{JNT} = zeros (JNT, nŴe)
1548+ V̂e:: Vector{JNT} , X̂e:: Vector{JNT} = zeros (JNT, nV̂e), zeros (JNT, nX̂e)
15431549 k:: Vector{JNT} = zeros (JNT, nk)
1544- û0:: Vector{JNT} , ŷ0:: Vector{JNT} = zeros (JNT, nu), zeros (JNT, nŷ)
1545- gc:: Vector{JNT} , g:: Vector{JNT} = zeros (JNT, nc), zeros (JNT, ng)
1546- function J! (Z̃, x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g)
1547- update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g, estim, Z̃)
1550+ û0:: Vector{JNT} , ŷ0:: Vector{JNT} = zeros (JNT, nu), zeros (JNT, nŷ)
1551+ gc:: Vector{JNT} , g:: Vector{JNT} = zeros (JNT, nc), zeros (JNT, ng)
1552+ function J! (Z̃, x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g)
1553+ update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g, estim, Z̃)
15481554 return obj_nonlinprog (estim, model, x̄, V̂, Ŵ, Z̃)
15491555 end
15501556 Z̃_J = fill (myNaN, nZ̃) # NaN to force update_predictions! at first call
15511557 J_cache = (
1552- Cache (x̂0arr), Cache (x̄), Cache (Ŵ), Cache (V̂), Cache (X̂0),
1553- Cache (û0), Cache (k), Cache (ŷ0), Cache (gc), Cache (g),
1558+ Cache (x̂0arr), Cache (x̄),
1559+ Cache (Ŵ), Cache (V̂), Cache (X̂0),
1560+ Cache (Ŵe), Cache (V̂e), Cache (X̂e),
1561+ Cache (û0), Cache (k), Cache (ŷ0), Cache (gc), Cache (g),
15541562 )
15551563 # temporarily "fill" the estimation window for the preparation of the gradient:
15561564 estim. Nk[] = He
@@ -1641,30 +1649,35 @@ function get_nonlincon_oracle(
16411649 ng, ngi = length (con. i_g), sum (con. i_g)
16421650 nc = con. nc
16431651 nŴ, nV̂, nX̂, nZ̃ = He* nx̂, He* nym, He* nx̂, length (estim. Z̃)
1652+ nŴe, nX̂e, nV̂e = (He+ 1 )* nx̂, (He+ 1 )* nx̂, (He+ 1 )* nym
16441653 strict = Val (true )
16451654 myNaN, myInf = convert (JNT, NaN ), convert (JNT, Inf )
16461655 x̂0arr:: Vector{JNT} , x̄:: Vector{JNT} = zeros (JNT, nx̂), zeros (JNT, nx̂)
16471656 Ŵ:: Vector{JNT} = zeros (JNT, nŴ)
1648- V̂:: Vector{JNT} , X̂0:: Vector{JNT} = zeros (JNT, nV̂), zeros (JNT, nX̂)
1657+ V̂:: Vector{JNT} , X̂0:: Vector{JNT} = zeros (JNT, nV̂), zeros (JNT, nX̂)
1658+ Ŵe:: Vector{JNT} = zeros (JNT, nŴe)
1659+ V̂e:: Vector{JNT} , X̂e:: Vector{JNT} = zeros (JNT, nV̂e), zeros (JNT, nX̂e)
16491660 k:: Vector{JNT} = zeros (JNT, nk)
16501661 û0:: Vector{JNT} , ŷ0:: Vector{JNT} = zeros (JNT, nu), zeros (JNT, nŷ)
16511662 gc:: Vector{JNT} , g:: Vector{JNT} = zeros (JNT, nc), zeros (JNT, ng)
16521663 gi:: Vector{JNT} = zeros (JNT, ngi)
16531664 λi:: Vector{JNT} = rand (JNT, ngi)
16541665 # -------------- inequality constraint: nonlinear oracle -------------------------
1655- function gi! (gi, Z̃, x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g)
1656- update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g, estim, Z̃)
1666+ function gi! (gi, Z̃, x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g)
1667+ update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g, estim, Z̃)
16571668 gi .= @views g[i_g]
16581669 return nothing
16591670 end
1660- function ℓ_gi (Z̃, λi, x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g, gi)
1661- update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, û0, k, ŷ0, gc, g, estim, Z̃)
1671+ function ℓ_gi (Z̃, λi, x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g, gi)
1672+ update_prediction! (x̂0arr, x̄, Ŵ, V̂, X̂0, Ŵe, V̂e, X̂e, û0, k, ŷ0, gc, g, estim, Z̃)
16621673 gi .= @views g[i_g]
16631674 return dot (λi, gi)
16641675 end
16651676 Z̃_∇gi = fill (myNaN, nZ̃) # NaN to force update_predictions! at first call
16661677 ∇gi_cache = (
1667- Cache (x̂0arr), Cache (x̄), Cache (Ŵ), Cache (V̂), Cache (X̂0),
1678+ Cache (x̂0arr), Cache (x̄),
1679+ Cache (Ŵ), Cache (V̂), Cache (X̂0),
1680+ Cache (Ŵe), Cache (V̂e), Cache (X̂e),
16681681 Cache (û0), Cache (k), Cache (ŷ0), Cache (gc), Cache (g),
16691682 )
16701683 # temporarily "fill" the estimation window for the preparation of the gradient:
@@ -1675,7 +1688,9 @@ function get_nonlincon_oracle(
16751688 ∇gi_structure = init_diffstructure (∇gi)
16761689 if ! isnothing (hess)
16771690 ∇²gi_cache = (
1678- Cache (x̂0arr), Cache (x̄), Cache (Ŵ), Cache (V̂), Cache (X̂0),
1691+ Cache (x̂0arr), Cache (x̄),
1692+ Cache (Ŵ), Cache (V̂), Cache (X̂0),
1693+ Cache (Ŵe), Cache (V̂e), Cache (X̂e),
16791694 Cache (û0), Cache (k), Cache (ŷ0), Cache (gc), Cache (g), Cache (gi)
16801695 )
16811696 estim. Nk[] = He # see comment above
@@ -1722,16 +1737,13 @@ function get_nonlincon_oracle(
17221737 return g_oracle
17231738end
17241739
1725- " By default, there is no nonlinear constraint, thus do nothing."
1726- set_nonlincon! (:: MovingHorizonEstimator , :: SimModel , _ , _ ) = nothing
1727-
17281740"""
1729- set_nonlincon!(estim::MovingHorizonEstimator, ::NonLinModel, optim, g_oracle)
1741+ set_nonlincon!(estim::MovingHorizonEstimator, optim, g_oracle)
17301742
1731- Set the nonlinear inequality constraints for `NonLinModel `, if any.
1743+ Set the nonlinear inequality constraints of `estim `, if any.
17321744"""
17331745function set_nonlincon! (
1734- estim:: MovingHorizonEstimator , :: NonLinModel , optim:: JuMP.GenericModel{JNT} , g_oracle
1746+ estim:: MovingHorizonEstimator , optim:: JuMP.GenericModel{JNT} , g_oracle
17351747) where JNT<: Real
17361748 Z̃var = optim[:Z̃var ]
17371749 nonlin_constraints = JuMP. all_constraints (
0 commit comments