From e60d82ac00cb75ae5d799d1e3984137970cc4d15 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Fri, 17 Apr 2020 16:21:34 -0700 Subject: [PATCH 1/9] modified the edgestarts attribute --- flow/networks/highway.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/flow/networks/highway.py b/flow/networks/highway.py index c63292067..286022ec8 100644 --- a/flow/networks/highway.py +++ b/flow/networks/highway.py @@ -128,9 +128,29 @@ def specify_routes(self, net_params): def specify_edge_starts(self): """See parent class.""" - edgestarts = [("highway_{}".format(i), 0) - for i in range(self.num_edges)] - return edgestarts + junction_length = 0.1 + + # Add the main edges. + edge_starts = [ + ("highway_{}".format(i), + i * (self.length / self.num_edges + junction_length)) + for i in range(self.num_edges) + ] + + return edge_starts + + def specify_internal_edge_starts(self): + """See parent class.""" + junction_length = 0.1 + + # Add the junctions. + edge_starts = [ + (":edge_{}".format(i + 1), + (i + 1) * self.length / self.num_edges + i * junction_length) + for i in range(self.num_edges - 1) + ] + + return edge_starts @staticmethod def gen_custom_start_pos(cls, net_params, initial_config, num_vehicles): From 04c8aad824042fac51a736bc82350a07bc7e2487 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Fri, 17 Apr 2020 16:22:23 -0700 Subject: [PATCH 2/9] added ignore_noise option --- flow/controllers/base_controller.py | 27 +++++++- flow/controllers/car_following_models.py | 86 ++++++++++++++++++++---- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 41780826b..4b7396394 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -37,6 +37,12 @@ class BaseController: Should be either "instantaneous" or "safe_velocity" noise : double variance of the gaussian from which to sample a noisy acceleration + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. """ def __init__(self, @@ -44,9 +50,11 @@ def __init__(self, car_following_params, delay=0, fail_safe=None, - noise=0): + noise=0, + ignore_noise=None): """Instantiate the base class for acceleration behavior.""" self.veh_id = veh_id + self.ignore_noise = ignore_noise or [] # magnitude of gaussian noise self.accel_noise = noise @@ -107,7 +115,22 @@ def get_action(self, env): # add noise to the accelerations, if requested if self.accel_noise > 0: - accel += np.random.normal(0, self.accel_noise) + if self.ignore_noise is None: + # Add noise to the vehicle for all positions in this case. + accel += np.random.normal(0, self.accel_noise) + else: + pos = env.k.vehicle.get_x_by_id(self.veh_id) + + # Check whether to apply the acceleration. If you are within + # one of the ignore_pos positions, noise is not applied to the + # accelerations. + apply_noise = True + for (min_pos, max_pos) in self.ignore_noise: + if min_pos <= pos < max_pos: + apply_noise = False + + if apply_noise: + accel += np.random.normal(0, self.accel_noise) # run the failsafes, if requested if self.fail_safe == 'instantaneous': diff --git a/flow/controllers/car_following_models.py b/flow/controllers/car_following_models.py index f86c546e8..1ee1d8886 100755 --- a/flow/controllers/car_following_models.py +++ b/flow/controllers/car_following_models.py @@ -41,6 +41,12 @@ class CFMController(BaseController): time delay (default: 0.0) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -56,6 +62,7 @@ def __init__(self, v_des=8, time_delay=0.0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate a CFM controller.""" BaseController.__init__( @@ -64,9 +71,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) + noise=noise, + ignore_noise=ignore_noise + ) - self.veh_id = veh_id self.k_d = k_d self.k_v = k_v self.k_c = k_c @@ -117,6 +125,12 @@ class BCMController(BaseController): time delay (default: 0.5) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -132,6 +146,7 @@ def __init__(self, v_des=8, time_delay=0.0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate a Bilateral car-following model controller.""" BaseController.__init__( @@ -140,9 +155,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) + noise=noise, + ignore_noise=ignore_noise + ) - self.veh_id = veh_id self.k_d = k_d self.k_v = k_v self.k_c = k_c @@ -197,6 +213,12 @@ class LACController(BaseController): time delay (default: 0.5) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -212,6 +234,7 @@ def __init__(self, a=0, time_delay=0.0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate a Linear Adaptive Cruise controller.""" BaseController.__init__( @@ -220,9 +243,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) + noise=noise, + ignore_noise=ignore_noise + ) - self.veh_id = veh_id self.k_1 = k_1 self.k_2 = k_2 self.h = h @@ -274,6 +298,12 @@ class OVMController(BaseController): time delay (default: 0.5) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -289,6 +319,7 @@ def __init__(self, v_max=30, time_delay=0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate an Optimal Vehicle Model controller.""" BaseController.__init__( @@ -297,8 +328,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) - self.veh_id = veh_id + noise=noise, + ignore_noise=ignore_noise + ) + self.v_max = v_max self.alpha = alpha self.beta = beta @@ -351,6 +384,12 @@ class LinearOVM(BaseController): time delay (default: 0.5) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -364,6 +403,7 @@ def __init__(self, h_st=5, time_delay=0.0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate a Linear OVM controller.""" BaseController.__init__( @@ -372,8 +412,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) - self.veh_id = veh_id + noise=noise, + ignore_noise=ignore_noise + ) + # 4.8*1.85 for case I, 3.8*1.85 for case II, per Nakayama self.v_max = v_max # TAU in Traffic Flow Dynamics textbook @@ -429,6 +471,12 @@ class IDMController(BaseController): linear jam distance, in m (default: 2) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -444,6 +492,7 @@ def __init__(self, s0=2, time_delay=0.0, noise=0, + ignore_noise=None, fail_safe=None, car_following_params=None): """Instantiate an IDM controller.""" @@ -453,7 +502,10 @@ def __init__(self, car_following_params, delay=time_delay, fail_safe=fail_safe, - noise=noise) + noise=noise, + ignore_noise=ignore_noise + ) + self.v0 = v0 self.T = T self.a = a @@ -530,6 +582,12 @@ class GippsController(BaseController): reaction time in s (default: 1) noise : float std dev of normal perturbation to the acceleration (default: 0) + ignore_noise : list of (float, float) + a list of (min_pos, max_pos) positions where noise should not be + applied to the accelerations. For example, if you would not like to + apply acceleration noise within the positions (0, 100) and (200, 300), + then this term is written as: [(0, 100), (200, 300)]. If set to None, + noise is applied to the accelerations everywhere. fail_safe : str type of flow-imposed failsafe the vehicle should posses, defaults to no failsafe (None) @@ -546,6 +604,7 @@ def __init__(self, tau=1, delay=0, noise=0, + ignore_noise=None, fail_safe=None): """Instantiate a Gipps' controller.""" BaseController.__init__( @@ -554,8 +613,9 @@ def __init__(self, car_following_params, delay=delay, fail_safe=fail_safe, - noise=noise - ) + noise=noise, + ignore_noise=ignore_noise + ) self.v_desired = v0 self.acc = acc From cae0270fc6f0e103d1613842a55a56f8366db823 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Sun, 19 Apr 2020 19:44:08 -0700 Subject: [PATCH 3/9] added new inflow to vehicle class --- flow/core/kernel/kernel.py | 4 +- flow/core/kernel/network/traci.py | 66 ++++++++++++++-------------- flow/core/kernel/simulation/traci.py | 3 +- flow/core/kernel/vehicle/base.py | 4 +- flow/core/kernel/vehicle/traci.py | 60 +++++++++++++++++++++++-- flow/envs/base.py | 3 +- 6 files changed, 98 insertions(+), 42 deletions(-) diff --git a/flow/core/kernel/kernel.py b/flow/core/kernel/kernel.py index abf7494b9..336c7cfa5 100644 --- a/flow/core/kernel/kernel.py +++ b/flow/core/kernel/kernel.py @@ -45,7 +45,7 @@ class Kernel(object): traffic simulators, e.g. SUMO, AIMSUN, TruckSim, etc... """ - def __init__(self, simulator, sim_params): + def __init__(self, simulator, sim_params, net_params): """Instantiate a Flow kernel object. Parameters @@ -65,7 +65,7 @@ def __init__(self, simulator, sim_params): if simulator == "traci": self.simulation = TraCISimulation(self) self.network = TraCIKernelNetwork(self, sim_params) - self.vehicle = TraCIVehicle(self, sim_params) + self.vehicle = TraCIVehicle(self, sim_params, net_params) self.traffic_light = TraCITrafficLight(self) elif simulator == 'aimsun': self.simulation = AimsunKernelSimulation(self) diff --git a/flow/core/kernel/network/traci.py b/flow/core/kernel/network/traci.py index c9ac80772..2ff3023b8 100644 --- a/flow/core/kernel/network/traci.py +++ b/flow/core/kernel/network/traci.py @@ -758,39 +758,39 @@ def generate_cfg(self, net_params, traffic_lights, routes): edges=' '.join(r) )) - # add the inflows from various edges to the xml file - if self.network.net_params.inflows is not None: - total_inflows = self.network.net_params.inflows.get() - for inflow in total_inflows: - # do not want to affect the original values - sumo_inflow = deepcopy(inflow) - - # convert any non-string element in the inflow dict to a string - for key in sumo_inflow: - if not isinstance(sumo_inflow[key], str): - sumo_inflow[key] = repr(sumo_inflow[key]) - - edge = sumo_inflow['edge'] - del sumo_inflow['edge'] - - if 'route' not in sumo_inflow: - # distribute the inflow rates across all routes from a - # given edge w.r.t. the provided fractions for each route - for i, (_, ft) in enumerate(routes[edge]): - sumo_inflow['name'] += str(i) - sumo_inflow['route'] = 'route{}_{}'.format(edge, i) - - for key in ['vehsPerHour', 'probability', 'period']: - if key in sumo_inflow: - sumo_inflow[key] = str(float(inflow[key]) * ft) - - if 'number' in sumo_inflow: - sumo_inflow['number'] = str( - int(float(inflow['number']) * ft)) - - routes_data.append(_flow(**sumo_inflow)) - else: - routes_data.append(_flow(**sumo_inflow)) + # # add the inflows from various edges to the xml file + # if self.network.net_params.inflows is not None: + # total_inflows = self.network.net_params.inflows.get() + # for inflow in total_inflows: + # # do not want to affect the original values + # sumo_inflow = deepcopy(inflow) + # + # # convert any non-string element in the inflow dict to a string + # for key in sumo_inflow: + # if not isinstance(sumo_inflow[key], str): + # sumo_inflow[key] = repr(sumo_inflow[key]) + # + # edge = sumo_inflow['edge'] + # del sumo_inflow['edge'] + # + # if 'route' not in sumo_inflow: + # # distribute the inflow rates across all routes from a + # # given edge w.r.t. the provided fractions for each route + # for i, (_, ft) in enumerate(routes[edge]): + # sumo_inflow['name'] += str(i) + # sumo_inflow['route'] = 'route{}_{}'.format(edge, i) + # + # for key in ['vehsPerHour', 'probability', 'period']: + # if key in sumo_inflow: + # sumo_inflow[key] = str(float(inflow[key]) * ft) + # + # if 'number' in sumo_inflow: + # sumo_inflow['number'] = str( + # int(float(inflow['number']) * ft)) + # + # routes_data.append(_flow(**sumo_inflow)) + # else: + # routes_data.append(_flow(**sumo_inflow)) printxml(routes_data, self.cfg_path + self.roufn) diff --git a/flow/core/kernel/simulation/traci.py b/flow/core/kernel/simulation/traci.py index 0ee29ada6..f71900d98 100644 --- a/flow/core/kernel/simulation/traci.py +++ b/flow/core/kernel/simulation/traci.py @@ -88,7 +88,8 @@ def start_simulation(self, network, sim_params): sumo_binary, "-c", network.cfg, "--remote-port", str(sim_params.port), "--num-clients", str(sim_params.num_clients), - "--step-length", str(sim_params.sim_step) + "--step-length", str(sim_params.sim_step), + "--max-depart-delay", "0", ] # use a ballistic integration step (if request) diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index d9fc773cd..9bc500b00 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -37,7 +37,8 @@ class KernelVehicle(object): def __init__(self, master_kernel, - sim_params): + sim_params, + net_params): """Instantiate the Flow vehicle kernel. Parameters @@ -51,6 +52,7 @@ def __init__(self, self.master_kernel = master_kernel self.kernel_api = None self.sim_step = sim_params.sim_step + self.net_params = net_params def pass_api(self, kernel_api): """Acquire the kernel api that was generated by the simulation kernel. diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 50cd106c9..15a8bd1bc 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -33,10 +33,37 @@ class TraCIVehicle(KernelVehicle): def __init__(self, master_kernel, - sim_params): + sim_params, + net_params): """See parent class.""" - KernelVehicle.__init__(self, master_kernel, sim_params) - + KernelVehicle.__init__(self, master_kernel, sim_params, net_params) + + self.__inflows = {x["name"]: x for x in self.net_params.inflows.get()} + + # TODO: add random inflow option + # cumulative inflow rate for all vehicles in a given edge + self.__inflows_by_edge = { + k: {"cumsum": [], "type": []} + for k in np.unique([ + self.__inflows[key]["edge"] for key in self.__inflows.keys() + ]) + } + + for key in self.__inflows.keys(): + edge = self.__inflows[key]["edge"] + inflow = self.__inflows[key]["vehsPerHour"] + + # Add the cumulative inflow rates and the type of inflow. + self.__inflows_by_edge[edge]["type"].append(key) + self.__inflows_by_edge[edge]["cumsum"].append(inflow) + if len(self.__inflows_by_edge[edge]["cumsum"]) > 1: + self.__inflows_by_edge[edge]["cumsum"][-1] += \ + self.__inflows_by_edge[edge]["cumsum"][-2] + + # number of vehicles of a specific inflow that have entered the network + self.__num_inflows = {name: 0 for name in self.__inflows.keys()} + + self.total_time = 0 self.__ids = [] # ids of all vehicles self.__human_ids = [] # ids of human-driven vehicles self.__controlled_ids = [] # ids of flow-controlled vehicles @@ -130,8 +157,33 @@ def update(self, reset): specifies whether the simulator was reset in the last simulation step """ - # copy over the previous speeds + # =================================================================== # + # Add the inflow vehicles. # + # =================================================================== # + self.total_time += 1 + + for key in self.__inflows.keys(): + veh_per_hour = self.__inflows[key]["vehsPerHour"] + steps_per_veh = int(3600 / (self.sim_step * veh_per_hour)) + + # Add a vehicle if the inflow rate requires it. + if self.total_time % steps_per_veh == 0: + name = self.__inflows[key]["name"] + self.add( + veh_id="{}_{}".format(name, self.__num_inflows[name]), + type_id=self.__inflows[key]["vtype"], + edge=self.__inflows[key]["edge"], + pos=0, + lane=self.__inflows[key]["departLane"], + speed=self.__inflows[key]["departSpeed"] + ) + self.__num_inflows[name] += 1 + + # =================================================================== # + # Update the vehicle states. # + # =================================================================== # + # copy over the previous speeds vehicle_obs = {} for veh_id in self.__ids: self.previous_speeds[veh_id] = self.get_speed(veh_id) diff --git a/flow/envs/base.py b/flow/envs/base.py index 1abb8a3c9..d246638ef 100644 --- a/flow/envs/base.py +++ b/flow/envs/base.py @@ -156,7 +156,8 @@ def __init__(self, # create the Flow kernel self.k = Kernel(simulator=self.simulator, - sim_params=self.sim_params) + sim_params=self.sim_params, + net_params=self.net_params) # use the network class's network parameters to generate the necessary # network components within the network kernel From 1b9265df1a533b9975b11a5e4d34ed979992046b Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Sun, 19 Apr 2020 22:05:04 -0700 Subject: [PATCH 4/9] some cleanup --- flow/core/kernel/network/traci.py | 35 ------------------------------- flow/networks/highway.py | 2 +- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/flow/core/kernel/network/traci.py b/flow/core/kernel/network/traci.py index 2ff3023b8..7c575adcb 100644 --- a/flow/core/kernel/network/traci.py +++ b/flow/core/kernel/network/traci.py @@ -7,7 +7,6 @@ import subprocess import xml.etree.ElementTree as ElementTree from lxml import etree -from copy import deepcopy E = etree.Element @@ -758,40 +757,6 @@ def generate_cfg(self, net_params, traffic_lights, routes): edges=' '.join(r) )) - # # add the inflows from various edges to the xml file - # if self.network.net_params.inflows is not None: - # total_inflows = self.network.net_params.inflows.get() - # for inflow in total_inflows: - # # do not want to affect the original values - # sumo_inflow = deepcopy(inflow) - # - # # convert any non-string element in the inflow dict to a string - # for key in sumo_inflow: - # if not isinstance(sumo_inflow[key], str): - # sumo_inflow[key] = repr(sumo_inflow[key]) - # - # edge = sumo_inflow['edge'] - # del sumo_inflow['edge'] - # - # if 'route' not in sumo_inflow: - # # distribute the inflow rates across all routes from a - # # given edge w.r.t. the provided fractions for each route - # for i, (_, ft) in enumerate(routes[edge]): - # sumo_inflow['name'] += str(i) - # sumo_inflow['route'] = 'route{}_{}'.format(edge, i) - # - # for key in ['vehsPerHour', 'probability', 'period']: - # if key in sumo_inflow: - # sumo_inflow[key] = str(float(inflow[key]) * ft) - # - # if 'number' in sumo_inflow: - # sumo_inflow['number'] = str( - # int(float(inflow['number']) * ft)) - # - # routes_data.append(_flow(**sumo_inflow)) - # else: - # routes_data.append(_flow(**sumo_inflow)) - printxml(routes_data, self.cfg_path + self.roufn) # this is the data that we will pass to the *.sumo.cfg file diff --git a/flow/networks/highway.py b/flow/networks/highway.py index 286022ec8..821a53518 100644 --- a/flow/networks/highway.py +++ b/flow/networks/highway.py @@ -147,7 +147,7 @@ def specify_internal_edge_starts(self): edge_starts = [ (":edge_{}".format(i + 1), (i + 1) * self.length / self.num_edges + i * junction_length) - for i in range(self.num_edges - 1) + for i in range(self.num_edges - 1) ] return edge_starts From d891974740ef5edb1550af4fbd8558cc11b59b6f Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Tue, 21 Apr 2020 13:56:35 -0700 Subject: [PATCH 5/9] some bug fixes --- flow/core/kernel/network/traci.py | 40 +++++++++++++++++++++++++++++++ flow/core/kernel/vehicle/traci.py | 17 +++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/flow/core/kernel/network/traci.py b/flow/core/kernel/network/traci.py index 7c575adcb..c3460ce89 100644 --- a/flow/core/kernel/network/traci.py +++ b/flow/core/kernel/network/traci.py @@ -7,6 +7,7 @@ import subprocess import xml.etree.ElementTree as ElementTree from lxml import etree +from copy import deepcopy E = etree.Element @@ -757,6 +758,45 @@ def generate_cfg(self, net_params, traffic_lights, routes): edges=' '.join(r) )) + # add the inflows from various edges to the xml file + if self.network.net_params.inflows is not None: + total_inflows = self.network.net_params.inflows.get() + for inflow in total_inflows: + # do not want to affect the original values + sumo_inflow = deepcopy(inflow) + + # The vehsPerHour feature has been moved to the VehicleParams + # class. + if "vehsPerHour" in sumo_inflow.keys(): + continue + + # convert any non-string element in the inflow dict to a string + for key in sumo_inflow: + if not isinstance(sumo_inflow[key], str): + sumo_inflow[key] = repr(sumo_inflow[key]) + + edge = sumo_inflow['edge'] + del sumo_inflow['edge'] + + if 'route' not in sumo_inflow: + # distribute the inflow rates across all routes from a + # given edge w.r.t. the provided fractions for each route + for i, (_, ft) in enumerate(routes[edge]): + sumo_inflow['name'] += str(i) + sumo_inflow['route'] = 'route{}_{}'.format(edge, i) + + for key in ['probability', 'period']: + if key in sumo_inflow: + sumo_inflow[key] = str(float(inflow[key]) * ft) + + if 'number' in sumo_inflow: + sumo_inflow['number'] = str( + int(float(inflow['number']) * ft)) + + routes_data.append(_flow(**sumo_inflow)) + else: + routes_data.append(_flow(**sumo_inflow)) + printxml(routes_data, self.cfg_path + self.roufn) # this is the data that we will pass to the *.sumo.cfg file diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 15a8bd1bc..705ffffce 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -13,6 +13,7 @@ from bisect import bisect_left import itertools from copy import deepcopy +import random # colors for vehicles WHITE = (255, 255, 255) @@ -163,18 +164,30 @@ def update(self, reset): self.total_time += 1 for key in self.__inflows.keys(): + # This inflow is using a sumo-specific feature, so ignore. + if "vehsPerHour" not in self.__inflows[key].keys(): + continue + veh_per_hour = self.__inflows[key]["vehsPerHour"] steps_per_veh = int(3600 / (self.sim_step * veh_per_hour)) # Add a vehicle if the inflow rate requires it. if self.total_time % steps_per_veh == 0: name = self.__inflows[key]["name"] + edge = self.__inflows[key]["edge"] + depart_lane = self.__inflows[key]["departLane"] + + # Choose a random lane to depart from. + if depart_lane == "free": + depart_lane = random.randint( + 0, self.master_kernel.network.num_lanes(edge) - 1) + self.add( veh_id="{}_{}".format(name, self.__num_inflows[name]), type_id=self.__inflows[key]["vtype"], - edge=self.__inflows[key]["edge"], + edge=edge, pos=0, - lane=self.__inflows[key]["departLane"], + lane=depart_lane, speed=self.__inflows[key]["departSpeed"] ) self.__num_inflows[name] += 1 From 02c01bc6cdce1569438b1d6626d3b54f1f86a160 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Tue, 21 Apr 2020 14:50:13 -0700 Subject: [PATCH 6/9] more fixes --- flow/core/kernel/vehicle/traci.py | 42 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 705ffffce..7f879c560 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -14,6 +14,7 @@ import itertools from copy import deepcopy import random +import math # colors for vehicles WHITE = (255, 255, 255) @@ -169,28 +170,33 @@ def update(self, reset): continue veh_per_hour = self.__inflows[key]["vehsPerHour"] - steps_per_veh = int(3600 / (self.sim_step * veh_per_hour)) + steps_per_veh = 3600 / (self.sim_step * veh_per_hour) # Add a vehicle if the inflow rate requires it. - if self.total_time % steps_per_veh == 0: + if steps_per_veh < 1 or self.total_time % int(steps_per_veh) == 0: name = self.__inflows[key]["name"] - edge = self.__inflows[key]["edge"] - depart_lane = self.__inflows[key]["departLane"] - - # Choose a random lane to depart from. - if depart_lane == "free": - depart_lane = random.randint( - 0, self.master_kernel.network.num_lanes(edge) - 1) - - self.add( - veh_id="{}_{}".format(name, self.__num_inflows[name]), - type_id=self.__inflows[key]["vtype"], - edge=edge, - pos=0, - lane=depart_lane, - speed=self.__inflows[key]["departSpeed"] + + # number of vehicles to add + num_vehicles = max( + 1, + # number of vehicles to add if inflow rate is greater than + # the simulation update time per step + math.floor(1/steps_per_veh) + + (1 if random.uniform(0, 1) < + ((1/steps_per_veh) - math.floor(1/steps_per_veh)) + else 0) ) - self.__num_inflows[name] += 1 + + for _ in range(num_vehicles): + self.add( + veh_id="{}_{}".format(name, self.__num_inflows[name]), + type_id=self.__inflows[key]["vtype"], + edge=self.__inflows[key]["edge"], + pos=0, + lane=self.__inflows[key]["departLane"], + speed=self.__inflows[key]["departSpeed"] + ) + self.__num_inflows[name] += 1 # =================================================================== # # Update the vehicle states. # From 694714e1acfe0cdfaf5d6b02721dc8124480de97 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Tue, 21 Apr 2020 19:18:28 -0700 Subject: [PATCH 7/9] bug fixes --- flow/core/kernel/kernel.py | 4 +- flow/core/kernel/simulation/traci.py | 2 +- flow/core/kernel/vehicle/aimsun.py | 7 +- flow/core/kernel/vehicle/base.py | 6 +- flow/core/kernel/vehicle/traci.py | 109 ++++++++++++++++---------- flow/envs/base.py | 9 ++- flow/envs/traffic_light_grid.py | 8 +- tests/fast_tests/test_environments.py | 2 +- 8 files changed, 85 insertions(+), 62 deletions(-) diff --git a/flow/core/kernel/kernel.py b/flow/core/kernel/kernel.py index 336c7cfa5..abf7494b9 100644 --- a/flow/core/kernel/kernel.py +++ b/flow/core/kernel/kernel.py @@ -45,7 +45,7 @@ class Kernel(object): traffic simulators, e.g. SUMO, AIMSUN, TruckSim, etc... """ - def __init__(self, simulator, sim_params, net_params): + def __init__(self, simulator, sim_params): """Instantiate a Flow kernel object. Parameters @@ -65,7 +65,7 @@ def __init__(self, simulator, sim_params, net_params): if simulator == "traci": self.simulation = TraCISimulation(self) self.network = TraCIKernelNetwork(self, sim_params) - self.vehicle = TraCIVehicle(self, sim_params, net_params) + self.vehicle = TraCIVehicle(self, sim_params) self.traffic_light = TraCITrafficLight(self) elif simulator == 'aimsun': self.simulation = AimsunKernelSimulation(self) diff --git a/flow/core/kernel/simulation/traci.py b/flow/core/kernel/simulation/traci.py index f71900d98..2ca73725f 100644 --- a/flow/core/kernel/simulation/traci.py +++ b/flow/core/kernel/simulation/traci.py @@ -89,7 +89,7 @@ def start_simulation(self, network, sim_params): "--remote-port", str(sim_params.port), "--num-clients", str(sim_params.num_clients), "--step-length", str(sim_params.sim_step), - "--max-depart-delay", "0", + # "--max-depart-delay", "0", TODO (ak): maybe use later ] # use a ballistic integration step (if request) diff --git a/flow/core/kernel/vehicle/aimsun.py b/flow/core/kernel/vehicle/aimsun.py index 3320d1515..c9bb238c1 100644 --- a/flow/core/kernel/vehicle/aimsun.py +++ b/flow/core/kernel/vehicle/aimsun.py @@ -97,17 +97,20 @@ def __init__(self, # FIXME lots of these used in simulation/aimsun.py, used when # we want to store the values in an emission file (necessary?) - def initialize(self, vehicles): + def initialize(self, vehicles, net_params): """Initialize vehicle state information. This is responsible for collecting vehicle type information from the - VehicleParams object and placing them within the Vehicles kernel. + VehicleParams object and inflow information from the NetParams object + and placing them within the Vehicles kernel. Parameters ---------- vehicles : flow.core.params.VehicleParams initial vehicle parameter information, including the types of individual vehicles and their initial speeds + net_params : flow.core.params.NetParams + network-specific parameters """ self.type_parameters = vehicles.type_parameters self.num_vehicles = 0 diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 9bc500b00..ebd348faa 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -35,10 +35,7 @@ class KernelVehicle(object): vehicle kernel of separate simulators. """ - def __init__(self, - master_kernel, - sim_params, - net_params): + def __init__(self, master_kernel, sim_params): """Instantiate the Flow vehicle kernel. Parameters @@ -52,7 +49,6 @@ def __init__(self, self.master_kernel = master_kernel self.kernel_api = None self.sim_step = sim_params.sim_step - self.net_params = net_params def pass_api(self, kernel_api): """Acquire the kernel api that was generated by the simulation kernel. diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 7f879c560..dd18cbb8a 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -33,39 +33,20 @@ class TraCIVehicle(KernelVehicle): Extends flow.core.kernel.vehicle.base.KernelVehicle """ - def __init__(self, - master_kernel, - sim_params, - net_params): + def __init__(self, master_kernel, sim_params): """See parent class.""" - KernelVehicle.__init__(self, master_kernel, sim_params, net_params) + KernelVehicle.__init__(self, master_kernel, sim_params) - self.__inflows = {x["name"]: x for x in self.net_params.inflows.get()} - - # TODO: add random inflow option + # a dictionary object containing information on the inflows of every + # inflow type + self._inflows = None # cumulative inflow rate for all vehicles in a given edge - self.__inflows_by_edge = { - k: {"cumsum": [], "type": []} - for k in np.unique([ - self.__inflows[key]["edge"] for key in self.__inflows.keys() - ]) - } - - for key in self.__inflows.keys(): - edge = self.__inflows[key]["edge"] - inflow = self.__inflows[key]["vehsPerHour"] - - # Add the cumulative inflow rates and the type of inflow. - self.__inflows_by_edge[edge]["type"].append(key) - self.__inflows_by_edge[edge]["cumsum"].append(inflow) - if len(self.__inflows_by_edge[edge]["cumsum"]) > 1: - self.__inflows_by_edge[edge]["cumsum"][-1] += \ - self.__inflows_by_edge[edge]["cumsum"][-2] - + self._inflows_by_edge = None # number of vehicles of a specific inflow that have entered the network - self.__num_inflows = {name: 0 for name in self.__inflows.keys()} + self._num_inflows = None + # time since the start of the simulation + self._total_time = None - self.total_time = 0 self.__ids = [] # ids of all vehicles self.__human_ids = [] # ids of human-driven vehicles self.__controlled_ids = [] # ids of flow-controlled vehicles @@ -114,18 +95,25 @@ def __init__(self, # old speeds used to compute accelerations self.previous_speeds = {} - def initialize(self, vehicles): + def initialize(self, vehicles, net_params): """Initialize vehicle state information. This is responsible for collecting vehicle type information from the - VehicleParams object and placing them within the Vehicles kernel. + VehicleParams object and inflow information from the NetParams object + and placing them within the Vehicles kernel. Parameters ---------- vehicles : flow.core.params.VehicleParams initial vehicle parameter information, including the types of individual vehicles and their initial speeds + net_params : flow.core.params.NetParams + network-specific parameters """ + # =================================================================== # + # Add the vehicle features. # + # =================================================================== # + self.type_parameters = vehicles.type_parameters self.minGap = vehicles.minGap self.num_vehicles = 0 @@ -142,6 +130,41 @@ def initialize(self, vehicles): if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 + # =================================================================== # + # Add the inflow features. # + # =================================================================== # + + self._total_time = 0 + + self._inflows = {x["name"]: x for x in net_params.inflows.get()} + + # TODO: add random inflow option + # cumulative inflow rate for all vehicles in a given edge + self._inflows_by_edge = { + k: {"cumsum": [], "type": []} + for k in np.unique([ + self._inflows[key]["edge"] for key in self._inflows.keys() + ]) + } + + for key in self._inflows.keys(): + # This inflow is using a sumo-specific feature, so ignore. + if "vehsPerHour" not in self._inflows[key].keys(): + continue + + edge = self._inflows[key]["edge"] + inflow = self._inflows[key]["vehsPerHour"] + + # Add the cumulative inflow rates and the type of inflow. + self._inflows_by_edge[edge]["type"].append(key) + self._inflows_by_edge[edge]["cumsum"].append(inflow) + if len(self._inflows_by_edge[edge]["cumsum"]) > 1: + self._inflows_by_edge[edge]["cumsum"][-1] += \ + self._inflows_by_edge[edge]["cumsum"][-2] + + # number of vehicles of a specific inflow that have entered the network + self._num_inflows = {name: 0 for name in self._inflows.keys()} + def update(self, reset): """See parent class. @@ -162,19 +185,19 @@ def update(self, reset): # =================================================================== # # Add the inflow vehicles. # # =================================================================== # - self.total_time += 1 + self._total_time += 1 - for key in self.__inflows.keys(): + for key in self._inflows.keys(): # This inflow is using a sumo-specific feature, so ignore. - if "vehsPerHour" not in self.__inflows[key].keys(): + if "vehsPerHour" not in self._inflows[key].keys(): continue - veh_per_hour = self.__inflows[key]["vehsPerHour"] + veh_per_hour = self._inflows[key]["vehsPerHour"] steps_per_veh = 3600 / (self.sim_step * veh_per_hour) # Add a vehicle if the inflow rate requires it. - if steps_per_veh < 1 or self.total_time % int(steps_per_veh) == 0: - name = self.__inflows[key]["name"] + if steps_per_veh < 1 or self._total_time % int(steps_per_veh) == 0: + name = self._inflows[key]["name"] # number of vehicles to add num_vehicles = max( @@ -189,14 +212,14 @@ def update(self, reset): for _ in range(num_vehicles): self.add( - veh_id="{}_{}".format(name, self.__num_inflows[name]), - type_id=self.__inflows[key]["vtype"], - edge=self.__inflows[key]["edge"], + veh_id="{}_{}".format(name, self._num_inflows[name]), + type_id=self._inflows[key]["vtype"], + edge=self._inflows[key]["edge"], pos=0, - lane=self.__inflows[key]["departLane"], - speed=self.__inflows[key]["departSpeed"] + lane=self._inflows[key]["departLane"], + speed=self._inflows[key]["departSpeed"] ) - self.__num_inflows[name] += 1 + self._num_inflows[name] += 1 # =================================================================== # # Update the vehicle states. # @@ -222,7 +245,7 @@ def update(self, reset): self.remove(veh_id) # remove exiting vehicles from the vehicle subscription if they # haven't been removed already - if vehicle_obs[veh_id] is None: + if veh_id in vehicle_obs and vehicle_obs[veh_id] is None: vehicle_obs.pop(veh_id, None) self._arrived_rl_ids.append(arrived_rl_ids) diff --git a/flow/envs/base.py b/flow/envs/base.py index d246638ef..052b974d3 100644 --- a/flow/envs/base.py +++ b/flow/envs/base.py @@ -156,15 +156,15 @@ def __init__(self, # create the Flow kernel self.k = Kernel(simulator=self.simulator, - sim_params=self.sim_params, - net_params=self.net_params) + sim_params=self.sim_params) # use the network class's network parameters to generate the necessary # network components within the network kernel self.k.network.generate_network(self.network) # initial the vehicles kernel using the VehicleParams object - self.k.vehicle.initialize(deepcopy(self.network.vehicles)) + self.k.vehicle.initialize(deepcopy(self.network.vehicles), + deepcopy(self.network.net_params)) # initialize the simulation using the simulation kernel. This will use # the network kernel as an input in order to determine what network @@ -259,7 +259,8 @@ def restart_simulation(self, sim_params, render=None): self.sim_params.emission_path = sim_params.emission_path self.k.network.generate_network(self.network) - self.k.vehicle.initialize(deepcopy(self.network.vehicles)) + self.k.vehicle.initialize(deepcopy(self.network.vehicles), + deepcopy(self.network.net_params)) kernel_api = self.k.simulation.start_simulation( network=self.k.network, sim_params=self.sim_params) self.k.pass_api(kernel_api) diff --git a/flow/envs/traffic_light_grid.py b/flow/envs/traffic_light_grid.py index 53391a329..953555885 100644 --- a/flow/envs/traffic_light_grid.py +++ b/flow/envs/traffic_light_grid.py @@ -477,10 +477,10 @@ def _reroute_if_final_edge(self, veh_id): self.k.vehicle.add( veh_id=veh_id, edge=route_id, - type_id=str(type_id), - lane=str(lane_index), - pos="0", - speed="max") + type_id=type_id, + lane=lane_index, + pos=0, + speed=20) def get_closest_to_intersection(self, edges, num_closest, padding=False): """Return the IDs of the vehicles that are closest to an intersection. diff --git a/tests/fast_tests/test_environments.py b/tests/fast_tests/test_environments.py index 48628c4ec..9b76bdff5 100644 --- a/tests/fast_tests/test_environments.py +++ b/tests/fast_tests/test_environments.py @@ -937,7 +937,7 @@ def test_reset_inflows(self): # reset the environment and get a new inflow rate env.reset() - expected_inflow = 1353.6 # just from checking the new inflow + expected_inflow = 1569.6 # just from checking the new inflow # check that the first inflow rate is approximately what the seeded # value expects it to be From 6dc187d6ad279b5c867c1b3d7f526f4dcd606cd3 Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Tue, 21 Apr 2020 21:01:33 -0700 Subject: [PATCH 8/9] added uniformly distributed inflows --- flow/core/kernel/simulation/traci.py | 2 +- flow/core/kernel/vehicle/traci.py | 54 +++++++++++++++++++++------ tests/fast_tests/test_environments.py | 2 +- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/flow/core/kernel/simulation/traci.py b/flow/core/kernel/simulation/traci.py index 2ca73725f..f71900d98 100644 --- a/flow/core/kernel/simulation/traci.py +++ b/flow/core/kernel/simulation/traci.py @@ -89,7 +89,7 @@ def start_simulation(self, network, sim_params): "--remote-port", str(sim_params.port), "--num-clients", str(sim_params.num_clients), "--step-length", str(sim_params.sim_step), - # "--max-depart-delay", "0", TODO (ak): maybe use later + "--max-depart-delay", "0", ] # use a ballistic integration step (if request) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index dd18cbb8a..834bdcd33 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -165,6 +165,10 @@ def initialize(self, vehicles, net_params): # number of vehicles of a specific inflow that have entered the network self._num_inflows = {name: 0 for name in self._inflows.keys()} + def _congested(self): + """Check if the network is congested.""" + return self.num_vehicles >= 40 + def update(self, reset): """See parent class. @@ -187,17 +191,39 @@ def update(self, reset): # =================================================================== # self._total_time += 1 - for key in self._inflows.keys(): + for edge in self._inflows_by_edge.keys(): # This inflow is using a sumo-specific feature, so ignore. - if "vehsPerHour" not in self._inflows[key].keys(): + if len(self._inflows_by_edge[edge]["cumsum"]) == 0: continue - veh_per_hour = self._inflows[key]["vehsPerHour"] + veh_per_hour = self._inflows_by_edge[edge]["cumsum"][-1] steps_per_veh = 3600 / (self.sim_step * veh_per_hour) # Add a vehicle if the inflow rate requires it. if steps_per_veh < 1 or self._total_time % int(steps_per_veh) == 0: - name = self._inflows[key]["name"] + # Choose the type of vehicle to push to this edge + names = self._inflows_by_edge[edge]["type"] + name = names[0] + cumsum = self._inflows_by_edge[edge]["cumsum"] + for i in range(len(names) - 1): + # Deal with cases with no vehicles. + if self._num_inflows[names[i]] == 0: + break + + # This is used in order to maintain the inflow rate ratio. + exp_inflow_ratio = (cumsum[i+1] - cumsum[i]) / cumsum[i] + act_inflow_ratio = self._num_inflows[names[i+1]] \ + / sum(self._num_inflows[names[j]] for j in range(i+1)) + + # If not enough vehicles of a specific type has been pushed + # to the network yet, add it to the network. + if exp_inflow_ratio < act_inflow_ratio: + break + else: + name = names[i + 1] + + # Choose the departure speed. + depart_speed = self._inflows[name]["departSpeed"] # number of vehicles to add num_vehicles = max( @@ -210,16 +236,16 @@ def update(self, reset): else 0) ) - for _ in range(num_vehicles): + for veh_num in range(num_vehicles): + total_time = self._total_time self.add( - veh_id="{}_{}".format(name, self._num_inflows[name]), - type_id=self._inflows[key]["vtype"], - edge=self._inflows[key]["edge"], + veh_id="{}_{}_{}".format(name, total_time, veh_num), + type_id=self._inflows[name]["vtype"], + edge=self._inflows[name]["edge"], pos=0, - lane=self._inflows[key]["departLane"], - speed=self._inflows[key]["departSpeed"] + lane=self._inflows[name]["departLane"], + speed=depart_speed ) - self._num_inflows[name] += 1 # =================================================================== # # Update the vehicle states. # @@ -460,6 +486,12 @@ def _add_departed(self, veh_id, veh_type): # get the subscription results from the new vehicle new_obs = self.kernel_api.vehicle.getSubscriptionResults(veh_id) + # Increment the vehicle counter of inflow vehicles of a specific type, + # if this is an inflow vehicle. + for key in self._num_inflows.keys(): + if veh_id.startswith(key): + self._num_inflows[key] += 1 + return new_obs def reset(self): diff --git a/tests/fast_tests/test_environments.py b/tests/fast_tests/test_environments.py index 9b76bdff5..48b404d0e 100644 --- a/tests/fast_tests/test_environments.py +++ b/tests/fast_tests/test_environments.py @@ -937,7 +937,7 @@ def test_reset_inflows(self): # reset the environment and get a new inflow rate env.reset() - expected_inflow = 1569.6 # just from checking the new inflow + expected_inflow = 1440.0 # just from checking the new inflow # check that the first inflow rate is approximately what the seeded # value expects it to be From 03f2208070d1e0f1e2d8210c6da4242f9b54e0cc Mon Sep 17 00:00:00 2001 From: AboudyKreidieh Date: Tue, 21 Apr 2020 21:02:08 -0700 Subject: [PATCH 9/9] minor cleanup --- flow/core/kernel/vehicle/traci.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 834bdcd33..84acabce0 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -165,10 +165,6 @@ def initialize(self, vehicles, net_params): # number of vehicles of a specific inflow that have entered the network self._num_inflows = {name: 0 for name in self._inflows.keys()} - def _congested(self): - """Check if the network is congested.""" - return self.num_vehicles >= 40 - def update(self, reset): """See parent class.