示例#1
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
 def _translated_domains(self):
     x_eq, u_eq = self.equilibrium_point()
     translated_domains = []
     for domain in self._domains():
         translated_domain = Polytope(domain.A, domain.b - domain.A.dot(np.vstack((x_eq, u_eq))))
         translated_domain.assemble()
         translated_domains.append(translated_domain)
     return translated_domains
示例#2
0
def extrude_2d_polytope(p_2d, z_limits):
    A = np.vstack((np.hstack((p_2d.lhs_min, np.zeros(
        (p_2d.lhs_min.shape[0], 1)))),
                   np.hstack((np.zeros(
                       (2, p_2d.lhs_min.shape[1])), np.array([[1.], [-1.]])))))
    b = np.vstack((p_2d.rhs_min, np.array([[z_limits[1]], [-z_limits[0]]])))
    p_3d = Polytope(A, b)
    p_3d.assemble()
    return p_3d
示例#3
0
    def polytope(self, qp):
        """
        Stores a polytope that describes the critical region in the parameter space.
        """

        # multipliers explicit solution
        [G_A, W_A, S_A] = [
            qp.G[self.active_set, :], qp.W[self.active_set, :],
            qp.S[self.active_set, :]
        ]
        [G_I, W_I, S_I] = [
            qp.G[self.inactive_set, :], qp.W[self.inactive_set, :],
            qp.S[self.inactive_set, :]
        ]
        H_A = np.linalg.inv(G_A.dot(qp.H_inv.dot(G_A.T)))
        self.lambda_A_offset = -H_A.dot(W_A)
        self.lambda_A_linear = -H_A.dot(S_A)

        # primal variables explicit solution
        self.z_offset = -qp.H_inv.dot(G_A.T.dot(self.lambda_A_offset))
        self.z_linear = -qp.H_inv.dot(G_A.T.dot(self.lambda_A_linear))

        # optimal value function explicit solution: V_star = .5 x' V_quadratic x + V_linear x + V_offset
        self.V_quadratic = self.z_linear.T.dot(qp.H).dot(
            self.z_linear) + qp.F_xx_q
        self.V_linear = self.z_offset.T.dot(qp.H).dot(
            self.z_linear) + qp.F_x_q.T
        self.V_offset = qp.F_q + .5 * self.z_offset.T.dot(qp.H).dot(
            self.z_offset)

        # primal original variables explicit solution
        self.u_offset = self.z_offset - qp.H_inv.dot(qp.F_u)
        self.u_linear = self.z_linear - qp.H_inv.dot(qp.F_xu.T)

        # equation (12) (modified: only inactive indices considered)
        lhs_type_1 = G_I.dot(self.z_linear) - S_I
        rhs_type_1 = -G_I.dot(self.z_offset) + W_I

        # equation (13)
        lhs_type_2 = -self.lambda_A_linear
        rhs_type_2 = self.lambda_A_offset

        # gather facets of type 1 and 2 to define the polytope (note the order: the ith facet of the cr is generated by the ith constraint)
        lhs = np.zeros((self.n_constraints, self.n_parameters))
        rhs = np.zeros((self.n_constraints, 1))
        lhs[self.inactive_set + self.active_set, :] = np.vstack(
            (lhs_type_1, lhs_type_2))
        rhs[self.inactive_set + self.active_set] = np.vstack(
            (rhs_type_1, rhs_type_2))

        # construct polytope
        self.polytope = Polytope(lhs, rhs)
        self.polytope.assemble()

        return
示例#4
0
    def test_DTLinearSystem(self):

        # constinuous time double integrator
        A = np.array([[0., 1.], [0., 0.]])
        B = np.array([[0.], [1.]])
        t_s = 1.

        # discrete time from continuous
        sys = DTLinearSystem.from_continuous(A, B, t_s, 'zoh')
        A_discrete = np.eye(2) + A * t_s
        B_discrete = B * t_s + np.array([[0., t_s**2 / 2.], [0., 0.]]).dot(B)
        self.assertTrue(all(np.isclose(sys.A.flatten(), A_discrete.flatten())))
        self.assertTrue(all(np.isclose(sys.B.flatten(), B_discrete.flatten())))

        # simulation forced dynamics
        x0 = np.array([[0.], [1.]])
        N = 10
        u = np.array([[1.]])
        u_list = [u] * N
        x_list = sys.simulate(x0, u_list)
        real_x_list = [[
            x0[0] + x0[1] * i * t_s + u[0, 0] * (i * t_s)**2 / 2.,
            x0[1] + u * i * t_s
        ] for i in range(0, N + 1)]
        self.assertTrue(all(np.isclose(x_list, real_x_list).flatten()))

        # test maximum output admissible set
        Q = np.eye(2)
        R = np.eye(1)
        P, K = dare(sys.A, sys.B, Q, R)
        x_max = np.ones((2, 1))
        x_min = -x_max
        X = Polytope.from_bounds(x_min, x_max)
        X.assemble()
        u_max = np.ones((1, 1))
        u_min = -u_max
        U = Polytope.from_bounds(u_min, u_max)
        U.assemble()
        moas = moas_closed_loop_from_orthogonal_domains(A, B, K, X, U)
        sys_cl = DTLinearSystem(A + B.dot(K), np.zeros((2, 1)))
        v0_max = max([v[0] for v in moas.vertices])
        v1_max = max([v[1] for v in moas.vertices])
        v0_min = min([v[0] for v in moas.vertices])
        v1_min = min([v[1] for v in moas.vertices])
        for x0 in list(np.linspace(v0_min, v0_max, 100)):
            for x1 in list(np.linspace(v1_min, v1_max, 100)):
                x_init = np.array([[x0], [x1]])
                if moas.applies_to(x_init):
                    u = np.zeros((1, 1))
                    u_list = [u] * N
                    x_list = sys_cl.simulate(x_init, u_list)
                    for x in x_list:
                        self.assertTrue(moas.applies_to(x))
