Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge custom output and failsafes to master #981

Merged
merged 88 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 85 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
b23d86c
merge custom output and failsafes to master
liljonnystyle Jun 19, 2020
1e6522c
add write_to_csv() function to master
liljonnystyle Jun 20, 2020
41a9aae
include pipeline README.md
liljonnystyle Jun 20, 2020
211c0e7
add data pipeline __init__
liljonnystyle Jun 20, 2020
ec854ad
add experiment.py changes
liljonnystyle Jun 20, 2020
44c15c9
Update simulate.py
liljonnystyle Jun 20, 2020
ee35aa2
Update simulate.py
liljonnystyle Jun 20, 2020
987cfb8
Update simulate.py
liljonnystyle Jun 20, 2020
df8f5a8
Update experiment.py
liljonnystyle Jun 20, 2020
b1c54e2
Update experiment.py
liljonnystyle Jun 21, 2020
1d2a6ff
fix flake8 issues
liljonnystyle Jun 21, 2020
7dcfcea
fix flake8 issues
liljonnystyle Jun 21, 2020
01994fc
fixed h-baselines bug
AboudyKreidieh Jun 21, 2020
cb37e44
Update experiment.py
liljonnystyle Jun 21, 2020
ea308d6
potential bug fix
AboudyKreidieh Jun 21, 2020
e50d254
merge custom output and failsafes to master
liljonnystyle Jun 19, 2020
df48a19
add write_to_csv() function to master
liljonnystyle Jun 20, 2020
df200e3
include pipeline README.md
liljonnystyle Jun 20, 2020
4f37627
add data pipeline __init__
liljonnystyle Jun 20, 2020
17d618a
add experiment.py changes
liljonnystyle Jun 20, 2020
f7a3c3b
Update simulate.py
liljonnystyle Jun 20, 2020
2f7942a
Update simulate.py
liljonnystyle Jun 20, 2020
880a6f0
Update simulate.py
liljonnystyle Jun 20, 2020
4691cd7
Update experiment.py
liljonnystyle Jun 20, 2020
88fa8ef
Update experiment.py
liljonnystyle Jun 21, 2020
907e75c
fix flake8 issues
liljonnystyle Jun 21, 2020
7897f93
fix flake8 issues
liljonnystyle Jun 21, 2020
6cecd19
Update experiment.py
liljonnystyle Jun 21, 2020
8902cdf
Merge branch 'jl-merge-getaccels' of https://github.com/flow-project/…
liljonnystyle Jun 21, 2020
2131df4
Merge branch 'master' of https://github.com/flow-project/flow into jl…
AboudyKreidieh Jun 22, 2020
af0b4f6
Replicated changes in 867. Done bug (#980)
kjang96 Jun 22, 2020
5edf782
address aboudy comments
liljonnystyle Jun 22, 2020
b704a70
revert change
liljonnystyle Jun 22, 2020
396796f
change warning print to ValueError message
liljonnystyle Jun 22, 2020
0627a12
update to new update_accel methods
liljonnystyle Jun 23, 2020
774e575
address brent comments
liljonnystyle Jun 23, 2020
16cba01
fix import typo
liljonnystyle Jun 23, 2020
8121120
address comments
liljonnystyle Jun 23, 2020
7cc347a
add display_warnings boolean
liljonnystyle Jun 23, 2020
3161580
add get_next_speed() function to base vehicle class
liljonnystyle Jun 23, 2020
2502bd1
revert addition of get_next_speed
liljonnystyle Jun 23, 2020
e7c16a1
merge custom output and failsafes to master
liljonnystyle Jun 19, 2020
e6a0953
add write_to_csv() function to master
liljonnystyle Jun 20, 2020
649fb8b
include pipeline README.md
liljonnystyle Jun 20, 2020
bee14b8
add data pipeline __init__
liljonnystyle Jun 20, 2020
998bdd2
add experiment.py changes
liljonnystyle Jun 20, 2020
cd44a71
Update simulate.py
liljonnystyle Jun 20, 2020
b7943de
Update simulate.py
liljonnystyle Jun 20, 2020
4d29048
Update simulate.py
liljonnystyle Jun 20, 2020
8d23f50
Update experiment.py
liljonnystyle Jun 20, 2020
6f56e3e
Update experiment.py
liljonnystyle Jun 21, 2020
9f8fc87
fix flake8 issues
liljonnystyle Jun 21, 2020
604b552
fix flake8 issues
liljonnystyle Jun 21, 2020
fcdbef3
Update experiment.py
liljonnystyle Jun 21, 2020
f7c1637
add experiment.py changes
liljonnystyle Jun 20, 2020
31ee51e
Update simulate.py
liljonnystyle Jun 20, 2020
e693ca7
Update simulate.py
liljonnystyle Jun 20, 2020
a4a0fb1
Update simulate.py
liljonnystyle Jun 20, 2020
0fc67bd
Update experiment.py
liljonnystyle Jun 20, 2020
62ca879
Update experiment.py
liljonnystyle Jun 21, 2020
c97e72e
fix flake8 issues
liljonnystyle Jun 21, 2020
eaec0c6
address aboudy comments
liljonnystyle Jun 22, 2020
af26b6f
revert change
liljonnystyle Jun 22, 2020
c921f19
change warning print to ValueError message
liljonnystyle Jun 22, 2020
5dca992
update to new update_accel methods
liljonnystyle Jun 23, 2020
cfd4393
address brent comments
liljonnystyle Jun 23, 2020
a1ce67f
fix import typo
liljonnystyle Jun 23, 2020
a2db16a
address comments
liljonnystyle Jun 23, 2020
77f6f13
add display_warnings boolean
liljonnystyle Jun 23, 2020
c89794d
add get_next_speed() function to base vehicle class
liljonnystyle Jun 23, 2020
3c301ea
revert addition of get_next_speed
liljonnystyle Jun 23, 2020
db1d4a1
Merge branch 'jl-merge-getaccels' of https://github.com/flow-project/…
liljonnystyle Jun 23, 2020
9f878b4
remove duped line from rebase
liljonnystyle Jun 23, 2020
bd5845e
Update base_controller.py
nathanlct Jun 23, 2020
99674e3
fix some bugs
liljonnystyle Jun 23, 2020
7374d22
Merge branch 'jl-merge-getaccels' of https://github.com/flow-project/…
liljonnystyle Jun 23, 2020
bdae632
revert change to get_feasible_action call signature
liljonnystyle Jun 23, 2020
708c6bb
change print syntax to be python3.5 compliant
liljonnystyle Jun 23, 2020
36c211a
add tests for new failsafe features
liljonnystyle Jun 24, 2020
9aaba62
fix failsafe unit tests
liljonnystyle Jun 24, 2020
f8a6256
fix failsafe unit tests
liljonnystyle Jun 24, 2020
79edd0a
fix unittest syntax
nathanlct Jun 24, 2020
baaec48
fix typo
nathanlct Jun 24, 2020
ea8d551
smooth default to True
liljonnystyle Jun 24, 2020
ee2150c
rearrange raise exception for test coverage
liljonnystyle Jun 24, 2020
674c494
some minor fixes
AboudyKreidieh Jul 7, 2020
8a70a83
cleanup
AboudyKreidieh Jul 7, 2020
fbdac90
moved simulation logging to the simulation kernel (#991)
AboudyKreidieh Jul 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 156 additions & 13 deletions flow/controllers/base_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ class BaseController(metaclass=ABCMeta):
specified to in this model are as desired.
delay : int
delay in applying the action (time)
fail_safe : str
Should be either "instantaneous" or "safe_velocity"
fail_safe : list of str or str
List of failsafes which can be "instantaneous", "safe_velocity",
"feasible_accel", or "obey_speed_limit". The order of applying the
falsafes will be based on the order in the list.
display_warnings : bool
Flag for toggling on/off printing failsafe warnings to screen.
noise : double
variance of the gaussian from which to sample a noisy acceleration
"""
Expand All @@ -45,6 +49,7 @@ def __init__(self,
car_following_params,
delay=0,
fail_safe=None,
display_warnings=True,
AboudyKreidieh marked this conversation as resolved.
Show resolved Hide resolved
noise=0):
"""Instantiate the base class for acceleration behavior."""
self.veh_id = veh_id
Expand All @@ -56,7 +61,29 @@ def __init__(self,
self.delay = delay

# longitudinal failsafe used by the vehicle
self.fail_safe = fail_safe
if isinstance(fail_safe, str):
failsafe_list = [fail_safe]
elif isinstance(fail_safe, list) or fail_safe is None:
failsafe_list = fail_safe
else:
failsafe_list = None
raise ValueError("fail_safe should be string or list of strings. Setting fail_safe to None\n")

failsafe_map = {
'instantaneous': self.get_safe_action_instantaneous,
'safe_velocity': self.get_safe_velocity_action,
'feasible_accel': lambda _, accel: self.get_feasible_action(accel),
'obey_speed_limit': self.get_obey_speed_limit_action
}
self.failsafes = []
if failsafe_list:
for check in failsafe_list:
if check in failsafe_map:
self.failsafes.append(failsafe_map.get(check))
else:
raise ValueError('Skipping {}, as it is not a valid failsafe.'.format(check))

self.display_warnings = display_warnings

self.max_accel = car_following_params.controller_params['accel']
# max deaccel should always be a positive
Expand All @@ -77,8 +104,8 @@ def get_action(self, env):
time step.

This method also augments the controller with the desired level of
stochastic noise, and utlizes the "instantaneous" or "safe_velocity"
failsafes if requested.
stochastic noise, and utlizes the "instantaneous", "safe_velocity",
"feasible_accel", and/or "obey_speed_limit" failsafes if requested.

Parameters
----------
Expand All @@ -90,6 +117,12 @@ def get_action(self, env):
float
the modified form of the acceleration
"""
# clear the current stored accels of this vehicle to None
env.k.vehicle.update_accel(self.veh_id, None, noise=False, failsafe=False)
env.k.vehicle.update_accel(self.veh_id, None, noise=False, failsafe=True)
env.k.vehicle.update_accel(self.veh_id, None, noise=True, failsafe=False)
env.k.vehicle.update_accel(self.veh_id, None, noise=True, failsafe=True)
nathanlct marked this conversation as resolved.
Show resolved Hide resolved

# this is to avoid abrupt decelerations when a vehicle has just entered
# a network and it's data is still not subscribed
if len(env.k.vehicle.get_edge(self.veh_id)) == 0:
Expand All @@ -107,16 +140,26 @@ def get_action(self, env):
if accel is None:
return None

# store the acceleration without noise to each vehicle
# run fail safe if requested
env.k.vehicle.update_accel(self.veh_id, accel, noise=False, failsafe=False)
accel_no_noise_with_failsafe = accel

for failsafe in self.failsafes:
accel_no_noise_with_failsafe = failsafe(env, accel_no_noise_with_failsafe)

env.k.vehicle.update_accel(self.veh_id, accel_no_noise_with_failsafe, noise=False, failsafe=True)

# add noise to the accelerations, if requested
if self.accel_noise > 0:
accel += np.random.normal(0, self.accel_noise)
accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise)
env.k.vehicle.update_accel(self.veh_id, accel, noise=True, failsafe=False)

# run the failsafes, if requested
if self.fail_safe == 'instantaneous':
accel = self.get_safe_action_instantaneous(env, accel)
elif self.fail_safe == 'safe_velocity':
accel = self.get_safe_velocity_action(env, accel)
# run the fail-safes, if requested
for failsafe in self.failsafes:
accel = failsafe(env, accel)

env.k.vehicle.update_accel(self.veh_id, accel, noise=True, failsafe=True)
return accel

def get_safe_action_instantaneous(self, env, action):
Expand Down Expand Up @@ -162,6 +205,13 @@ def get_safe_action_instantaneous(self, env, action):
# if the vehicle will crash into the vehicle ahead of it in the
# next time step (assuming the vehicle ahead of it is not
# moving), then stop immediately
if self.display_warnings:
print(
"=====================================\n"
"Vehicle {} is about to crash. Instantaneous acceleration "
"clipping applied.\n"
"=====================================".format(self.veh_id))

return -this_vel / sim_step
else:
# if the vehicle is not in danger of crashing, continue with
Expand Down Expand Up @@ -223,8 +273,8 @@ def safe_velocity(self, env):
Returns
-------
float
maximum safe velocity given a maximum deceleration and delay in
performing the breaking action
maximum safe velocity given a maximum deceleration, delay in
performing the breaking action, and speed limit
"""
lead_id = env.k.vehicle.get_leader(self.veh_id)
lead_vel = env.k.vehicle.get_speed(lead_id)
Expand All @@ -235,4 +285,97 @@ def safe_velocity(self, env):

v_safe = 2 * h / env.sim_step + dv - this_vel * (2 * self.delay)

# check for speed limit FIXME: this is not called
# this_edge = env.k.vehicle.get_edge(self.veh_id)
# edge_speed_limit = env.k.network.speed_limit(this_edge)

if this_vel > v_safe:
if self.display_warnings:
print(
"=====================================\n"
"Speed of vehicle {} is greater than safe speed. Safe velocity "
"clipping applied.\n"
"=====================================".format(self.veh_id))

return v_safe

def get_obey_speed_limit_action(self, env, action):
"""Perform the "obey_speed_limit" failsafe action.

Checks if the computed acceleration would put us above edge speed limit.
If it would, output the acceleration that would put at the speed limit
velocity.

Parameters
----------
env : flow.envs.Env
current environment, which contains information of the state of the
network at the current time step
action : float
requested acceleration action

Returns
-------
float
the requested action clipped by the speed limit
"""
# check for speed limit
this_edge = env.k.vehicle.get_edge(self.veh_id)
edge_speed_limit = env.k.network.speed_limit(this_edge)

this_vel = env.k.vehicle.get_speed(self.veh_id)
sim_step = env.sim_step

if this_vel + action * sim_step > edge_speed_limit:
AboudyKreidieh marked this conversation as resolved.
Show resolved Hide resolved
if edge_speed_limit > 0:
if self.display_warnings:
print(
"=====================================\n"
"Speed of vehicle {} is greater than speed limit. Obey "
"speed limit clipping applied.\n"
"=====================================".format(self.veh_id))
return (edge_speed_limit - this_vel) / sim_step
else:
return -this_vel / sim_step
AboudyKreidieh marked this conversation as resolved.
Show resolved Hide resolved
else:
return action

def get_feasible_action(self, action):
liljonnystyle marked this conversation as resolved.
Show resolved Hide resolved
"""Perform the "feasible_accel" failsafe action.

Checks if the computed acceleration would put us above maximum
acceleration or deceleration. If it would, output the acceleration
equal to maximum acceleration or deceleration.

Parameters
----------
action : float
requested acceleration action

Returns
-------
float
the requested action clipped by the feasible acceleration or
deceleration.
"""
if action > self.max_accel:
action = self.max_accel

if self.display_warnings:
print(
"=====================================\n"
"Acceleration of vehicle {} is greater than the max "
"acceleration. Feasible acceleration clipping applied.\n"
"=====================================".format(self.veh_id))

if action < -self.max_deaccel:
action = -self.max_deaccel

if self.display_warnings:
print(
"=====================================\n"
"Deceleration of vehicle {} is greater than the max "
"deceleration. Feasible acceleration clipping applied.\n"
"=====================================".format(self.veh_id))

return action
27 changes: 24 additions & 3 deletions flow/core/experiment.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Contains an experiment class for running simulations."""
from flow.core.util import emission_to_csv
from flow.utils.registry import make_create_env
import datetime
from flow.utils.data_pipeline import write_dict_to_csv, get_extra_info
from collections import defaultdict
from datetime import datetime
import logging
import time
import os
import numpy as np
import uuid


class Experiment:
Expand Down Expand Up @@ -81,7 +84,7 @@ def __init__(self, flow_params, custom_callables=None):
self.env = create_env()

logging.info(" Starting experiment {} at {}".format(
self.env.network.name, str(datetime.datetime.utcnow())))
self.env.network.name, str(datetime.utcnow())))

