Code covered by the BSD License  

Highlights from
Model Predictive Control of Multi-Input, Multi-Output (MIMO) systems

from Model Predictive Control of Multi-Input, Multi-Output (MIMO) systems by Pooya Rezaei
Quadratic Programming is used to simulate Model Predictive Control of MIMO systems

MPC_calculation_delta_u(x0,yref_Np,MPC_case)
% Sets up model and all matrices needed to construct Hessian and
% constraints in QP-problem. The matrices that do not change during the
% iteration through time, are only constructed once. Others are modified
% each time this function is called.

% The formulation and original code (for SISO systems) is by Elling W.
% Jacobsen from KTH university, Sweden. The formulation can be found here:
% http://www.kth.se/polopoly_fs/1.120161!/Menu/general/column-content/attachment/f13_ewj.pdf
% The code is modified and generalized for MIMO systems by Pooya Rezaei, 
% University of Vermont, USA. 

function [u,MPC_case] = MPC_calculation_delta_u(x0,yref_Np,MPC_case)

% MPC Set-up
nx = MPC_case.nx;
ny = MPC_case.ny;
nu = MPC_case.nu;
xref_vec = MPC_case.C_pinv*yref_Np;
xref_vec = [xref_vec; zeros(nu,MPC_case.Np)];
xref_vec = reshape(xref_vec,(nx+nu)*MPC_case.Np,1);

if ~isfield(MPC_case,'opt') % This condition is true only the first time this function is called
    % Define A_tilde, B_tilde, C_tilde for delta_u formulation
    MPC_case.A_tilde = [MPC_case.A, MPC_case.B; zeros(nu,nx), eye(nu)];
    MPC_case.B_tilde = [MPC_case.B; eye(nu)];
    MPC_case.C_tilde = [MPC_case.C, zeros(ny,nu)];
    
    % Compute mapping from present (initial) state, Ahat, and future inputs, Bhat,
    % to future states: x_future=Ahat*x0+Bhat*u_future
    Ahat = zeros(MPC_case.Np*(nx+nu),nx+nu);
    Bhat = zeros(MPC_case.Np*(nx+nu),MPC_case.Np*nu);
    for i = 1:MPC_case.Np
        idx_i = ((i-1)*(nx+nu)+1):i*(nx+nu);
        Ahat(idx_i,:) = MPC_case.A_tilde^i;
        for j = 1:MPC_case.Np
            idx_j = ((j-1)*nu+1):j*nu;
            if i >= j
                Bhat(idx_i,idx_j) = MPC_case.A_tilde^(i-j)*MPC_case.B_tilde;
            end
        end 
    end
    MPC_case.opt.Ahat = Ahat;
    MPC_case.opt.Bhat = Bhat;

    % Get system and MPC data
    MPC_case.Qx = MPC_case.C'*MPC_case.Qy*MPC_case.C;
    MPC_case.S = MPC_case.C'*MPC_case.Sy*MPC_case.C;
    % Stack up state and output weights in block-diagonal matrices to
    % replace summing in objective function by matrix multiplication in QP
    Qxhat = zeros(MPC_case.Np*(nx+nu),MPC_case.Np*(nx+nu));
    for i = 1:MPC_case.Np
        idx = (i-1)*(nx+nu)+1:(i-1)*(nx+nu)+nx;
        if i < MPC_case.Np
            Qxhat(idx,idx) = MPC_case.Qx;
        else
            Qxhat(idx,idx) = MPC_case.S;
        end
    end
%     Qxhat = sparse(Qxhat);
    MPC_case.opt.Qxhat = Qxhat;
    
    Quhat = zeros(nu*MPC_case.Np,nu*MPC_case.Np);
    for i = 1:MPC_case.Np
        idx = (i-1)*nu+1:i*nu;
        Quhat(idx,idx) = MPC_case.Qu;
    end        
%     Quhat = sparse(Quhat);
    
    % Constraints on states in matrix form: y_min<Hhat*x<y_max
    Hhat = zeros(MPC_case.Np*ny,MPC_case.Np*(nx+nu));
    for i = 1:MPC_case.Np
        idx_i = (i-1)*ny+1:i*ny;
        idx_j = (i-1)*(nx+nu)+1:i*(nx+nu);
        Hhat(idx_i,idx_j) = MPC_case.C_tilde;
    end        
%     Hhat = sparse(Hhat);
    MPC_case.opt.Hhat = Hhat;
    
    %% Sets up objective function and constraints for QP problem
    % Constraints
    Aineq_ymax = Hhat*Bhat;
    Aineq_ymin = -Aineq_ymax;
    Aineq_xmax = Bhat;
    Aineq_xmin = -Aineq_xmax;
    MPC_case.opt.Aineq = [Aineq_ymax; Aineq_ymin; Aineq_xmax; Aineq_xmin];

    % Lower & Upper bounds for variables
    MPC_case.opt.delta_u_max = repmat(MPC_case.delta_u_max,MPC_case.Np,1);
    MPC_case.opt.delta_u_min = repmat(MPC_case.delta_u_min,MPC_case.Np,1);

    % Obj func
    H = Bhat'*Qxhat*Bhat+Quhat;
    % H = (H+H')/2;
    round_digit = 9;
    MPC_case.opt.H = round(H*10^round_digit)/10^round_digit; % Added to avoid CPLEX error about H being not symmetric
end
Xdev = MPC_case.opt.Ahat*x0-xref_vec;
bineq_ymax = repmat(MPC_case.ymax,MPC_case.Np,1)-MPC_case.opt.Hhat*(Xdev+xref_vec);
bineq_ymin = repmat(-MPC_case.ymin,MPC_case.Np,1)+MPC_case.opt.Hhat*(Xdev+xref_vec);
bineq_xmax = repmat(MPC_case.xmax,MPC_case.Np,1)-(Xdev+xref_vec);
bineq_xmin = repmat(-MPC_case.xmin,MPC_case.Np,1)+(Xdev+xref_vec);

bineq = [bineq_ymax; bineq_ymin; bineq_xmax; bineq_xmin];
f = Xdev'*MPC_case.opt.Qxhat*MPC_case.opt.Bhat;

% Calculate future Np inputs:
[delta_u,~,exitflag] = quadprog(MPC_case.opt.H,f,MPC_case.opt.Aineq,bineq,[],[],MPC_case.opt.delta_u_min,MPC_case.opt.delta_u_max); 
x1 = MPC_case.A_tilde*x0+MPC_case.B_tilde*delta_u(1:nu);
u = x1(nx+1:nx+nu);
% cplexqp can also be used here with the same inputs and outputs if it is
% installed.
if exitflag<0
    disp('WARNING: infeasible QP problem.')
    pause(0.1)
end



Contact us