示例#5
0
    def test_DTPWASystem(self):

        # PWA dynamics
        t_s = .1
        A_1 = np.array([[0., 1.], [10., 0.]])
        B_1 = np.array([[0.], [1.]])
        c_1 = np.array([[0.], [0.]])
        sys_1 = DTAffineSystem.from_continuous(A_1, B_1, c_1, t_s, 'zoh')
        A_2 = np.array([[0., 1.], [-100., 0.]])
        B_2 = B_1
        c_2 = np.array([[0.], [10.]])
        sys_2 = DTAffineSystem.from_continuous(A_2, B_2, c_2, t_s, 'zoh')
        sys = [sys_1, sys_2]

        # PWA state domains
        x_max_1 = np.array([[.1], [1.5]])
        x_max_2 = np.array([[.2], [x_max_1[1, 0]]])
        x_min_1 = -x_max_2
        x_min_2 = np.array([[x_max_1[0, 0]], [x_min_1[1, 0]]])
        X_1 = Polytope.from_bounds(x_min_1, x_max_1)
        X_1.assemble()
        X_2 = Polytope.from_bounds(x_min_2, x_max_2)
        X_2.assemble()
        X = [X_1, X_2]

        # PWA input domains
        u_max = np.array([[4.]])
        u_min = -u_max
        U_1 = Polytope.from_bounds(u_min, u_max)
        U_1.assemble()
        U_2 = U_1
        U = [U_1, U_2]
        sys = DTPWASystem.from_orthogonal_domains(sys, X, U)

        # simualate
        N = 10
        x_0 = np.array([[.0], [.5]])
        u_list = [np.ones((1, 1)) for i in range(N)]
        x_sim_1, switching_sequence = sys.simulate(x_0, u_list)
        x_sim_1 = np.vstack(x_sim_1)

        # condense for the simulated switching sequence
        A_bar, B_bar, c_bar = sys.condense(switching_sequence)
        x_sim_2 = A_bar.dot(x_0) + B_bar.dot(np.vstack(u_list)) + c_bar

        # compare the 2 simulations
        self.assertTrue(all(np.isclose(x_sim_1.flatten(), x_sim_2.flatten())))
示例#6
0
    def test_MPCController(self):

        # double integrator
        A = np.array([[0., 1.], [0., 0.]])
        B = np.array([[0.], [1.]])
        t_s = 1.
        sys = ds.DTLinearSystem.from_continuous(A, B, t_s)

        # mpc controller
        N = 5
        Q = np.eye(A.shape[0])
        R = np.eye(B.shape[1])
        objective_norm = 'two'
        P, K = ds.dare(sys.A, sys.B, Q, R)
        u_max = np.array([[1.]])
        u_min = -u_max
        U = Polytope.from_bounds(u_min, u_max)
        U.assemble()
        x_max = np.array([[1.], [1.]])
        x_min = -x_max
        X = Polytope.from_bounds(x_min, x_max)
        X.assemble()
        X_N = ds.moas_closed_loop_from_orthogonal_domains(
            sys.A, sys.B, K, X, U)
        controller = MPCController(sys, N, objective_norm, Q, R, P, X, U, X_N)

        # explicit vs implicit solution
        controller.get_explicit_solution()
        n_test = 100
        for i in range(n_test):
            x0 = np.random.rand(2, 1)
            u_explicit, V_explicit = controller.feedforward_explicit(x0)
            u_implicit, V_implicit = controller.feedforward(x0)
            u_explicit = np.vstack(u_explicit)
            u_implicit = np.vstack(u_implicit)
            if any(np.isnan(u_explicit)) or any(np.isnan(u_implicit)):
                self.assertTrue(all(np.isnan(u_explicit)))
                self.assertTrue(all(np.isnan(u_implicit)))
                self.assertTrue(np.isnan(V_explicit))
                self.assertTrue(np.isnan(V_implicit))
                # self.assertFalse(controller.condensed_program.feasible_set.applies_to(x0))
            else:
                self.assertTrue(
                    all(
                        np.isclose(u_explicit, u_implicit,
                                   rtol=1.e-4).flatten()))
                self.assertTrue(np.isclose(V_explicit, V_implicit, rtol=1.e-4))
