Ejemplo n.º 1
0
    def __init__(self):
        rospy.init_node('error_validation_node')
        # Initialize subscribers to sensors and motors
        rospy.Subscriber('/scan', LaserScan, self.read_sensor)

        # Initialize publishers for visualization
        self.error_markers_pub = rospy.Publisher('/error_markers',
                                                 MarkerArray,
                                                 queue_size=10)
        self.odom_pose_pub = rospy.Publisher('odom_particle_pose',
                                             Pose,
                                             queue_size=10)

        self.latest_scan_ranges = []

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

        # Class initializations
        self.map_model = MapModel()
        self.tf_helper = TFHelper()
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()
        """for static error validation"""
        self.static_particle = Particle(x=0, y=0, theta=0, weight=1)
        self.sample_ranges = np.ones(361)
        self.predicted_obstacle_x = 0.0
        self.predicted_obstacle_y = 0.0
Ejemplo n.º 2
0
    def __init__(self):
        rospy.init_node('pf')

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.occupancy_field = OccupancyField()
        self.tfh = TFHelper()
        self.ros_boss = RosBoss()

        # publisher for the particle cloud for visualizing in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        # pose_listener responds to selection of a new approximate robot
        # location (for instance using rviz)
        rospy.Subscriber("/odom", Odometry, self.update_initial_pose)

        self.current_particles = {}
        self.max_particle_number = 100
        self.map_width = self.occupancy_field.map.info.width
        self.map_height = self.occupancy_field.map.info.height
        self.map_origin = (self.occupancy_field.map.info.origin.position.x,
                           self.occupancy_field.map.info.origin.position.y)
        self.map_resolution = self.occupancy_field.map.info.resolution
        self.particle_viz = ParticlesMarker()

        self.last_time = time.time()
Ejemplo n.º 3
0
    def __init__(self):
        rospy.init_node('pf_node')

        # Initialize subscribers to sensors and motors
        rospy.Subscriber('/scan', LaserScan, self.read_sensor)
        # Initialize publishers for visualization
        self.particle_pose_pub = rospy.Publisher('/particle_pose_array',
                                                 PoseArray,
                                                 queue_size=10)
        self.odom_pose_pub = rospy.Publisher('odom_pose',
                                             PoseArray,
                                             queue_size=10)
        self.map_marker_pub = rospy.Publisher('/map_marker',
                                              Marker,
                                              queue_size=10)
        # self.particle_obstacles_pub = rospy.Publisher('/particle_obstacles', Marker, queue_size=10)

        self.latest_scan_ranges = []

        # Class initializations
        self.p_distrib = ParticleDistribution()
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()
        self.map_model = MapModel()
        self.tf_helper = TFHelper()

        # When to run the particle filter
        self.distance_moved_threshold = 0.2  # m
        self.angle_turned_threshold = 10  # deg

        # After map model has been initialized, create the initial particle distribution
        self.p_distrib.init_particles(self.map_model)
        self.particle_pose_pub.publish(
            self.p_distrib.get_particle_pose_array())
Ejemplo n.º 4
0
    def __init__(self):
        rospy.init_node("propagation_test_node")

        self.tf_helper = TFHelper()

        self.base_link_pose = PoseStamped()
        self.base_link_pose.header.frame_id = "base_link"
        self.base_link_pose.header.stamp = rospy.Time(0)

        self.last_odom_pose = PoseStamped()
        self.last_odom_pose.header.frame_id = "odom"
        self.last_odom_pose.header.stamp = rospy.Time(0)

        self.particle_pose_pub = rospy.Publisher('/particle_pose_array',
                                                 PoseArray,
                                                 queue_size=10)
        self.odom_pose_pub = rospy.Publisher('/odom_pose',
                                             PoseArray,
                                             queue_size=10)

        self.pose_array = PoseArray()
        self.pose_array.header.stamp = rospy.Time(0)
        self.pose_array.header.frame_id = "odom"

        self.p = Particle(x=0, y=0, theta=180, weight=0)
        self.p_array = PoseArray()
        self.p_array.header.stamp = rospy.Time(0)
        self.p_array.header.frame_id = "map"

        self.is_first = True
Ejemplo n.º 5
0
 def __init__(self):
     rospy.init_node('calc_pos_node')
     self.pos_change_pub = rospy.Publisher('/pos_change',
                                           Vector3,
                                           queue_size=10)
     self.prev_pos = Vector3(0, 0,
                             0)  # previous odometry of position and angle
     self.prev_time = time.time()
     self.transform_helper = TFHelper()
Ejemplo n.º 6
0
    def __init__(self):
    	self.transform_helper = TFHelper()
    	self.odom_sub = rospy.Subscriber('odom', Odometry, self.odom_callback)

        self.odom_pos = None
        self.odom_ori = None
        self.odom_header = None

        self.robot_xyyaw_pose = None
Ejemplo n.º 7
0
    def __init__(self):
        self.initialized = False        # make sure we don't perform updates before everything is setup
        rospy.init_node('pf')           # tell roscore that we are creating a new node named "pf"

        self.base_frame = "base_link"   # the frame of the robot base
        self.map_frame = "map"          # the name of the map coordinate frame
        self.odom_frame = "odom"        # the name of the odometry coordinate frame
        self.scan_topic = "scan"        # the topic where we will get laser scans from

        self.n_particles = 300                # the number of particles to use
        self.initial_uncertainty_xy = 1       # Amplitute factor of initial x and y uncertainty
        self.initial_uncertainty_theta = 0.5  # Amplitude factor of initial theta uncertainty
        self.variance_scale = 0.15             # Scaling term for variance effect on resampling
        self.n_particles_average = 20          # Number of particles to average for pose update
        self.linear_var_thresh = 0.05           # Maximum confidence along x/y (meters)
        self.angular_var_thresh = 0.2          # Maximum confidence along theta (radians)
        # self.resample_noise_xy = 0.1          # Amplitude factor of resample x and y noise
        # self.resample_noise_theta = 0.1       # Amplitude factor of resample theta noise

        self.d_thresh = 0.2             # the amount of linear movement before performing an update
        self.a_thresh = math.pi/6       # the amount of angular movement before performing an update

        self.laser_max_distance = 2.0   # maximum penalty to assess in the likelihood field model

        # TODO: define additional constants if needed

        # Setup pubs and subs

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

        # publish the current particle cloud.  This enables viewing particles in rviz.
        self.particle_pub = rospy.Publisher("particlecloud", PoseArray, queue_size=10)
        # publish custom particle array messge type
        self.particle_viz_pub = rospy.Publisher("weighted_particlecloud", ParticleArray, queue_size=10)

        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_received)

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        self.particle_cloud = []

        # change use_projected_stable_scan to True to use point clouds instead of laser scans
        self.use_projected_stable_scan = False
        self.last_projected_stable_scan = None
        if self.use_projected_stable_scan:
            # subscriber to the odom point cloud
            rospy.Subscriber("projected_stable_scan", PointCloud, self.projected_scan_received)

        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.initialized = True
Ejemplo n.º 8
0
    def __init__(self):
        self.initialized = False  # make sure we don't perform updates before everything is setup
        rospy.init_node(
            'pf')  # tell roscore that we are creating a new node named "pf"

        self.base_frame = "base_link"  # the frame of the robot base
        self.map_frame = "map"  # the name of the map coordinate frame
        self.odom_frame = "odom"  # the name of the odometry coordinate frame
        self.scan_topic = "scan"  # the topic where we will get laser scans from

        self.n_particles = 300  # the number of particles to use

        self.d_thresh = 0.2  # the amount of linear movement before performing an update
        self.a_thresh = math.pi / 6  # the amount of angular movement before performing an update

        self.laser_max_distance = 2.0  # maximum penalty to assess in the likelihood field model

        # Setup pubs and subs

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

        # publish the current particle cloud.  This enables viewing particles in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        self.weight_pub = rospy.Publisher('visualization_marker',
                                          MarkerArray,
                                          queue_size=10)

        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_received)

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        # Holds all particles
        self.particle_cloud = []
        # Holds pre-normalized probabilities for each particle
        self.scan_probabilities = []

        # change use_projected_stable_scan to True to use point clouds instead of laser scans
        self.use_projected_stable_scan = False
        self.last_projected_stable_scan = None
        if self.use_projected_stable_scan:
            # subscriber to the odom point cloud
            rospy.Subscriber("projected_stable_scan", PointCloud,
                             self.projected_scan_received)

        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.initialized = True
