Skip to content

Commit 11ea3df

Browse files
authored
Merge pull request #226 from JuliaControl/debug_mhe
debug: `MovingHorizonEstimator` no longer crash with error termination flag
2 parents 6e5618a + f1415d7 commit 11ea3df

File tree

5 files changed

+46
-6
lines changed

5 files changed

+46
-6
lines changed

src/estimator/mhe/execute.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ function set_warmstart_mhe!(V̂, X̂0, estim::MovingHorizonEstimator{NT}, Z̃var
472472
# will be inevitably different at the following time step.
473473
Z̃s[nx̃+Nk*nŵ+1:end] .= 1
474474
JuMP.set_start_value.(Z̃var, Z̃s)
475+
return Z̃s
475476
end
476477

477478
"Correct the covariance estimate at arrival using `covestim` [`StateEstimator`](@ref)."

src/general.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ function limit_solve_time(optim::GenericModel, Ts)
4949
JuMP.set_time_limit_sec(optim, Ts)
5050
catch err
5151
if isa(err, MOI.UnsupportedAttribute{MOI.TimeLimitSec})
52-
@warn "Solving time limit is not supported by the optimizer."
52+
@warn "Solving time limit is not supported by the $(JuMP.solver_name(optim)) "*
53+
"optimizer."
5354
else
5455
rethrow()
5556
end

src/precompile.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,18 @@ exmpc.estim()
7979
u = exmpc([55, 30])
8080
sim!(exmpc, 2, [55, 30])
8181

82-
f(x,u,_,model) = model.A*x + model.Bu*u
83-
h(x,_,model) = model.C*x
82+
function f!(xnext, x, u, _, model)
83+
mul!(xnext, model.A , x)
84+
mul!(xnext, model.Bu, u, 1, 1)
85+
return nothing
86+
end
87+
function h!(y, x, _, model)
88+
mul!(y, model.C, x)
89+
return nothing
90+
end
8491

8592
nlmodel = setop!(
86-
NonLinModel(f, h, Ts, 2, 2, 2, solver=nothing, p=model),
93+
NonLinModel(f!, h!, Ts, 2, 2, 2, solver=nothing, p=model),
8794
uop=[10, 10], yop=[50, 30]
8895
)
8996
y = nlmodel()
@@ -118,7 +125,7 @@ u = nmpc_mhe([55, 30])
118125
sim!(nmpc_mhe, 2, [55, 30])
119126

120127
function JE( _ , Ŷe, _ , R̂y)
121-
= Ŷe[3:end]
128+
= @views Ŷe[3:end]
122129
= R̂y -
123130
return dot(Ȳ, Ȳ)
124131
end

test/2_test_state_estim.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,16 @@ end
10351035
info = getinfo(mhe5)
10361036
@test info[:x̂] x̂ atol=1e-9
10371037
@test info[:Ŷ][end-1:end] [50, 30] atol=1e-9
1038+
1039+
# coverage of the branch with error termination status (with an infeasible problem):
1040+
mhe_infeas = MovingHorizonEstimator(nonlinmodel, He=1, Cwt=Inf)
1041+
mhe_infeas = setconstraint!(mhe_infeas, v̂min=[1, 1], v̂max=[-1, -1])
1042+
@test_logs(
1043+
(:error, "MHE terminated without solution: estimation in open-loop "*
1044+
"(more info in debug log)"),
1045+
preparestate!(mhe_infeas, [0, 0], [0])
1046+
)
1047+
10381048
# for coverage of NLP functions, the univariate syntax of JuMP.@operator
10391049
mhe6 = MovingHorizonEstimator(nonlinmodel, He=1, Cwt=Inf)
10401050
setconstraint!(mhe6, v̂min=[-51,-52], v̂max=[53,54])

test/3_test_predictive_control.jl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@test mpc6.weights.L_Hp Diagonal(diagm(repeat(Float64[0, 1], 15)))
2020
@test mpc6.weights.L_Hp isa Hermitian{Float64, Diagonal{Float64, Vector{Float64}}}
2121
mpc7 = @test_logs(
22-
(:warn, "Solving time limit is not supported by the optimizer."),
22+
(:warn, "Solving time limit is not supported by the DAQP optimizer."),
2323
LinMPC(model, optim=JuMP.Model(DAQP.Optimizer))
2424
)
2525
@test solver_name(mpc7.optim) == "DAQP"
@@ -121,6 +121,16 @@ end
121121
ΔU_diff = diff(getinfo(mpc7)[:U])
122122
@test ΔU_diff[[2, 4, 5, 7, 8, 9]] zeros(6) atol=1e-9
123123

124+
# coverage of the branch with error termination status (with an infeasible problem):
125+
mpc_infeas = LinMPC(linmodel2, Hp=1, Hc=1, Cwt=Inf)
126+
mpc_infeas = setconstraint!(mpc_infeas, umin=[+1], umax=[-1])
127+
preparestate!(mpc_infeas, [0], [0])
128+
@test_logs(
129+
(:error, "MPC terminated without solution: returning last solution shifted "*
130+
"(more info in debug log)"),
131+
moveinput!(mpc_infeas, [0], [0])
132+
)
133+
124134
@test_throws DimensionMismatch moveinput!(mpc1, [0,0,0])
125135
@test_throws DimensionMismatch moveinput!(mpc1, [0], [0,0])
126136
@test_throws DimensionMismatch moveinput!(mpc1; D̂ = fill(0, mpc1.Hp+1))
@@ -823,6 +833,17 @@ end
823833
ΔU_diff = diff(getinfo(nmpc11)[:U])
824834
@test ΔU_diff[[2, 4, 5, 7, 8, 9]] zeros(6) atol=1e-9
825835

836+
# coverage of the branch with error termination status (with an infeasible problem):
837+
nmpc_infeas = NonLinMPC(nonlinmodel, Hp=1, Hc=1, Cwt=Inf)
838+
nmpc_infeas = setconstraint!(nmpc_infeas, umin=[+1], umax=[-1])
839+
preparestate!(nmpc_infeas, [0], [0])
840+
@test_logs(
841+
(:error, "MPC terminated without solution: returning last solution shifted "*
842+
"(more info in debug log)"),
843+
moveinput!(nmpc_infeas, [0], [0])
844+
)
845+
846+
826847
@test_nowarn ModelPredictiveControl.info2debugstr(info)
827848
end
828849

0 commit comments

Comments
 (0)