11module LinearMPCext
22
3- using ModelPredictiveControl, LinearMPC
3+ using ModelPredictiveControl
44using LinearAlgebra, SparseArrays
55using JuMP
66
7+ import LinearMPC
78import ModelPredictiveControl: isblockdiag
89
910function Base. convert (:: Type{LinearMPC.MPC} , mpc:: ModelPredictiveControl.LinMPC )
@@ -25,16 +26,20 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
2526 LinearMPC. set_offset! (newmpc; uo= uoff, ho= yoff, doff= doff, xo= xoff, fo= foff)
2627 # --- State observer parameters ---
2728 Q, R = estim. cov. Q̂, estim. cov. R̂
28- set_state_observer! (newmpc; C= estim. Ĉm, Q, R)
29+ LinearMPC . set_state_observer! (newmpc; C= estim. Ĉm, Q, R)
2930 # --- Objective function weights ---
3031 Q = weights. M_Hp[1 : ny, 1 : ny]
3132 Qf = weights. M_Hp[end - ny+ 1 : end , end - ny+ 1 : end ]
3233 Rr = weights. Ñ_Hc[1 : nu, 1 : nu]
3334 R = weights. L_Hp[1 : nu, 1 : nu]
3435 LinearMPC. set_objective! (newmpc; Q, Rr, R, Qf)
35- if ! weights. isinf_C
36+ only_hard = weights. isinf_C
37+ if ! only_hard
38+ # LinearMPC relies on a different softening mechanism, so we apply
39+ # an approximate conversion factor on the softening weight:
3640 Cwt = weights. Ñ_Hc[end , end ]
37- newmpc. settings. soft_weight = Cwt
41+ conversion_factor = 0.1 # 0.09066
42+ newmpc. settings. soft_weight = conversion_factor* Cwt
3843 end
3944 # --- Custom move blocking ---
4045 LinearMPC. move_block! (newmpc, mpc. nb) # un-comment when debugged
@@ -59,9 +64,26 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
5964 for i in 1 : nu
6065 lb = isfinite (umin_k[i]) ? [umin_k[i]] : zeros (0 )
6166 ub = isfinite (umax_k[i]) ? [umax_k[i]] : zeros (0 )
62- soft = ( c_u_k[i] > 0 )
67+ soft = ! only_hard && c_u_k[i] > 0
6368 Au = I_u[i: i, :]
64- add_constraint! (newmpc; Au, lb, ub, ks, soft)
69+ LinearMPC. add_constraint! (newmpc; Au, lb, ub, ks, soft)
70+ end
71+ end
72+ # --- Input increment constraints ---
73+ nΔU = Hc * nu
74+ ΔUmin, ΔUmax = mpc. con. ΔŨmin[1 : nΔU], mpc. con. ΔŨmax[1 : nΔU]
75+ C_Δu = - mpc. con. A_ΔŨmin[1 : nΔU, end ]
76+ I_Δu = Matrix {Float64} (I, nu, nu)
77+ for k in 0 : Hc- 1
78+ Δumin_k, Δumax_k = ΔUmin[k* nu+ 1 : (k+ 1 )* nu], ΔUmax[k* nu+ 1 : (k+ 1 )* nu]
79+ c_Δu_k = C_Δu[k* nu+ 1 : (k+ 1 )* nu]
80+ ks = [k + 1 ] # a `1` in ks argument corresponds to the present time step k+0
81+ for i in 1 : nu
82+ lb = isfinite (Δumin_k[i]) ? [Δumin_k[i]] : zeros (0 )
83+ ub = isfinite (Δumax_k[i]) ? [Δumax_k[i]] : zeros (0 )
84+ soft = ! only_hard && c_Δu_k[i] > 0
85+ Au, Aup = I_Δu[i: i, :], - I_Δu[i: i, :]
86+ LinearMPC. add_constraint! (newmpc; Au, Aup, lb, ub, ks, soft)
6587 end
6688 end
6789 # --- Output constraints ---
@@ -74,9 +96,9 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
7496 for i in 1 : ny
7597 lb = isfinite (ymin_k[i]) ? [ymin_k[i]] : zeros (0 )
7698 ub = isfinite (ymax_k[i]) ? [ymax_k[i]] : zeros (0 )
77- soft = ( c_y_k[i] > 0 )
99+ soft = ! only_hard && c_y_k[i] > 0
78100 Ax, Ad = C[i: i, :], Dd[i: i, :]
79- add_constraint! (newmpc; Ax, Ad, lb, ub, ks, soft)
101+ LinearMPC . add_constraint! (newmpc; Ax, Ad, lb, ub, ks, soft)
80102 end
81103 end
82104 # --- Terminal constraints ---
@@ -87,9 +109,9 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
87109 for i in 1 : nx̂
88110 lb = isfinite (x̂0min[i]) ? [x̂0min[i]] : zeros (0 )
89111 ub = isfinite (x̂0max[i]) ? [x̂0max[i]] : zeros (0 )
90- soft = ( c_x̂[i] > 0 )
112+ soft = ! only_hard && c_x̂[i] > 0
91113 Ax = I_x̂[i: i, :]
92- add_constraint! (newmpc; Ax, lb, ub, ks, soft)
114+ LinearMPC . add_constraint! (newmpc; Ax, lb, ub, ks, soft)
93115 end
94116 return newmpc
95117end
@@ -142,20 +164,36 @@ function validate_weights(mpc::ModelPredictiveControl.LinMPC)
142164end
143165
144166function validate_constraints (mpc:: ModelPredictiveControl.LinMPC )
145- ΔŨmin, ΔŨmax = mpc. con. ΔŨmin, mpc. con. ΔŨmax
146- C_umin, C_umax = - mpc. con. A_Umin[:, end ], - mpc. con. A_Umax[:, end ]
147- C_ymin, C_ymax = - mpc. con. A_Ymin[:, end ], - mpc. con. A_Ymax[:, end ]
148- C_x̂min, C_x̂max = - mpc. con. A_x̂min[:, end ], - mpc. con. A_x̂max[:, end ]
167+ nΔU = mpc. Hc * mpc. estim. model. nu
168+ C_umin, C_umax = - mpc. con. A_Umin[:, end ], - mpc. con. A_Umax[:, end ]
169+ C_Δumin, C_Δumax = - mpc. con. A_ΔŨmin[1 : nΔU, end ], - mpc. con. A_ΔŨmax[1 : nΔU, end ]
170+ C_ymin, C_ymax = - mpc. con. A_Ymin[:, end ], - mpc. con. A_Ymax[:, end ]
171+ C_x̂min, C_x̂max = - mpc. con. A_x̂min[:, end ], - mpc. con. A_x̂max[:, end ]
149172 is0or1 (C) = all (x -> x ≈ 0 || x ≈ 1 , C)
150- if ! is0or1 (C_umin) || ! is0or1 (C_umax) || ! is0or1 (C_ymin) || ! is0or1 (C_ymax)
173+ if (
174+ ! is0or1 (C_umin) || ! is0or1 (C_umax) ||
175+ ! is0or1 (C_Δumin) || ! is0or1 (C_Δumax) ||
176+ ! is0or1 (C_ymin) || ! is0or1 (C_ymax) ||
177+ ! is0or1 (C_x̂min) || ! is0or1 (C_x̂max)
178+
179+ )
151180 error (" LinearMPC only supports softness parameters c = 0 or 1." )
152181 end
153- if ! isapprox (C_umin, C_umax) || ! isapprox (C_ymin, C_ymax) || ! isapprox (C_x̂min, C_x̂max)
182+ if (
183+ ! isapprox (C_umin, C_umax) ||
184+ ! isapprox (C_Δumin, C_Δumax) ||
185+ ! isapprox (C_ymin, C_ymax) ||
186+ ! isapprox (C_x̂min, C_x̂max)
187+ )
154188 error (" LinearMPC only supports identical softness parameters for lower and upper bounds." )
155189 end
156- nΔU = mpc. Hc * mpc. estim. model. nu
157- if any (isfinite, ΔŨmin[1 : nΔU]) || any (isfinite, ΔŨmax[1 : nΔU])
158- error (" LinearMPC does not support constraints on input increments Δu" )
190+ issoft (C) = any (x -> x > 0 , C)
191+ if ! mpc. weights. isinf_C && sum (mpc. con. i_b) > 1 # ignore the slack variable ϵ bound
192+ if issoft (C_umin) || issoft (C_Δumin) || issoft (C_ymin) || issoft (C_x̂min)
193+ @warn " The LinearMPC conversion applies an approximate conversion " *
194+ " of the soft constraints.\n You may need to adjust the soft_weight " *
195+ " field of the LinearMPC.MPC object to replicate behaviors."
196+ end
159197 end
160198 return nothing
161199end
@@ -174,8 +212,8 @@ are supported, including these restrictions:
174212- the transcription method must be [`SingleShooting`](@ref).
175213- the state estimator must be a [`SteadyKalmanFilter`](@ref) with `direct=true`.
176214- only block-diagonal weights are allowed.
177- - input increment constraints `` \m athbf{Δu_{min}}`` and `` \m athbf{Δu_{max}}`` are not
178- supported for now .
215+ - the constraint relaxation mechanism is different, so a 1-on-1 conversion of the soft
216+ constraints is impossible (use `Cwt=Inf` to disable relaxation) .
179217
180218But the package has also several exclusive functionalities, such as pre-stabilization,
181219constrained explicit MPC, and binary manipulated inputs. See the [`LinearMPC.jl`](@extref LinearMPC)
0 commit comments