示例#7
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
class BoxAtlasKinematicLimits(object):
    def __init__(self):
        # position bounds
        # body
        self.q_b_min = np.array([[0.3],[0.3]])
        self.q_b_max = np.array([[0.7],[0.7]])

        # left foot (limits in the body frame)
        self.q_lf_min = np.array([[0.],[-.7]])
        self.q_lf_max = np.array([[.4],[-.3]])

        # right foot (limits in the body frame)
        self.q_rf_min = np.array([[-.4],[-.7]])
        self.q_rf_max = np.array([[0.],[-.3]])

        # hand (limits in the body frame)
        self.q_h_min = np.array([[-.6],[-.1]])
        self.q_h_max = np.array([[-.2],[.3]])

        # velocity bounds

        # body
        self.v_b_max = 5 * np.ones((2,1))
        self.v_b_min = - self.v_b_max

        self.polytope = None  # will be generated in self._assemble()

    def _assemble(self):
        selection_matrix = np.vstack((np.eye(2), -np.eye(2)))

        # left foot
        lhs = np.hstack((-selection_matrix, selection_matrix, np.zeros((4,6))))
        rhs = np.vstack((self.q_lf_max, -self.q_lf_min))
        self.polytope = Polytope(lhs, rhs)

        # right foot
        lhs = np.hstack((-selection_matrix, np.zeros((4,2)), selection_matrix, np.zeros((4,4))))
        rhs = np.vstack((self.q_rf_max, -self.q_rf_min))
        self.polytope.add_facets(lhs, rhs)

        # hand
        lhs = np.hstack((-selection_matrix, np.zeros((4,4)), selection_matrix, np.zeros((4,2))))
        rhs = np.vstack((self.q_h_max, -self.q_h_min))
        self.polytope.add_facets(lhs, rhs)

        # body
        self.polytope.add_bounds(self.q_b_min, self.q_b_max, [0,1])
        self.polytope.add_bounds(self.v_b_min, self.v_b_max, [8,9])

        self.polytope.assemble()
        return self.polytope
示例#8
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
    def random_state(self, X=None, controller=None):
        """
        Sample a random state within the lower and upper bounds of the piecewise
        affine system, and further restrict that state to some polytope X. By
        default, X is the polytope defined by the robot's kinematic limits.

        If `controller` is not None, use the given controller to check if there
        is a feasible input sequence from the given sample before returning it
        """
        if X is None:
            A = self.kinematic_limits.polytope.A
            b = self.kinematic_limits.polytope.b
            x_eq, u_eq = self.equilibrium_point()
            X = Polytope(A, b - A.dot(x_eq)).assemble()

        while True:
            x = np.random.rand(self.pwa_system.n_x, 1)
            x = np.multiply(x, (self.pwa_system.x_max - self.pwa_system.x_min)) + self.pwa_system.x_min
            if X.applies_to(x):
                if controller is not None:
                    u, xtraj, ss, cost = controller.feedforward(x)
                    if np.any(np.isnan(u[0])):
                        continue
                return x
示例#9
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
    def _assemble(self):
        selection_matrix = np.vstack((np.eye(2), -np.eye(2)))

        # left foot
        lhs = np.hstack((-selection_matrix, selection_matrix, np.zeros((4,6))))
        rhs = np.vstack((self.q_lf_max, -self.q_lf_min))
        self.polytope = Polytope(lhs, rhs)

        # right foot
        lhs = np.hstack((-selection_matrix, np.zeros((4,2)), selection_matrix, np.zeros((4,4))))
        rhs = np.vstack((self.q_rf_max, -self.q_rf_min))
        self.polytope.add_facets(lhs, rhs)

        # hand
        lhs = np.hstack((-selection_matrix, np.zeros((4,4)), selection_matrix, np.zeros((4,2))))
        rhs = np.vstack((self.q_h_max, -self.q_h_min))
        self.polytope.add_facets(lhs, rhs)

        # body
        self.polytope.add_bounds(self.q_b_min, self.q_b_max, [0,1])
        self.polytope.add_bounds(self.v_b_min, self.v_b_max, [8,9])

        self.polytope.assemble()
        return self.polytope
示例#10
0
 def _state_constraints(self):
     n = len(self.moving_limbs)
     selection_matrix = np.vstack((np.eye(2), -np.eye(2)))
     X = Polytope(np.zeros((0, 2 * (n + 2))), np.zeros((0, 1)))
     for i, limb in enumerate(self.moving_limbs):
         lhs = np.hstack((np.zeros(
             (4, i * 2)), selection_matrix, np.zeros(
                 (4, (n - 1 - i) * 2)), -selection_matrix, np.zeros(
                     (4, 2))))
         rhs = np.vstack((self.kinematic_limits[limb]['max'],
                          -self.kinematic_limits[limb]['min']))
         X.add_facets(lhs, rhs)
     for limb in self.fixed_limbs:
         q_b_min = self.nominal_limb_positions[
             limb] - self.kinematic_limits[limb]['max']
         q_b_max = self.nominal_limb_positions[
             limb] - self.kinematic_limits[limb]['min']
         X.add_bounds(q_b_min, q_b_max, [2 * n, 2 * n + 1])
     X.add_bounds(self.kinematic_limits['b']['min'],
                  self.kinematic_limits['b']['max'], [2 * n, 2 * n + 1])
     X.add_bounds(self.velocity_limits['b']['min'],
                  self.velocity_limits['b']['max'], [2 * n + 2, 2 * n + 3])
     # X = Polytope(X.A, X.b - X.A.dot(self.x_eq))
     return X