logging.info("Initializing environment.")

Expand Down Expand Up @@ -137,10 +140,20 @@ def rl_actions(*_):
t = time.time()
times = []

# data pipeline
extra_info = defaultdict(list)
source_id = 'flow_{}'.format(uuid.uuid4().hex)

if convert_to_csv and self.env.simulator == "traci":
dir_path = self.env.sim_params.emission_path
trajectory_table_path = os.path.join(dir_path, '{}.csv'.format(source_id))

for i in range(num_runs):
ret = 0
vel = []
custom_vals = {key: [] for key in self.custom_callables.keys()}
run_id = "run_{}".format(i)
self.env.pipeline_params = (extra_info, source_id, run_id)
state = self.env.reset()
for j in range(num_steps):
t0 = time.time()
Expand All @@ -153,6 +166,14 @@ def rl_actions(*_):
vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids)))
ret += reward

# collect additional information for the data pipeline
get_extra_info(self.env.k.vehicle, extra_info, veh_ids, source_id, run_id)
liljonnystyle marked this conversation as resolved.
Show resolved Hide resolved

# write to disk every 100 steps
if convert_to_csv and self.env.simulator == "traci" and j % 100 == 0:
write_dict_to_csv(trajectory_table_path, extra_info, not j and not i)
extra_info.clear()

