예제 #1
0
    def __init__(self):
        # Get parameters
        self.particle_filter_frame = \
                rospy.get_param("~particle_filter_frame")

        # Initialize the models
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()
예제 #2
0
def do_long_range_message(config, robot1, robot2, motion1, motion2):
    """Potentially transfer long range messages between robots.

    Depending on whether the robots are within thresh distance, they will
    exchange long range measurements. The long range measurements are sent in
    the form of LongRangeMeasurement objects.

    Args:
        config: Configuration file.
        robot1: First Robot object.
        robot2: Second Robot object.
        motion1: First RobotMotion object.
        motion2: Second RobotMotion object.
    """

    # If within long range, exchange long range sensor measurements.
    if dist(motion1.pos, motion2.pos) < config['long_thresh']:
        # Get message data from each robot.
        message1to2_data = robot1.get_long_range_message()
        message2to1_data = robot2.get_long_range_message()

        # Create the sensor model, which will be used to make all measurements.
        sm = SensorModel(config)

        # Compute the pairwise sensor measurements between each robot.
        num_sensors = len(config['sensor_parameters'])

        robot1_measurements = [
            sm.get_measurement(motion1, i, motion2) for i in range(num_sensors)
        ]
        robot2_measurements = [
            sm.get_measurement(motion2, i, motion1) for i in range(num_sensors)
        ]

        logging.debug("Robot 1 Measurements: %s", robot1_measurements)
        logging.debug("Robot 2 Measurements: %s", robot2_measurements)

        # Stuff the data into the LongRangeMessages
        message1to2 = LongRangeMessage(message1to2_data, robot2_measurements)
        message2to1 = LongRangeMessage(message2to1_data, robot1_measurements)

        # And then transmit both of the messages.
        # Do it this way to ensure that receiving a message doesn't
        # modify some state inside the robot.
        robot1.receive_long_range_message(message2to1)
        robot2.receive_long_range_message(message1to2)

        # indicate whether these two robots communicated
        return True
    return False
예제 #3
0
def test_hyperparameter():
    np.random.seed(10008)
    map_obj = MapReader('../data/map/wean.dat')
    occupancy_map = map_obj.get_map()

    # generate a random particle, then sent out beams from that location
    # indices = np.where(occupancy_map.flatten() == 0)[0]
    # ind = np.random.choice(indices, 1)[0]
    # y, x = ind // w, ind % w
    # theta = -np.pi / 2
    # angle = np.pi * (40 / 180)
    X = init_particles_freespace(1, occupancy_map)

    sensor = SensorModel(occupancy_map)
    X = X.reshape(1, -1)
    z_t_star = sensor.ray_casting(X[:, :3], num_beams=10)
    print(z_t_star)
    print(z_t_star.max())

    z = np.arange(sensor._max_range + 2).astype(np.float)
    p_hit, p_short, p_max, p_rand = sensor.estimate_density(z, z_t_star[0][5])
    # plot(1, p_hit)
    # plot(2, p_short)
    # plot(3, p_max)
    # plot(4, p_rand)

    # w_hit = 3 # 99 / 2 / 2.5 / 4  # 1.
    # w_short = 0.05 # 2 * 198 / 4 / 2.5 / 4  # 1
    # w_max = 0.1 # 49 / 2.5 / 4  # 0.5
    # w_rand = 10 # 990 / 4  # 5

    # self._z_hit = 99 / 2 / 2.5 / 4  # 1.
    # self._z_short = 198 / 4 // 2.5 / 4  # 1
    # self._z_max = 49 / 2.5 / 4  # 0.5
    # self._z_rand = 990 / 4  # 5

    # w_hit = 1.
    # w_short = 0.1
    # w_max = 0.5
    # w_rand = 10

    w_hit = 1000  # 99 / 2 / 2.5 / 4  # 1.
    w_short = 0.01  # 2 * 198 / 4 / 2.5 / 4  # 1
    w_max = 0.03  # 49 / 4 / 4  # 0.5
    w_rand = 12500

    p = sensor._z_hit * p_hit + sensor._z_short * p_short + sensor._z_max * p_max + sensor._z_rand * p_rand
    plot(5, p)
    plt.show()
