예제 #1
0
class ParticleFilterNode(object):
    """ The class that represents a Particle Filter ROS Node
    """
    def __init__(self):
        rospy.init_node('pf')

        real_robot = False
        # create instances of two helper objects that are provided to you
        # as part of the project
        self.particle_filter = ParticleFilter()
        self.occupancy_field = OccupancyField()
        self.TFHelper = TFHelper()
        self.sensor_model = sensor_model = SensorModel(
            model_noise_rate=0.5,
            odometry_noise_rate=0.15,
            world_model=self.occupancy_field,
            TFHelper=self.TFHelper)

        self.position_delta = None  # Pose, delta from current to previous odometry reading
        self.last_scan = None  # list of ranges
        self.odom = None  # Pose, current odometry reading

        self.x_y_spread = 0.3  # Spread constant for x-y initialization of particles
        self.z_spread = 0.2  # Spread constant for z initialization of particles

        self.n_particles = 150  # number of particles

        # pose_listener responds to selection of a new approximate robot
        # location (for instance using rviz)
        rospy.Subscriber("initialpose", PoseWithCovarianceStamped,
                         self.update_initial_pose)

        rospy.Subscriber("odom", Odometry, self.update_position)
        rospy.Subscriber("stable_scan", LaserScan, self.update_scan)

        # publisher for the particle cloud for visualizing in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)

    def update_scan(self, msg):
        """Updates the scan to the most recent reading"""
        self.last_scan = [(i, msg.ranges[i]) for i in range(len(msg.ranges))]

    def update_position(self, msg):
        """Calculate delta in position since last odometry reading, update current odometry reading"""
        self.position_delta = Vector3()

        this_pos = self.TFHelper.convert_pose_to_xy_and_theta(msg.pose.pose)
        if self.odom is not None:
            prev_pos = self.TFHelper.convert_pose_to_xy_and_theta(self.odom)
            self.position_delta.x = this_pos.x - prev_pos.x
            self.position_delta.y = this_pos.y - prev_pos.y
            self.position_delta.z = self.TFHelper.angle_diff(
                this_pos.z, prev_pos.z)
        else:
            self.position_delta = this_pos

        self.odom = msg.pose.pose

        self.particle_filter.predict(self.position_delta)

    def reinitialize_particles(self, initial_pose):
        """Reinitialize particles when a new initial pose is given."""
        self.particle_filter.particles = []
        for i in range(self.n_particles):
            pos = Vector3()

            initial_pose_trans = self.TFHelper.convert_pose_to_xy_and_theta(
                initial_pose)

            pos.x = initial_pose_trans.x + (2 * randn() - 1) * self.x_y_spread
            pos.y = initial_pose_trans.y + (2 * randn() - 1) * self.x_y_spread
            pos.z = initial_pose_trans.z + (2 * randn() - 1) * self.z_spread

            new_particle = Particle(position=pos,
                                    weight=1 / float(self.n_particles),
                                    sensor_model=self.sensor_model)
            self.particle_filter.add_particle(new_particle)

    def update_initial_pose(self, msg):
        """ Callback function to handle re-initializing the particle filter
            based on a pose estimate.  These pose estimates could be generated
            by another ROS Node or could come from the rviz GUI """
        self.reinitialize_particles(msg.pose.pose)

        self.TFHelper.fix_map_to_odom_transform(msg.pose.pose,
                                                msg.header.stamp)

    def publish_particles(self):
        """ Extract position from each particle, transform into pose, and publish them as PoseArray"""
        pose_array = PoseArray()
        for p in self.particle_filter.particles:
            pose_array.poses.append(
                self.TFHelper.convert_vector3_to_pose(p.position))
        pose_array.header.frame_id = "map"
        self.particle_pub.publish(pose_array)

    def run(self):
        r = rospy.Rate(5)

        while not (rospy.is_shutdown()):
            # in the main loop all we do is continuously broadcast the latest
            # map to odom transform
            self.TFHelper.send_last_map_to_odom_transform()
            if len(self.particle_filter.particles) > 0:
                if self.last_scan != None:
                    self.particle_filter.integrate_observation(self.last_scan)
                    self.last_scan = None
                    self.particle_filter.normalize()
                    self.publish_particles()
                    self.particle_filter.resample()
            r.sleep()