示例#1
0
    def get_ctrl_output(self):
        # use self.x self.y and self.theta to
	# compute the right control input here

	### YOUR CODE HERE ###
        x = self.x
        y = self.y
        th = self.theta
	xg = 3
	yg = 3
	thg = -np.pi/2
        
        rho = ((xg-x)**2+(yg-y)**2)**0.5
        m = np.arctan2((yg-y),(xg-x)) #angle between rho vector and x-axis
        al = wrapToPi(m-th)
        dl = wrapToPi(m-thg)

        k1 = 1
        k2 = 1
        k3 = 1

        #control input
        V = k1*rho*np.cos(al)
        w = k2*al+k1*np.sinc(al/np.pi)*np.cos(al)*(al+k3*dl)

        V_max = 0.5
        om_max = 1
        V = min(abs(V),V_max)*np.sign(V)     #saturate V
        om = min(abs(w),om_max)*np.sign(w) #saturate om
        #ctrl = np.array([V,om])

        cmd_x_dot = V
        cmd_theta_dot = om
	### END OF YOUR CODE ###

        cmd = Twist()
        cmd.linear.x = cmd_x_dot
        cmd.angular.z = cmd_theta_dot
        return cmd
示例#2
0
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        pho = ((x - self.x_g)**2 + (y - self.y_g)**2)**(0.5)
        alpha = wrapToPi(np.arctan2((self.y_g-y),(self.x_g - x)) - th)
        sigma = wrapToPi(alpha + th - self.th_g)
        V = self.k1*pho*np.cos(alpha)
        om = self.k2*alpha + self.k1*np.sinc(alpha/np.pi)*np.cos(alpha)*(alpha + self.k3*sigma)

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        return V, om
def ctrl_pose(x,y,th,x_g,y_g,th_g):
    #(x,y,th): current state
    #(x_g,y_g,th_g): desired final state

    
    #Code pose controller
    rho = math.sqrt((x-x_g)**2 + (y-y_g)**2);
    alpha = wrapToPi(math.atan2((y-y_g), (x-x_g)) - (th) + math.pi);
    delta = wrapToPi(alpha + (th-th_g));

    #Define control inputs (V,om) - without saturation constraints
    k1 = 0.3
    k2 = 0.3
    k3 = 0.3
    V = k1*rho*math.cos(alpha)
    om = k2*alpha + k1*math.cos(alpha)*np.sinc(alpha/math.pi)*(alpha + k3*delta)

    # Apply saturation limits
    V = np.sign(V)*min(0.5, np.abs(V))
    om = np.sign(om)*min(1, np.abs(om))

    return np.array([V, om])
示例#4
0
    def NEESes(
        cls,
        x: np.ndarray,
        P: np.ndarray,
        x_gt: np.ndarray,
    ) -> np.ndarray:
        """Calculates the total NEES and the NEES for the substates
        Args:
            x (np.ndarray): The estimate
            P (np.ndarray): The state covariance
            x_gt (np.ndarray): The ground truth
        Raises:
            AssertionError: If any input is of the wrong shape, and if debug mode is on, certain numeric properties
        Returns:
            np.ndarray: NEES for [all, position, heading], shape (3,)
        """

        assert x.shape == (3, ), f"EKFSLAM.NEES: x shape incorrect {x.shape}"
        assert P.shape == (3, 3), f"EKFSLAM.NEES: P shape incorrect {P.shape}"
        assert x_gt.shape == (
            3, ), f"EKFSLAM.NEES: x_gt shape incorrect {x_gt.shape}"

        d_x = x - x_gt
        d_x[2] = utils.wrapToPi(d_x[2])
        assert (-np.pi <= d_x[2] <=
                np.pi), "EKFSLAM.NEES: error heading must be between (-pi, pi)"

        d_p = d_x[0:2]
        P_p = P[0:2, 0:2]
        assert d_p.shape == (2, ), "EKFSLAM.NEES: d_p must be 2 long"
        d_heading = d_x[2]  # Note: scalar
        assert np.ndim(
            d_heading) == 0, "EKFSLAM.NEES: d_heading must be scalar"
        P_heading = P[2, 2]  # Note: scalar
        assert np.ndim(
            P_heading) == 0, "EKFSLAM.NEES: P_heading must be scalar"

        # NB: Needs to handle both vectors and scalars! Additionally, must handle division by zero
        NEES_all = d_x @ (np.linalg.solve(P, d_x))
        NEES_pos = d_p @ (np.linalg.solve(P_p, d_p))
        try:
            NEES_heading = d_heading**2 / P_heading
        except ZeroDivisionError:
            NEES_heading = 1.0  # TODO: beware

        NEESes = np.array([NEES_all, NEES_pos, NEES_heading])
        NEESes[np.isnan(NEESes)] = 1.0  # We may divide by zero, # TODO: beware

        assert np.all(NEESes >= 0), "ESKF.NEES: one or more negative NEESes"
        return NEESes