예제 #4
0
 def __init__(self, map_file='../data/map/wean.dat'):
     self.map = Map(map_file)
     #self.map.display_gaussian([], 'Gaussian Blurred Map')
     self._number_particles = 1000
     self.particles = list()
     self._particle_probabilities = []
     for _ in range(0, self._number_particles):
         print 'Initializing particle', _
         particle = Particle(self.map)
         #particle.x = 4080 + np.random.normal(scale=35)
         #particle.y = 3980 + np.random.normal(scale=15)
         #particle.theta = math.pi + 0.1 + np.random.normal(scale=.1)
         self.particles.append(particle)
         self._particle_probabilities.append(1)
     self._motion_model = MotionModel(0.001, 0.001, 0.1, 0.1)
     self._sensor_model = SensorModel(self.map)
     self._ranges = []
예제 #5
0
    def __init__(self):
        # Get parameters
        self.particle_filter_frame = \
                rospy.get_param("~particle_filter_frame")

        # Initialize publishers/subscribers
        #
        #  *Important Note #1:* It is critical for your particle
        #     filter to obtain the following topic names from the
        #     parameters for the autograder to work correctly. Note
        #     that while the Odometry message contains both a pose and
        #     a twist component, you will only be provided with the
        #     twist component, so you should rely only on that
        #     information, and *not* use the pose component.
        scan_topic = rospy.get_param("~scan_topic", "/scan")
        odom_topic = rospy.get_param("~odom_topic", "/odom")
        self.laser_sub = rospy.Subscriber(scan_topic, LaserScan,
                                          YOUR_LIDAR_CALLBACK, # TODO: Fill this in
                                          queue_size=1)
        self.odom_sub  = rospy.Subscriber(odom_topic, Odometry,
                                          YOUR_ODOM_CALLBACK, # TODO: Fill this in
                                          queue_size=1)

        #  *Important Note #2:* You must respond to pose
        #     initialization requests sent to the /initialpose
        #     topic. You can test that this works properly using the
        #     "Pose Estimate" feature in RViz, which publishes to
        #     /initialpose.
        self.pose_sub  = rospy.Subscriber("/initialpose", PoseWithCovarianceStamped,
                                          YOUR_POSE_INITIALIZATION_CALLBACK, # TODO: Fill this in
                                          queue_size=1)

        #  *Important Note #3:* You must publish your pose estimate to
        #     the following topic. In particular, you must use the
        #     pose field of the Odometry message. You do not need to
        #     provide the twist part of the Odometry message. The
        #     odometry you publish here should be with respect to the
        #     "/map" frame.
        self.odom_pub  = rospy.Publisher("/pf/pose/odom", Odometry, queue_size = 1)
        
        # Initialize the models
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()
예제 #6
0
def test_raycasting_vectorize():

    np.random.seed(10008)
    map_obj = MapReader('../data/map/wean.dat')
    occupancy_map = map_obj.get_map()

    # generate a random particle, then sent out beams from that location
    h, w = occupancy_map.shape
    indices = np.where(occupancy_map.flatten() == 0)[0]
    ind = np.random.choice(indices, 1)[0]
    y, x = ind // w, ind % w
    theta = np.pi / 2
    X = np.array([[x, y, theta]])
    X = np.repeat(X, 2, axis=0)
    X[:, :2] *= 10

    num_beams = 180
    sensor = SensorModel(occupancy_map)
    z_t_star = sensor.ray_casting(X, num_beams=num_beams)

    x0, y0 = X[0, :2]
    angle = np.arange(num_beams) * (np.pi / num_beams)
    angle = theta + angle - np.pi / 2
    x1 = x0 + z_t_star * np.cos(angle)
    y1 = y0 - z_t_star * np.sin(angle)

    x0, y0 = x0 / 10, y0 / 10
    x1, y1 = x1 / 10, y1 / 10
    # plot figure
    fig = plt.figure()
    plt.imshow(occupancy_map)
    plt.scatter(x0, y0, c='red')
    plt.scatter(x1, y1, c='yellow')
    print(f'(x0, y0): ({x0}, {y0}), (x1, y1): ({x1}, {y1})')
    # plt.plot((x0, x1), (y0, y1), color='yellow')

    plt.show()
    print(z_t_star)