示例#11
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
    def controller(self,
                   N=10,
                   Q=10 * np.eye(10),
                   R=np.eye(9),
                   objective_norm="two",
                   X_N=Polytope.from_bounds(-np.ones((10,1)), np.ones((10,1)))):
        # terminal set and cost
        # terminal_mode = 4
        #P, K = dare(translated_affine_systems[terminal_mode].A, translated_affine_systems[terminal_mode].B, Q, R)
        #X_N = ds.moas_closed_loop(translated_affine_systems[terminal_mode].A, translated_affine_systems[terminal_mode].B, K, X[1], U[1])
        P = Q
        if not X_N.assembled:
            X_N.assemble()

        # hybrid controller
        return MPCHybridController(self.pwa_system, N, objective_norm, Q, R, P, X_N)
示例#12
0
    def test_orthogonal_projection(self):

        # test plane generator
        points = [
            np.array([[1.], [0.], [0.]]),
            np.array([[-1.], [0.], [0.]]),
            np.array([[0.], [1.], [0.]])
        ]
        a, b = plane_through_points(points)
        self.assertTrue(np.allclose(a, np.array([[0.], [0.], [1.]])))
        self.assertTrue(np.allclose(b, np.array([[0.]])))
        points = [
            np.array([[1.], [0.], [0.]]),
            np.array([[0.], [1.], [0.]]),
            np.array([[0.], [0.], [1.]])
        ]
        a, b = plane_through_points(points)
        real_a = np.ones((3, 1)) / np.sqrt(3)
        self.assertTrue(np.allclose(a, real_a))
        self.assertTrue(np.isclose(b[0, 0], real_a[0, 0]))

        # test CHM
        n_test = 100
        n_ineq = 20
        n_var = 5
        res = [1, 3, 4]
        for i in range(n_test):
            everything_ok = False
            while not everything_ok:
                A = np.random.randn(n_ineq, n_var)
                b = np.random.rand(n_ineq, 1)
                x_offeset = np.random.rand(n_var, 1)
                b += A.dot(x_offeset)
                p = Polytope(A, b)
                try:
                    p.assemble()
                    everything_ok = True
                except ValueError:
                    pass
            p_proj_ve = p.orthogonal_projection(res, 'vertex_enumeration')
            for method in ['convex_hull']:  #, 'block_elimination']:
                p_proj = p.orthogonal_projection(res, method)
                self.assertTrue(
                    p_proj.lhs_min.shape[0] == p_proj_ve.lhs_min.shape[0])
                # note that sometimes qhull gives the same vertex twice!
                for v in p_proj.vertices:
                    self.assertTrue(
                        any([
                            np.allclose(v, v_ve) for v_ve in p_proj_ve.vertices
                        ]))
                for v_ve in p_proj_ve.vertices:
                    self.assertTrue(
                        any([np.allclose(v, v_ve) for v in p_proj.vertices]))
示例#13
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
    def _assemble(self, kinematic_limits, friction_coefficient, stiffness):
        # force bounds

        # left foot
        f_lf_x_max = - friction_coefficient * stiffness * (kinematic_limits.q_b_min[1,0]+kinematic_limits.q_lf_min[1,0])
        f_lf_x_min = - f_lf_x_max

        # right foot
        f_rf_x_max = - friction_coefficient * stiffness * (kinematic_limits.q_b_min[1,0]+kinematic_limits.q_rf_min[1,0])
        f_rf_x_min = - f_rf_x_max

        # hand
        f_h_y_max = - friction_coefficient * stiffness * (kinematic_limits.q_b_min[0,0]+kinematic_limits.q_h_min[0,0])
        f_h_y_min = - f_h_y_max

        input_max = np.vstack((self.v_lf_max, self.v_rf_max, self.v_h_max, f_lf_x_max, f_rf_x_max, f_h_y_max))
        input_min = np.vstack((self.v_lf_min, self.v_rf_min, self.v_h_min, f_lf_x_min, f_rf_x_min, f_h_y_min))
        self.polytope = Polytope.from_bounds(input_min, input_max).assemble()
        return self.polytope
示例#14
0
文件: plot.py 项目: Ujkwon/py-mpc
def state_partition(critical_regions,
                    feasible_set,
                    active_set=False,
                    facet_index=False,
                    **kwargs):
    if critical_regions is None:
        raise ValueError(
            'Explicit solution not computed yet! First run .compute_explicit_solution().'
        )

    fig, ax = plt.subplots()
    for cr in critical_regions:
        p = Polytope(cr.polytope.lhs_min, cr.polytope.rhs_min)
        p.add_facets(feasible_set.lhs_min, feasible_set.rhs_min)
        p.assemble()
        try:
            pass
            p.plot(facecolor=np.random.rand(3), **kwargs)
        except AttributeError:
            pass
        ax.autoscale_view()
    return
示例#15
0
文件: boxatlas.py 项目: Ujkwon/py-mpc
 def _mode_independent_constraints(self, kinematic_limits, input_limits):
     kinematic_limits._assemble()
     input_limits._assemble(kinematic_limits, self.friction_coefficient, self.stiffness)
     lhs = np.vstack((
         np.hstack((
             kinematic_limits.polytope.A,
             np.zeros((
                 kinematic_limits.polytope.A.shape[0],
                 input_limits.polytope.A.shape[1]
                 ))
             )),
         np.hstack((
             np.zeros((
                 input_limits.polytope.A.shape[0],
                 kinematic_limits.polytope.A.shape[1]
                 )),
             input_limits.polytope.A
             ))
         ))
     rhs = np.vstack((kinematic_limits.polytope.b, input_limits.polytope.b))
     return Polytope(lhs, rhs)
