1
0
mirror of https://github.com/Ahp06/SUMO_Emissions.git synced 2024-11-22 03:26:30 +00:00

Merge pull request #4 from Ahp06/Axel

Axel
This commit is contained in:
Axel HUYNH-PHUC 2018-12-07 22:18:10 +01:00 committed by GitHub
commit b012c4c4a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 445 additions and 246 deletions

View File

@ -9,36 +9,70 @@ import traci
from shapely.geometry.linestring import LineString from shapely.geometry.linestring import LineString
from model import Area, Vehicle from model import Area, Vehicle
from traci._trafficlight import Logic
def remove_vehicle(veh_id):
traci.vehicle.remove(veh_id, traci.constants.REMOVE_PARKING)
def lanes_in_area(area):
for lane_id in traci.lane.getIDList():
polygon_lane = LineString(traci.lane.getShape(lane_id))
if area.rectangle.intersects(polygon_lane):
yield lane_id
def compute_edge_weight(edge_id): def compute_edge_weight(edge_id):
return (traci.edge.getCOEmission(edge_id) return (traci.edge.getCOEmission(edge_id)
+ traci.edge.getNOxEmission(edge_id) + traci.edge.getNOxEmission(edge_id)
+ traci.edge.getHCEmission(edge_id) + traci.edge.getHCEmission(edge_id)
+ traci.edge.getPMxEmission(edge_id) + traci.edge.getPMxEmission(edge_id)
+ traci.edge.getCO2Emission(edge_id)) + traci.edge.getCO2Emission(edge_id))/(traci.edge.getLaneNumber(edge_id))
def adjust_edges_weights(): def adjust_edges_weights():
for edge_id in traci.edge.getIDList(): for edge_id in traci.edge.getIDList():
weight = compute_edge_weight(edge_id) # by default edges weight = length/mean speed weight = compute_edge_weight(edge_id) # by default edges weight = length/mean speed
traci.edge.adaptTraveltime(edge_id, weight) traci.edge.setEffort(edge_id, weight)
for veh_id in traci.vehicle.getIDList():
traci.vehicle.rerouteEffort(veh_id)
def lock_area(area: Area): def limit_speed_into_area(area: Area, vehicles: Iterable[Vehicle], speed_rf):
max_speed = 30 area.limited_speed = True
print(f'Setting max speed into {area.name} to {max_speed} km/h') for lane in area._lanes:
traci.lane.setMaxSpeed(lane.lane_id, speed_rf * lane.initial_max_speed)
def modifyLogic(logic, rf): #rf for "reduction factor"
new_phases = []
for phase in logic._phases:
new_phase = traci.trafficlight.Phase(phase.duration*rf,phase.minDuration*rf,phase.maxDuration*rf,phase.phaseDef)
new_phases.append(new_phase)
return traci.trafficlight.Logic("new-program", 0 , 0 , 0 , new_phases)
def adjust_traffic_light_phase_duration(area, reduction_factor):
area.tls_adjusted = True
for tl in area._tls:
for logic in tl._logics:
traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, modifyLogic(logic,reduction_factor))
def count_vehicles_in_area(area):
vehicles_in_area = 0
for lane in area._lanes:
vehicles_in_area += traci.lane.getLastStepVehicleNumber(lane.lane_id)
return vehicles_in_area
def lock_area(area):
area.locked = True area.locked = True
for lane in area._lanes: for lane in area._lanes:
traci.lane.setMaxSpeed(lane.lane_id, max_speed / 3.6) traci.lane.setDisallowed(lane.lane_id, 'passenger')
def reverse_actions(area):
#Reset max speed to original
if not area.limited_speed:
area.limited_speed = False
for lane in area.lanes:
traci.lane.setMaxSpeed(lane.lane_id, lane.initial_max_speed / 3.6)
#Reset traffic lights initial duration
if not area.tls_adjusted:
area.tls_adjusted = False
for initial_logic in tl._logics:
traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, initial_logic)
#Unlock the area
if not area.locked:
area.locked = False
for lane in area._lanes:
traci.lane.setAllowed(lane.lane_id, '') #empty means all classes are allowed

