Skip to content

Commit 9edf6aa

Browse files
committed
added: tests for MovingHorizonEstimator
1 parent 3719df7 commit 9edf6aa

File tree

4 files changed

+153
-8
lines changed

4 files changed

+153
-8
lines changed

Project.toml

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

66
[deps]
77
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"

src/estimator/mhe.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ Construct the estimator from the augmented covariance matrices `P̂0`, `Q̂` and
190190
This syntax allows nonzero off-diagonal elements in ``\mathbf{P̂}_{-1}(0), \mathbf{Q̂, R̂}``.
191191
"""
192192
function MovingHorizonEstimator(
193-
model::SM, He, i_ym, nint_u, nint_ym, P̂0, Q̂, R̂, optim
194-
) where {NT<:Real, SM<:SimModel{NT}}
195-
return MovingHorizonEstimator{NT, SM}(
193+
model::SM, He, i_ym, nint_u, nint_ym, P̂0, Q̂, R̂, optim::JM
194+
) where {NT<:Real, SM<:SimModel{NT}, JM<:JuMP.GenericModel}
195+
return MovingHorizonEstimator{NT, SM, JM}(
196196
model, He, i_ym, nint_u, nint_ym, P̂0, Q̂ , R̂, optim
197197
)
198198
end
@@ -531,7 +531,7 @@ end
531531
Nonlinear constrains for [`MovingHorizonEstimator`](@ref).
532532
"""
533533
function con_nonlinprog!(g, estim::MovingHorizonEstimator, ::SimModel, X̂)
534-
nX̂con, nX̂ = length(estim.X̂min), estim.Nk[]*estim.nx̂
534+
nX̂con, nX̂ = length(estim.X̂min), estim.nx̂*(estim.Nk[]+1)
535535
for i in eachindex(g)
536536
estim.i_g[i] || continue
537537
if i nX̂con

test/test_predictive_control.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ end
429429
nmpc4 = NonLinMPC(nonlinmodel, Hp=15, Mwt=[0], Nwt=[0], Lwt=[1])
430430
u = moveinput!(nmpc4, [0], d, R̂u=fill(12, nmpc4.Hp))
431431
@test u [12] atol=5e-2
432-
nmpc5 = setconstraint!(NonLinMPC(nonlinmodel, Hp=15, Cwt=Inf), ymin=[-1])
432+
nmpc5 = setconstraint!(NonLinMPC(nonlinmodel, Hp=15, Cwt=Inf), ymax=[1])
433433
g_Ymax_end = nmpc5.optim.nlp_model.operators.registered_multivariate_operators[end].f
434434
@test g_Ymax_end((1.0, 1.0)) 0.0 # test gfunc_i(i,::NTuple{N, Float64})
435435
# test gfunc_i(i,::NTuple{N, ForwardDiff.Dual}) :

test/test_state_estim.jl

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ end
415415
@test ukf9. I(2)
416416

417417
linmodel2 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0)
418-
ukf9 = UnscentedKalmanFilter(linmodel2)
419-
@test isa(ukf9, UnscentedKalmanFilter{Float32})
418+
ukf10 = UnscentedKalmanFilter(linmodel2)
419+
@test isa(ukf10, UnscentedKalmanFilter{Float32})
420420
end
421421