예제 #7
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)
config = {
        'sensor_sigma': 0.1,
        'sensor_parameters': [
                {
                    'delta': [1, 0],
                    'orientation': 0,
                    'fov': 180,
                },
                {
                    'delta': [-1, 0],
                    'orientation': 180,
                    'fov': 180,
                },
            ]
}
sm = SensorModel(config)

config1 = {
        'start': [0, 0],
        'sigma_initial': [[0.1, 0],
                          [0, 0.1]],
        'id': 1
}
motion1 = RobotMotion(config1)
motion1.pos = np.array([0, 0])
motion1.th = math.radians(0)

motion2 = RobotMotion(config1)
motion2.pos = np.array([10, 0])

print(sm.get_measurement(motion1, 0, motion2))
예제 #9
0
    def __init__(self):

        # Get parameters
        self.particle_filter_frame = rospy.get_param("~particle_filter_frame")
        self.MAX_PARTICLES = int(rospy.get_param("~num_particles"))
        self.MAX_VIZ_PARTICLES = int(rospy.get_param("~max_viz_particles"))
        self.PUBLISH_ODOM = bool(rospy.get_param("~publish_odom", "1"))
        self.ANGLE_STEP = int(rospy.get_param("~angle_step"))
        self.DO_VIZ = bool(rospy.get_param("~do_viz"))

        # MCL algorithm
        self.iters = 0
        self.lidar_initialized = False
        self.odom_initialized = False
        self.map_initialized = False
        self.last_odom_pose = None  # last received odom pose
        self.last_stamp = None
        self.laser_angles = None
        self.downsampled_angles = None
        self.state_lock = Lock()

        # paritcles
        self.inferred_pose = None
        self.particles = np.zeros((self.MAX_PARTICLES, 3))
        self.particle_indices = np.arange(self.MAX_PARTICLES)
        self.weights = np.ones(self.MAX_PARTICLES) / float(self.MAX_PARTICLES)

        # Initialize the models
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()
        # initialize the state
        self.smoothing = Utils.CircularArray(10)
        self.timer = Utils.Timer(10)
        self.initialize_global()
        # map
        self.permissible_region = None

        self.SHOW_FINE_TIMING = False

        # these topics are for visualization
        self.pose_pub = rospy.Publisher("/pf/viz/inferred_pose",
                                        PoseStamped,
                                        queue_size=1)
        self.particle_pub = rospy.Publisher("/pf/viz/particles",
                                            PoseArray,
                                            queue_size=1)
        self.pub_fake_scan = rospy.Publisher("/pf/viz/fake_scan",
                                             LaserScan,
                                             queue_size=1)
        self.path_pub = rospy.Publisher('/pf/viz/path', Path, queue_size=1)
        self.path = Path()

        if self.PUBLISH_ODOM:
            self.odom_pub = rospy.Publisher("/pf/pose/odom",
                                            Odometry,
                                            queue_size=1)

        # these topics are for coordinate space things
        self.pub_tf = tf.TransformBroadcaster()

        # these topics are to receive data from the racecar
        self.laser_sub = rospy.Subscriber(rospy.get_param(
            "~scan_topic", "/scan"),
                                          LaserScan,
                                          self.callback_lidar,
                                          queue_size=1)
        self.odom_sub = rospy.Subscriber(rospy.get_param(
            "~odom_topic", "/odom"),
                                         Odometry,
                                         self.callback_odom,
                                         queue_size=1)
        self.pose_sub = rospy.Subscriber("/initialpose",
                                         PoseWithCovarianceStamped,
                                         self.clicked_pose,
                                         queue_size=1)
        self.click_sub = rospy.Subscriber("/clicked_point",
                                          PointStamped,
                                          self.clicked_pose,
                                          queue_size=1)

        print "Finished initializing, waiting on messages..."