View File

@ -4,6 +4,12 @@ Global configuration for the simulation
import os import os
import sys import sys
import datetime
import logging
###############################################################################
############################# SIMULATION FILE #################################
###############################################################################
if 'SUMO_HOME' in os.environ: if 'SUMO_HOME' in os.environ:
tools = os.path.join(os.environ['SUMO_HOME'], 'tools') tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
@ -13,14 +19,99 @@ else:
_SUMOCMD = 'sumo' # use 'sumo-gui' cmd for UI _SUMOCMD = 'sumo' # use 'sumo-gui' cmd for UI
_SUMOCFG = "mulhouse_simulation/osm.sumocfg" _SUMOCFG = "mulhouse_simulation/osm.sumocfg"
sumo_binary = os.path.join(os.environ['SUMO_HOME'], 'bin', _SUMOCMD)
sumo_cmd = [sumo_binary, "-c", _SUMOCFG]
###############################################################################
################################## LOGS #######################################
###############################################################################
now = datetime.datetime.now()
current_date = now.strftime("%Y_%m_%d_%H_%M_%S")
LOG_FILENAME = f'sumo_logs_{current_date}.log'
# create logger
logger = logging.getLogger("sumo_logger")
logger.setLevel(logging.INFO)
# create console handler and set level to info
handler = logging.FileHandler(LOG_FILENAME)
# create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# add formatter to handler
handler.setFormatter(formatter)
# add handler to logger
logger.addHandler(handler)
###############################################################################
########################## SIMULATION CONFIGURATION ###########################
###############################################################################
CELLS_NUMBER = 10 CELLS_NUMBER = 10
EMISSIONS_THRESHOLD = 500000 EMISSIONS_THRESHOLD = 500000
n_steps = 200 n_steps = 200
lock_mode = True ###############################################################################
routing_mode = False ########################## ACTIONS CONFIGURATION ##############################
###############################################################################
sumo_binary = os.path.join(os.environ['SUMO_HOME'], 'bin', _SUMOCMD) #Set this mode to True if you want running a basic simulation without actions
sumo_cmd = [sumo_binary, "-c", _SUMOCFG] without_actions_mode = False
#Limit the speed into areas when the threshold is exceeded
speed_rf = 0.1
limit_speed_mode = True
#Decrease all traffic lights duration into the area when the threshold is exceeded
trafficLights_duration_rf = 0.2
adjust_traffic_light_mode = True
#Vehicles are routed according to the less polluted route (HEAVY)
weight_routing_mode = False
#Lock the area when the threshold is exceeded (NOT FIXED)
lock_area_mode = False
#Weight routing mode cannot be combinated with other actions
if weight_routing_mode:
limit_speed_mode = False
adjust_traffic_light_mode = False
#If without_actions_mode is choosen
if without_actions_mode:
limit_speed_mode = False
adjust_traffic_light_mode = False
weight_routing_mode = False
lock_area_mode = False
###############################################################################
########################## SIMULATION REFERENCES ##############################
###############################################################################
# Total of emissions of all pollutants in mg for n steps of simulation without locking areas
total_emissions200 = 43970763.15084749
total_emissions300 = 87382632.08217141
total_emissions400 = 140757491.8489904
total_emissions500 = 202817535.43856794
###############################################################################
########################## CONFIGURATION METHODS ##############################
###############################################################################
def get_basics_emissions():
if n_steps == 200:
return total_emissions200
if n_steps == 300:
return total_emissions300
if n_steps == 400:
return total_emissions400
if n_steps == 500:
return total_emissions500
def show_config():
return (str(f'Grid : {CELLS_NUMBER}x{CELLS_NUMBER}\n')
+ str(f'step number = {n_steps}\n')
+ str(f'weight routing mode = {weight_routing_mode}\n')
+ str(f'lock area mode = {lock_area_mode}\n')
+ str(f'limit speed mode = {limit_speed_mode}, RF = {speed_rf*100}%\n')
+ str(f'adjust traffic light mode = {adjust_traffic_light_mode} , RF = {trafficLights_duration_rf*100}%\n'))

