Skip to content

Commit d7f752e

Browse files
committed
test: cleanup and rework MHE custom constraints
1 parent 9df75ed commit d7f752e

1 file changed

Lines changed: 68 additions & 138 deletions

File tree

test/2_test_state_estim.jl

Lines changed: 68 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,18 @@ end
868868
mhe13 = MovingHorizonEstimator(linmodel2, He=5)
869869
@test isa(mhe13, MovingHorizonEstimator{Float32})
870870

871+
function gcl(X̂e, _ , _ , _ , _ , _ , _ , _ , nx, ε)
872+
gc = X̂e .- 100 .- ε
873+
return [gc; zeros(nc .- length(X̂e))]
874+
end
875+
He = 3
876+
nx̂ = linmodel.nx
877+
nc = nx̂*(He+1)
878+
mhe = MovingHorizonEstimator(linmodel, nint_ym=0, Cwt=1e5, He=He, gc=gcl, nc=nc, p=nc)
879+
@test mhe.con.nc == nc
880+
@test mhe.> 0
881+
@test mhe.He == 3
882+
871883
@test_throws ArgumentError MovingHorizonEstimator(linmodel)
872884
@test_throws ArgumentError MovingHorizonEstimator(linmodel, He=0)
873885
@test_throws ArgumentError MovingHorizonEstimator(linmodel, Cwt=-1)
@@ -1324,8 +1336,8 @@ end
13241336
info = getinfo(mhe)
13251337
@test info[:V̂] [-1,-1] atol=5e-2
13261338

1327-
f(x,u,_,model) = model.A*x + model.Bu*u
1328-
h(x,_,model) = model.C*x
1339+
f = (x,u,_,model) -> model.A*x + model.Bu*u
1340+
h = (x,_,model) -> model.C*x
13291341
nonlinmodel = NonLinModel(f, h, Ts, 2, 2, 2, p=linmodel, solver=nothing)
13301342
nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30])
13311343
mhe2 = MovingHorizonEstimator(nonlinmodel, He=1, nint_ym=0)
@@ -1374,6 +1386,60 @@ end
13741386
info = getinfo(mhe2)
13751387
@test info[:V̂] [-1,-1] atol=5e-2
13761388

1389+
linmodel2 = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1390+
linmodel2 = setop!(linmodel2, uop=[10,50], yop=[50,30], dop=[5])
1391+
function gclv!(LHS, X̂e, _, _, _, _, _, _, _, nx̂, _ )
1392+
for i in 1:div(length(X̂e), nx̂)
1393+
LHS[(i-1)+1] = 0.5 - X̂e[(i-1)*nx̂ + 1] # First state >= 0.5
1394+
end
1395+
return nothing
1396+
end
1397+
nx̂ = linmodel2.nx
1398+
nc = 2
1399+
mhe = MovingHorizonEstimator(linmodel2, He=1, gc=gclv!, nc=nc, nint_ym=0, p=nx̂)
1400+
preparestate!(mhe, [50, 30], [5])
1401+
= updatestate!(mhe, [10, 50], [50, 30], [5])
1402+
@test x̂[1] 0.5 atol = 5e-2
1403+
1404+
function gcln!(LHS, _, _, Ŵe, _, _, _, _,_, nx̂, _)
1405+
LHS .= Ŵe[1:nx̂]
1406+
return nothing
1407+
end
1408+
nx̂ = linmodel2.nx
1409+
nc = linmodel2.nx
1410+
mhe = MovingHorizonEstimator(linmodel2, He=1, gc=gcln!, nc=nc, nint_ym=0, direct=false, p=nx̂)
1411+
preparestate!(mhe, [50, 30], [5])
1412+
= updatestate!(mhe, [10, 50], [50, 30], [5])
1413+
@test mhe.Ŵ zeros(nx̂) atol=5e-2
1414+
1415+
f = (x,u,d,model) -> model.A*x + model.Bu*u + model.Bd*d
1416+
h = (x,d,model) -> model.C*x + model.Dd*d
1417+
nonlinmodel2 = NonLinModel(f, h, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel2)
1418+
nonlinmodel2 = setop!(nonlinmodel2, uop=[10,50], yop=[50,30], dop=[5])
1419+
function gcnlv!(LHS, X̂e, _, _, _, _, _, _, _, nx̂, _)
1420+
for i in 1:div(length(X̂e), nx̂)
1421+
LHS[(i-1)+1] = 0.5 - X̂e[(i-1)*nx̂ + 1] # First state >= 0.5
1422+
end
1423+
return nothing
1424+
end
1425+
nc = 2
1426+
nx̂ = nonlinmodel2.nx
1427+
mhe = MovingHorizonEstimator(nonlinmodel2, He=1, gc=gcnlv!, nc=nc, nint_ym=0, p=nx̂)
1428+
preparestate!(mhe, [50, 30], [5])
1429+
= updatestate!(mhe, [10, 50], [50, 30], [5])
1430+
@test x̂[1] 0.5 atol = 5e-2
1431+
1432+
function gclnl!(LHS, _, _, Ŵe, _, _, _, _,_, nx̂, _)
1433+
LHS .= Ŵe[1:nx̂]
1434+
return nothing
1435+
end
1436+
nx̂ = nonlinmodel2.nx
1437+
nc = nonlinmodel2.nx
1438+
mhe = MovingHorizonEstimator(nonlinmodel2, He=1, gc=gclnl!, nc=nc, nint_ym=0, p=nx̂)
1439+
preparestate!(mhe, [50, 30], [5])
1440+
= updatestate!(mhe, [10, 50], [50, 30], [5])
1441+
@test mhe.Ŵ zeros(nx̂) atol=5e-2
1442+
13771443
end
13781444