示例#16
0
 def _contact_mode_constraints(self, contact_mode, X, U):
     n = len(self.moving_limbs)
     X_mode = copy(X)
     # force the limbs to stay inside the polyhedra
     for i, limb in enumerate(self.moving_limbs):
         moving_limb = self.topology['moving'][limb][contact_mode[limb]]
         A = moving_limb.A
         b = moving_limb.b
         lhs = np.hstack((np.zeros(
             (A.shape[0], i * 2)), A, np.zeros(
                 (A.shape[0], 2 * (n - i) + 2))))
         X_mode.add_facets(lhs, b)
     # gather state and input constraints
     lhs = linalg.block_diag(*[X_mode.A, U.A])
     rhs = np.vstack((X_mode.b, U.b))
     D = Polytope(lhs, rhs)
     # friction constraints
     mu = self.parameters['friction_coefficient']
     k = self.parameters['stiffness']
     c = self.parameters['damping']
     for i, limb in enumerate(self.moving_limbs):
         moving_limb = self.topology['moving'][limb][contact_mode[limb]]
         if moving_limb.contact_surface is not None:
             a = moving_limb.A[moving_limb.contact_surface, :]
             b = moving_limb.b[moving_limb.contact_surface, 0]
             lhs = np.zeros((2, self.n_x + self.n_u))
             lhs[:, i * 2:(i + 1) *
                 2] = mu * k * np.array([[a[0], a[1]], [a[0], a[1]]])
             lhs[:, self.n_x + i * 2:self.n_x +
                 (i + 1) * 2] = c * np.array([[-a[1], a[0]], [a[1], -a[0]]])
             rhs = mu * k * b * np.ones((2, 1))
             D.add_facets(lhs, rhs)
     xu_eq = np.vstack((self.x_eq, self.u_eq))
     D = Polytope(D.A, D.b - D.A.dot(xu_eq))
     D.assemble()
     return D
示例#17
0
 def _input_constraints(self):
     # bounds
     u_min = np.zeros((0, 1))
     u_max = np.zeros((0, 1))
     for limb in self.moving_limbs:
         u_min = np.vstack((u_min, self.velocity_limits[limb]['min']))
         u_max = np.vstack((u_max, self.velocity_limits[limb]['max']))
     for limb in self.fixed_limbs:
         u_min = np.vstack((u_min, self.force_limits[limb]['min']))
         u_max = np.vstack((u_max, self.force_limits[limb]['max']))
     U = Polytope.from_bounds(u_min, u_max)
     # friction limits
     n_moving = len(self.moving_limbs)
     n_fixed = len(self.fixed_limbs)
     lhs_friction = np.array(
         [[-self.parameters['friction_coefficient'], 1.],
          [-self.parameters['friction_coefficient'], -1.]])
     lhs = np.hstack((np.zeros((n_fixed * 2, n_moving * 2)),
                      linalg.block_diag(*[lhs_friction] * n_fixed)))
     rhs = np.zeros((n_fixed * 2, 1))
     U.add_facets(lhs, rhs)
     # U = Polytope(U.A, U.b - U.A.dot(self.u_eq))
     return U
示例#18
0
    def test_Polytope(self):

        # empty
        A = np.array([[1., 0.], [-1., 0.], [0., 1.]])
        b = np.array([[1.], [-2.], [0.]])
        p = Polytope(A, b)
        p.assemble()
        self.assertTrue(p.empty)

        # unbounded (easy)
        A = np.array([[1., 1.], [1., -1.]])
        b = np.array([[0.], [0.]])
        p = Polytope(A, b)
        with self.assertRaises(ValueError):
            p.assemble()

        # unbounded (difficult)
        A = np.array([[1., 0.], [-1., 0.], [0., 1.]])
        b = np.array([[1.], [1.], [0.]])
        p = Polytope(A, b)
        with self.assertRaises(ValueError):
            p.assemble()

        # bounded and not empty
        A = np.array([[1., 0.], [-1., 0.], [0., 1.], [0., -1.]])
        b = np.array([[1.], [1.], [1.], [1.]])
        p = Polytope(A, b)
        p.assemble()
        self.assertFalse(p.empty)
        self.assertTrue(p.bounded)

        # coincident facets
        A = np.array([[1., 0.], [1. - 1.e-10, 1.e-10], [-1., 0.], [0., 1.],
                      [0., -1.]])
        b = np.ones((5, 1))
        p = Polytope(A, b)
        p.assemble()
        true_coincident_facets = [[0, 1], [0, 1], [2], [3], [4]]
        self.assertEqual(p.coincident_facets, true_coincident_facets)
        self.assertTrue(p.minimal_facets[0] in [0, 1])
        self.assertEqual(p.minimal_facets[1:], [2, 3, 4])

        # coincident facets, minimal facets, points on facets
        A = np.array([[1., 1.], [-1., 1.], [0., -1.], [0., 1.], [0., 1.],
                      [1. - 1.e-10, 1. - 1.e-10]])
        b = np.array([[1.], [1.], [1.], [1.], [2.], [1.]])
        p = Polytope(A, b)
        p.assemble()
        true_coincident_facets = [[0, 5], [1], [2], [3], [4], [0, 5]]
        true_minimal_facets = [1, 2, 5]
        true_facet_centers = [[[-1.], [0.]], [[0.], [-1.]], [[1.], [0.]]]
        self.assertEqual(p.coincident_facets, true_coincident_facets)
        self.assertEqual(true_minimal_facets, p.minimal_facets)
        for i in range(0, len(true_facet_centers)):
            self.assertTrue(
                all(np.isclose(true_facet_centers[i], p.facet_centers(i))))

        # from_ and add_ methods
        x_max = np.ones((2, 1))
        x_min = -x_max
        p = Polytope.from_bounds(x_min, x_max)
        p.add_bounds(x_min * 2., x_max / 2.)
        A = np.array([[-1., 0.], [0., -1.]])
        b = np.array([[.2], [2.]])
        p.add_facets(A, b)
        p.assemble()
        self.assertFalse(p.empty)
        self.assertTrue(p.bounded)
        true_vertices = [
            np.array([[.5], [.5]]),
            np.array([[-.2], [.5]]),
            np.array([[-.2], [-1.]]),
            np.array([[.5], [-1.]])
        ]
        self.assertTrue(
            all([
                any(np.all(np.isclose(vertex, true_vertices), axis=1))
                for vertex in p.vertices
            ]))

        # intersection and inclusion
        x_max = np.ones((2, 1))
        x_min = -x_max
        p1 = Polytope.from_bounds(x_min, x_max)
        p1.assemble()
        x_max = np.ones((2, 1)) * 2.
        x_min = -x_max
        p2 = Polytope.from_bounds(x_min, x_max)
        p2.assemble()
        x_min = np.zeros((2, 1))
        p3 = Polytope.from_bounds(x_min, x_max)
        p3.assemble()
        x_max = np.ones((2, 1)) * 5.
        x_min = np.ones((2, 1)) * 4.
        p4 = Polytope.from_bounds(x_min, x_max)
        p4.assemble()

        # intersection
        self.assertTrue(p1.intersect_with(p2))
        self.assertTrue(p1.intersect_with(p3))
        self.assertFalse(p1.intersect_with(p4))

        # inclusion
        self.assertTrue(p1.included_in(p2))
        self.assertFalse(p1.included_in(p3))
        self.assertFalse(p1.included_in(p4))
