|
868 | 868 | mhe13 = MovingHorizonEstimator(linmodel2, He=5) |
869 | 869 | @test isa(mhe13, MovingHorizonEstimator{Float32}) |
870 | 870 |
|
| 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.nε > 0 |
| 881 | + @test mhe.He == 3 |
| 882 | + |
871 | 883 | @test_throws ArgumentError MovingHorizonEstimator(linmodel) |
872 | 884 | @test_throws ArgumentError MovingHorizonEstimator(linmodel, He=0) |
873 | 885 | @test_throws ArgumentError MovingHorizonEstimator(linmodel, Cwt=-1) |
@@ -1324,8 +1336,8 @@ end |
1324 | 1336 | info = getinfo(mhe) |
1325 | 1337 | @test info[:V̂] ≈ [-1,-1] atol=5e-2 |
1326 | 1338 |
|
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 |
1329 | 1341 | nonlinmodel = NonLinModel(f, h, Ts, 2, 2, 2, p=linmodel, solver=nothing) |
1330 | 1342 | nonlinmodel = setop!(nonlinmodel, uop=[10,50], yop=[50,30]) |
1331 | 1343 | mhe2 = MovingHorizonEstimator(nonlinmodel, He=1, nint_ym=0) |
@@ -1374,6 +1386,60 @@ end |
1374 | 1386 | info = getinfo(mhe2) |
1375 | 1387 | @test info[:V̂] ≈ [-1,-1] atol=5e-2 |
1376 | 1388 |
|
| 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 | + x̂ = 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 | + x̂ = 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 | + x̂ = 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 | + x̂ = updatestate!(mhe, [10, 50], [50, 30], [5]) |
| 1441 | + @test mhe.Ŵ ≈ zeros(nx̂) atol=5e-2 |
| 1442 | + |
1377 | 1443 | end |
1378 | 1444 |
|
1379 | 1445 | @testitem "MovingHorizonEstimator set model" setup=[SetupMPCtests] begin |
@@ -1566,142 +1632,6 @@ end |
1566 | 1632 | @test X̂_lin ≈ X̂_nonlin atol=1e-3 rtol=1e-3 |
1567 | 1633 | end |
1568 | 1634 |
|
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.nε > 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 | | - x̂ = 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 | | - x̂ = 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 | | - x̂ = 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 | | - x̂ = 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 | | - |
1705 | 1635 | @testitem "ManualEstimator construction" setup=[SetupMPCtests] begin |
1706 | 1636 | using .SetupMPCtests, ControlSystemsBase, LinearAlgebra |
1707 | 1637 | linmodel = LinModel(sys,Ts,i_u=[1,2]) |
|
0 commit comments