Exemplo n.º 1
0
class CarlaEnv(gym.Env):
    """An OpenAI gym wrapper for CARLA simulator."""
    def __init__(self, params):
        # parameters
        self.display_size = params['display_size']  # rendering screen size
        self.max_past_step = params['max_past_step']
        self.number_of_vehicles = params['number_of_vehicles']
        self.number_of_walkers = params['number_of_walkers']
        self.dt = params['dt']
        self.task_mode = params['task_mode']
        self.max_time_episode = params['max_time_episode']
        self.max_waypt = params['max_waypt']
        self.obs_range = params['obs_range']
        self.lidar_bin = params['lidar_bin']
        self.d_behind = params['d_behind']
        self.obs_size = int(self.obs_range / self.lidar_bin)
        self.out_lane_thres = params['out_lane_thres']
        self.desired_speed = params['desired_speed']
        self.max_ego_spawn_times = params['max_ego_spawn_times']
        self.display_route = params['display_route']
        if 'pixor' in params.keys():
            self.pixor = params['pixor']
            self.pixor_size = params['pixor_size']
        else:
            self.pixor = False

        # Terminal condition
        if 'terminal_condition' in params:
            self.terminal_condition = params['terminal_condition']
        else:
            self.terminal_condition = 'all'

        # Destination
        if params['task_mode'] == 'roundabout':
            self.dests = [[4.46, -61.46, 0], [-49.53, -2.89, 0],
                          [-6.48, 55.47, 0], [35.96, 3.33, 0]]
        else:
            self.dests = None

        # action and observation spaces
        self.discrete = params['discrete']
        self.discrete_act = [params['discrete_acc'],
                             params['discrete_steer']]  # acc, steer
        self.n_acc = len(self.discrete_act[0])
        self.n_steer = len(self.discrete_act[1])
        if self.discrete:
            self.action_space = spaces.Discrete(self.n_acc * self.n_steer)
        else:
            self.action_space = spaces.Box(
                np.array([
                    params['continuous_accel_range'][0],
                    params['continuous_steer_range'][0]
                ]),
                np.array([
                    params['continuous_accel_range'][1],
                    params['continuous_steer_range'][1]
                ]),
                dtype=np.float32)  # acc, steer
        observation_space_dict = {
            'camera':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'lidar':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'birdeye':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'state':
            spaces.Box(np.array([-2, -1, -5, 0]),
                       np.array([2, 1, 30, 1]),
                       dtype=np.float32)
        }
        if self.pixor:
            observation_space_dict.update({
                'roadmap':
                spaces.Box(low=0,
                           high=255,
                           shape=(self.obs_size, self.obs_size, 3),
                           dtype=np.uint8),
                'vh_clas':
                spaces.Box(low=0,
                           high=1,
                           shape=(self.pixor_size, self.pixor_size, 1),
                           dtype=np.float32),
                'vh_regr':
                spaces.Box(low=-5,
                           high=5,
                           shape=(self.pixor_size, self.pixor_size, 6),
                           dtype=np.float32),
                'pixor_state':
                spaces.Box(np.array([-1000, -1000, -1, -1, -5]),
                           np.array([1000, 1000, 1, 1, 20]),
                           dtype=np.float32)
            })
        self.observation_space = spaces.Dict(observation_space_dict)

        # Connect to carla server and get world object
        print('connecting to Carla server...')
        client = carla.Client('localhost', params['port'])
        client.set_timeout(10.0)
        self.world = client.load_world(params['town'])
        print('Carla server connected!')

        # Set weather
        self.world.set_weather(carla.WeatherParameters.ClearNoon)

        # Get spawn points
        self.vehicle_spawn_points = list(
            self.world.get_map().get_spawn_points())
        self.walker_spawn_points = []
        for i in range(self.number_of_walkers):
            spawn_point = carla.Transform()
            loc = self.world.get_random_location_from_navigation()
            if (loc != None):
                spawn_point.location = loc
                self.walker_spawn_points.append(spawn_point)

        # Create the ego vehicle blueprint
        self.ego_bp = self._create_vehicle_bluepprint(
            params['ego_vehicle_filter'], color='49,8,8')

        # Collision sensor
        self.collision_hist = []  # The collision history
        self.collision_hist_l = 1  # collision history length
        self.collision_bp = self.world.get_blueprint_library().find(
            'sensor.other.collision')

        # Lidar sensor
        self.lidar_data = None
        self.lidar_height = 2.1
        self.lidar_trans = carla.Transform(
            carla.Location(x=0.0, z=self.lidar_height))
        self.lidar_bp = self.world.get_blueprint_library().find(
            'sensor.lidar.ray_cast')
        self.lidar_bp.set_attribute('channels', '32')
        self.lidar_bp.set_attribute('range', '5000')

        # Camera sensor
        self.camera_img = np.zeros((self.obs_size, self.obs_size, 3),
                                   dtype=np.uint8)
        self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
        self.camera_bp = self.world.get_blueprint_library().find(
            'sensor.camera.rgb')
        # Modify the attributes of the blueprint to set image resolution and field of view.
        self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
        self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
        self.camera_bp.set_attribute('fov', '110')
        # Set the time in seconds between sensor captures
        self.camera_bp.set_attribute('sensor_tick', '0.02')

        # Set fixed simulation step for synchronous mode
        self.settings = self.world.get_settings()
        self.settings.fixed_delta_seconds = self.dt

        # Record the time of total steps and resetting steps
        self.reset_step = 0
        self.total_step = 0

        # Initialize the renderer
        self._init_renderer()

        # Get pixel grid points
        if self.pixor:
            x, y = np.meshgrid(
                np.arange(self.pixor_size),
                np.arange(self.pixor_size))  # make a canvas with coordinates
            x, y = x.flatten(), y.flatten()
            self.pixel_grid = np.vstack((x, y)).T

    def reset(self):
        # Clear sensor objects
        self.collision_sensor = None
        self.lidar_sensor = None
        self.camera_sensor = None

        # Delete sensors, vehicles and walkers
        self._clear_all_actors([
            'sensor.other.collision', 'sensor.lidar.ray_cast',
            'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker',
            'walker.*'
        ])

        # Disable sync mode
        self._set_synchronous_mode(False)

        # Spawn surrounding vehicles
        random.shuffle(self.vehicle_spawn_points)
        count = self.number_of_vehicles
        if count > 0:
            for spawn_point in self.vehicle_spawn_points:
                if self._try_spawn_random_vehicle_at(spawn_point,
                                                     number_of_wheels=[4]):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_vehicle_at(random.choice(
                    self.vehicle_spawn_points),
                                                 number_of_wheels=[4]):
                count -= 1

        # Spawn pedestrians
        random.shuffle(self.walker_spawn_points)
        count = self.number_of_walkers
        if count > 0:
            for spawn_point in self.walker_spawn_points:
                if self._try_spawn_random_walker_at(spawn_point):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_walker_at(
                    random.choice(self.walker_spawn_points)):
                count -= 1

        # Get actors polygon list
        self.vehicle_polygons = []
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        self.walker_polygons = []
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)

        # Spawn the ego vehicle
        ego_spawn_times = 0
        while True:
            if ego_spawn_times > self.max_ego_spawn_times:
                self.reset()

            if self.task_mode == 'random':
                transform = random.choice(self.vehicle_spawn_points)
            if self.task_mode == 'roundabout':
                self.start = [52.1 + np.random.uniform(-5, 5), -4.2,
                              178.66]  # random
                # self.start=[52.1,-4.2, 178.66] # static
                transform = set_carla_transform(self.start)
            if self._try_spawn_ego_vehicle_at(transform):
                break
            else:
                ego_spawn_times += 1
                time.sleep(0.1)

        # Add collision sensor
        self.collision_sensor = self.world.spawn_actor(self.collision_bp,
                                                       carla.Transform(),
                                                       attach_to=self.ego)
        self.collision_sensor.listen(lambda event: get_collision_hist(event))

        def get_collision_hist(event):
            impulse = event.normal_impulse
            intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
            self.collision_hist.append(intensity)
            if len(self.collision_hist) > self.collision_hist_l:
                self.collision_hist.pop(0)

        self.collision_hist = []

        # Add lidar sensor
        self.lidar_sensor = self.world.spawn_actor(self.lidar_bp,
                                                   self.lidar_trans,
                                                   attach_to=self.ego)
        self.lidar_sensor.listen(lambda data: get_lidar_data(data))

        def get_lidar_data(data):
            self.lidar_data = data

        # Add camera sensor

        self.camera_sensor = self.world.spawn_actor(self.camera_bp,
                                                    self.camera_trans,
                                                    attach_to=self.ego)
        self.camera_sensor.listen(lambda data: get_camera_img(data))

        def get_camera_img(data):
            array = np.frombuffer(data.raw_data, dtype=np.dtype("uint8"))
            array = np.reshape(array, (data.height, data.width, 4))
            array = array[:, :, :3]
            array = array[:, :, ::-1]
            self.camera_img = array

        # Update timesteps
        self.time_step = 0
        self.reset_step += 1

        # Enable sync mode
        self.settings.synchronous_mode = True
        self.world.apply_settings(self.settings)

        self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

        # Set ego information for render
        self.birdeye_render.set_hero(self.ego, self.ego.id)

        return self._get_obs()

    def step(self, action):
        # Calculate acceleration and steering
        if self.discrete:
            acc = self.discrete_act[0][action // self.n_steer]
            steer = self.discrete_act[1][action % self.n_steer]
        else:
            acc = action[0]
            steer = action[1]

        # Convert acceleration to throttle and brake
        if acc > 0:
            throttle = np.clip(acc / 3, 0, 1)
            brake = 0
        else:
            throttle = 0
            brake = np.clip(-acc / 8, 0, 1)

        # Apply control
        act = carla.VehicleControl(throttle=float(throttle),
                                   steer=float(-steer),
                                   brake=float(brake))
        self.ego.apply_control(act)

        self.world.tick()

        # Append actors polygon list
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        while len(self.vehicle_polygons) > self.max_past_step:
            self.vehicle_polygons.pop(0)
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)
        while len(self.walker_polygons) > self.max_past_step:
            self.walker_polygons.pop(0)

        # route planner
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

        # state information
        info = {
            'waypoints': self.waypoints,
            'vehicle_front': self.vehicle_front
        }

        # Update timesteps
        self.time_step += 1
        self.total_step += 1

        return (self._get_obs(), self._get_reward(), self._terminal(),
                copy.deepcopy(info))

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def render(self, mode):
        pass

    def _create_vehicle_bluepprint(self,
                                   actor_filter,
                                   color=None,
                                   number_of_wheels=[4]):
        """Create the blueprint for a specific actor type.

    Args:
      actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

    Returns:
      bp: the blueprint object of carla.
    """
        blueprints = self.world.get_blueprint_library().filter(actor_filter)
        blueprint_library = []
        for nw in number_of_wheels:
            blueprint_library = blueprint_library + [
                x for x in blueprints
                if int(x.get_attribute('number_of_wheels')) == nw
            ]
        bp = random.choice(blueprint_library)
        if bp.has_attribute('color'):
            if not color:
                color = random.choice(
                    bp.get_attribute('color').recommended_values)
            bp.set_attribute('color', color)
        return bp

    def _init_renderer(self):
        """Initialize the birdeye view renderer.
    """
        pygame.init()
        self.display = pygame.display.set_mode(
            (self.display_size * 3, self.display_size),
            pygame.HWSURFACE | pygame.DOUBLEBUF)

        pixels_per_meter = self.display_size / self.obs_range
        pixels_ahead_vehicle = (self.obs_range / 2 -
                                self.d_behind) * pixels_per_meter
        birdeye_params = {
            'screen_size': [self.display_size, self.display_size],
            'pixels_per_meter': pixels_per_meter,
            'pixels_ahead_vehicle': pixels_ahead_vehicle
        }
        self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

    def _set_synchronous_mode(self, synchronous=True):
        """Set whether to use the synchronous mode.
    """
        self.settings.synchronous_mode = synchronous
        self.world.apply_settings(self.settings)

    def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
        """Try to spawn a surrounding vehicle at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
        blueprint = self._create_vehicle_bluepprint(
            'vehicle.*', number_of_wheels=number_of_wheels)
        blueprint.set_attribute('role_name', 'autopilot')
        vehicle = self.world.try_spawn_actor(blueprint, transform)
        if vehicle is not None:
            vehicle.set_autopilot()
            return True
        return False

    def _try_spawn_random_walker_at(self, transform):
        """Try to spawn a walker at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
        walker_bp = random.choice(
            self.world.get_blueprint_library().filter('walker.*'))
        # set as not invencible
        if walker_bp.has_attribute('is_invincible'):
            walker_bp.set_attribute('is_invincible', 'false')
        walker_actor = self.world.try_spawn_actor(walker_bp, transform)

        if walker_actor is not None:
            walker_controller_bp = self.world.get_blueprint_library().find(
                'controller.ai.walker')
            walker_controller_actor = self.world.spawn_actor(
                walker_controller_bp, carla.Transform(), walker_actor)
            # start walker
            walker_controller_actor.start()
            # set walk to random point
            walker_controller_actor.go_to_location(
                self.world.get_random_location_from_navigation())
            # random max speed
            walker_controller_actor.set_max_speed(
                1 + random.random()
            )  # max speed between 1 and 2 (default is 1.4 m/s)
            return True
        return False

    def _try_spawn_ego_vehicle_at(self, transform):
        """Try to spawn the ego vehicle at specific transform.
    Args:
      transform: the carla transform object.
    Returns:
      Bool indicating whether the spawn is successful.
    """
        vehicle = None
        # Check if ego position overlaps with surrounding vehicles
        overlap = False
        for idx, poly in self.vehicle_polygons[-1].items():
            poly_center = np.mean(poly, axis=0)
            ego_center = np.array([transform.location.x, transform.location.y])
            dis = np.linalg.norm(poly_center - ego_center)
            if dis > 8:
                continue
            else:
                overlap = True
                break

        if not overlap:
            vehicle = self.world.try_spawn_actor(self.ego_bp, transform)

        if vehicle is not None:
            self.ego = vehicle
            return True

        return False

    def _get_actor_polygons(self, filt):
        """Get the bounding box polygon of actors.

    Args:
      filt: the filter indicating what type of actors we'll look at.

    Returns:
      actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
    """
        actor_poly_dict = {}
        for actor in self.world.get_actors().filter(filt):
            # Get x, y and yaw of the actor
            trans = actor.get_transform()
            x = trans.location.x
            y = trans.location.y
            yaw = trans.rotation.yaw / 180 * np.pi
            # Get length and width
            bb = actor.bounding_box
            l = bb.extent.x
            w = bb.extent.y
            # Get bounding box polygon in the actor's local coordinate
            poly_local = np.array([[l, w], [l, -w], [-l, -w],
                                   [-l, w]]).transpose()
            # Get rotation matrix to transform to global coordinate
            R = np.array([[np.cos(yaw), -np.sin(yaw)],
                          [np.sin(yaw), np.cos(yaw)]])
            # Get global bounding box polygon
            poly = np.matmul(R, poly_local).transpose() + np.repeat(
                [[x, y]], 4, axis=0)
            actor_poly_dict[actor.id] = poly
        return actor_poly_dict

    def _get_obs(self):
        """Get the observations."""
        ## Birdeye rendering
        self.birdeye_render.vehicle_polygons = self.vehicle_polygons
        self.birdeye_render.walker_polygons = self.walker_polygons
        self.birdeye_render.waypoints = self.waypoints

        # birdeye view with roadmap and actors
        birdeye_render_types = ['roadmap', 'actors']
        if self.display_route:
            birdeye_render_types.append('waypoints')
        self.birdeye_render.render(self.display, birdeye_render_types)
        birdeye = pygame.surfarray.array3d(self.display)
        birdeye = birdeye[0:self.display_size, :, :]
        birdeye = display_to_rgb(birdeye, self.obs_size)

        # Roadmap
        if self.pixor:
            roadmap_render_types = ['roadmap']
            if self.display_route:
                roadmap_render_types.append('waypoints')
            self.birdeye_render.render(self.display, roadmap_render_types)
            roadmap = pygame.surfarray.array3d(self.display)
            roadmap = roadmap[0:self.display_size, :, :]
            roadmap = display_to_rgb(roadmap, self.obs_size)
            # Add ego vehicle
            for i in range(self.obs_size):
                for j in range(self.obs_size):
                    if abs(birdeye[i, j, 0] - 255) < 20 and abs(
                            birdeye[i, j, 1] -
                            0) < 20 and abs(birdeye[i, j, 0] - 255) < 20:
                        roadmap[i, j, :] = birdeye[i, j, :]

        # Display birdeye image
        birdeye_surface = rgb_to_display_surface(birdeye, self.display_size)
        self.display.blit(birdeye_surface, (0, 0))

        ## Lidar image generation
        point_cloud = []
        # Get point cloud data
        for location in self.lidar_data:
            point_cloud.append([location.x, location.y, -location.z])
        point_cloud = np.array(point_cloud)
        # Separate the 3D space to bins for point cloud, x and y is set according to self.lidar_bin,
        # and z is set to be two bins.
        y_bins = np.arange(-(self.obs_range - self.d_behind),
                           self.d_behind + self.lidar_bin, self.lidar_bin)
        x_bins = np.arange(-self.obs_range / 2,
                           self.obs_range / 2 + self.lidar_bin, self.lidar_bin)
        z_bins = [-self.lidar_height - 1, -self.lidar_height + 0.25, 1]
        # Get lidar image according to the bins
        lidar, _ = np.histogramdd(point_cloud, bins=(x_bins, y_bins, z_bins))
        lidar[:, :, 0] = np.array(lidar[:, :, 0] > 0, dtype=np.uint8)
        lidar[:, :, 1] = np.array(lidar[:, :, 1] > 0, dtype=np.uint8)
        # Add the waypoints to lidar image
        if self.display_route:
            wayptimg = (birdeye[:, :, 0] <= 10) * (birdeye[:, :, 1] <= 10) * (
                birdeye[:, :, 2] >= 240)
        else:
            wayptimg = birdeye[:, :, 0] < 0  # Equal to a zero matrix
        wayptimg = np.expand_dims(wayptimg, axis=2)
        wayptimg = np.fliplr(np.rot90(wayptimg, 3))

        # Get the final lidar image
        lidar = np.concatenate((lidar, wayptimg), axis=2)
        lidar = np.flip(lidar, axis=1)
        lidar = np.rot90(lidar, 1)
        lidar = lidar * 255

        # Display lidar image
        lidar_surface = rgb_to_display_surface(lidar, self.display_size)
        self.display.blit(lidar_surface, (self.display_size, 0))

        ## Display camera image
        camera = resize(self.camera_img, (self.obs_size, self.obs_size)) * 255
        camera_surface = rgb_to_display_surface(camera, self.display_size)
        self.display.blit(camera_surface, (self.display_size * 2, 0))

        # Display on pygame
        pygame.display.flip()

        # State observation
        ego_trans = self.ego.get_transform()
        ego_x = ego_trans.location.x
        ego_y = ego_trans.location.y
        ego_yaw = ego_trans.rotation.yaw / 180 * np.pi
        lateral_dis, w = get_preview_lane_dis(self.waypoints, ego_x, ego_y)
        delta_yaw = np.arcsin(
            np.cross(w, np.array(np.array([np.cos(ego_yaw),
                                           np.sin(ego_yaw)]))))
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        state = np.array([lateral_dis, -delta_yaw, speed, self.vehicle_front])

        if self.pixor:
            ## Vehicle classification and regression maps (requires further normalization)
            vh_clas = np.zeros((self.pixor_size, self.pixor_size))
            vh_regr = np.zeros((self.pixor_size, self.pixor_size, 6))

            # Generate the PIXOR image. Note in CARLA it is using left-hand coordinate
            # Get the 6-dim geom parametrization in PIXOR, here we use pixel coordinate
            for actor in self.world.get_actors().filter('vehicle.*'):
                x, y, yaw, l, w = get_info(actor)
                x_local, y_local, yaw_local = get_local_pose(
                    (x, y, yaw), (ego_x, ego_y, ego_yaw))
                if actor.id != self.ego.id:
                    if abs(
                            y_local
                    ) < self.obs_range / 2 + 1 and x_local < self.obs_range - self.d_behind + 1 and x_local > -self.d_behind - 1:
                        x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel = get_pixel_info(
                            local_info=(x_local, y_local, yaw_local, l, w),
                            d_behind=self.d_behind,
                            obs_range=self.obs_range,
                            image_size=self.pixor_size)
                        cos_t = np.cos(yaw_pixel)
                        sin_t = np.sin(yaw_pixel)
                        logw = np.log(w_pixel)
                        logl = np.log(l_pixel)
                        pixels = get_pixels_inside_vehicle(
                            pixel_info=(x_pixel, y_pixel, yaw_pixel, l_pixel,
                                        w_pixel),
                            pixel_grid=self.pixel_grid)
                        for pixel in pixels:
                            vh_clas[pixel[0], pixel[1]] = 1
                            dx = x_pixel - pixel[0]
                            dy = y_pixel - pixel[1]
                            vh_regr[pixel[0], pixel[1], :] = np.array(
                                [cos_t, sin_t, dx, dy, logw, logl])

            # Flip the image matrix so that the origin is at the left-bottom
            vh_clas = np.flip(vh_clas, axis=0)
            vh_regr = np.flip(vh_regr, axis=0)

            # Pixor state, [x, y, cos(yaw), sin(yaw), speed]
            pixor_state = [
                ego_x, ego_y,
                np.cos(ego_yaw),
                np.sin(ego_yaw), speed
            ]

        obs = {
            'camera': camera.astype(np.uint8),
            'lidar': lidar.astype(np.uint8),
            'birdeye': birdeye.astype(np.uint8),
            'state': state,
        }

        if self.pixor:
            obs.update({
                'roadmap':
                roadmap.astype(np.uint8),
                'vh_clas':
                np.expand_dims(vh_clas, -1).astype(np.float32),
                'vh_regr':
                vh_regr.astype(np.float32),
                'pixor_state':
                pixor_state,
            })

        return obs

    def _get_reward(self):
        """Calculate the step reward."""
        # reward for speed tracking
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        r_speed = -abs(speed - self.desired_speed)

        # reward for collision
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -1

        # reward for steering:
        r_steer = -self.ego.get_control().steer**2

        # reward for out of lane
        ego_x, ego_y = get_pos(self.ego)
        dis, w = get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        if abs(dis) > self.out_lane_thres:
            r_out = -1

        # longitudinal speed
        lspeed = np.array([v.x, v.y])
        lspeed_lon = np.dot(lspeed, w)

        # cost for too fast
        r_fast = 0
        if lspeed_lon > self.desired_speed:
            r_fast = -1

        # cost for lateral acceleration
        r_lat = -abs(self.ego.get_control().steer) * lspeed_lon**2

        r = 200 * r_collision + 1 * lspeed_lon + 10 * r_fast + 1 * r_out + r_steer * 5 + 0.2 * r_lat - 0.1

        return r

    # def _terminal(self):
    #   """Calculate whether to terminate the current episode."""
    #   # Get ego state
    #   ego_x, ego_y = get_pos(self.ego)

    #   # If collides
    #   if len(self.collision_hist)>0:
    #     return True

    #   # If reach maximum timestep
    #   if self.time_step>self.max_time_episode:
    #     return True

    #   # If at destination
    #   if self.dests is not None: # If at destination
    #     for dest in self.dests:
    #       if np.sqrt((ego_x-dest[0])**2+(ego_y-dest[1])**2)<4:
    #         return True

    #   # If out of lane
    #   dis, _ = get_lane_dis(self.waypoints, ego_x, ego_y)
    #   if abs(dis) > self.out_lane_thres:
    #     return True

    #   return False

    def _terminal(self):
        """ 
    Calculate whether to terminate the current episode with own condition
    Add the terminal condition into 'terminal_condition' parameters:
      Collision: if Collision happen
      Step: maximum step
      Destination: if ego reach the destination
      Distence: if ego is out of lane
    """
        # get ego state
        ego_x, ego_y = get_pos(self.ego)

        # If collides
        if len(self.collision_hist) > 0 and (
                'Collision' in self.terminal_condition
                or self.terminal_condition == 'all'):
            return True

        # If reach maximum timestep
        if self.time_step > self.max_time_episode and (
                'Step' in self.terminal_condition
                or self.terminal_condition == 'all'):
            return True

        # If at destination
        if self.dests is not None and ('Destination' in self.terminal_condition
                                       or self.terminal_condition
                                       == 'all'):  # If at destination
            for dest in self.dests:
                if np.sqrt((ego_x - dest[0])**2 + (ego_y - dest[1])**2) < 4:
                    return True

        # If out of lane
        dis, _ = get_lane_dis(self.waypoints, ego_x, ego_y)
        if abs(dis) > self.out_lane_thres and (
                'Distence' in self.terminal_condition
                or self.terminal_condition == 'all'):
            return True

        return False

    def _clear_all_actors(self, actor_filters):
        """Clear specific actors."""
        for actor_filter in actor_filters:
            for actor in self.world.get_actors().filter(actor_filter):
                if actor.is_alive:
                    if actor.type_id == 'controller.ai.walker':
                        actor.stop()
                    actor.destroy()
