Skip to content

Commit

Permalink
Generic highway scenario (#667)
Browse files Browse the repository at this point in the history
* Default num_vehicles to 0 when adding vehicle type

* add color_vehicles param if we don't want auto vehicle coloration

* Add color_vehicles param in docstring

* More params and docstring for InFlows

* Consider new inflow params period and number when adding them in sumo

* Complete wrt changes and add examples to inflows tutorial

* Add images for new inflow tutorial

* Fix allowed value for 'begin' param

* Add precisions for kwargs

* Fix docstyle

* Make attribute SimParams.color_vehicles back-compatible for tests

* Fix test_environments that expects one vehicle of each type to be added by default

* Fix bug in case inflow generates vehicles into several routes

* Made new highway example with on and off ramps

* Add angle for on and off ramps

* Modify network parameters

* Fix docstring

* Fix docstring

* Fix pep8

* Fix pep8

* Fix bug of vehicles colliding

* test highway ramps

* Fix vehicles collision

* Add ramps angle in additional params

* updated python version

* updated rllib pkl data to match new python

* added channel to conda

* added different channels
  • Loading branch information
nathanlct authored and AboudyKreidieh committed Jul 30, 2019
1 parent fd0ad46 commit 0b8503f
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ before_install:
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- export TEST_FLAG="True"
- conda config --append channels https://repo.anaconda.com/pkgs/free
- conda config --append channels https://repo.anaconda.com/pkgs/pro

# Set up requirements for flow
- conda env create -f environment.yml
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: flow

dependencies:
- python==3.5.2
- python==3.6.8
- numpy==1.14.0
- scipy==1.1.0
- lxml==4.2.4
Expand Down
119 changes: 119 additions & 0 deletions examples/sumo/highway_ramps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Example of a highway section network with on/off ramps."""

from flow.core.params import SumoParams, EnvParams, \
NetParams, InitialConfig, InFlows, SumoCarFollowingParams, \
SumoLaneChangeParams
from flow.core.params import VehicleParams
from flow.core.experiment import Experiment
from flow.scenarios.highway_ramps import HighwayRampsScenario, \
ADDITIONAL_NET_PARAMS
from flow.controllers import IDMController
from flow.envs.loop.loop_accel import AccelEnv, ADDITIONAL_ENV_PARAMS


additional_net_params = ADDITIONAL_NET_PARAMS.copy()

# lengths
additional_net_params["highway_length"] = 600
additional_net_params["on_ramps_length"] = 65
additional_net_params["off_ramps_length"] = 65

# number of lanes
additional_net_params["highway_lanes"] = 4
additional_net_params["on_ramps_lanes"] = 1
additional_net_params["off_ramps_lanes"] = 1

# speed limits
additional_net_params["highway_speed"] = 15
additional_net_params["on_ramps_speed"] = 10
additional_net_params["off_ramps_speed"] = 10

# ramps
additional_net_params["on_ramps_pos"] = [200, 400]
additional_net_params["off_ramps_pos"] = [300, 500]

# probability of exiting at the next off-ramp
additional_net_params["next_off_ramp_proba"] = 0.05

# inflow rates in vehs/hour
HIGHWAY_INFLOW_RATE = 1000
ON_RAMPS_INFLOW_RATE = 200


def highway_ramps_example(render=None):
"""
Perform a simulation of vehicles on a highway section with ramps.
Parameters
----------
render: bool, optional
Specifies whether or not to use the GUI during the simulation.
Returns
-------
exp: flow.core.experiment.Experiment
A non-RL experiment demonstrating the performance of human-driven
vehicles on a highway section with on and off ramps.
"""
sim_params = SumoParams(
render=True,
emission_path="./data/",
sim_step=0.2,
restart_instance=True)

if render is not None:
sim_params.render = render

vehicles = VehicleParams()
vehicles.add(
veh_id="human",
acceleration_controller=(IDMController, {
"noise": 0.2
}),
car_following_params=SumoCarFollowingParams(
speed_mode="obey_safe_speed",
),
lane_change_params=SumoLaneChangeParams(lane_change_mode=1621))

env_params = EnvParams(
additional_params=ADDITIONAL_ENV_PARAMS,
sims_per_step=5,
warmup_steps=0)

inflows = InFlows()
inflows.add(
veh_type="human",
edge="highway_0",
vehs_per_hour=HIGHWAY_INFLOW_RATE,
depart_lane="free",
depart_speed="max",
name="highway_flow")
for i in range(len(additional_net_params["on_ramps_pos"])):
inflows.add(
veh_type="human",
edge="on_ramp_{}".format(i),
vehs_per_hour=ON_RAMPS_INFLOW_RATE,
depart_lane="first",
depart_speed="max",
name="on_ramp_flow")

net_params = NetParams(
inflows=inflows,
additional_params=additional_net_params)

initial_config = InitialConfig(spacing="uniform", perturbation=5.0)

scenario = HighwayRampsScenario(
name="highway-ramp",
vehicles=vehicles,
net_params=net_params,
initial_config=initial_config)

env = AccelEnv(env_params, sim_params, scenario)

return Experiment(env)


if __name__ == "__main__":
exp = highway_ramps_example()
exp.run(1, 3600, convert_to_csv=True)
238 changes: 238 additions & 0 deletions flow/scenarios/highway_ramps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
"""Contains the highway with ramps scenario class."""

from flow.scenarios.base_scenario import Scenario
from flow.core.params import InitialConfig, TrafficLightParams
from collections import defaultdict
from numpy import pi, sin, cos


ADDITIONAL_NET_PARAMS = {
# lengths of highway, on-ramps and off-ramps respectively
"highway_length": 300,
"on_ramps_length": 100,
"off_ramps_length": 100,
# number of lanes on highway, on-ramps and off-ramps respectively
"highway_lanes": 1,
"on_ramps_lanes": 1,
"off_ramps_lanes": 1,
# speed limit on highway, on-ramps and off-ramps respectively
"highway_speed": 10,
"on_ramps_speed": 10,
"off_ramps_speed": 10,
# positions of the on-ramps
"on_ramps_pos": [],
# positions of the off-ramps
"off_ramps_pos": [],
# probability for a vehicle to exit the highway at the next off-ramp
"next_off_ramp_proba": 0.2,
# ramps angles
"angle_on_ramps": - 3 * pi / 4,
"angle_off_ramps": - pi / 4
}


class HighwayRampsScenario(Scenario):
"""Scenario class for a highway section with on and off ramps.
This scenario consists of a single or multi-lane highway network with a
variable number of on-ramps and off-ramps at arbitrary positions,
with arbitrary numbers of lanes. It can be used to generate periodic
perturbation on a more realistic highway.
Parameters in net_params:
* **highway_length** : total length of the highway
* **on_ramps_length** : length of each on-ramp
* **off_ramps_length** : length of each off-ramp
* **highway_lanes** : number of lanes on the highway
* **on_ramps_lanes** : number of lanes on each on-ramp
* **off_ramps_lanes** : number of lanes on each off-ramp
* **highway_speed** : speed limit on the highway
* **on_ramps_speed** : speed limit on each on-ramp
* **off_ramps_speed** : speed limit on each off-ramp
* **on_ramps_pos** : positions of the in-ramps on the highway (int list)
* **off_ramps_pos** : positions of the off-ramps on the highway (int list)
* **next_off_ramp_proba** : probability for a vehicle to exit the highway
at the next off-ramp
"""

def __init__(self,
name,
vehicles,
net_params,
initial_config=InitialConfig(),
traffic_lights=TrafficLightParams()):
"""Initialize a highway with on and off ramps scenario."""
for p in ADDITIONAL_NET_PARAMS.keys():
if p not in net_params.additional_params:
raise KeyError('Network parameter "{}" not supplied'.format(p))

# load parameters into class
params = net_params.additional_params

self.highway_length = params['highway_length']
self.on_ramps_length = params['on_ramps_length']
self.off_ramps_length = params['off_ramps_length']

self.highway_lanes = params['highway_lanes']
self.on_ramps_lanes = params['on_ramps_lanes']
self.off_ramps_lanes = params['off_ramps_lanes']

self.highway_speed = params['highway_speed']
self.on_ramps_speed = params['on_ramps_speed']
self.off_ramps_speed = params['off_ramps_speed']

self.on_ramps_pos = params['on_ramps_pos']
self.off_ramps_pos = params['off_ramps_pos']

self.p = params['next_off_ramp_proba']

self.angle_on_ramps = params['angle_on_ramps']
self.angle_off_ramps = params['angle_off_ramps']

# generate position of all network nodes
self.ramps_pos = sorted(self.on_ramps_pos + self.off_ramps_pos)
self.nodes_pos = sorted(list(set([0] + self.ramps_pos +
[self.highway_length])))

# highway_pos[x] = id of the highway node whose starting position is x
self.highway_pos = {x: i for i, x in enumerate(self.nodes_pos)}
# ramp_pos[x] = id of the ramp node whose intersection with the highway
# is at position x
self.ramp_pos = {x: "on_ramp_{}".format(i)
for i, x in enumerate(self.on_ramps_pos)}
self.ramp_pos.update({x: "off_ramp_{}".format(i)
for i, x in enumerate(self.off_ramps_pos)})

# make sure scenario is constructible
if (len(self.ramps_pos) > 0 and
(min(self.ramps_pos) <= 0 or
max(self.ramps_pos) >= self.highway_length)):
raise ValueError('All ramps positions should be positive and less '
'than highway length. Current ramps positions: {}'
'. Current highway length: {}.'.format(
self.ramps_pos, self.highway_length))
if len(self.ramps_pos) != len(list(set(self.ramps_pos))):
raise ValueError('Two ramps positions cannot be equal.')

super().__init__(name, vehicles, net_params, initial_config,
traffic_lights)

def specify_nodes(self, net_params):
"""See parent class."""
nodes_highway = [{
"id": "highway_{}".format(i),
"x": self.nodes_pos[i],
"y": 0,
"radius": 10
} for i in range(len(self.nodes_pos))]

nodes_on_ramps = [{
"id": "on_ramp_{}".format(i),
"x": x + self.on_ramps_length * cos(self.angle_on_ramps),
"y": self.on_ramps_length * sin(self.angle_on_ramps)
} for i, x in enumerate(self.on_ramps_pos)]

nodes_off_ramps = [{
"id": "off_ramp_{}".format(i),
"x": x + self.off_ramps_length * cos(self.angle_off_ramps),
"y": self.off_ramps_length * sin(self.angle_off_ramps)
} for i, x in enumerate(self.off_ramps_pos)]

return nodes_highway + nodes_on_ramps + nodes_off_ramps

def specify_edges(self, net_params):
"""See parent class."""
highway_edges = [{
"id": "highway_{}".format(i),
"type": "highway",
"from": "highway_{}".format(i),
"to": "highway_{}".format(i + 1),
"length": self.nodes_pos[i + 1] - self.nodes_pos[i]
} for i in range(len(self.nodes_pos) - 1)]

on_ramps_edges = [{
"id": "on_ramp_{}".format(i),
"type": "on_ramp",
"from": "on_ramp_{}".format(i),
"to": "highway_{}".format(self.highway_pos[x]),
"length": self.on_ramps_length
} for i, x in enumerate(self.on_ramps_pos)]

off_ramps_edges = [{
"id": "off_ramp_{}".format(i),
"type": "off_ramp",
"from": "highway_{}".format(self.highway_pos[x]),
"to": "off_ramp_{}".format(i),
"length": self.off_ramps_length
} for i, x in enumerate(self.off_ramps_pos)]

return highway_edges + on_ramps_edges + off_ramps_edges

def specify_routes(self, net_params):
"""See parent class."""
def get_routes(start_node_pos):
"""Compute the routes recursively."""
if start_node_pos not in self.nodes_pos:
raise ValueError('{} is not a node position.'.format(
start_node_pos))

id_highway_node = self.highway_pos[start_node_pos]

if id_highway_node + 1 >= len(self.nodes_pos):
return [(["highway_{}".format(id_highway_node - 1)], 1)]

id_ramp_node = self.ramp_pos[start_node_pos]

routes = get_routes(self.nodes_pos[id_highway_node + 1])

if id_ramp_node.startswith("on"):
return ([
(["highway_{}".format(id_highway_node - 1)] + route, prob)
for route, prob in routes if not route[0].startswith("on")
] + [
([id_ramp_node] + route, prob)
for route, prob in routes if not route[0].startswith("on")
] + [
(route, prob)
for route, prob in routes if route[0].startswith("on")
])
else:
return ([
(["highway_{}".format(id_highway_node - 1)] + route,
(1 - self.p) * prob)
for route, prob in routes if not route[0].startswith("on")
] + [
(["highway_{}".format(id_highway_node - 1), id_ramp_node],
self.p * prob)
for route, prob in routes if not route[0].startswith("on")
] + [
(route, prob)
for route, prob in routes if route[0].startswith("on")
])

routes = get_routes(self.nodes_pos[1])

rts = defaultdict(list)
for route, prob in routes:
rts[route[0]].append((route, prob))

return rts

def specify_types(self, net_params):
"""See parent class."""
types = [{
"id": "highway",
"numLanes": self.highway_lanes,
"speed": self.highway_speed
}, {
"id": "on_ramp",
"numLanes": self.on_ramps_lanes,
"speed": self.on_ramps_speed
}, {
"id": "off_ramp",
"numLanes": self.off_ramps_lanes,
"speed": self.off_ramps_speed
}]

return types
Binary file modified tests/data/rllib_data/multi_agent/checkpoint_1/checkpoint-1
Binary file not shown.
Binary file not shown.

0 comments on commit 0b8503f

Please sign in to comment.