Ejemplo n.º 9
0
    def __init__(self, waypoints):
        rospy.init_node('contourBot')

        # parameters related to waypoints
        self.waypoints = waypoints
        self.reach_first_point = False
        self.angle_offset = 0.005
        self.distance_offset = 0.1

        # current position of robot
        self.odom_pose = None
        self.cur_pose_x = 0
        self.cur_pose_y = 0
        self.cur_pose_yaw = 0

        # next waypoint robot tries to go to
        self.last_waypoint = [0, 0]
        self.waypoint_pose_x = 0
        self.waypoint_pose_y = 0
        self.waypoint_pose_theta = 0
        self.finish_trace = False

        # robot motion parameters
        self.angle_diff = math.inf
        self.distance_to_waypoint = math.inf
        self.velocity = Twist()
        self.k_linear_vel = 1.5
        self.k_angular_vel = 1.5

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()
        self.transform_helper = TFHelper()

        # coordinate frames
        self.base_frame = "base_link"
        self.odom_frame = "odom"

        # set up pubs and subs
        # rospy.Subscriber("/odom", Pose, self.update_robot_pose)
        self.vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=10)
        self.waypoints_marker_pub = rospy.Publisher('visualization_marker', Marker, queue_size=10)

        # visualization parameters
        self.waypoints_marker = Marker()
        self.waypoint_marker_line_color = ColorRGBA()
        self.waypoint_marker_line_color.r = 0
        self.waypoint_marker_line_color.g = 1.0
        self.waypoint_marker_line_color.b = 0
        self.waypoint_marker_line_color.a = 1.0

        self.initialize_waypoints_as_markers()
Ejemplo n.º 10
0
 def __init__(self):
     rospy.init_node('pf')
     self.particle_publisher = rospy.Publisher("particlecloud",
                                               PoseArray,
                                               queue_size=10)
     self.occupancy_field = OccupancyField()
     self.transform_helper = TFHelper()
     self.particle_manager = ParticleManager()
     self.sensor_manager = SensorManager()
     self.particle_manager.init_particles(self.occupancy_field)
     self.scanDistance = 0.2
     self.scanAngle = 0.5
     self.moved = (0, 0)
Ejemplo n.º 11
0
    def __init__(self):
        # Initialize node and attributes
        rospy.init_node("ParticleFilter")

        # Subscribers
        self.lidar_sub = rospy.Subscriber("/scan", LaserScan,
                                          self.lidar_callback)
        self.odom_sub = rospy.Subscriber("/odom", Odometry, self.odom_callback)
        self.initial_estimate_sub = rospy.Subscriber(
            "/initialpose", PoseWithCovarianceStamped,
            self.pose_estimate_callback)

        # publishers
        self.all_particles_pub = rospy.Publisher("/visualization_particles",
                                                 MarkerArray,
                                                 queue_size=10)
        self.init_particles_pub = rospy.Publisher("/visualization_init",
                                                  MarkerArray,
                                                  queue_size=10)
        self.new_particles_pub = rospy.Publisher("/particlecloud",
                                                 PoseArray,
                                                 queue_size=10)

        # constants
        self.number_of_particles = 30
        pos_std_dev = 0.25
        ori_std_dev = 25 * math.pi / 180
        self.initial_std_dev = np.array(
            [[pos_std_dev, pos_std_dev, ori_std_dev]]).T
        self.lidar_std_dev = 0.02
        self.resample_threshold = 0.1

        # changing attributes
        self.particles = np.ones([3, self.number_of_particles], dtype=np.float)
        self.weights = np.ones(self.number_of_particles, dtype=np.float)
        self.odom_tf_time = 0
        self.base_tf_time = 0
        self.scan = None
        self.prev_pose = None
        self.delta_pose = None
        self.initial_pose_estimate = None
        self.pose = None

        # helper classes
        self.occupancy_field = OccupancyField()
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()
        self.transform_helper = TFHelper()

        rospy.loginfo("Initialized")
Ejemplo n.º 12
0
    def __init__(self):
        # pose_listener responds to selection of a new approximate robot
        # location (for instance using rviz)

        # publisher for the particle cloud for visualizing in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        # create instances of two helper objects that are provided to you
        # as part of the project
        self.num_particles = 500  # # of particles to use
        self.odom_pose = PoseStamped()
        self.transform_helper = TFHelper()
        self.particle_cloud = []
Ejemplo n.º 13
0
class CalcPosNode(object):
    def __init__(self):
        rospy.init_node('calc_pos_node')
        self.pos_change_pub = rospy.Publisher('/pos_change',
                                              Vector3,
                                              queue_size=10)
        self.prev_pos = Vector3(0, 0,
                                0)  # previous odometry of position and angle
        self.prev_time = time.time()
        self.transform_helper = TFHelper()

    def odometry_callback(self, msg):
        # operate on the msg
        # set self.prev_pos to the last used position
        if time.time() - self.prev_time < TIME_THROTTLE:
            return
        self.prev_time = time.time()
        # storing and working with absolute odometry, publishing relative changes
        transformed_pos = self.transform_helper.convert_pose_to_xy_and_theta(
            msg.pose.pose)  # transform to x,y,yaw
        new_pos = Vector3(transformed_pos[0], transformed_pos[1],
                          math.degrees(transformed_pos[2]))
        self.pos_change_pub.publish(new_pos.x - self.prev_pos.x,
                                    new_pos.y - self.prev_pos.y,
                                    (new_pos.z - self.prev_pos.z) % 360)
        self.prev_pos = new_pos

    def run(self):
        # read in the data (odometry)
        # calculate the change in position (difference between current and last position)
        rospy.Subscriber('/odom', Odometry, self.odometry_callback)
        while not rospy.is_shutdown():
            rospy.spin()
Ejemplo n.º 14
0
class ParticleFilter(object):
    """ The class that represents a Particle Filter ROS Node
    """
    def __init__(self):
        # pose_listener responds to selection of a new approximate robot
        # location (for instance using rviz)

        # publisher for the particle cloud for visualizing in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        # create instances of two helper objects that are provided to you
        # as part of the project
        self.num_particles = 500  # # of particles to use
        self.odom_pose = PoseStamped()
        self.transform_helper = TFHelper()
        self.particle_cloud = []

    def particle_cloud_init(self, xy_theta=None):
        '''
        Initialize the particle cloud
        xy_theta: a triple consisting of the mean x, y, and theta (yaw) to initialize the
                particle cloud around. If None, odometry will be used instead
        '''
        #add some noise to our particle cloud
        linear_noise = 1.0
        angular_noise = math.pi / 2.0

        # Use odom if there's no xy_theta
        if xy_theta == None:
            xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
                self.odom_pose.pose)

        self.particle_cloud = []
        for x in range(self.num_particles):
            x = xy_theta[0] + (random_sample() * linear_noise -
                               (linear_noise / 2.0))
            y = xy_theta[1] + (random_sample() * linear_noise -
                               (linear_noise / 2.0))
            theta = xy_theta[2] + (random_sample() * angular_noise -
                                   (angular_noise / 2.0))
            particles = Particle(x, y, theta)
            self.particle_cloud.append(particles)

        self.particle_normalizer()

    def particle_normalizer(self):
        '''Make sure the particle weights sum to 1'''
        weights_sum = sum(particle.w for particle in self.particle_cloud)
        for particle in self.particle_cloud:
            particle.w /= weights_sum

    def particle_publisher(self, msg):
        particles = []
        for p in self.particle_cloud:
            particles.append(p.particle_to_pose())
        self.particle_pub.publish(
            PoseArray(header=Header(stamp=rospy.Time.now(), frame_id="map"),
                      poses=particles))
