Beispiel #1
0
    def update_waypoint(self,
                        index,
                        new_waypoint,
                        new_der_fixed=None,
                        defer=False):
        """Set new waypoint value and whether derivatives are fixed

        Uses:

        Modifies:
            self.
            waypoints:
            der_fixed:

        Args:
            new_waypoint: array of waypoint derivatives with length equal to
                n_der, the number of free derivatives given the polynomial order
            new_der_fixed: Boolean array of n_der (max potential number of free
                derivatives per segment) x 1 (one column per waypoint).
                Fixing a derivative at a waypoint is performed by setting the
                corresponding entry to True.

        Returns:

        Raises:
            minsnap.exceptions.InputError if
                np.size(waypoint) != np.shape(self.waypoints)[1]

            minsnap.exceptions.InputError if
                np.size(der_fixed) != np.shape(self.der_fixed)[1]
        """

        if new_der_fixed is not None:
            if np.size(new_der_fixed) != np.shape(self.der_fixed)[0]:
                msg = "Mismatch between length of new derivative fixed array "
                msg = msg + "and size of 2nd dimension of stored derivative "
                msg = msg + "fixed array"
                raise exceptions.InputError(new_der_fixed, msg)
            der_fixed_changed = np.array(index).copy()
        else:
            der_fixed_changed = np.array([])

        if np.size(new_waypoint) != np.shape(self.der_fixed)[0]:
            msg = "Mismatch between length of new waypoint array and size of"
            msg = msg + "2nd dimension of stored derivative fixed array"
            raise exceptions.InputError(new_waypoint, msg)

        if np.size(der_fixed_changed):
            self.der_fixed[:, index] = np.array(new_der_fixed).copy()
        self.waypoints[:, index] = np.array(new_waypoint).copy()

        if not defer:
            self.get_free_ders(waypoints_changed=np.array(index),
                               der_fixed_changed=der_fixed_changed,
                               times_changed=np.array([]))
            self.get_poly_coeffs()
            self.get_piece_poly()
Beispiel #2
0
    def update_waypoints(self, new_waypoints, new_der_fixed=None, defer=False):
        """Set new waypoint values and whether derivatives are fixed

        Uses:

        Modifies:
            self.
            waypoints:
            der_fixed:

        Args:
            new_waypoints: full array of waypoints and derivatives with the same
                size as the internal der_fixed array
            new_der_fixed: Boolean array of n_der (max potential number of free
                derivatives per segment) x n_seg + 1 (one column per waypoint).
                Fixing a derivative at a waypoint is performed by setting the
                corresponding entry to True.

        Returns:

        Raises:
            minsnap.exceptions.InputError if
                np.shape(waypoints) != np.shape(self.der_fixed)
        """

        if new_der_fixed is not None:
            if np.shape(new_der_fixed) != np.shape(self.der_fixed):
                raise exceptions.InputError(
                    new_der_fixed,
                    "Mismatch between shape of new derivative fixed" +
                    " array and shape of stored derivative fixed" + "array")
        else:
            # this is for convenience only (we are not updating der_fixed)
            new_der_fixed = self.der_fixed

        if np.shape(new_waypoints) != np.shape(new_der_fixed):
            raise exceptions.InputError(
                new_waypoints, "Mismatch between shape of new waypoints" +
                " array and shape of new derivative fixed" + "array")

        self.der_fixed = np.array(new_der_fixed).copy()
        self.waypoints = np.array(new_waypoints).copy()

        if not defer:
            # update all data structures
            self.get_free_ders()
            self.get_poly_coeffs()
            self.get_piece_poly()