示例#19
0
class CriticalRegion:
    """
    Implements the algorithm from Tondel et al. "An algorithm for multi-parametric quadratic programming and explicit MPC solutions"

    VARIABLES:
        n_constraints: number of contraints in the qp
        n_parameters: number of parameters of the qp
        active_set: active set inside the critical region
        inactive_set: list of indices of non active contraints inside the critical region
        polytope: polytope describing the ceritical region in the parameter space
        weakly_active_constraints: list of indices of constraints that are weakly active iside the entire critical region
        candidate_active_sets: list of lists of active sets, its ith element collects the set of all the possible
            active sets that can be found crossing the ith minimal facet of the polyhedron
        z_linear: linear term in the piecewise affine primal solution z_opt = z_linear*x + z_offset
        z_offset: offset term in the piecewise affine primal solution z_opt = z_linear*x + z_offset
        u_linear: linear term in the piecewise affine primal solution u_opt = u_linear*x + u_offset
        u_offset: offset term in the piecewise affine primal solution u_opt = u_linear*x + u_offset
        lambda_A_linear: linear term in the piecewise affine dual solution (only active multipliers) lambda_A = lambda_A_linear*x + lambda_A_offset
        lambda_A_offset: offset term in the piecewise affine dual solution (only active multipliers) lambda_A = lambda_A_linear*x + lambda_A_offset
    """
    def __init__(self, active_set, qp):

        # store active set
        print 'Computing critical region for the active set ' + str(active_set)
        [self.n_constraints, self.n_parameters] = qp.S.shape
        self.active_set = active_set
        self.inactive_set = sorted(
            list(set(range(self.n_constraints)) - set(active_set)))

        # find the polytope
        self.polytope(qp)
        if self.polytope.empty:
            return

        # find candidate active sets for the neighboiring regions
        minimal_coincident_facets = [
            self.polytope.coincident_facets[i]
            for i in self.polytope.minimal_facets
        ]
        self.candidate_active_sets = self.candidate_active_sets(
            active_set, minimal_coincident_facets)

        # find weakly active constraints
        self.find_weakly_active_constraints()

        # expand the candidates if there are weakly active constraints
        if self.weakly_active_constraints:
            self.candidate_active_set = self.expand_candidate_active_sets(
                self.candidate_active_set, self.weakly_active_constraints)

        return

    def polytope(self, qp):
        """
        Stores a polytope that describes the critical region in the parameter space.
        """

        # multipliers explicit solution
        [G_A, W_A, S_A] = [
            qp.G[self.active_set, :], qp.W[self.active_set, :],
            qp.S[self.active_set, :]
        ]
        [G_I, W_I, S_I] = [
            qp.G[self.inactive_set, :], qp.W[self.inactive_set, :],
            qp.S[self.inactive_set, :]
        ]
        H_A = np.linalg.inv(G_A.dot(qp.H_inv.dot(G_A.T)))
        self.lambda_A_offset = -H_A.dot(W_A)
        self.lambda_A_linear = -H_A.dot(S_A)

        # primal variables explicit solution
        self.z_offset = -qp.H_inv.dot(G_A.T.dot(self.lambda_A_offset))
        self.z_linear = -qp.H_inv.dot(G_A.T.dot(self.lambda_A_linear))

        # optimal value function explicit solution: V_star = .5 x' V_quadratic x + V_linear x + V_offset
        self.V_quadratic = self.z_linear.T.dot(qp.H).dot(
            self.z_linear) + qp.F_xx_q
        self.V_linear = self.z_offset.T.dot(qp.H).dot(
            self.z_linear) + qp.F_x_q.T
        self.V_offset = qp.F_q + .5 * self.z_offset.T.dot(qp.H).dot(
            self.z_offset)

        # primal original variables explicit solution
        self.u_offset = self.z_offset - qp.H_inv.dot(qp.F_u)
        self.u_linear = self.z_linear - qp.H_inv.dot(qp.F_xu.T)

        # equation (12) (modified: only inactive indices considered)
        lhs_type_1 = G_I.dot(self.z_linear) - S_I
        rhs_type_1 = -G_I.dot(self.z_offset) + W_I

        # equation (13)
        lhs_type_2 = -self.lambda_A_linear
        rhs_type_2 = self.lambda_A_offset

        # gather facets of type 1 and 2 to define the polytope (note the order: the ith facet of the cr is generated by the ith constraint)
        lhs = np.zeros((self.n_constraints, self.n_parameters))
        rhs = np.zeros((self.n_constraints, 1))
        lhs[self.inactive_set + self.active_set, :] = np.vstack(
            (lhs_type_1, lhs_type_2))
        rhs[self.inactive_set + self.active_set] = np.vstack(
            (rhs_type_1, rhs_type_2))

        # construct polytope
        self.polytope = Polytope(lhs, rhs)
        self.polytope.assemble()

        return

    def find_weakly_active_constraints(self, toll=1e-8):
        """
        Stores the list of constraints that are weakly active in the whole critical region
        enumerated in the as in the equation G z <= W + S x ("original enumeration")
        (by convention weakly active constraints are included among the active set,
        so that only constraints of type 2 are anlyzed)
        """

        # equation (13), again...
        lhs_type_2 = -self.lambda_A_linear
        rhs_type_2 = self.lambda_A_offset

        # weakly active constraints are included in the active set
        self.weakly_active_constraints = []
        for i in range(0, len(self.active_set)):

            # to be weakly active in the whole region they can only be in the form 0^T x <= 0
            if np.linalg.norm(lhs_type_2[i, :]) + np.absolute(
                    rhs_type_2[i, :]) < toll:
                print('Weakly active constraint detected!')
                self.weakly_active_constraints.append(self.active_set[i])

        return

    @staticmethod
    def candidate_active_sets(active_set, minimal_coincident_facets):
        """
        Computes one candidate active set for each non-redundant facet of a critical region
        (Theorem 2 and Corollary 1).

        INPUTS:
        active_set: active set of the parent critical region
        minimal_coincident_facets: list of facets coincident to the minimal facets
            (i.e.: [coincident_facets[i] for i in minimal_facets])

        OUTPUTS:
            candidate_active_sets: list of the candidate active sets for each minimal facet
        """

        # initialize list of condidate active sets
        candidate_active_sets = []

        # cross each non-redundant facet of the parent CR
        for coincident_facets in minimal_coincident_facets:

            # add or remove each constraint crossed to the active set of the parent CR
            candidate_active_set = set(active_set).symmetric_difference(
                set(coincident_facets))
            candidate_active_sets.append([sorted(list(candidate_active_set))])

        return candidate_active_sets

    @staticmethod
    def expand_candidate_active_sets(candidate_active_sets,
                                     weakly_active_constraints):
        """
        Expands the candidate active sets if there are some weakly active contraints (Theorem 5).

        INPUTS:
            candidate_active_sets: list of the candidate active sets for each minimal facet
            weakly_active_constraints: list of weakly active constraints (in the "original enumeration")

        OUTPUTS:
            candidate_active_sets: list of the candidate active sets for each minimal facet
        """

        # determine every possible combination of the weakly active contraints
        wac_combinations = []
        for n in range(1, len(weakly_active_constraints) + 1):
            wac_combinations_n = itertools.combinations(
                weakly_active_constraints, n)
            wac_combinations += [list(c) for c in wac_combinations_n]

        # for each minimal facet of the CR add or remove each combination of wakly active constraints
        for i in range(0, len(candidate_active_sets)):
            active_set = candidate_active_sets[i][0]
            for combination in wac_combinations:
                further_active_set = set(active_set).symmetric_difference(
                    combination)
                candidate_active_sets[i].append(
                    sorted(list(further_active_set)))

        return candidate_active_sets

    def z_optimal(self, x):
        """
        Returns the explicit solution of the mpQP as a function of the parameter.

        INPUTS:
            x: value of the parameter

        OUTPUTS:
            z_optimal: solution of the QP
        """

        z_optimal = self.z_offset + self.z_linear.dot(x).reshape(
            self.z_offset.shape)
        return z_optimal

    def lambda_optimal(self, x):
        """
        Returns the explicit value of the multipliers of the mpQP as a function of the parameter.

        INPUTS:
            x: value of the parameter

        OUTPUTS:
            lambda_optimal: optimal multipliers
        """

        lambda_A_optimal = self.lambda_A_offset + self.lambda_A_linear.dot(x)
        lambda_optimal = np.zeros(len(self.active_set + self.inactive_set))
        for i in range(0, len(self.active_set)):
            lambda_optimal[self.active_set[i]] = lambda_A_optimal[i]
        return lambda_optimal

    def applies_to(self, x):
        """
        Determines is a given point belongs to the critical region.

        INPUTS:
            x: value of the parameter

        OUTPUTS:
            is_inside: flag (True if x is in the CR, False otherwise)
        """

        # check if x is inside the polytope
        is_inside = self.polytope.applies_to(x)

        return is_inside