Ejemplo n.º 15
0
    def __init__(self, top_particle_pub, particle_cloud_pub, particle_cloud_marker_pub):
        # # pose_listener responds to selection of a new approximate robot
        # # location (for instance using rviz)
        # rospy.Subscriber("initialpose",
        #                  PoseWithCovarianceStamped,
        #                  self.update_initial_pose)

        self.top_particle_pub = top_particle_pub
        self.particle_cloud_pub = particle_cloud_pub
        self.particle_cloud_marker_pub = particle_cloud_marker_pub

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.particles = []
        self.markerArray = MarkerArray()
Ejemplo n.º 16
0
    def __init__(self):
        rospy.init_node('pf')

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

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

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
Ejemplo n.º 17
0
	def __init__(self):
		rospy.init_node('motor_model')
		self.pub = rospy.Publisher('/cmd_vel', Twist, queue_size = 10)
		self.pub2 = rospy.Publisher('Robot_marker', Marker, queue_size = 10)
		self.pub3 = rospy.Publisher('visualization_marker_array', MarkerArray, queue_size = 10)
		rospy.Subscriber('/odom', Odometry, self.read_pos)
		self.rate = rospy.Rate(10)
		self.key = None
		self.pose = None   #as a Twist
		self.orientation = None #as a quaternion
		self.tf = TFHelper()
		self.real_pose = None
		self.particle_array = []
		self.num_particles = 15
		self.create_particle_array()
		self.sd_filter = self.num_particles/3.0 #standard deviation for random sampling
		self.sd_position = .1 #standard deviation of position noise
		self.sd_theta = 5/3.0 #standard deviation (bounded by 5 degrees) of theta noise
Ejemplo n.º 18
0
    def __init__(self):
        """
        __init__ function to create main attributes, setup threshold values, setup rosnode subs and pubs
        """
        rospy.init_node('pf')
        self.initialized = False
        self.num_particles = 150
        self.d_thresh = 0.2  # the amount of linear movement before performing an update
        self.a_thresh = math.pi / 6  # the amount of angular movement before performing an update
        self.particle_cloud = []
        self.lidar_points = []
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        self.best_particle_pub = rospy.Publisher("particlebest",
                                                 PoseStamped,
                                                 queue_size=10)
        self.base_frame = "base_link"  # the frame of the robot base
        self.map_frame = "map"  # the name of the map coordinate frame
        self.odom_frame = "odom"  # the name of the odometry coordinate frame
        self.scan_topic = "scan"  # the topic where we will get laser scans from
        self.best_guess = (
            None, None)  # (index of particle with highest weight, its weight)
        self.particles_to_replace = .075
        self.n_effective = 0  # this is a measure of the particle diversity

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

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_received)

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.initialized = True
    def __init__(self):
        print("init RobotLocalizer")
        rospy.init_node('localizer')
        self.tfHelper = TFHelper()

        self.xs = None
        self.ys = None
        self.ranges = []  # Lidar scan

        self.last_odom_msg = None
        print(self.last_odom_msg)
        self.diff_transform = {
            'translation': None,
            'rotation': None,
        }

        self.odom_changed = False  # Toggles to True when the odom frame has changed enough

        # subscribers and publisher
        self.laser_sub = rospy.Subscriber('/scan', LaserScan,
                                          self.process_scan)
        self.odom_sub = rospy.Subscriber("/odom", Odometry, self.update_odom)

        ### Used for the particle filter
        # publisher for the particle cloud for visualizing in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        # publisher for the top weighted particle
        self.topparticle_pub = rospy.Publisher("topparticle",
                                               PoseArray,
                                               queue_size=10)

        # publisher for rviz markers
        self.pub_markers = rospy.Publisher('/visualization_markerarray',
                                           MarkerArray,
                                           queue_size=10)

        self.particle_filter = ParticleFilter(self.topparticle_pub,
                                              self.particle_pub,
                                              self.pub_markers)
        print("ParticleFilter initialized")

        print("RobotLocalizer initialized")
Ejemplo n.º 20
0
    def __init__(self, num_positions=500, num_orientations=10):
        # TODO: Interface with SLAM algorithm's published map
        # Initialize map.
        rospy.init_node(
            "neato_mdp")  # May break if markov_model is also subscribed...?
        rospy.wait_for_service("static_map")
        static_map = rospy.ServiceProxy("static_map", GetMap)
        # Initialize MDP
        self.mdp = MDP(num_positions=num_positions,
                       num_orientations=num_orientations,
                       map=static_map().map)
        self.state_idx = None  # Current state idx is unknown.
        self.curr_odom_pose = Pose()
        self.tf_helper = TFHelper()
        # Velocity publisher
        self.cmd_vel_publisher = rospy.Publisher("/cmd_vel",
                                                 Twist,
                                                 queue_size=10,
                                                 latch=True)
        self.odom_subscriber = rospy.Subscriber('/odom', Odometry,
                                                self.set_odom)
        self.goal_state = None
        # Visualize robot
        self.robot_state_pub = rospy.Publisher('/robot_state_marker',
                                               Marker,
                                               queue_size=10)
        self.robot_state_pose_pub = rospy.Publisher('/robot_state_pose',
                                                    PoseArray,
                                                    queue_size=10)
        self.goal_state_pub = rospy.Publisher('/goal_state_marker',
                                              Marker,
                                              queue_size=10)
        # # pose_listener responds to selection of a new approximate robot location (for instance using rviz)
        #
        self.odom_pose = PoseStamped()
        self.odom_pose.header.stamp = rospy.Time(0)
        self.odom_pose.header.frame_id = 'odom'
        #
        rospy.Subscriber("initialpose", PoseWithCovarianceStamped,
                         self.update_initial_pose)

        rospy.Subscriber("move_base_simple/goal", PoseStamped,
                         self.update_goal_state)
