def __init__(self, dynamics, u_open_loop, t_open_loop):
        """__init__ Create controller
        
        Arguments:
            dynamics {dynamical system} -- dynamics for the controller
            u_open_loop {numpy array [Nu,Nt]} -- open loop time series
            t_open_loop {numpy array [Nt,} -- time vector
        """

        Controller.__init__(self, dynamics)
        self.u_open_loop = u_open_loop
        self.t_open_loop = t_open_loop
        self.m = u_open_loop.shape[1]
    def __init__(self, bilinear_dynamics, output, lin_controller_gain):
        """Create an FBLinController object.

        Policy is u = (act)^-1 * (aux), where drift and act are
        components of drift vector and actuation matrix corresponding to
        highest-order derivatives of each output coordinate and aux is an
        auxilliary linear controller.

        Inputs:
        Bilinear dynamics, fb_lin_dynamics: FBLinDynamics
        Auxilliary linear controller, linear_controller: LinearController
        """

        Controller.__init__(self, bilinear_dynamics)
        self.dynamics = bilinear_dynamics
        self.output = output
        self.lin_controller_gain = lin_controller_gain
        self.u_prev = zeros(self.dynamics.m)
Esempio n. 3
0
    def __init__(self,
                 dynamics,
                 nom_controller,
                 pert_noise_var,
                 const_offset=0,
                 umin=None,
                 umax=None):
        """Create a PDController object.

        Policy is u = -K_p * e_p - K_d * e_d, where e_p and e_d are propotional
        and derivative components of error.

        Inputs:
        Dynamics, dynamics: Dynamics
        Nominal controller, controller: Controller
        """

        Controller.__init__(self, dynamics)
        self.nom_controller = nom_controller
        self.pert_noise_var = pert_noise_var
        self.const_offset = const_offset
        self.umin = umin
        self.umax = umax
