Skip to content

Commit e8be023

Browse files
committed
debug: setting softness parameters for MHE now works
1 parent abf1af0 commit e8be023

File tree

2 files changed

+70
-19
lines changed

2 files changed

+70
-19
lines changed

src/estimator/mhe/construct.jl

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ the augmented process model and ``\mathbf{R̂}, \mathbf{Q̂}`` covariances. The
197197
# Arguments
198198
- `model::SimModel` : (deterministic) model for the estimations.
199199
- `He=nothing` : estimation horizon ``H_e``, must be specified.
200-
- `Cwt=Inf` : slack variable weight ``C`` (scalar), use `Cwt=Inf` for hard constraints only.
200+
- `Cwt=Inf` : slack variable weight ``C``, default to `Inf` meaning hard constraints only.
201201
- `optim=default_optim_mhe(model)` : a [`JuMP.Model`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.Model)
202202
with a quadratic/nonlinear optimizer for solving (default to [`Ipopt`](https://github.com/jump-dev/Ipopt.jl),
203203
or [`OSQP`](https://osqp.org/docs/parsers/jump.html) if `model` is a [`LinModel`](@ref)).
@@ -337,10 +337,10 @@ Extended Help for details on model augmentation and time-varying constraints.
337337
- `v̂max = fill(+Inf,nym)` : sensor noise upper bounds ``\mathbf{v̂_{max}}``.
338338
- `c_x̂min = fill(0.0,nx̂)` : `x̂min` softness weights ``\mathbf{c_{x̂_{min}}}``.
339339
- `c_x̂max = fill(0.0,nx̂)` : `x̂max` softness weights ``\mathbf{c_{x̂_{max}}}``.
340-
- `c_ŵmin = fill(1.0,nx̂)` : `ŵmin` softness weights ``\mathbf{c_{ŵ_{min}}}``.
341-
- `c_ŵmax = fill(1.0,nx̂)` : `ŵmax` softness weights ``\mathbf{c_{ŵ_{max}}}``.
342-
- `c_v̂min = fill(1.0,nym)` : `v̂min` softness weights ``\mathbf{c_{v̂_{min}}}``.
343-
- `c_v̂max = fill(1.0,nym)` : `v̂max` softness weights ``\mathbf{c_{v̂_{max}}}``.
340+
- `c_ŵmin = fill(0.0,nx̂)` : `ŵmin` softness weights ``\mathbf{c_{ŵ_{min}}}``.
341+
- `c_ŵmax = fill(0.0,nx̂)` : `ŵmax` softness weights ``\mathbf{c_{ŵ_{max}}}``.
342+
- `c_v̂min = fill(0.0,nym)` : `v̂min` softness weights ``\mathbf{c_{v̂_{min}}}``.
343+
- `c_v̂max = fill(0.0,nym)` : `v̂max` softness weights ``\mathbf{c_{v̂_{max}}}``.
344344
- all the keyword arguments above but with a first capital letter, e.g. `X̂max` or `C_ŵmax`:
345345
for time-varying constraints (see Extended Help).
346346
@@ -412,7 +412,7 @@ function setconstraint!(
412412
isnothing(C_v̂max) && !isnothing(c_v̂max) && (C_v̂max = repeat(c_v̂max, He))
413413
if !all(isnothing.((C_x̂min, C_x̂max, C_ŵmin, C_ŵmax, C_v̂min, C_v̂max)))
414414
!isinf(C) || throw(ArgumentError("Slack variable weight Cwt must be finite to set softness parameters"))
415-
notSolvedYet || throw(ArgumentError("Cannot set softness parameters after calling updatestate!"))
415+
notSolvedYet || error("Cannot set softness parameters after calling updatestate!")
416416
end
417417
if !isnothing(X̂min)
418418
size(X̂min) == (nX̂con,) || throw(ArgumentError("X̂min size must be $((nX̂con,))"))
@@ -445,14 +445,14 @@ function setconstraint!(
445445
any(C_x̂min .< 0) && error("C_x̂min weights should be non-negative")
446446
con.A_x̃min[end-nx̂+1:end, end] = -C_x̂min[1:nx̂] # if C is finite : x̃ = [ϵ; x̂]
447447
con.C_x̂min[:] = C_x̂min[nx̂+1:end]
448-
size(con.A_X̂min) 0 && (con.A_X̂min[:, end] = -con.C_x̂min) # for LinModel
448+
size(con.A_X̂min, 1) 0 && (con.A_X̂min[:, end] = -con.C_x̂min) # for LinModel
449449
end
450450
if !isnothing(C_x̂max)
451451
size(C_x̂max) == (nX̂con,) || throw(ArgumentError("C_x̂max size must be $((nX̂con,))"))
452452
any(C_x̂max .< 0) && error("C_x̂max weights should be non-negative")
453453
con.A_x̃max[end-nx̂+1:end, end] = -C_x̂max[1:nx̂] # if C is finite : x̃ = [ϵ; x̂]
454454
con.C_x̂max[:] = C_x̂max[nx̂+1:end]
455-
size(con.A_X̂max) 0 && (con.A_X̂max[:, end] = -con.C_x̂max) # for LinModel
455+
size(con.A_X̂max, 1) 0 && (con.A_X̂max[:, end] = -con.C_x̂max) # for LinModel
456456
end
457457
if !isnothing(C_ŵmin)
458458
size(C_ŵmin) == (nŵ*He,) || throw(ArgumentError("C_ŵmin size must be $((nŵ*He,))"))
@@ -467,14 +467,14 @@ function setconstraint!(
467467
if !isnothing(C_v̂min)
468468
size(C_v̂min) == (nym*He,) || throw(ArgumentError("C_v̂min size must be $((nym*He,))"))
469469
any(C_v̂min .< 0) && error("C_v̂min weights should be non-negative")
470-
con.C_V̂min[:] = C_v̂min
471-
size(con.A_V̂min) 0 && (con.A_V̂min[:, end] = -con.C_V̂min) # for LinModel
470+
con.C_v̂min[:] = C_v̂min
471+
size(con.A_V̂min, 1) 0 && (con.A_V̂min[:, end] = -con.C_v̂min) # for LinModel
472472
end
473473
if !isnothing(C_v̂max)
474474
size(C_v̂max) == (nym*He,) || throw(ArgumentError("C_v̂max size must be $((nym*He,))"))
475475
any(C_v̂max .< 0) && error("C_v̂max weights should be non-negative")
476-
con.C_V̂max[:] = C_v̂max
477-
size(con.A_V̂max) 0 && (con.A_V̂max[:, end] = -con.C_V̂max) # for LinModel
476+
con.C_v̂max[:] = C_v̂max
477+
size(con.A_V̂max, 1) 0 && (con.A_V̂max[:, end] = -con.C_v̂max) # for LinModel
478478
end
479479
i_x̃min, i_x̃max = .!isinf.(con.x̃min) , .!isinf.(con.x̃max)
480480
i_X̂min, i_X̂max = .!isinf.(con.X̂min) , .!isinf.(con.X̂max)
@@ -598,8 +598,8 @@ function init_defaultcon_mhe(
598598
V̂min, V̂max = fill(convert(NT,-Inf), nYm), fill(convert(NT,+Inf), nYm)
599599
c_x̂min, c_x̂max = fill(0.0, nx̂), fill(0.0, nx̂)
600600
C_x̂min, C_x̂max = fill(0.0, nX̂), fill(0.0, nX̂)
601-
C_ŵmin, C_ŵmax = fill(1.0, nŴ), fill(1.0, nŴ)
602-
C_v̂min, C_v̂max = fill(1.0, nYm), fill(1.0, nYm)
601+
C_ŵmin, C_ŵmax = fill(0.0, nŴ), fill(0.0, nŴ)
602+
C_v̂min, C_v̂max = fill(0.0, nYm), fill(0.0, nYm)
603603
A_x̃min, A_x̃max, x̃min, x̃max, ẽx̄ = relaxarrival(model, C, c_x̂min, c_x̂max, x̂min, x̂max, ex̄)
604604
A_X̂min, A_X̂max, Ẽx̂ = relaxX̂(model, C, C_x̂min, C_x̂max, Ex̂)
605605
A_Ŵmin, A_Ŵmax = relaxŴ(model, C, C_ŵmin, C_ŵmax, nx̂)

test/test_state_estim.jl

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,12 +561,14 @@ end
561561
@test mhe1.nyu == 0
562562
@test mhe1.nxs == 2
563563
@test mhe1.nx̂ == 6
564+
@test size(mhe1.Ẽ, 2) == 6*mhe1.nx̂
564565

565566
mhe2 = MovingHorizonEstimator(nonlinmodel, He=5)
566567
@test mhe2.nym == 2
567568
@test mhe2.nyu == 0
568569
@test mhe2.nxs == 2
569570
@test mhe2.nx̂ == 6
571+
@test size(mhe1.Ẽ, 2) == 6*mhe1.nx̂
570572

571573
mhe3 = MovingHorizonEstimator(nonlinmodel, He=5, i_ym=[2])
572574
@test mhe3.nym == 1
@@ -612,12 +614,17 @@ end
612614
mhe10 = MovingHorizonEstimator(nonlinmodel, He=5, optim=Model(OSQP.Optimizer))
613615
@test solver_name(mhe10.optim) == "OSQP"
614616

617+
mhe11 = MovingHorizonEstimator(nonlinmodel, He=5, Cwt=1e3)
618+
@test size(mhe11.Ẽ, 2) == 6*mhe11.nx̂ + 1
619+
@test mhe11.C == 1e3
620+
615621
linmodel2 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0)
616622
mhe11 = MovingHorizonEstimator(linmodel2, He=5)
617623
@test isa(mhe11, MovingHorizonEstimator{Float32})
618624

619625
@test_throws ArgumentError MovingHorizonEstimator(linmodel1)
620626
@test_throws ArgumentError MovingHorizonEstimator(linmodel1, He=0)
627+
@test_throws ArgumentError MovingHorizonEstimator(linmodel1, Cwt=-1)
621628
end
622629

623630
@testset "MovingHorizonEstimator estimation and getinfo" begin
@@ -677,30 +684,60 @@ end
677684

678685
@testset "MovingHorizonEstimator set constraints" begin
679686
linmodel1 = setop!(LinModel(sys,Ts,i_u=[1,2]), uop=[10,50], yop=[50,30])
680-
mhe1 = MovingHorizonEstimator(linmodel1, He=1, nint_ym=0)
687+
mhe1 = MovingHorizonEstimator(linmodel1, He=1, nint_ym=0, Cwt=1e3)
681688
setconstraint!(mhe1, x̂min=[-51,-52], x̂max=[53,54])
682689
@test all((mhe1.con.X̂min, mhe1.con.X̂max) .≈ ([-51,-52], [53,54]))
683-
@test all((mhe1.con.x̃min, mhe1.con.x̃max) .≈ ([-51,-52], [53,54]))
690+
@test all((mhe1.con.x̃min[2:end], mhe1.con.x̃max[2:end]) .≈ ([-51,-52], [53,54]))
684691
setconstraint!(mhe1, ŵmin=[-55,-56], ŵmax=[57,58])
685692
@test all((mhe1.con.Ŵmin, mhe1.con.Ŵmax) .≈ ([-55,-56], [57,58]))
686693
setconstraint!(mhe1, v̂min=[-59,-60], v̂max=[61,62])
687694
@test all((mhe1.con.V̂min, mhe1.con.V̂max) .≈ ([-59,-60], [61,62]))
688-
689-
mhe2 = MovingHorizonEstimator(linmodel1, He=4, nint_ym=0)
695+
setconstraint!(mhe1, c_x̂min=[0.01,0.02], c_x̂max=[0.03,0.04])
696+
@test all((-mhe1.con.A_X̂min[:, end], -mhe1.con.A_X̂max[:, end]) .≈ ([0.01, 0.02], [0.03,0.04]))
697+
@test all((-mhe1.con.A_x̃min[2:end, end], -mhe1.con.A_x̃max[2:end, end]) .≈ ([0.01,0.02], [0.03,0.04]))
698+
setconstraint!(mhe1, c_ŵmin=[0.05,0.06], c_ŵmax=[0.07,0.08])
699+
@test all((-mhe1.con.A_Ŵmin[:, end], -mhe1.con.A_Ŵmax[:, end]) .≈ ([0.05, 0.06], [0.07,0.08]))
700+
setconstraint!(mhe1, c_v̂min=[0.09,0.10], c_v̂max=[0.11,0.12])
701+
@test all((-mhe1.con.A_V̂min[:, end], -mhe1.con.A_V̂max[:, end]) .≈ ([0.09, 0.10], [0.11,0.12]))
702+
703+
mhe2 = MovingHorizonEstimator(linmodel1, He=4, nint_ym=0, Cwt=1e3)
690704
setconstraint!(mhe2, X̂min=-1(1:10), X̂max=1(1:10))
691705
@test all((mhe2.con.X̂min, mhe2.con.X̂max) .≈ (-1(3:10), 1(3:10)))
692-
@test all((mhe2.con.x̃min, mhe2.con.x̃max) .≈ (-1(1:2), 1(1:2)))
706+
@test all((mhe2.con.x̃min[2:end], mhe2.con.x̃max[2:end]) .≈ (-1(1:2), 1(1:2)))
693707
setconstraint!(mhe2, Ŵmin=-1(11:18), Ŵmax=1(11:18))
694708
@test all((mhe2.con.Ŵmin, mhe2.con.Ŵmax) .≈ (-1(11:18), 1(11:18)))
695709
setconstraint!(mhe2, V̂min=-1(31:38), V̂max=1(31:38))
696710
@test all((mhe2.con.V̂min, mhe2.con.V̂max) .≈ (-1(31:38), 1(31:38)))
711+
setconstraint!(mhe2, C_x̂min=0.01(1:10), C_x̂max=0.02(1:10))
712+
@test all((-mhe2.con.A_X̂min[:, end], -mhe2.con.A_X̂max[:, end]) .≈ (0.01(3:10), 0.02(3:10)))
713+
@test all((-mhe2.con.A_x̃min[2:end, end], -mhe2.con.A_x̃max[2:end, end]) .≈ (0.01(1:2), 0.02(1:2)))
714+
setconstraint!(mhe2, C_ŵmin=0.03(11:18), C_ŵmax=0.04(11:18))
715+
@test all((-mhe2.con.A_Ŵmin[:, end], -mhe2.con.A_Ŵmax[:, end]) .≈ (0.03(11:18), 0.04(11:18)))
716+
setconstraint!(mhe2, C_v̂min=0.05(31:38), C_v̂max=0.06(31:38))
717+
@test all((-mhe2.con.A_V̂min[:, end], -mhe2.con.A_V̂max[:, end]) .≈ (0.05(31:38), 0.06(31:38)))
718+
719+
f(x,u,d) = linmodel1.A*x + linmodel1.Bu*u
720+
h(x,d) = linmodel1.C*x
721+
nonlinmodel = setop!(NonLinModel(f, h, Ts, 2, 2, 2), uop=[10,50], yop=[50,30])
722+
723+
mhe3 = MovingHorizonEstimator(nonlinmodel, He=4, nint_ym=0, Cwt=1e3)
724+
setconstraint!(mhe3, C_x̂min=0.01(1:10), C_x̂max=0.02(1:10))
725+
@test all((mhe3.con.C_x̂min, mhe3.con.C_x̂max) .≈ (0.01(3:10), 0.02(3:10)))
726+
setconstraint!(mhe3, C_v̂min=0.03(11:18), C_v̂max=0.04(11:18))
727+
@test all((mhe3.con.C_v̂min, mhe3.con.C_v̂max) .≈ (0.03(11:18), 0.04(11:18)))
697728

698729
@test_throws ArgumentError setconstraint!(mhe2, x̂min=[-1])
699730
@test_throws ArgumentError setconstraint!(mhe2, x̂max=[+1])
700731
@test_throws ArgumentError setconstraint!(mhe2, ŵmin=[-1])
701732
@test_throws ArgumentError setconstraint!(mhe2, ŵmax=[+1])
702733
@test_throws ArgumentError setconstraint!(mhe2, v̂min=[-1])
703734
@test_throws ArgumentError setconstraint!(mhe2, v̂max=[+1])
735+
@test_throws ArgumentError setconstraint!(mhe2, c_x̂min=[-1])
736+
@test_throws ArgumentError setconstraint!(mhe2, c_x̂max=[+1])
737+
@test_throws ArgumentError setconstraint!(mhe2, c_ŵmin=[-1])
738+
@test_throws ArgumentError setconstraint!(mhe2, c_ŵmax=[+1])
739+
@test_throws ArgumentError setconstraint!(mhe2, c_v̂min=[-1])
740+
@test_throws ArgumentError setconstraint!(mhe2, c_v̂max=[+1])
704741

705742
updatestate!(mhe1, [10, 50], [50, 30])
706743
@test_throws ErrorException setconstraint!(mhe1, x̂min=[-Inf,-Inf])
@@ -709,6 +746,20 @@ end
709746
@test_throws ErrorException setconstraint!(mhe1, ŵmax=[+Inf,+Inf])
710747
@test_throws ErrorException setconstraint!(mhe1, v̂min=[-Inf,-Inf])
711748
@test_throws ErrorException setconstraint!(mhe1, v̂max=[+Inf,+Inf])
749+
@test_throws ErrorException setconstraint!(mhe1, c_x̂min=[100,100])
750+
@test_throws ErrorException setconstraint!(mhe1, c_x̂max=[200,200])
751+
@test_throws ErrorException setconstraint!(mhe1, c_ŵmin=[300,300])
752+
@test_throws ErrorException setconstraint!(mhe1, c_ŵmax=[400,400])
753+
@test_throws ErrorException setconstraint!(mhe1, c_v̂min=[500,500])
754+
@test_throws ErrorException setconstraint!(mhe1, c_v̂max=[600,600])
755+
756+
mhe4 = MovingHorizonEstimator(nonlinmodel, He=1, nint_ym=0, Cwt=Inf)
757+
@test_throws ArgumentError setconstraint!(mhe4, c_x̂min=[1,1])
758+
@test_throws ArgumentError setconstraint!(mhe4, c_x̂max=[1,1])
759+
@test_throws ArgumentError setconstraint!(mhe4, c_ŵmin=[1,1])
760+
@test_throws ArgumentError setconstraint!(mhe4, c_ŵmax=[1,1])
761+
@test_throws ArgumentError setconstraint!(mhe4, c_v̂min=[1,1])
762+
@test_throws ArgumentError setconstraint!(mhe4, c_v̂max=[1,1])
712763
end
713764

714765
@testset "MovingHorizonEstimator constraint violation" begin

0 commit comments

Comments
 (0)