View File

@ -1,13 +1,18 @@
from typing import List from typing import List
import traci import traci
import time
from shapely.geometry import LineString from shapely.geometry import LineString
from parse import search
import actions import actions
import config import config
import sys import sys
from model import Area, Vehicle, Lane from model import Area, Vehicle, Lane , TrafficLight , Phase , Logic
from traci import trafficlight
logger = config.logger
def init_grid(simulation_bounds, cells_number): def init_grid(simulation_bounds, cells_number):
grid = list() grid = list()
@ -19,18 +24,52 @@ def init_grid(simulation_bounds, cells_number):
ar_bounds = ((i * width, j * height), (i * width, (j + 1) * height), ar_bounds = ((i * width, j * height), (i * width, (j + 1) * height),
((i + 1) * width, (j + 1) * height), ((i + 1) * width, j * height)) ((i + 1) * width, (j + 1) * height), ((i + 1) * width, j * height))
area = Area(ar_bounds) area = Area(ar_bounds)
area.name = 'area ({},{})'.format(i, j) area.name = 'Area ({},{})'.format(i, j)
grid.append(area) grid.append(area)
traci.polygon.add(area.name, ar_bounds, (0, 255, 0)) traci.polygon.add(area.name, ar_bounds, (0, 255, 0))
return grid return grid
def get_all_lanes() -> List[Lane]:
lanes = []
for lane_id in traci.lane.getIDList():
polygon_lane = LineString(traci.lane.getShape(lane_id))
initial_max_speed = traci.lane.getMaxSpeed(lane_id)
lanes.append(Lane(lane_id, polygon_lane, initial_max_speed))
return lanes
def parse_phase(phase_repr):
duration = search('duration: {:f}', phase_repr)
minDuration = search('minDuration: {:f}', phase_repr)
maxDuration = search('maxDuration: {:f}', phase_repr)
phaseDef = search('phaseDef: {}\n', phase_repr)
if phaseDef is None: phaseDef = ''
else : phaseDef = phaseDef[0]
return Phase(duration[0], minDuration[0], maxDuration[0], phaseDef)
def add_data_to_areas(areas: List[Area]):
lanes = get_all_lanes()
for area in areas:
for lane in lanes: # add lanes
if area.rectangle.intersects(lane.polygon):
area.add_lane(lane)
for tl_id in traci.trafficlight.getIDList(): # add traffic lights
if lane.lane_id in traci.trafficlight.getControlledLanes(tl_id):
logics = []
for l in traci.trafficlight.getCompleteRedYellowGreenDefinition(tl_id): #add logics
phases = []
for phase in traci.trafficlight.Logic.getPhases(l): #add phases to logics
phases.append(parse_phase(phase.__repr__()))
logics.append(Logic(l,phases))
area.add_tl(TrafficLight(tl_id,logics))
def compute_vehicle_emissions(veh_id): def compute_vehicle_emissions(veh_id):
return (traci.vehicle.getCOEmission(veh_id) return (traci.vehicle.getCOEmission(veh_id)
+ traci.vehicle.getNOxEmission(veh_id) +traci.vehicle.getNOxEmission(veh_id)
+ traci.vehicle.getHCEmission(veh_id) +traci.vehicle.getHCEmission(veh_id)
+ traci.vehicle.getPMxEmission(veh_id) +traci.vehicle.getPMxEmission(veh_id)
+ traci.vehicle.getCO2Emission(veh_id)) +traci.vehicle.getCO2Emission(veh_id))
def get_all_vehicles() -> List[Vehicle]: def get_all_vehicles() -> List[Vehicle]:
@ -39,73 +78,76 @@ def get_all_vehicles() -> List[Vehicle]:
veh_pos = traci.vehicle.getPosition(veh_id) veh_pos = traci.vehicle.getPosition(veh_id)
vehicle = Vehicle(veh_id, veh_pos) vehicle = Vehicle(veh_id, veh_pos)
vehicle.emissions = compute_vehicle_emissions(veh_id) vehicle.emissions = compute_vehicle_emissions(veh_id)
traci.vehicle.setRoutingMode(veh_id, traci.constants.ROUTING_MODE_AGGREGATED)
vehicles.append(vehicle) vehicles.append(vehicle)
return vehicles return vehicles
def get_all_lanes() -> List[Lane]:
lanes = []
for lane_id in traci.lane.getIDList():
polygon_lane = LineString(traci.lane.getShape(lane_id))
lanes.append(Lane(lane_id, polygon_lane))
return lanes
def get_emissions(grid: List[Area], vehicles: List[Vehicle]): def get_emissions(grid: List[Area], vehicles: List[Vehicle]):
for area in grid: for area in grid:
for vehicle in vehicles: for vehicle in vehicles:
if vehicle.pos in area: if vehicle.pos in area:
area.emissions += vehicle.emissions area.emissions += vehicle.emissions
if config.lock_mode and area.emissions > config.EMISSIONS_THRESHOLD and not area.locked: if area.emissions > config.EMISSIONS_THRESHOLD:
actions.lock_area(area)
traci.polygon.setColor(area.name, (255, 0, 0))
traci.polygon.setFilled(area.name, True)
if config.limit_speed_mode and not area.limited_speed:
logger.info(f'Action - Decreased max speed into {area.name} by {config.speed_rf*100}%')
actions.limit_speed_into_area(area, vehicles, config.speed_rf)
traci.polygon.setColor(area.name, (255, 0, 0))
traci.polygon.setFilled(area.name, True)
if config.adjust_traffic_light_mode and not area.tls_adjusted:
logger.info(f'Action - Decreased traffic lights duration by {config.trafficLights_duration_rf*100}%')
actions.adjust_traffic_light_phase_duration(area, config.trafficLights_duration_rf)
def add_lanes_to_areas(areas: List[Area]): if config.lock_area_mode and not area.locked:
lanes = get_all_lanes() if actions.count_vehicles_in_area(area):
for area in areas: logger.info(f'Action - {area.name} blocked')
for lane in lanes: actions.lock_area(area)
if area.rectangle.intersects(lane.polygon):
area.add_lane(lane)
def main(): def main():
grid = list() grid = list()
try: try:
traci.start(config.sumo_cmd) traci.start(config.sumo_cmd)
grid = init_grid(traci.simulation.getNetBoundary(), config.CELLS_NUMBER)
add_lanes_to_areas(grid)
logger.info('Loading data for the simulation')
start = time.perf_counter()
grid = init_grid(traci.simulation.getNetBoundary(), config.CELLS_NUMBER)
add_data_to_areas(grid)
loading_time = round(time.perf_counter() - start,2)
logger.info(f'Data loaded ({loading_time}s)')
logger.info('Start of the simulation')
step = 0 step = 0
while step < config.n_steps: # traci.simulation.getMinExpectedNumber() > 0: while step < config.n_steps : #traci.simulation.getMinExpectedNumber() > 0:
traci.simulationStep() traci.simulationStep()
vehicles = get_all_vehicles() vehicles = get_all_vehicles()
get_emissions(grid, vehicles) get_emissions(grid, vehicles)
if config.routing_mode: if config.weight_routing_mode:
logger.info('Action - Lane weights adjusted')
actions.adjust_edges_weights() actions.adjust_edges_weights()
# actions.rerouteAllVehicles()
step += 1 step += 1
sys.stdout.write(f'Simulation step = {step}/{config.n_steps}' + '\r')
sys.stdout.flush()
finally: finally:
traci.close(False) traci.close(False)
simulation_time = round(time.perf_counter() - start,2)
logger.info(f'End of the simulation ({simulation_time}s)')
total_emissions = 0 total_emissions = 0
for area in grid: for area in grid:
total_emissions += area.emissions total_emissions += area.emissions
# Total of emissions of all pollutants in mg for 200 steps of simulation without locking areas logger.info(f'Total emissions = {total_emissions} mg')
total_emissions200 = 43970763.15084749
print(f'\n**** Total emissions = {total_emissions} mg ****') if not config.without_actions_mode:
diff_with_lock = (total_emissions200 - total_emissions) / total_emissions200 ref = config.get_basics_emissions()
print(f'**** Reduction percentage of emissions = {diff_with_lock*100} % ****\n') diff_with_actions = (ref - total_emissions)/ref
logger.info(f'Reduction percentage of emissions = {diff_with_actions*100} %')
logger.info('With the configuration : \n' + str(config.show_config()))
logger.info('Logs END')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -3,27 +3,57 @@ from typing import Tuple, Set
from shapely.geometry import Point, LineString from shapely.geometry import Point, LineString
from shapely.geometry import Polygon from shapely.geometry import Polygon
from shapely.geometry.base import BaseGeometry from shapely.geometry.base import BaseGeometry
from traci._trafficlight import Logic as SUMO_Logic
class Lane: class Lane:
def __init__(self, lane_id: str, polygon: LineString): def __init__(self, lane_id: str, polygon: LineString, initial_max_speed: float):
self.polygon = polygon self.polygon = polygon
self.lane_id = lane_id self.lane_id = lane_id
self.initial_max_speed = initial_max_speed
def __hash__(self): def __hash__(self):
"""Overrides the default implementation""" """Overrides the default implementation"""
return hash(self.lane_id) return hash(self.lane_id)
class Phase:
def __init__(self, duration: float, minDuration: float, maxDuration : float, phaseDef: str):
self.duration = duration
self.minDuration = minDuration
self.maxDuration = maxDuration
self.phaseDef = phaseDef
def __repr__(self) -> str:
repr = f'Phase(duration:{self.duration},minDuration:{self.minDuration},maxDuration:{self.maxDuration},phaseDef:{self.phaseDef})'
return str(repr)
class Logic:
def __init__(self, logic: SUMO_Logic, phases: Set[Phase]):
self._logic = logic
self._phases: Set[Phase] = phases
class TrafficLight:
def __init__(self, tl_id: str, logics: Set[Logic]):
self.tl_id = tl_id
self._logics: Set[Logic] = logics
def __hash__(self):
"""Overrides the default implementation"""
return hash(self.tl_id)
class Area: class Area:
def __init__(self, coords, name=''): def __init__(self, coords, name=''):
self.limited_speed = False
self.locked = False self.locked = False
self.tls_adjusted = False
self.rectangle = Polygon(coords) self.rectangle = Polygon(coords)
self.name = name self.name = name
self.emissions = 0.0 self.emissions = 0.0
self._lanes: Set[Lane] = set() self._lanes: Set[Lane] = set()
self._tls: Set[TrafficLight] = set()
def __eq__(self, other): def __eq__(self, other):
return self.rectangle.__eq__(other) return self.rectangle.__eq__(other)
@ -41,6 +71,9 @@ class Area:
def add_lane(self, lane: Lane): def add_lane(self, lane: Lane):
self._lanes.add(lane) self._lanes.add(lane)
def add_tl(self, tl: TrafficLight):
self._tls.add(tl)
def remove_lane(self, lane: Lane): def remove_lane(self, lane: Lane):
self._lanes.remove(lane) self._lanes.remove(lane)
@ -52,7 +85,6 @@ class Area:
(xmax, ymax), (xmax, ymax),
(xmax, ymin))) (xmax, ymin)))
class Vehicle: class Vehicle:
def __init__(self, veh_id: int, pos: Tuple[float, float]): def __init__(self, veh_id: int, pos: Tuple[float, float]):