Esempio n. 4
0
    def __init__(self,
                 lifted_linear_dynamics,
                 N,
                 dt,
                 umin,
                 umax,
                 xmin,
                 xmax,
                 Q,
                 R,
                 QN,
                 q_d,
                 const_offset=None,
                 terminal_constraint=False,
                 add_slack=False):
        """__init__ Create an MPC controller
        
        Arguments:
            lifted_linear_dynamics {LinearLiftedDynamics} -- Lifted linear continuous-time dynamics
            N {integer} -- MPC prediction horizon, number of timesteps
            dt {float} -- time step in seconds
            umin {numpy array [Nu,]} -- minimum control bound
            umax {numpy array [Nu,]} -- maximum control bound
            xmin {numpy array [Ns,]} -- minimum state bound
            xmax {numpy array [Ns,]} -- maximum state bound
            Q {numpy array [Ns,Ns]} -- state cost matrix
            R {numpy array [Nu,Nu]} -- control cost matrix
            QN {numpy array [Ns,]} -- final state cost
            xr {numpy array [Ns,]} -- reference trajectory
        """

        Controller.__init__(self, lifted_linear_dynamics)
        self.dynamics_object = lifted_linear_dynamics
        self.dt = dt
        if lifted_linear_dynamics.continuous_mdl:
            Ac = lifted_linear_dynamics.A
            Bc = lifted_linear_dynamics.B
            [self.nx, self.nu] = Bc.shape
            lin_model_d = cont2discrete(
                (Ac, Bc, np.eye(self.nx), np.zeros((self.nu, 1))), dt)
            self._osqp_Ad = sparse.csc_matrix(lin_model_d[0])
            self._osqp_Bd = sparse.csc_matrix(lin_model_d[1])
        else:
            self._osqp_Ad = lifted_linear_dynamics.A
            self._osqp_Bd = lifted_linear_dynamics.B
            [self.nx, self.nu] = self._osqp_Bd.shape
        self.C = lifted_linear_dynamics.C

        self.Q = Q
        self.QN = QN
        self.R = R
        self.N = N
        self.xmin = xmin
        self.xmax = xmax
        self.umin = umin
        self.umax = umax

        if const_offset is None:
            self.const_offset = np.zeros(self.nu)
        else:
            self.const_offset = const_offset

        # Total desired path
        self.q_d = q_d
        self.ns = q_d.shape[0]
        if self.q_d.ndim == 2:
            # Add copies of the final state in the desired trajectory to enable prediction beyond trajectory horizon:
            self.q_d = np.hstack([
                self.q_d,
                np.transpose(np.tile(self.q_d[:, -1], (self.N + 1, 1)))
            ])
        self.terminal_constraint = terminal_constraint
        self.add_slack = add_slack

        # Initialize OSQP MPC Problem:
        self.build_objective_()
        self.build_constraints_()
        self.prob = osqp.OSQP()
        self.prob.setup(self._osqp_P,
                        self._osqp_q,
                        self._osqp_A,
                        self._osqp_l,
                        self._osqp_u,
                        warm_start=True,
                        verbose=False)
        self._osqp_result = None
        self.comp_time = []
    def __init__(self,
                 linear_dynamics,
                 N,
                 dt,
                 umin,
                 umax,
                 xmin,
                 xmax,
                 Q,
                 R,
                 QN,
                 xr,
                 plotMPC=False,
                 plotMPC_filename="",
                 lifting=False,
                 edmd_object=None,
                 name="noname",
                 soft=False,
                 D=None):
        """Create an MPC Controller object.

        Sizes:
        - N: number of timesteps for predictions
        - Nqd: number of timesteps of the desired trajectory
        - nx: number of states (original or lifted)
        - ns: number or original states
        - nu: number of control inputs

        Inputs:
        - initilized linear_dynamics, LinearSystemDynamics object. It takes Ac and Bc from it.
        - number of timesteps, N, integer
        - time step, dt, float
        - minimum control,  umin, numpy 1d array [nu,]
        - maximum control,  umax, numpy 1d array [nu,]
        - minimum state,    xmin, numpy 1d array [ns,]
        - maximum state,    xmax, numpy 1d array [ns,]
        - state cost matrix    Q, sparse numpy 2d array [ns,ns]. In practice it is always diagonal. 
        - control cost matrix, R, sparse numpy 2d array [nu,nu]. In practice it is always diagonal. 
        - final state cost matrix,  QN, sparse numpy 2d array [ns,ns]. In practice it is always diagonal. 
        - reference state trajectory, xr, numpy 2d array [ns,Nqd] OR numpy 1d array [ns,]
        (Optional)
        - flag to plot MPC thoughts, plotMPC=False, boolean
        - filename to save the previosu plot, plotMPC_filename="", string
        - flag to use or not lifting, lifting=False, boolean
        - object to store the eDMD data, edmd_object=Edmd(). It has been initialized. s
        """

        Controller.__init__(self, linear_dynamics)

        # Load arguments
        Ac, Bc = linear_dynamics.linear_system()
        [nx, nu] = Bc.shape
        ns = xr.shape[0]

        self.dt = dt
        self.plotMPC = plotMPC
        self.plotMPC_filename = plotMPC_filename
        self.verbose = False
        self.q_d = xr

        self.Q = Q
        self.R = R
        self.lifting = lifting
        self.soft = soft

        self.nu = nu
        self.nx = nx
        self.ns = ns

        #Discretize dynamics:
        self.dt = dt
        if lifting:
            self.C = edmd_object.C
            self.edmd_object = edmd_object
        else:
            self.C = sparse.eye(ns)
        lin_model_d = sp.signal.cont2discrete((Ac, Bc, self.C, zeros((ns, 1))),
                                              dt)
        Ad = sparse.csc_matrix(
            lin_model_d[0])  #TODO: If bad behavior, delete this
        Bd = sparse.csc_matrix(
            lin_model_d[1])  #TODO: If bad behavior, delete this

        # Total desired path
        if self.q_d.ndim == 2:
            self.Nqd = self.q_d.shape[1]
            xr = self.q_d[:, :N]

        # Prediction horizon
        self.N = N
        x0 = np.zeros(nx)
        self.run_time = np.zeros([
            0,
        ])
        self.xr = self.q_d[:, :N]

        Rbd = sparse.kron(sparse.eye(N), R)
        Qbd = sparse.kron(sparse.eye(N), Q)
        Bbd = block_diag(Bd, nu).tocoo()

        # Check Xmin and Xmax
        if xmin.shape[
                0] == ns and xmin.ndim == 1:  # it is a single vector we tile it
            x_min_flat = np.kron(np.ones(N), xmin)
            x_max_flat = np.kron(np.ones(N), xmax)
        elif xmin.shape[0] == ns * N:  # if it is a long vector it is ok
            x_min_flat = xmin
            x_max_flat = xmax
        elif xmin.shape[0] == ns and xmin.shape[
                1] == N:  # if it is a block we flatten it
            x_min_flat = np.reshape(xmin, (N * ns, ), order='F')
            x_max_flat = np.reshape(xmax, (N * ns, ), order='F')
        else:
            raise ValueError('xmin has wrong dimensions. xmin shape={}'.format(
                xmin.shape))
        self.x_min_flat = x_min_flat
        self.x_max_flat = x_max_flat

        # Check Umin and Umax
        if umin.shape[0] == nu and umin.ndim == 1:
            u_min_flat = np.kron(np.ones(N), umin)
            u_max_flat = np.kron(np.ones(N), umax)
        elif umin.shape[0] == nu * N:
            u_min_flat = umin
            u_max_flat = umax
        elif umin.shape[0] == nu and umin.shape[1] == N:
            u_min_flat = np.reshape(umin, (N * nu, ), order='F')
            u_max_flat = np.reshape(umax, (N * nu, ), order='F')
        else:
            raise ValueError('umin has wrong dimensions. Umin shape={}'.format(
                umin.shape))
        self.u_min_flat = u_min_flat
        self.u_max_flat = u_max_flat

        #! GET a & b
        # Write B:
        diag_AkB = Bd
        data_list = Bbd.data
        row_list = Bbd.row
        col_list = Bbd.col
        B = sparse.coo_matrix
        for i in range(N):
            if i < N - 1:
                AkB_bd_temp = block_diag(diag_AkB, N - i)
            else:
                AkB_bd_temp = diag_AkB.tocoo()
            data_list = np.hstack([data_list, AkB_bd_temp.data])
            row_list = np.hstack([
                row_list, AkB_bd_temp.row + np.full(
                    (AkB_bd_temp.row.shape[0], ), nx * i)
            ])
            col_list = np.hstack([col_list, AkB_bd_temp.col])

            diag_AkB = Ad.dot(diag_AkB)

        B = sparse.coo_matrix((data_list, (row_list, col_list)),
                              shape=(N * nx, N * nu))

        a = Ad.copy()
        Ak = Ad.copy()
        for i in range(N - 1):
            Ak = Ak.dot(Ad)
            a = sparse.vstack([a, Ak])

        self.a = a
        self.B = B

        # check_ab = True
        # if check_ab:
        #     x0  = np.linspace(-5,40,nx)
        #     x00 = np.linspace(-5,40,nx)
        #     # Store data Init
        #     nsim = N
        #     xst = np.zeros((nx,nsim))
        #     ust = np.zeros((nu,nsim))

        #     # Simulate in closed loop

        #     for i in range(nsim):
        #         # Fake pd controller
        #         ctrl = np.zeros(nu,) #np.random.rand(nu,)
        #         x0 = Ad.dot(x0) + Bd.dot(ctrl)

        #         # Store Data
        #         xst[:,i] = x0
        #         ust[:,i] = ctrl

        #     x_dense = np.reshape(a @ x00 + B @ (ust.flatten('F')),(N,nx)).T

        #     plt.figure()
        #     plt.subplot(2,1,1)
        #     for i in range(nx):
        #         plt.plot(range(nsim),xst[i,:],'d',label="sim "+str(i))
        #         plt.plot(range(nsim),x_dense[i,:],'d',label="ax+bu "+str(i))
        #     plt.xlabel('Time(s)')
        #     plt.grid()
        #     plt.legend()

        #     plt.subplot(2,1,2)
        #     for i in range(nu):
        #         plt.plot(range(nsim),ust[i,:],label=str(i))
        #     plt.xlabel('Time(s)')
        #     plt.grid()
        #     plt.legend()
        #     plt.savefig("AB_check_for_"+name+".png",bbox_inches='tight')
        #     plt.close()

        # Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
        if (self.lifting):
            # Compute Block Diagonal elements
            self.Cbd = sparse.kron(sparse.eye(N), self.C)
            CQCbd = self.Cbd.T @ Qbd @ self.Cbd
            self.CtQ = self.C.T @ Q
            Cbd = self.Cbd

            P = Rbd + B.T @ CQCbd @ B
            self.BTQbda = B.T @ CQCbd @ a
            Aineq_x = Cbd @ B

            xrQB = B.T @ np.reshape(self.CtQ.dot(xr), (N * nx, ), order='F')
            l = np.hstack([x_min_flat - Cbd @ a @ x0, u_min_flat])
            u = np.hstack([x_max_flat - Cbd @ a @ x0, u_max_flat])

        else:
            # - quadratic objective
            P = Rbd + B.T @ Qbd @ B
            self.BTQbda = B.T @ Qbd @ a
            xrQB = B.T @ np.reshape(Q.dot(xr), (N * nx, ), order='F')
            Aineq_x = B

            l = np.hstack([x_min_flat - a @ x0, u_min_flat])
            u = np.hstack([x_max_flat - a @ x0, u_max_flat])

        x0aQb = self.BTQbda @ x0
        self.Cbda = Cbd @ a
        self.BQxr = self.B.T @ np.reshape(self.CtQ.dot(self.xr), (N * nx, ),
                                          order='F')
        q = x0aQb - xrQB
        Aineq_u = sparse.eye(N * nu)
        A = sparse.vstack([Aineq_x, Aineq_u]).tocsc()

        if soft:
            Pdelta = sparse.kron(sparse.eye(N), D)
            P = sparse.block_diag([P, Pdelta])
            qdelta = np.zeros(N * ns)
            q = np.hstack([q, qdelta])
            Adelta = sparse.csc_matrix(
                np.vstack([np.eye(N * ns),
                           np.zeros((N * nu, N * ns))]))
            A = sparse.hstack([A, Adelta])

        # plot_matrices = True
        # if plot_matrices:
        #     #! Visualize Matrices
        #     fig = plt.figure()

        #     fig.suptitle("QP Matrices to solve MP in dense form. N={}, nx={}, nu={}".format(N,nx,nu),fontsize=20)
        #     plt.subplot(2,4,1,xlabel="Ns*(N+1)", ylabel="Ns*(N+1)")
        #     plt.imshow(a.toarray(),  interpolation='nearest', cmap=cm.Greys_r)
        #     plt.title("a in $x=ax_0+bu$")
        #     plt.subplot(2,4,2,xlabel="Ns*(N+1)", ylabel="Nu*N")
        #     plt.imshow(B.toarray(),  interpolation='nearest', cmap=cm.Greys_r)
        #     plt.title("b in $x=ax_0+bu$")
        #     plt.subplot(2,4,3,xlabel="ns*(N+1) + ns*(N+1) + nu*N", ylabel="Ns*(N+1)+Nu*N")
        #     plt.imshow(A.toarray(),  interpolation='nearest', cmap=cm.Greys_r)
        #     plt.title("A total in $l\\leq Ax \\geq u$")
        #     plt.subplot(2,4,4)
        #     plt.imshow(P.toarray(),  interpolation='nearest', cmap=cm.Greys_r)
        #     plt.title("P in $J=u^TPu+q^Tu$")
        #     plt.subplot(2,4,5)
        #     plt.imshow(Qbd.toarray(),  interpolation='nearest', cmap=cm.Greys_r)
        #     plt.title("Qbd")

        #     #! Visualize Vectors
        #     plt.subplot(2,4,6)
        #     plt.plot(l)
        #     plt.title('l in  $l\\leq Ax \\geq u$')
        #     plt.grid()
        #     plt.subplot(2,4,7)
        #     plt.plot(u)
        #     plt.title("l in  $l\\leq Ax \\geq u$")
        #     plt.grid()
        #     plt.subplot(2,4,8)
        #     plt.plot(q)
        #     plt.title("q in $J=u^TPu+q^Tu$")
        #     plt.grid()
        #     plt.tight_layout()
        #     plt.savefig("MPC_matrices_for_"+name+".png",bbox_inches='tight')
        #     plt.close()
        #plt.show()

        self.osqp_size = P.shape[0]
        # Create an OSQP object
        self.prob = osqp.OSQP()
        # Setup workspace
        self.prob.setup(P=P.tocsc(),
                        q=q,
                        A=A,
                        l=l,
                        u=u,
                        warm_start=True,
                        verbose=self.verbose)

        if self.plotMPC:
            # Figure to plot MPC thoughts
            self.fig, self.axs = plt.subplots(self.ns + self.nu)
            if nx == 4:
                ylabels = [
                    '$x$', '$\\theta$', '$\\dot{x}$', '$\\dot{\\theta}$'
                ]
            else:
                ylabels = [str(i) for i in range(nx)]

            for ii in range(self.ns):
                self.axs[ii].set(xlabel='Time(s)', ylabel=ylabels[ii])
                self.axs[ii].grid()
            for ii in range(self.ns, self.ns + self.nu):
                self.axs[ii].set(xlabel='Time(s)', ylabel='u')
                self.axs[ii].grid()
