From 14132a8e46df8e8d0ffda036eb2cba0da090a0fb Mon Sep 17 00:00:00 2001 From: franckgaga Date: Wed, 23 Sep 2026 13:21:59 -0400 Subject: [PATCH 1/3] debug: MHE with `NonLinModelDAE` and `nint_ym=0, nint_ym=0` now works The `SteadyKalmanFilter` construction for the default `covestim` was crashing because of an unobservable dummy `LinModel`. --- src/estimator/mhe/construct.jl | 4 +++- test/2_test_state_estim.jl | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/estimator/mhe/construct.jl b/src/estimator/mhe/construct.jl index b0ade0423..2ce3a2ad1 100644 --- a/src/estimator/mhe/construct.jl +++ b/src/estimator/mhe/construct.jl @@ -662,8 +662,10 @@ function default_covestim_mhe(model::SimModel, i_ym, nint_u, nint_ym, P̂_0, Q̂ elseif model isa NonLinModel return UnscentedKalmanFilter(model, i_ym, nint_u, nint_ym, P̂_0, Q̂, R̂; direct) else + # SteadyKalmanFilter with arbitrary LinModel (will be ignored): nx, nu, ny = model.nx, model.nu, model.ny - A, Bu, C = 0.1*I(nx), ones(nx, nu), ones(ny, nx) + A = range(0.1, 0.9, length=nx) # distinct and stable eigenvalues for observability + Bu, C = ones(nx, nu), ones(ny, nx) dummy_model = LinModel(A, Bu, C, 0, 0, model.Ts) return SteadyKalmanFilter(dummy_model, i_ym, nint_u, nint_ym, Q̂, R̂; direct) end diff --git a/test/2_test_state_estim.jl b/test/2_test_state_estim.jl index d17376c63..46ffe1a86 100644 --- a/test/2_test_state_estim.jl +++ b/test/2_test_state_estim.jl @@ -1059,6 +1059,8 @@ end mhe2 = MovingHorizonEstimator(dae; He=3, transcription, direct=false) @test mhe2.direct == false + @test_nowarn MovingHorizonEstimator(dae; He=3, nint_ym=0, nint_u=0) # default skf covestim test + @test_throws ArgumentError MovingHorizonEstimator(dae, He=3, transcription=SingleShooting()) @test_throws ArgumentError MovingHorizonEstimator(dae, He=3, transcription=MultipleShooting()) end From 3ce0e0df57de85242dcc74508d6764d1df1867c8 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Wed, 23 Sep 2026 13:36:48 -0400 Subject: [PATCH 2/3] debug: handle `nx=1` case --- Project.toml | 2 +- src/estimator/mhe/construct.jl | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 8a146db26..b8139c3d0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ModelPredictiveControl" uuid = "61f9bdb8-6ae4-484a-811f-bbf86720c31c" -version = "2.14.0" +version = "2.14.1" authors = ["Francis Gagnon"] [deps] diff --git a/src/estimator/mhe/construct.jl b/src/estimator/mhe/construct.jl index 2ce3a2ad1..68d8fee40 100644 --- a/src/estimator/mhe/construct.jl +++ b/src/estimator/mhe/construct.jl @@ -664,7 +664,8 @@ function default_covestim_mhe(model::SimModel, i_ym, nint_u, nint_ym, P̂_0, Q̂ else # SteadyKalmanFilter with arbitrary LinModel (will be ignored): nx, nu, ny = model.nx, model.nu, model.ny - A = range(0.1, 0.9, length=nx) # distinct and stable eigenvalues for observability + poles = nx > 1 ? range(0.1, 0.9, length=nx) : [0.5] + A = Diagonal(poles) # distinct and stable poles for observability Bu, C = ones(nx, nu), ones(ny, nx) dummy_model = LinModel(A, Bu, C, 0, 0, model.Ts) return SteadyKalmanFilter(dummy_model, i_ym, nint_u, nint_ym, Q̂, R̂; direct) From fb471850daae7947928822ead226ef0077073a61 Mon Sep 17 00:00:00 2001 From: franckgaga Date: Wed, 23 Sep 2026 14:00:23 -0400 Subject: [PATCH 3/3] test: smarter test (also verify the arrival covariance) --- test/2_test_state_estim.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/2_test_state_estim.jl b/test/2_test_state_estim.jl index 46ffe1a86..03363df08 100644 --- a/test/2_test_state_estim.jl +++ b/test/2_test_state_estim.jl @@ -1059,7 +1059,10 @@ end mhe2 = MovingHorizonEstimator(dae; He=3, transcription, direct=false) @test mhe2.direct == false - @test_nowarn MovingHorizonEstimator(dae; He=3, nint_ym=0, nint_u=0) # default skf covestim test + mhe_skf = MovingHorizonEstimator(dae; He=3, nint_ym=0, nint_u=0, σP_0 = [0.5]) + @test mhe_skf.cov.P̂_0 ≈ [0.5^2] + @test mhe_skf.cov.invP̄ ≈ [1/(0.5^2)] + @test mhe_skf.P̂arr_old ≈ [0.5^2] @test_throws ArgumentError MovingHorizonEstimator(dae, He=3, transcription=SingleShooting()) @test_throws ArgumentError MovingHorizonEstimator(dae, He=3, transcription=MultipleShooting())