Exemplo n.º 2
0
class CarlaEnv(gym.Env):
    """An OpenAI gym wrapper for CARLA simulator."""
    def __init__(self, params):
        # parameters
        self.display_size = params['display_size']  # rendering screen size
        self.max_past_step = params['max_past_step']
        self.number_of_vehicles = params['number_of_vehicles']
        self.number_of_walkers = params['number_of_walkers']
        self.dt = params['dt']
        self.task_mode = params['task_mode']
        self.max_time_episode = params['max_time_episode']
        self.max_waypt = params['max_waypt']
        self.obs_range = params['obs_range']
        self.lidar_bin = params['lidar_bin']
        self.d_behind = params['d_behind']
        self.obs_size = int(self.obs_range / self.lidar_bin)
        self.image_height = params['image_height']
        self.image_width = params['image_width']
        self.out_lane_thres = params['out_lane_thres']
        self.desired_speed = params['desired_speed']
        self.max_ego_spawn_times = params['max_ego_spawn_times']
        self.display_route = params['display_route']
        if 'pixor' in params.keys():
            self.pixor = params['pixor']
            self.pixor_size = params['pixor_size']
        else:
            self.pixor = False
        if 'image_xform' in params.keys():
            self.xform = True
        else:
            self.xform = False

        self.step_rewards = []
        self.step_actions = []
        self.step_steering = []
        self.max_distance = 2.0

        # Destination
        if params['task_mode'] == 'roundabout':
            self.dests = [[4.46, -61.46, 0], [-49.53, -2.89, 0],
                          [-6.48, 55.47, 0], [35.96, 3.33, 0]]
        else:
            self.dests = None

        # action and observation spaces
        self.discrete = params['discrete']
        self.discrete_act = [params['discrete_acc'],
                             params['discrete_steer']]  # acc, steer
        self.n_acc = len(self.discrete_act[0])
        self.n_steer = len(self.discrete_act[1])
        if self.discrete:
            self.action_space = spaces.Discrete(self.n_acc * self.n_steer)
        else:
            self.action_space = spaces.Box(
                np.array([
                    params['continuous_accel_range'][0],
                    params['continuous_steer_range'][0]
                ]),
                np.array([
                    params['continuous_accel_range'][1],
                    params['continuous_steer_range'][1]
                ]),
                dtype=np.float32)  # acc, steer
        observation_space_dict = {
            'orig_camera':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.image_height, self.image_width, 3),
                       dtype=np.uint8),
            'camera':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'lidar':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'birdeye':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'state':
            spaces.Box(np.array([-2, -1, -5, 0]),
                       np.array([2, 1, 30, 1]),
                       dtype=np.float32)
        }
        if self.pixor:
            observation_space_dict.update({
                'roadmap':
                spaces.Box(low=0,
                           high=255,
                           shape=(self.obs_size, self.obs_size, 3),
                           dtype=np.uint8),
                'vh_clas':
                spaces.Box(low=0,
                           high=1,
                           shape=(self.pixor_size, self.pixor_size, 1),
                           dtype=np.float32),
                'vh_regr':
                spaces.Box(low=-5,
                           high=5,
                           shape=(self.pixor_size, self.pixor_size, 6),
                           dtype=np.float32),
                'pixor_state':
                spaces.Box(np.array([-1000, -1000, -1, -1, -5]),
                           np.array([1000, 1000, 1, 1, 20]),
                           dtype=np.float32)
            })
        self.observation_space = spaces.Dict(observation_space_dict)

        # Connect to carla server and get world object
        print('connecting to Carla server...')
        client = carla.Client('localhost', params['port'])
        client.set_timeout(10.0)
        self.client = client
        self.world = client.load_world(params['town'])
        print('Carla server connected!')

        self.birdview_producer = BirdViewProducer(
            self.client,  # carla.Client
            target_size=PixelDimensions(width=self.image_width,
                                        height=self.image_height),
            pixels_per_meter=4,
            crop_type=BirdViewCropType.FRONT_AND_REAR_AREA)

        # Set weather
        self.world.set_weather(carla.WeatherParameters.ClearNoon)

        # Get spawn points
        self.vehicle_spawn_points = list(
            self.world.get_map().get_spawn_points())
        self.walker_spawn_points = []
        for i in range(self.number_of_walkers):
            spawn_point = carla.Transform()
            loc = self.world.get_random_location_from_navigation()
            if (loc != None):
                spawn_point.location = loc
                self.walker_spawn_points.append(spawn_point)

        # Create the ego vehicle blueprint
        self.ego_bp = self._create_vehicle_bluepprint(
            params['ego_vehicle_filter'], color='49,8,8')

        # Collision sensor
        self.collision_hist = []  # The collision history
        self.collision_hist_l = 1  # collision history length
        self.collision_bp = self.world.get_blueprint_library().find(
            'sensor.other.collision')

        # Lidar sensor
        self.lidar_data = None
        self.lidar_height = 2.1
        self.lidar_trans = carla.Transform(
            carla.Location(x=0.0, z=self.lidar_height))
        self.lidar_bp = self.world.get_blueprint_library().find(
            'sensor.lidar.ray_cast')
        self.lidar_bp.set_attribute('channels', '32')
        self.lidar_bp.set_attribute('range', '5000')

        # Camera sensor
        self.camera_img = np.zeros((self.obs_size, self.obs_size, 3),
                                   dtype=np.uint8)
        self.original_camera_image = np.zeros(
            (self.image_height, self.image_width, 3), dtype=np.uint8)
        self.speed = np.zeros((1), dtype=np.uint8)
        self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
        self.camera_bp = self.world.get_blueprint_library().find(
            'sensor.camera.rgb')
        # Modify the attributes of the blueprint to set image resolution and field of view.
        self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
        self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
        self.camera_bp.set_attribute('fov', '110')
        # Set the time in seconds between sensor captures
        self.camera_bp.set_attribute('sensor_tick', '0.02')

        # Set fixed simulation step for synchronous mode
        self.settings = self.world.get_settings()
        self.settings.fixed_delta_seconds = self.dt

        # Record the time of total steps and resetting steps
        self.reset_step = 0
        self.total_step = 0

        # Initialize the renderer
        self._init_renderer()

        self.current_waypoint_index = 0
        self.auto_pilot_mode = False

        # Get pixel grid points
        if self.pixor:
            x, y = np.meshgrid(
                np.arange(self.pixor_size),
                np.arange(self.pixor_size))  # make a canvas with coordinates
            x, y = x.flatten(), y.flatten()
            self.pixel_grid = np.vstack((x, y)).T

    def get_client(self):
        return self.client

    def get_car(self):
        return self.ego

    def get_speed(self):
        v = self.ego.get_velocity()
        speed_kmh = 3.6 * np.sqrt(v.x**2 + v.y**2 + v.z**2)
        return speed_kmh

    def reset(self):
        # Clear sensor objects
        self.collision_sensor = None
        self.lidar_sensor = None
        self.camera_sensor = None
        self.step_rewards = []
        self.step_actions = []
        self.step_steering = []

        self.birdview_producer = BirdViewProducer(
            self.client,  # carla.Client
            target_size=PixelDimensions(width=self.image_width,
                                        height=self.image_height),
            pixels_per_meter=4,
            crop_type=BirdViewCropType.FRONT_AND_REAR_AREA)

        # Delete sensors, vehicles and walkers
        self._clear_all_actors([
            'sensor.other.collision', 'sensor.lidar.ray_cast',
            'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker',
            'walker.*'
        ])

        # Disable sync mode
        self._set_synchronous_mode(False)

        # Spawn surrounding vehicles
        random.shuffle(self.vehicle_spawn_points)
        count = self.number_of_vehicles
        if count > 0:
            for spawn_point in self.vehicle_spawn_points:
                if self._try_spawn_random_vehicle_at(spawn_point,
                                                     number_of_wheels=[4]):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_vehicle_at(random.choice(
                    self.vehicle_spawn_points),
                                                 number_of_wheels=[4]):
                count -= 1

        # Spawn pedestrians
        random.shuffle(self.walker_spawn_points)
        count = self.number_of_walkers
        if count > 0:
            for spawn_point in self.walker_spawn_points:
                if self._try_spawn_random_walker_at(spawn_point):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_walker_at(
                    random.choice(self.walker_spawn_points)):
                count -= 1

        # Get actors polygon list
        self.vehicle_polygons = []
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        self.walker_polygons = []
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)

        # Spawn the ego vehicle
        ego_spawn_times = 0
        while True:
            if ego_spawn_times > self.max_ego_spawn_times:
                self.reset()

            if self.task_mode == 'random':
                transform = random.choice(self.vehicle_spawn_points)
            if self.task_mode == 'roundabout':
                self.start = [52.1 + np.random.uniform(-5, 5), -4.2,
                              178.66]  # random
                # self.start=[52.1,-4.2, 178.66] # static
                transform = set_carla_transform(self.start)
            if self._try_spawn_ego_vehicle_at(transform):
                break
            else:
                ego_spawn_times += 1
                time.sleep(0.1)

        # Add collision sensor
        self.collision_sensor = self.world.spawn_actor(self.collision_bp,
                                                       carla.Transform(),
                                                       attach_to=self.ego)
        self.collision_sensor.listen(lambda event: get_collision_hist(event))

        def get_collision_hist(event):
            impulse = event.normal_impulse
            intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
            self.collision_hist.append(intensity)
            if len(self.collision_hist) > self.collision_hist_l:
                self.collision_hist.pop(0)

        self.collision_hist = []

        # Add lidar sensor
        self.lidar_sensor = self.world.spawn_actor(self.lidar_bp,
                                                   self.lidar_trans,
                                                   attach_to=self.ego)
        self.lidar_sensor.listen(lambda data: get_lidar_data(data))

        def get_lidar_data(data):
            self.lidar_data = data

        # Add camera sensor
        self.camera_sensor = self.world.spawn_actor(self.camera_bp,
                                                    self.camera_trans,
                                                    attach_to=self.ego)
        self.camera_sensor.listen(lambda data: get_camera_img(data))

        def get_camera_img(data):
            array = np.frombuffer(data.raw_data, dtype=np.dtype("uint8"))
            array = np.reshape(array, (data.height, data.width, 4))
            array = array[:, :, :3]
            i3 = (array) / 255.
            array = array[:, :, ::-1]
            self.camera_img = array
            self.original_camera_image = i3

        # Update timesteps
        self.time_step = 0
        self.reset_step += 1

        # Enable sync mode
        self.settings.synchronous_mode = True
        self.world.apply_settings(self.settings)

        self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()
        # get all the route waypoints
        self.route_waypoints = self.routeplanner._get_waypoints_data()
        #print(" Route waypoints : {} ".format(len(self.route_waypoints)))
        # Set ego information for render
        self.birdeye_render.set_hero(self.ego, self.ego.id)

        self.current_waypoint_index = 0
        self.step_start_location = self.ego.get_location()
        self.step_last_location = self.step_start_location
        return self._get_obs()

    def dump(self):
        print("Step throttle : {} ".format(self.step_actions))
        print("Step steering : {} ".format(self.step_steering))
        print("Step rewards  : {} ".format(self.step_rewards))

    def get_steering_angle(self):
        physics_control = self.ego.get_physics_control()
        for wheel in physics_control.wheels:
            print(wheel.max_steer_angle)

    def auto(self, value):
        self.auto_pilot_mode = value
        self.ego.set_autopilot(value)

    def isauto(self):
        return self.auto_pilot_mode

    def move(self):
        self.world.tick()
        return self._get_obs()

    def get_action_auto(self):
        control = self.ego.get_control()
        return control.throttle, control.steer

    def move_auto(self):
        self.world.tick()
        # Keep track of closest waypoint on the route
        transform = self.ego.get_transform()
        waypoint_index = self.current_waypoint_index
        for _ in range(len(self.waypoints)):
            # Check if we passed the next waypoint along the route
            next_waypoint_index = waypoint_index + 1
            wp = self.route_waypoints[next_waypoint_index %
                                      len(self.waypoints)]
            dot = np.dot(
                vector(wp.transform.get_forward_vector())[:2],
                vector(transform.location - wp.transform.location)[:2])
            if dot > 0.0:  # Did we pass the waypoint?
                waypoint_index += 1  # Go to next waypoint

        self.current_waypoint_index = waypoint_index
        v_transform = self.ego.get_transform()
        current_waypoint = self.route_waypoints[self.current_waypoint_index %
                                                len(self.waypoints)]
        next_waypoint = self.route_waypoints[(self.current_waypoint_index + 1)
                                             % len(self.waypoints)]
        self.distance_from_center = distance_to_line(
            vector(current_waypoint.transform.location),
            vector(next_waypoint.transform.location),
            vector(v_transform.location))
        self.current_waypoint = current_waypoint
        self.next_waypoint = next_waypoint
        control = self.ego.get_control()
        isdone, isout = self._terminal()
        reward = self.get_reward_speed(isdone, control.steer, isout)
        return self._get_obs(), control.throttle, control.steer, reward, isdone

    def step(self, action):
        # Calculate acceleration and steering
        self.throttle = 0
        self.steer = 0
        if self.discrete:
            #acc = self.discrete_act[0][action//self.n_steer]
            #steer = self.discrete_act[1][action%self.n_steer]
            #acc = self.discrete_act[0][action]
            #steer = self.discrete_act[1][action%self.n_steer]
            acc = action[0]
            steer = action[1]
        else:
            acc = action[0]
            steer = action[1]

        # Keep track of closest waypoint on the route
        transform = self.ego.get_transform()
        waypoint_index = self.current_waypoint_index
        for _ in range(len(self.waypoints)):
            # Check if we passed the next waypoint along the route
            next_waypoint_index = waypoint_index + 1
            wp = self.route_waypoints[next_waypoint_index %
                                      len(self.waypoints)]
            dot = np.dot(
                vector(wp.transform.get_forward_vector())[:2],
                vector(transform.location - wp.transform.location)[:2])
            if dot > 0.0:  # Did we pass the waypoint?
                waypoint_index += 1  # Go to next waypoint

        self.current_waypoint_index = waypoint_index
        v_transform = self.ego.get_transform()
        current_waypoint = self.route_waypoints[self.current_waypoint_index %
                                                len(self.waypoints)]
        next_waypoint = self.route_waypoints[(self.current_waypoint_index + 1)
                                             % len(self.waypoints)]
        self.distance_from_center = distance_to_line(
            vector(current_waypoint.transform.location),
            vector(next_waypoint.transform.location),
            vector(v_transform.location))
        self.current_waypoint = current_waypoint
        self.next_waypoint = next_waypoint
        #print("current way point idx {} ".format(self.current_waypoint_index))
        # Convert acceleration to throttle and brake
        if acc > 0:
            #throttle = np.clip(acc/3,0,1)
            #throttle = np.clip(acc,0,1)
            #throttle = acc
            #brake = 0
            throttle = acc
            brake = 0
        else:
            throttle = 0
            brake = 0
            #brake = np.clip(-acc/8,0,1)
            #brake = np.clip(-acc,0,1)

        self.throttle = throttle
        self.steer = steer

        # Apply control
        if self.auto_pilot_mode:
            self.world.tick()
        else:
            act = carla.VehicleControl(throttle=float(throttle),
                                       steer=float(steer),
                                       brake=float(brake))
            self.ego.apply_control(act)
            self.world.tick()

        # Append actors polygon list
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        while len(self.vehicle_polygons) > self.max_past_step:
            self.vehicle_polygons.pop(0)
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)
        while len(self.walker_polygons) > self.max_past_step:
            self.walker_polygons.pop(0)

        # route planner
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()
        # get all the route waypoints
        self.route_waypoints = self.routeplanner._get_waypoints_data()

        # state information
        info = {
            'waypoints': self.waypoints,
            'vehicle_front': self.vehicle_front
        }

        # Update timesteps
        self.time_step += 1
        self.total_step += 1

        isdone, isout = self._terminal()
        self.step_end_location = self.ego.get_location()
        #reward , centre , coll, out = self._get_reward_speed_centering(isdone)
        reward = self.get_reward_speed(isdone, steer, isout)
        self.step_actions.append(throttle)
        self.step_steering.append(steer)
        self.step_rewards.append(reward)

        if isdone:
            print("Final Speed reward : {}".format(reward))
        return (self._get_obs(), reward, isdone, copy.deepcopy(info))

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def render(self, mode):
        pass

    def _create_vehicle_bluepprint(self,
                                   actor_filter,
                                   color=None,
                                   number_of_wheels=[4]):
        """Create the blueprint for a specific actor type.

    Args:
      actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

    Returns:
      bp: the blueprint object of carla.
    """
        blueprints = self.world.get_blueprint_library().filter(actor_filter)
        blueprint_library = []
        for nw in number_of_wheels:
            blueprint_library = blueprint_library + [
                x for x in blueprints
                if int(x.get_attribute('number_of_wheels')) == nw
            ]
        bp = random.choice(blueprint_library)
        if bp.has_attribute('color'):
            if not color:
                color = random.choice(
                    bp.get_attribute('color').recommended_values)
            bp.set_attribute('color', color)
        return bp

    def _init_renderer(self):
        """Initialize the birdeye view renderer.
    """
        pygame.init()
        self.display = pygame.display.set_mode(
            (self.display_size * 3, self.display_size),
            pygame.HWSURFACE | pygame.DOUBLEBUF)

        pixels_per_meter = self.display_size / self.obs_range
        pixels_ahead_vehicle = (self.obs_range / 2 -
                                self.d_behind) * pixels_per_meter
        birdeye_params = {
            'screen_size': [self.display_size, self.display_size],
            'pixels_per_meter': pixels_per_meter,
            'pixels_ahead_vehicle': pixels_ahead_vehicle
        }
        self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

    def _set_synchronous_mode(self, synchronous=True):
        """Set whether to use the synchronous mode.
    """
        self.settings.synchronous_mode = synchronous
        self.world.apply_settings(self.settings)

    def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
        """Try to spawn a surrounding vehicle at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
        blueprint = self._create_vehicle_bluepprint(
            'vehicle.*', number_of_wheels=number_of_wheels)
        blueprint.set_attribute('role_name', 'autopilot')
        vehicle = self.world.try_spawn_actor(blueprint, transform)
        if vehicle is not None:
            vehicle.set_autopilot()
            return True
        return False

    def _try_spawn_random_walker_at(self, transform):
        """Try to spawn a walker at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
        walker_bp = random.choice(
            self.world.get_blueprint_library().filter('walker.*'))
        # set as not invencible
        if walker_bp.has_attribute('is_invincible'):
            walker_bp.set_attribute('is_invincible', 'false')
        walker_actor = self.world.try_spawn_actor(walker_bp, transform)

        if walker_actor is not None:
            walker_controller_bp = self.world.get_blueprint_library().find(
                'controller.ai.walker')
            walker_controller_actor = self.world.spawn_actor(
                walker_controller_bp, carla.Transform(), walker_actor)
            # start walker
            walker_controller_actor.start()
            # set walk to random point
            walker_controller_actor.go_to_location(
                self.world.get_random_location_from_navigation())
            # random max speed
            walker_controller_actor.set_max_speed(
                1 + random.random()
            )  # max speed between 1 and 2 (default is 1.4 m/s)
            return True
        return False

    def _try_spawn_ego_vehicle_at(self, transform):
        """Try to spawn the ego vehicle at specific transform.
    Args:
      transform: the carla transform object.
    Returns:
      Bool indicating whether the spawn is successful.
    """
        vehicle = None
        # Check if ego position overlaps with surrounding vehicles
        overlap = False
        for idx, poly in self.vehicle_polygons[-1].items():
            poly_center = np.mean(poly, axis=0)
            ego_center = np.array([transform.location.x, transform.location.y])
            dis = np.linalg.norm(poly_center - ego_center)
            if dis > 8:
                continue
            else:
                overlap = True
                break

        if not overlap:
            vehicle = self.world.try_spawn_actor(self.ego_bp, transform)

        if vehicle is not None:
            vehicle.set_simulate_physics(True)
            self.ego = vehicle
            return True

        return False

    def _get_actor_polygons(self, filt):
        """Get the bounding box polygon of actors.

    Args:
      filt: the filter indicating what type of actors we'll look at.

    Returns:
      actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
    """
        actor_poly_dict = {}
        for actor in self.world.get_actors().filter(filt):
            # Get x, y and yaw of the actor
            trans = actor.get_transform()
            x = trans.location.x
            y = trans.location.y
            yaw = trans.rotation.yaw / 180 * np.pi
            # Get length and width
            bb = actor.bounding_box
            l = bb.extent.x
            w = bb.extent.y
            # Get bounding box polygon in the actor's local coordinate
            poly_local = np.array([[l, w], [l, -w], [-l, -w],
                                   [-l, w]]).transpose()
            # Get rotation matrix to transform to global coordinate
            R = np.array([[np.cos(yaw), -np.sin(yaw)],
                          [np.sin(yaw), np.cos(yaw)]])
            # Get global bounding box polygon
            poly = np.matmul(R, poly_local).transpose() + np.repeat(
                [[x, y]], 4, axis=0)
            actor_poly_dict[actor.id] = poly
        return actor_poly_dict

    def _get_obs(self):
        """Get the observations."""
        ## Birdeye rendering
        self.birdeye_render.vehicle_polygons = self.vehicle_polygons
        self.birdeye_render.walker_polygons = self.walker_polygons
        self.birdeye_render.waypoints = self.waypoints

        # birdeye view with roadmap and actors
        birdeye_render_types = ['roadmap', 'actors']
        if self.display_route:
            birdeye_render_types.append('waypoints')
        self.birdeye_render.render(self.display, birdeye_render_types)
        birdeye = pygame.surfarray.array3d(self.display)
        birdeye = birdeye[0:self.display_size, :, :]
        birdeye = display_to_rgb(birdeye, self.obs_size)

        # Roadmap
        if self.pixor:
            roadmap_render_types = ['roadmap']
            if self.display_route:
                roadmap_render_types.append('waypoints')
            self.birdeye_render.render(self.display, roadmap_render_types)
            roadmap = pygame.surfarray.array3d(self.display)
            roadmap = roadmap[0:self.display_size, :, :]
            roadmap = display_to_rgb(roadmap, self.obs_size)
            # Add ego vehicle
            for i in range(self.obs_size):
                for j in range(self.obs_size):
                    if abs(birdeye[i, j, 0] - 255) < 20 and abs(
                            birdeye[i, j, 1] -
                            0) < 20 and abs(birdeye[i, j, 0] - 255) < 20:
                        roadmap[i, j, :] = birdeye[i, j, :]

        # Display birdeye image
        birdeye_surface = rgb_to_display_surface(birdeye, self.display_size)
        self.display.blit(birdeye_surface, (0, 0))

        ## Lidar image generation
        point_cloud = []
        # Get point cloud data
        for location in self.lidar_data:
            point_cloud.append([location.x, location.y, -location.z])
        point_cloud = np.array(point_cloud)
        # Separate the 3D space to bins for point cloud, x and y is set according to self.lidar_bin,
        # and z is set to be two bins.
        y_bins = np.arange(-(self.obs_range - self.d_behind),
                           self.d_behind + self.lidar_bin, self.lidar_bin)
        x_bins = np.arange(-self.obs_range / 2,
                           self.obs_range / 2 + self.lidar_bin, self.lidar_bin)
        z_bins = [-self.lidar_height - 1, -self.lidar_height + 0.25, 1]
        # Get lidar image according to the bins
        lidar, _ = np.histogramdd(point_cloud, bins=(x_bins, y_bins, z_bins))
        lidar[:, :, 0] = np.array(lidar[:, :, 0] > 0, dtype=np.uint8)
        lidar[:, :, 1] = np.array(lidar[:, :, 1] > 0, dtype=np.uint8)
        # Add the waypoints to lidar image
        if self.display_route:
            wayptimg = (birdeye[:, :, 0] <= 10) * (birdeye[:, :, 1] <= 10) * (
                birdeye[:, :, 2] >= 240)
        else:
            wayptimg = birdeye[:, :, 0] < 0  # Equal to a zero matrix
        wayptimg = np.expand_dims(wayptimg, axis=2)
        wayptimg = np.fliplr(np.rot90(wayptimg, 3))

        # Get the final lidar image
        lidar = np.concatenate((lidar, wayptimg), axis=2)
        lidar = np.flip(lidar, axis=1)
        lidar = np.rot90(lidar, 1)
        lidar = lidar * 255

        # Display lidar image
        lidar_surface = rgb_to_display_surface(lidar, self.display_size)
        self.display.blit(lidar_surface, (self.display_size, 0))

        ## Display camera image
        camera = resize(self.camera_img, (self.obs_size, self.obs_size)) * 255
        camera_surface = rgb_to_display_surface(camera, self.display_size)
        self.display.blit(camera_surface, (self.display_size * 2, 0))

        ## Display processed camera image
        original_camera = resize(self.original_camera_image,
                                 (self.image_height, self.image_width))
        #print("original camera : {}".format(original_camera))

        o_camera_surface = rgb_to_display_surface(camera, self.display_size)
        self.display.blit(o_camera_surface, (self.display_size * 2, 0))

        # Display on pygame
        pygame.display.flip()

        # State observation
        ego_trans = self.ego.get_transform()
        ego_x = ego_trans.location.x
        ego_y = ego_trans.location.y
        ego_yaw = ego_trans.rotation.yaw / 180 * np.pi
        lateral_dis, w = get_preview_lane_dis(self.waypoints, ego_x, ego_y)
        delta_yaw = np.arcsin(
            np.cross(w, np.array(np.array([np.cos(ego_yaw),
                                           np.sin(ego_yaw)]))))
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        state = np.array([lateral_dis, -delta_yaw, speed, self.vehicle_front])

        if self.pixor:
            ## Vehicle classification and regression maps (requires further normalization)
            vh_clas = np.zeros((self.pixor_size, self.pixor_size))
            vh_regr = np.zeros((self.pixor_size, self.pixor_size, 6))

            # Generate the PIXOR image. Note in CARLA it is using left-hand coordinate
            # Get the 6-dim geom parametrization in PIXOR, here we use pixel coordinate
            for actor in self.world.get_actors().filter('vehicle.*'):
                x, y, yaw, l, w = get_info(actor)
                x_local, y_local, yaw_local = get_local_pose(
                    (x, y, yaw), (ego_x, ego_y, ego_yaw))
                if actor.id != self.ego.id:
                    if abs(
                            y_local
                    ) < self.obs_range / 2 + 1 and x_local < self.obs_range - self.d_behind + 1 and x_local > -self.d_behind - 1:
                        x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel = get_pixel_info(
                            local_info=(x_local, y_local, yaw_local, l, w),
                            d_behind=self.d_behind,
                            obs_range=self.obs_range,
                            image_size=self.pixor_size)
                        cos_t = np.cos(yaw_pixel)
                        sin_t = np.sin(yaw_pixel)
                        logw = np.log(w_pixel)
                        logl = np.log(l_pixel)
                        pixels = get_pixels_inside_vehicle(
                            pixel_info=(x_pixel, y_pixel, yaw_pixel, l_pixel,
                                        w_pixel),
                            pixel_grid=self.pixel_grid)
                        for pixel in pixels:
                            vh_clas[pixel[0], pixel[1]] = 1
                            dx = x_pixel - pixel[0]
                            dy = y_pixel - pixel[1]
                            vh_regr[pixel[0], pixel[1], :] = np.array(
                                [cos_t, sin_t, dx, dy, logw, logl])

            # Flip the image matrix so that the origin is at the left-bottom
            vh_clas = np.flip(vh_clas, axis=0)
            vh_regr = np.flip(vh_regr, axis=0)

            # Pixor state, [x, y, cos(yaw), sin(yaw), speed]
            pixor_state = [
                ego_x, ego_y,
                np.cos(ego_yaw),
                np.sin(ego_yaw), speed
            ]

        birdview: BirdView = self.birdview_producer.produce(
            agent_vehicle=self.ego)
        bgr = cv2.cvtColor(BirdViewProducer.as_rgb(birdview), cv.COLOR_BGR2RGB)

        v = self.ego.get_velocity()
        speed_kmh = 3.6 * np.sqrt(v.x**2 + v.y**2 + v.z**2)
        speed_kmh = np.array([speed_kmh])
        obs = {
            'original_camera': original_camera.astype(np.uint8),
            'speed': speed_kmh.astype(np.uint8),
            'camera': camera.astype(np.uint8),
            'lidar': lidar.astype(np.uint8),
            'birdeye': birdeye.astype(np.uint8),
            'driving_image': bgr.astype(np.uint8),
            'state': state,
        }

        if self.pixor:
            obs.update({
                'roadmap':
                roadmap.astype(np.uint8),
                'vh_clas':
                np.expand_dims(vh_clas, -1).astype(np.float32),
                'vh_regr':
                vh_regr.astype(np.float32),
                'pixor_state':
                pixor_state,
            })

        return obs

    def get_reward_speed(self, isdone, steer, isout):
        v = self.ego.get_velocity()
        speed_kmh = 3.6 * np.sqrt(v.x**2 + v.y**2 + v.z**2)

        if speed_kmh <= min_speed:
            r_speed = speed_kmh
        elif speed_kmh > max_speed:
            r_speed = max_speed - speed_kmh
        else:
            r_speed = speed_kmh

        r_steer = -(steer * steer)
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -10.0

        r_out = 0
        if isout:
            r_out = -1

        reward_t = r_speed + r_steer + r_collision + r_out - 0.1
        if math.isnan(reward_t):
            print("Reward is Nan")
            print("r_speed :  {} ".format(r_speed))
            print("r_steer :  {} ".format(r_steer))
            print("steer :  {} ".format(steer))
        if reward_t == 0:
            print("Reward is 0")
        return reward_t

    def _get_reward_speed_centering(self, isdone):
        """
        reward = Positive speed reward for being close to target speed,
                 however, quick decline in reward beyond target speed
               * centering factor (1 when centered, 0 when not)
               * angle factor (1 when aligned with the road, 0 when more than 20 degress off)
    """
        v = self.ego.get_velocity()
        fwd = vector(v)
        #wp         = self.world.get_map().get_waypoint(self.ego.get_location())
        #wp_fwd     = vector(wp.transform.rotation.get_forward_vector())
        wp_fwd = vector(
            self.current_waypoint.transform.rotation.get_forward_vector())
        angle = angle_diff(fwd, wp_fwd)
        speed_kmh = 3.6 * np.sqrt(v.x**2 + v.y**2 + v.z**2)

        if self.throttle <= 0:
            #if throttle is negative we can not move
            speed_reward = -10.0
        else:
            if speed_kmh < min_speed:  # When speed is in [0, min_speed] range
                speed_reward = speed_kmh
            elif speed_kmh >= min_speed and speed_kmh <= target_speed:
                speed_reward = speed_kmh
            elif speed_kmh > target_speed:
                speed_reward = min_speed  # only give min speed reward

        collision_reward = 0
        if len(self.collision_hist) > 0:
            collision_reward = -1

        current_location = self.ego.get_location()
        start = self.step_last_location
        dist = current_location.distance(start)  # in meteres
        dist_reward = dist
        print("Distance reward = {} * Speed rewards {} , throttle {} ".format(
            dist_reward, speed_reward, self.throttle))

        # update last location
        self.step_last_location = current_location

        # Interpolated from 1 when centered to 0 when 3 m from center
        centering_factor = max(
            1.0 - self.distance_from_center / self.max_distance, 0.0)
        centering_reward = max(self.distance_from_center / self.max_distance,
                               0.0)

        # Interpolated from 1 when aligned with the road to 0 when +/- 20 degress of road
        angle_factor = max(1.0 - abs(angle / np.deg2rad(20)), 0.0)

        # reward for out of lane
        ego_x, ego_y = get_pos(self.ego)
        dis, w = get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        if abs(dis) > self.out_lane_thres:
            r_out = -1

        # Final reward
        reward = dist_reward + centering_factor + 10 * collision_reward
        #print("Dist reward {}  * centering_factor {} ".format(dist_reward, centering_factor))
        return reward, centering_factor, 10 * collision_reward, r_out

    def _get_reward(self):
        """Calculate the step reward."""
        # reward for speed tracking
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        r_speed = -abs(speed - self.desired_speed)

        # reward for collision
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -1

        # reward for steering:
        r_steer = -self.ego.get_control().steer**2

        # reward for out of lane
        ego_x, ego_y = get_pos(self.ego)
        dis, w = get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        if abs(dis) > self.out_lane_thres:
            r_out = -1

        # reward for not being in the centre of the lane
        # get the way point to the centre of the road
        waypoint = self.world.get_map().get_waypoint(self.ego.get_location(),
                                                     project_to_road=True)
        ways = np.array([[
            waypoint.transform.location.x, waypoint.transform.location.y,
            waypoint.transform.rotation.yaw
        ]])
        dis, w = get_preview_lane_dis(ways, ego_x, ego_y, 0)
        if (np.isnan(dis)):
            r_centre = 2.383e-07
            print("Car centre distance is nan , rcenter = {}", r_centre)
        else:
            r_centre = abs(dis)
        #print("Car centre distance : {}".format(r_centre))

        # longitudinal speed
        lspeed = np.array([v.x, v.y])
        lspeed_lon = np.dot(lspeed, w)

        # cost for too fast
        r_fast = 0
        if lspeed_lon > self.desired_speed:
            r_fast = -1

        # cost for lateral acceleration
        r_lat = -abs(self.ego.get_control().steer) * lspeed_lon**2

        #r = 200*r_collision + 1*lspeed_lon + 10*r_fast + 1*r_out + r_steer*5 + 0.2*r_lat - 0.1
        #r = 300*r_collision + 1*lspeed_lon + 10*r_fast + 200*r_out + r_steer*5 + 0.2*r_lat - 0.1
        r = 200 * r_collision + 1 * lspeed_lon + 10 * r_fast + 70 * r_out + r_steer * 5 + 0.2 * r_lat - 120 * r_centre - 0.1

        return r, (-120 * r_centre), (200 * r_collision), (70 * r_out)

    def _terminal(self):
        """Calculate whether to terminate the current episode."""
        # Get ego state
        ego_x, ego_y = get_pos(self.ego)

        # If collides
        if len(self.collision_hist) > 0:
            return True, False

        # If reach maximum timestep
        if self.time_step > self.max_time_episode:
            return True, False

        # If at destination
        if self.dests is not None:  # If at destination
            for dest in self.dests:
                if np.sqrt((ego_x - dest[0])**2 + (ego_y - dest[1])**2) < 4:
                    return True, False

        # Stop if distance from center > max distance
        if self.distance_from_center > self.max_distance:
            print("End : Distance from center more than max distance : {} ".
                  format(self.distance_from_center))
            return True, True

        # If out of lane
        dis, _ = get_lane_dis(self.waypoints, ego_x, ego_y)
        if abs(dis) > self.out_lane_thres:
            print("End : Out of Lane , distance : {} ".format(dis))
            return True, True

        # Speed is too fast
        v = self.ego.get_velocity()
        speed_kmh = 3.6 * np.sqrt(v.x**2 + v.y**2 + v.z**2)
        if max_speed > 0 and speed_kmh > max_speed:
            print("End : Too fast {} ".format(speed_kmh))
            return True, False
        return False, False

    def _clear_all_actors(self, actor_filters):
        """Clear specific actors."""
        for actor_filter in actor_filters:
            for actor in self.world.get_actors().filter(actor_filter):
                if actor.is_alive:
                    if actor.type_id == 'controller.ai.walker':
                        actor.stop()
                    actor.destroy()
                    print("Destroy : {} ".format(actor.type_id))
                else:
                    print("Not Destroyed : {} ".format(actor.type_id))
