Source code for highway_env.envs.exit_env

from __future__ import annotations

import numpy as np

from highway_env import utils
from highway_env.envs.common.abstract import ConnectedLaneNeighboursMixin
from highway_env.envs.common.action import Action
from highway_env.envs.highway_env import HighwayEnv
from highway_env.road.lane import CircularLane
from highway_env.road.road import Road, RoadNetwork
from highway_env.vehicle.controller import ControlledVehicle
from highway_env.vehicle.kinematics import Vehicle


[docs] class ExitEnv(HighwayEnv): """ """
[docs] @classmethod def default_config(cls) -> dict: config = super().default_config() config.update( { "observation": { "type": "ExitObservation", "vehicles_count": 15, "features": ["presence", "x", "y", "vx", "vy", "cos_h", "sin_h"], "clip": False, }, "action": {"type": "DiscreteMetaAction", "target_speeds": [18, 24, 30]}, "lanes_count": 6, "collision_reward": 0, "high_speed_reward": 0.1, "right_lane_reward": 0, "normalize_reward": True, "goal_reward": 1, "vehicles_count": 20, "vehicles_density": 1.5, "controlled_vehicles": 1, "duration": 18, # [s], "simulation_frequency": 5, "scaling": 5, } ) return config
def _reset(self) -> None: self._create_road() self._create_vehicles()
[docs] def step(self, action) -> tuple[np.ndarray, float, bool, bool, dict]: obs, reward, terminated, truncated, info = super().step(action) info.update({"is_success": self._is_success()}) return obs, reward, terminated, truncated, info
def _create_road( self, road_length=1000, exit_position=400, exit_length=100 ) -> None: net = RoadNetwork.straight_road_network( self.config["lanes_count"], start=0, length=exit_position, nodes_str=("0", "1"), ) net = RoadNetwork.straight_road_network( self.config["lanes_count"] + 1, start=exit_position, length=exit_length, nodes_str=("1", "2"), net=net, ) net = RoadNetwork.straight_road_network( self.config["lanes_count"], start=exit_position + exit_length, length=road_length - exit_position - exit_length, nodes_str=("2", "3"), net=net, ) for _from in net.graph: for _to in net.graph[_from]: for _id in range(len(net.graph[_from][_to])): net.get_lane((_from, _to, _id)).speed_limit = 26 - 3.4 * _id exit_position = np.array( [ exit_position + exit_length, self.config["lanes_count"] * CircularLane.DEFAULT_WIDTH, ] ) radius = 150 exit_center = exit_position + np.array([0, radius]) lane = CircularLane( center=exit_center, radius=radius, start_phase=3 * np.pi / 2, end_phase=2 * np.pi, forbidden=True, ) net.add_lane("2", "exit", lane) self.road = Road( network=net, np_random=self.np_random, record_history=self.config["show_trajectories"], neighbour_vehicles_connected_lanes=self.config[ "neighbour_vehicles_connected_lanes" ], ) def _create_vehicles(self) -> None: """Create some new random vehicles of a given type, and add them on the road.""" self.controlled_vehicles = [] for _ in range(self.config["controlled_vehicles"]): vehicle = Vehicle.create_random( self.road, speed=25.0, lane_from="0", lane_to="1", lane_id=0, spacing=self.config["ego_spacing"], ) vehicle = self.action_type.vehicle_class( self.road, vehicle.position, vehicle.heading, vehicle.speed ) self.controlled_vehicles.append(vehicle) self.road.vehicles.append(vehicle) vehicles_type = utils.class_from_path(self.config["other_vehicles_type"]) for _ in range(self.config["vehicles_count"]): lanes = np.arange(self.config["lanes_count"]) lane_id = self.road.np_random.choice( lanes, size=1, p=lanes / lanes.sum() ).astype(int)[0] lane = self.road.network.get_lane(("0", "1", lane_id)) vehicle = vehicles_type.create_random( self.road, lane_from="0", lane_to="1", lane_id=lane_id, speed=lane.speed_limit, spacing=1 / self.config["vehicles_density"], ).plan_route_to("3") vehicle.enable_lane_change = False self.road.vehicles.append(vehicle) def _reward(self, action: Action) -> float: """ The reward is defined to foster driving at high speed, on the rightmost lanes, and to avoid collisions. :param action: the last action performed :return: the corresponding reward """ reward = sum( self.config.get(name, 0) * reward for name, reward in self._rewards(action).items() ) if self.config["normalize_reward"]: reward = utils.lmap( reward, [self.config["collision_reward"], self.config["goal_reward"]], [0, 1], ) reward = np.clip(reward, 0, 1) return reward def _rewards(self, action: Action) -> dict[str, float]: lane_index = ( self.vehicle.target_lane_index if isinstance(self.vehicle, ControlledVehicle) else self.vehicle.lane_index ) scaled_speed = utils.lmap( self.vehicle.speed, self.config["reward_speed_range"], [0, 1] ) return { "collision_reward": self.vehicle.crashed, "goal_reward": self._is_success(), "high_speed_reward": np.clip(scaled_speed, 0, 1), "right_lane_reward": lane_index[-1], } def _is_success(self): lane_index = ( self.vehicle.target_lane_index if isinstance(self.vehicle, ControlledVehicle) else self.vehicle.lane_index ) goal_reached = lane_index == ( "1", "2", self.config["lanes_count"], ) or lane_index == ("2", "exit", 0) return goal_reached def _is_terminated(self) -> bool: """The episode is over if the ego vehicle crashed.""" return self.vehicle.crashed def _is_truncated(self) -> bool: """The episode is truncated if the time limit is reached.""" return self.time >= self.config["duration"]
class ConnectedLaneExitEnv(ConnectedLaneNeighboursMixin, ExitEnv): pass # class DenseLidarExitEnv(DenseExitEnv): # @classmethod # def default_config(cls) -> dict: # return dict(super().default_config(), # observation=dict(type="LidarObservation"))