Beispiel #3
0
def get_seed_times(poses, avg_vel_des):
    """
    Get the seed times for a given set of distances between points

    Args:
        poses: a dictionary with keys x, y, z and values that are lists
            for each coordinate
        avg_vel_des: the desired average velocity

    Returns:
        seed_times: the seed time for each segment before relative optimization

    Raises:
        minsnap.exceptions.InputError if the input values are of incorrect
            sizes or shapes
    """

    for key in poses.keys():
        if len(np.shape(poses[key])) != 1:
            raise exceptions.InputError(
                poses[key], "Only pass the 0th derivatives to" +
                " minsnap.utils.get_seed_times" +
                " as an array with 1 dimension")

    diff_norms = np.sqrt(
        np.diff(poses['x'])**2.0 + np.diff(poses['y'])**2.0 +
        np.diff(poses['z'])**2.0)

    return diff_norms / avg_vel_des
Beispiel #4
0
    def __init__(self,
                 waypoints,
                 order,
                 costs,
                 der_fixed,
                 times=None,
                 der_ineq=None,
                 delta=None,
                 print_output=False,
                 closed_loop=False):
        if np.shape(waypoints) != np.shape(der_fixed):
            raise exceptions.InputError(
                waypoints, "Mismatch between size of waypoints" +
                " array and size of derivative fixed" + "array")

        # if der_ineq is None:
        # no inequalities set
        der_ineq = np.array(der_fixed)
        der_ineq[:, :] = False
        # elif np.shape(der_ineq) != np.shape(der_fixed):
        #     raise exceptions.InputError(der_ineq,
        #             "Mismatch between size of der_ineq array and size of derivative fixed array")
        # elif (der_ineq[der_ineq]==der_fixed[der_ineq]).any():
        #     raise exceptions.InputError(der_ineq,"Invalid inequality and fixed input arrays;\n Have conflicting selections of constraints i.e. both are true for the same derivative")

        # constants
        self.waypoints = np.array(waypoints).copy()
        self.order = order
        self.n_der = utils.n_coeffs_free_ders(order)[1]
        self.costs = np.array(costs).copy()
        self.der_fixed = np.array(der_fixed).copy()
        self.der_ineq = np.array(der_ineq).copy()
        self.delta = delta
        self.n_seg = np.shape(waypoints)[1] - 1
        self.print_output = print_output
        self.closed_loop = closed_loop
        self.opt_time = 0.0

        self.Q = None
        self.M = None
        self.A = None
        self.R = None
        self.pinv_A = None
        self.free_ders = None
        self.fixed_ders = None
        self.piece_poly = None
        self.coeffs = None

        # don't need segment times to create the object
        # allow deferral of joint optimization
        if times is not None:
            self.times = np.array(times).copy()
            self.get_free_ders()
            self.get_poly_coeffs()
            self.get_piece_poly()
Beispiel #5
0
def dup_internal(to_dup, axis=0):
    """
    Duplicate all internal elements of the desired axis

    If to_dup is one dimensional, add a dimension so that the desired axis
        can be duplicated along.

    Args:
        to_dup: numpy array to duplicate
            Eg: [[0, 1, 2, 3],
                 [4, 5, 6, 7]]
        axis: axis to duplicate internal elements along

    Returns:
        A numpy array with every internal column duplicated.
            Eg: [[0, 1, 1, 2, 2, 3],
                 [4, 5, 5, 6, 6, 7]]

    Raises:
        minsnap.exceptions.InputError if to_dup has more than two dimensions
    """

    # to_dup should already be a numpy array, but make it one anyway
    to_dup = np.array(to_dup)

    # TODO([email protected]) - is this the desired way to handle these two
    # cases?
    # 1d array can be saved by adding dimension
    if len(np.shape(to_dup)) == 1:
        if axis == 0:
            to_dup = np.expand_dims(to_dup, 1)
        if axis == 1:
            to_dup = np.expand_dims(to_dup, 0)
    # we choose not to handle more than 2 dimensions
    if len(np.shape(to_dup)) > 2:
        raise exceptions.InputError(to_dup, "Input has more than 2 dimensions")

    # if there are no internal elements along the desired axis:
    if np.shape(to_dup)[axis] <= 2:
        return to_dup

    dupped = np.repeat(
        to_dup,
        np.r_[1, 2 * np.ones(np.shape(to_dup)[axis] - 2, dtype=int), 1],
        axis=axis)

    # dupped = np.c_[to_dup[:, [0]],
    #               np.repeat(to_dup[:, 1:-1], 2, axis=1),
    #               to_dup[:, [-1]]]
    return dupped