Ejemplo n.º 21
0
class ParticleFilter(object):
    """ The class that represents a Particle Filter ROS Node
    """
    def __init__(self):
        rospy.init_node('pf')

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

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

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()

    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 """
        xy_theta = \
            self.transform_helper.convert_pose_to_xy_and_theta(msg.pose.pose)

        # TODO this should be deleted before posting
        self.transform_helper.fix_map_to_odom_transform(msg.pose.pose,
                                                        msg.header.stamp)
        # initialize your particle filter based on the xy_theta tuple

    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.transform_helper.send_last_map_to_odom_transform()
            r.sleep()
Ejemplo n.º 22
0
    def __init__(self):
        rospy.init_node("sensor_likelihood_test")
        self.occupancy_field = OccupancyField()
        self.tf_helper = TFHelper()

        self.latest_scan_ranges = []
        rospy.Subscriber('/scan', LaserScan, self.read_sensor)

        self.odom_poses = PoseArray()
        self.odom_poses.header.frame_id = "odom"
        self.particle_pose_pub = rospy.Publisher('/particle_pose_array',
                                                 PoseArray,
                                                 queue_size=10)
        self.odom_pose_pub = rospy.Publisher('odom_pose',
                                             PoseArray,
                                             queue_size=10)
        self.marker_pub = rospy.Publisher('/visualization_marker_array',
                                          MarkerArray,
                                          queue_size=10)

        self.p_distrib = ParticleDistribution()
        self.init_particles()
        # self.p = Particle(x=0, y=0, theta=0, weight=1)

        self.particle_poses = PoseArray()
        self.particle_poses.header.frame_id = "map"

        self.last_odom_pose = PoseStamped()
        self.last_odom_pose.header.frame_id = "odom"
        self.last_odom_pose.header.stamp = rospy.Time(0)

        self.base_link_pose = PoseStamped()
        self.base_link_pose.header.frame_id = "base_link"
        self.base_link_pose.header.stamp = rospy.Time(0)
        self.counter = 0
        self.is_first = True

        # pose_listener responds to selection of a new approximate robot
        # location (for instance using rviz)
        rospy.Subscriber("initialpose", PoseWithCovarianceStamped,
                         self.update_initial_pose)
Ejemplo n.º 23
0
 def __init__(self):
     """initialize node, ROS things, etc."""
     rospy.init_node("ParticleFilter")
     rospy.Subscriber('/scan', LaserScan, self.process_scan)
     self.pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
     self.pub2 = rospy.Publisher('Robot_marker', Marker, queue_size=10)
     self.pub3 = rospy.Publisher('visualization_marker_array',
                                 MarkerArray,
                                 queue_size=10)
     self.particle_pub = rospy.Publisher("particle_cloud",
                                         PointCloud,
                                         queue_size=10)
     rospy.Subscriber('/odom', Odometry, self.read_pos)
     self.rate = rospy.Rate(20)
     self.xs_bl = []  # list of xs from lidar in base link frame
     self.ys_bl = []  # list of ys from lidar in base link frame
     self.scanx_now = []
     self.scany_now = []
     self.last_pose = None
     self.pose = None  #as a Twist
     self.orientation = None  #as a quaternion
     self.tf = TFHelper()
     self.real_pose = None
     self.particle_array = []
     self.num_particles = 500  #adjust to increase number of particles
     self.update_rate = .5  #in seconds
     self.sd_filter = self.num_particles * 3.0 / (
         4)  #standard deviation for random sampling
     self.current_pose = (0, 0, 0)
     self.previous_pose = (0, 0, 0)
     self.previous_time = rospy.get_time()
     self.current_time = rospy.get_time()
     self.cloud_array = PointCloud(
     )  #point cloud for laser scan visualization
     self.x_array = []
     self.y_array = []
     self.initialize_pf()
     print('Init complete')
Ejemplo n.º 24
0
    def __init__(self):
        print('Initializing')
        self.initialized = False  # make sure we don't perform updates before everything is setup
        rospy.init_node('localizer')
        self.pf = ParticleFilter()

        self.base_frame = "base_link"  # Robot base frame
        self.map_frame = "map"  # Map coord frame
        self.odom_frame = "odom"  # Odom coord frame
        self.scan_topic = "scan"  # Laser scan topic

        self.linear_threshold = 0.1  # the amount of linear movement before performing an update
        self.angular_threshold = math.pi / 10  # the amount of angular movement before performing an update

        self.max_dist = 2.0  # maximum penalty to assess in the likelihood field model

        self.odom_pose = PoseStamped()
        self.robot_pose = Pose()

        self.robot_pose = Pose()

        self.scan_sub = rospy.Subscriber('/scan', LaserScan, self.process_scan)
        # init pf
        # subscribers and publisher
        self.odom_sub = rospy.Subscriber("/odom", Odometry,
                                         self.odom_particle_updater)
        rospy.Subscriber("initialpose", PoseWithCovarianceStamped,
                         self.pose_updater)
        # enable listening for and broadcasting coord transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.current_odom_xy_theta = []

        print("initialization complete")
        self.initialized = True
Ejemplo n.º 25
0
    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)
Ejemplo n.º 26
0
class RunRobot(object):
    ''' Represents all of the sensor and robot model related operations'''
    def __init__(self):
    	self.transform_helper = TFHelper()
    	self.odom_sub = rospy.Subscriber('odom', Odometry, self.odom_callback)

        self.odom_pos = None
        self.odom_ori = None
        self.odom_header = None

        self.robot_xyyaw_pose = None

    def odom_callback(self, msg):
        self.odom_header = msg.header
        self.odom_pos = msg.pose.pose.position
        self.odom_ori = msg.pose.pose.orientation

    def laserCallback(self, msg):
        ''' Represents all of the logic for handling laser messages'''
        # Set ranges for front, back, left, and right
        front_left = msg.ranges[0:90]
        back_left  = msg.ranges[90:180]
        back_right   = msg.ranges[180:270]
        front_right  = msg.ranges[270:360]

        laser_diff = 0 #set difference between quadrants = 0

        # Calculate difference between left front and back to determine where wall is on left side
        for front_left, back_left in zip(front_left, reversed(back_left)):
            if front_left == 0.0 or back_left == 0.0:
                continue
            laser_diff += front_left - back_left

        # Calculate diff between right front and back to determine where wall is on right side
        for front_right, back_right in zip(front_right, reversed(back_right)):
            if front_right == 0.0 or back_right == 0.0:
                continue
            laser_diff += back_right - front_right

	def robot_position(self):
		'''Represents the position of the robot as a x, y, yaw tuple'''

        pose = self.transform_helper.convert_translation_rotation_to_pose(self.odom_pos, self.odom_ori)
        self.robot_xyyaw_pose = self.transform.helper.convert_pose_to_xy_and_theta(pose)
        return self.robot_xyyaw_pose
Ejemplo n.º 27
0
 def __init__(self):
     rospy.init_node('finite_state')
     self.distance_threshold = 0.8
     self.state = "teleop"
     self.x = 0
     self.y = 0
     self.rotation = 0
     self.linear_error = 0
     self.angular_error = 0
     self.linear_k = 0.1
     self.angular_k = .005
     self.vel_msg = Twist()
     self.vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=10)
     # rospy.Subscriber('stable_scan', LaserScan, self.process_scan)
     rospy.Subscriber('projected_stable_scan', PointCloud2,
                      self.projected_scan_received)
     rospy.Subscriber('odom', Odometry, self.process_odom)
     # Initializing user input
     self.settings = termios.tcgetattr(sys.stdin)
     self.key = None
     self.map_y = []
     self.map_x = []
     self.map_neatox = []
     self.map_neatoy = []
     # enable listening for and broadcasting coordinate transforms
     self.tf_listener = TransformListener()
     self.tf_broadcaster = TransformBroadcaster()
     self.base_frame = "base_link"  # the frame of the robot base
     self.map_frame = "map"  # the name of the map coordinate frame
     self.odom_frame = "odom"  # the name of the odometry coordinate frame
     self.scan_topic = "scan"  # the topic where we will get laser scans from
     self.transform_helper = TFHelper()
     self.initialized = True
     self.moved_flag = False
     self.init_x = None
     self.init_y = None
     self.init_z = None
     self.starting_threshold = 0.1
     self.transform = None
     self.index_saved = 0
     self.old_scan = []
     self.new_scan = []
     self.data = {"odom": [], "scans": [], "closures": []}
Ejemplo n.º 28
0
class ParticleFilter:
    """ The class that represents a Particle Filter ROS Node
        Attributes list:
            initialized: a Boolean flag to communicate to other class methods that initializaiton is complete
            base_frame: the name of the robot base coordinate frame (should be "base_link" for most robots)
            map_frame: the name of the map coordinate frame (should be "map" in most cases)
            odom_frame: the name of the odometry coordinate frame (should be "odom" in most cases)
            scan_topic: the name of the scan topic to listen to (should be "scan" in most cases)
            start_particles: the number of particles first initalized
            end_particles: the number of particles which end in the filter
            middle_step: the step at which the number of particles has decayed about 50%
            d_thresh: the amount of linear movement before triggering a filter update
            a_thresh: the amount of angular movement before triggering a filter update
            laser_max_distance: the maximum distance to an obstacle we should use in a likelihood calculation
            pose_listener: a subscriber that listens for new approximate pose estimates (i.e. generated through the rviz GUI)
            particle_pub: a publisher for the particle cloud
            laser_subscriber: listens for new scan data on topic self.scan_topic
            tf_listener: listener for coordinate transforms
            tf_broadcaster: broadcaster for coordinate transforms
            particle_cloud: a list of particles representing a probability distribution over robot poses
            current_odom_xy_theta: the pose of the robot in the odometry frame when the last filter update was performed.
                                   The pose is expressed as a list [x,y,theta] (where theta is the yaw)
            map: the map we will be localizing ourselves in.  The map should be of type nav_msgs/OccupancyGrid
    """
    def __init__(self):
        """ Define a new particle filter

        """
        print("RUNNING")
        self.initialized = False  # make sure we don't perform updates before everything is setup
        self.kidnap = False
        rospy.init_node(
            'pf')  # tell roscore that we are creating a new node named "pf"

        self.base_frame = "base_link"  # the frame of the robot base
        self.map_frame = "map"  # the name of the map coordinate frame
        self.odom_frame = "odom"  # the name of the odometry coordinate frame
        self.scan_topic = "scan"  # the topic where we will get laser scans from

        self.start_particles = 1000  # the number of particles to use
        self.end_particles = 200
        self.resample_count = 10
        self.middle_step = 10

        self.d_thresh = 0.2  # the amount of linear movement before performing an update
        self.a_thresh = math.pi / 6  # the amount of angular movement before performing an update

        self.laser_max_distance = 2.0  # maximum penalty to assess in the likelihood field model

        # Setup pubs and subs

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

        # publish the current particle cloud.  This enables viewing particles in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)

        # publish weights for live graph node
        self.weight_pub = rospy.Publisher("/graph_data",
                                          Float64MultiArray,
                                          queue_size=10)

        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_received)

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        self.particle_cloud = []

        # change use_projected_stable_scan to True to use point clouds instead of laser scans
        self.use_projected_stable_scan = False
        self.last_projected_stable_scan = None
        if self.use_projected_stable_scan:
            # subscriber to the odom point cloud
            rospy.Subscriber("projected_stable_scan", PointCloud,
                             self.projected_scan_received)

        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()

        # publish the marker array
        # self.viz = rospy.Publisher('/particle_marker', Marker, queue_size=10)
        # self.marker = Marker()
        self.viz = rospy.Publisher('/particle_marker',
                                   MarkerArray,
                                   queue_size=10)
        self.markerArray = MarkerArray()

        self.initialized = True

    # assigns robot pose. used only a visual debugger, the real data comes from the bag file.
    def update_robot_pose(self, timestamp):
        #print("Guessing Robot Position")
        self.normalize_particles(self.particle_cloud)
        weights = [p.w for p in self.particle_cloud]
        index_best = weights.index(max(weights))
        best_particle = self.particle_cloud[index_best]

        self.robot_pose = self.transform_helper.covert_xy_and_theta_to_pose(
            best_particle.x, best_particle.y, best_particle.theta)
        self.transform_helper.fix_map_to_odom_transform(
            self.robot_pose, timestamp)

    def projected_scan_received(self, msg):
        self.last_projected_stable_scan = msg

    # deadreckons particles with respect to robot motion.
    def update_particles_with_odom(self, msg):
        """ To apply the robot transformations to a particle, it can be broken down into a rotations, a linear movement, and another rotation (which could equal 0)
        """
        #print("Deadreckoning")
        new_odom_xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            self.odom_pose.pose)
        # compute the change in x,y,theta since our last update
        if self.current_odom_xy_theta:
            old_odom_xy_theta = self.current_odom_xy_theta
            delta = (new_odom_xy_theta[0] - self.current_odom_xy_theta[0],
                     new_odom_xy_theta[1] - self.current_odom_xy_theta[1],
                     new_odom_xy_theta[2] - self.current_odom_xy_theta[2])

            delta_x = delta[0]
            delta_y = delta[1]
            delta_theta = delta[2]

            direction = math.atan2(delta_y, delta_x)
            theta_1 = self.transform_helper.angle_diff(
                direction, self.current_odom_xy_theta[2])

            for p in self.particle_cloud:
                distance = math.sqrt((delta_x**2) +
                                     (delta_y**2)) + np.random.normal(
                                         0, 0.001)
                dx = distance * np.cos(p.theta + theta_1)
                dy = distance * np.sin(p.theta + theta_1)

                p.x += dx
                p.y += dy
                p.theta += delta_theta + np.random.normal(0, 0.005)

            self.current_odom_xy_theta = new_odom_xy_theta
        else:
            self.current_odom_xy_theta = new_odom_xy_theta
            return

    def resample_particles(self):
        """ Resample the particles according to the new particle weights.
            The weights stored with each particle should define the probability that a particular
            particle is selected in the resampling step.  You may want to make use of the given helper
            function draw_random_sample.
        """
        # print("Resampling Particles")
        # make sure the distribution is normalized
        self.normalize_particles(self.particle_cloud)

        particle_cloud_length = len(self.particle_cloud)

        n_particles = ParticleFilter.sigmoid_function(self.resample_count,
                                                      self.start_particles,
                                                      self.end_particles,
                                                      self.middle_step, 1)
        print("Number of Particles Reassigned: " + str(n_particles))

        norm_weights = [p.w for p in self.particle_cloud]
        # print("Weights: "+ str(norm_weights))

        top_percent = 0.20

        ordered_indexes = np.argsort(norm_weights)
        ordered_particles = [
            self.particle_cloud[index] for index in ordered_indexes
        ]
        best_particles = ordered_particles[int(particle_cloud_length *
                                               (1 - top_percent)):]

        new_particles = ParticleFilter.draw_random_sample(
            self.particle_cloud, norm_weights,
            n_particles - int(particle_cloud_length * top_percent))
        dist = 0.001  # adding a square meter of noise around each ideal particle
        self.particle_cloud = []
        self.particle_cloud += best_particles
        for p in new_particles:
            x_pos, y_pos, angle = p.x, p.y, p.theta
            x_particle = np.random.normal(x_pos, dist)
            y_particle = np.random.normal(y_pos, dist)
            theta_particle = np.random.normal(angle, 0.05)
            self.particle_cloud.append(
                Particle(x_particle, y_particle, theta_particle))
        self.normalize_particles(self.particle_cloud)
        self.resample_count += 1

    def update_particles_with_laser(self, msg):
        """ Updates the particle weights in response to the scan contained in the msg """
        #transform laser data from particle's perspective to map coords
        #print("Assigning Weights")
        scan = msg.ranges
        num_particles = len(self.particle_cloud)
        num_scans = 361
        step = 2

        angles = np.arange(num_scans)  # will be scan indices (0-361)
        distances = np.array(scan)  # will be scan values (scan)
        angles_rad = np.deg2rad(angles)

        for p in self.particle_cloud:

            sin_values = np.sin(angles_rad + p.theta)
            cos_values = np.cos(angles_rad + p.theta)
            d_angles_sin = np.multiply(distances, sin_values)
            d_angles_cos = np.multiply(distances, cos_values)

            d_angles_sin = d_angles_sin[0:361:step]
            d_angles_cos = d_angles_cos[0:361:step]

            total_beam_x = np.add(p.x, d_angles_cos)
            total_beam_y = np.add(p.y, d_angles_sin)

            particle_distances = self.occupancy_field.get_closest_obstacle_distance(
                total_beam_x, total_beam_y)

            cleaned_particle_distances = [
                2 * np.exp(-(dist**2)) for dist in particle_distances
                if (math.isnan(dist) != True)
            ]

            p_d_cubed = np.power(cleaned_particle_distances, 3)
            p.w = np.sum(p_d_cubed)

        self.normalize_particles(self.particle_cloud)

    @staticmethod
    def draw_random_sample(choices, probabilities, n):
        """ Return a random sample of n elements from the set choices with the specified probabilities
            choices: the values to sample from represented as a list
            probabilities: the probability of selecting each element in choices represented as a list
            n: the number of samples
        """
        values = np.array(range(len(choices)))
        probs = np.array(probabilities)
        bins = np.add.accumulate(probs)
        inds = values[np.digitize(random_sample(n), bins)]
        samples = []
        for i in inds:
            samples.append(deepcopy(choices[int(i)]))
        return samples

    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 """
        #print("Initial Pose Set")
        xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            msg.pose.pose)
        self.initialize_particle_cloud(msg.header.stamp, xy_theta)

    def initialize_particle_cloud(self, timestamp, xy_theta=None):
        """ 
        Initialize the particle cloud.
        Arguments
        xy_theta: a triple consisting of the mean x, y, and theta (yaw) to initialize the
                    particle cloud around.  If this input is omitted, the odometry will be used 
        Also check to see if we are attempting the robot kidnapping problem or are given an initial 2D pose
        """

        if self.kidnap:
            print("Kidnap Problem")
            x_bound, y_bound = self.occupancy_field.get_obstacle_bounding_box()

            x_particle = np.random.uniform(x_bound[0],
                                           x_bound[1],
                                           size=self.start_particles)
            y_particle = np.random.uniform(y_bound[0],
                                           y_bound[1],
                                           size=self.start_particles)
            theta_particle = np.deg2rad(
                np.random.randint(0, 360, size=self.start_particles))

        else:
            print("Starting with Inital Position")
            if xy_theta is None:
                print("No Position Definied")
                xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
                    self.odom_pose.pose)
            x, y, theta = xy_theta

            x_particle = np.random.normal(x, 0.25, size=self.start_particles)
            y_particle = np.random.normal(y, 0.25, size=self.start_particles)
            theta_particle = np.random.normal(theta,
                                              0.001,
                                              size=self.start_particles)

        self.particle_cloud = [Particle(x_particle[i],\
                                        y_particle[i],\
                                        theta_particle[i]) \
                                for i in range(self.start_particles)]

    def normalize_particles(self, particle_list):
        """ Make sure the particle weights define a valid distribution (i.e. sum to 1.0) """
        #print("Normalize Particles")
        old_weights = [p.w for p in particle_list]
        new_weights = []
        for p in particle_list:
            p.w = float(p.w) / sum(old_weights)
            new_weights.append(p.w)
        float_array = Float64MultiArray()
        float_array.data = new_weights
        self.weight_pub.publish(float_array)

    def publish_particles(self, msg):
        """
        Publishes particle poses on the map.
        Uses Paul's default code at the moment, maybe later attempt to publish a visualization/MarkerArray
        """

        particles_conv = []

        for num, p in enumerate(self.particle_cloud):
            particles_conv.append(p.as_pose())

        self.particle_pub.publish(
            PoseArray(header=Header(stamp=rospy.Time.now(),
                                    frame_id=self.map_frame),
                      poses=particles_conv))

        # self.marker_update("map", self.particle_cloud, False)
        # self.viz.publish()

    def scan_received(self, msg):
        """ 
        All control flow happens here!
        Special init case then goes into loop
        """

        if not (self.initialized):
            # wait for initialization to complete
            return

        # wait a little while to see if the transform becomes available.  This fixes a race
        # condition where the scan would arrive a little bit before the odom to base_link transform
        # was updated.
        # self.tf_listener.waitForTransform(self.base_frame, msg.header.frame_id, msg.header.stamp, rospy.Duration(0.5))
        if not (self.tf_listener.canTransform(
                self.base_frame, msg.header.frame_id, msg.header.stamp)):
            # need to know how to transform the laser to the base frame
            # this will be given by either Gazebo or neato_node
            return

        if not (self.tf_listener.canTransform(self.base_frame, self.odom_frame,
                                              msg.header.stamp)):
            # need to know how to transform between base and odometric frames
            # this will eventually be published by either Gazebo or neato_node
            return

        # calculate pose of laser relative to the robot base
        p = PoseStamped(
            header=Header(stamp=rospy.Time(0), frame_id=msg.header.frame_id))
        self.laser_pose = self.tf_listener.transformPose(self.base_frame, p)

        # find out where the robot thinks it is based on its odometry
        p = PoseStamped(header=Header(stamp=msg.header.stamp,
                                      frame_id=self.base_frame),
                        pose=Pose())
        self.odom_pose = self.tf_listener.transformPose(self.odom_frame, p)
        # store the the odometry pose in a more convenient format (x,y,theta)
        new_odom_xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            self.odom_pose.pose)
        if not self.current_odom_xy_theta:
            self.current_odom_xy_theta = new_odom_xy_theta
            return

        if not (self.particle_cloud):
            print("Particle Cloud Empty")
            # now that we have all of the necessary transforms we can update the particle cloud
            self.initialize_particle_cloud(msg.header.stamp)
            self.update_particles_with_laser(msg)
            self.normalize_particles(self.particle_cloud)
            self.update_robot_pose(msg.header.stamp)
            self.resample_particles()
        elif (math.fabs(new_odom_xy_theta[0] - self.current_odom_xy_theta[0]) >
              self.d_thresh
              or math.fabs(new_odom_xy_theta[1] -
                           self.current_odom_xy_theta[1]) > self.d_thresh
              or math.fabs(new_odom_xy_theta[2] -
                           self.current_odom_xy_theta[2]) > self.a_thresh):
            # we have moved far enough to do an update!
            print("UPDATING PARTICLES")
            self.update_particles_with_odom(msg)  # update based on odometry
            if self.last_projected_stable_scan:
                last_projected_scan_timeshift = deepcopy(
                    self.last_projected_stable_scan)
                last_projected_scan_timeshift.header.stamp = msg.header.stamp
                self.scan_in_base_link = self.tf_listener.transformPointCloud(
                    "base_link", last_projected_scan_timeshift)

            self.update_particles_with_laser(msg)  # update based on laser scan
            self.update_robot_pose(msg.header.stamp)  # update robot's pose
            self.resample_particles(
            )  # resample particles to focus on areas of high density

        # publish particles (so things like rviz can see them)
        self.publish_particles(msg)

    def marker_update(self, frame_id, p_cloud, delete):
        num = 0
        if (delete):
            self.markerArray.markers = []
        else:
            for p in p_cloud:
                marker = Marker()
                marker.header.frame_id = frame_id
                marker.header.stamp = rospy.Time.now()
                marker.ns = "my_namespace"
                marker.id = num
                marker.type = Marker.ARROW
                marker.action = Marker.ADD
                marker.pose = p.as_pose()
                marker.pose.position.z = 0.5
                marker.scale.x = 1.0
                marker.scale.y = 0.1
                marker.scale.z = 0.1
                marker.color.a = 1.0  # Don't forget to set the alpha!
                marker.color.r = 1.0
                marker.color.g = 0.0
                marker.color.b = 0.0

                num += 1

                self.markerArray.markers.append(marker)

    @staticmethod
    def sigmoid_function(value, max_output, min_output, middle, inc=1):
        particle_difference = max_output - min_output
        exponent = inc * (value - (middle / 2))
        return int(particle_difference / (1 + np.exp(exponent)) + min_output)