Exemplo n.º 3
0
class CarlaEnv(gym.Env):
  """An OpenAI gym wrapper for CARLA simulator."""

  def __init__(self, params):
    # parameters
    self.display_size = params['display_size']  # rendering screen size
    self.max_past_step = params['max_past_step']
    self.number_of_vehicles = params['number_of_vehicles']
    self.number_of_walkers = params['number_of_walkers']
    self.dt = params['dt']
    self.task_mode = params['task_mode']
    self.max_time_episode = params['max_time_episode']
    self.max_waypt = params['max_waypt']
    self.obs_range = params['obs_range']
    self.lidar_bin = params['lidar_bin']
    self.d_behind = params['d_behind']
    self.obs_size = int(self.obs_range/self.lidar_bin)
    self.out_lane_thres = params['out_lane_thres']
    self.desired_speed = params['desired_speed']
    self.max_ego_spawn_times = params['max_ego_spawn_times']
    self.display_route = params['display_route']
    self.pixor_size = params['pixor_size']

    # Destination
    if params['task_mode'] == 'roundabout':
      self.dests = [[4.46, -61.46, 0], [-49.53, -2.89, 0], [-6.48, 55.47, 0], [35.96, 3.33, 0]]
    else:
      self.dests = None

    # action and observation spaces
    self.discrete = params['discrete']
    self.discrete_act = [params['discrete_acc'], params['discrete_steer']] # acc, steer
    self.n_acc = len(self.discrete_act[0])
    self.n_steer = len(self.discrete_act[1])
    if self.discrete:
      self.action_space = spaces.Discrete(self.n_acc*self.n_steer)
    else:
      self.action_space = spaces.Box(np.array([params['continuous_accel_range'][0], 
      params['continuous_steer_range'][0]]), np.array([params['continuous_accel_range'][1],
      params['continuous_steer_range'][1]]), dtype=np.float32)  # acc, steer
    self.observation_space = spaces.Dict({'birdeye': spaces.Box(low=0, high=255, shape=(self.obs_size, self.obs_size, 3), dtype=np.uint8),
      'lidar': spaces.Box(low=0, high=255, shape=(self.obs_size, self.obs_size, 3), dtype=np.uint8),
      'camera': spaces.Box(low=0, high=255, shape=(self.obs_size, self.obs_size, 3), dtype=np.uint8),
      'roadmap': spaces.Box(low=0, high=255, shape=(self.obs_size, self.obs_size, 3), dtype=np.uint8),
      'vh_clas': spaces.Box(low=0, high=1, shape=(self.pixor_size, self.pixor_size, 1), dtype=np.float32),
      'vh_regr': spaces.Box(low=-5, high=5, shape=(self.pixor_size, self.pixor_size, 6), dtype=np.float32),
      'state': spaces.Box(np.array([-2, -1, -5, 0]), np.array([2, 1, 30, 1]), dtype=np.float32),
      'pixor_state': spaces.Box(np.array([-1000, -1000, -1, -1, -5]), np.array([1000, 1000, 1, 1, 20]), dtype=np.float32),
      'costmap': spaces.Box(low=0, high=255, shape=(self.obs_size, self.obs_size, 1), dtype=np.uint8)}) #costmap should be a 2d array

    # Connect to carla server and get world object
    print('connecting to Carla server...')
    client = carla.Client('localhost', params['port'])
    client.set_timeout(10.0)
    self.world = client.load_world(params['town'])
    print('Carla server connected!')

    # Set weather
    self.world.set_weather(carla.WeatherParameters.ClearNoon)

    # Get spawn points
    self.vehicle_spawn_points = list(self.world.get_map().get_spawn_points())
    self.walker_spawn_points = []
    for i in range(self.number_of_walkers):
      spawn_point = carla.Transform()
      loc = self.world.get_random_location_from_navigation()
      if (loc != None):
        spawn_point.location = loc
        self.walker_spawn_points.append(spawn_point)

    # Create the ego vehicle blueprint
    self.ego_bp = self._create_vehicle_bluepprint(params['ego_vehicle_filter'], color='49,8,8')

    # Collision sensor
    self.collision_hist = [] # The collision history
    self.collision_hist_l = 1 # collision history length
    self.collision_bp = self.world.get_blueprint_library().find('sensor.other.collision')

    # Lidar sensor
    self.lidar_data = None
    self.lidar_height = 2.1
    self.lidar_trans = carla.Transform(carla.Location(x=0.0, z=self.lidar_height))
    self.lidar_bp = self.world.get_blueprint_library().find('sensor.lidar.ray_cast')
    self.lidar_bp.set_attribute('channels', '32')
    self.lidar_bp.set_attribute('range', '5000')

    # Camera sensor
    self.camera_img = np.zeros((self.obs_size, self.obs_size, 3), dtype=np.uint8)
    self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
    self.camera_bp = self.world.get_blueprint_library().find('sensor.camera.rgb')
    # Modify the attributes of the blueprint to set image resolution and field of view.
    self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
    self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
    self.camera_bp.set_attribute('fov', '110')
    # Set the time in seconds between sensor captures
    self.camera_bp.set_attribute('sensor_tick', '0.02')

    # Set fixed simulation step for synchronous mode
    self.settings = self.world.get_settings()
    self.settings.fixed_delta_seconds = self.dt

    # Record the time of total steps and resetting steps
    self.reset_step = 0
    self.total_step = 0
    
    # Initialize the renderer
    self._init_renderer()

    # Get pixel grid points
    x, y = np.meshgrid(np.arange(self.pixor_size), np.arange(self.pixor_size)) # make a canvas with coordinates
    x, y = x.flatten(), y.flatten()
    self.pixel_grid = np.vstack((x,y)).T 

  def reset(self):
    # Clear sensor objects  
    self.collision_sensor = None
    self.lidar_sensor = None
    self.camera_sensor = None

    # Delete sensors, vehicles and walkers
    self._clear_all_actors(['sensor.other.collision', 'sensor.lidar.ray_cast', 'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker', 'walker.*'])

    # Disable sync mode
    self._set_synchronous_mode(False)

    # Spawn surrounding vehicles
    random.shuffle(self.vehicle_spawn_points)
    count = self.number_of_vehicles
    if count > 0:
      for spawn_point in self.vehicle_spawn_points:
        if self._try_spawn_random_vehicle_at(spawn_point, number_of_wheels=[4]):
          count -= 1
        if count <= 0:
          break
    while count > 0:
      if self._try_spawn_random_vehicle_at(random.choice(self.vehicle_spawn_points), number_of_wheels=[4]):
        count -= 1

    # Spawn pedestrians
    random.shuffle(self.walker_spawn_points)
    count = self.number_of_walkers
    if count > 0:
      for spawn_point in self.walker_spawn_points:
        if self._try_spawn_random_walker_at(spawn_point):
          count -= 1
        if count <= 0:
          break
    while count > 0:
      if self._try_spawn_random_walker_at(random.choice(self.walker_spawn_points)):
        count -= 1

    # Get actors polygon list
    self.vehicle_polygons = []
    vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
    self.vehicle_polygons.append(vehicle_poly_dict)
    self.walker_polygons = []
    walker_poly_dict = self._get_actor_polygons('walker.*')
    self.walker_polygons.append(walker_poly_dict)

    # Spawn the ego vehicle
    ego_spawn_times = 0
    while True:
      if ego_spawn_times > self.max_ego_spawn_times:
        self.reset()

      if self.task_mode == 'random':
        #transform = random.choice(self.vehicle_spawn_points)
        transform = self.vehicle_spawn_points[0]
        #transform.rotation.yaw = 0
        tup = (transform.location.x, transform.location.y, transform.rotation.yaw)
        print("Transform: " + str(tup))
      if self.task_mode == 'roundabout':
        self.start=[52.1+np.random.uniform(-5,5),-4.2, 178.66] # random
        # self.start=[52.1,-4.2, 178.66] # static
        transform = self._set_carla_transform(self.start)
      if self._try_spawn_ego_vehicle_at(transform):
        break
      else:
        ego_spawn_times += 1
        time.sleep(0.1)

    # Add collision sensor
    self.collision_sensor = self.world.spawn_actor(self.collision_bp, carla.Transform(), attach_to=self.ego)
    self.collision_sensor.listen(lambda event: get_collision_hist(event))
    def get_collision_hist(event):
      impulse = event.normal_impulse
      intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
      self.collision_hist.append(intensity)
      if len(self.collision_hist)>self.collision_hist_l:
        self.collision_hist.pop(0)
    self.collision_hist = []

    # Add lidar sensor
    self.lidar_sensor = self.world.spawn_actor(self.lidar_bp, self.lidar_trans, attach_to=self.ego)
    self.lidar_sensor.listen(lambda data: get_lidar_data(data))
    def get_lidar_data(data):
      self.lidar_data = data

    # Add camera sensor
    self.camera_sensor = self.world.spawn_actor(self.camera_bp, self.camera_trans, attach_to=self.ego)
    self.camera_sensor.listen(lambda data: get_camera_img(data))
    def get_camera_img(data):
      array = np.frombuffer(data.raw_data, dtype = np.dtype("uint8"))
      array = np.reshape(array, (data.height, data.width, 4))
      array = array[:, :, :3]
      array = array[:, :, ::-1]
      self.camera_img = array

    # Update timesteps
    self.time_step=0
    self.reset_step+=1

    # Enable sync mode
    self.settings.synchronous_mode = True
    self.world.apply_settings(self.settings)

    self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
    self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

    # Set ego information for render
    self.birdeye_render.set_hero(self.ego, self.ego.id)

    return self._get_obs()
  
  def step(self, action):
    # Calculate acceleration and steering
    if self.discrete:
      acc = self.discrete_act[0][action//self.n_steer]
      steer = self.discrete_act[1][action%self.n_steer]
    else:
      acc = action[0]
      steer = action[1]

    # Convert acceleration to throttle and brake
    if acc > 0:
      throttle = np.clip(acc/3,0,1)
      brake = 0
    else:
      throttle = 0
      brake = np.clip(-acc/8,0,1)

    # Apply control
    act = carla.VehicleControl(throttle=float(throttle), steer=float(-steer), brake=float(brake))
    self.ego.apply_control(act)

    self.world.tick()

    # Append actors polygon list
    vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
    self.vehicle_polygons.append(vehicle_poly_dict)
    while len(self.vehicle_polygons) > self.max_past_step:
      self.vehicle_polygons.pop(0)
    walker_poly_dict = self._get_actor_polygons('walker.*')
    self.walker_polygons.append(walker_poly_dict)
    while len(self.walker_polygons) > self.max_past_step:
      self.walker_polygons.pop(0)

    # route planner
    self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

    # state information
    info = {
      'waypoints': self.waypoints,
      'vehicle_front': self.vehicle_front
    }
    
    # Update timesteps
    self.time_step += 1
    self.total_step += 1
    return (self._get_obs(), self._get_reward(), self._terminal(), copy.deepcopy(info))

  def seed(self, seed=None):
    self.np_random, seed = seeding.np_random(seed)
    return [seed]

  def render(self, mode):
    pass

  def _create_vehicle_bluepprint(self, actor_filter, color=None, number_of_wheels=[4]):
    """Create the blueprint for a specific actor type.

    Args:
      actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

    Returns:
      bp: the blueprint object of carla.
    """
    blueprints = self.world.get_blueprint_library().filter(actor_filter)
    blueprint_library = []
    for nw in number_of_wheels:
      blueprint_library = blueprint_library + [x for x in blueprints if int(x.get_attribute('number_of_wheels')) == nw]
    bp = random.choice(blueprint_library)
    if bp.has_attribute('color'):
      if not color:
        color = random.choice(bp.get_attribute('color').recommended_values)
      bp.set_attribute('color', color)
    return bp

  def _init_renderer(self):
    """Initialize the birdeye view renderer.
    """
    pygame.init()
    self.display = pygame.display.set_mode(
    (self.display_size * 4, self.display_size),
    pygame.HWSURFACE | pygame.DOUBLEBUF)

    pixels_per_meter = self.display_size / self.obs_range
    pixels_ahead_vehicle = (self.obs_range/2 - self.d_behind) * pixels_per_meter
    birdeye_params = {
      'screen_size': [self.display_size, self.display_size],
      'pixels_per_meter': pixels_per_meter,
      'pixels_ahead_vehicle': pixels_ahead_vehicle
    }
    self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

  def _set_synchronous_mode(self, synchronous = True):
    """Set whether to use the synchronous mode.
    """
    self.settings.synchronous_mode = synchronous
    self.world.apply_settings(self.settings)

  def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
    """Try to spawn a surrounding vehicle at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
    blueprint = self._create_vehicle_bluepprint('vehicle.*', number_of_wheels=number_of_wheels)
    blueprint.set_attribute('role_name', 'autopilot')
    vehicle = self.world.try_spawn_actor(blueprint, transform)
    if vehicle is not None:
      vehicle.set_autopilot()
      return True
    return False

  def _try_spawn_random_walker_at(self, transform):
    """Try to spawn a walker at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
    walker_bp = random.choice(self.world.get_blueprint_library().filter('walker.*'))
    # set as not invencible
    if walker_bp.has_attribute('is_invincible'):
      walker_bp.set_attribute('is_invincible', 'false')
    walker_actor = self.world.try_spawn_actor(walker_bp, transform)

    if walker_actor is not None:
      walker_controller_bp = self.world.get_blueprint_library().find('controller.ai.walker')
      walker_controller_actor = self.world.spawn_actor(walker_controller_bp, carla.Transform(), walker_actor)
      # start walker
      walker_controller_actor.start()
      # set walk to random point
      walker_controller_actor.go_to_location(self.world.get_random_location_from_navigation())
      # random max speed
      walker_controller_actor.set_max_speed(1 + random.random())    # max speed between 1 and 2 (default is 1.4 m/s)
      return True
    return False

  def _try_spawn_ego_vehicle_at(self, transform):
    """Try to spawn the ego vehicle at specific transform.
    Args:
      transform: the carla transform object.
    Returns:
      Bool indicating whether the spawn is successful.
    """
    vehicle = None
    # Check if ego position overlaps with surrounding vehicles
    overlap = False
    for idx, poly in self.vehicle_polygons[0].items():
      poly_center = np.mean(poly, axis=0)
      ego_center = np.array([transform.location.x, transform.location.y])
      dis = np.linalg.norm(poly_center - ego_center)
      if dis > 8:
        continue
      else:
        overlap = True
        break

    if not overlap:
      vehicle = self.world.try_spawn_actor(self.ego_bp, transform)

    if vehicle is not None:
      self.ego=vehicle
      return True
      
    return False

  def _set_carla_transform(self, pose):
    """Get a carla tranform object given pose.

    Args:
      pose: [x, y, yaw].

    Returns:
      transform: the carla transform object
    """
    transform = carla.Transform()
    transform.location.x = pose[0]
    transform.location.y = pose[1]
    transform.rotation.yaw = pose[2]
    return transform

  def _get_actor_polygons(self, filt):
    """Get the bounding box polygon of actors.

    Args:
      filt: the filter indicating what type of actors we'll look at.

    Returns:
      actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
    """
    actor_poly_dict={}
    for actor in self.world.get_actors().filter(filt):
      # Get x, y and yaw of the actor
      trans=actor.get_transform()
      x=trans.location.x
      y=trans.location.y
      yaw=trans.rotation.yaw/180*np.pi
      # Get length and width
      bb=actor.bounding_box
      l=bb.extent.x
      w=bb.extent.y
      # Get bounding box polygon in the actor's local coordinate
      poly_local=np.array([[l,w],[l,-w],[-l,-w],[-l,w]]).transpose()
      # Get rotation matrix to transform to global coordinate
      R=np.array([[np.cos(yaw),-np.sin(yaw)],[np.sin(yaw),np.cos(yaw)]])
      # Get global bounding box polygon
      poly=np.matmul(R,poly_local).transpose()+np.repeat([[x,y]],4,axis=0)
      actor_poly_dict[actor.id]=poly
    return actor_poly_dict

  def _get_ego(self):
    """ Get the ego vehicle object
    """
    return self.ego

  def _display_to_rgb(self, display):
    """ Transform image grabbed from pygame display to an rgb image uint8 matrix
    """
    rgb = np.fliplr(np.rot90(display, 3))  # flip to regular view
    rgb = resize(rgb, (self.obs_size, self.obs_size))  # resize
    rgb = rgb * 255
    return rgb

  def _rgb_to_display_surface(self, rgb):
    """ Generate pygame surface given an rgb image uint8 matrix
    """
    surface = pygame.Surface((self.display_size, self.display_size)).convert()
    display = resize(rgb, (self.display_size, self.display_size))
    display = np.flip(display, axis=1)
    display = np.rot90(display, 1)
    pygame.surfarray.blit_array(surface, display)
    return surface

  def _get_obs(self):
    """Get the observations."""
    ## Birdeye rendering
    self.birdeye_render.vehicle_polygons = self.vehicle_polygons
    self.birdeye_render.walker_polygons = self.walker_polygons
    self.birdeye_render.waypoints = self.waypoints

    # birdeye view with roadmap and actors
    birdeye_render_types = ['roadmap', 'actors']
    if self.display_route:
      birdeye_render_types.append('waypoints')
    self.birdeye_render.render(self.display, birdeye_render_types)
    birdeye = pygame.surfarray.array3d(self.display)
    birdeye = birdeye[0:self.display_size, :, :]
    birdeye = self._display_to_rgb(birdeye)

    # Roadmap
    roadmap_render_types = ['roadmap']
    if self.display_route:
      roadmap_render_types.append('waypoints')
    self.birdeye_render.render(self.display, roadmap_render_types)
    roadmap = pygame.surfarray.array3d(self.display)
    roadmap = roadmap[0:self.display_size, :, :]
    roadmap = self._display_to_rgb(roadmap)
    # Add ego vehicle
    for i in range(self.obs_size):
      for j in range(self.obs_size):
        if abs(birdeye[i, j, 0] - 255)<20 and abs(birdeye[i, j, 1] - 0)<20 and abs(birdeye[i, j, 0] - 255)<20:
          roadmap[i, j, :] = birdeye[i, j, :]

    # Display birdeye image
    birdeye_surface = self._rgb_to_display_surface(birdeye)
    self.display.blit(birdeye_surface, (0, 0))

    ## Lidar image generation
    point_cloud = []
    # Get point cloud data
    for location in self.lidar_data:
      point_cloud.append([location.x, location.y, -location.z])
    point_cloud = np.array(point_cloud)
    # Separate the 3D space to bins for point cloud, x and y is set according to self.lidar_bin,
    # and z is set to be two bins.
    y_bins = np.arange(-(self.obs_range - self.d_behind), self.d_behind+self.lidar_bin, self.lidar_bin)
    x_bins = np.arange(-self.obs_range/2, self.obs_range/2+self.lidar_bin, self.lidar_bin)
    z_bins = [-self.lidar_height-1, -self.lidar_height+0.25, 1]
    # Get lidar image according to the bins
    lidar, _ = np.histogramdd(point_cloud, bins=(x_bins, y_bins, z_bins))
    lidar[:,:,0] = np.array(lidar[:,:,0]>0, dtype=np.uint8)
    lidar[:,:,1] = np.array(lidar[:,:,1]>0, dtype=np.uint8)
    # Add the waypoints to lidar image
    if self.display_route:
      wayptimg = (birdeye[:,:,0] <= 10) * (birdeye[:,:,1] <= 10) * (birdeye[:,:,2] >= 240)
    else:
      wayptimg = birdeye[:,:,0] < 0  # Equal to a zero matrix
    wayptimg = np.expand_dims(wayptimg, axis=2)
    wayptimg = np.fliplr(np.rot90(wayptimg, 3))

    # Get the final lidar image
    lidar = np.concatenate((lidar, wayptimg), axis=2)
    lidar = np.flip(lidar, axis=1)
    lidar = np.rot90(lidar, 1)
    lidar = lidar * 255

    # Display lidar image
    lidar_surface = self._rgb_to_display_surface(lidar)
    self.display.blit(lidar_surface, (self.display_size, 0))

    ## Display camera image
    camera = resize(self.camera_img, (self.obs_size, self.obs_size)) * 255
    camera_surface = self._rgb_to_display_surface(camera)
    self.display.blit(camera_surface, (self.display_size * 2, 0))

    ## Display roadmap image
    # roadmap_surface = self._rgb_to_display_surface(roadmap)
    # self.display.blit(roadmap_surface, (self.display_size * 3, 0))

    ## Vehicle classification and regression maps (requires further normalization)
    vh_clas = np.zeros((self.pixor_size, self.pixor_size))
    vh_regr = np.zeros((self.pixor_size, self.pixor_size, 6))

    # Generate the PIXOR image. Note in CARLA it is using left-hand coordinate
    def get_actor_info(actor):
      trans=actor.get_transform()
      x=trans.location.x
      y=trans.location.y
      yaw=trans.rotation.yaw/180*np.pi
      # Get length and width
      bb=actor.bounding_box
      l=bb.extent.x  # half the length
      w=bb.extent.y  # half the width
      return (x, y, yaw, l, w)

    def global_to_local_pose(pose, ego_pose):
      x, y, yaw = pose
      ego_x, ego_y, ego_yaw = ego_pose
      R = np.array([[np.cos(ego_yaw), np.sin(ego_yaw)], 
        [-np.sin(ego_yaw), np.cos(ego_yaw)]])
      vec_local = R.dot(np.array([x - ego_x, y - ego_y]))
      yaw_local = yaw - ego_yaw
      return (vec_local[0], vec_local[1], yaw_local)

    def local_to_pixel_info(info):
      """Here the ego local coordinate is left-handed, the pixel
      coordinate is also left-handed, with its origin at the left bottom.
      """
      x, y, yaw, l, w = info
      x_pixel = (x + self.d_behind)/self.obs_range*self.pixor_size 
      y_pixel = y/self.obs_range*self.pixor_size + self.pixor_size/2
      yaw_pixel = yaw
      l_pixel = l/self.obs_range*self.pixor_size
      w_pixel = w/self.obs_range*self.pixor_size
      return (x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel)

    def get_pixels_from_info(info):
      """Get pixels from information in pixel coordinate, 
      which the origin is at the left bottom.
      """
      poly = get_poly_from_info(info)     
      p = Path(poly) # make a polygon
      grid = p.contains_points(self.pixel_grid)
      isinPoly = np.where(grid==True)
      pixels = np.take(self.pixel_grid, isinPoly, axis=0)[0]
      return pixels

    def get_poly_from_info(info):
      x, y, yaw, l, w = info
      poly_local=np.array([[l,w],[l,-w],[-l,-w],[-l,w]]).transpose()
      # Get rotation matrix to transform to the coordinate
      R=np.array([[np.cos(yaw),-np.sin(yaw)],[np.sin(yaw),np.cos(yaw)]])
      # Get bounding box polygon
      poly=np.matmul(R,poly_local).transpose()+np.repeat([[x,y]],4,axis=0)
      return poly

    # Get the 6-dim geom parametrization in PIXOR, here we use pixel coordinate
    # for convenience
    ego_trans = self.ego.get_transform()
    ego_x = ego_trans.location.x
    ego_y = ego_trans.location.y
    ego_yaw = ego_trans.rotation.yaw/180*np.pi
    for actor in self.world.get_actors().filter('vehicle.*'):
      x, y, yaw, l, w = get_actor_info(actor)
      x_local, y_local, yaw_local = global_to_local_pose(
        (x, y, yaw), (ego_x, ego_y, ego_yaw))
      if actor.id != self.ego.id and np.sqrt(x_local**2 + y_local**2) < self.obs_range**1.5:
        x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel = local_to_pixel_info(
          (x_local, y_local, yaw_local, l, w))
        cos_t = np.cos(yaw_pixel)
        sin_t = np.sin(yaw_pixel)
        logw = np.log(w_pixel)
        logl = np.log(l_pixel)
        pixels = get_pixels_from_info((x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel))
        for pixel in pixels:
          vh_clas[pixel[0], pixel[1]] = 1
          dx = x_pixel - pixel[0]
          dy = y_pixel - pixel[1]
          # dx = (x_pixel - pixel[0])/self.obs_size*self.obs_range
          # dy = (y_pixel - pixel[1])/self.obs_size*self.obs_range
          vh_regr[pixel[0], pixel[1], :] = np.array(
            [cos_t, sin_t, dx, dy, logw, logl])

    # Flip the image matrix so that the origin is at the left-bottom
    vh_clas = np.flip(vh_clas, axis=0)
    vh_regr = np.flip(vh_regr, axis=0)

    ## Display pixor images
    # vh_clas_display = np.stack([vh_clas, vh_clas, vh_clas], axis=2) * 255
    # vh_clas_surface = self._rgb_to_display_surface(vh_clas_display)
    # self.display.blit(vh_clas_surface, (self.display_size * 4, 0))
    # vh_regr1 = vh_regr[:, :, 0:3]
    # vh_regr2 = vh_regr[:, :, 3:6]
    # vh_regr1_surface = self._rgb_to_display_surface(vh_regr1)
    # self.display.blit(vh_regr1_surface, (self.display_size * 5, 0))
    # vh_regr2_surface = self._rgb_to_display_surface(vh_regr2)
    # self.display.blit(vh_regr2_surface, (self.display_size * 6, 0))

    # Display on pygame
    pygame.display.flip()

    # State observation
    lateral_dis, w = self._get_preview_lane_dis(self.waypoints, ego_x, ego_y)
    delta_yaw = np.arcsin(np.cross(w, 
      np.array(np.array([np.cos(ego_yaw), np.sin(ego_yaw)]))))
    v = self.ego.get_velocity()
    speed = np.sqrt(v.x**2 + v.y**2)
    state = np.array([lateral_dis, - delta_yaw, speed, self.vehicle_front])

    # Pixor state, [x, y, cos(yaw), sin(yaw), speed]
    pixor_state = [ego_x, ego_y, np.cos(ego_yaw), np.sin(ego_yaw), speed]


    """
    Explanation of how my costmap implementation works:
      First we get a list of all of the waypoints from the current position. We iterate through this list in pairs so that there is a current 
      waypoint and a previous waypoint. These along with parameter cost are passed into _get_costmap which returns a costmap only relevant to the
      lane defined by the line between the two points. This costmap is summed with the global costmap. This profess is repeated for the left and right
      lanes of the current waypoint if they exist and are in the same direction.  
    """
    def _get_perp_dis(x1, y1, x2, y2, x3, y3):
    """ Computes the perpindicular distance between a point (x3,y3) and a line segment defined by points
        (x1, y1) and (x2, y2). If the point doesn't have a perpincicular line within the line segment, the
        distance is inf
    """  
      x = np.array([x3, y3])
      p = np.array([x1, y1])
      q = np.array([x2, y2])
      lamb = np.dot((x - p), (q - p)) / np.dot((q - p), (q - p)) #lamb checks if point is within line segment
      if lamb <= 1 and lamb >= 0:
        s = p + (lamb * (q - p))
        return np.linalg.norm(x - s)
      return float('inf')


    ego_y = ego_y - 2 #shift the cost map down because originally it was too high 

    def _get_costmap(pWaypoint, cWaypoint, cost):
    """ Generates a costmap for a current waypoint cWaypoint and its preceding waypoint pWaypoint using cost.
        I refer a lot to global vs local frame. Global means the xy coordinate in the Carla coordinates
        Local is the coordinate in the costmap matrix.
        Also the letters x and y are swapped when referring to the local frame but it works 
    """
      single_costmap = np.zeros((self.obs_size, self.obs_size))

      #Definitions for the waypoints' x and y coordinates in the global frame 
      laneWidth = pWaypoint.lane_width
      cX = cWaypoint.transform.location.x
      cY = cWaypoint.transform.location.y
      pX = pWaypoint.transform.location.x
      pY = pWaypoint.transform.location.y

      #If we draw a square around the center of the ego vehicle (length is determined by range), this is the bottom left corner in global coords
      corner_x = ego_x - (self.obs_range / 2)
      corner_y = ego_y - (self.obs_range / 2)

      #Here we create two matrices with the same dimensions as the costmap. One represents the x coordinate and one represents the y coordinate in the local frame.
      y_array, x_array = np.meshgrid(np.arange(0, self.obs_size), np.arange(0, self.obs_size))
      #y_array is [[0 1 2 ... 255] [0 1 2 ... 255] ...] 
      #x_array is [[0 0 0 .... 0] [1 1 1 .... 1]... [255 255 ... 255]]

      rotated_x_array = (2 * ego_x) - ((x_array * self.lidar_bin) + corner_x)
      rotated_y_array = (y_array * self.lidar_bin) + corner_y
      c = np.cos(ego_yaw)
      s = np.sin(ego_yaw)
      global_x_array = (c * (rotated_x_array - ego_x)) - (s * (rotated_y_array - ego_y)) + ego_x #for each point in our matrix, we have their global coordinates 
      global_y_array = (s * (rotated_x_array - ego_x)) + (c * (rotated_y_array - ego_y)) + ego_y

      p = np.array([pX, pY])
      q = np.array([cX, cY])
      q_dif = q - p
      lamb_array= (((global_x_array - pX) * (cX - pX)) + ((global_y_array - pY) * (cY - pY ))) / np.dot((q_dif), (q_dif))
      sX = pX + (lamb_array * (cX - pX))
      sY = pY + (lamb_array * (cY - pY))

      distanceMap = np.sqrt(np.square(global_x_array - sX) + np.square(global_y_array - sY))
      penal = (laneWidth / 2) * (-cost) / abs(cost)      
      distanceMap = np.where((lamb_array <=1) & (lamb_array >= 0) & (distanceMap <= laneWidth / 2), distanceMap, penal) #will have perpDistance in the spot if its within the lane
      single_costmap = distanceMap * (abs(cost / (laneWidth / 2))) + cost

      return single_costmap

    #Generate list of waypoints. Previously, we relied on self.routeplanner._actualWaypoints
    #there is way to save space for this. Instead of recalculating all waypoints, we can reuse most of them.

    #we can do this for each neighboring lane 
    ego_waypoints = [self.world.get_map().get_waypoint(self.ego.get_location())] #make current ego position into a waypoint
    current_dir= ego_waypoints[0].lane_id #positive or negative integer depending on which direction the lane is going in 
    left_lane = ego_waypoints[0].get_left_lane()
    right_lane = ego_waypoints[0].get_right_lane()

    waypoints_threshold = 5 #this is how many waypoints to keep 
    sampling_radius = 5 #how many meters away to sample for the next waypoint

    if left_lane and left_lane.lane_type == carla.LaneType.Driving and left_lane.lane_id * current_dir >= 0: #check if neighboring lane exists, is drivable, and in same direction
      ego_waypoints.append(left_lane)

    if right_lane and right_lane.lane_type == carla.LaneType.Driving and right_lane.lane_id * current_dir >= 0:
      ego_waypoints.append(right_lane)
    dir_list = [] #list of lists of waypoints. each list inside represents a possible path


    #we loop through ego_waypoints for the center, left, and right lanes 
    for current_waypoint in ego_waypoints:
      #current_waypoint = self.world.get_map().get_waypoint(self.ego.get_location())
      current_waypoints = [current_waypoint]
      lane_end = False #did we reach the end of the lane
      ctr = 0 #counter to make sure we don't pass the waypoints threshold
      next_waypoints = [] #these represents all the other directions we have to go in after the current waypoint. TODO contains a tuple of starting waypoint and list of lists
      while (not lane_end) and ctr < waypoints_threshold:
        sample_waypoint = current_waypoints[-1]
        ctr += 1
        next_waypoint = sample_waypoint.next(sampling_radius)

        # if (sample_waypoint.is_junction):
        #   junc = sample_waypoint.get_junction().get_waypoints(carla.LaneType.Driving)
        #   print("Sample", sample_waypoint)
        #   print("Junc", junc)
        #   for tup in junc:
        #     if tup[0] == sample_waypoint:
        #       next_waypoints.append([tup[0], tup[1]])

        #TODO so that we don't just stop when there's multiple directions but instead stop when the lane ends 
        if (len(next_waypoint) != 1): #if there is more than 1 waypoint to go to we stop because we have to explore those directions 
          lane_end = True
          #print("Testing if the last means in junction", sample_waypoint.is_junction)
        else: 
          current_waypoints.append(next_waypoint[0]) #if there's only one direction just append it to the current array

      last_waypoint = current_waypoints[-1] #this will be the starting waypoint for all the diverging lanes 

      dir_list.append(current_waypoints)

      if lane_end:
        #this means the lane changes direction so we have to compute the new lanes for the junction
        #print("last waypoint coming up")
        next_waypoints = last_waypoint.next(sampling_radius)
        #print(next_waypoints)
        for new_direction in next_waypoints: #we append some points
          new_waypoints = [last_waypoint, new_direction]
          ctr = 0
          lane_end = False
          while not lane_end and ctr < waypoints_threshold + 5:
            ctr += 1
            sample_waypoint = new_waypoints[-1]

            next_waypoint = sample_waypoint.next(sampling_radius)
            if (len(next_waypoint) > 1):
              lane_end = True
            else: 
              new_waypoints.append(next_waypoint[0])
          #print('ctr', ctr)
          #print("new_waypoints", new_waypoints)
          dir_list.append(new_waypoints)


    #listofWaypoints = self.routeplanner._actualWaypoints

    cost = -10
    costmap = np.zeros((self.obs_size, self.obs_size))

    for listofWaypoints in dir_list:
      if len(listofWaypoints) < 1:
        print("Not enough waypoints to form costmap")

      else:
        pWaypoint = listofWaypoints[0]
        for cWaypoint in listofWaypoints[1:]:

          currentDirection = cWaypoint.lane_id #positive or negative integer depending on which direction the lane is going in 
          costmap = costmap + _get_costmap(pWaypoint, cWaypoint, cost)

          #The current implementation of left and right lanes is contingent on whether the current lane has a left/right lane AND the previous lane has a left/right lane
          pleftWaypoint = pWaypoint.get_left_lane()
          prightWaypoint = pWaypoint.get_right_lane()
          cleftWaypoint = cWaypoint.get_left_lane()
          crightWaypoint = cWaypoint.get_right_lane()
          pWaypoint = cWaypoint




        # if pleftWaypoint and (pleftWaypoint.lane_id * currentDirection >= 0): #check if left waypoint exists for the previous waypoint and it goes in the same direction 
        #   if cleftWaypoint and (cleftWaypoint.lane_id * currentDirection >= 0): #check if the left waypoint exists for the current waypoint and it goes in the same direction
        #     costmap = costmap + _get_costmap(pleftWaypoint, cleftWaypoint, cost)

        # if prightWaypoint and (prightWaypoint.lane_id * currentDirection >= 0):
        #   if crightWaypoint and (crightWaypoint.lane_id * currentDirection >= 0):
        #     costmap = costmap + _get_costmap(prightWaypoint, crightWaypoint, cost)


    #Here we convert the cost map which ranges from -cost to 0 (low cost to high cost) to a displayable costmap that has values from 0 to 255
    costmap = np.clip(costmap, cost, 0)
    costmap = (costmap - cost) * 255 / abs(cost)

    costmap_surface = self._rgb_to_display_surface(np.moveaxis(np.array([costmap, costmap, costmap]), 0, -1))
    self.display.blit(costmap_surface, (self.display_size * 3, 0))

    obs = {}
    obs.update({
      'birdeye':birdeye.astype(np.uint8),
      'lidar':lidar.astype(np.uint8),
      'camera':camera.astype(np.uint8),
      'roadmap':roadmap.astype(np.uint8),
      'vh_clas':np.expand_dims(vh_clas, -1).astype(np.float32),
      'vh_regr':vh_regr.astype(np.float32),
      'state': state,
      'pixor_state': pixor_state,
      'costmap' : costmap
    })
    
    return obs

  def _get_reward(self):
    """Calculate the step reward."""
    # reward for speed tracking
    v = self.ego.get_velocity()
    speed = np.sqrt(v.x**2 + v.y**2)
    r_speed = -abs(speed - self.desired_speed)
    
    # reward for collision
    r_collision = 0
    if len(self.collision_hist) > 0:
      r_collision = -1

    # reward for steering:
    r_steer = -self.ego.get_control().steer**2

    # reward for out of lane
    ego_x, ego_y = self._get_ego_pos()
    dis, w = self._get_lane_dis(self.waypoints, ego_x, ego_y)
    r_out = 0
    if abs(dis) > self.out_lane_thres:
      r_out = -1

    # longitudinal speed
    lspeed = np.array([v.x, v.y])
    lspeed_lon = np.dot(lspeed, w)

    # cost for too fast
    r_fast = 0
    if lspeed_lon > self.desired_speed:
      r_fast = -1

    # cost for lateral acceleration
    r_lat = - abs(self.ego.get_control().steer) * lspeed_lon**2

    r = 200*r_collision + 1*lspeed_lon + 10*r_fast + 1*r_out + r_steer*5 + 0.2*r_lat - 0.1

    return r

  def _get_ego_pos(self):
    """Get the ego vehicle pose (x, y)."""
    ego_trans = self.ego.get_transform()
    ego_x = ego_trans.location.x
    ego_y = ego_trans.location.y
    return ego_x, ego_y

  def _get_lane_dis(self, waypoints, x, y):
    """Calculate distance from (x, y) to waypoints."""
    dis_min = 1000
    for pt in waypoints:
      d = np.sqrt((x-pt[0])**2 + (y-pt[1])**2)
      if d < dis_min:
        dis_min = d
        waypt=pt
    vec = np.array([x - waypt[0],y - waypt[1]])
    lv = np.linalg.norm(np.array(vec))
    w = np.array([np.cos(waypt[2]/180*np.pi), np.sin(waypt[2]/180*np.pi)])
    cross = np.cross(w, vec/lv)
    dis = - lv * cross
    return dis, w

  def _get_preview_lane_dis(self, waypoints, x, y, idx=2):
    """Calculate distance from (x, y) to waypoints."""
    dis_min = 1000
    waypt = waypoints[idx]
    vec = np.array([x - waypt[0],y - waypt[1]])
    lv = np.linalg.norm(np.array(vec))
    w = np.array([np.cos(waypt[2]/180*np.pi), np.sin(waypt[2]/180*np.pi)])
    cross = np.cross(w, vec/lv)
    dis = - lv * cross
    return dis, w

  def _terminal(self):
    """Calculate whether to terminate the current episode."""
    # Get ego state
    ego_x, ego_y = self._get_ego_pos()

    # If collides
    if len(self.collision_hist)>0: 
      return True

    # If reach maximum timestep
    if self.time_step>self.max_time_episode:
      return True

    # If at destination
    if self.dests is not None: # If at destination
      for dest in self.dests:
        if np.sqrt((ego_x-dest[0])**2+(ego_y-dest[1])**2)<4:
          return True

    # If out of lane
    dis, _ = self._get_lane_dis(self.waypoints, ego_x, ego_y)
    if abs(dis) > self.out_lane_thres:
      return True

    return False

  def _clear_all_actors(self, actor_filters):
    """Clear specific actors."""
    for actor_filter in actor_filters:
      for actor in self.world.get_actors().filter(actor_filter):
        if actor.is_alive:
          if actor.type_id == 'controller.ai.walker':
            actor.stop()
          actor.destroy()
Exemplo n.º 4
0
class CarlaEnv(gym.Env):
    """An OpenAI gym wrapper for CARLA simulator."""
    def __init__(self, params):
        # parameters
        self.display_size = params['display_size']  # rendering screen size
        self.max_past_step = params['max_past_step']
        self.number_of_vehicles = params['number_of_vehicles']
        self.number_of_walkers = params['number_of_walkers']
        self.dt = params['dt']
        self.task_mode = params['task_mode']
        self.max_time_episode = params['max_time_episode']
        self.max_waypt = params['max_waypt']
        self.obs_range = params['obs_range']
        self.lidar_bin = params['lidar_bin']
        self.d_behind = params['d_behind']
        self.obs_size = int(self.obs_range / self.lidar_bin)
        self.out_lane_thres = params['out_lane_thres']
        self.desired_speed = params['desired_speed']

        # Destination
        if params['task_mode'] == 'roundabout':
            self.dests = [[4.46, -61.46, 0], [-49.53, -2.89, 0],
                          [-6.48, 55.47, 0], [35.96, 3.33, 0]]
        else:
            self.dests = None

        # action and observation spaces
        self.discrete = params['discrete']
        self.discrete_act = [params['discrete_acc'],
                             params['discrete_steer']]  # acc, steer
        self.n_acc = len(self.discrete_act[0])
        self.n_steer = len(self.discrete_act[1])
        if self.discrete:
            self.action_space = spaces.Discrete(self.n_acc * self.n_steer)
        else:
            self.action_space = spaces.Box(
                np.array([
                    params['continuous_accel_range'][0],
                    params['continuous_steer_range'][0]
                ]),
                np.array([
                    params['continuous_accel_range'][1],
                    params['continuous_steer_range'][1]
                ]),
                dtype=np.float32)  # acc, steer
        self.observation_space = spaces.Dict({
            'birdeye':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'lidar':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'camera':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8)
        })

        # Connect to carla server and get world object
        print('connecting to Carla server...')
        client = carla.Client('localhost', params['port'])
        client.set_timeout(10.0)
        self.world = client.load_world(params['town'])
        print('Carla server connected!')

        # Set weather
        self.world.set_weather(carla.WeatherParameters.ClearNoon)

        # Get spawn points
        self.vehicle_spawn_points = list(
            self.world.get_map().get_spawn_points())
        self.walker_spawn_points = []
        for i in range(self.number_of_walkers):
            spawn_point = carla.Transform()
            loc = self.world.get_random_location_from_navigation()
            if (loc != None):
                spawn_point.location = loc
                self.walker_spawn_points.append(spawn_point)

        # Create the ego vehicle blueprint
        self.ego_bp = self._create_vehicle_bluepprint(
            params['ego_vehicle_filter'], color='49,8,8')

        # Collision sensor
        self.collision_hist = []  # The collision history
        self.collision_hist_l = 1  # collision history length
        self.collision_bp = self.world.get_blueprint_library().find(
            'sensor.other.collision')

        # Lidar sensor
        self.lidar_data = None
        self.lidar_height = 2.1
        self.lidar_trans = carla.Transform(
            carla.Location(x=0.0, z=self.lidar_height))
        self.lidar_bp = self.world.get_blueprint_library().find(
            'sensor.lidar.ray_cast')
        self.lidar_bp.set_attribute('channels', '32')
        self.lidar_bp.set_attribute('range', '5000')

        # Camera sensor
        self.camera_img = np.zeros((self.obs_size, self.obs_size, 3),
                                   dtype=np.uint8)
        self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
        self.camera_bp = self.world.get_blueprint_library().find(
            'sensor.camera.rgb')
        # Modify the attributes of the blueprint to set image resolution and field of view.
        self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
        self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
        self.camera_bp.set_attribute('fov', '110')
        # Set the time in seconds between sensor captures
        self.camera_bp.set_attribute('sensor_tick', '0.02')

        # Set fixed simulation step for synchronous mode
        self.settings = self.world.get_settings()
        self.settings.fixed_delta_seconds = self.dt

        # Record the time of total steps and resetting steps
        self.reset_step = 0
        self.total_step = 0

        # Initialize the renderer
        self._init_renderer()

    def reset(self):
        # Clear sensor objects
        self.collision_sensor = None
        self.lidar_sensor = None
        self.camera_sensor = None

        # Delete sensors, vehicles and walkers
        self._clear_all_actors([
            'sensor.other.collision', 'sensor.lidar.ray_cast',
            'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker',
            'walker.*'
        ])

        # Disable sync mode
        self._set_synchronous_mode(False)

        # Spawn surrounding vehicles
        random.shuffle(self.vehicle_spawn_points)
        count = self.number_of_vehicles
        if count > 0:
            for spawn_point in self.vehicle_spawn_points:
                if self._try_spawn_random_vehicle_at(spawn_point,
                                                     number_of_wheels=[4]):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_vehicle_at(random.choice(
                    self.vehicle_spawn_points),
                                                 number_of_wheels=[4]):
                count -= 1

        # Spawn the ego vehicle
        while True:
            if self.task_mode == 'random':
                transform = random.choice(self.vehicle_spawn_points)
            if self.task_mode == 'roundabout':
                self.start = [52.1 + np.random.uniform(-5, 5), -4.2,
                              178.66]  # random
                transform = self._set_carla_transform(self.start)
            if self._try_spawn_ego_vehicle_at(transform):
                break

        # Spawn pedestrians
        random.shuffle(self.walker_spawn_points)
        count = self.number_of_walkers
        if count > 0:
            for spawn_point in self.walker_spawn_points:
                if self._try_spawn_random_walker_at(spawn_point):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_walker_at(
                    random.choice(self.walker_spawn_points)):
                count -= 1

        # Get actors polygon list
        self.vehicle_polygons = []
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        self.walker_polygons = []
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)

        # Add collision sensor
        self.collision_sensor = self.world.spawn_actor(self.collision_bp,
                                                       carla.Transform(),
                                                       attach_to=self.ego)
        self.collision_sensor.listen(lambda event: get_collision_hist(event))

        def get_collision_hist(event):
            impulse = event.normal_impulse
            intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
            self.collision_hist.append(intensity)
            if len(self.collision_hist) > self.collision_hist_l:
                self.collision_hist.pop(0)

        self.collision_hist = []

        # Add lidar sensor
        self.lidar_sensor = self.world.spawn_actor(self.lidar_bp,
                                                   self.lidar_trans,
                                                   attach_to=self.ego)
        self.lidar_sensor.listen(lambda data: get_lidar_data(data))

        def get_lidar_data(data):
            self.lidar_data = data

        # Add camera sensor
        self.camera_sensor = self.world.spawn_actor(self.camera_bp,
                                                    self.camera_trans,
                                                    attach_to=self.ego)
        self.camera_sensor.listen(lambda data: get_camera_img(data))

        def get_camera_img(data):
            array = np.frombuffer(data.raw_data, dtype=np.dtype("uint8"))
            array = np.reshape(array, (data.height, data.width, 4))
            array = array[:, :, :3]
            array = array[:, :, ::-1]
            self.camera_img = array

        # Update timesteps
        self.time_step = 0
        self.reset_step += 1

        # Enable sync mode
        self.settings.synchronous_mode = True
        self.world.apply_settings(self.settings)

        self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

        # Set ego information for render
        self.birdeye_render.set_hero(self.ego, self.ego.id)

        return self._get_obs()

    def step(self, action):
        # Calculate acceleration and steering
        if self.discrete:
            acc = self.discrete_act[0][action // self.n_steer]
            steer = self.discrete_act[1][action % self.n_steer]
        else:
            acc = action[0]
            steer = action[1]

        # Convert acceleration to throttle and brake
        if acc > 0:
            throttle = np.clip(acc / 3, 0, 1)
            brake = 0
        else:
            throttle = 0
            brake = np.clip(-acc / 8, 0, 1)

        # Apply control
        act = carla.VehicleControl(throttle=float(throttle),
                                   steer=float(steer),
                                   brake=float(brake))
        self.ego.apply_control(act)

        self.world.tick()

        # Append actors polygon list
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        while len(self.vehicle_polygons) > self.max_past_step:
            self.vehicle_polygons.pop(0)
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)
        while len(self.walker_polygons) > self.max_past_step:
            self.walker_polygons.pop(0)

        # route planner
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

        # state information
        info = {
            'waypoints': self.waypoints,
            'vehicle_front': self.vehicle_front
        }

        # Update timesteps
        self.time_step += 1
        self.total_step += 1

        return (self._get_obs(), self._get_reward(), self._terminal(),
                copy.deepcopy(info))

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def render(self, mode):
        pass

    def _create_vehicle_bluepprint(self,
                                   actor_filter,
                                   color=None,
                                   number_of_wheels=[4]):
        """Create the blueprint for a specific actor type.

		Args:
			actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

		Returns:
			bp: the blueprint object of carla.
		"""
        blueprints = self.world.get_blueprint_library().filter(actor_filter)
        blueprint_library = []
        for nw in number_of_wheels:
            blueprint_library = blueprint_library + [
                x for x in blueprints
                if int(x.get_attribute('number_of_wheels')) == nw
            ]
        bp = random.choice(blueprint_library)
        if bp.has_attribute('color'):
            if not color:
                color = random.choice(
                    bp.get_attribute('color').recommended_values)
            bp.set_attribute('color', color)
        return bp

    def _init_renderer(self):
        """Initialize the birdeye view renderer.
		"""
        pygame.init()
        self.display = pygame.display.set_mode(
            (self.display_size * 3, self.display_size),
            pygame.HWSURFACE | pygame.DOUBLEBUF)

        pixels_per_meter = self.display_size / self.obs_range
        pixels_ahead_vehicle = (self.obs_range / 2 -
                                self.d_behind) * pixels_per_meter
        birdeye_params = {
            'screen_size': [self.display_size, self.display_size],
            'pixels_per_meter': pixels_per_meter,
            'pixels_ahead_vehicle': pixels_ahead_vehicle
        }
        self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

    def _set_synchronous_mode(self, synchronous=True):
        """Set whether to use the synchronous mode.
		"""
        self.settings.synchronous_mode = synchronous
        self.world.apply_settings(self.settings)

    def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
        """Try to spawn a surrounding vehicle at specific transform with random bluprint.

		Args:
			transform: the carla transform object.

		Returns:
			Bool indicating whether the spawn is successful.
		"""
        blueprint = self._create_vehicle_bluepprint(
            'vehicle.*', number_of_wheels=number_of_wheels)
        blueprint.set_attribute('role_name', 'autopilot')
        vehicle = self.world.try_spawn_actor(blueprint, transform)
        if vehicle is not None:
            vehicle.set_autopilot()
            return True
        return False

    def _try_spawn_random_walker_at(self, transform):
        """Try to spawn a walker at specific transform with random bluprint.

		Args:
			transform: the carla transform object.

		Returns:
			Bool indicating whether the spawn is successful.
		"""
        walker_bp = random.choice(
            self.world.get_blueprint_library().filter('walker.*'))
        # set as not invencible
        if walker_bp.has_attribute('is_invincible'):
            walker_bp.set_attribute('is_invincible', 'false')
        walker_actor = self.world.try_spawn_actor(walker_bp, transform)

        if walker_actor is not None:
            walker_controller_bp = self.world.get_blueprint_library().find(
                'controller.ai.walker')
            walker_controller_actor = self.world.spawn_actor(
                walker_controller_bp, carla.Transform(), walker_actor)
            # start walker
            walker_controller_actor.start()
            # set walk to random point
            walker_controller_actor.go_to_location(
                self.world.get_random_location_from_navigation())
            # random max speed
            walker_controller_actor.set_max_speed(
                1 + random.random()
            )  # max speed between 1 and 2 (default is 1.4 m/s)
            return True
        return False

    def _try_spawn_ego_vehicle_at(self, transform):
        """Try to spawn the ego vehicle at specific transform.

		Args:
			transform: the carla transform object.

		Returns:
			Bool indicating whether the spawn is successful.
		"""
        vehicle = self.world.try_spawn_actor(self.ego_bp, transform)
        if vehicle is not None:
            self.ego = vehicle
            return True
        return False

    def _set_carla_transform(self, pose):
        """Get a carla tranform object given pose.

		Args:
			pose: [x, y, yaw].

		Returns:
			transform: the carla transform object
		"""
        transform = carla.Transform()
        transform.location.x = pose[0]
        transform.location.y = pose[1]
        transform.rotation.yaw = pose[2]
        return transform

    def _get_actor_polygons(self, filt):
        """Get the bounding box polygon of actors.

		Args:
			filt: the filter indicating what type of actors we'll look at.

		Returns:
			actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
		"""
        actor_poly_dict = {}
        for actor in self.world.get_actors().filter(filt):
            # Get x, y and yaw of the actor
            trans = actor.get_transform()
            x = trans.location.x
            y = trans.location.y
            yaw = trans.rotation.yaw / 180 * np.pi
            # Get length and width
            bb = actor.bounding_box
            l = bb.extent.x
            w = bb.extent.y
            # Get bounding box polygon in the actor's local coordinate
            poly_local = np.array([[l, w], [l, -w], [-l, -w],
                                   [-l, w]]).transpose()
            # Get rotation matrix to transform to global coordinate
            R = np.array([[np.cos(yaw), -np.sin(yaw)],
                          [np.sin(yaw), np.cos(yaw)]])
            # Get global bounding box polygon
            poly = np.matmul(R, poly_local).transpose() + np.repeat(
                [[x, y]], 4, axis=0)
            actor_poly_dict[actor.id] = poly
        return actor_poly_dict

    def _get_ego(self):
        """ Get the ego vehicle object
		"""
        return self.ego

    def _get_obs(self):
        """Get the observations."""
        ## Birdeye rendering
        self.birdeye_render.vehicle_polygons = self.vehicle_polygons
        self.birdeye_render.walker_polygons = self.walker_polygons
        self.birdeye_render.waypoints = self.waypoints

        self.birdeye_render.render(self.display)
        # pygame.display.flip()
        birdeye = pygame.surfarray.array3d(self.display)
        birdeye = birdeye[0:self.display_size, :, :]
        birdeye = np.fliplr(np.rot90(birdeye, 3))  # flip to regular view
        birdeye = resize(birdeye, (self.obs_size, self.obs_size))  # resize
        birdeye = birdeye * 255
        birdeye.astype(np.uint8)

        ## Lidar image generation
        point_cloud = []
        # Get point cloud data
        for location in self.lidar_data:
            point_cloud.append([location.x, location.y, -location.z])
        point_cloud = np.array(point_cloud)
        # Separate the 3D space to bins for point cloud, x and y is set according to self.lidar_bin,
        # and z is set to be two bins.
        y_bins = np.arange(-(self.obs_range - self.d_behind),
                           self.d_behind + self.lidar_bin, self.lidar_bin)
        x_bins = np.arange(-self.obs_range / 2,
                           self.obs_range / 2 + self.lidar_bin, self.lidar_bin)
        z_bins = [-self.lidar_height - 1, -self.lidar_height + 0.25, 1]
        # Get lidar image according to the bins
        lidar, _ = np.histogramdd(point_cloud, bins=(x_bins, y_bins, z_bins))
        lidar[:, :, 0] = np.array(lidar[:, :, 0] > 0, dtype=np.uint8)
        lidar[:, :, 1] = np.array(lidar[:, :, 1] > 0, dtype=np.uint8)
        # Add the waypoints to lidar image
        wayptimg = (birdeye[:, :, 0] == 0) * (birdeye[:, :, 1]
                                              == 0) * (birdeye[:, :, 2] == 255)
        wayptimg = np.expand_dims(wayptimg, axis=2)
        wayptimg = np.fliplr(np.rot90(wayptimg, 3))
        wayptimg.astype(np.uint8)
        # Get the final lidar image
        lidar = np.concatenate((lidar, wayptimg), axis=2)
        lidar = np.flip(lidar, axis=1)
        lidar = np.rot90(lidar, 1)
        lidar = lidar * 255
        # Display lidar image
        lidar_surface = pygame.Surface(
            (self.display_size, self.display_size)).convert()
        lidar_display = resize(lidar, (self.display_size, self.display_size))
        lidar_display = np.flip(lidar_display, axis=1)
        lidar_display = np.rot90(lidar_display, 1)
        pygame.surfarray.blit_array(lidar_surface, lidar_display)
        self.display.blit(lidar_surface, (self.display_size, 0))

        # Display camera image
        camera_surface = pygame.Surface(
            (self.display_size, self.display_size)).convert()
        camera_display = resize(self.camera_img,
                                (self.display_size, self.display_size))
        camera_display = np.flip(camera_display, axis=1)
        camera_display = np.rot90(camera_display, 1)
        camera_display = camera_display * 255
        pygame.surfarray.blit_array(camera_surface, camera_display)
        self.display.blit(camera_surface, (self.display_size * 2, 0))

        camera = pygame.surfarray.array3d(self.display)
        camera = camera[2 * self.display_size:, :, :]
        camera = np.fliplr(np.rot90(camera, 3))  # flip to regular view
        camera = resize(camera, (self.obs_size, self.obs_size))  # resize
        camera = camera * 255
        camera.astype(np.uint8)

        # Display on pygame
        pygame.display.flip()

        obs = {'birdeye': birdeye, 'lidar': lidar, 'camera': camera}

        return obs

    def _get_reward(self):
        """Calculate the step reward."""
        # reward for speed tracking
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        r_speed = -abs(speed - self.desired_speed)

        # reward for collision
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -1

        # reward for steering:
        r_steer = -self.ego.get_control().steer**2

        # reward for out of lane
        ego_x, ego_y = self._get_ego_pos()
        dis, w = self._get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        if dis > self.out_lane_thres:
            r_out = -1

        # longitudinal speed
        lspeed = np.array([v.x, v.y])
        lspeed_lon = np.dot(lspeed, w)

        # cost for too fast
        r_fast = 0
        if lspeed_lon > self.desired_speed:
            r_fast = -1

        # cost for lateral acceleration
        r_lat = -abs(self.ego.get_control().steer) * lspeed_lon**2

        r = 200 * r_collision + 1 * lspeed_lon + 10 * r_fast + 1 * r_out + r_steer * 5 + 0.2 * r_lat - 0.1

        return r

    def _get_ego_pos(self):
        """Get the ego vehicle pose (x, y)."""
        ego_trans = self.ego.get_transform()
        ego_x = ego_trans.location.x
        ego_y = ego_trans.location.y
        return ego_x, ego_y

    def _get_lane_dis(self, waypoints, x, y):
        """Calculate distance from (x, y) to waypoints."""
        dis_min = 1000
        for pt in waypoints:
            d = np.sqrt((x - pt[0])**2 + (y - pt[1])**2)
            if d < dis_min:
                dis_min = d
                waypt = pt
        vec = np.array([x - waypt[0], y - waypt[1]])
        lv = np.linalg.norm(np.array(vec))
        w = np.array(
            [np.cos(waypt[2] / 180 * np.pi),
             np.sin(waypt[2] / 180 * np.pi)])
        costh = np.dot(vec / lv, w)
        sinth = np.sqrt(1 - costh**2)
        dis = lv * sinth
        return dis, w

    def _terminal(self):
        """Calculate whether to terminate the current episode."""
        # Get ego state
        ego_x, ego_y = self._get_ego_pos()

        # If collides
        if len(self.collision_hist) > 0:
            return True

        # If reach maximum timestep
        if self.time_step > self.max_time_episode:
            return True

        # If at destination
        if self.dests is not None:  # If at destination
            for dest in self.dests:
                if np.sqrt((ego_x - dest[0])**2 + (ego_y - dest[1])**2) < 4:
                    return True

        # If out of lane
        dis, _ = self._get_lane_dis(self.waypoints, ego_x, ego_y)
        if dis > self.out_lane_thres:
            return True

        return False

    def _clear_all_actors(self, actor_filters):
        """Clear specific actors."""
        for actor_filter in actor_filters:
            for actor in self.world.get_actors().filter(actor_filter):
                if actor.is_alive:
                    if actor.type_id == 'controller.ai.walker':
                        actor.stop()
                    actor.destroy()