Beispiel #6
0
    def get_cost(self, times, times_changed=None, defer=False):
        """Utility function to calculate trajectory cost

        Richter, Bry, Roy; Polynomial Trajectory Planning for Quadrotor Flight
        Equation 31

        Uses:
            Everything used by the following functions:
                self.get_free_ders()
                self.get_poly_coeffs()

        Modifies:
            self.
            times: time in which to complete each segment (not the transition
                times)

            Everything modified by the following functions:
                self.get_free_ders()
                self.get_poly_coeffs()

        Args:

        Returns:
            self.
            piece_poly_cost: cost for integral of squared norm
                of specified derivatives

        Raises:
            minsnap.exceptions.InputError if
                np.size(times) != np.shape(der_fixed)[1] - 1
        """

        if np.size(times) != np.shape(self.der_fixed)[1] - 1:
            raise exceptions.InputError(
                times, "Mismatch between number of segment times" +
                " and number of segments")

        self.times = np.array(times).copy()

        # for reuse by update_times()
        if not defer:
            self.get_free_ders(waypoints_changed=np.array([]),
                               der_fixed_changed=np.array([]),
                               times_changed=times_changed)
            self.get_poly_coeffs()

            return self.piece_poly_cost
Beispiel #7
0
    def get_free_ders(self,
                      waypoints_changed=None,
                      der_fixed_changed=None,
                      times_changed=None):
        """Solve for free derivatives of polynomial segments

        Richter, Bry, Roy; Polynomial Trajectory Planning for Quadrotor Flight
        Equation 31

        Uses:
            self.
            waypoints: Numpy array of the waypoints (including derivatives)
            times: time in which to complete each segment (not the transition
                times)
            cost: weight in the sum of Hessian matrices (Equation 15)
            order: the order of the polynomial segments
            der_fixed: Boolean array of n_der (max potential number of free
                derivatives per segment) x n_seg + 1 (one column per waypoint).
                Fixing a derivative at a waypoint is performed by setting the
                corresponding entry to True.

        # TODO([email protected]) - decide how many of these fields to store vs
        # recompute
        Modifies:
            self.
            free_ders: Numpy matrix (column vector) of the free derivatives
            fixed_ders: Numpy matrix (column vector) of the fixed derivatives
            Q: the cost matrix in terms of polynomial coefficients
            M: the selector matrix mapping the ordering of derivatives to/from
                the form where free and fixed derivatives are partitioned
            A: the constraint matrix for segment boundaries
            pinv_A: the (pseudo-)inverse of A
            R: the cost matrix in terms of the free derivatives

        Args:

        Returns:

        Raises:
            minsnap.exceptions.InputError if
                np.size(times) != np.shape(der_fixed)[1] - 1
        """

        # def get_free_ders_setup_matrices(self, waypoints_changed=None,
        #                   der_fixed_changed=None,
        #                   times_changed=None):
        start_timer = time.time()

        if times_changed is None:
            times_changed = np.r_[0:self.n_seg]
        if der_fixed_changed is None:
            der_fixed_changed = np.r_[0:self.n_seg + 1]
        if waypoints_changed is None:
            waypoints_changed = np.r_[0:self.n_seg + 1]

        waypoints = self.waypoints
        times = self.times
        costs = self.costs
        order = self.order
        der_fixed = self.der_fixed
        der_ineq = self.der_ineq
        delta = self.delta
        n_seg = self.n_seg
        n_der = utils.n_coeffs_free_ders(order)[1]
        n_ineq = sum(sum(der_ineq))

        if np.size(times) != np.shape(der_fixed)[1] - 1:
            raise exceptions.InputError(
                times, "Mismatch between number of segment times" +
                " and number of segments")

        if np.shape(waypoints) != np.shape(der_fixed):
            raise exceptions.InputError(
                waypoints, "Mismatch between size of waypoints" +
                " array and size of derivative fixed" + "array")

        waypoints = np.matrix(waypoints)

        if n_ineq > 0:
            #TODO([email protected] & [email protected]) - investigate other ways to prevent singular matrices here
            limit = 0.03
            if sum(np.array(times) < limit) > 0:
                print(
                    "Warning: changing times because a lower limit has been reached"
                )
                temp = np.array(times)
                temp[temp < limit] = limit
                times = np.array(temp)
                self.times = times

        # See README.md on MATLAB vs Octave vs Numpy linear equation system solving
        # Porting this code: R{i} = M{i} / A{i}' * Q{i} / A{i} * M{i}';
        # With even polynomial order, the odd number of coefficients is not equal
        # to twice the number of free derivatives (floor of (odd # / 2)).
        # Therefore, A is not square.

        # Based on some quick checks, the inverse of A is still quite sparse,
        # especially when only the 0th derivative is fixed at internal
        # waypoints.

        # convert COO sparse output to CSC for fast matrix arithmetic
        if np.size(times_changed) or self.Q is None or self.A is None:
            if (self.Q is None
                    or self.A is None):  #or np.size(times_changed) > 1):
                Q = cost.block_cost(times, costs, order).tocsc(copy=True)
                A = constraint.block_constraint(times, order).tocsc(copy=True)
            else:
                # if we know which segment times have changed, only update
                # the corresponding blocks in the block matrix
                cost.update(self.Q, times_changed, times[times_changed], costs,
                            order)
                constraint.update(self.A, times_changed, times[times_changed],
                                  order)
                Q = self.Q
                A = self.A

            if (A.shape[0] == A.shape[1]):
                pinv_A = sp.sparse.linalg.inv(A)
                # TODO([email protected]) - could this be made to outperform the line above?
                #pinv_A = constraint.invert(A, order)
            else:
                # is there some way to keep this sparse? sparse SVD?
                pinv_A = np.asmatrix(sp.linalg.pinv(A.todense()))
        else:
            # TODO([email protected]) - is this the cleanest way?
            Q = self.Q
            A = self.A
            pinv_A = self.pinv_A

        if np.size(der_fixed_changed) or self.M is None:
            M = selector.block_selector(
                der_fixed, closed_loop=self.closed_loop).tocsc(copy=True)
        else:
            # TODO([email protected]) - is this the cleanest way?
            M = self.M

        if np.size(times_changed) or np.size(
                der_fixed_changed) or self.R is None:
            # all are matrices; OK to use * for multiply
            # R{i} = M{i} * pinv(full(A{i}))' * Q{i} * pinv(full(A{i})) * M{i}';
            R = M * pinv_A.T * Q * pinv_A * M.T

            # partition R by slicing along columns, converting to csr, then slicing
            # along rows
            if self.closed_loop:
                num_der_fixed = np.sum(der_fixed[:, :-1])
            else:
                num_der_fixed = np.sum(der_fixed)

            # Equation 29
            try:
                R_free = R[:, num_der_fixed:].tocsr()
            except AttributeError:
                # oops - we are a numpy matrix
                R_free = R[:, num_der_fixed:]
            R_fixed_free = R_free[:num_der_fixed, :]
            R_free_free = R_free[num_der_fixed:, :]

        else:
            R = self.R

            if hasattr(self, 'R_fixed_free'):
                R_fixed_free = self.R_fixed_free
                R_free_free = self.R_free_free
            else:
                # partition R by slicing along columns, converting to csr, then slicing
                # along rows
                if self.closed_loop:
                    num_der_fixed = np.sum(der_fixed[:, :-1])
                else:
                    num_der_fixed = np.sum(der_fixed)

                # Equation 29
                try:
                    R_free = R[:, num_der_fixed:].tocsr()
                except AttributeError:
                    # oops - we are a numpy matrix
                    R_free = R[:, num_der_fixed:]
                R_fixed_free = R_free[:num_der_fixed, :]
                R_free_free = R_free[num_der_fixed:, :]

        # TODO([email protected]) - transpose waypoints and der_fixed at input
        if not self.closed_loop:
            fixed_ders = waypoints.T[np.nonzero(der_fixed.T)].T  # Equation 31
        else:
            fixed_ders = waypoints[:, :-1].T[np.nonzero(
                der_fixed[:, :-1].T)].T  # Equation 31
        """ Solve """
        # the fixed derivatives, D_F in the paper, are just the waypoints for which
        # der_fixed is true
        # DP{i} = -RPP \ RFP' * DF{i};

        if n_ineq == 0:
            # Run for unconstrained case:
            # Solve for the free derivatives
            # Solve the unconstrained system
            free_ders = np.asmatrix(
                sp.sparse.linalg.spsolve(R_free_free,
                                         -R_fixed_free.T * fixed_ders)).T

        # Run for Constrained cases
        elif n_ineq > 0:  # If there are inequality constraints
            # Inequalities
            # Isolate the waypoints for which there is an inequality
            ineq_way_p = waypoints.T[np.nonzero(der_ineq.T)].T

            if type(delta) != float:
                # Take out the delta for the inequality constrained parts
                ineq_delta = delta.T[np.nonzero(der_ineq.T)].reshape(
                    [ineq_way_p.shape[0], 1])
            else:
                # Just a constant
                ineq_delta = delta

            W = np.concatenate((
                (ineq_way_p - ineq_delta),
                (-ineq_way_p - ineq_delta),
            ),
                               axis=0)

            # Selector matix
            if np.size(der_fixed_changed) or not hasattr(self, 'E'):
                E = constraint.block_ineq_constraint(der_fixed,
                                                     der_ineq).tocsc(copy=True)
            elif self.E is None:
                E = constraint.block_ineq_constraint(der_fixed,
                                                     der_ineq).tocsc(copy=True)
            else:
                E = self.E

            ### Solve with CVXOPT
            # Setup matrices
            P = matrix(2 * R_free_free.toarray(), tc='d')
            q_vec = matrix(np.array(2 * fixed_ders.T * R_fixed_free).T, tc='d')
            G = matrix(-E.toarray().astype(np.double), tc='d')
            h_vec = matrix(np.array(-W), tc='d')

            # Checks on the problem setup
            if np.linalg.matrix_rank(np.concatenate((P, G))) < P.size[0]:
                print('Warning: rank of [P;G] {} is less than size of P: {}'.
                      format(np.linalg.matrix_rank(np.concatenate((P, G))),
                             P.size[0]))
            else:
                if self.print_output:
                    print(
                        'Rank of A is {} size is {}\nRank for Q is {}, size is {}'
                        .format(np.linalg.matrix_rank(A.toarray()), A.shape,
                                np.linalg.matrix_rank(Q.toarray()), Q.shape))
                    print('Rank of R is {}, size is {}'.format(
                        np.linalg.matrix_rank(R.toarray()), R.shape))
                    print(
                        'Rank P is {}\nRank of G is: {}\nRank of [P;G] {} size of P: {}\n'
                        .format(np.linalg.matrix_rank(P),
                                np.linalg.matrix_rank(G),
                                np.linalg.matrix_rank(np.concatenate((P, G))),
                                P.size[0]))

            # To suppress output
            solvers.options['show_progress'] = False

            # Run cvxopt solver
            sol = solvers.qp(P, q_vec, G, h_vec)  #,initvals=primalstart)

            # Solution
            free_ders = np.matrix(sol['x'])

            if self.print_output:
                print('cvx solution is:\n{}'.format(free_ders))
                print('cvx cost is:\n{}'.format(sol['primal objective']))
                print('Constraints are: \n{}'.format(E * free_ders - W))

        # self.fixed_terms = fixed_terms
        self.opt_time = time.time() - start_timer
        self.free_ders = free_ders
        self.fixed_ders = fixed_ders
        self.Q = Q
        self.M = M
        self.A = A
        self.pinv_A = pinv_A
        self.R = R
        self.R_fixed_free = R_fixed_free
        self.R_free_free = R_free_free