示例#5
0
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        ########## Code starts here ##########
        temp = (np.arctan2(self.y_g - y, self.x_g - x))
        alpha = wrapToPi(temp - th)
        delta = wrapToPi(temp - self.th_g)
        rho = np.sqrt((self.x_g - x)**2 + (self.y_g - y)**2)
        V = self.k1 * rho * np.cos(alpha)
        om = self.k2 * alpha + self.k1 * \
            (np.sinc(alpha / np.pi) * np.cos(alpha)) * (alpha + self.k3 * delta)

        my_message1 = Float32()
        my_message1.data = alpha
        self.pub1.publish(my_message1)
        my_message2 = Float32()
        my_message2.data = delta
        self.pub2.publish(my_message2)
        my_message3 = Float32()
        my_message3.data = rho
        self.pub3.publish(my_message3)

        ########## Code ends here ##########

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        return V, om
示例#6
0
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        ########## Code starts here ##########
        #calculate change of variables from cartesion
        rho = np.sqrt((x - self.x_g)**2 + (y - self.y_g)**2)  #rho
        alpha = wrapToPi(np.arctan2(y - self.y_g, x - self.x_g) - th +
                         np.pi)  #alpha
        delta = wrapToPi(alpha + th - self.th_g)  #delta
        """
        NOTE: to get the final pose, we can "rotate" the space by the goal heading by simply
              subtracting off theta goal in the delta calculation
        """
        #calulate control values
        V = self.k1 * rho * np.cos(alpha)
        om = self.k2 * alpha + self.k1 * (
            np.sinc(alpha / np.pi) * np.cos(alpha)) * (alpha + self.k3 * delta)
        ########## Code ends here ##########

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        ##### PUBLISHING ####
        self.alpha_pub.publish(alpha)
        self.rho_pub.publish(rho)
        self.delta_pub.publish(delta)

        return V, om
示例#7
0
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        ########## Code starts here ##########
        x_delta = x - self.x_g
        y_delta = y - self.y_g
        th_delta = th - self.th_g

        # need rotation
        Rotation = np.array([[np.cos(self.th_g), -np.sin(self.th_g)],
                             [np.sin(self.th_g),
                              np.cos(self.th_g)]])

        new_x, new_y = np.dot(Rotation.T, np.array([x_delta, y_delta]))

        rho = np.sqrt(new_x**2 + new_y**2)
        alpha = wrapToPi(np.arctan2(new_y, new_x) - th_delta + np.pi)
        delta = wrapToPi(alpha + th_delta)

        V = self.k1 * rho * np.cos(alpha)
        om = self.k2 * alpha + self.k1 * np.sinc(
            2 * alpha / np.pi) * (alpha + self.k3 * delta)
        ########## Code ends here ##########

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        return V, om
示例#8
0
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        ########## Code starts here ##########
        # get differences in position
        x_diff = self.x_g - x
        y_diff = self.y_g - y

        # calculate polar coordinates
        rho = np.sqrt(x_diff**2 + y_diff**2)
        alpha = wrapToPi(np.arctan2(y_diff, x_diff) - th)
        delta = wrapToPi(alpha + th - self.th_g)

        # calculate the controls (keeping in mind thresholds)
        if rho < RHO_THRES and alpha < ALPHA_THRES and delta < DELTA_THRES:
            V = 0
            om = 0
        else:
            V = self.k1 * rho * np.cos(alpha)
            om = self.k2 * alpha + self.k1 * np.sinc(
                alpha / np.pi) * np.cos(alpha) * (alpha + self.k3 * delta)

        ########## Code ends here ##########

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        return V, om
    def compute_control(self, x, y, th, t):
        """
        Inputs:
            x,y,th: Current state
            t: Current time (you shouldn't need to use this)
        Outputs: 
            V, om: Control actions

        Hints: You'll need to use the wrapToPi function. The np.sinc function
        may also be useful, look up its documentation
        """
        ########## Code starts here ##########
        delta_x = self.x_g - x
        delta_y = self.y_g - y
        rho = np.sqrt(delta_x**2 + delta_y**2)
        alpha = wrapToPi(np.arctan2(delta_y, delta_x) - th)
        delta = wrapToPi(th + alpha - self.th_g)

        if np.abs(rho) < RHO_THRES or np.abs(alpha) < ALPHA_THRES or np.abs(
                delta) < DELTA_THRES:
            #V = 0 what's up with this?
            V = self.k1 * rho * np.cos(alpha)
            om = self.k2 * alpha + self.k1 * np.sinc(
                alpha / np.pi) * np.cos(alpha) * (alpha + self.k3 * delta)
        else:
            V = self.k1 * rho * np.cos(alpha)
            om = self.k2 * alpha + self.k1 * np.sin(alpha) / alpha * np.cos(
                alpha) * (alpha + self.k3 * delta)

        ########## Code ends here ##########

        # apply control limits
        V = np.clip(V, -self.V_max, self.V_max)
        om = np.clip(om, -self.om_max, self.om_max)

        return V, om
示例#10
0
 def get_goal_pose(self, target_name, distance):
     """
     Given the target and distance from the vendor, computes the 
     position the robot should travel to.
     """
     if target_name not in self.target_names:
         return
     marker_id = self.target_names.index(target_name)
     theta = self.heading[marker_id]
     if theta is None:
         return
     x, y = self.marker_locations[marker_id]
     x += np.cos(theta) * distance
     y += np.sin(theta) * distance
     robot_heading = wrapToPi(theta + np.pi)
     return (x, y, robot_heading)