示例#20
0
def reorder_coordinates_visualizer(p):
    T = np.array([[0., 1., 0.], [0., 0., 1.], [1., 0., 0.]])
    A = p.lhs_min.dot(T)
    p_rotated = Polytope(A, p.rhs_min)
    p_rotated.assemble()
    return p_rotated
示例#21
0
    def test_MPCHybridController(self):

        # PWA dynamics
        t_s = .1
        A_1 = np.array([[0., 1.], [1., 0.]])
        B_1 = np.array([[0.], [1.]])
        c_1 = np.array([[0.], [0.]])
        sys_1 = ds.DTAffineSystem.from_continuous(A_1, B_1, c_1, t_s)
        A_2 = np.array([[0., 1.], [-1., 0.]])
        B_2 = B_1
        c_2 = np.array([[0.], [1.]])
        sys_2 = ds.DTAffineSystem.from_continuous(A_2, B_2, c_2, t_s)
        sys = [sys_1, sys_2]

        # PWA state domains
        x_max_1 = np.array([[1.], [1.5]])
        x_max_2 = np.array([[2.], [x_max_1[1, 0]]])
        x_min_1 = -x_max_2
        x_min_2 = np.array([[x_max_1[0, 0]], [x_min_1[1, 0]]])
        X_1 = Polytope.from_bounds(x_min_1, x_max_1)
        X_1.assemble()
        X_2 = Polytope.from_bounds(x_min_2, x_max_2)
        X_2.assemble()
        X = [X_1, X_2]

        # PWA input domains
        u_max = np.array([[1.]])
        u_min = -u_max
        U_1 = Polytope.from_bounds(u_min, u_max)
        U_1.assemble()
        U_2 = U_1
        U = [U_1, U_2]

        # PWA system
        pwa_sys = ds.DTPWASystem.from_orthogonal_domains(sys, X, U)

        # hybrid controller
        N = 10
        Q = np.eye(A_1.shape[0])
        R = np.eye(B_1.shape[1])
        P = Q
        X_N = X_1
        for objective_norm in ['one', 'two']:
            controller = MPCHybridController(pwa_sys, N, objective_norm, Q, R,
                                             P, X_N)

            # compare the cost of the MIP and the condensed program
            n_test = 100
            for i in range(n_test):
                x0 = np.random.rand(A_1.shape[0], 1)
                u_mip, _, ss, V_mip = controller.feedforward(x0)
                if not np.isnan(V_mip):
                    prog = controller.condense_program(ss)
                    u_condensed, V_condensed = prog.solve(
                        x0, u_length=B_1.shape[1])
                    argmin_error = max([
                        np.linalg.norm(u_mip[i] - u_condensed[i])
                        for i in range(len(u_mip))
                    ])
                    # if not argmin_error < 1.e-5:
                    #     print argmin_error, objective_norm
                    self.assertTrue(np.isclose(V_mip, V_condensed))
                    if objective_norm == 'two':
                        self.assertTrue(argmin_error < 1.e-4)