Beispiel #8
0
    def insert(self,
               new_index,
               new_waypoint,
               new_times,
               new_der_fixed,
               defer=False):
        """
        Insert new waypoints to start at the selected index

        Uses:
            Everything used by the following functions:
                self.get_cost()
                self.get_piece_poly()

        Modifies:
            self.
            waypoints:
            der_fixed:
            n_seg:
            times: time in which to complete each segment (not the transition
                times)

            Everything modified by the following functions:
                self.get_cost()
                self.get_piece_poly()

        Args:
            new_index: index that new waypoints should start at after insertion
            new_waypoint: numpy array
            new_times:
            new_der_fixed:

        Returns:

        Raises:
        """

        # Check input
        if new_der_fixed is not None:
            if np.shape(new_der_fixed)[0] != np.shape(self.der_fixed)[0]:
                msg = "Mismatch between length of new derivative fixed array "
                msg = msg + "and size of 2nd dimension of stored derivative "
                msg = msg + "fixed array"
                raise exceptions.InputError(new_der_fixed, msg)
            der_fixed_changed = np.array(new_index).copy()
        else:
            der_fixed_changed = np.array([])

        if np.shape(new_waypoint)[0] != np.shape(self.der_fixed)[0]:
            msg = "Mismatch between length of new waypoint array and size of"
            msg = msg + "2nd dimension of stored derivative fixed array"
            raise exceptions.InputError(new_waypoint, msg)

        # modify waypoints
        self.waypoints = np.insert(self.waypoints,
                                   new_index,
                                   np.array(new_waypoint),
                                   axis=1)

        # modify der_fixed
        self.der_fixed = np.insert(self.der_fixed,
                                   new_index,
                                   np.array(new_der_fixed),
                                   axis=1)
        # Correct der fixed around it
        if new_index == 0:
            self.der_fixed[1:, 1] = False
        elif new_index == (self.n_seg + 1):
            self.der_fixed[1:, -2] = False

        # modify n_seg - increase by 1
        self.n_seg += 1

        # modify times - add a time and change time for segments on either side of new point
        if new_index > self.times.size:
            self.times = np.append(self.times, new_times[0])
        else:
            self.times = np.insert(self.times, new_index, new_times[1])

        if new_index != 0 and new_index <= self.times.size - 1:
            self.times[new_index - 1] = new_times[0]

        # modify Q and A matrices
        self.Q = cost.insert(self.Q,
                             new_index=new_index,
                             new_times=new_times,
                             costs=self.costs,
                             order=self.order)
        self.A = constraint.insert(self.A,
                                   new_index=new_index,
                                   new_times=new_times,
                                   order=self.order)

        # Get inverse A
        if (self.A.shape[0] == self.A.shape[1]):
            pinv_A = sp.sparse.linalg.inv(self.A)
        else:
            # is there some way to keep this sparse? sparse SVD?
            pinv_A = np.asmatrix(sp.linalg.pinv(self.A.todense()))

        self.pinv_A = pinv_A

        if not defer:
            self.get_free_ders(waypoints_changed=None,
                               der_fixed_changed=None,
                               times_changed=np.array([]))
            self.get_poly_coeffs()
            self.get_piece_poly()