Exemplo n.º 5
0
class CarlaEnv(gym.Env):
    """An OpenAI gym wrapper for CARLA simulator."""
    def __init__(self, params):
        # parameters
        print('init mai hai ham')
        action_params = params['action_params']
        self.path_params = params['path_params']

        self.display_size = params['display_size']  # rendering screen size
        self.max_past_step = params['max_past_step']
        self.number_of_vehicles = params['number_of_vehicles']
        self.number_of_walkers = params['number_of_walkers']
        self.task_mode = params['task_mode']
        self.max_time_episode = params['max_time_episode']
        self.max_waypt = params['max_waypt']
        self.obs_range = params['obs_range']
        self.lidar_bin = params['lidar_bin']
        self.d_behind = params['d_behind']
        self.obs_size = int(self.obs_range / self.lidar_bin)
        self.out_lane_thres = params['out_lane_thres']
        self.desired_speed = params['desired_speed']
        self.max_ego_spawn_times = params['max_ego_spawn_times']
        self.display_route = params['display_route']
        control_params = params['control_params']
        self.args_lateral_dict = control_params['args_lateral_dict']
        self.args_longitudinal_dict = control_params['args_longitudinal_dict']

        D_T_S = action_params['d_t_s']
        N_S_SAMPLE = action_params['n_s_sample']
        MINT = action_params['mint']
        MAXT = action_params['maxt']
        MAX_ROAD_WIDTH = action_params['max_road_width']
        MAX_LAT_VEL = action_params['max_lat_vel']
        TARGET_SPEED = self.path_params['TARGET_SPEED']

        self.dt = self.path_params['DT']  # time interval between 2 frames

        if 'pixor' in params.keys():
            self.pixor = params['pixor']
            self.pixor_size = params['pixor_size']
        else:
            self.pixor = False

        # Destination
        if params['task_mode'] == 'roundabout':
            self.dests = [[4.46, -61.46, 0], [-49.53, -2.89, 0],
                          [-6.48, 55.47, 0], [35.96, 3.33, 0]]
        else:
            self.dests = None

        # action and observation spaces
        self.discrete = params['discrete']
        self.discrete_act = [params['discrete_acc'],
                             params['discrete_steer']]  # acc, steer
        self.n_acc = len(self.discrete_act[0])
        self.n_steer = len(self.discrete_act[1])
        if self.discrete:
            self.action_space = spaces.Discrete(self.n_acc * self.n_steer)
        else:
            self.action_space = spaces.Box(
                low=np.array([
                    TARGET_SPEED - D_T_S * N_S_SAMPLE, MINT, -MAX_ROAD_WIDTH,
                    -MAX_LAT_VEL
                ]),
                high=np.array([
                    TARGET_SPEED + D_T_S * N_S_SAMPLE, MAXT, MAX_ROAD_WIDTH,
                    MAX_LAT_VEL
                ]),
                dtype=np.float32)

        observation_space_dict = {
            'camera':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'lidar':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'birdeye':
            spaces.Box(low=0,
                       high=255,
                       shape=(self.obs_size, self.obs_size, 3),
                       dtype=np.uint8),
            'state':
            spaces.Box(np.array([-2, -1, -5, 0]),
                       np.array([2, 1, 30, 1]),
                       dtype=np.float32)
        }
        if self.pixor:  # dont change
            observation_space_dict.update({
                'roadmap':
                spaces.Box(low=0,
                           high=255,
                           shape=(self.obs_size, self.obs_size, 3),
                           dtype=np.uint8),
                'vh_clas':
                spaces.Box(low=0,
                           high=1,
                           shape=(self.pixor_size, self.pixor_size, 1),
                           dtype=np.float32),
                'vh_regr':
                spaces.Box(low=-5,
                           high=5,
                           shape=(self.pixor_size, self.pixor_size, 6),
                           dtype=np.float32),
                'pixor_state':
                spaces.Box(np.array([-1000, -1000, -1, -1, -5]),
                           np.array([1000, 1000, 1, 1, 20]),
                           dtype=np.float32)
            })
        self.observation_space = spaces.Dict(observation_space_dict)
        self.observation_space = spaces.Box(
            low=-1,
            high=1,
            shape=(self.obs_size * self.obs_size * 3 + 4, 1),
            dtype=np.float32)
        # Connect to carla server and get world object
        print('connecting to Carla server...')
        client = carla.Client('localhost', params['port'])
        client.set_timeout(10.0)
        self.world = client.load_world(params['town'])
        print('Carla server connected!')

        # Set weather
        self.world.set_weather(carla.WeatherParameters.ClearNoon)

        # Get spawn points
        self.vehicle_spawn_points = list(
            self.world.get_map().get_spawn_points())
        self.walker_spawn_points = []
        for i in range(self.number_of_walkers):
            spawn_point = carla.Transform()
            loc = self.world.get_random_location_from_navigation()
            if (loc != None):
                spawn_point.location = loc
                self.walker_spawn_points.append(spawn_point)

        # Create the ego vehicle blueprint
        self.ego_bp = self._create_vehicle_bluepprint(
            params['ego_vehicle_filter'], color='49,8,8')

        # Collision sensor
        self.collision_hist = []  # The collision history
        self.collision_hist_l = 1  # collision history length
        self.collision_bp = self.world.get_blueprint_library().find(
            'sensor.other.collision')

        # Lidar sensor
        self.lidar_data = None
        self.lidar_height = 2.1  # Note : HEIGHT OF LIDAR
        self.lidar_trans = carla.Transform(
            carla.Location(x=0.0, z=self.lidar_height))
        self.lidar_bp = self.world.get_blueprint_library().find(
            'sensor.lidar.ray_cast')
        self.lidar_bp.set_attribute('channels', '32')
        self.lidar_bp.set_attribute('range', '5000')

        # Camera sensor
        self.camera_img = np.zeros((self.obs_size, self.obs_size, 3),
                                   dtype=np.uint8)
        self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
        self.camera_bp = self.world.get_blueprint_library().find(
            'sensor.camera.rgb')
        # Modify the attributes of the blueprint to set image resolution and field of view.
        self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
        self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
        self.camera_bp.set_attribute('fov', '110')
        # Set the time in seconds between sensor captures
        self.camera_bp.set_attribute('sensor_tick', '0.02')

        # Set fixed simulation step for synchronous mode
        self.settings = self.world.get_settings()
        self.settings.fixed_delta_seconds = self.dt

        # Record the time of total steps and resetting steps
        self.reset_step = 0
        self.total_step = 0

        # Initialize the renderer
        self._init_renderer()

        # Get pixel grid points
        if self.pixor:
            x, y = np.meshgrid(
                np.arange(self.pixor_size),
                np.arange(self.pixor_size))  # make a canvas with coordinates
            x, y = x.flatten(), y.flatten()
            self.pixel_grid = np.vstack((x, y)).T

    def reset(self):
        # Clear sensor objects
        print('reset')
        self.collision_sensor = None
        self.lidar_sensor = None
        self.camera_sensor = None

        # Delete sensors, vehicles and walkers
        self._clear_all_actors([
            'sensor.other.collision', 'sensor.lidar.ray_cast',
            'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker',
            'walker.*'
        ])

        # Disable sync mode
        self._set_synchronous_mode(False)

        # Spawn surrounding vehicles
        random.shuffle(self.vehicle_spawn_points)
        count = self.number_of_vehicles
        if count > 0:
            for spawn_point in self.vehicle_spawn_points:
                if self._try_spawn_random_vehicle_at(spawn_point,
                                                     number_of_wheels=[4]):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_vehicle_at(random.choice(
                    self.vehicle_spawn_points),
                                                 number_of_wheels=[4]):
                count -= 1

        # Spawn pedestrians
        random.shuffle(self.walker_spawn_points)
        count = self.number_of_walkers
        if count > 0:
            for spawn_point in self.walker_spawn_points:
                if self._try_spawn_random_walker_at(spawn_point):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_walker_at(
                    random.choice(self.walker_spawn_points)):
                count -= 1

        # Get actors polygon list
        self.vehicle_polygons = []
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        self.walker_polygons = []
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)

        # Spawn the ego vehicle
        ego_spawn_times = 0
        while True:
            if ego_spawn_times > self.max_ego_spawn_times:
                self.reset()

            if self.task_mode == 'random':
                transform = random.choice(self.vehicle_spawn_points)
            if self.task_mode == 'roundabout':
                self.start = [52.1 + np.random.uniform(-5, 5), -4.2,
                              178.66]  # random
                # self.start=[52.1,-4.2, 178.66] # static
                transform = set_carla_transform(self.start)
            if self.task_mode == 'debug':
                self.start = [88.71, -237, 4]  # static
                transform = set_carla_transform(self.start)
            if self._try_spawn_ego_vehicle_at(transform):
                break
            else:
                ego_spawn_times += 1
                time.sleep(0.1)

        # Initializing Controller (Requires ego vehicle spawn)
        self._vehicle_controller = VehiclePIDController(
            self.ego,
            args_lateral=self.args_lateral_dict,
            args_longitudinal=self.args_longitudinal_dict)

        # Add collision sensor
        self.collision_sensor = self.world.spawn_actor(self.collision_bp,
                                                       carla.Transform(),
                                                       attach_to=self.ego)
        self.collision_sensor.listen(lambda event: get_collision_hist(event))

        def get_collision_hist(event):
            impulse = event.normal_impulse
            intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
            self.collision_hist.append(intensity)
            if len(self.collision_hist) > self.collision_hist_l:
                self.collision_hist.pop(0)

        self.collision_hist = []

        # Add lidar sensor
        self.lidar_sensor = self.world.spawn_actor(self.lidar_bp,
                                                   self.lidar_trans,
                                                   attach_to=self.ego)
        self.lidar_sensor.listen(lambda data: get_lidar_data(data))

        def get_lidar_data(data):
            self.lidar_data = data

        # Add camera sensor
        self.camera_sensor = self.world.spawn_actor(self.camera_bp,
                                                    self.camera_trans,
                                                    attach_to=self.ego)
        self.camera_sensor.listen(lambda data: get_camera_img(data))

        def get_camera_img(data):
            array = np.frombuffer(data.raw_data, dtype=np.dtype("uint8"))
            array = np.reshape(array, (data.height, data.width, 4))
            array = array[:, :, :3]
            array = array[:, :, ::-1]
            self.camera_img = array

        # Update timesteps
        self.time_step = 0
        self.reset_step += 1

        # Enable sync mode
        self.settings.synchronous_mode = True
        self.world.apply_settings(self.settings)

        # ===================================================================================
        self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()
        # ===================================================================================

        W_X_0 = [t[0] for t in self.waypoints]
        W_Y_0 = [t[1] for t in self.waypoints]
        self.tx, self.ty, self.tyaw, self.tc, self.csp = frenet_optimal_trajectory.generate_target_course(
            W_X_0, W_Y_0)

        self.birdeye_render.set_hero(self.ego, self.ego.id)

        return self._get_obs()

    def step(self, action):
        # Get target speed, target_waypoint
        print('step mai hai ham')
        ego_trans = self.ego.get_transform()
        ego_z = ego_trans.location.z
        # act = carla.VehicleControl(throttle=float(throttle), steer=float(-steer), brake=float(brake))
        Ti = action[1]
        di = action[2]
        di_d = action[3]
        tv = action[0]
        # Initial Conditions
        s0, c_speed, c_d, c_d_d, c_d_dd = self.initial_conditions()

        # Generate Frenet Path
        path = frenet_optimal_trajectory.frenet_optimal_planning(
            self.csp, s0, c_speed, c_d, c_d_d, c_d_dd, Ti, di, di_d, tv,
            self.path_params)

        self.target_waypoint = [path.x[1], path.y[1], ego_z]
        # sqrt(pow(1 - rk[1]*c_d, 2)*pow(c_speed, 2) + pow(c_d_d, 2))
        # To be given in Km/h
        self._target_speed = ((((1 - self.tc[1])**2) * ((path.s_d[1])**2) +
                               ((path.d_d[1])**2))**0.5) * 3.6
        # Apply control
        control = self._vehicle_controller.run_step(self._target_speed,
                                                    self.target_waypoint)
        self.ego.apply_control(control)

        self.world.tick()

        # Append actors polygon list
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        while len(self.vehicle_polygons) > self.max_past_step:
            self.vehicle_polygons.pop(0)
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)
        while len(self.walker_polygons) > self.max_past_step:
            self.walker_polygons.pop(0)

        # route planner
        self.waypoints, _, self.vehicle_front = self.routeplanner.run_step()

        # state information
        info = {
            'waypoints': self.waypoints,
            'vehicle_front': self.vehicle_front
        }

        # Update timesteps
        self.time_step += 1
        self.total_step += 1

        return (self._get_obs(), self._get_reward(), self._terminal(),
                copy.deepcopy(info))

    def initial_conditions(self):
        print('initial_conditions mai hai ham')
        vel = self.ego.get_velocity()
        ego_trans = self.ego.get_transform()
        ego_x, ego_y = get_pos(self.ego)

        v = ((vel.x**2) + (vel.y**2))**0.5
        min_id, c_d = self.find_nearest_in_global_path()

        vec1 = [ego_x - self.tx[min_id], ego_y - self.ty[min_id]]
        vec2 = [
            self.tx[min_id] - self.tx[min_id + 1],
            self.tx[min_id] - self.ty[min_id + 1]
        ]
        curl = vec1[0] * vec2[1] - vec1[1] * vec2[0]
        if (curl < 0):
            c_d = c_d * (-1)

        s0 = self.calc_s(min_id)
        ego_yaw = ego_trans.rotation.yaw / 180 * np.pi
        global_path_yaw = self.tyaw[min_id]
        delta_theta = ego_yaw - global_path_yaw
        c_d_d = v * math.sin(delta_theta)
        k_r = self.tc[min_id]
        c_speed = v * math.cos(delta_theta) / (1 - k_r * c_d)
        c_d_dd = 0

        return s0, c_speed, c_d, c_d_d, c_d_dd

    def find_nearest_in_global_path(self):
        print('find nearest in global path')
        ego_x, ego_y = get_pos(self.ego)
        tx = np.asarray(self.tx, dtype=np.float32)
        ty = np.asarray(self.ty, dtype=np.float32)
        temp = (tx - ego_x)**2 + (ty - ego_y)**2
        temp = np.sqrt(temp)
        min_id = np.argmin(temp)
        min_dist = np.min(temp)
        return min_id, min_dist

    def calc_s(self, min_id):
        print('calcs mai hai ham')

        tx = np.asarray(self.tx, dtype=np.float32)
        ty = np.asarray(self.ty, dtype=np.float32)
        to_stop = min_id + 1
        tx_cut = tx[:to_stop]
        ty_cut = ty[:to_stop]
        tx_cut_2 = np.hstack((tx_cut[0], tx_cut))
        ty_cut_2 = np.hstack((ty_cut[0], ty_cut))
        tx_cut_2 = tx_cut_2[:to_stop]
        ty_cut_2 = ty_cut_2[:to_stop]
        s = np.sum(np.sqrt((tx_cut - tx_cut_2)**2 + (ty_cut - ty_cut_2)**2))
        return s

    def seed(self, seed=None):
        print('seed')
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def render(self, mode):
        pass

    def _create_vehicle_bluepprint(self,
                                   actor_filter,
                                   color=None,
                                   number_of_wheels=[4]):
        print('create vehicale blyeprint')
        """Create the blueprint for a specific actor type.

		Args:
		  actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

		Returns:
		  bp: the blueprint object of carla.
		"""
        blueprints = self.world.get_blueprint_library().filter(actor_filter)
        blueprint_library = []
        for nw in number_of_wheels:
            blueprint_library = blueprint_library + \
                [x for x in blueprints if int(
                    x.get_attribute('number_of_wheels')) == nw]
        bp = random.choice(blueprint_library)
        if bp.has_attribute('color'):
            if not color:
                color = random.choice(
                    bp.get_attribute('color').recommended_values)
            bp.set_attribute('color', color)
        return bp

    def _init_renderer(self):
        """Initialize the birdeye view renderer.
        """
        print('init renderer mai hai ham')
        pygame.init()
        self.display = pygame.display.set_mode(
            (self.display_size * 3, self.display_size),
            pygame.HWSURFACE | pygame.DOUBLEBUF)

        pixels_per_meter = self.display_size / self.obs_range
        pixels_ahead_vehicle = (self.obs_range / 2 -
                                self.d_behind) * pixels_per_meter
        birdeye_params = {
            'screen_size': [self.display_size, self.display_size],
            'pixels_per_meter': pixels_per_meter,
            'pixels_ahead_vehicle': pixels_ahead_vehicle
        }
        self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

    def _set_synchronous_mode(self, synchronous=True):
        """Set whether to use the synchronous mode.
        """
        print('set synchrounous mode mai hai ham')
        self.settings.synchronous_mode = synchronous
        self.world.apply_settings(self.settings)

    def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
        """Try to spawn a surrounding vehicle at specific transform with random bluprint.

        Args:
          transform: the carla transform object.

        Returns:
          Bool indicating whether the spawn is successful.
        """
        print('try spawn rnadom vehicle mai hai ham')
        blueprint = self._create_vehicle_bluepprint(
            'vehicle.*', number_of_wheels=number_of_wheels)
        blueprint.set_attribute('role_name', 'autopilot')
        vehicle = self.world.try_spawn_actor(blueprint, transform)
        if vehicle is not None:
            vehicle.set_autopilot()
            return True
        return False

    def _try_spawn_random_walker_at(self, transform):
        """Try to spawn a walker at specific transform with random bluprint.

        Args:
          transform: the carla transform object.

        Returns:
          Bool indicating whether the spawn is successful.
        """
        print('try spawn random walker mai hai ham')
        walker_bp = random.choice(
            self.world.get_blueprint_library().filter('walker.*'))
        # set as not invencible
        if walker_bp.has_attribute('is_invincible'):
            walker_bp.set_attribute('is_invincible', 'false')
        walker_actor = self.world.try_spawn_actor(walker_bp, transform)

        if walker_actor is not None:
            walker_controller_bp = self.world.get_blueprint_library().find(
                'controller.ai.walker')
            walker_controller_actor = self.world.spawn_actor(
                walker_controller_bp, carla.Transform(), walker_actor)
            # start walker
            walker_controller_actor.start()
            # set walk to random point
            walker_controller_actor.go_to_location(
                self.world.get_random_location_from_navigation())
            # random max speed
            # max speed between 1 and 2 (default is 1.4 m/s)
            walker_controller_actor.set_max_speed(1 + random.random())
            return True
        return False

    def _try_spawn_ego_vehicle_at(self, transform):
        """Try to spawn the ego vehicle at specific transform.
        Args:
          transform: the carla transform object.
        Returns:
          Bool indicating whether the spawn is successful.
        """
        print('try spawn ego vehicle at mai hai ham')

        vehicle = None
        # Check if ego position overlaps with surrounding vehicles
        overlap = False
        for idx, poly in self.vehicle_polygons[-1].items():
            poly_center = np.mean(poly, axis=0)
            ego_center = np.array([transform.location.x, transform.location.y])
            dis = np.linalg.norm(poly_center - ego_center)
            if dis > 8:
                continue
            else:
                overlap = True
                break

        if not overlap:
            vehicle = self.world.try_spawn_actor(self.ego_bp, transform)

        if vehicle is not None:
            self.ego = vehicle
            return True

        return False

    def _get_actor_polygons(self, filt):
        """Get the bounding box polygon of actors.

        Args:
          filt: the filter indicating what type of actors we'll look at.

        Returns:
          actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
        """
        print('get actor polygons mai hai ham')

        actor_poly_dict = {}
        for actor in self.world.get_actors().filter(filt):
            # Get x, y and yaw of the actor
            trans = actor.get_transform()
            x = trans.location.x
            y = trans.location.y
            yaw = trans.rotation.yaw / 180 * np.pi
            # Get length and width
            bb = actor.bounding_box
            l = bb.extent.x
            w = bb.extent.y
            # Get bounding box polygon in the actor's local coordinate
            poly_local = np.array([[l, w], [l, -w], [-l, -w],
                                   [-l, w]]).transpose()
            # Get rotation matrix to transform to global coordinate
            R = np.array([[np.cos(yaw), -np.sin(yaw)],
                          [np.sin(yaw), np.cos(yaw)]])
            # Get global bounding box polygon
            poly = np.matmul(R, poly_local).transpose() + \
                np.repeat([[x, y]], 4, axis=0)
            actor_poly_dict[actor.id] = poly
        return actor_poly_dict

    def _get_obs(self):
        """Get the observations."""
        # Birdeye rendering
        print('get obs mai hai ham')

        self.birdeye_render.vehicle_polygons = self.vehicle_polygons
        self.birdeye_render.walker_polygons = self.walker_polygons
        self.birdeye_render.waypoints = self.waypoints

        # birdeye view with roadmap and actors
        birdeye_render_types = ['roadmap', 'actors']
        if self.display_route:
            birdeye_render_types.append('waypoints')
        self.birdeye_render.render(self.display, birdeye_render_types)
        birdeye = pygame.surfarray.array3d(self.display)
        birdeye = birdeye[0:self.display_size, :, :]
        birdeye = display_to_rgb(birdeye, self.obs_size)

        # Roadmap
        if self.pixor:
            roadmap_render_types = ['roadmap']
            if self.display_route:
                roadmap_render_types.append('waypoints')
            self.birdeye_render.render(self.display, roadmap_render_types)
            roadmap = pygame.surfarray.array3d(self.display)
            roadmap = roadmap[0:self.display_size, :, :]
            roadmap = display_to_rgb(roadmap, self.obs_size)
            # Add ego vehicle
            for i in range(self.obs_size):
                for j in range(self.obs_size):
                    if abs(birdeye[i, j, 0] - 255) < 20 and abs(
                            birdeye[i, j, 1] -
                            0) < 20 and abs(birdeye[i, j, 0] - 255) < 20:
                        roadmap[i, j, :] = birdeye[i, j, :]

        # Display birdeye image
        birdeye_surface = rgb_to_display_surface(birdeye, self.display_size)
        self.display.blit(birdeye_surface, (0, 0))

        # Lidar image generation
        point_cloud = []
        # Get point cloud data
        for location in self.lidar_data:
            point_cloud.append([location.x, location.y, -location.z])
        point_cloud = np.array(point_cloud)
        # Separate the 3D space to bins for point cloud, x and y is set according to self.lidar_bin,
        # and z is set to be two bins.
        y_bins = np.arange(-(self.obs_range - self.d_behind),
                           self.d_behind + self.lidar_bin, self.lidar_bin)
        x_bins = np.arange(-self.obs_range / 2,
                           self.obs_range / 2 + self.lidar_bin, self.lidar_bin)
        z_bins = [-self.lidar_height - 1, -self.lidar_height + 0.25, 1]
        # Get lidar image according to the bins
        lidar, _ = np.histogramdd(point_cloud, bins=(x_bins, y_bins, z_bins))
        # Only 2 bins in z-direction. Taking positive values.
        lidar[:, :, 0] = np.array(lidar[:, :, 0] > 0, dtype=np.uint8)
        lidar[:, :, 1] = np.array(lidar[:, :, 1] > 0, dtype=np.uint8)
        # Add the waypoints to lidar image
        if self.display_route:
            wayptimg = (birdeye[:, :, 0] <= 10) * \
                (birdeye[:, :, 1] <= 10) * (birdeye[:, :, 2] >= 240)
        else:
            wayptimg = birdeye[:, :, 0] < 0  # Equal to a zero matrix
        wayptimg = np.expand_dims(wayptimg, axis=2)
        wayptimg = np.fliplr(np.rot90(wayptimg, 3))

        # Get the final lidar image
        lidar = np.concatenate((lidar, wayptimg), axis=2)
        lidar = np.flip(lidar, axis=1)
        lidar = np.rot90(lidar, 1)
        lidar = lidar * 255

        # Display lidar image
        lidar_surface = rgb_to_display_surface(lidar, self.display_size)
        self.display.blit(lidar_surface, (self.display_size, 0))

        # Display camera image
        camera = resize(self.camera_img, (self.obs_size, self.obs_size)) * 255
        camera_surface = rgb_to_display_surface(camera, self.display_size)
        self.display.blit(camera_surface, (self.display_size * 2, 0))

        # Display on pygame
        pygame.display.flip()

        # State observation
        ego_trans = self.ego.get_transform()
        ego_x = ego_trans.location.x
        ego_y = ego_trans.location.y
        ego_yaw = ego_trans.rotation.yaw / 180 * np.pi  # in Radians
        lateral_dis, w = get_preview_lane_dis(self.waypoints, ego_x, ego_y)
        delta_yaw = np.arcsin(
            np.cross(w, np.array(np.array([np.cos(ego_yaw),
                                           np.sin(ego_yaw)]))))
        v = self.ego.get_velocity()
        ang_v = self.ego.get_angular_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        # state = np.array([lateral_dis, - delta_yaw, speed,ang_v.z, self.vehicle_front])
        state = np.array([lateral_dis, -delta_yaw, speed, ang_v.z])
        # state.reshape((4, 1))

        if self.pixor:
            # Vehicle classification and regression maps (requires further normalization)
            vh_clas = np.zeros((self.pixor_size, self.pixor_size))
            vh_regr = np.zeros((self.pixor_size, self.pixor_size, 6))

            # Generate the PIXOR image. Note in CARLA it is using left-hand coordinate
            # Get the 6-dim geom parametrization in PIXOR, here we use pixel coordinate
            for actor in self.world.get_actors().filter('vehicle.*'):
                x, y, yaw, l, w = get_info(actor)
                x_local, y_local, yaw_local = get_local_pose(
                    (x, y, yaw), (ego_x, ego_y, ego_yaw))
                if actor.id != self.ego.id:
                    if abs(
                            y_local
                    ) < self.obs_range / 2 + 1 and x_local < self.obs_range - self.d_behind + 1 and x_local > -self.d_behind - 1:
                        x_pixel, y_pixel, yaw_pixel, l_pixel, w_pixel = get_pixel_info(
                            local_info=(x_local, y_local, yaw_local, l, w),
                            d_behind=self.d_behind,
                            obs_range=self.obs_range,
                            image_size=self.pixor_size)
                        cos_t = np.cos(yaw_pixel)
                        sin_t = np.sin(yaw_pixel)
                        logw = np.log(w_pixel)
                        logl = np.log(l_pixel)
                        pixels = get_pixels_inside_vehicle(
                            pixel_info=(x_pixel, y_pixel, yaw_pixel, l_pixel,
                                        w_pixel),
                            pixel_grid=self.pixel_grid)
                        for pixel in pixels:
                            vh_clas[pixel[0], pixel[1]] = 1
                            dx = x_pixel - pixel[0]
                            dy = y_pixel - pixel[1]
                            vh_regr[pixel[0], pixel[1], :] = np.array(
                                [cos_t, sin_t, dx, dy, logw, logl])

            # Flip the image matrix so that the origin is at the left-bottom
            vh_clas = np.flip(vh_clas, axis=0)
            vh_regr = np.flip(vh_regr, axis=0)

            # Pixor state, [x, y, cos(yaw), sin(yaw), speed]
            pixor_state = [
                ego_x, ego_y,
                np.cos(ego_yaw),
                np.sin(ego_yaw), speed
            ]

        obs = {
            'camera': camera.astype(np.uint8),
            'lidar': lidar.astype(np.uint8),
            'birdeye': birdeye.astype(np.uint8),
            'state': state
        }

        if self.pixor:
            obs.update({
                'roadmap':
                roadmap.astype(np.uint8),
                'vh_clas':
                np.expand_dims(vh_clas, -1).astype(np.float32),
                'vh_regr':
                vh_regr.astype(np.float32),
                'pixor_state':
                pixor_state,
            })
        print(camera.shape)
        print(lidar.shape)
        print(birdeye.shape)
        print(state.shape)
        # float32 mai likhna hai
        temp = lidar.flatten()
        print(temp.shape)
        print("hello")
        temp = np.concatenate((temp, state))
        temp = temp.reshape((temp.shape[0], 1))
        temp = scale(temp, -1, 1)
        print(temp.shape)
        return temp

    def _get_reward(self):
        """Calculate the step reward."""
        print('get reward mai hai ham')

        # reward for speed tracking
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        r_speed = -abs(speed - self.desired_speed)

        # reward for collision
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -1

        # reward for steering:
        r_steer = -self.ego.get_control().steer**2

        # reward for out of lane
        ego_x, ego_y = get_pos(self.ego)
        dis, w = get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        if abs(dis) > self.out_lane_thres:
            r_out = -1

        # longitudinal speed
        lspeed = np.array([v.x, v.y])
        lspeed_lon = np.dot(lspeed, w)

        # cost for too fast
        r_fast = 0
        if lspeed_lon > self.desired_speed:
            r_fast = -1

        # cost for lateral acceleration
        r_lat = -abs(self.ego.get_control().steer) * lspeed_lon**2

        r = 200*r_collision + 1*lspeed_lon + 10 * \
            r_fast + 1*r_out + r_steer*5 + 0.2*r_lat - 0.1

        return r

    def _terminal(self):
        """Calculate whether to terminate the current episode."""
        print('terminal mai hai ham')
        # Get ego state
        ego_x, ego_y = get_pos(self.ego)

        # If collides
        if len(self.collision_hist) > 0:
            return True

        # If reach maximum timestep
        if self.time_step > self.max_time_episode:
            return True

        # If at destination
        if self.dests is not None:  # If at destination
            for dest in self.dests:
                if np.sqrt((ego_x - dest[0])**2 + (ego_y - dest[1])**2) < 4:
                    return True

        # If out of lane
        dis, _ = get_lane_dis(self.waypoints, ego_x, ego_y)
        if abs(dis) > self.out_lane_thres:
            return True

        return False

    def _clear_all_actors(self, actor_filters):
        """Clear specific actors."""
        print('clear all  mai hai ham')

        for actor_filter in actor_filters:
            for actor in self.world.get_actors().filter(actor_filter):
                if actor.is_alive:
                    if actor.type_id == 'controller.ai.walker':
                        actor.stop()
                    actor.destroy()