# Compute the results for the custom callables.
for (key, lambda_func) in self.custom_callables.items():
custom_vals[key].append(lambda_func(self.env))
Expand Down Expand Up @@ -184,7 +205,6 @@ def rl_actions(*_):
time.sleep(0.1)

# collect the location of the emission file
dir_path = self.env.sim_params.emission_path
emission_filename = \
"{0}-emission.xml".format(self.env.network.name)
emission_path = os.path.join(dir_path, emission_filename)
Expand All @@ -194,5 +214,6 @@ def rl_actions(*_):

# Delete the .xml version of the emission file.
os.remove(emission_path)
write_dict_to_csv(trajectory_table_path, extra_info)

return info_dict
17 changes: 17 additions & 0 deletions flow/core/kernel/vehicle/aimsun.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def __init__(self,
# number of vehicles to exit the network for every time-step
self._num_arrived = []
self._arrived_ids = []
self._arrived_rl_ids = []

# contains conversion from Flow-ID to Aimsun-ID
self._id_aimsun2flow = {}
Expand Down Expand Up @@ -174,11 +175,17 @@ def update(self, reset):
added_vehicles = self.kernel_api.get_entered_ids()
exited_vehicles = self.kernel_api.get_exited_ids()

# keep track of arrived rl vehicles
arrived_rl_ids = []

# add the new vehicles if they should be tracked
for aimsun_id in added_vehicles:
veh_type = self.kernel_api.get_vehicle_type_name(aimsun_id)
if veh_type in self.tracked_vehicle_types:
self._add_departed(aimsun_id)
if aimsun_id in self.get_rl_ids():
arrived_rl_ids.append(aimsun_id)
self._arrived_rl_ids.append(arrived_rl_ids)

# remove the exited vehicles if they were tracked
if not reset:
Expand Down Expand Up @@ -639,6 +646,16 @@ def get_arrived_ids(self):
"""See parent class."""
raise NotImplementedError

def get_arrived_rl_ids(self, k=1):
"""See parent class."""
if len(self._arrived_rl_ids) > 0:
arrived = []
for arr in self._arrived_rl_ids[-k:]:
arrived.extend(arr)
return arrived
else:
return 0

def get_departed_ids(self):
"""See parent class."""
raise NotImplementedError
Expand Down
Loading