Skip to content

Commit 0231e4e

Browse files
committed
added: ill-conditioned QP warning at controller construction
The condition number is verified only at construction of `SingleShooting` controllers (no verification in `setmodel!`)
1 parent 438c8e2 commit 0231e4e

File tree

6 files changed

+41
-11
lines changed

6 files changed

+41
-11
lines changed

docs/src/manual/nonlinmpc.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ Nonlinear MPC is more computationally expensive than [`LinMPC`](@ref). Solving t
331331
should always be faster than the sampling time ``T_s = 0.1`` s for real-time operation. This
332332
requirement is sometimes hard to meet on electronics or mechanical systems because of the
333333
fast dynamics. To ease the design and comparison with [`LinMPC`](@ref), the [`linearize`](@ref)
334-
function allows automatic linearization of [`NonLinModel`](@ref) based on [`ForwardDiff`](@extref ForwardDiff).
334+
function allows automatic linearization of [`NonLinModel`](@ref) defaulting to [`ForwardDiff`](@extref ForwardDiff).
335335
We first linearize `model` at the point ``θ = π`` rad and ``ω = τ = 0`` (inverted position):
336336

337337
```@example man_nonlin
@@ -341,7 +341,6 @@ linmodel = linearize(model, x=[π, 0], u=[0])
341341
A [`SteadyKalmanFilter`](@ref) and a [`LinMPC`](@ref) are designed from `linmodel`:
342342

343343
```@example man_nonlin
344-
345344
skf = SteadyKalmanFilter(linmodel; σQ, σR, nint_u, σQint_u)
346345
mpc = LinMPC(skf; Hp, Hc, Mwt, Nwt, Cwt=Inf)
347346
mpc = setconstraint!(mpc, umin=[-1.5], umax=[+1.5])

src/controller/construct.jl

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,10 @@ function validate_args(mpc::PredictiveController, ry, d, D̂, R̂y, R̂u)
564564
end
565565