13791445
@testitem "MovingHorizonEstimator set model" setup=[SetupMPCtests] begin
@@ -1566,142 +1632,6 @@ end
15661632
@test X̂_lin X̂_nonlin atol=1e-3 rtol=1e-3
15671633
end
15681634

1569-
1570-
@testitem "MovingHorizonEstimator construction with custom constraint (LinModel)" setup=[SetupMPCtests] begin
1571-
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
1572-
linmodel = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1573-
linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5])
1574-
1575-
# Custom constraint: simple bound on states using only X̂e and ε
1576-
function gc_lin(X̂e, _ , _ , _ , _ , _ , _ , _ ,nX̂e , ε)
1577-
gc = X̂e .- 100 .- ε # all states must be <= 100
1578-
return [gc; zeros(nX̂e .- length(X̂e))]
1579-
end
1580-
He = 3
1581-
nx̂ = length(linmodel.A) + 2 # number of states (with augmentation)
1582-
nc = nx̂ * (He+1) # Approximate constraint count for He=3
1583-
nX̂e = nx̂ * (He+1)
1584-
mhe = MovingHorizonEstimator(linmodel, Cwt=1e5, He=He, gc=gc_lin, nc=nc, p=nX̂e)
1585-
1586-
@test mhe.con.nc == nc
1587-
@test mhe.> 0
1588-
@test mhe.He == 3
1589-
end
1590-
1591-
@testitem "MovingHorizonEstimator custom constraint violation (LinModel)" setup=[SetupMPCtests] begin
1592-
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
1593-
linmodel = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1594-
linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5])
1595-
1596-
# Custom constraint function using only X̂e and ε
1597-
# This constraint enforces that the first state must be >= 0.5
1598-
function gc_constraint!(LHS, X̂e, V̂e, Ŵe, Ue, Yem, De, P̄, x̄, p, ε)
1599-
nx̂ = 4 # number of states (with augmentation)
1600-
# Extract and constrain first state at each time step
1601-
for i in 1:div(length(X̂e), nx̂)
1602-
LHS[(i-1)+1] = 0.5 - X̂e[(i-1)*nx̂ + 1] # First state >= 0.5
1603-
end
1604-
return nothing
1605-
end
1606-
1607-
nc = 3 # One constraint per time step (He=1 initially, but grows)
1608-
mhe = MovingHorizonEstimator(linmodel, He=1, gc=gc_constraint!, nc=nc, Cwt=1e3, nint_ym=0)
1609-
1610-
preparestate!(mhe, [50, 30], [5])
1611-
= updatestate!(mhe, [10, 50], [50, 30], [5])
1612-
# The constraint should be satisfied (first state should be >= 0.5)
1613-
@test x̂[1] >= 0.5 - 0.1 # Allow some tolerance
1614-
end
1615-
1616-
@testitem "MovingHorizonEstimator custom constraint violation (NonLinModel)" setup=[SetupMPCtests] begin
1617-
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
1618-
using JuMP, Ipopt
1619-
linmodel = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1620-
linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5])
1621-
1622-
f = (x,u,d,model) -> model.A*x + model.Bu*u + model.Bd*d
1623-
h = (x,d,model) -> model.C*x + model.Dd*d
1624-
nonlinmodel = NonLinModel(f, h, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel)
1625-
nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30], dop=[5])
1626-
1627-
# Custom constraint function using only X̂e and ε
1628-
# This constraint enforces that the first state must be >= 0.5
1629-
function gc_constraint_nl!(LHS, X̂e, V̂e, Ŵe, Ue, Yem, De, P̄, x̄, p, ε)
1630-
nx̂ = 6 # number of states (with augmentation)
1631-
# Extract and constrain first state at each time step
1632-
for i in 1:div(length(X̂e), nx̂)
1633-
LHS[(i-1)+1] = 0.5 - X̂e[(i-1)*nx̂ + 1] # First state >= 0.5
1634-
end
1635-
return nothing
1636-
end
1637-
1638-
nc = 3 # One constraint per time step
1639-
optim = Model(Ipopt.Optimizer)
1640-
mhe = MovingHorizonEstimator(nonlinmodel, He=1, gc=gc_constraint_nl!, nc=nc, Cwt=1e3, nint_ym=0; optim)
1641-
1642-
preparestate!(mhe, [50, 30], [5])
1643-
= updatestate!(mhe, [10, 50], [50, 30], [5])
1644-
# The constraint should be satisfied (first state should be >= 0.5)
1645-
@test x̂[1] >= 0.5 - 0.1 # Allow some tolerance
1646-
end
1647-
1648-
@testitem "MovingHorizonEstimator custom constraint on noise (LinModel)" setup=[SetupMPCtests] begin
1649-
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
1650-
linmodel = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1651-
linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5])
1652-
1653-
# Custom constraint function using Ŵe and ε
1654-
# This constraint enforces that process noise must be <= 0
1655-
function gc_noise_constraint!(LHS, X̂e, V̂e, Ŵe, Ue, Yem, De, P̄, x̄, p, ε)
1656-
nx̂ = 4 # number of states (with augmentation)
1657-
# Constraint: all process noise components must be <= 0
1658-
for i in 1:length(Ŵe)
1659-
LHS[i] = Ŵe[i] # Ŵe <= 0
1660-
end
1661-
return nothing
1662-
end
1663-
1664-
nc = 4 * 2 # Two time steps with 4 states each
1665-
mhe = MovingHorizonEstimator(linmodel, He=1, gc=gc_noise_constraint!, nc=nc, Cwt=1e3, nint_ym=0)
1666-
1667-
preparestate!(mhe, [50, 30], [5])
1668-
= updatestate!(mhe, [10, 50], [50, 30], [5])
1669-
# The constraint should be satisfied (process noise should be <= 0)
1670-
@test all(mhe.Ŵ .<= 0.1) # Allow some tolerance
1671-
end
1672-
1673-
@testitem "MovingHorizonEstimator custom constraint on noise (NonLinModel)" setup=[SetupMPCtests] begin
1674-
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
1675-
using JuMP, Ipopt
1676-
linmodel = LinModel(sys, Ts, i_u=[1,2], i_d=[3])
1677-
linmodel = setop!(linmodel, uop=[10,50], yop=[50,30], dop=[5])
1678-
1679-
f = (x,u,d,model) -> model.A*x + model.Bu*u + model.Bd*d
1680-
h = (x,d,model) -> model.C*x + model.Dd*d
1681-
nonlinmodel = NonLinModel(f, h, Ts, 2, 4, 2, 1, solver=nothing, p=linmodel)
1682-
nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30], dop=[5])
1683-
1684-
# Custom constraint function using Ŵe and ε
1685-
# This constraint enforces that process noise must be <= 0
1686-
function gc_noise_constraint_nl!(LHS, X̂e, V̂e, Ŵe, Ue, Yem, De, P̄, x̄, p, ε)
1687-
nx̂ = 6 # number of states (with augmentation)
1688-
# Constraint: all process noise components must be <= 0
1689-
for i in 1:length(Ŵe)
1690-
LHS[i] = Ŵe[i] # Ŵe <= 0
1691-
end
1692-
return nothing
1693-
end
1694-
1695-
nc = 6 * 2 # Two time steps with 6 states each
1696-
optim = Model(Ipopt.Optimizer)
1697-
mhe = MovingHorizonEstimator(nonlinmodel, He=1, gc=gc_noise_constraint_nl!, nc=nc, Cwt=1e3, nint_ym=0; optim)
1698-
1699-
preparestate!(mhe, [50, 30], [5])
1700-
= updatestate!(mhe, [10, 50], [50, 30], [5])
1701-
# The constraint should be satisfied (process noise should be <= 0)
1702-
@test all(mhe.Ŵ .<= 0.1) # Allow some tolerance
1703-
end
1704-
17051635
@testitem "ManualEstimator construction" setup=[SetupMPCtests] begin
17061636
using .SetupMPCtests, ControlSystemsBase, LinearAlgebra
17071637
linmodel = LinModel(sys,Ts,i_u=[1,2])

0 commit comments

Comments
 (0)