@@ -67,32 +67,21 @@ The predictive controllers support both soft and hard constraints, defined by:
6767\b egin{alignat*}{3}
6868 \m athbf{u_{min} - c_{u_{min}}} ϵ &≤ \m athbf{u}(k+j) &&≤ \m athbf{u_{max} + c_{u_{max}}} ϵ &&\q quad j = 0, 1 ,..., H_c - 1 \\
6969 \m athbf{Δu_{min} - c_{Δu_{min}}} ϵ &≤ \m athbf{Δu}(k+j) &&≤ \m athbf{Δu_{max} + c_{Δu_{max}}} ϵ &&\q quad j = 0, 1 ,..., H_c - 1 \\
70- \m athbf{y_{min} - c_{y_{min}}} ϵ &≤ \m athbf{ŷ}(k+j) &&≤ \m athbf{y_{max} + c_{y_{max}}} ϵ &&\q quad j = 1, 2 ,..., H_p \\
70+ \m athbf{y_{min} - c_{y_{min}}} ϵ &≤ \m athbf{ŷ}(k+j) &&≤ \m athbf{y_{max} + c_{y_{max}}} ϵ &&\q quad j = 1, 2 ,..., H_p
7171\e nd{alignat*}
7272```
7373and also ``ϵ ≥ 0``. All the constraint parameters are vector. Use `±Inf` values when there
7474is no bound. The constraint softness parameters ``\m athbf{c}``, also called equal concern
7575for relaxation, are non-negative values that specify the softness of the associated bound.
7676Use `0.0` values for hard constraints. The output constraints ``\m athbf{y_{min}}`` and
77- ``\m athbf{y_{max}}`` are soft by default.
78-
79- The bounds can be modified after calling [`moveinput!`](@ref), that is, at runtime, but not
80- the softness parameters ``\m athbf{c}``. It is not possible to modify `±Inf` constraints
81- at runtime.
82-
83- !!! tip
84- To keep a variable unconstrained while maintaining the ability to add a constraint later
85- at runtime, set the bound to an absolute value sufficiently large when you create the
86- controller.
87-
88- Varying the constraints over the prediction/control horizon is not supported yet but it will
89- be implemented soon.
77+ ``\m athbf{y_{max}}`` are soft by default. See Extended Help for time-varying constraints.
9078
9179# Arguments
9280!!! info
9381 The default constraints are mentioned here for clarity but omitting a keyword argument
9482 will not re-assign to its default value (defaults are set at construction only).
9583
84+ - `mpc::PredictiveController` : predictive controller to set constraints.
9685- `umin=fill(-Inf,nu)` : manipulated input lower bounds ``\m athbf{u_{min}}``
9786- `umax=fill(+Inf,nu)` : manipulated input upper bounds ``\m athbf{u_{max}}``
9887- `Δumin=fill(-Inf,nu)` : manipulated input increment lower bounds ``\m athbf{Δu_{min}}``
@@ -105,6 +94,8 @@ be implemented soon.
10594- `c_Δumax=fill(0.0,nu)` : `Δumax` softness weights ``\m athbf{c_{Δu_{max}}}``
10695- `c_ymin=fill(1.0,ny)` : `ymin` softness weights ``\m athbf{c_{y_{min}}}``
10796- `c_ymax=fill(1.0,ny)` : `ymax` softness weights ``\m athbf{c_{y_{max}}}``
97+ - all keyword arguments above but with a capital letter e.g. `Ymax` or `c_ΔUmin` : for
98+ time-varying constraints (see Extended Help)
10899
109100# Examples
110101```jldoctest
@@ -122,6 +113,30 @@ LinMPC controller with a sample time Ts = 4.0 s, OSQP optimizer, SteadyKalmanFil
122113 0 unmeasured outputs yu
123114 0 measured disturbances d
124115```
116+
117+ # Extended Help
118+ The bounds can be modified after calling [`moveinput!`](@ref), that is at runtime, but not
119+ the softness parameters ``\m athbf{c}``. It is not possible to modify `±Inf` constraints
120+ at runtime.
121+
122+ !!! tip
123+ To keep a variable unconstrained while maintaining the ability to add a constraint later
124+ at runtime, set the bound to an absolute value sufficiently large when you create the
125+ controller.
126+
127+ It is also possible to specify time-varying constraints over prediction ``H_p`` and control
128+ ``H_c`` horizons. In such a case, they are defined by:
129+ ```math
130+ \b egin{alignat*}{3}
131+ \m athbf{U_{min} - c_{U_{min}}} ϵ &≤ \m athbf{U} &&≤ \m athbf{U_{max} + c_{U_{max}}} ϵ \\
132+ \m athbf{ΔU_{min} - c_{ΔU_{min}}} ϵ &≤ \m athbf{ΔU} &&≤ \m athbf{ΔU_{max} + c_{ΔY_{max}}} ϵ \\
133+ \m athbf{Y_{min} - c_{Y_{min}}} ϵ &≤ \m athbf{Ŷ} &&≤ \m athbf{Y_{max} + c_{Y_{max}}} ϵ
134+ \e nd{alignat*}
135+ ```
136+ For this, use the same keyword arguments as above but with a capital letter:
137+ - `Umin` \ `Umax` \ `c_Umin` \ `c_Umax` : ``\m athbf{U}`` constraints `(nu*Hp,)`.
138+ - `ΔUmin` \ `ΔUmax` \ `c_ΔUmin` \ `c_ΔUmax` : ``\m athbf{ΔU}`` constraints `(nu*Hc,)`.
139+ - `Ymin` \ `Ymax` \ `c_Ymin` \ `c_Ymax` : ``\m athbf{Ŷ}`` constraints `(nu*Hp,)`.
125140"""
126141function setconstraint! (
127142 mpc:: PredictiveController ;
@@ -131,20 +146,18 @@ function setconstraint!(
131146 c_umin = nothing , c_umax = nothing ,
132147 c_Δumin = nothing , c_Δumax = nothing ,
133148 c_ymin = nothing , c_ymax = nothing ,
134- # will be deleted in the future:
149+ Umin = nothing , Umax = nothing ,
150+ ΔUmin = nothing , ΔUmax = nothing ,
151+ Ymin = nothing , Ymax = nothing ,
152+ c_Umax = nothing , c_Umin = nothing ,
153+ c_ΔUmax = nothing , c_ΔUmin = nothing ,
154+ c_Ymax = nothing , c_Ymin = nothing ,
155+ # ------------ will be deleted in the future ---------------
135156 ŷmin = nothing , ŷmax = nothing ,
136157 c_ŷmin = nothing , c_ŷmax = nothing ,
158+ # ----------------------------------------------------------
137159)
138- model, con, optim = mpc. estim. model, mpc. con, mpc. optim
139- nu, ny, Hp, Hc = model. nu, model. ny, mpc. Hp, mpc. Hc
140- notSolvedYet = (termination_status (optim) == OPTIMIZE_NOT_CALLED)
141- C, E = mpc. C, mpc. Ẽ[:, 1 : nu* Hc]
142- if ! all (isnothing .([c_umin, c_umax, c_Δumin, c_Δumax, c_ymin, c_ymax]))
143- ! isinf (C) || throw (ArgumentError (" Slack variable Cwt must be finite to set softness parameters" ))
144- notSolvedYet || error (" Cannot set softness parameters after calling moveinput!" )
145- end
146-
147- # these 4 `if`s will be deleted in the future:
160+ # ----- these 4 `if`s will be deleted in the future --------
148161 if ! isnothing (ŷmin)
149162 Base. depwarn (" keyword arg ŷmin is deprecated, use ymin instead" , :setconstraint! )
150163 ymin = ŷmin
@@ -161,81 +174,89 @@ function setconstraint!(
161174 Base. depwarn (" keyword arg ŷmax is deprecated, use ymax instead" , :setconstraint! )
162175 c_ymax = c_ŷmax
163176 end
164-
165- if ! isnothing (umin)
166- size (umin) == (nu,) || throw (ArgumentError (" umin size must be $((nu,)) " ))
167- Umin = repeat (umin, Hp)
177+ # ----------------------------------------------------------
178+ model, con, optim = mpc. estim. model, mpc. con, mpc. optim
179+ nu, ny, Hp, Hc = model. nu, model. ny, mpc. Hp, mpc. Hc
180+ notSolvedYet = (termination_status (optim) == OPTIMIZE_NOT_CALLED)
181+ C, E = mpc. C, mpc. Ẽ[:, 1 : nu* Hc]
182+ isnothing (Umin) && ! isnothing (umin) && (Umin = repeat (umin, Hp))
183+ isnothing (Umax) && ! isnothing (umax) && (Umax = repeat (umax, Hp))
184+ isnothing (ΔUmin) && ! isnothing (Δumin) && (ΔUmin = repeat (Δumin, Hc))
185+ isnothing (ΔUmax) && ! isnothing (Δumax) && (ΔUmax = repeat (Δumax, Hc))
186+ isnothing (Ymin) && ! isnothing (ymin) && (Ymin = repeat (ymin, Hp))
187+ isnothing (Ymax) && ! isnothing (ymax) && (Ymax = repeat (ymax, Hp))
188+ isnothing (c_Umin) && ! isnothing (c_umin) && (c_Umin = repeat (c_umin, Hp))
189+ isnothing (c_Umax) && ! isnothing (c_umax) && (c_Umax = repeat (c_umax, Hp))
190+ isnothing (c_ΔUmin) && ! isnothing (c_Δumin) && (c_ΔUmin = repeat (c_Δumin, Hc))
191+ isnothing (c_ΔUmax) && ! isnothing (c_Δumax) && (c_ΔUmax = repeat (c_Δumax, Hc))
192+ isnothing (c_Ymin) && ! isnothing (c_ymin) && (c_Ymin = repeat (c_ymin, Hp))
193+ isnothing (c_Ymax) && ! isnothing (c_ymax) && (c_Ymax = repeat (c_ymax, Hp))
194+ if ! all (isnothing .([c_Umin, c_Umax, c_ΔUmin, c_ΔUmax, c_Ymin, c_Ymax]))
195+ ! isinf (C) || throw (ArgumentError (" Slack variable Cwt must be finite to set softness parameters" ))
196+ notSolvedYet || error (" Cannot set softness parameters after calling moveinput!" )
197+ end
198+ if ! isnothing (Umin)
199+ size (Umin) == (nu* Hp,) || throw (ArgumentError (" Umin size must be $((nu* Hp,)) " ))
168200 con. Umin[:] = Umin
169201 end
170- if ! isnothing (umax)
171- size (umax) == (nu,) || throw (ArgumentError (" umax size must be $((nu,)) " ))
172- Umax = repeat (umax, Hp)
202+ if ! isnothing (Umax)
203+ size (Umax) == (nu* Hp,) || throw (ArgumentError (" Umax size must be $((nu* Hp,)) " ))
173204 con. Umax[:] = Umax
174205 end
175- if ! isnothing (Δumin)
176- size (Δumin) == (nu,) || throw (ArgumentError (" Δumin size must be $((nu,)) " ))
177- ΔUmin = repeat (Δumin, Hc)
206+ if ! isnothing (ΔUmin)
207+ size (ΔUmin) == (nu* Hc,) || throw (ArgumentError (" ΔUmin size must be $((nu* Hc,)) " ))
178208 con. ΔŨmin[1 : nu* Hc] = ΔUmin
179209 end
180- if ! isnothing (Δumax)
181- size (Δumax) == (nu,) || throw (ArgumentError (" Δumax size must be $((nu,)) " ))
182- ΔUmax = repeat (Δumax, Hc)
210+ if ! isnothing (ΔUmax)
211+ size (ΔUmax) == (nu* Hc,) || throw (ArgumentError (" ΔUmax size must be $((nu* Hc,)) " ))
183212 con. ΔŨmax[1 : nu* Hc] = ΔUmax
184213 end
185- if ! isnothing (ymin)
186- size (ymin) == (ny,) || throw (ArgumentError (" ymin size must be $((ny,)) " ))
187- Ymin = repeat (ymin, Hp)
214+ if ! isnothing (Ymin)
215+ size (Ymin) == (ny* Hp,) || throw (ArgumentError (" Ymin size must be $((ny* Hp,)) " ))
188216 con. Ymin[:] = Ymin
189217 end
190- if ! isnothing (ymax)
191- size (ymax) == (ny,) || throw (ArgumentError (" ymax size must be $((ny,)) " ))
192- Ymax = repeat (ymax, Hp)
218+ if ! isnothing (Ymax)
219+ size (Ymax) == (ny* Hp,) || throw (ArgumentError (" Ymax size must be $((ny* Hp,)) " ))
193220 con. Ymax[:] = Ymax
194221 end
195- if ! isnothing (c_umin)
196- size (c_umin) == (nu,) || throw (ArgumentError (" c_umin size must be $((nu,)) " ))
197- any (c_umin .< 0 ) && error (" c_umin weights should be non-negative" )
198- c_Umin = repeat (c_umin, Hp)
222+ if ! isnothing (c_Umin)
223+ size (c_Umin) == (nu* Hp,) || throw (ArgumentError (" c_Umin size must be $((nu* Hp,)) " ))
224+ any (c_Umin .< 0 ) && error (" c_Umin weights should be non-negative" )
199225 con. A_Umin[:, end ] = - c_Umin
200226 end
201- if ! isnothing (c_umax)
202- size (c_umax) == (nu,) || throw (ArgumentError (" c_umax size must be $((nu,)) " ))
203- any (c_umax .< 0 ) && error (" c_umax weights should be non-negative" )
204- c_Umax = repeat (c_umax, Hp)
227+ if ! isnothing (c_Umax)
228+ size (c_Umax) == (nu* Hp,) || throw (ArgumentError (" c_Umax size must be $((nu* Hp,)) " ))
229+ any (c_Umax .< 0 ) && error (" c_Umax weights should be non-negative" )
205230 con. A_Umax[:, end ] = - c_Umax
206231 end
207- if ! isnothing (c_Δumin)
208- size (c_Δumin) == (nu,) || throw (ArgumentError (" c_Δumin size must be $((nu,)) " ))
209- any (c_Δumin .< 0 ) && error (" c_Δumin weights should be non-negative" )
210- c_ΔUmin = repeat (c_Δumin, Hc)
232+ if ! isnothing (c_ΔUmin)
233+ size (c_ΔUmin) == (nu* Hc,) || throw (ArgumentError (" c_ΔUmin size must be $((nu* Hc,)) " ))
234+ any (c_ΔUmin .< 0 ) && error (" c_ΔUmin weights should be non-negative" )
211235 con. A_ΔŨmin[1 : end - 1 , end ] = - c_ΔUmin
212236 end
213- if ! isnothing (c_Δumax)
214- size (c_Δumax) == (nu,) || throw (ArgumentError (" c_Δumax size must be $((nu,)) " ))
215- any (c_Δumax .< 0 ) && error (" c_Δumax weights should be non-negative" )
216- c_ΔUmax = repeat (c_Δumax, Hc)
237+ if ! isnothing (c_ΔUmax)
238+ size (c_ΔUmax) == (nu* Hc,) || throw (ArgumentError (" c_ΔUmax size must be $((nu* Hc,)) " ))
239+ any (c_ΔUmax .< 0 ) && error (" c_ΔUmax weights should be non-negative" )
217240 con. A_ΔŨmax[1 : end - 1 , end ] = - c_ΔUmax
218241 end
219- if ! isnothing (c_ymin)
220- size (c_ymin) == (ny,) || throw (ArgumentError (" c_ymin size must be $((ny,)) " ))
221- any (c_ymin .< 0 ) && error (" c_ymin weights should be non-negative" )
222- c_Ymin = repeat (c_ymin, Hp)
242+ if ! isnothing (c_Ymin)
243+ size (c_Ymin) == (ny* Hp,) || throw (ArgumentError (" c_Ymin size must be $((ny* Hp,)) " ))
244+ any (c_Ymin .< 0 ) && error (" c_Ymin weights should be non-negative" )
223245 con. c_Ymin[:] = c_Ymin
224246 A_Ymin ,_ = relaxŶ (model, C, con. c_Ymin, con. c_Ymax, E)
225247 con. A_Ymin[:] = A_Ymin
226248 end
227- if ! isnothing (c_ymax)
228- size (c_ymax) == (ny,) || throw (ArgumentError (" c_ymax size must be $((ny,)) " ))
229- any (c_ymax .< 0 ) && error (" c_ymax weights should be non-negative" )
230- c_Ymax = repeat (c_ymax, Hp)
249+ if ! isnothing (c_Ymax)
250+ size (c_Ymax) == (ny* Hp,) || throw (ArgumentError (" c_Ymax size must be $((ny* Hp,)) " ))
251+ any (c_Ymax .< 0 ) && error (" c_Ymax weights should be non-negative" )
231252 con. c_Ymax[:] = c_Ymax
232253 _, A_Ymax = relaxŶ (model, C, con. c_Ymin, con. c_Ymax, E)
233254 con. A_Ymax[:] = A_Ymax
234255 end
235256 i_Umin, i_Umax = .! isinf .(con. Umin), .! isinf .(con. Umax)
236257 i_ΔŨmin, i_ΔŨmax = .! isinf .(con. ΔŨmin), .! isinf .(con. ΔŨmin)
237258 i_Ymin, i_Ymax = .! isinf .(con. Ymin), .! isinf .(con. Ymax)
238- if notSolvedYet
259+ if notSolvedYet
239260 con. i_b[:], con. A[:] = init_linconstraint (model,
240261 i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax,
241262 con. A_Umin, con. A_Umax, con. A_ΔŨmin, con. A_ΔŨmax, con. A_Ymin, con. A_Ymax
@@ -1008,7 +1029,7 @@ function init_stochpred(estim::InternalModel, Hp)
10081029 return Ks, Ps
10091030end
10101031" Return empty matrices if `estim` is not a [`InternalModel`](@ref)."
1011- init_stochpred (:: StateEstimator , _ ) = zeros (0 , 0 ), zeros (0 , 0 )
1032+ init_stochpred (estim :: StateEstimator , _ ) = zeros (0 , estim . nxs ), zeros (0 , estim . model . ny )
10121033
10131034
10141035@doc raw """
0 commit comments