예제 #10
0
    parser.add_argument('--path_to_log', default='../data/log/robotdata1.log')
    parser.add_argument('--output', default='results')
    parser.add_argument('--num_particles', default=500, type=int)
    parser.add_argument('--visualize', action='store_true')
    args = parser.parse_args()

    src_path_map = args.path_to_map
    src_path_log = args.path_to_log
    os.makedirs(args.output, exist_ok=True)

    map_obj = MapReader(src_path_map)
    occupancy_map = map_obj.get_map()
    logfile = open(src_path_log, 'r')

    motion_model = MotionModel()
    sensor_model = SensorModel(occupancy_map)
    resampler = Resampling()

    map_model = ReadTable()
    ray_map = map_model.return_Map()
    print(ray_map.shape)
    # ray_map = 0

    bool_occ_map = (occupancy_map < 0.35) & (occupancy_map >= 0)

    num_particles = args.num_particles

    initial_num = num_particles

    # X_bar = init_particles_random(num_particles, occupancy_map)
    X_bar = init_particles_freespace(num_particles, occupancy_map)
예제 #11
0
    # initialize mesh map module
    print('Load mesh map and initialize map module...')
    map_module = MapModule(map_poses, map_file)

    # initialize particles
    print('Monte Carlo localization initializing...')
    map_size, road_coords = gen_coords_given_poses(map_poses)
    if config['pose_tracking']:
        particles = init_particles_pose_tracking(numParticles,
                                                 poses[start_idx])
    else:
        particles = init_particles_given_coords(numParticles, road_coords)

    # initialize sensor model
    sensor_model = SensorModel(map_module, scan_folder, config['range_image'])
    if config['range_image']['render_instanced']:
        update_weights = sensor_model.update_weights_instanced
    else:
        update_weights = sensor_model.update_weights

    # generate odom commands
    commands = gen_commands(poses)

    # initialize a visualizer
    if visualize:
        plt.ion()
        visualizer = Visualizer(map_size,
                                poses,
                                map_poses,
                                grid_res=grid_res,
예제 #12
0
    def __init__(
        self,
        publish_tf,
        n_particles,
        n_viz_particles,
        odometry_topic,
        motor_state_topic,
        servo_state_topic,
        scan_topic,
        laser_ray_step,
        exclude_max_range_rays,
        max_range_meters,
        speed_to_erpm_offset,
        speed_to_erpm_gain,
        steering_angle_to_servo_offset,
        steering_angle_to_servo_gain,
        car_length,
    ):
        """
          Initializes the particle filter
            publish_tf: Whether or not to publish the tf. Should be false in sim, true on real robot
            n_particles: The number of particles
            n_viz_particles: The number of particles to visualize
            odometry_topic: The topic containing odometry information
            motor_state_topic: The topic containing motor state information
            servo_state_topic: The topic containing servo state information
            scan_topic: The topic containing laser scans
            laser_ray_step: Step for downsampling laser scans
            exclude_max_range_rays: Whether to exclude rays that are beyond the max range
            max_range_meters: The max range of the laser
            speed_to_erpm_offset: Offset conversion param from rpm to speed
            speed_to_erpm_gain: Gain conversion param from rpm to speed
            steering_angle_to_servo_offset: Offset conversion param from servo position to steering angle
            steering_angle_to_servo_gain: Gain conversion param from servo position to steering angle
            car_length: The length of the car
        """
        self.PUBLISH_TF = publish_tf
        # The number of particles in this implementation, the total number of particles is constant.
        self.N_PARTICLES = n_particles
        self.N_VIZ_PARTICLES = n_viz_particles  # The number of particles to visualize

        # Cached list of particle indices
        self.particle_indices = np.arange(self.N_PARTICLES)
        # Numpy matrix of dimension N_PARTICLES x 3
        self.particles = np.zeros((self.N_PARTICLES, 3))
        # Numpy matrix containing weight for each particle
        self.weights = np.ones(self.N_PARTICLES) / float(self.N_PARTICLES)

        # A lock used to prevent concurrency issues. You do not need to worry about this
        self.state_lock = Lock()

        self.tfl = tf.TransformListener(
        )  # Transforms points between coordinate frames

        # Get the map
        map_msg = rospy.wait_for_message(MAP_TOPIC, OccupancyGrid)
        self.map_info = map_msg.info  # Save info about map for later use

        # Create numpy array representing map for later use
        array_255 = np.array(map_msg.data).reshape(
            (map_msg.info.height, map_msg.info.width))
        self.permissible_region = np.zeros_like(array_255, dtype=bool)
        # Numpy array of dimension (map_msg.info.height, map_msg.info.width), with values 0: not permissible, 1: permissible
        self.permissible_region[array_255 == 0] = 1

        # Globally initialize the particles

        # Publish particle filter state
        # Used to create a tf between the map and the laser for visualization
        self.pub_tf = tf.TransformBroadcaster()

        # Publishes the expected pose
        self.pose_pub = rospy.Publisher(PUBLISH_PREFIX + "/inferred_pose",
                                        PoseStamped,
                                        queue_size=1)
        # Publishes a subsample of the particles
        self.particle_pub = rospy.Publisher(PUBLISH_PREFIX + "/particles",
                                            PoseArray,
                                            queue_size=1)
        # Publishes the most recent laser scan
        self.pub_laser = rospy.Publisher(PUBLISH_PREFIX + "/scan",
                                         LaserScan,
                                         queue_size=1)
        # Publishes the path of the car
        self.pub_odom = rospy.Publisher(PUBLISH_PREFIX + "/odom",
                                        Odometry,
                                        queue_size=1)

        rospy.sleep(1.0)
        self.initialize_global()

        # An object used for resampling
        self.resampler = ReSampler(self.particles, self.weights,
                                   self.state_lock)

        # An object used for applying sensor model
        self.sensor_model = SensorModel(
            scan_topic,
            laser_ray_step,
            exclude_max_range_rays,
            max_range_meters,
            map_msg,
            self.particles,
            self.weights,
            car_length,
            self.state_lock,
        )

        # An object used for applying kinematic motion model
        self.motion_model = KinematicMotionModel(
            motor_state_topic,
            servo_state_topic,
            speed_to_erpm_offset,
            speed_to_erpm_gain,
            steering_angle_to_servo_offset,
            steering_angle_to_servo_gain,
            car_length,
            self.particles,
            self.state_lock,
        )

        self.permissible_x, self.permissible_y = np.where(
            self.permissible_region == 1)

        # Parameters/flags/vars for global localization
        self.global_localize = False
        self.global_suspend = False
        self.ents = None
        self.ents_sum = 0.0
        self.noisy_cnt = 0
        # number of regions to partition. Simulation: 25, Real: 5.
        self.NUM_REGIONS = 25
        # number of updates for regional localization. Simulation 5, Real: 3.
        self.REGIONAL_ROUNDS = 5
        self.regions = []
        self.click_mode = True
        self.debug_mode = False
        if self.debug_mode:
            self.global_localize = True  # True when doing global localization

        # precompute regions. Each region is represented by arrays of x, y indices on the map
        region_size = len(self.permissible_x) / self.NUM_REGIONS
        idx = np.argsort(self.permissible_y)  # column-major
        _px, _py = self.permissible_x[idx], self.permissible_y[idx]
        for i in range(self.NUM_REGIONS):
            self.regions.append((
                _px[i * region_size:(i + 1) * region_size],
                _py[i * region_size:(i + 1) * region_size],
            ))

        # Subscribe to the '/initialpose' topic. Publised by RVIZ. See clicked_pose_cb function in this file for more info
        # three different reactions to click on rviz
        self.click_sub = rospy.Subscriber(
            "/initialpose",
            PoseWithCovarianceStamped,
            self.clicked_pose_cb,
            queue_size=1,
        )
        self.init_sub = rospy.Subscriber("/initialpose",
                                         PoseWithCovarianceStamped,
                                         self.reinit_cb,
                                         queue_size=1)

        rospy.wait_for_message(scan_topic, LaserScan)
        print("Initialization complete")
예제 #13
0
    def __init__(self):
        # using locks so threads can't access self.particles at the same time (avoiding race condition)
        self.lock = Lock()

        self.lock.acquire()

        # Get parameters
        self.particle_filter_frame = \
                rospy.get_param("~particle_filter_frame") # should be '/base_link_pf' in sim and '/base_link' on car

        # Initialize the models
        self.motion_model = MotionModel()
        self.sensor_model = SensorModel()

        # Topics
        self.odom_topic = rospy.get_param("~odom_topic")
        self.scan_topic = rospy.get_param("~scan_topic")
        self.particle_marker_topic = '/particle_marker'
        self.click_topic = '/move_base_simple/goal'
        self.pose_array_topic = '/pose_array'
        self.inferred_pose_topic = '/inferred_pose'

        self.error_x_topic = '/error_x'
        self.error_y_topic = '/error_y'
        self.error_t_topic = '/error_t'

        # Subscribers and Publishers
        rospy.Subscriber(
            self.odom_topic, Odometry,
            self.odom_callback)  # '/odom' for sim and 'vesc/odom' for car
        rospy.Subscriber(self.scan_topic, LaserScan, self.scan_callback)

        self.tf_broadcaster = TransformBroadcaster()

        # Listening to clicks in rviz and publishing particle markers to rviz
        rospy.Subscriber(self.click_topic, PoseStamped, self.click_callback)
        self.particle_marker_pub = rospy.Publisher(self.particle_marker_topic,
                                                   Marker,
                                                   queue_size=1)
        self.pose_array_pub = rospy.Publisher(self.pose_array_topic,
                                              PoseArray,
                                              queue_size=1)
        self.inferred_pose_pub = rospy.Publisher(self.inferred_pose_topic,
                                                 Marker,
                                                 queue_size=1)

        self.error_x_pub = rospy.Publisher(self.error_x_topic,
                                           Float32,
                                           queue_size=1)
        self.error_y_pub = rospy.Publisher(self.error_y_topic,
                                           Float32,
                                           queue_size=1)
        self.error_t_pub = rospy.Publisher(self.error_t_topic,
                                           Float32,
                                           queue_size=1)

        self.rate = rospy.Rate(30)  # should be greater than 20 Hz

        # Implement the MCL algorithm
        # using the sensor model and the motion model
        #
        # Make sure you include some way to initialize
        # your particles, ideally with some sort
        # of interactive interface in rviz
        #
        # Publish a transformation frame between the map
        # and the particle_filter_frame.

        # Initializing particles and weights
        self.num_particles = rospy.get_param('~num_particles')
        # currently intializing all particles as origin
        self.particles = np.zeros((self.num_particles, 3))
        # all particles are initially equally likely
        self.weights = np.full((self.num_particles), 1. / self.num_particles)

        # for error calculations
        self.groundtruth = [0, 0, 0]

        # send drive commands to test in sim
        self.drive_pub = rospy.Publisher('/drive',
                                         AckermannDriveStamped,
                                         queue_size=1)
        self.drive_msg = AckermannDriveStamped()
        self.drive_msg.drive.steering_angle = 0
        self.drive_msg.drive.speed = 1

        if self.SIM == False:  # don't toggle on driving if working on real robot
            self.DRIVE = False

        self.publish_transform()

        self.lock.release()
예제 #14
0
 def setUp(self):
     self.sensor_model = SensorModel(map)
     self.particle = Particle(map)
     self.particle.x = 250
     self.particle.y = 250
     self.particle.theta = 0
예제 #15
0
    def init_world_once(self, do_test=True):
        # create a blank world, PRM style
        num_nodes = self.config["num_nodes"]
        connection_radius = self.config["connection_radius"]
        environment_size = self.config["environment_size"]
        self.sensor_range = self.config["sensor_range"]

        # vertices
        self.vertices_surface = []
        self.vertices = []
        count = 0
        for vertex_idx in xrange(num_nodes):
            x = random.uniform(0, environment_size[0])
            y = random.uniform(0, environment_size[1])
            for z in [-10, self.surface_level]:
                v = Vertex(x, y, z, count)
                count += 1
                self.vertices.append(v)
                if z == self.surface_level:
                    self.vertices_surface.append(True)
                else:
                    self.vertices_surface.append(False)

        # edges, stored as a matrix indexed as [vertex_start, vertex_end]
        self.num_nodes = len(
            self.vertices
        )  #doubled the input num_nodes in creating two layers of vertices instead of one
        self.edge_matrix = [None] * self.num_nodes
        self.edge_adjacency_idx_lists = [None] * self.num_nodes
        self.edge_adjacency_edge_lists = [None] * self.num_nodes
        for vertex_start_idx in xrange(self.num_nodes):
            self.edge_matrix[vertex_start_idx] = [None] * self.num_nodes
            self.edge_adjacency_idx_lists[vertex_start_idx] = []
            self.edge_adjacency_edge_lists[vertex_start_idx] = []
            for vertex_end_idx in xrange(self.num_nodes):

                cost = distance(self.vertices[vertex_start_idx],
                                self.vertices[vertex_end_idx])
                if vertex_start_idx != vertex_end_idx and cost <= connection_radius and not (
                        self.vertices_surface[vertex_start_idx]
                        and self.vertices_surface[vertex_end_idx]):
                    exists = True
                else:
                    exists = False
                edge = Edge(vertex_start_idx, vertex_end_idx, cost, exists)
                self.edge_matrix[vertex_start_idx][vertex_end_idx] = edge

                if exists:
                    self.edge_adjacency_idx_lists[vertex_start_idx].append(
                        vertex_end_idx)
                    self.edge_adjacency_edge_lists[vertex_start_idx].append(
                        edge)

        if do_test:
            self.test_indices()

        #self.vertex_target_idx = self.create_target_idx() #single robot

        # Define prior distribution
        self.num_classes = self.config["num_classes"]
        self.prob_of_class0 = self.config["prob_of_class0"]
        self.prob_of_other_classes = (1 - self.prob_of_class0) / (
            self.num_classes - 1)
        self.prior = np.zeros(self.num_classes)  #p_y0, p_y1, ...
        for i in range(self.num_classes):
            if i == 0:
                self.prior[i] = self.prob_of_class0
            else:
                self.prior[i] = self.prob_of_other_classes

        self.randomize_targets()
        self.original_classes_y = copy.copy(self.classes_y)

        # Define single random drop-off location (on surface) per world
        temp = random.randint(0, len(self.vertices) - 1)
        while self.vertices_surface[temp] != True:
            temp = random.randint(0, len(self.vertices) - 1)
        self.drop_off_idx = temp
        '''
        # Set all class 2 vertices to is_armed = True
        self.is_armed_array = np.zeros(self.num_nodes, dtype=bool) #default, False for all
        for v in xrange(len(self.classes_y)):
            if self.classes_y[v] == 2: #it is a mine
                self.is_armed_array[v] = True
        '''
        '''
        # Vertex classes
        # Each vertex has a number between 0 and n, which represents the class of the vertex
        num_v_y0 = int(self.num_nodes*self.prob_of_class0)
        num_v_other = self.num_nodes - num_v_y0
        num_v_each = num_v_other/(self.num_classes-1)
        classes_y = []
        classes_y.extend([0]*num_v_y0) # add the non-target vertex classes
        for i in range(1,self.num_classes):
            classes_y.extend([i]*num_v_each) # add the target vertex classes

        # G round truth vertex classes
        random.shuffle(classes_y) #shuffle the list to randomize location of targets
        '''

        self.comms_range = self.config["comms_range"]

        self.vertices_in_comms_range = self.generateCommsRangeVertices()

        # Setup sensor model
        self.sensor_model = SensorModel(self.config, self.num_nodes, self)