示例#11
0
    def f(self, x: np.ndarray, u: np.ndarray) -> np.ndarray:
        """Add the odometry u to the robot state x.
        Parameters
         x : np.ndarray, shape=(3,) the robot state
         u : np.ndarray, shape=(3,) the odometry
        Returns:
         np.ndarray, shape = (3,) the predicted state
        """
        # eq (11.7). Should wrap heading angle between (-pi, pi), see utils.wrapToPi
        xpred = np.array([
            x[0] + u[0] * np.cos(x[2]) - u[1] * np.sin(x[2]),
            x[1] + u[0] * np.sin(x[2]) + u[1] * np.cos(x[2]),
            utils.wrapToPi(x[2] + u[2])
        ])

        #assert xpred.shape == (3,), "EKFSLAM.f: wrong shape for xpred"
        return xpred
示例#12
0
def modelStep(state_km1, odom):
    L = GP.wheelbase
    Ts = GP.Ts

    x_km1 = state_km1[0]
    y_km1 = state_km1[1]
    theta_km1 = state_km1[2]

    v = odom[0]
    gamma = odom[1]

    x_k = x_km1 + v * np.cos(theta_km1) * Ts
    y_k = y_km1 + v * np.sin(theta_km1) * Ts
    theta_k = theta_km1 + (v / L) * np.tan(gamma) * Ts

    theta_k = wrapToPi(theta_k)

    return np.array([x_k, y_k, theta_k])
示例#13
0
    def Fu(self, x: np.ndarray, u: np.ndarray) -> np.ndarray:
        """Calculate the Jacobian of f with respect to u.

        Parameters
        ----------
        x : np.ndarray, shape=(3,)
            the robot state
        u : np.ndarray, shape=(3,)
            the odometry

        Returns
        -------
        np.ndarray
            The Jacobian of f wrt. u.
        """
        psi = utils.wrapToPi(x[2])
        Fu = np.array([[np.cos(psi), -np.sin(psi), 0],
                       [np.sin(psi), np.cos(psi), 0], [0, 0, 1]])  # eq (11.14)

        assert Fu.shape == (3, 3), "EKFSLAM.Fu: wrong shape"
        return Fu
    def run_pose_controller(self):
        """ runs a simple feedback pose controller """

        try:
            (translation, rotation) = self.trans_listener.lookupTransform(
                '/map', '/base_footprint', rospy.Time(0))
            self.x = translation[0]
            self.y = translation[1]
            euler = tf.transformations.euler_from_quaternion(rotation)
            self.theta = euler[2]
        except (tf.LookupException, tf.ConnectivityException,
                tf.ExtrapolationException):
            pass

        rel_coords = np.array([self.x - self.x_g, self.y - self.y_g])
        R = np.array([[np.cos(self.theta_g),
                       np.sin(self.theta_g)],
                      [-np.sin(self.theta_g),
                       np.cos(self.theta_g)]])
        rel_coords_rot = np.dot(R, rel_coords)

        th_rot = self.theta - self.theta_g
        rho = linalg.norm(rel_coords)
        ang = np.arctan2(rel_coords_rot[1], rel_coords_rot[0]) + np.pi
        angs = wrapToPi(np.array([ang - th_rot, ang]))
        alpha = angs[0]
        delta = angs[1]

        V = K1 * rho * np.cos(alpha)
        om = K2 * alpha + K1 * np.sinc(
            2 * alpha / np.pi) * (alpha + K3 * delta)

        # Apply saturation limits
        cmd_x_dot = np.sign(V) * min(V_MAX, np.abs(V))
        cmd_theta_dot = np.sign(om) * min(W_MAX, np.abs(om))

        cmd_msg = Twist()
        cmd_msg.linear.x = cmd_x_dot
        cmd_msg.angular.z = cmd_theta_dot
        self.cmd_vel_publisher.publish(cmd_msg)
示例#15
0
def ctrl_pose(x, y, th, x_g, y_g, th_g):
    #(x,y,th): current state
    #(x_g,y_g,th_g): desired final state

    # Relative pos in global frame
    rel_pos_global = np.array([x - x_g, y - y_g])
    # Rotation matrix from relative to global
    R = np.array([[np.cos(th_g), -np.sin(th_g)], [np.sin(th_g), np.cos(th_g)]])
    # Relative pos in relative frame
    rel_pos = R.T.dot(rel_pos_global)

    # New state variables
    theta = th - th_g
    rho = np.linalg.norm(rel_pos)
    delta = np.arctan2(rel_pos[1], rel_pos[0]) + np.pi
    alpha = delta - theta
    # Wrap angles to [-pi, pi]
    delta, alpha = wrapToPi(np.array([delta, alpha]))

    # Gains
    k1 = 0.5
    k2 = 0.8
    k3 = 0.8

    # Define control inputs (V,om) - without saturation constraints
    thresh = 1e-5

    if (np.array([rho, delta, alpha]) <= np.array([thresh] * 3)).all():
        V, om = 0.0, 0.0
    else:
        V = k1 * rho * np.cos(alpha)
        om = k2 * alpha + k1 * np.sinc(
            2 * alpha / np.pi) * (alpha + k3 * delta)

    # Apply saturation limits
    V = np.sign(V) * min(0.5, np.abs(V))
    om = np.sign(om) * min(1, np.abs(om))

    return np.array([V, om])
