Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ext/LinearMPCext.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
if !only_hard
issoft(C) = any(x -> x > 0, C)
C_u = -mpc.con.A_Umin[:, end]
C_Δu = -mpc.con.A_ΔŨmin[1:nΔU, end]
C_Δu = -mpc.con.A_ΔUmin[:, end]
C_y = -mpc.con.A_Ymin[:, end]
C_w = -mpc.con.A_Wmin[:, end]
c_x̂ = -mpc.con.A_x̂min[:, end]
Expand All @@ -56,7 +56,7 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
# LinearMPC relies on a different softening mechanism (new implicit slacks for each
# softened bounds), so we apply an approximate conversion factor on the Cwt weight:
Cwt = weights.Ñ_Hc[end, end]
nsoft = sum((mpc.con.A[:,end] .< 0) .& (mpc.con.i_b)) - 1
nsoft = sum((mpc.con.A[:,end] .< 0) .& (mpc.con.i_b))
newmpc.settings.soft_weight = 10*sqrt(nsoft*Cwt)
else
C_u = zeros(nu*Hp)
Expand All @@ -81,7 +81,7 @@ function Base.convert(::Type{LinearMPC.MPC}, mpc::ModelPredictiveControl.LinMPC)
end
end
# --- Input increment constraints ---
ΔUmin, ΔUmax = mpc.con.ΔŨmin[1:nΔU], mpc.con.ΔŨmax[1:nΔU]
ΔUmin, ΔUmax = mpc.con.ΔUmin, mpc.con.ΔUmax
I_Δu = Matrix{Float64}(I, nu, nu)
for k = 0:Hc-1
Δumin_k, Δumax_k = ΔUmin[k*nu+1:(k+1)*nu], ΔUmax[k*nu+1:(k+1)*nu]
Expand Down Expand Up @@ -198,7 +198,7 @@ function validate_constraints(mpc::ModelPredictiveControl.LinMPC)
nΔU = mpc.Hc * mpc.estim.model.nu
mpc.weights.isinf_C && return nothing # only hard constraints are entirely supported
C_umin, C_umax = -mpc.con.A_Umin[:, end], -mpc.con.A_Umax[:, end]
C_Δumin, C_Δumax = -mpc.con.A_ΔŨmin[1:nΔU, end], -mpc.con.A_ΔŨmax[1:nΔU, end]
C_Δumin, C_Δumax = -mpc.con.A_ΔUmin[:, end], -mpc.con.A_ΔUmax[:, end]
C_ymin, C_ymax = -mpc.con.A_Ymin[:, end], -mpc.con.A_Ymax[:, end]
C_wmin, C_wmax = -mpc.con.A_Wmin[:, end], -mpc.con.A_Wmax[:, end]
C_x̂min, C_x̂max = -mpc.con.A_x̂min[:, end], -mpc.con.A_x̂max[:, end]
Expand Down
167 changes: 105 additions & 62 deletions src/controller/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,22 @@ struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
# bounds over the prediction horizon (deviation vectors from operating points):
U0min ::Vector{NT}
U0max ::Vector{NT}
ΔŨmin ::Vector{NT}
ΔŨmax ::Vector{NT}
ΔUmin ::Vector{NT}
ΔUmax ::Vector{NT}
Y0min ::Vector{NT}
Y0max ::Vector{NT}
Wmin ::Vector{NT}
Wmax ::Vector{NT}
x̂0min ::Vector{NT}
x̂0max ::Vector{NT}
# vectors for the box constraints:
Z̃min ::Vector{NT}
Z̃max ::Vector{NT}
# A matrices for the linear inequality constraints:
A_Umin ::SparseMatrixCSC{NT, Int}
A_Umax ::SparseMatrixCSC{NT, Int}
A_ΔŨmin ::SparseMatrixCSC{NT, Int}
A_ΔŨmax ::SparseMatrixCSC{NT, Int}
A_ΔUmin ::SparseMatrixCSC{NT, Int}
A_ΔUmax ::SparseMatrixCSC{NT, Int}
A_Ymin ::Matrix{NT}
A_Ymax ::Matrix{NT}
A_Wmin ::Matrix{NT}
Expand All @@ -181,7 +184,7 @@ struct ControllerConstraint{NT<:Real, GCfunc<:Union{Nothing, Function}}
beq ::Vector{NT}
# nonlinear equality constraints:
neq ::Int
# constraint softness parameter vectors needing seperate storage:
# constraint softness parameter vectors needing separate storage:
C_ymin ::Vector{NT}
C_ymax ::Vector{NT}
C_wmin ::Vector{NT}
Expand Down Expand Up @@ -260,7 +263,7 @@ LinMPC controller with a sample time Ts = 4.0 s:
│ └ 0 measured disturbances d
└ optimization:
├ 3 decision variables Z̃ (1 slack variable)
25 linear inequality constraints A (0 custom)
20 linear inequality constraints A (0 custom)
└ 0 linear equality constraints Aeq
```

Expand Down Expand Up @@ -345,9 +348,9 @@ function setconstraint!(
ΔUmin = DeltaUmin, ΔUmax = DeltaUmax,
C_Δumin = C_Deltaumin, C_Δumax = C_Deltaumax,
)
model, con = mpc.estim.model, mpc.con
model, estim, con = mpc.estim.model, mpc.estim, mpc.con
transcription, optim = mpc.transcription, mpc.optim
nu, ny, nx̂, Hp, Hc = model.nu, model.ny, mpc.estim.nx̂, mpc.Hp, mpc.Hc
nu, ny, nx̂, Hp, Hc = model.nu, model.ny, estim.nx̂, mpc.Hp, mpc.Hc
nϵ, nw, nc = mpc.nϵ, con.nw, con.nc
notSolvedYet = (JuMP.termination_status(optim) == JuMP.OPTIMIZE_NOT_CALLED)
if isnothing(Umin) && !isnothing(umin)
Expand All @@ -371,20 +374,20 @@ function setconstraint!(
if isnothing(ΔUmin) && !isnothing(Δumin)
size(Δumin) == (nu,) || throw(DimensionMismatch("Δumin size must be $((nu,))"))
for i = 1:nu*Hc
con.ΔŨmin[i] = Δumin[(i-1) % nu + 1]
con.ΔUmin[i] = Δumin[(i-1) % nu + 1]
end
elseif !isnothing(ΔUmin)
size(ΔUmin) == (nu*Hc,) || throw(DimensionMismatch("ΔUmin size must be $((nu*Hc,))"))
con.ΔŨmin[1:nu*Hc] .= ΔUmin
con.ΔUmin .= ΔUmin
end
if isnothing(ΔUmax) && !isnothing(Δumax)
size(Δumax) == (nu,) || throw(DimensionMismatch("Δumax size must be $((nu,))"))
for i = 1:nu*Hc
con.ΔŨmax[i] = Δumax[(i-1) % nu + 1]
con.ΔUmax[i] = Δumax[(i-1) % nu + 1]
end
elseif !isnothing(ΔUmax)
size(ΔUmax) == (nu*Hc,) || throw(DimensionMismatch("ΔUmax size must be $((nu*Hc,))"))
con.ΔŨmax[1:nu*Hc] .= ΔUmax
con.ΔUmax .= ΔUmax
end
if isnothing(Ymin) && !isnothing(ymin)
size(ymin) == (ny,) || throw(DimensionMismatch("ymin size must be $((ny,))"))
Expand All @@ -404,7 +407,6 @@ function setconstraint!(
size(Ymax) == (ny*Hp,) || throw(DimensionMismatch("Ymax size must be $((ny*Hp,))"))
con.Y0max .= Ymax .- mpc.Yop
end

if isnothing(Wmin) && !isnothing(wmin)
size(wmin) == (nw,) || throw(DimensionMismatch("wmin size must be $((nw,))"))
for i = 1:nw*(Hp+1)
Expand Down Expand Up @@ -462,12 +464,12 @@ function setconstraint!(
if !isnothing(C_Δumin)
size(C_Δumin) == (nu*Hc,) || throw(DimensionMismatch("C_Δumin size must be $((nu*Hc,))"))
any(<(0), C_Δumin) && error("C_Δumin weights should be non-negative")
con.A_ΔŨmin[1:end-1, end] .= -C_Δumin
con.A_ΔUmin[:, end] .= -C_Δumin
end
if !isnothing(C_Δumax)
size(C_Δumax) == (nu*Hc,) || throw(DimensionMismatch("C_Δumax size must be $((nu*Hc,))"))
any(<(0), C_Δumax) && error("C_Δumax weights should be non-negative")
con.A_ΔŨmax[1:end-1, end] .= -C_Δumax
con.A_ΔUmax[:, end] .= -C_Δumax
end
if !isnothing(C_ymin)
size(C_ymin) == (ny*Hp,) || throw(DimensionMismatch("C_ymin size must be $((ny*Hp,))"))
Expand Down Expand Up @@ -506,43 +508,59 @@ function setconstraint!(
size(con.A_x̂max, 1) ≠ 0 && (con.A_x̂max[:, end] .= -con.c_x̂max) # for LinModel
end
end
i_Umin, i_Umax = .!isinf.(con.U0min), .!isinf.(con.U0max)
i_ΔŨmin, i_ΔŨmax = .!isinf.(con.ΔŨmin), .!isinf.(con.ΔŨmax)
i_Ymin, i_Ymax = .!isinf.(con.Y0min), .!isinf.(con.Y0max)
i_Wmin, i_Wmax = .!isinf.(con.Wmin), .!isinf.(con.Wmax)
i_x̂min, i_x̂max = .!isinf.(con.x̂0min), .!isinf.(con.x̂0max)
Z̃min, Z̃max = init_boxconstraint_mpc(
estim, transcription, Hp, Hc, nϵ,
con.ΔUmin, con.ΔUmax, con.x̂0min, con.x̂0max,
con.A_ΔUmin, con.A_ΔUmax, con.A_x̂min, con.A_x̂max
)
if notSolvedYet
con.i_b[:], con.i_g[:], con.A[:] = init_matconstraint_mpc(
model, transcription, nc,
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
i_Ymin, i_Ymax, i_Wmin, i_Wmax,
i_x̂min, i_x̂max,
con.A_Umin, con.A_Umax, con.A_ΔŨmin, con.A_ΔŨmax,
con.A_Ymin, con.A_Ymax, con.A_Wmin, con.A_Wmax,
model, transcription, Z̃min, Z̃max, nc, nϵ,
con.U0min, con.U0max, con.ΔUmin, con.ΔUmax,
con.Y0min, con.Y0max, con.Wmin, con.Wmax,
con.x̂0min, con.x̂0max,
con.A_Umin, con.A_Umax, con.A_ΔUmin, con.A_ΔUmax,
con.A_Ymin, con.A_Ymax, con.A_Wmin, con.A_Wmax,
con.A_x̂min, con.A_x̂max,
con.Aeq
)
con.Z̃min[:], con.Z̃max[:] = Z̃min, Z̃max
A = con.A[con.i_b, :]
b = con.b[con.i_b]
Z̃var::Vector{JuMP.VariableRef} = optim[:Z̃var]
JuMP.delete(optim, optim[:linconstraint])
JuMP.unregister(optim, :linconstraint)
@constraint(optim, linconstraint, A*Z̃var .≤ b)
for i in eachindex(Z̃var)
JuMP.has_lower_bound(Z̃var[i]) && JuMP.delete_lower_bound(Z̃var[i])
JuMP.has_upper_bound(Z̃var[i]) && JuMP.delete_upper_bound(Z̃var[i])
!isinf(Z̃min[i]) && JuMP.set_lower_bound(Z̃var[i], Z̃min[i])
!isinf(Z̃max[i]) && JuMP.set_upper_bound(Z̃var[i], Z̃max[i])
end
reset_nonlincon!(mpc)
else
i_b, i_g = init_matconstraint_mpc(
model, transcription, nc,
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax,
i_Ymin, i_Ymax, i_Wmin, i_Wmax,
i_x̂min, i_x̂max,
model, transcription, Z̃min, Z̃max, nc, nϵ,
con.U0min, con.U0max, con.ΔUmin, con.ΔUmax,
con.Y0min, con.Y0max, con.Wmin, con.Wmax,
con.x̂0min, con.x̂0max
)
if i_b ≠ con.i_b || i_g ≠ con.i_g
diff_Z̃min, diff_Z̃max = diff_infs(Z̃min, con.Z̃min), diff_infs(Z̃max, con.Z̃max)
if i_b ≠ con.i_b || i_g ≠ con.i_g || diff_Z̃min || diff_Z̃max
error("Cannot modify ±Inf constraints after calling moveinput!")
end
con.Z̃min[:], con.Z̃max[:] = Z̃min, Z̃max
for i in eachindex(Z̃var)
!isinf(Z̃min[i]) && JuMP.set_lower_bound(Z̃var[i], con.Z̃min[i])
!isinf(Z̃max[i]) && JuMP.set_upper_bound(Z̃var[i], con.Z̃max[i])
end
end
return mpc
end

"Verify that the `Inf` values in `Z̃new` are the same that in `Z̃old`."
diff_infs(Z̃new, Z̃old) = any(isinf(x) ≠ isinf(y) for (x,y) in zip(Z̃new, Z̃old))

"By default, no nonlinear constraints, return nothing."
reset_nonlincon!(::PredictiveController) = nothing

Expand Down Expand Up @@ -909,20 +927,19 @@ function init_defaultcon_mpc(
W̄d = sparse(repeatdiag(Wd, Hp+1))
W̄r = sparse(repeatdiag(Wr, Hp+1))
A_Umin, A_Umax, P̃u = relaxU(Pu, C_umin, C_umax, nϵ)
A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃Δu = relaxΔU(PΔu, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ)
A_ΔUmin, A_ΔUmax, P̃Δu = relaxΔU(PΔu, C_Δumin, C_Δumax, nϵ)
A_Ymin, A_Ymax, Ẽ = relaxŶ(E, C_ymin, C_ymax, nϵ)
A_Wmin, A_Wmax, Ẽw = relaxW(E, Pu, Hp, W̄y, W̄u, C_wmin, C_wmax, nϵ)
A_x̂min, A_x̂max, ẽx̂ = relaxterminal(ex̂, c_x̂min, c_x̂max, nϵ)
Aeq, ẼS = augmentdefect(ES, nϵ)
i_Umin, i_Umax = .!isinf.(U0min), .!isinf.(U0max)
i_ΔŨmin, i_ΔŨmax = .!isinf.(ΔŨmin), .!isinf.(ΔŨmax)
i_Ymin, i_Ymax = .!isinf.(Y0min), .!isinf.(Y0max)
i_Wmin, i_Wmax = .!isinf.(Wmin), .!isinf.(Wmax)
i_x̂min, i_x̂max = .!isinf.(x̂0min), .!isinf.(x̂0max)
Z̃min, Z̃max = init_boxconstraint_mpc(
estim, transcription, Hp, Hc, nϵ,
ΔUmin, ΔUmax, x̂0min, x̂0max, A_ΔUmin, A_ΔUmax, A_x̂min, A_x̂max
)
i_b, i_g, A, Aeq, neq = init_matconstraint_mpc(
model, transcription, nc,
i_Umin, i_Umax, i_ΔŨmin, i_ΔŨmax, i_Ymin, i_Ymax, i_Wmin, i_Wmax, i_x̂min, i_x̂max,
A_Umin, A_Umax, A_ΔŨmin, A_ΔŨmax, A_Ymin, A_Ymax, A_Wmin, A_Wmax, A_x̂max, A_x̂min,
model, transcription, Z̃min, Z̃max, nc, nϵ,
U0min, U0max, ΔUmin, ΔUmax, Y0min, Y0max, Wmin, Wmax, x̂0min, x̂0max,
A_Umin, A_Umax, A_ΔUmin, A_ΔUmax, A_Ymin, A_Ymax, A_Wmin, A_Wmax, A_x̂max, A_x̂min,
Aeq
)
# dummy fx̂, Fw and FS vectors (updated just before optimization)
Expand All @@ -933,9 +950,10 @@ function init_defaultcon_mpc(
ẽx̂ , fx̂ , gx̂ , jx̂ , kx̂ , vx̂ , bx̂ ,
ẼS , FS , GS , JS , KS , VS , BS ,
Ẽw , Fw , W̄y , W̄u , W̄d , W̄r , nw ,
U0min , U0max , ΔŨmin , ΔŨmax ,
U0min , U0max , ΔUmin , ΔUmax ,
Y0min , Y0max , Wmin , Wmax , x̂0min , x̂0max ,
A_Umin , A_Umax , A_ΔŨmin , A_ΔŨmax ,
Z̃min , Z̃max ,
A_Umin , A_Umax , A_ΔUmin , A_ΔUmax ,
A_Ymin , A_Ymax , A_Wmin , A_Wmax , A_x̂min , A_x̂max ,
A , b , i_b ,
Aeq , beq ,
Expand Down Expand Up @@ -997,7 +1015,7 @@ function relaxU(Pu::AbstractMatrix{NT}, C_umin, C_umax, nϵ) where NT<:Real
end

@doc raw"""
relaxΔU(PΔu, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) -> A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃Δu
relaxΔU(PΔu, C_Δumin, C_Δumax, nϵ) -> A_ΔUmin, A_ΔUmax, P̃Δu

Augment input increments constraints with slack variable ϵ for softening.

Expand All @@ -1006,38 +1024,28 @@ Denoting the decision variables augmented with the slack variable
augmented conversion matrix ``\mathbf{P̃_{Δu}}``, similar to the one described at
[`init_ZtoΔU`](@ref), but extracting the input increments augmented with the slack variable
``\mathbf{ΔŨ} = [\begin{smallmatrix} \mathbf{ΔU} \\ ϵ \end{smallmatrix}] = \mathbf{P̃_{Δu} Z̃}``.
Also, knowing that ``0 ≤ ϵ ≤ ∞``, it also returns the augmented bounds
``\mathbf{ΔŨ_{min}} = [\begin{smallmatrix} \mathbf{ΔU_{min}} \\ 0 \end{smallmatrix}]`` and
``\mathbf{ΔŨ_{max}} = [\begin{smallmatrix} \mathbf{ΔU_{min}} \\ ∞ \end{smallmatrix}]``,
and the ``\mathbf{A}`` matrices for the inequality constraints:
It also returns the ``\mathbf{A}`` matrices for the inequality constraints:
```math
\begin{bmatrix}
\mathbf{A_{ΔŨ_{min}}} \\
\mathbf{A_{ΔŨ_{max}}}
\mathbf{A_{ΔU_{min}}} \\
\mathbf{A_{ΔU_{max}}}
\end{bmatrix} \mathbf{Z̃} ≤
\begin{bmatrix}
- \mathbf{ΔŨ_{min}} \\
+ \mathbf{ΔŨ_{max}}
- \mathbf{ΔU_{min}} \\
+ \mathbf{ΔU_{max}}
\end{bmatrix}
```
Note that strictly speaking, the lower bound on the slack variable ϵ is a decision variable
bound, which is more precise than a linear inequality constraint. However, it is more
convenient to treat it as a linear inequality constraint since the optimizer `OSQP.jl` does
not support pure bounds on the decision variables.
"""
function relaxΔU(PΔu::AbstractMatrix{NT}, C_Δumin, C_Δumax, ΔUmin, ΔUmax, nϵ) where NT<:Real
function relaxΔU(PΔu::AbstractMatrix{NT}, C_Δumin, C_Δumax, nϵ) where NT<:Real
nZ = size(PΔu, 2)
if nϵ == 1 # Z̃ = [Z; ϵ]
ΔŨmin, ΔŨmax = [ΔUmin; NT[0.0]], [ΔUmax; NT[Inf]] # 0 ≤ ϵ ≤ ∞
A_ϵ = [zeros(NT, 1, nZ) NT[1.0]]
A_ΔŨmin, A_ΔŨmax = -[PΔu C_Δumin; A_ϵ], [PΔu -C_Δumax; A_ϵ]
A_ΔUmin, A_ΔUmax = -[PΔu C_Δumin], [PΔu -C_Δumax]
P̃Δu = [PΔu zeros(NT, size(PΔu, 1), 1); zeros(NT, 1, size(PΔu, 2)) NT[1.0]]
else # Z̃ = Z (only hard constraints)
ΔŨmin, ΔŨmax = ΔUmin, ΔUmax
A_ΔŨmin, A_ΔŨmax = -PΔu, PΔu
A_ΔUmin, A_ΔUmax = -PΔu, PΔu
P̃Δu = PΔu
end
return A_ΔŨmin, A_ΔŨmax, ΔŨmin, ΔŨmax, P̃Δu
return A_ΔUmin, A_ΔUmax, P̃Δu
end

@doc raw"""
Expand Down Expand Up @@ -1216,6 +1224,41 @@ function augmentdefect(ES::AbstractMatrix{NT}, nϵ) where NT<:Real
return Aeq, ẼS
end

"""
init_boxconstraints_mpc(
estim::StateEstimator, transcription::TranscriptionMethod, Hp, Hc, nϵ,
ΔUmin, ΔUmax, x̂0min, x̂0max, A_ΔUmin, A_ΔUmax, A_x̂min, A_x̂max
) -> Z̃min, Z̃max

Init the decision variable box constraints `Z̃min` and `Z̃max`.
"""
function init_boxconstraint_mpc(
estim::StateEstimator{NT}, transcription::TranscriptionMethod, Hp, Hc, nϵ,
ΔUmin, ΔUmax, x̂0min, x̂0max, A_ΔUmin, A_ΔUmax, A_x̂min, A_x̂max
) where {NT<:Real}
nΔU, nX̂ = estim.model.nu*Hc, estim.nx̂*Hp
nZ̃ = get_nZ(estim, transcription, Hp, Hc) + nϵ
Z̃min, Z̃max = fill(convert(NT,-Inf), nZ̃), fill(convert(NT,+Inf), nZ̃)
nϵ > 0 && (Z̃min[end] = 0)
if nϵ > 0
n_C_Δumin = @views A_ΔUmin[:, end]
n_C_Δumax = @views A_ΔUmax[:, end]
for i in eachindex(ΔUmin)
iszero(n_C_Δumin[i]) && (Z̃min[i] = ΔUmin[i])
end
for i in eachindex(ΔUmax)
iszero(n_C_Δumax[i]) && (Z̃max[i] = ΔUmax[i])
end
else
Z̃min[1:nΔU] .= ΔUmin
Z̃max[1:nΔU] .= ΔUmax
end
Z̃min, Z̃max = boxconstraint_terminal!(
Z̃min, Z̃max, transcription, nΔU, nX̂, nϵ, x̂0min, x̂0max, A_x̂min, A_x̂max
)
return Z̃min, Z̃max
end

@doc raw"""
init_stochpred(estim::InternalModel, Hp) -> Ks, Ps

Expand Down
10 changes: 6 additions & 4 deletions src/controller/execute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,17 @@ function optim_objective!(mpc::PredictiveController{NT}) where {NT<:Real}
return mpc.Z̃
end


"By default, no need to update the objective function."
set_objective_linear_coef!(::PredictiveController, ::SimModel, _ ) = nothing
set_objective_linear_coef!(::PredictiveController, ::SimModel, _) = nothing

"Update the linear coefficients of the quadratic objective with `mpc.q̃` if applicable."
"Update the linear coefficients of the quadratic objective with `mpc.q̃` for `LinModel`."
function set_objective_linear_coef!(mpc::PredictiveController, ::LinModel, Z̃var)
mpc.weights.iszero_E && JuMP.set_objective_coefficient(mpc.optim, Z̃var, mpc.q̃)
return nothing
end


"""
preparestate!(mpc::PredictiveController, ym, d=[]) -> x̂

Expand Down Expand Up @@ -756,8 +758,8 @@ function setmodel_controller!(mpc::PredictiveController, uop_old, x̂op_old)
con.A .= [
con.A_Umin
con.A_Umax
con.A_ΔŨmin
con.A_ΔŨmax
con.A_ΔUmin
con.A_ΔUmax
con.A_Ymin
con.A_Ymax
con.A_x̂min
Expand Down
Loading
Loading