Exemplo n.º 6
0
class CarlaEnv(gym.Env):
    """An OpenAI gym wrapper for CARLA simulator."""
    def __init__(self, params):
        # parameters
        self.display_size = params['display_size']  # rendering screen size
        self.max_past_step = params['max_past_step']
        self.number_of_vehicles = params['number_of_vehicles']
        self.number_of_walkers = params['number_of_walkers']
        self.dt = params['dt']
        self.task_mode = params['task_mode']
        self.max_time_episode = params['max_time_episode']
        self.max_waypt = params['max_waypt']
        self.obs_range = params['obs_range']
        self.lidar_bin = params['lidar_bin']
        self.d_behind = params['d_behind']
        self.obs_size = int(self.obs_range / self.lidar_bin)
        self.out_lane_thres = params['out_lane_thres']
        self.desired_speed = params['desired_speed']
        self.max_ego_spawn_times = params['max_ego_spawn_times']
        self.display_route = params['display_route']
        self.use_rgb_camera = params['RGB_cam']
        self.traffic_vehicles = []
        #self.discrete_acc = [-1,-0.5,0.0,0.5,1.0] # discrete actions for throttle
        self.discrete_vel = [-5.0, -1.0, 0.0, 1.0,
                             5.0]  # discrete actions for velocity
        self.discrete_actions = params[
            'discrete']  # boolean to use discrete or continoius action space
        self.cur_action = None
        self.pedal_pid = PID(0.7, 0.01, 0.0)
        self.pedal_pid.output_limits = (-1, 1)
        self.rl_speed = 0.0
        self.pedal_pid.setpoint = 0.0
        self.dist_to_lead = -1
        # Destination
        if params['task_mode'] == 'acc_1':
            self.dests = [[592.1, 244.7, 0]]  # stopping condition in Town 06
        else:
            self.dests = None
        self.idle_timesteps = 0
        # action and observation spaces

        # self.action_space = spaces.Box(np.array([params['continuous_accel_range'][0]], dtype=np.float32), np.array([params['continuous_accel_range'][1]], dtype=np.float32))  # acc
        #self.action_space = spaces.Discrete(len(self.discrete_acc))

        if self.discrete_actions:
            self.action_space = spaces.Discrete(
                5)  # slow down -5, slowdown -1 m/s, do nothing, speed up 1 m/s
        else:
            self.action_space = spaces.Box(np.array([0.0]), np.array(
                [30.0]))  # speed is continous from 0 m.s to 30 m.s
        self.observation_space = spaces.Box(np.array([0, 0, -1.0]),
                                            np.array([40, 40, 100]),
                                            dtype=np.float32)

        # Connect to carla server and get world object
        print('connecting to Carla server...')
        self.client = carla.Client('localhost', params['port'])
        self.client.set_timeout(10.0)
        self.world = self.client.load_world(params['town'])
        print('Carla server connected!')
        self.map = self.world.get_map()
        self.tm = self.client.get_trafficmanager(int(8000))
        self.tm_port = self.tm.get_port()
        #print('Traffic Manager Port ' + self.tm_port)
        # Set weather
        self.world.set_weather(carla.WeatherParameters.ClearNoon)

        # Get spawn points
        self.vehicle_spawn_points = list(
            self.world.get_map().get_spawn_points())
        self.walker_spawn_points = []
        for i in range(self.number_of_walkers):
            spawn_point = carla.Transform()
            loc = self.world.get_random_location_from_navigation()
            if (loc != None):
                spawn_point.location = loc
                self.walker_spawn_points.append(spawn_point)

        # Create the ego vehicle blueprint
        self.ego_bp = self._create_vehicle_bluepprint(
            params['ego_vehicle_filter'], color='49,8,8')

        # Collision sensor
        self.collision_hist = []  # The collision history
        self.collision_hist_l = 1  # collision history length
        self.collision_bp = self.world.get_blueprint_library().find(
            'sensor.other.collision')

        # Camera sensor
        self.camera_img = np.zeros((self.obs_size, self.obs_size, 3),
                                   dtype=np.uint8)
        self.camera_trans = carla.Transform(carla.Location(x=0.8, z=1.7))
        self.camera_bp = self.world.get_blueprint_library().find(
            'sensor.camera.rgb')
        # Modify the attributes of the blueprint to set image resolution and field of view.
        self.camera_bp.set_attribute('image_size_x', str(self.obs_size))
        self.camera_bp.set_attribute('image_size_y', str(self.obs_size))
        self.camera_bp.set_attribute('fov', '110')
        # Set the time in seconds between sensor captures
        self.camera_bp.set_attribute('sensor_tick', '0.02')

        # Set fixed simulation step for synchronous mode
        self.settings = self.world.get_settings()
        self.settings.fixed_delta_seconds = self.dt

        # Record the time of total steps and resetting steps
        self.reset_step = 0
        self.total_step = 0

        # Initialize the renderer
        self._init_renderer()

    def reset(self):
        # Clear sensor objects
        self._clear_all_sensors()
        self.collision_sensor = None
        self.camera_sensor = None
        self.idle_timesteps = 0
        # Delete sensors, vehicles and walkers
        self._clear_all_actors([
            'sensor.other.collision', 'sensor.lidar.ray_cast',
            'sensor.camera.rgb', 'vehicle.*', 'controller.ai.walker',
            'walker.*'
        ])

        # Disable sync mode
        self._set_synchronous_mode(False)

        # Spawn vehicles in same lane as ego vehicle and ahead
        ego_vehicle_traffic_spawns_1 = get_spawn_points_for_traffic(
            40, [-7, -6, -5, -4], self.map, self.number_of_vehicles)
        ego_vehicle_traffic_spawns_2 = get_spawn_points_for_traffic(
            39, [-7, -6, -5, -4], self.map, self.number_of_vehicles, start=15)
        ego_vehicle_traffic_spawns = ego_vehicle_traffic_spawns_1 + ego_vehicle_traffic_spawns_2
        random.shuffle(ego_vehicle_traffic_spawns)
        count = self.number_of_vehicles
        if count > 0:
            for spawn_point in ego_vehicle_traffic_spawns:
                if self._try_spawn_random_vehicle_at(spawn_point,
                                                     number_of_wheels=[4]):
                    count -= 1
                if count <= 0:
                    break
        while count > 0:
            if self._try_spawn_random_vehicle_at(
                    random.choice(ego_vehicle_traffic_spawns),
                    number_of_wheels=[4]):
                count -= 1

        # set autopilot for all traffic vehicles:

        batch = []
        for vehicle in self.traffic_vehicles:
            batch.append(carla.command.SetAutopilot(vehicle, True))
        self.client.apply_batch_sync(batch)

        # Get actors polygon list
        self.vehicle_polygons = []
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        self.walker_polygons = []
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)

        # Spawn the ego vehicle
        ego_spawn_times = 0
        while True:
            if ego_spawn_times > self.max_ego_spawn_times:
                self.reset()

            if self.task_mode == 'acc_1':
                wpt1 = get_waypoint_for_ego_spawn(road_id=39,
                                                  lane_id=-5,
                                                  s=0,
                                                  map=self.map)
                transform = wpt1.transform
                transform.location.z += 2.0
            if self._try_spawn_ego_vehicle_at(transform):
                break
            else:
                print('trying to spawn %d' % ego_spawn_times)
                ego_spawn_times += 1
                time.sleep(0.1)

        # Add collision sensor
        self.collision_sensor = self.world.spawn_actor(self.collision_bp,
                                                       carla.Transform(),
                                                       attach_to=self.ego)
        self.collision_sensor.listen(lambda event: get_collision_hist(event))

        def get_collision_hist(event):
            impulse = event.normal_impulse
            intensity = np.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
            self.collision_hist.append(intensity)
            if len(self.collision_hist) > self.collision_hist_l:
                self.collision_hist.pop(0)

        self.collision_hist = []

        # Add camera sensor
        if self.use_rgb_camera:
            self.camera_sensor = self.world.spawn_actor(self.camera_bp,
                                                        self.camera_trans,
                                                        attach_to=self.ego)
            self.camera_sensor.listen(lambda data: get_camera_img(data))

            def get_camera_img(data):
                array = np.frombuffer(data.raw_data, dtype=np.dtype("uint8"))
                array = np.reshape(array, (data.height, data.width, 4))
                array = array[:, :, :3]
                array = array[:, :, ::-1]
                self.camera_img = array

        # Update timesteps
        self.time_step = 0
        self.reset_step += 1

        # Enable sync mode
        self.settings.synchronous_mode = False
        if not self.use_rgb_camera:
            self.settings.no_rendering_mode = True
        self.world.apply_settings(self.settings)

        self.routeplanner = RoutePlanner(self.ego, self.max_waypt)
        self.waypoints, _, self.vehicle_hazards = self.routeplanner.run_step()
        # Set ego information for render
        self.birdeye_render.set_hero(self.ego, self.ego.id)

        return self._get_obs()

    def step(self, action):
        # Calculate acceleration and steering
        #acc = self.discrete_acc[action]
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)

        if speed < 1e-1:
            self.idle_timesteps += 1
        else:
            self.idle_timesteps = 0

        if self.discrete_actions:
            self.rl_speed += self.discrete_vel[action]  # [-1.0 0.0, 1.0]
        else:
            self.rl_speed = action  # range from 0 to 30
        self.rl_speed = np.clip(self.rl_speed, 0.0, 30.0)

        pid = self.pedal_pid(-(self.rl_speed - speed))
        acc = pid
        # acc = self.pedal_pid(speed)

        # Convert acceleration to throttle and brake
        if acc > 0:
            throttle = np.clip(acc, 0, 1)
            brake = 0
        else:
            throttle = 0
            brake = np.clip(-acc, 0, 1)
        #print(acc,speed,self.desired_speed-speed)
        #print("rl-speed %.2f, pid %.2f, acc %.2f" % (self.rl_speed  , pid, acc) )

        # Apply control
        act = carla.VehicleControl(throttle=float(throttle),
                                   steer=0.0,
                                   brake=float(brake))
        self.ego.apply_control(act)

        self.world.tick()

        # Append actors polygon list
        vehicle_poly_dict = self._get_actor_polygons('vehicle.*')
        self.vehicle_polygons.append(vehicle_poly_dict)
        while len(self.vehicle_polygons) > self.max_past_step:
            self.vehicle_polygons.pop(0)
        walker_poly_dict = self._get_actor_polygons('walker.*')
        self.walker_polygons.append(walker_poly_dict)
        while len(self.walker_polygons) > self.max_past_step:
            self.walker_polygons.pop(0)

        # route planner
        self.waypoints, _, self.vehicle_hazards = self.routeplanner.run_step()
        #print(self.vehicle_hazards)
        # state information
        info = {
            'waypoints': self.waypoints,
        }
        # Update timesteps
        self.time_step += 1
        self.total_step += 1

        return (self._get_obs(), self._get_reward(), self._terminal(),
                copy.deepcopy(info))

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def render(self, mode):
        pass

    def _create_vehicle_bluepprint(self,
                                   actor_filter,
                                   color=None,
                                   number_of_wheels=[4]):
        """Create the blueprint for a specific actor type.

    Args:
      actor_filter: a string indicating the actor type, e.g, 'vehicle.lincoln*'.

    Returns:
      bp: the blueprint object of carla.
    """
        blueprints = self.world.get_blueprint_library().filter(actor_filter)
        blueprint_library = []
        for nw in number_of_wheels:
            blueprint_library = blueprint_library + [
                x for x in blueprints
                if int(x.get_attribute('number_of_wheels')) == nw
            ]
        bp = random.choice(blueprint_library)
        if bp.has_attribute('color'):
            if not color:
                color = random.choice(
                    bp.get_attribute('color').recommended_values)
            bp.set_attribute('color', color)
        return bp

    def _init_renderer(self):
        """Initialize the birdeye view renderer.
    """
        pygame.init()
        self.pyfont = pygame.font.SysFont(None, 22)
        self.display = pygame.display.set_mode(
            (self.display_size * 3, self.display_size),
            pygame.HWSURFACE | pygame.DOUBLEBUF)

        pixels_per_meter = self.display_size / self.obs_range
        pixels_ahead_vehicle = (self.obs_range / 2 -
                                self.d_behind) * pixels_per_meter
        birdeye_params = {
            'screen_size': [self.display_size, self.display_size],
            'pixels_per_meter': pixels_per_meter,
            'pixels_ahead_vehicle': pixels_ahead_vehicle
        }
        self.birdeye_render = BirdeyeRender(self.world, birdeye_params)

    def _set_synchronous_mode(self, synchronous=True):
        """Set whether to use the synchronous mode.
    """
        self.settings.synchronous_mode = synchronous
        self.world.apply_settings(self.settings)

    def _try_spawn_random_vehicle_at(self, transform, number_of_wheels=[4]):
        """Try to spawn a surrounding vehicle at specific transform with random bluprint.

    Args:
      transform: the carla transform object.

    Returns:
      Bool indicating whether the spawn is successful.
    """
        blueprint = self._create_vehicle_bluepprint(
            'vehicle.*', number_of_wheels=number_of_wheels)
        blueprint.set_attribute('role_name', 'autopilot')
        vehicle = self.world.try_spawn_actor(blueprint, transform)
        if vehicle is not None:
            self.traffic_vehicles.append(vehicle)
            #vehicle.set_autopilot()
            # batch = []
            # batch.append(carla.command.SetAutopilot(vehicle,True))
            # self.client.apply_batch_sync(batch) # not how this is supposed to be done but oh well
            #vehicle.enable_constant_velocity(np.random.uniform(low=18.0,high=30.0))
            vehicle.set_autopilot(True, self.tm_port)
            self.tm.auto_lane_change(vehicle, False)
            high = np.random.uniform(low=-20, high=0)
            low = np.random.uniform(low=-20, high=0)

            self.tm.vehicle_percentage_speed_difference(
                vehicle, random.choice([high, low])
            )  # percentage difference between posted speed and vehicle speed. Negative is greater
            return True
        return False

    def _try_spawn_ego_vehicle_at(self, transform):
        """Try to spawn the ego vehicle at specific transform.
    Args:
      transform: the carla transform object.
    Returns:
      Bool indicating whether the spawn is successful.
    """
        vehicle = None
        # Check if ego position overlaps with surrounding vehicles
        overlap = False
        for idx, poly in self.vehicle_polygons[-1].items():
            poly_center = np.mean(poly, axis=0)
            ego_center = np.array([transform.location.x, transform.location.y])
            dis = np.linalg.norm(poly_center - ego_center)
            if dis > 8:
                continue
            else:
                overlap = True
                print('overlapping vehicle when trying to spawn')
                break

        if not overlap:
            vehicle = self.world.try_spawn_actor(self.ego_bp, transform)

        if vehicle is not None:
            self.ego = vehicle

            # batch = []
            # batch.append(carla.command.SetAutopilot(self.ego,False))
            # self.client.apply_batch_sync(batch)
            # self.tm.vehicle_percentage_speed_difference(self.ego,-30)
            #self.tm.distance_to_leading_vehicle(self.ego,20.0)
            #self.tm.set_global_distance_to_leading_vehicle(0.0)
            # self.tm.auto_lane_change(self.ego,False)

            return True
        print('could not spawn vehicle')
        return False

    def _get_actor_polygons(self, filt):
        """Get the bounding box polygon of actors.

    Args:
      filt: the filter indicating what type of actors we'll look at.

    Returns:
      actor_poly_dict: a dictionary containing the bounding boxes of specific actors.
    """
        actor_poly_dict = {}
        for actor in self.world.get_actors().filter(filt):
            # Get x, y and yaw of the actor
            trans = actor.get_transform()
            x = trans.location.x
            y = trans.location.y
            yaw = trans.rotation.yaw / 180 * np.pi
            # Get length and width
            bb = actor.bounding_box
            l = bb.extent.x
            w = bb.extent.y
            # Get bounding box polygon in the actor's local coordinate
            poly_local = np.array([[l, w], [l, -w], [-l, -w],
                                   [-l, w]]).transpose()
            # Get rotation matrix to transform to global coordinate
            R = np.array([[np.cos(yaw), -np.sin(yaw)],
                          [np.sin(yaw), np.cos(yaw)]])
            # Get global bounding box polygon
            poly = np.matmul(R, poly_local).transpose() + np.repeat(
                [[x, y]], 4, axis=0)
            actor_poly_dict[actor.id] = poly
        return actor_poly_dict

    def _get_obs(self):
        """Get the observations."""
        ## Birdeye rendering
        self.birdeye_render.vehicle_polygons = self.vehicle_polygons
        self.birdeye_render.walker_polygons = self.walker_polygons
        self.birdeye_render.waypoints = self.waypoints

        # birdeye view with roadmap and actors
        birdeye_render_types = ['roadmap', 'actors']
        if self.display_route:
            birdeye_render_types.append('waypoints')
        self.birdeye_render.render(self.display, birdeye_render_types)
        birdeye = pygame.surfarray.array3d(self.display)
        birdeye = birdeye[0:self.display_size, :, :]
        birdeye = display_to_rgb(birdeye, self.obs_size)

        # State observation
        ego_trans = self.ego.get_transform()
        ego_x = ego_trans.location.x
        ego_y = ego_trans.location.y
        ego_yaw = ego_trans.rotation.yaw / 180 * np.pi
        lateral_dis, w = get_preview_lane_dis(self.waypoints, ego_x, ego_y)
        delta_yaw = np.arcsin(
            np.cross(w, np.array(np.array([np.cos(ego_yaw),
                                           np.sin(ego_yaw)]))))
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)

        lead_vehicle = None
        if len(self.vehicle_hazards) > 0:
            dist_to_lead = 100
            for i in range(len(self.vehicle_hazards)):
                hazard_loc = self.vehicle_hazards[i].get_transform()
                dist_to_lead_ = ((hazard_loc.location.x - ego_x)**2 +
                                 (hazard_loc.location.y - ego_y)**2)**(1 / 2)
                if dist_to_lead_ < dist_to_lead:
                    dist_to_lead = dist_to_lead_
                    lead_vehicle = self.vehicle_hazards[i]
        else:
            dist_to_lead = -1
        self.dist_to_lead = dist_to_lead

        if lead_vehicle is not None:
            lead_v = lead_vehicle.get_velocity()
            lead_speed = np.sqrt(lead_v.x**2 + lead_v.y**2)
        else:
            lead_speed = -1

        state = np.array([speed, lead_speed, dist_to_lead])
        ## Get leading vehicle info

        ## Display camera image
        if self.use_rgb_camera:
            camera = resize(self.camera_img,
                            (self.obs_size, self.obs_size)) * 255
            camera_surface = rgb_to_display_surface(camera, self.display_size)
            self.display.blit(camera_surface, (self.display_size * 2, 0))
        else:
            camera = np.zeros((self.obs_size, self.obs_size, 3),
                              dtype=np.uint8)

        ## Display info statistics
        self.display.blit(
            self.pyfont.render('speed ' + str(round(speed, 1)) + ' m/s', True,
                               (255, 0, 0)), (self.display_size * 1, 80))
        self.display.blit(
            self.pyfont.render(
                'lead_dist ' + str(round(dist_to_lead, 1)) + ' m', True,
                (255, 255, 0)), (self.display_size * 1, 120))
        self.display.blit(
            self.pyfont.render('lead_v ' + str(round(lead_speed, 1)) + ' m/s',
                               True, (255, 255, 0)),
            (self.display_size * 1, 100))
        # Display on pygame
        pygame.display.flip()

        return state

    def _get_reward(self):
        """Calculate the step reward."""
        # reward for speed tracking
        v = self.ego.get_velocity()
        speed = np.sqrt(v.x**2 + v.y**2)
        r_speed = -abs(speed - self.desired_speed)

        # reward for collision
        r_collision = 0
        if len(self.collision_hist) > 0:
            r_collision = -1

        # reward for steering:

        # reward for out of lane
        ego_x, ego_y = get_pos(self.ego)
        dis, w = get_lane_dis(self.waypoints, ego_x, ego_y)
        r_out = 0
        # if abs(dis) > self.out_lane_thres:
        #   r_out = -1

        # longitudinal speed
        lspeed = np.array([v.x, v.y])
        lspeed_lon = np.dot(lspeed, w)

        # cost for too fast
        r_fast = 0
        if lspeed_lon > self.desired_speed:
            r_fast = -1
        # cost for too slow

        # cost for too fast
        r_slow = 0
        if lspeed_lon < self.desired_speed:
            r_slow = -1
        # cost for lateral acceleration
        r_lat = -abs(self.ego.get_control().steer) * lspeed_lon**2

        # cost for idling
        r_idle = -1 * self.idle_timesteps

        # cost for too close following distance:
        r_space = 0
        if self.dist_to_lead > 0 and self.dist_to_lead <= 35.0:
            r_space = -(45.0 - self.dist_to_lead) * 15

        r = 200 * r_collision + 1 * lspeed_lon + 10 * r_fast + 5 * r_slow + r_idle + 12 * r_speed + r_space
        print(
            'reward [collision %.2f] [distance %.2f] [overspeed %.2f] [underspeed %.2f] [idle %f] [speed mismatch %.2f] [too close %.2f]'
            % (200 * r_collision, 1 * lspeed_lon, 10 * r_fast, 5 * r_slow,
               r_idle, 12 * r_speed, r_space))
        return r

    def _terminal(self):
        """Calculate whether to terminate the current episode."""
        # Get ego state
        ego_x, ego_y = get_pos(self.ego)

        # If collides
        if len(self.collision_hist) > 0:
            return True

        # If reach maximum timestep
        if self.time_step > self.max_time_episode:
            return True

        # If at destination
        if self.dests is not None:  # If at destination
            for dest in self.dests:
                if np.sqrt((ego_x - dest[0])**2 + (ego_y - dest[1])**2) < 4:
                    return True

        # If out of lane
        dis, _ = get_lane_dis(self.waypoints, ego_x, ego_y)
        if abs(dis) > self.out_lane_thres:
            return True

        # if stopped for a vehicle ahead
        # TODO
        if self.idle_timesteps > 500:
            print('terminal due to too many idle timesteps')
            return True
        return False

    def _clear_all_actors(self, actor_filters):
        """Clear specific actors."""
        for actor_filter in actor_filters:
            for actor in self.world.get_actors().filter(actor_filter):
                if actor.is_alive:
                    if actor.type_id == 'controller.ai.walker':
                        actor.stop()
                    actor.destroy()

    def _clear_all_sensors(self):
        try:
            self.collision_sensor.destroy()
            self.camera_sensor.destroy()
        except:
            pass