566566
@doc raw"""
567-
init_quadprog(model::LinModel, weights::ControllerWeights, Ẽ, P̃Δu, P̃u) -> H̃
567+
init_quadprog(
568+
model::LinModel, transcriptions::TranscriptionMethod, weights::ControllerWeights,
569+
Ẽ, P̃Δu, P̃u; warn_cond=1e6
570+
) -> H̃
568571
569572
Init the quadratic programming Hessian `H̃` for MPC.
570573
@@ -582,19 +585,43 @@ in which ``\mathbf{Ẽ}``, ``\mathbf{P̃_{Δu}}`` and ``\mathbf{P̃_{u}}`` matri
582585
at [`relaxŶ`](@ref), [`relaxΔU`](@ref) and [`relaxU`](@ref) documentation, respectively. The
583586
vector ``\mathbf{q̃}`` and scalar ``r`` need recalculation each control period ``k``, see
584587
[`initpred!`](@ref). ``r`` does not impact the minima position. It is thus useless at
585-
optimization but required to evaluate the minimal ``J`` value.
588+
optimization but required to evaluate the minimal ``J`` value. A `@warn` will be displayed
589+
if the condition number `cond(H̃) ≥ warn_cond` and `transcription` is a `SingleShooting`
590+
(`warn_cond=Inf` for no warning).
586591
"""
587-
function init_quadprog(::LinModel, weights::ControllerWeights, Ẽ, P̃Δu, P̃u)
592+
function init_quadprog(
593+
::LinModel, transcription::TranscriptionMethod, weights::ControllerWeights,
594+
Ẽ, P̃Δu, P̃u; warn_cond=1e6
595+
)
588596
M_Hp, Ñ_Hc, L_Hp = weights.M_Hp, weights.Ñ_Hc, weights.L_Hp
589597
= Hermitian(2*(Ẽ'*M_Hp*+ P̃Δu'*Ñ_Hc*P̃Δu + P̃u'*L_Hp*P̃u), :L)
598+
verify_cond(transcription, H̃, warn_cond)
590599
return
591600
end
592601
"Return empty matrix if `model` is not a [`LinModel`](@ref)."
593-
function init_quadprog(::SimModel{NT}, weights::ControllerWeights, _, _, _) where {NT<:Real}
602+
function init_quadprog(
603+
::SimModel{NT}, ::TranscriptionMethod, ::ControllerWeights, args...; kwargs...
604+
) where {NT<:Real}
594605
= Hermitian(zeros(NT, 0, 0), :L)
595606
return
596607
end
597608

609+
"Check the condition number of `H̃` and display a `@warn` if `cond(H̃) > warn_cond`."
610+
function verify_cond(::SingleShooting, H̃, warn_cond)
611+
if !isinf(warn_cond)
612+
cond_H̃ = cond(H̃)
613+
cond_H̃ > warn_cond && @warn(
614+
"The Hessian condition number cond_H̃ > $warn_cond. The optimization "*
615+
"problem may be ill-conditioned.\n Consider changing the tunings, using "*
616+
"MultipleShooting, or using an optimizer more robust to this like DAQP.",
617+
cond_H̃,
618+
)
619+
end
620+
return nothing
621+
end
622+
"No check if `transcription` is not a `SingleShooting`."
623+
verify_cond(::TranscriptionMethod,_,_) = nothing
624+
598625
"""
599626
init_defaultcon_mpc(
600627
estim::StateEstimator,

src/controller/execute.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ end
609609
"Update the prediction matrices, linear constraints and JuMP optimization."
610610
function setmodel_controller!(mpc::PredictiveController, uop_old, x̂op_old)
611611
model, estim, transcription = mpc.estim.model, mpc.estim, mpc.transcription
612+
weights = mpc.weights
612613
nu, ny, nd, Hp, Hc = model.nu, model.ny, model.nd, mpc.Hp, mpc.Hc
613614
optim, con = mpc.optim, mpc.con
614615
# --- prediction matrices ---
@@ -676,7 +677,8 @@ function setmodel_controller!(mpc::PredictiveController, uop_old, x̂op_old)
676677
con.x̂0min .-= estim.x̂op # convert x̂ to x̂0 with the new operating point
677678
con.x̂0max .-= estim.x̂op # convert x̂ to x̂0 with the new operating point
678679
# --- quadratic programming Hessian matrix ---
679-
= init_quadprog(model, mpc.weights, mpc.Ẽ, mpc.P̃Δu, mpc.P̃u)
680+
# do not verify the condition number of the Hessian here:
681+
= init_quadprog(model, transcription, weights, mpc.Ẽ, mpc.P̃Δu, mpc.P̃u; warn_cond=Inf)
680682
mpc.H̃ .=
681683
# --- JuMP optimization ---
682684
Z̃var::Vector{JuMP.VariableRef} = optim[:Z̃var]

src/controller/explicitmpc.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct ExplicitMPC{
5757
# dummy val (updated just before optimization):
5858
F = zeros(NT, ny*Hp)
5959
P̃Δu, P̃u, Ẽ = PΔu, Pu, E # no slack variable ϵ for ExplicitMPC
60-
= init_quadprog(model, weights, Ẽ, P̃Δu, P̃u)
60+
= init_quadprog(model, transcription, weights, Ẽ, P̃Δu, P̃u)
6161
# dummy vals (updated just before optimization):
6262
q̃, r = zeros(NT, size(H̃, 1)), zeros(NT, 1)
6363
H̃_chol = cholesky(H̃)
@@ -225,6 +225,7 @@ addinfo!(info, mpc::ExplicitMPC) = info
225225
"Update the prediction matrices and Cholesky factorization."
226226
function setmodel_controller!(mpc::ExplicitMPC, uop_old, _ )
227227
model, estim, transcription = mpc.estim.model, mpc.estim, mpc.transcription
228+
weights = mpc.weights
228229
nu, ny, nd, Hp, Hc = model.nu, model.ny, model.nd, mpc.Hp, mpc.Hc
229230
# --- predictions matrices ---
230231
E, G, J, K, V, B = init_predmat(model, estim, transcription, Hp, Hc)
@@ -236,7 +237,8 @@ function setmodel_controller!(mpc::ExplicitMPC, uop_old, _ )
236237
mpc.V .= V
237238
mpc.B .= B
238239
# --- quadratic programming Hessian matrix ---
239-
= init_quadprog(model, mpc.weights, mpc.Ẽ, mpc.P̃Δu, mpc.P̃u)
240+
# do not verify the condition number of the Hessian here:
241+
= init_quadprog(model, transcription, weights, mpc.Ẽ, mpc.P̃Δu, mpc.P̃u, warn_cond=Inf)
240242
mpc.H̃ .=
241243
set_objective_hessian!(mpc)
242244
# --- operating points ---

src/controller/linmpc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ struct LinMPC{
7979
ex̂, fx̂, gx̂, jx̂, kx̂, vx̂, bx̂,
8080
Eŝ, Fŝ, Gŝ, Jŝ, Kŝ, Vŝ, Bŝ
8181
)
82-
= init_quadprog(model, weights, Ẽ, P̃Δu, P̃u)
82+
= init_quadprog(model, transcription, weights, Ẽ, P̃Δu, P̃u)
8383
# dummy vals (updated just before optimization):
8484
q̃, r = zeros(NT, size(H̃, 1)), zeros(NT, 1)
8585
Ks, Ps = init_stochpred(estim, Hp)

src/controller/nonlinmpc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ struct NonLinMPC{
103103
Eŝ, Fŝ, Gŝ, Jŝ, Kŝ, Vŝ, Bŝ,
104104
gc!, nc
105105
)
106-
= init_quadprog(model, weights, Ẽ, P̃Δu, P̃u)
106+
= init_quadprog(model, transcription, weights, Ẽ, P̃Δu, P̃u)
107107
# dummy vals (updated just before optimization):
108108
q̃, r = zeros(NT, size(H̃, 1)), zeros(NT, 1)
109109
Ks, Ps = init_stochpred(estim, Hp)

0 commit comments

Comments
 (0)