示例#16
0
    def Fx(self, x: np.ndarray, u: np.ndarray) -> np.ndarray:
        """Calculate the Jacobian of f with respect to x.

        Parameters
        ----------
        x : np.ndarray, shape=(3,)
            the robot state
        u : np.ndarray, shape=(3,)
            the odometry

        Returns
        -------
        np.ndarray
            The Jacobian of f wrt. x.
        """
        psi = utils.wrapToPi(x[2])
        Fx = np.array([[1, 0, -u[0] * np.sin(psi) - u[1] * np.cos(psi)],
                       [0, 1, u[0] * np.cos(psi) - u[1] * np.sin(psi)],
                       [0, 0, 1]])  # eq (11.13)

        assert Fx.shape == (3, 3), "EKFSLAM.Fx: wrong shape"
        return Fx
示例#17
0
    def f(self, x: np.ndarray, u: np.ndarray) -> np.ndarray:
        """Add the odometry u to the robot state x.

        Parameters
        ----------
        x : np.ndarray, shape=(3,)
            the robot state
        u : np.ndarray, shape=(3,)
            the odometry

        Returns
        -------
        np.ndarray, shape = (3,)
            the predicted state
        """

        pos = x[:2] + rotmat2d(x[2]) @ u[:2]
        yaw_angle = utils.wrapToPi(x[2] + u[2]) # Wrap heading angle between (-pi, pi)

        xpred = np.array([*pos, yaw_angle]) 

        assert xpred.shape == (3,), "EKFSLAM.f: wrong shape for xpred"
        return xpred
示例#18
0
    def add_food_to_list(self, msg, label):
        '''
        Description:Add the food item to the data matrix
        Arguments:msg from the topic, food item label
        Returns:False if nothing was added, true if added
        '''
        # get the angle of the frame wrt the world
        theta_food = 0.5 * wrapToPi(msg.thetaleft -
                                    msg.thetaright) + self.theta

        # find the x, y, of the food using the angle
        x_food = self.x  #+(msg.distance - self.chunky_radius)*np.cos(theta_food)
        y_food = self.y  #+(msg.distance - self.chunky_radius)*np.sin(theta_food)
        # check to see if the food was added or we have a better distance, but only when we are exploring
        if self.exploring and (
                self.food_found[label] is 0
                or msg.distance < self.food_data[label, 3]
        ):  #np.abs(theta_food) < self.food_data[label,2]:

            # popluate the array at the correct row
            self.food_data[
                label] = x_food, y_food, theta_food, msg.distance, msg.confidence

            # indicate that we found the food
            self.food_found[label] = 1

            # add marker to location of food
            #self.broadcast_tf(x_food,y_food,0)
            self.add_marker(x_food, y_food, label)

            # return true to indicate successful addition
            return True

        # else return false
        else:
            return False
示例#19
0
# Apply control and simulate
Trj = [init_state.copy()]
Vs = []
t = 0
x = init_state
while t < 2000:
    x_torch = numpy2torch(x.copy())
    with torch.no_grad():
        _, u, V = net(x_torch.unsqueeze(0))
        u = torch.clamp(u, -10., 10.)  # Limit the amount of control input
    u = torch2numpy(u[0])

    times = np.arange(2) * dt
    x_orig = x.copy()
    x_orig[:n_link] = wrapToPi(x_orig[:n_link] + target)
    y = odeint(system.gradient, x_orig, times, args=(u, ))
    y = y[1]
    y[:n_link] = wrapToPi(y[:n_link] - target)

    x = y

    Trj.append(x.copy())

    Vs.append(V.item())

    print('t = {}, V = {}'.format(t, V.item()))

    t += 1