422422
@testset "UnscentedKalmanFilter estimator methods" begin
@@ -547,3 +547,148 @@ end
547547
@test [0, 0]
548548
@test isa(x̂, Vector{Float32})
549549
end
550+
551+
@testset "MovingHorizonEstimator construction" begin
552+
linmodel1 = LinModel(sys,Ts,i_d=[3])
553+
f(x,u,d) = linmodel1.A*x + linmodel1.Bu*u + linmodel1.Bd*d
554+
h(x,d) = linmodel1.C*x + linmodel1.Du*d
555+
nonlinmodel = NonLinModel(f, h, Ts, 2, 4, 2, 1)
556+
557+
mhe1 = MovingHorizonEstimator(linmodel1, He=5)
558+
@test mhe1.nym == 2
559+
@test mhe1.nyu == 0
560+
@test mhe1.nxs == 2
561+
@test mhe1.nx̂ == 6
562+
563+
mhe2 = MovingHorizonEstimator(nonlinmodel, He=5)
564+
@test mhe2.nym == 2
565+
@test mhe2.nyu == 0
566+
@test mhe2.nxs == 2
567+
@test mhe2.nx̂ == 6
568+
569+
mhe3 = MovingHorizonEstimator(nonlinmodel, He=5, i_ym=[2])
570+
@test mhe3.nym == 1
571+
@test mhe3.nyu == 1
572+
@test mhe3.nxs == 1
573+
@test mhe3.nx̂ == 5
574+
575+
mhe4 = MovingHorizonEstimator(nonlinmodel, He=5, σQ=[1,2,3,4], σQint_ym=[5, 6], σR=[7, 8])
576+
@test mhe4. Hermitian(diagm(Float64[1, 4, 9 ,16, 25, 36]))
577+
@test mhe4. Hermitian(diagm(Float64[49, 64]))
578+
579+
mhe5 = MovingHorizonEstimator(nonlinmodel, He=5, nint_ym=[2,2])
580+
@test mhe5.nxs == 4
581+
@test mhe5.nx̂ == 8
582+
583+
mhe6 = MovingHorizonEstimator(nonlinmodel, He=5, σP0=[1,2,3,4], σP0int_ym=[5,6])
584+
@test mhe6.P̂0 Hermitian(diagm(Float64[1, 4, 9 ,16, 25, 36]))
585+
@test mhe6. Hermitian(diagm(Float64[1, 4, 9 ,16, 25, 36]))
586+
@test mhe6.P̂0 !== mhe6.
587+
588+
mhe7 = MovingHorizonEstimator(nonlinmodel, He=10)
589+
@test mhe7.He == 10
590+
@test length(mhe7.X̂) == 10*6
591+
@test length(mhe7.Ym) == 10*2
592+
@test length(mhe7.U) == 10*2
593+
@test length(mhe7.D) == 10*1
594+
@test length(mhe7.Ŵ) == 10*6
595+
596+
mhe8 = MovingHorizonEstimator(nonlinmodel, He=5, nint_u=[1, 1], nint_ym=[0, 0])
597+
@test mhe8.nxs == 2
598+
@test mhe8.nx̂ == 6
599+
@test mhe8.nint_u == [1, 1]
600+
@test mhe8.nint_ym == [0, 0]
601+
602+
mhe9 = MovingHorizonEstimator(nonlinmodel, 5, 1:2, 0, [1, 1], I(6), I(6), I(2), Model(Ipopt.Optimizer))
603+
@test mhe9.P̂0 I(6)
604+
@test mhe9. I(6)
605+
@test mhe9. I(2)
606+
607+
mhe10 = MovingHorizonEstimator(nonlinmodel, He=5, optim=Model(OSQP.Optimizer))
608+
@test solver_name(mhe10.optim) == "OSQP"
609+
610+
linmodel2 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0)
611+
mhe11 = MovingHorizonEstimator(linmodel2, He=5)
612+
@test isa(mhe11, MovingHorizonEstimator{Float32})
613+
614+
@test_throws ArgumentError MovingHorizonEstimator(linmodel1)
615+
@test_throws ArgumentError MovingHorizonEstimator(linmodel1, He=0)
616+
end
617+
618+
@testset "MovingHorizonEstimator estimator methods" begin
619+
linmodel1 = LinModel(sys,Ts,i_u=[1,2])
620+
f(x,u,_) = linmodel1.A*x + linmodel1.Bu*u
621+
h(x,_) = linmodel1.C*x
622+
nonlinmodel = setop!(NonLinModel(f, h, Ts, 2, 2, 2), uop=[10,50], yop=[50,30])
623+
mhe1 = MovingHorizonEstimator(nonlinmodel, He=2)
624+
@test updatestate!(mhe1, [10, 50], [50, 30]) zeros(4) atol=1e-9
625+
@test updatestate!(mhe1, [10, 50], [50, 30], Float64[]) zeros(4) atol=1e-9
626+
@test mhe1. zeros(4) atol=1e-9
627+
@test evaloutput(mhe1) mhe1() [50, 30]
628+
@test evaloutput(mhe1, Float64[]) mhe1(Float64[]) [50, 30]
629+
@test initstate!(mhe1, [10, 50], [50, 30+1]) zeros(4) atol=1e-9
630+
setstate!(mhe1, [1,2,3,4])
631+
@test mhe1. [1,2,3,4]
632+
for i in 1:100
633+
updatestate!(mhe1, [11, 52], [50, 30])
634+
end
635+
@test mhe1() [50, 30] atol=1e-3
636+
for i in 1:100
637+
updatestate!(mhe1, [10, 50], [51, 32])
638+
end
639+
@test mhe1() [51, 32] atol=1e-3
640+
mhe2 = MovingHorizonEstimator(linmodel1, He=2, nint_u=[1, 1], nint_ym=[0, 0])
641+
for i in 1:100
642+
updatestate!(mhe2, [11, 52], [50, 30])
643+
end
644+
@test mhe2() [50, 30] atol=1e-3
645+
for i in 1:100
646+
updatestate!(mhe2, [10, 50], [51, 32])
647+
end
648+
@test mhe2() [51, 32] atol=1e-3
649+
linmodel3 = LinModel{Float32}(0.5*ones(1,1), ones(1,1), ones(1,1), zeros(1,0), zeros(1,0), 1.0)
650+
mhe3 = MovingHorizonEstimator(linmodel3, He=1)
651+
= updatestate!(mhe3, [0], [0])
652+
@test [0, 0] atol=1e-3
653+
@test isa(x̂, Vector{Float32})
654+
655+
mhe4 = setconstraint!(MovingHorizonEstimator(nonlinmodel, He=1, nint_ym=0), x̂max=[50,50])
656+
g_X̂max_end = mhe4.optim.nlp_model.operators.registered_multivariate_operators[end].f
657+
@test g_X̂max_end((1.0, 1.0, 1.0, 1.0)) 0.0 # test gfunc_i(i,::NTuple{N, Float64})
658+
# test gfunc_i(i,::NTuple{N, ForwardDiff.Dual}) :
659+
@test ForwardDiff.gradient(g_X̂max_end, [1.0, 1.0, 1.0, 1.0]) [0.0, 0.0, 0.0, 0.0]
660+
end
661+
662+
@testset "MovingHorizonEstimator set constraints" begin
663+
linmodel1 = setop!(LinModel(sys,Ts,i_u=[1,2]), uop=[10,50], yop=[50,30])
664+
mhe1 = MovingHorizonEstimator(linmodel1, He=1, nint_ym=0)
665+
setconstraint!(mhe1, x̂min=[-51,-52], x̂max=[53,54])
666+
@test all((mhe1.X̂min, mhe1.X̂max) .≈ ([-51,-52,-51,-52], [53,54,53,54]))
667+
668+
mhe2 = MovingHorizonEstimator(linmodel1, He=4, nint_ym=0)
669+
setconstraint!(mhe2, X̂min=-1(1:10), X̂max=1(1:10))
670+
@test all((mhe2.X̂min, mhe2.X̂max) .≈ (-1(1:10), 1(1:10)))
671+
672+
@test_throws ArgumentError setconstraint!(mhe2, x̂min=[-1])
673+
@test_throws ArgumentError setconstraint!(mhe2, x̂max=[+1])
674+
675+
updatestate!(mhe1, [10, 50], [50, 30])
676+
@test_throws ErrorException setconstraint!(mhe1, x̂min=[-Inf,-Inf])
677+
@test_throws ErrorException setconstraint!(mhe1, x̂max=[+Inf,+Inf])
678+
end
679+
680+
@testset "MovingHorizonEstimator constraint violation" begin
681+
linmodel1 = LinModel(sys,Ts,i_u=[1,2])
682+
f(x,u,_) = linmodel1.A*x + linmodel1.Bu*u
683+
h(x,_) = linmodel1.C*x
684+
nonlinmodel = setop!(NonLinModel(f, h, Ts, 2, 2, 2), uop=[10,50], yop=[50,30])
685+
mhe = MovingHorizonEstimator(nonlinmodel, He=1, nint_ym=0)
686+
687+
setconstraint!(mhe, x̂min=[1,1], x̂max=[100,100])
688+
= updatestate!(mhe, [10, 50], [50, 30])
689+
@test [1, 1] atol=1e-3
690+
691+
setconstraint!(mhe, x̂min=[-100,-100], x̂max=[-1,-1])
692+
= updatestate!(mhe, [10, 50], [50, 30])
693+
@test [-1, -1] atol=1e-3
694+
end

0 commit comments

Comments
 (0)