Source code for highway_env.envs.racetrack_env

from itertools import repeat, product
from typing import Tuple, Dict, Text

import numpy as np

from highway_env import utils
from highway_env.envs.common.abstract import AbstractEnv
from highway_env.road.lane import LineType, StraightLane, CircularLane, SineLane
from highway_env.road.road import Road, RoadNetwork
from highway_env.vehicle.behavior import IDMVehicle


[docs]class RacetrackEnv(AbstractEnv): """ A continuous control environment. The agent needs to learn two skills: - follow the tracks - avoid collisions with other vehicles Credits and many thanks to @supperted825 for the idea and initial implementation. See https://github.com/eleurent/highway-env/issues/231 """
[docs] @classmethod def default_config(cls) -> dict: config = super().default_config() config.update({ "observation": { "type": "OccupancyGrid", "features": ['presence', 'on_road'], "grid_size": [[-18, 18], [-18, 18]], "grid_step": [3, 3], "as_image": False, "align_to_vehicle_axes": True }, "action": { "type": "ContinuousAction", "longitudinal": False, "lateral": True, "target_speeds": [0, 5, 10] }, "simulation_frequency": 15, "policy_frequency": 5, "duration": 300, "collision_reward": -1, "lane_centering_cost": 4, "lane_centering_reward": 1, "action_reward": -0.3, "controlled_vehicles": 1, "other_vehicles": 1, "screen_width": 600, "screen_height": 600, "centering_position": [0.5, 0.5], }) return config
def _reward(self, action: np.ndarray) -> float: rewards = self._rewards(action) reward = sum(self.config.get(name, 0) * reward for name, reward in rewards.items()) reward = utils.lmap(reward, [self.config["collision_reward"], 1], [0, 1]) reward *= rewards["on_road_reward"] return reward def _rewards(self, action: np.ndarray) -> Dict[Text, float]: _, lateral = self.vehicle.lane.local_coordinates(self.vehicle.position) return { "lane_centering_reward": 1/(1+self.config["lane_centering_cost"]*lateral**2), "action_reward": np.linalg.norm(action), "collision_reward": self.vehicle.crashed, "on_road_reward": self.vehicle.on_road, } def _is_terminated(self) -> bool: return self.vehicle.crashed def _is_truncated(self) -> bool: return self.time >= self.config["duration"] def _reset(self) -> None: self._make_road() self._make_vehicles() def _make_road(self) -> None: net = RoadNetwork() # Set Speed Limits for Road Sections - Straight, Turn20, Straight, Turn 15, Turn15, Straight, Turn25x2, Turn18 speedlimits = [None, 10, 10, 10, 10, 10, 10, 10, 10] # Initialise First Lane lane = StraightLane([42, 0], [100, 0], line_types=(LineType.CONTINUOUS, LineType.STRIPED), width=5, speed_limit=speedlimits[1]) self.lane = lane # Add Lanes to Road Network - Straight Section net.add_lane("a", "b", lane) net.add_lane("a", "b", StraightLane([42, 5], [100, 5], line_types=(LineType.STRIPED, LineType.CONTINUOUS), width=5, speed_limit=speedlimits[1])) # 2 - Circular Arc #1 center1 = [100, -20] radii1 = 20 net.add_lane("b", "c", CircularLane(center1, radii1, np.deg2rad(90), np.deg2rad(-1), width=5, clockwise=False, line_types=(LineType.CONTINUOUS, LineType.NONE), speed_limit=speedlimits[2])) net.add_lane("b", "c", CircularLane(center1, radii1+5, np.deg2rad(90), np.deg2rad(-1), width=5, clockwise=False, line_types=(LineType.STRIPED, LineType.CONTINUOUS), speed_limit=speedlimits[2])) # 3 - Vertical Straight net.add_lane("c", "d", StraightLane([120, -20], [120, -30], line_types=(LineType.CONTINUOUS, LineType.NONE), width=5, speed_limit=speedlimits[3])) net.add_lane("c", "d", StraightLane([125, -20], [125, -30], line_types=(LineType.STRIPED, LineType.CONTINUOUS), width=5, speed_limit=speedlimits[3])) # 4 - Circular Arc #2 center2 = [105, -30] radii2 = 15 net.add_lane("d", "e", CircularLane(center2, radii2, np.deg2rad(0), np.deg2rad(-181), width=5, clockwise=False, line_types=(LineType.CONTINUOUS, LineType.NONE), speed_limit=speedlimits[4])) net.add_lane("d", "e", CircularLane(center2, radii2+5, np.deg2rad(0), np.deg2rad(-181), width=5, clockwise=False, line_types=(LineType.STRIPED, LineType.CONTINUOUS), speed_limit=speedlimits[4])) # 5 - Circular Arc #3 center3 = [70, -30] radii3 = 15 net.add_lane("e", "f", CircularLane(center3, radii3+5, np.deg2rad(0), np.deg2rad(136), width=5, clockwise=True, line_types=(LineType.CONTINUOUS, LineType.STRIPED), speed_limit=speedlimits[5])) net.add_lane("e", "f", CircularLane(center3, radii3, np.deg2rad(0), np.deg2rad(137), width=5, clockwise=True, line_types=(LineType.NONE, LineType.CONTINUOUS), speed_limit=speedlimits[5])) # 6 - Slant net.add_lane("f", "g", StraightLane([55.7, -15.7], [35.7, -35.7], line_types=(LineType.CONTINUOUS, LineType.NONE), width=5, speed_limit=speedlimits[6])) net.add_lane("f", "g", StraightLane([59.3934, -19.2], [39.3934, -39.2], line_types=(LineType.STRIPED, LineType.CONTINUOUS), width=5, speed_limit=speedlimits[6])) # 7 - Circular Arc #4 - Bugs out when arc is too large, hence written in 2 sections center4 = [18.1, -18.1] radii4 = 25 net.add_lane("g", "h", CircularLane(center4, radii4, np.deg2rad(315), np.deg2rad(170), width=5, clockwise=False, line_types=(LineType.CONTINUOUS, LineType.NONE), speed_limit=speedlimits[7])) net.add_lane("g", "h", CircularLane(center4, radii4+5, np.deg2rad(315), np.deg2rad(165), width=5, clockwise=False, line_types=(LineType.STRIPED, LineType.CONTINUOUS), speed_limit=speedlimits[7])) net.add_lane("h", "i", CircularLane(center4, radii4, np.deg2rad(170), np.deg2rad(56), width=5, clockwise=False, line_types=(LineType.CONTINUOUS, LineType.NONE), speed_limit=speedlimits[7])) net.add_lane("h", "i", CircularLane(center4, radii4+5, np.deg2rad(170), np.deg2rad(58), width=5, clockwise=False, line_types=(LineType.STRIPED, LineType.CONTINUOUS), speed_limit=speedlimits[7])) # 8 - Circular Arc #5 - Reconnects to Start center5 = [43.2, 23.4] radii5 = 18.5 net.add_lane("i", "a", CircularLane(center5, radii5+5, np.deg2rad(240), np.deg2rad(270), width=5, clockwise=True, line_types=(LineType.CONTINUOUS, LineType.STRIPED), speed_limit=speedlimits[8])) net.add_lane("i", "a", CircularLane(center5, radii5, np.deg2rad(238), np.deg2rad(268), width=5, clockwise=True, line_types=(LineType.NONE, LineType.CONTINUOUS), speed_limit=speedlimits[8])) road = Road(network=net, np_random=self.np_random, record_history=self.config["show_trajectories"]) self.road = road def _make_vehicles(self) -> None: """ Populate a road with several vehicles on the highway and on the merging lane, as well as an ego-vehicle. """ rng = self.np_random # Controlled vehicles self.controlled_vehicles = [] for i in range(self.config["controlled_vehicles"]): lane_index = ("a", "b", rng.integers(2)) if i == 0 else \ self.road.network.random_lane_index(rng) controlled_vehicle = self.action_type.vehicle_class.make_on_lane(self.road, lane_index, speed=None, longitudinal=rng.uniform(20, 50)) self.controlled_vehicles.append(controlled_vehicle) self.road.vehicles.append(controlled_vehicle) # Front vehicle vehicle = IDMVehicle.make_on_lane(self.road, ("b", "c", lane_index[-1]), longitudinal=rng.uniform( low=0, high=self.road.network.get_lane(("b", "c", 0)).length ), speed=6+rng.uniform(high=3)) self.road.vehicles.append(vehicle) # Other vehicles for i in range(rng.integers(self.config["other_vehicles"])): random_lane_index = self.road.network.random_lane_index(rng) vehicle = IDMVehicle.make_on_lane(self.road, random_lane_index, longitudinal=rng.uniform( low=0, high=self.road.network.get_lane(random_lane_index).length ), speed=6+rng.uniform(high=3)) # Prevent early collisions for v in self.road.vehicles: if np.linalg.norm(vehicle.position - v.position) < 20: break else: self.road.vehicles.append(vehicle)