Trj = np.array(Trj[:-1])
示例#20
0
    def update(self,
               eta: np.ndarray,
               P: np.ndarray,
               z: np.ndarray,
               gps=None) -> Tuple[np.ndarray, np.ndarray, float, np.ndarray]:
        """Update eta and P with z, associating landmarks and adding new ones.

        Parameters
        ----------
        eta : np.ndarray
            [description]
        P : np.ndarray
            [description]
        z : np.ndarray, shape=(#detections, 2)
            [description]

        Returns
        -------
        Tuple[np.ndarray, np.ndarray, float, np.ndarray]
            [description]
        """
        numLmk = (eta.size - 3) // 2
        assert (len(eta) -
                3) % 2 == 0, "EKFSLAM.update: landmark lenght not even"

        if numLmk > 0:
            # Prediction and innovation covariance
            zpred = self.h(eta)  # TODO
            H = self.H(eta)  # TODO

            # Here you can use simply np.kron (a bit slow) to form the big (very big in VP after a while) R,
            # or be smart with indexing and broadcasting (3d indexing into 2d mat) realizing you are adding the same R on all diagonals
            S = H @ P @ H.T + block_diag_einsum(self.R, numLmk)

            assert (S.shape == zpred.shape *
                    2), "EKFSLAM.update: wrong shape on either S or zpred"
            z = z.ravel()  # 2D -> flat

            # Perform data association
            za, zpred, Ha, Sa, a = self.associate(z, zpred, H, S)

            # No association could be made, so skip update
            if za.shape[0] == 0:
                etaupd = eta  # TODO
                Pupd = P  # TODO
                NIS = 1  # TODO: beware this one when analysing consistency.

            else:
                # Create the associated innovation
                v = za.ravel() - zpred  # za: 2D -> flat
                v[1::2] = utils.wrapToPi(v[1::2])

                # Kalman mean update
                #S_cho_factors = la.cho_factor(Sa) # Optional, used in places for S^-1, see scipy.linalg.cho_factor and scipy.linalg.cho_solve
                W = P @ Ha.T @ la.inv(
                    Sa)  # TODO, Kalman gain, can use S_cho_factors
                #W = la.cho_solve(S_cho_factors, Ha @ P).T
                etaupd = eta + W @ v  # TODO, Kalman update

                # Kalman cov update: use Joseph form for stability
                jo = -W @ Ha
                jo[np.diag_indices(
                    jo.shape[0])] += 1  # same as adding Identity mat
                Pupd = jo @ P  # @ jo.T + W @ np.kron(np.eye(za.size//2), self.R)@W.T
                # TODO, Kalman update. This is the main workload on VP after speedups

                # calculate NIS, can use S_cho_factors
                NIS = (v.T @ la.inv(Sa) @ v
                       )  # - CI[0]) / (CI[1] - CI[0]) # TODO
                #NIS = v.T @ la.cho_solve(S_cho_factors, v)

                # When tested, remove for speed
                assert np.allclose(
                    Pupd, Pupd.T), "EKFSLAM.update: Pupd not symmetric"
                assert np.all(np.linalg.eigvals(Pupd) > 0
                              ), "EKFSLAM.update: Pupd not positive definite"

        else:  # All measurements are new landmarks,
            a = np.full(z.shape[0], -1)
            z = z.flatten()
            NIS = 0  # TODO: beware this one, you can change the value to for instance 1
            etaupd = eta
            Pupd = P

        # Create new landmarks if any is available
        if self.do_asso:
            is_new_lmk = a == -1
            if np.any(is_new_lmk):
                z_new_inds = np.empty_like(z, dtype=bool)
                z_new_inds[::2] = is_new_lmk
                z_new_inds[1::2] = is_new_lmk
                z_new = z[z_new_inds]
                etaupd, Pupd = self.add_landmarks(
                    etaupd, Pupd, z_new)  # TODO, add new landmarks.

        assert np.allclose(Pupd,
                           Pupd.T), "EKFSLAM.update: Pupd must be symmetric"
        assert np.all(
            np.linalg.eigvals(Pupd) >= 0), "EKFSLAM.update: Pupd must be PSD"

        return etaupd, Pupd, NIS, a
示例#21
0
 def at_goal(self):
     """
     returns whether the robot has reached the goal position with enough
     accuracy to return to idle state
     """
     return (linalg.norm(np.array([self.x-self.x_g, self.y-self.y_g])) < self.at_thresh and abs(wrapToPi(self.theta - self.theta_g)) < self.at_thresh_theta)