示例#22
0
import numpy as np
from pympc.geometry.polytope import Polytope
import matplotlib.pyplot as plt

from pympc.geometry.convex_hull import PolytopeProjectionInnerApproximation

n_var = 5
n_cons = 100
# A = np.random.randn(n_cons, n_var)
# b = np.random.rand(n_cons, 1)
# np.save('A_random', A)
# np.save('b_random', b)
A = np.load('A_random.npy')
b = np.load('b_random.npy')
p0 = Polytope(A, b)
p0.assemble()

app = PolytopeProjectionInnerApproximation(A, b, [0, 1])

p1 = Polytope(app.A, app.b)
p1.assemble()

p0.plot()
p1.plot()

plt.show()

for point in [np.array([[0.], [.1]]), np.array([[-.13], [.0]])]:
    print 'aaa'
    p0.plot()
    app.include_point(point)
示例#23
0
 def visualize_environment(self, vis):
     z_lim = [-.3, .3]
     environment_limits = np.ones((2, 1)) * .6
     for limb in self.moving_limbs:
         for i, moving_limb in enumerate(self.topology['moving'][limb]):
             if moving_limb.contact_surface is not None:
                 contact_domain = Polytope(moving_limb.A, moving_limb.b)
                 contact_domain.add_bounds(-environment_limits,
                                           environment_limits)
                 contact_domain.assemble()
                 contact_domain = extrude_2d_polytope(contact_domain, z_lim)
                 vis = visualize_3d_polytope(contact_domain,
                                             'w_' + limb + '_' + str(i),
                                             vis)
     for limb in self.fixed_limbs:
         fixed_limb = self.topology['fixed'][limb]
         b = fixed_limb.normal.T.dot(fixed_limb.position)
         contact_domain = Polytope(fixed_limb.normal.T, b)
         contact_domain.add_bounds(-environment_limits, environment_limits)
         contact_domain.assemble()
         contact_domain = extrude_2d_polytope(contact_domain, z_lim)
         vis = visualize_3d_polytope(contact_domain, 'w_' + limb, vis)
     return vis