Esempio n. 6
0
    def __init__(self,
                 linear_dynamics,
                 N,
                 dt,
                 umin,
                 umax,
                 xmin,
                 xmax,
                 Q,
                 R,
                 QN,
                 xr,
                 plotMPC=False,
                 plotMPC_filename="",
                 lifting=False,
                 edmd_object=None,
                 name="noname",
                 soft=False,
                 D=None):
        """__init__ [summary]
        
        Arguments:
            linear_dynamics {dynamical sytem} -- it contains the A and B matrices in continous time
            N {integer} -- number of timesteps
            dt {float} -- time step in seconds
            umin {numpy array [Nu,]} -- minimum control bound
            umax {numpy array [Nu,]} -- maximum control bound
            xmin {numpy array [Ns,]} -- minimum state bound
            xmax {numpy array [Ns,]} -- maximum state bound
            Q {numpy array [Ns,Ns]} -- state cost matrix
            R {numpy array [Nu,Nu]} -- control cost matrix
            QN {numpy array [Ns,]} -- final state cost
            xr {numpy array [Ns,]} -- reference trajectory
        
        Keyword Arguments:
            plotMPC {bool} -- flag to plot results (default: {False})
            plotMPC_filename {str} -- plotting filename (default: {""})
            lifting {bool} -- flag to use state lifting (default: {False})
            edmd_object {edmd object} -- lifting object. It contains projection matrix and lifting function (default: {Edmd()})
            name {str} -- name for all saved files (default: {"noname"})
            soft {bool} -- flag to enable soft constraints (default: {False})
            D {[type]} -- cost matrix for the soft variables (default: {None})
        """

        Controller.__init__(self, linear_dynamics)

        # Load arguments
        Ac, Bc = linear_dynamics.linear_system()
        [nx, nu] = Bc.shape
        ns = xr.shape[0]

        #Discretize dynamics:
        self.dt = dt
        if lifting:
            self.C = edmd_object.C
            self.edmd_object = edmd_object
        else:
            self.C = sparse.eye(ns)

        lin_model_d = cont2discrete((Ac, Bc, self.C, zeros((ns, 1))), dt)
        Ad = sparse.csc_matrix(
            lin_model_d[0])  #TODO: If bad behavior, delete this
        Bd = sparse.csc_matrix(
            lin_model_d[1])  #TODO: If bad behavior, delete this
        self.plotMPC = plotMPC
        self.plotMPC_filename = plotMPC_filename
        self.q_d = xr

        self.Q = Q
        self.R = R
        self.lifting = lifting

        self.nu = nu
        self.nx = nx
        self.ns = ns
        self.soft = soft
        self.comp_time = []

        # Total desired path
        if self.q_d.ndim == 2:
            self.Nqd = self.q_d.shape[1]
            xr = self.q_d[:, :N]

        # Prediction horizon
        self.N = N
        x0 = np.zeros(nx)
        self.run_time = np.zeros([
            0,
        ])

        Rbd = sparse.kron(sparse.eye(N), R)
        Qbd = sparse.kron(sparse.eye(N), Q)
        Bbd = block_diag(Bd, nu).tocoo()

        # Check Xmin and Xmax
        if xmin.shape[
                0] == ns and xmin.ndim == 1:  # it is a single vector we tile it
            x_min_flat = np.kron(np.ones(N), xmin)
            x_max_flat = np.kron(np.ones(N), xmax)
        elif xmin.shape[0] == ns * N:  # if it is a long vector it is ok
            x_min_flat = xmin
            x_max_flat = xmax
        elif xmin.shape[0] == ns and xmin.shape[
                1] == N:  # if it is a block we flatten it
            x_min_flat = np.reshape(xmin, (N * ns, ), order='F')
            x_max_flat = np.reshape(xmax, (N * ns, ), order='F')
        else:
            raise ValueError('xmin has wrong dimensions. xmin shape={}'.format(
                xmin.shape))
        self.x_min_flat = x_min_flat
        self.x_max_flat = x_max_flat

        # Check Umin and Umax
        if umin.shape[0] == nu and umin.ndim == 1:
            u_min_flat = np.kron(np.ones(N), umin)
            u_max_flat = np.kron(np.ones(N), umax)
        elif umin.shape[0] == nu * N:
            u_min_flat = umin
            u_max_flat = umax
        elif umin.shape[0] == nu and umin.shape[1] == N:
            u_min_flat = np.reshape(umin, (N * nu, ), order='F')
            u_max_flat = np.reshape(umax, (N * nu, ), order='F')
        else:
            raise ValueError('umin has wrong dimensions. Umin shape={}'.format(
                umin.shape))
        self.u_min_flat = u_min_flat
        self.u_max_flat = u_max_flat

        #! GET a & b
        # Write B:
        diag_AkB = Bd
        data_list = Bbd.data
        row_list = Bbd.row
        col_list = Bbd.col
        B = sparse.coo_matrix
        for i in range(N):
            if i < N - 1:
                AkB_bd_temp = block_diag(diag_AkB, N - i)
            else:
                AkB_bd_temp = diag_AkB.tocoo()
            data_list = np.hstack([data_list, AkB_bd_temp.data])
            row_list = np.hstack([
                row_list, AkB_bd_temp.row + np.full(
                    (AkB_bd_temp.row.shape[0], ), nx * i)
            ])
            col_list = np.hstack([col_list, AkB_bd_temp.col])

            diag_AkB = Ad.dot(diag_AkB)

        B = sparse.coo_matrix((data_list, (row_list, col_list)),
                              shape=(N * nx, N * nu))

        a = Ad.copy()
        Ak = Ad.copy()
        for i in range(N - 1):
            Ak = Ak.dot(Ad)
            a = sparse.vstack([a, Ak])

        self.a = a
        self.B = B

        check_ab = False
        if check_ab:
            x0 = np.linspace(-5, 40, nx)
            x00 = np.linspace(-5, 40, nx)
            # Store data Init
            nsim = N
            xst = np.zeros((nx, nsim))
            ust = np.zeros((nu, nsim))

            # Simulate in closed loop

            for i in range(nsim):
                # Fake pd controller
                ctrl = np.zeros(nu, )  #np.random.rand(nu,)
                x0 = Ad.dot(x0) + Bd.dot(ctrl)

                # Store Data
                xst[:, i] = x0
                ust[:, i] = ctrl

            x_dense = np.reshape(a @ x00 + B @ (ust.flatten('F')), (N, nx)).T

            plt.figure()
            plt.subplot(2, 1, 1)
            for i in range(nx):
                plt.plot(range(nsim), xst[i, :], 'd', label="sim " + str(i))
                plt.plot(range(nsim),
                         x_dense[i, :],
                         'd',
                         label="ax+bu " + str(i))
            plt.xlabel('Time(s)')
            plt.grid()
            plt.legend()

            plt.subplot(2, 1, 2)
            for i in range(nu):
                plt.plot(range(nsim), ust[i, :], label=str(i))
            plt.xlabel('Time(s)')
            plt.grid()
            plt.legend()
            plt.savefig("AB_check_for_" + name + ".pdf",
                        bbox_inches='tight',
                        format='pdf',
                        dpi=2400)
            plt.close()

        # Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))

        # Compute Block Diagonal elements
        self.Cbd = sparse.kron(sparse.eye(N), self.C)
        CQCbd = self.Cbd.T @ Qbd @ self.Cbd
        self.CtQ = self.C.T @ Q
        Cbd = self.Cbd

        P = Rbd + B.T @ CQCbd @ B

        self.BTQbda = B.T @ CQCbd @ a
        Aineq_x = Cbd @ B

        xrQB = B.T @ np.reshape(self.CtQ.dot(xr), (N * nx, ), order='F')
        l = np.hstack([x_min_flat - Cbd @ a @ x0, u_min_flat])
        u = np.hstack([x_max_flat - Cbd @ a @ x0, u_max_flat])

        x0aQb = self.BTQbda @ x0
        q = x0aQb - xrQB
        Aineq_u = sparse.eye(N * nu)
        A = sparse.vstack([Aineq_x, Aineq_u]).tocsc()

        if soft:
            Pdelta = sparse.kron(sparse.eye(N), D)
            P = sparse.block_diag([P, Pdelta])
            qdelta = np.zeros(N * ns)
            q = np.hstack([q, qdelta])
            Adelta = sparse.csc_matrix(
                np.vstack([np.eye(N * ns),
                           np.zeros((N * nu, N * ns))]))
            A = sparse.hstack([A, Adelta])

        plot_matrices = False
        if plot_matrices:
            #! Visualize Matrices
            fig = plt.figure()

            fig.suptitle(
                "QP Matrices to solve MP in dense form. N={}, nx={}, nu={}".
                format(N, nx, nu),
                fontsize=20)
            plt.subplot(2, 4, 1, xlabel="Ns*(N+1)", ylabel="Ns*(N+1)")
            plt.imshow(a.toarray(), interpolation='nearest', cmap=cm.Greys_r)
            plt.title("a in $x=ax_0+bu$")
            plt.subplot(2, 4, 2, xlabel="Ns*(N+1)", ylabel="Nu*N")
            plt.imshow(B.toarray(), interpolation='nearest', cmap=cm.Greys_r)
            plt.title("b in $x=ax_0+bu$")
            plt.subplot(2,
                        4,
                        3,
                        xlabel="ns*(N+1) + ns*(N+1) + nu*N",
                        ylabel="Ns*(N+1)+Nu*N")
            plt.imshow(A.toarray(), interpolation='nearest', cmap=cm.Greys_r)
            plt.title("A total in $l\\leq Ax \\geq u$")
            plt.subplot(2, 4, 4)
            plt.imshow(P.toarray(), interpolation='nearest', cmap=cm.Greys_r)
            plt.title("P in $J=u^TPu+q^Tu$")
            plt.subplot(2, 4, 5)
            plt.imshow(Qbd.toarray(), interpolation='nearest', cmap=cm.Greys_r)
            plt.title("Qbd")

            #! Visualize Vectors
            plt.subplot(2, 4, 6)
            plt.plot(l)
            plt.title('l in  $l\\leq Ax \\geq u$')
            plt.grid()
            plt.subplot(2, 4, 7)
            plt.plot(u)
            plt.title("l in  $l\\leq Ax \\geq u$")
            plt.grid()
            plt.subplot(2, 4, 8)
            plt.plot(q)
            plt.title("q in $J=u^TPu+q^Tu$")
            plt.grid()
            plt.tight_layout()
            plt.savefig("MPC_matrices_for_" + name + ".pdf",
                        bbox_inches='tight',
                        format='pdf',
                        dpi=2400)
            plt.close()
            #plt.show()

        # Create an OSQP object
        self.prob = osqp.OSQP()
        # Setup workspace

        self.prob.setup(P=P.tocsc(),
                        q=q,
                        A=A,
                        l=l,
                        u=u,
                        warm_start=True,
                        verbose=False)

        if self.plotMPC:
            # Figure to plot MPC thoughts
            self.fig, self.axs = plt.subplots(self.ns + self.nu)
            if nx == 4:
                ylabels = [
                    '$x$', '$\\theta$', '$\\dot{x}$', '$\\dot{\\theta}$'
                ]
            else:
                ylabels = [str(i) for i in range(nx)]

            for ii in range(self.ns):
                self.axs[ii].set(xlabel='Time(s)', ylabel=ylabels[ii])
                self.axs[ii].grid()
            for ii in range(self.ns, self.ns + self.nu):
                self.axs[ii].set(xlabel='Time(s)', ylabel='u')
                self.axs[ii].grid()
    def __init__(self,
                 dynamics,
                 N,
                 dt,
                 umin,
                 umax,
                 xmin,
                 xmax,
                 Q,
                 R,
                 QN,
                 xr,
                 solver_settings,
                 const_offset=None,
                 terminal_constraint=False,
                 add_slack=False,
                 q_slack=1e3):
        """
        Initialize the nonlinear mpc class.
        :param dynamics: (AffindeDynamics) dynamics object describing system dynamics
        :param N: (int) Prediction horizon in number of timesteps
        :param dt: (float) Time interval between time steps
        :param umin: (np.array) Actuation lower bounds
        :param umax: (np.array) Actuation upper bounds
        :param xmin: (np.array) State lower bounds
        :param xmax: (np.array) State upper bounds
        :param Q: (sparse.csc_matrix) State deviation penalty matrix
        :param R: (sparse.csc_matrix) Actuation penalty matrix
        :param QN: (sparse.csc_matrix) Terminal state deviation penalty matrix
        :param xr: (np.array) Desired state, setpoint
        :param const_offset: (np.array) Constant offset of the control inputs
        :param terminal_constraint: (boolean) Constrain terminal state to be xr
        :param add_slack: (boolean) Add slack variables to state constraints
        :param q_slack: (float) Penalty value of slack terms q||s||^2, where s are the slack variables
        """

        Controller.__init__(self, dynamics)

        self.dynamics_object = dynamics
        self.nx = self.dynamics_object.n
        self.nu = self.dynamics_object.m
        self.dt = dt
        if type(self.dynamics_object) == BilinearLiftedDynamics:
            self.C = self.dynamics_object.C
        else:
            self.C = np.eye(self.nx)
            self.dynamics_object.lift = lambda x, t: x

        self.Q = Q
        self.QN = QN
        self.R = R
        self.N = N
        self.xmin = xmin
        self.xmax = xmax
        self.umin = umin
        self.umax = umax

        if const_offset is None:
            self.const_offset = np.zeros((self.nu, 1))
        else:
            self.const_offset = const_offset

        assert xr.ndim == 1, 'Desired trajectory not supported'
        self.xr = xr
        self.ns = xr.shape[0]
        self.terminal_constraint = terminal_constraint

        self.add_slack = add_slack
        self.Q_slack = q_slack * sparse.eye(self.ns * (self.N))

        self.solver_settings = solver_settings
        self.embed_pkg_str = 'nmpc_' + str(self.nx) + '_' + str(
            self.nu) + '_' + str(self.N)

        self.prep_time = []
        self.qp_time = []
        self.comp_time = []
        self.x_iter = []
        self.u_iter = []