Ejemplo n.º 29
0
class ParticleFilter(object):
    """
    Class to represent Particle Filter ROS Node
    Subscribes to /initialpose for initial pose estimate
    Publishes top particle estimate to /particlebest and all particles in cloud to /particlecloud
    """
    def __init__(self):
        """
        __init__ function to create main attributes, setup threshold values, setup rosnode subs and pubs
        """
        rospy.init_node('pf')
        self.initialized = False
        self.num_particles = 150
        self.d_thresh = 0.2  # the amount of linear movement before performing an update
        self.a_thresh = math.pi / 6  # the amount of angular movement before performing an update
        self.particle_cloud = []
        self.lidar_points = []
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)
        self.best_particle_pub = rospy.Publisher("particlebest",
                                                 PoseStamped,
                                                 queue_size=10)
        self.base_frame = "base_link"  # the frame of the robot base
        self.map_frame = "map"  # the name of the map coordinate frame
        self.odom_frame = "odom"  # the name of the odometry coordinate frame
        self.scan_topic = "scan"  # the topic where we will get laser scans from
        self.best_guess = (
            None, None)  # (index of particle with highest weight, its weight)
        self.particles_to_replace = .075
        self.n_effective = 0  # this is a measure of the particle diversity

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

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_received)

        # create instances of two helper objects that are provided to you
        # as part of the project
        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.initialized = True

    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 """
        xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            msg.pose.pose)
        print("xy_theta", xy_theta)
        self.initialize_particle_cloud(
            msg.header.stamp,
            xy_theta)  # creates particle cloud at position passed in
        # by message
        print("INITIALIZING POSE")

        # Use the helper functions to fix the transform
    def initialize_particle_cloud(self, timestamp, xy_theta):
        """
        Creates initial particle cloud based on robot pose estimate position
        """
        self.particle_cloud = []
        angle_variance = math.pi / 10  # POint the points in the general direction of the robot
        x_cur = xy_theta[0]
        y_cur = xy_theta[1]
        theta_cur = self.transform_helper.angle_normalize(xy_theta[2])
        # print("theta_cur: ", theta_cur)
        for i in range(self.num_particles):
            # Generate values for and add a new particle!!
            x_rel = random.uniform(-.3, .3)
            y_rel = random.uniform(-.3, .3)
            new_theta = (random.uniform(theta_cur - angle_variance,
                                        theta_cur + angle_variance))
            # TODO: Could use a tf transform to add x and y in the robot's coordinate system
            new_particle = Particle(x_cur + x_rel, y_cur + y_rel, new_theta)
            self.particle_cloud.append(new_particle)
        print("Done initializing particles")
        self.normalize_particles()
        # publish particles (so things like rviz can see them)
        self.publish_particles()
        print("normalized correctly")
        self.update_robot_pose(timestamp)
        print("updated robot pose")

    def normalize_particles(self):
        """
        Normalizes particle weights to total but retains weightage
        """
        total_weights = sum([particle.w for particle in self.particle_cloud])
        # if your weights aren't normalized then normalize them
        if total_weights != 1.0:
            for i in self.particle_cloud:
                i.w = i.w / total_weights

    def update_robot_pose(self, timestamp):
        """ Update the estimate of the robot's pose in the map frame given the updated particles.
            There are two logical methods for this:
                (1): compute the mean pose based on all the high weight particles
                (2): compute the most likely pose (i.e. the mode of the distribution)
        """
        # first make sure that the particle weights are normalized
        self.normalize_particles()
        print("Normalized particles in update robot pose")

        # create average pose for robot pose based on entire particle cloud
        average_x = 0
        average_y = 0
        average_theta = 0

        # walk through all particles, calculate weighted average for x, y, z, in particle map.
        for p in self.particle_cloud:
            average_x += p.x * p.w
            average_y += p.y * p.w
            average_theta += p.theta * p.w

        # # create new particle representing weighted average values, pass in Pose to new robot pose
        self.robot_pose = Particle(average_x, average_y,
                                   average_theta).as_pose()

        print(timestamp)
        self.transform_helper.fix_map_to_odom_transform(
            self.robot_pose, timestamp)
        print("Done fixing map to odom")

    def publish_particles(self):
        """
        Publish entire particle cloud as pose array for visualization in RVIZ
        Also publish the top / best particle based on its weight
        """
        # Convert the particles from xy_theta to pose!!
        pose_particle_cloud = []
        for p in self.particle_cloud:
            pose_particle_cloud.append(p.as_pose())
        self.particle_pub.publish(
            PoseArray(header=Header(stamp=rospy.Time.now(),
                                    frame_id=self.map_frame),
                      poses=pose_particle_cloud))

        # doing shit based off best pose
        best_pose_quat = max(self.particle_cloud,
                             key=attrgetter('w')).as_pose()
        #self.best_particle_pub.publish(header=Header(stamp=rospy.Time.now(), frame_id=self.map_frame), pose=best_pose_quat)

    def update_particles_with_odom(self, msg):
        """ Update the particles using the newly given odometry pose.
            The function computes the value delta which is a tuple (x,y,theta)
            that indicates the change in position and angle between the odometry
            when the particles were last updated and the current odometry.
            msg: this is not really needed to implement this, but is here just in case.
        """
        new_odom_xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            self.odom_pose.pose)
        # compute the change in x,y,theta since our last update
        if self.current_odom_xy_theta:
            old_odom_xy_theta = self.current_odom_xy_theta
            delta = (new_odom_xy_theta[0] - self.current_odom_xy_theta[0],
                     new_odom_xy_theta[1] - self.current_odom_xy_theta[1],
                     new_odom_xy_theta[2] - self.current_odom_xy_theta[2])

            self.current_odom_xy_theta = new_odom_xy_theta
        else:
            self.current_odom_xy_theta = new_odom_xy_theta
            return

        # TODO: FIX noise incorporation into movement.

        min_travel = 0.2
        xy_spread = 0.02 / min_travel  # More variance with driving forward
        theta_spread = .005 / min_travel

        random_vals_x = np.random.normal(0, abs(delta[0] * xy_spread),
                                         self.num_particles)
        random_vals_y = np.random.normal(0, abs(delta[1] * xy_spread),
                                         self.num_particles)
        random_vals_theta = np.random.normal(0, abs(delta[2] * theta_spread),
                                             self.num_particles)

        for p_num, p in enumerate(self.particle_cloud):
            # compute phi, or basically the angle from 0 that the particle
            # needs to be moving - phi equals OG diff angle - robot angle + OG partilce angle
            # ADD THE NOISE!!
            noisy_x = (delta[0] + random_vals_x[p_num])
            noisy_y = (delta[1] + random_vals_y[p_num])

            ang_of_dest = math.atan2(noisy_y, noisy_x)
            # calculate angle needed to turn in angle_to_dest
            ang_to_dest = self.transform_helper.angle_diff(
                self.current_odom_xy_theta[2], ang_of_dest)
            d = math.sqrt(noisy_x**2 + noisy_y**2)

            phi = p.theta + ang_to_dest
            p.x += math.cos(phi) * d
            p.y += math.sin(phi) * d
            p.theta += self.transform_helper.angle_normalize(
                delta[2] + random_vals_theta[p_num])

        self.current_odom_xy_theta = new_odom_xy_theta

    def update_particles_with_laser(self, msg):
        """
        calculate particle weights based off laser scan data passed into param
        """
        # print("Updating particles with Laser")
        lidar_points = msg.ranges

        for p_deg, p in enumerate(self.particle_cloud):
            # do we need to compute particle pos in diff frame?
            p.occ_scan_mapped = []  # reset list
            for scan_distance in lidar_points:
                # handle edge case
                if scan_distance == 0.0:
                    continue
                # calc a delta theta and use that to overlay scan data onto the particle headings
                pt_rad = deg2rad(p_deg)
                particle_pt_theta = self.transform_helper.angle_normalize(
                    p.theta + pt_rad)
                particle_pt_x = p.x + math.cos(
                    particle_pt_theta) * scan_distance
                particle_pt_y = p.y + math.sin(
                    particle_pt_theta) * scan_distance
                # calculate distance from every single scan point in particle frame
                occ_value = self.occupancy_field.get_closest_obstacle_distance(
                    particle_pt_x, particle_pt_y)
                # Think about cutting off max penalty if occ_value is too big
                p.occ_scan_mapped.append(occ_value)

            # assign weights based off newly assigned occ_scan_mapped
            # apply gaussian e**-d**2 to every weight, then cube to emphasize
            p.occ_scan_mapped = [(math.e / (d)**2) if (d)**2 != 0 else
                                 (math.e / (d + .01)**2)
                                 for d in p.occ_scan_mapped]
            p.occ_scan_mapped = [d**3 for d in p.occ_scan_mapped]
            p.w = sum(p.occ_scan_mapped)
            #print("Set weight to: ", p.w)
            p.occ_scan_mapped = []
        self.normalize_particles()

    @staticmethod
    def draw_random_sample(choices, probabilities, n):
        """ Return a random sample of n elements from the set choices with the specified probabilities
            choices: the values to sample from represented as a list
            probabilities: the probability of selecting each element in choices represented as a list
            n: the number of samples
        """
        values = np.array(range(len(choices)))
        probs = np.array(probabilities)
        bins = np.add.accumulate(probs)
        inds = values[np.digitize(random_sample(n), bins)]
        samples = []
        for i in inds:
            samples.append(deepcopy(choices[int(i)]))
        return samples

    def resample_particles(self):
        """
        Re initialize particles in self.particle_cloud
        based on preivous weightages.
        """
        weights = [p.w for p in self.particle_cloud]

        # after calculating all particle weights, we want to calc the n_effective
        # self.n_effective = 0
        self.n_effective = 1 / sum(
            [w**2 for w in weights])  # higher is more diversity, so less noise
        print("n_effective: ", self.n_effective)

        temp_particle_cloud = self.draw_random_sample(
            self.particle_cloud, weights,
            int((1 - self.particles_to_replace) * self.num_particles))
        # temp_particle_cloud = self.draw_random_sample(self.particle_cloud, weights, self.num_particles)

        particle_cloud_to_transform = self.draw_random_sample(
            self.particle_cloud, weights, self.num_particles - int(
                (1 - self.particles_to_replace) * self.num_particles))

        # NOISE POLLUTION - larger noise, smaller # particles
        # normal_std_xy = .25
        normal_std_xy = 10 / self.n_effective  # feedback loop? 8,3
        normal_std_theta = 3 / self.n_effective
        # normal_std_theta = math.pi/21
        random_vals_x = np.random.normal(0, normal_std_xy,
                                         len(particle_cloud_to_transform))
        random_vals_y = np.random.normal(0, normal_std_xy,
                                         len(particle_cloud_to_transform))
        random_vals_theta = np.random.normal(0, normal_std_theta,
                                             len(particle_cloud_to_transform))

        for p_num, p in enumerate(
                particle_cloud_to_transform):  # add in noise in x,y, theta
            p.x += random_vals_x[p_num]
            p.y += random_vals_y[p_num]
            p.theta += random_vals_theta[p_num]

        # reset the partilce cloud based on the newly transformed particles
        self.particle_cloud = temp_particle_cloud + particle_cloud_to_transform

    def scan_received(self, msg):
        """
        Callback function for recieving laser scan - should pass data into global scan object
        """
        """ This is the default logic for what to do when processing scan data.
            Feel free to modify this, however, we hope it will provide a good
            guide.  The input msg is an object of type sensor_msgs/LaserScan """
        if not (self.initialized):
            # wait for initialization to complete
            return

        if not (self.tf_listener.canTransform(
                self.base_frame, msg.header.frame_id, msg.header.stamp)):
            # need to know how to transform the laser to the base frame
            # this will be given by either Gazebo or neato_node
            return

        if not (self.tf_listener.canTransform(self.base_frame, self.odom_frame,
                                              msg.header.stamp)):
            # need to know how to transform between base and odometric frames
            # this will eventually be published by either Gazebo or neato_node
            return

        # calculate pose of laser relative to the robot base
        p = PoseStamped(
            header=Header(stamp=rospy.Time(0), frame_id=msg.header.frame_id))
        self.laser_pose = self.tf_listener.transformPose(self.base_frame, p)

        # find out where the robot thinks it is based on its odometry
        p = PoseStamped(header=Header(stamp=msg.header.stamp,
                                      frame_id=self.base_frame),
                        pose=Pose())
        # grab from listener & store the the odometry pose in a more convenient format (x,y,theta)
        self.odom_pose = self.tf_listener.transformPose(self.odom_frame, p)
        new_odom_xy_theta = self.transform_helper.convert_pose_to_xy_and_theta(
            self.odom_pose.pose)
        if not self.current_odom_xy_theta:
            self.current_odom_xy_theta = new_odom_xy_theta
            return

        # Now we've done all calcs, we exit the scan_recieved() method by either initializing a cloud
        if not (self.particle_cloud):
            # now that we have all of the necessary transforms we can update the particle cloud
            # TODO: Where do we get the xy_theta needed for initialize_particle_cloud?
            self.initialize_particle_cloud(msg.header.stamp,
                                           self.current_odom_xy_theta)

        elif (math.fabs(new_odom_xy_theta[0] - self.current_odom_xy_theta[0]) >
              self.d_thresh
              or math.fabs(new_odom_xy_theta[1] -
                           self.current_odom_xy_theta[1]) > self.d_thresh
              or math.fabs(new_odom_xy_theta[2] -
                           self.current_odom_xy_theta[2]) > self.a_thresh):
            # we have moved far enough to do an update!
            self.update_particles_with_odom(msg)
            self.update_particles_with_laser(msg)  # update based on laser scan
            self.update_robot_pose(msg.header.stamp)  # update robot's pose
            self.resample_particles(
            )  # resample particles to focus on areas of high density
        # # publish particles (so things like rviz can see them)
        self.publish_particles()

    def run(self):
        """
        main run loop for rosnode
        """
        r = rospy.Rate(5)
        print("Nathan and Adi ROS Loop code is starting!!!")
        while not (rospy.is_shutdown()):
            # in the main loop all we do is continuously broadcast the latest
            # map to odom transform
            self.transform_helper.send_last_map_to_odom_transform()
            r.sleep()
Ejemplo n.º 30
0
    def __init__(self):
        self.initialized = False  # make sure we don't perform updates before everything is setup
        rospy.init_node(
            'pf')  # tell roscore that we are creating a new node named "pf"

        self.base_frame = "base_link"  # the frame of the robot base
        self.map_frame = "map"  # the name of the map coordinate frame
        self.odom_frame = "odom"  # the name of the odometry coordinate frame
        self.scan_topic = "scan"  # the topic where we will get laser scans from

        self.n_particles = 600  # the number of particles to use
        self.particle_init_options = ParticleInitOptions.UNIFORM_DISTRIBUTION

        self.d_thresh = 0.1  # the amount of linear movement before performing an update
        self.a_thresh = math.pi / 12.0  # the amount of angular movement before performing an update

        self.num_lidar_points = 180  # int from 1 to 360

        # Note: self.laser_max_distance is NOT implemented
        # TODO: Experiment with setting a max acceptable distance for lidar scans
        self.laser_max_distance = 2.0  # maximum penalty to assess in the likelihood field model

        # Setup pubs and subs

        # pose_listener responds to selection of a new approximate robot location (for instance using rviz)
        rospy.Subscriber("initialpose", PoseWithCovarianceStamped,
                         self.update_initial_pose)
        # laser_subscriber listens for data from the lidar
        rospy.Subscriber(self.scan_topic,
                         LaserScan,
                         self.scan_received,
                         queue_size=1)

        # publish the current particle cloud.  This enables viewing particles in rviz.
        self.particle_pub = rospy.Publisher("particlecloud",
                                            PoseArray,
                                            queue_size=10)

        # publish our hypotheses points
        self.hypothesis_pub = rospy.Publisher("hypotheses",
                                              MarkerArray,
                                              queue_size=10)

        # Publish our hypothesis points right before they get udpated through odom
        self.before_odom_hypothesis_pub = rospy.Publisher(
            "before_odom_hypotheses", MarkerArray, queue_size=10)

        # Publish where the hypothesis points will be once they're updated with the odometry
        self.future_hypothesis_pub = rospy.Publisher("future_hypotheses",
                                                     MarkerArray,
                                                     queue_size=10)

        # Publish the lidar scan that pf.py sees
        self.lidar_pub = rospy.Publisher("lidar_visualization",
                                         MarkerArray,
                                         queue_size=1)

        # Publish the lidar scan projected from the first hypothesis
        self.projected_lidar_pub = rospy.Publisher(
            "projected_lidar_visualization", MarkerArray, queue_size=1)

        # enable listening for and broadcasting coordinate transforms
        self.tf_listener = TransformListener()
        self.tf_broadcaster = TransformBroadcaster()

        self.particle_cloud = []

        # change use_projected_stable_scan to True to use point clouds instead of laser scans
        self.use_projected_stable_scan = False
        self.last_projected_stable_scan = None
        if self.use_projected_stable_scan:
            # subscriber to the odom point cloud
            rospy.Subscriber("projected_stable_scan", PointCloud,
                             self.projected_scan_received)

        self.current_odom_xy_theta = []
        self.occupancy_field = OccupancyField()
        self.transform_helper = TFHelper()
        self.initialized = True