示例#22
0
    def replan(self):
        """
        loads goal into pose controller
        runs planner based on current pose
        if plan long enough to track:
            smooths resulting traj, loads it into traj_controller
            sets self.current_plan_start_time
            sets mode to ALIGN
        else:
            sets mode to PARK
        """
        # Make sure we have a map
        if not self.occupancy:
            rospy.loginfo(
                "Navigator: replanning canceled, waiting for occupancy map.")
            self.switch_mode(Mode.IDLE)
            return

        # Attempt to plan a path
        state_min = self.snap_to_grid((-self.plan_horizon, -self.plan_horizon))
        state_max = self.snap_to_grid((self.plan_horizon, self.plan_horizon))
        x_init = self.snap_to_grid((self.x, self.y))
        self.plan_start = x_init
        x_goal = self.snap_to_grid((self.x_g, self.y_g))
        problem = AStar(state_min, state_max, x_init, x_goal, self.occupancy,
                        self.plan_resolution)

        rospy.loginfo("Navigator: computing navigation plan")
        success = problem.solve()
        if not success:
            rospy.loginfo("Planning failed")
            return
        rospy.loginfo("Planning Succeeded")

        planned_path = problem.path

        # Check whether path is too short
        if len(planned_path) < 4:
            rospy.loginfo("Path too short to track")
            self.switch_mode(Mode.PARK)
            return

        # Smooth and generate a trajectory
        traj_new, t_new = compute_smoothed_traj(planned_path, self.v_des,
                                                self.spline_alpha,
                                                self.traj_dt)

        # If currently tracking a trajectory, check whether new trajectory will take more time to follow
        if self.mode == Mode.TRACK:
            t_remaining_curr = self.current_plan_duration - self.get_current_plan_time(
            )

            # Estimate duration of new trajectory
            th_init_new = traj_new[0, 2]
            th_err = wrapToPi(th_init_new - self.theta)
            t_init_align = abs(th_err / self.om_max)
            t_remaining_new = t_init_align + t_new[-1]

            if t_remaining_new > t_remaining_curr:
                rospy.loginfo(
                    "New plan rejected (longer duration than current plan)")
                self.publish_smoothed_path(traj_new,
                                           self.nav_smoothed_path_rej_pub)
                return

        # Otherwise follow the new plan
        self.publish_planned_path(planned_path, self.nav_planned_path_pub)
        self.publish_smoothed_path(traj_new, self.nav_smoothed_path_pub)

        self.pose_controller.load_goal(self.x_g, self.y_g, self.theta_g)
        self.traj_controller.load_traj(t_new, traj_new)

        self.current_plan_start_time = rospy.get_rostime()
        self.current_plan_duration = t_new[-1]

        self.th_init = traj_new[0, 2]
        self.heading_controller.load_goal(self.th_init)

        if not self.aligned():
            rospy.loginfo("Not aligned with start direction")
            self.switch_mode(Mode.ALIGN)
            return

        rospy.loginfo("Ready to track")
        self.switch_mode(Mode.TRACK)
    def replan(self, force=False):
        """
        loads goal into pose controller
        runs planner based on current pose
        if plan long enough to track:
            smooths resulting traj, loads it into traj_controller
            sets self.current_plan_start_time
            sets mode to ALIGN
        else:
            sets mode to PARK
        """
        # Make sure we have a map
        if not self.occupancy:
            rospy.loginfo(
                "Navigator: replanning canceled, waiting for occupancy map.")
            return

        # Check if the goal is free
        if not self.x_g or not self.y_g or not self.theta_g:
            rospy.loginfo("No goal.")
            return

        X_g = (self.x_g, self.y_g)

        # Before planning a path, load goal pose
        self.pose_controller.load_goal(X_g[0], X_g[1], self.theta_g)

        # Attempt to plan a path
        state_min = self.snap_to_grid((-self.plan_horizon, -self.plan_horizon))
        state_max = self.snap_to_grid((self.plan_horizon, self.plan_horizon))
        x_init = nearest_free((self.x, self.y), self.occupancy_strict,
                              self.plan_resolution)
        self.plan_start = x_init
        problem = AStar(state_min, state_max, x_init, X_g,
                        self.occupancy_strict, self.occupancy,
                        self.plan_resolution)
        success = problem.solve()
        if not success:
            if self.prev_goal != X_g:
                self.error_count = 0
            self.prev_goal = X_g
            self.error_count += 1
            return
        planned_path = problem.path

        # Check whether path is too short
        if len(planned_path) < 4:
            self.switch_mode(Mode.PARK)
            return

        # Smooth and generate a trajectory
        traj_new, t_new = compute_smoothed_traj(planned_path, self.v_des,
                                                self.spline_alpha,
                                                self.traj_dt)

        # If currently tracking a trajectory, check whether new trajectory will take more time to follow
        if self.mode == Mode.TRACK:
            t_remaining_curr = self.current_plan_duration - self.get_current_plan_time(
            )

            # Estimate duration of new trajectory
            th_init_new = traj_new[0, 2]
            th_err = wrapToPi(th_init_new - self.theta)
            t_init_align = abs(th_err / self.om_max)
            t_remaining_new = t_init_align + t_new[-1]

            if t_remaining_new > t_remaining_curr and not (self.new_goal
                                                           or force):
                self._publish_smoothed_path(traj_new,
                                            self.nav_smoothed_path_rej_pub)
                return

        # Otherwise follow the new plan
        self.publish_planned_path(planned_path, self.nav_planned_path_pub)
        self._publish_smoothed_path(traj_new, self.nav_smoothed_path_pub)

        self.traj_controller.load_traj(t_new, traj_new)

        self.current_plan_start_time = rospy.get_rostime()
        self.current_plan_duration = t_new[-1]
        self.new_goal = False

        self.th_init = traj_new[0, 2]
        self.heading_controller.load_goal(self.th_init)

        if not self.aligned():
            self.switch_mode(Mode.ALIGN)
            return

        self.switch_mode(Mode.TRACK)
示例#24
0
    CI_ANEES = np.array(chi2.interval(alpha, df*N)) / N
    print(f"CI ANEES {tag}: {CI_ANEES}")
    print(f"ANEES {tag}: {NEES.mean()}")

fig4.tight_layout()

# %% RMSE

ylabels = ['m', 'deg']
scalings = np.array([1, 180/np.pi])

fig5, ax5 = plt.subplots(nrows=2, ncols=1, figsize=(7, 5), num=5, clear=True, sharex=True)

pos_err = np.linalg.norm(pose_est[:N,:2] - poseGT[:N,:2], axis=1)
heading_err = np.abs(utils.wrapToPi(pose_est[:N,2] - poseGT[:N,2]))

errs = np.vstack((pos_err, heading_err))

for ax, err, tag, ylabel, scaling in zip(ax5, errs, tags[1:], ylabels, scalings):
    ax.plot(err*scaling)
    ax.set_title(f"{tag}: RMSE {np.sqrt((err**2).mean())*scaling} {ylabel}")
    ax.set_ylabel(f"[{ylabel}]")
    ax.grid()

fig5.tight_layout()

# %% Movie time

if playMovie:
    try:
示例#25
0
    def get_ctrl_output(self):
        """ runs a simple feedback pose controller """
        if (rospy.get_rostime().to_sec()-self.cmd_pose_time.to_sec()) < TIMEOUT:
            # if you are not using gazebo, your need to use a TF look-up to find robot's states
            # relevant for hw 3+
            if not use_gazebo:
                try:
                    origin_frame = "/map" if mapping else "/odom"
                    (translation,rotation) = self.trans_listener.lookupTransform(origin_frame, '/base_footprint', rospy.Time(0))
                    self.x = translation[0]
                    self.y = translation[1]
                    euler = tf.transformations.euler_from_quaternion(rotation)
                    self.theta = euler[2]
                except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
                    pass

            ######### YOUR CODE HERE ############
            # robot's state is self.x, self.y, self.theta
            # robot's desired state is self.x_g, self.y_g, self.theta_g
            # fill out cmd_x_dot = ... cmd_theta_dot = ...
            x = self.x
            y = self.y
            th = self.theta
            xg = self.x_g
            yg = self.y_g
            thg = self.theta_g

            rho = ((xg-x)**2+(yg-y)**2)**0.5
            m = np.arctan2((yg-y),(xg-x)) #angle between rho vector and x-axis
            al = wrapToPi(m-th)
            dl = wrapToPi(m-thg)

            k1 = 1
            k2 = 1
            k3 = 1

            #control input
            V = k1*rho*np.cos(al)
            w = k2*al+k1*np.sinc(al/np.pi)*np.cos(al)*(al+k3*dl)

            V_max = 0.5
            om_max = 1
            V = min(abs(V),V_max)*np.sign(V)     #saturate V
            om = min(abs(w),om_max)*np.sign(w) #saturate om

            cmd_x_dot = V
            cmd_theta_dot = om

            ######### END OF YOUR CODE ##########

        else:
            # haven't received a command in a while so stop
            rospy.loginfo("Pose controller TIMEOUT: commanding zero controls")
            cmd_x_dot = 0
            cmd_theta_dot = 0


        cmd = Twist()
        cmd.linear.x = cmd_x_dot
        cmd.angular.z = cmd_theta_dot
        return cmd
示例#26
0
    def f(self, x, u):
        xpred = np.hstack(
            (utils.rot_mat(x[2]) @ u, utils.wrapToPi(x[2] + u[2])))

        return xpred
示例#27
0
 def aligned(self):
     """
     returns whether robot is aligned with starting direction of path
     (enough to switch to tracking controller)
     """
     return (abs(wrapToPi(self.theta - self.th_init)) < self.theta_start_thresh)
示例#28
0
    def update(
            self, eta: np.ndarray, P: np.ndarray,
            z: np.ndarray) -> Tuple[np.ndarray, np.ndarray, float, np.ndarray]:
        """Update eta and P with z, associating landmarks and adding new ones.
        Parameters
         eta : np.ndarray
         P : np.ndarray
         z : np.ndarray, shape=(#detections, 2)
        Returns
         Tuple[np.ndarray, np.ndarray, float, np.ndarray]
            [description]
        """
        numLmk = (eta.size - 3) // 2
        assert (len(eta) -
                3) % 2 == 0, "EKFSLAM.update: landmark lenght not even"

        print(f"numLmk={numLmk}")

        if numLmk > 0:
            # Prediction and innovation covariance
            zpred = self.h(eta)
            H = self.H(eta)

            #R = np.kron(np.eye(numLmk), self.R)
            R = np.diag(np.repeat([self.R[0, 0], self.R[1, 1]], numLmk))
            S = H @ P @ H.T + R

            assert (S.shape == zpred.shape *
                    2), f"EKFSLAM.update: wrong shape on either S or zpred"

            z = z.ravel()  # 2D -> flat

            # Perform data association
            za, zpred, Ha, Sa, a = self.associate(z, zpred, H, S)
            print(
                f"numLmk={numLmk}  S: {S.shape}, Sa: {Sa.shape}, R: {R.shape}")

            # No association could be made, so skip update
            if za.shape[0] == 0:
                etaupd = eta
                Pupd = P
                NIS = 1  # TODO: beware this one when analysing consistency.

            else:
                # Create the associated innovation
                v = za.ravel() - zpred  # za: 2D -> flat
                v[1::2] = utils.wrapToPi(v[1::2])

                # Kalman mean update
                S_cho_factors = la.cho_factor(Sa)
                Sa_inv = la.cho_solve(S_cho_factors, np.eye(Sa.shape[0]))

                W = P @ Ha.T @ Sa_inv
                etaupd = eta + W @ v  # Kalman update
                # Kalman cov update: use Joseph form for stability
                jo = -W @ Ha
                jo[np.diag_indices(
                    jo.shape[0])] += 1  # same as adding Identity mat

                Pupd = jo @ P @ jo.T
                Pupd += W @ R[:Sa.shape[0], :Sa.shape[
                    0]] @ W.T  # TODO, Kalman update. This is the main workload on VP after speedups
                #Pupd = P - W @ Ha @ P

                #EKF:  P_upd = (I - W @ H) @ P @ (I - W @ H).T + W @ self.sensor_model.R(x) @ W.T

                # calculate NIS, can use S_cho_factors
                NIS = self.NIS(Sa_inv, v)

                # When tested, remove for speed
                assert np.allclose(
                    Pupd, Pupd.T), "EKFSLAM.update: Pupd not symmetric"
                assert np.all(np.linalg.eigvals(Pupd) > 0
                              ), "EKFSLAM.update: Pupd not positive definite"

        else:  # All measurements are new landmarks,
            a = np.full(z.shape[0], -1)
            z = z.flatten()
            NIS = 1  # TODO: beware this one, you can change the value to for instance 1
            etaupd = eta
            Pupd = P

        # Create new landmarks if any is available
        if self.do_asso:
            is_new_lmk = a == -1
            if np.any(is_new_lmk):
                z_new_inds = np.empty_like(z, dtype=bool)
                z_new_inds[::2] = is_new_lmk
                z_new_inds[1::2] = is_new_lmk
                z_new = z[z_new_inds]
                # TODO, add new landmarks:
                etaupd, Pupd = self.add_landmarks(etaupd, Pupd,
                                                  z_new)  #(eta, P, z_new)

        assert np.allclose(Pupd,
                           Pupd.T), "EKFSLAM.update: Pupd must be symmetric"
        assert np.all(
            np.linalg.eigvals(Pupd) >= 0), "EKFSLAM.update: Pupd must be PSD"

        return etaupd, Pupd, NIS, a
示例#29
0
    def replan(self):
        """
        loads goal into pose controller
        runs planner based on current pose
        if plan long enough to track:
            smooths resulting traj, loads it into traj_controller
            sets self.current_plan_start_time
            sets mode to ALIGN
        else:
            sets mode to PARK
        """
        # Make sure we have a map
        if not self.occupancy:
            rospy.loginfo(
                "Navigator: replanning canceled, waiting for occupancy map.")
            self.switch_mode(Mode.IDLE)
            return

        # Attempt to plan a path
        state_min = self.snap_to_grid((-self.plan_horizon, -self.plan_horizon))
        state_max = self.snap_to_grid((self.plan_horizon, self.plan_horizon))
        x_init = self.snap_to_grid((self.x, self.y))
        self.plan_start = x_init
        x_goal = self.snap_to_grid((self.x_g, self.y_g))
        problem = AStar(state_min, state_max, x_init, x_goal, self.occupancy,
                        self.plan_resolution)

        rospy.loginfo("Navigator: computing navigation plan")

        success = problem.solve()
        if not success:
            rospy.loginfo("Planning failed")

            #tell squirtle we could not find a path
            msg = String()
            msg.data = "no_path"
            self.publish_squirtle.publish(msg)

            if self.mode == Mode.BACKING_UP:
                self.deflate_map()
                self.switch_mode(Mode.IDLE)
            return
        rospy.loginfo("Planning Succeeded")

        planned_path = problem.path

        # Check whether path is too short
        if self.at_goal():
            #check the stop flag and don't post 'at_goal' if set (or else Squirtle will get confused at stop signs)
            if not self.stop_flag:
                rospy.loginfo("Path already at goal pose")
                #tell squirtle we are at the goal
                msg = String()
                msg.data = "at_goal"
                self.publish_squirtle.publish(msg)
            self.switch_mode(Mode.IDLE)
            return
        elif len(planned_path) < 4:
            rospy.loginfo("Path too short to track")
            self.switch_mode(Mode.PARK)
            return

        # Smooth and generate a trajectory
        traj_new, t_new = compute_smoothed_traj(planned_path, self.v_des,
                                                self.spline_alpha,
                                                self.traj_dt)

        # If currently tracking a trajectory, check whether new trajectory will take more time to follow
        if self.mode == Mode.TRACK:
            t_remaining_curr = self.current_plan_duration - self.get_current_plan_time(
            )

            # Estimate duration of new trajectory
            th_init_new = traj_new[0, 2]
            th_err = wrapToPi(th_init_new - self.theta)
            t_init_align = abs(th_err / self.om_max)
            t_remaining_new = t_init_align + t_new[-1]

            if t_remaining_new > t_remaining_curr:
                rospy.loginfo(
                    "New plan rejected (longer duration than current plan)")
                self.publish_smoothed_path(traj_new,
                                           self.nav_smoothed_path_rej_pub)
                return

        # Otherwise follow the new plan
        self.publish_planned_path(planned_path, self.nav_planned_path_pub)
        self.publish_smoothed_path(traj_new, self.nav_smoothed_path_pub)

        self.pose_controller.load_goal(self.x_g, self.y_g, self.theta_g)
        self.traj_controller.load_traj(t_new, traj_new)

        self.current_plan_start_time = rospy.get_rostime()
        self.current_plan_duration = t_new[-1]

        self.th_init = traj_new[0, 2]
        self.heading_controller.load_goal(self.th_init)

        if not self.aligned():
            rospy.loginfo("Not aligned with start direction")
            if self.mode == Mode.BACKING_UP:
                self.switch_mode(Mode.INFLATE_ALIGN)
            else:
                self.switch_mode(Mode.ALIGN)
            return

        if self.mode == Mode.BACKING_UP:  #what happens if we want to nav to new goal here...?
            self.start_timer(INFLATE_TIME)
            self.switch_mode(Mode.INFLATE)
        else:
            rospy.loginfo("Ready to track")
            self.switch_mode(Mode.TRACK)
示例#30
0
    torch.set_default_tensor_type('torch.FloatTensor')

n_link = 2
dt = 0.01

# Target
target = [np.pi, np.pi]
target = np.array(target)[np.newaxis, :]

# Training data
train_data = np.load(os.path.join(args.dataset, 'train.npy'),
                     allow_pickle=True).item()

train_X = train_data['states']
train_X = train_X.reshape(-1, 2 * n_link)
train_X[:, :n_link] = wrapToPi(train_X[:, :n_link] - target)

train_y = train_data['grads']
train_y = train_y.reshape(-1, 2 * n_link)

train_y_forced = train_data['grads_forced']
train_y_forced = train_y_forced.reshape(-1, 2 * n_link)

train_u = train_data['forces']
train_u = train_u.reshape(-1, n_link)

Xscale = np.max(np.abs(train_X), axis=0, keepdims=True)
yscale = np.max(np.abs(train_y), axis=0, keepdims=True)
yfscale = np.max(np.abs(train_y_forced), axis=0, keepdims=True)
yscale = np.maximum(yscale, yfscale)