├── stats └── .gitkeep ├── .vscode └── settings.json ├── .gitignore ├── requirement.txt ├── autorun.sh ├── main.bat ├── plot.py ├── simulator ├── maps │ ├── traffic.sumocfg │ └── map.net.xml ├── route_manager.py └── simulator.py ├── README.md ├── main.sh ├── multiMain.py ├── runner.py ├── connection.py ├── BapRuAlgorithmManager.py └── algorithm.py /stats/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "/usr/bin/python3" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | log* 3 | stats/* 4 | !stats/.gitkeep 5 | simulator/maps/map.rou.xml -------------------------------------------------------------------------------- /requirement.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | numpy 3 | scipy 4 | matplotlib 5 | xlwt 6 | xlrd 7 | xlutils 8 | -------------------------------------------------------------------------------- /autorun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in {1..50} 3 | do 4 | echo $i 5 | python3 main.py 6 | done -------------------------------------------------------------------------------- /main.bat: -------------------------------------------------------------------------------- 1 | FOR %%G in (0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 1) DO ( 2 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.4 --car_flow %%G --heartbeat_factor 2 --saving_file_name advanced_ideal_stableperiod_0.4_heartbeat_2.csv 3 | ) 4 | -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.express as px 3 | import os 4 | import matplotlib.pyplot as plt 5 | import argparse 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument("file_name", help="csv file") 9 | args = parser.parse_args() 10 | 11 | df = pd.read_csv(args.file_name, index_col = 0) 12 | plt.plot(df) 13 | plt.show() -------------------------------------------------------------------------------- /simulator/maps/traffic.sumocfg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simulation of leader selection algorithm for vehicular ad-hoc network: 2 | This is the simulation for leader selection algorithm, to see more details on the algorithm, visit the paper: 3 | https://arxiv.org/abs/1912.06776 4 | 5 | # Citing 6 | To cite the paper, use this Bibtex: 7 | ``` 8 | @inproceedings{zhang2019leader, 9 | title={Leader selection in Vehicular Ad-hoc Networks: a Proactive Approach}, 10 | author={Zhang, Rusheng and Jacquemot, Baptiste and Bakirci, Kagan and Bartholme, Sacha and Kaempf, Killian and Freydt, Baptiste and Montandon, Loic and Zhang, Shenqi and Tonguz, Ozan}, 11 | booktitle={2020 IEEE 88th Vehicular Technology Conference (VTC-Fall)}, 12 | year={2020}, 13 | organization={IEEE} 14 | } 15 | ``` 16 | To cite this code, you can use the following bibtex: 17 | ``` 18 | @misc{leaderselectionrepo, 19 | author = {Rusheng Zhang and Baptiste Jacquemot}, 20 | title = {Leader Selection Algorithm Repo}, 21 | year = {2019}, 22 | publisher = {GitHub}, 23 | journal = {GitHub repository}, 24 | howpublished = {\url{https://github.com/beedrill/leader-selection-simulation}}, 25 | } 26 | ``` 27 | 28 | 29 | # prerequisites 30 | - [SUMO](https://sumo.dlr.de/) version > 1.0 31 | - __SUMO_HOME__ environment parameter specified correctly 32 | - python packages: numpy, scipy, matplotlib, pandas, to install all required python packages, do: 33 | ```bash 34 | pip install -r requirement.txt 35 | ``` 36 | 37 | # Execute 38 | To execute the code, simply do: 39 | ```bash 40 | python runner.py 41 | ``` 42 | 43 | Algorithm parameters can be specified, check: 44 | ```bash 45 | python runner.py -h 46 | ``` 47 | 48 | # More details 49 | You can specify how to run the algorithm in more details, check __multiMain.py__ for example, it contains an example of doing multiple simulations, retrieve usefule data and calculate the average 50 | 51 | You write a batch script or bash script to use runner.py the way you want, check __main.sh__ and __main.bat__ for examples. 52 | -------------------------------------------------------------------------------- /simulator/route_manager.py: -------------------------------------------------------------------------------- 1 | # import xml.dom.minidom as minidom 2 | import xml.etree.cElementTree as ET 3 | import random 4 | import numpy as np 5 | 6 | class RouteManager(object): 7 | def __init__(self, map_folder, 8 | route_configs={ 9 | "pattern":"fixed" #or "random" 10 | }): 11 | self.route_configs = route_configs 12 | 13 | def bind_simulator(self, sim): 14 | self.simulator = sim 15 | 16 | def init_routes(self): 17 | raise NotImplementedError 18 | 19 | def step(self): 20 | # print ("route manager stepped") 21 | return 22 | 23 | class DefaultRouteManager(RouteManager): 24 | #Static array for random directions 25 | DIRECTIONS = ["west_in east_out", "north_in south_out", "east_in west_out", "south_in north_out"] 26 | 27 | def __init__(self, map_folder, car_flow = 1, 28 | route_configs={ 29 | "pattern":"fixed" #or "random" 30 | }): 31 | super(DefaultRouteManager, self).__init__(map_folder, route_configs = route_configs) 32 | self.car_flow = car_flow 33 | 34 | def init_routes(self, max_arrival_time = 240): 35 | # if is_traffic_dense: 36 | # beta = 1 37 | # else: 38 | # beta = 4 39 | beta = 1. / self.car_flow 40 | exp = np.random.exponential(beta, 1000) 41 | next_depart = 0 42 | i = 0 43 | routes = ET.Element("routes") 44 | ET.SubElement(routes, "vType", accel="0.8", decel="4.5", id="car", length="5", maxSpeed="40", sigma="0.5") 45 | while next_depart <= max_arrival_time : 46 | vehicle = ET.SubElement(routes, "vehicle" , color="1, 0, 0", depart= str(next_depart), id= str(i), type="car") 47 | ET.SubElement(vehicle, "route", edges = random.choice(DefaultRouteManager.DIRECTIONS)) 48 | next_depart += exp[i] 49 | i += 1 50 | 51 | tree = ET.ElementTree(routes) 52 | tree.write('simulator/maps/map.rou.xml') -------------------------------------------------------------------------------- /main.sh: -------------------------------------------------------------------------------- 1 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 2 | do 3 | python runner.py --algorithm basic --trials 10 --channel_condition ideal --stable_period 0.1 --car_flow $FLOW --heartbeat_factor 2 --saving_file_name basic_ideal_stableperiod_0.1_heartbeat_2.csv 4 | done 5 | 6 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 7 | do 8 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.1 --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_stableperiod_0.1_heartbeat_2.csv 9 | done 10 | 11 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 12 | do 13 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.2 --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_stableperiod_0.2_heartbeat_2.csv 14 | done 15 | 16 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 17 | do 18 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.4 --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_stableperiod_0.4_heartbeat_2.csv 19 | done 20 | 21 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 22 | do 23 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.1 --explicit_leader_switch --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_leaderSwitch_stableperiod_0.1_heartbeat_2.csv 24 | done 25 | 26 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 27 | do 28 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.2 --explicit_leader_switch --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_leaderSwitch_stableperiod_0.2_heartbeat_2.csv 29 | done 30 | 31 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 32 | do 33 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.4 --explicit_leader_switch --car_flow $FLOW --heartbeat_factor 2 --saving_file_name advanced_ideal_leaderSwitch_stableperiod_0.4_heartbeat_2.csv 34 | done 35 | 36 | for FLOW in 0.25 0.35 0.45 0.55 0.65 0.75 0.85 1 37 | do 38 | python runner.py --algorithm advanced --trials 10 --channel_condition ideal --stable_period 0.1 --car_flow $FLOW --heartbeat_factor 2 --optimize_backward_msg_propagation --saving_file_name advanced_ideal_stableperiod_0.1_optBack_heartbeat_2.csv 39 | done 40 | 41 | -------------------------------------------------------------------------------- /multiMain.py: -------------------------------------------------------------------------------- 1 | from simulator.simulator import Simulator 2 | from simulator.route_manager import DefaultRouteManager 3 | from BapRuAlgorithmManager import BapRuAlgorithmManager as algo 4 | # from algorithm import DummyAlgorithmManager as algo 5 | from connection import DeterminedConnectionManager as determinedConn 6 | from connection import IdealConnectionManager as idealConn 7 | from connection import HarshConnectionManager as harshConn 8 | connec = [determinedConn,idealConn, harshConn] 9 | rm = DefaultRouteManager("simulator/maps") 10 | 11 | number_simulation = 10 12 | 13 | param = [True, False] # True means implement leader switch 14 | # False is without leader switch 15 | for k in range(len(param)): 16 | 17 | leader_msg_count = 0 18 | pos_msg_count = 0 19 | valid_time = 0 20 | avg_cvg_time = 0 21 | max_cvg_time = 0 22 | nbr_leader_changes = 0 23 | 24 | print("use param: ", param[k]) 25 | algo.ACTIVATE_SWITCH = param[k] 26 | 27 | for i in range(number_simulation): 28 | print("================================================") 29 | print("simulation number: ", i) 30 | 31 | sim = Simulator( 32 | rm, #route manager 33 | algo, #algorithm module 34 | connec[2], 35 | "simulator/maps", #map folder 36 | False 37 | ) 38 | 39 | rm.bind_simulator(sim) 40 | sim.start_simulation() 41 | 42 | 43 | 44 | leader_msg_count += sim.get_count("leader_msg") # number of leader messages 45 | pos_msg_count += sim.get_count("pos_msg") # number of position messages (messages sending back to leader) 46 | valid_time += sim.get_valid_time() # valid time percentage of the time having 1 leader 47 | avg_cvg_time += sim.get_avg_cvg_time() # average convergence time 48 | max_cvg_time += sim.get_max_cvg_time() # maximum convergence time 49 | nbr_leader_changes += sim.get_nbr_leader_changes() # number of times leader changes 50 | 51 | leader_msg_count /= number_simulation 52 | pos_msg_count /= number_simulation 53 | valid_time /= number_simulation 54 | avg_cvg_time /= number_simulation 55 | max_cvg_time /= number_simulation 56 | nbr_leader_changes /= number_simulation 57 | print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") 58 | print("result: ") 59 | print("nbr of leader messages: ", leader_msg_count) 60 | print("nbr of pos messages: ", pos_msg_count) 61 | print("% time with one leader", valid_time * 100) 62 | print("avg conv time: ", avg_cvg_time) 63 | print("max conv time: ", max_cvg_time) 64 | print("number of leader changes: ", nbr_leader_changes) 65 | print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") 66 | -------------------------------------------------------------------------------- /runner.py: -------------------------------------------------------------------------------- 1 | from simulator.simulator import Simulator 2 | from simulator.route_manager import DefaultRouteManager 3 | from BapRuAlgorithmManager import BapRuAlgorithmManager as algo 4 | from algorithm import BapAlgorithmManager as basic_algo 5 | #from algorithm import DummyAlgorithmManager as algo 6 | from connection import DeterminedConnectionManager as determinedConn 7 | from connection import IdealConnectionManager as idealConn 8 | from connection import HarshConnectionManager as harshConn 9 | import argparse 10 | import sys 11 | map_dir = "simulator/maps" 12 | parser = argparse.ArgumentParser(description='Simulation code for a proactive Leader selection algorithm in vehicular network') 13 | parser.add_argument('--algorithm', default = 'advanced', help='specify algorithm, can be either basic or advanced, default is advanced; NOTE that basic algorithm will not adopt any optimization method, even if you specify in the commandline args') 14 | parser.add_argument('--explicit_leader_switch', action='store_true', help='implement explicit leader switch') 15 | parser.add_argument('--channel_condition', default = 'ideal', help='specify channel condition value can be either ideal or harsh') 16 | parser.add_argument('--stable_period', default = 0.1, type =float, help='period when leader is converged') 17 | parser.add_argument('--threshold_dec_freq_msg', default = 0.5, type =float, help='the leader decrease the period of broadcasting after this threshold') 18 | parser.add_argument('--car_flow', default = 0.1, type =float, help='period when leader is converged') 19 | parser.add_argument('--heartbeat_factor', default = 4, type = float, help='heartbeat factor (if broadcast period is 100ms, heartbeat factor is 2, then the heartbeat detection will be 200ms)') 20 | parser.add_argument('--optimize_backward_msg_propagation', action='store_true', help='use this to implement the optimization method that reduce the number of backward messages') 21 | parser.add_argument('--saving_file_name', default = None, type = str, help='specify the file name to save the results') 22 | parser.add_argument('--trials', default = 1, type = int, help='how many trials to do and get results as average') 23 | if __name__ == "__main__": 24 | 25 | cmd_args = parser.parse_args() 26 | 27 | ## specify algorithm: 28 | if cmd_args.algorithm == 'basic': 29 | algo = basic_algo 30 | elif cmd_args.algorithm != 'advanced': 31 | print ("unknown algorithm parameter... exiting.") 32 | sys.exit(1) 33 | 34 | ## car flow specification: 35 | rm = DefaultRouteManager( map_dir, car_flow = cmd_args.car_flow ) 36 | 37 | ## explicit leader switch: 38 | algo.ACTIVATE_SWITCH = cmd_args.explicit_leader_switch 39 | 40 | ## time threshold after leader message broadcast frequency decrease: 41 | algo.THRESHOLD_DEC_FREQ_MSG = cmd_args.threshold_dec_freq_msg 42 | 43 | ## heartbeat factor (if broadcast period is 100ms, heartbeat factor is 2, then the heartbeat detection will be 200ms): 44 | algo.HEARTBEAT_FACTOR = cmd_args.heartbeat_factor 45 | 46 | ## Period factor (defined as the factor to multiply the original broadcast period when leader is converged, for example, if original broadcast period is 100ms, 47 | # the period factor is 2 then after reaching convergence, the period factor will be 200ms): 48 | algo.PERIOD_LEADER_STABLE = cmd_args.stable_period 49 | 50 | ## optimize backward messages: 51 | algo.REDUCE_BACKWARD_MESSAGE = cmd_args.optimize_backward_msg_propagation 52 | 53 | ## channel condition specification 54 | if cmd_args.channel_condition == 'ideal': 55 | connec = idealConn 56 | elif cmd_args.channel_condition == 'harsh': 57 | connec = harshConn 58 | else: 59 | print ("unknown channel condition parameter... exiting.") 60 | sys.exit(1) 61 | 62 | 63 | total_num_leader_msg = 0 64 | total_orig_num_leader_msg = 0 65 | total_num_pos_msg = 0 66 | total_valid_time = 0 67 | total_avg_cvg_time = 0 68 | total_max_cvg_time = 0 69 | total_num_leader_changes = 0 70 | 71 | 72 | for _ in range(cmd_args.trials): 73 | sim = Simulator( 74 | rm, #route manager 75 | algo, #algorithm module 76 | connec, 77 | "simulator/maps", #map folder 78 | visual = False, # set to True if need to visually observe the simulation 79 | new_route = True # set to True if not need to re-generate vehicle routes 80 | ) 81 | rm.bind_simulator(sim) 82 | sim.start_simulation() 83 | 84 | num_leader_msg = sim.get_count("leader_msg") 85 | num_orig_leader_msg = sim.get_count("original_leader_msg") 86 | num_pos_msg = sim.get_count("pos_msg") 87 | valid_time = sim.get_valid_time() 88 | avg_cvg_time = sim.get_avg_cvg_time() 89 | max_cvg_time = sim.get_max_cvg_time() 90 | num_leader_changes = sim.get_nbr_leader_changes() 91 | 92 | print() 93 | print("nbr of leader messages: ", num_leader_msg) 94 | print("nbr of original leader messages: ", num_orig_leader_msg) 95 | print("nbr of pos messages: ", num_pos_msg) 96 | print("% time with one leader", valid_time*100) 97 | print("avg conv time: ", avg_cvg_time) 98 | print("max conv time: ", max_cvg_time) 99 | print("number of leader changes: ", num_leader_changes) 100 | 101 | total_num_leader_msg += sim.get_count("leader_msg") 102 | total_orig_num_leader_msg += sim.get_count("original_leader_msg") 103 | total_num_pos_msg += sim.get_count("pos_msg") 104 | total_valid_time += sim.get_valid_time() 105 | total_avg_cvg_time += sim.get_avg_cvg_time() 106 | total_max_cvg_time += sim.get_max_cvg_time() 107 | total_num_leader_changes += sim.get_nbr_leader_changes() 108 | 109 | if cmd_args.saving_file_name: 110 | print ('writing to file: stats/'+cmd_args.saving_file_name) 111 | f = open('stats/'+cmd_args.saving_file_name, 'a') 112 | f.write('{}, {}, {}, {}, {}, {}, {}\n'.format( 113 | total_num_leader_msg/cmd_args.trials, #number of leader messages 114 | total_orig_num_leader_msg/cmd_args.trials, #number of leader messages sent by leaders only 115 | total_num_pos_msg/cmd_args.trials, # number of position messages (messages sending back to leader) 116 | total_valid_time/cmd_args.trials, # valid time percentage of the time having 1 leader 117 | total_avg_cvg_time/cmd_args.trials, # average convergence time 118 | total_max_cvg_time/cmd_args.trials, # maximum convergence time 119 | total_num_leader_changes/cmd_args.trials # number of times leader changes 120 | )) 121 | f.close() -------------------------------------------------------------------------------- /connection.py: -------------------------------------------------------------------------------- 1 | import re 2 | import math 3 | from scipy.stats import nakagami 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import random 7 | 8 | POWER_TRANSMISSION =100 9 | TRUNCATE_DISTANCE = 300 #If -1 it doesnt truncate at all 10 | 11 | def decision(probability): 12 | return random.random() < probability 13 | 14 | def getDistance(coord1, coord2): 15 | return math.sqrt((coord1[0]-coord2[0])**2 + (coord1[1]-coord2[1])**2) 16 | 17 | class ConnectionManager(): 18 | def __init__(self, veh): 19 | self.id = veh.id 20 | self.vehicle = veh 21 | self.simulator = veh.simulator 22 | self.connected_list = [] 23 | self.curr_msg_buffer = [] 24 | self.next_msg_buffer = [] 25 | self.latest_control_msg_time = -1.0 # The last time when the vehicle receives a traffic control message 26 | self.latest_control_message = "" 27 | self.earliest_initialization_message = "" 28 | self.response_message_list = [] 29 | self.position_message_list = [] 30 | self.num_broadcast = {} 31 | 32 | def bind_simulator(self, sim): 33 | print("connection bind simulator") 34 | self.simulator = sim 35 | self.vehicle_list = self.simulator.vehicle_list 36 | 37 | def step(self): 38 | self.get_connected_list() 39 | self.curr_msg_buffer = self.next_msg_buffer 40 | self.next_msg_buffer = [] 41 | 42 | def connected(self, vid): 43 | # check if the vehicle is connected (communicatable) to another vehicle (vid) 44 | # need to implement in the inherit class 45 | raise NotImplementedError 46 | 47 | def broadcast(self, msg): 48 | for vid in self.connected_list: 49 | if vid in self.simulator.vehicle_list.keys(): 50 | self.simulator.vehicle_list[vid].connection_manager.next_msg_buffer.append(msg) 51 | 52 | def get_connected_list(self): 53 | self.connected_list = [] 54 | for vid in self.simulator.vehicle_list: 55 | if self.id != vid and self.connected(vid) == True: 56 | self.connected_list.append(vid) 57 | 58 | def classify_message(self): 59 | self.latest_control_message = "" 60 | self.earliest_initialization_message = "" 61 | earliest_selection_start_time = float(self.simulator.time) 62 | self.response_message_list = [] 63 | self.position_message_list = [] 64 | for msg in self.curr_msg_buffer: 65 | message_parsed = re.split(",", msg) 66 | if message_parsed[0] == "1": 67 | time = float(message_parsed[1]) 68 | if time > self.latest_control_msg_time: 69 | self.latest_control_msg_time = time 70 | self.latest_control_message = msg 71 | elif message_parsed[0] == "2": 72 | time = float(message_parsed[1]) 73 | if time < earliest_selection_start_time: 74 | earliest_selection_start_time = time 75 | self.earliest_initialization_message = msg 76 | elif message_parsed[0] == "3": 77 | self.response_message_list.append(msg) 78 | elif message_parsed[0] == "4": 79 | self.position_message_list.append(msg) 80 | 81 | def get_latest_control_message(self): 82 | return self.latest_control_message 83 | 84 | def get_earliest_initialization_message(self): 85 | return self.earliest_initialization_message 86 | 87 | def get_response_message_list(self): 88 | return self.response_message_list 89 | 90 | def get_position_message_list(self): 91 | return self.position_message_list 92 | 93 | def get_num_broadcast(self): 94 | return self.num_broadcast 95 | 96 | class DeterminedConnectionManager(ConnectionManager): 97 | # in this connection manager, the connection between vehicle is determined(non-probablistic) and 98 | def __init__(self, veh): 99 | super(DeterminedConnectionManager, self).__init__(veh) 100 | 101 | 102 | def connected(self, vid): 103 | v = self.simulator.vehicle_list[vid] 104 | if self.vehicle.original_lane == v.original_lane: 105 | return True 106 | if self.vehicle.lane_position <= 30 and v.lane_position <= 30: 107 | return True 108 | return False 109 | 110 | # Three types of connection: Ideal, Moderate and Harsh 111 | class HarshConnectionManager(ConnectionManager): 112 | def __init__(self, veh): 113 | super(HarshConnectionManager,self).__init__(veh) 114 | 115 | def getProbability(self, x,psi): 116 | param = math.pow(x/psi,2) 117 | if TRUNCATE_DISTANCE >= 0 and x > TRUNCATE_DISTANCE: 118 | #print('truncate') 119 | return 0 120 | return math.exp(-param) 121 | 122 | 123 | def connected(self, vid): 124 | 125 | ownPosition = self.vehicle.position 126 | targetPosition = self.simulator.vehicle_list[vid].position 127 | distance = getDistance(ownPosition,targetPosition) 128 | 129 | #m = 1.0 130 | omega = POWER_TRANSMISSION 131 | proba = self.getProbability(distance,omega) 132 | #print("probability: ", proba) 133 | return decision(proba) 134 | ''' 135 | class ModerateConnectionManager(ConnectionManager): 136 | def __init__(self, veh): 137 | super(ModerateConnectionManager,self).__init__(veh) 138 | 139 | def getProbability(self, x,psi): 140 | param = math.pow(x/psi,2) 141 | return math.exp(-2*param) * (1+2* param) 142 | 143 | def connected(self, vid): 144 | ownPosition = self.vehicle.position 145 | targetPosition = self.simulator.vehicle_list[vid].position 146 | distance = getDistance(ownPosition,targetPosition) 147 | 148 | #m=2.0 149 | omega=POWER_TRANSMISSION 150 | proba = self.getProbability(distance,omega) 151 | #print("probability: ", proba) 152 | return decision(proba) 153 | ''' 154 | class IdealConnectionManager(ConnectionManager): 155 | def __init__(self, veh): 156 | super(IdealConnectionManager,self).__init__(veh) 157 | 158 | def getProbability(self, x,psi): 159 | param = math.pow(x/psi,2) 160 | if TRUNCATE_DISTANCE >= 0 and x > TRUNCATE_DISTANCE: 161 | #print('truncate') 162 | return 0 163 | return math.exp(-3*param) * (1+3* param + 9./2 * math.pow(param,2)) 164 | 165 | 166 | def connected(self, vid): 167 | ownPosition = self.vehicle.position 168 | targetPosition = self.simulator.vehicle_list[vid].position 169 | distance = getDistance(ownPosition,targetPosition) 170 | 171 | #m=3.0 172 | omega=POWER_TRANSMISSION 173 | proba = self.getProbability(distance,omega) 174 | #print("probability: ", proba, distance, ownPosition, targetPosition) 175 | return decision(proba) 176 | 177 | -------------------------------------------------------------------------------- /simulator/maps/map.net.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /simulator/simulator.py: -------------------------------------------------------------------------------- 1 | import re 2 | import csv 3 | import xlwt 4 | import xlrd 5 | from xlutils.copy import copy 6 | import math 7 | 8 | try: 9 | import traci 10 | except Exception: 11 | import os, sys 12 | if 'SUMO_HOME' in os.environ: 13 | tools = os.path.join(os.environ['SUMO_HOME'], 'tools') 14 | sys.path.append(tools) 15 | import traci 16 | else: 17 | sys.exit("please declare environment variable 'SUMO_HOME'") 18 | 19 | class Simulator(): 20 | 21 | def __init__(self, route_manager, algorithm_module, connection_module, map_folder, visual = True, new_route = True): 22 | self.map_folder = map_folder 23 | self.visual = visual 24 | self.new_route = new_route # boolean use to indicate if we need to create a new route for this simulation 25 | self.algorithm_module = algorithm_module 26 | self.connection_module = connection_module 27 | if visual: 28 | self.sumo_binary = "sumo-gui" 29 | else: 30 | self.sumo_binary = "sumo" 31 | self.sumoCmd = [self.sumo_binary, "--step-length", "0.1", "-c", self.map_folder + "/traffic.sumocfg"] 32 | self.route_manager = route_manager 33 | self.time = 0 34 | self.vehicle_list = {} 35 | # Some constants 36 | self.silent_time_threshold = 2 # If a group leader experiences a certain time period without any message, it will start a leader selection. 37 | self.selection_time_threshold = 2 # The time length for a leader selection proposer to select messages 38 | self.leader_time_threshold = 200 # The time length for a leader. If the time is beyond the threshold, the leader will choose a new leader. 39 | # For the log file 40 | self.log = open("log.txt", "w") 41 | 42 | #for the stats 43 | csvfile_msg = open('stats/stats_msg.csv', 'w', newline='') 44 | self.csvwriter_msg = csv.writer(csvfile_msg, delimiter=',') 45 | 46 | csvfile_leader = open('stats/stats_leader.csv', 'w', newline='') 47 | self.csvwriter_leader = csv.writer(csvfile_leader, delimiter=',') 48 | 49 | csvfile_cvg = open('stats/stats_cvg.csv', 'w', newline='') 50 | self.csvwriter_cvg = csv.writer(csvfile_cvg, delimiter=',') 51 | 52 | self.num_msg_file = open("stats/num_msg.txt", "w") 53 | 54 | self.num_only_one_leader = 0 55 | self.num_valid_step = 0 # if there is at least on car a the center of the intersection 56 | self.only_one_leader_file = open("stats/only_one_leader.txt", "w") 57 | 58 | self.avg_convergence_time = 0 59 | self.max_convergence_time = 0 60 | self.cur_convergence_time = 0 61 | self.num_of_picks = 0 62 | self.pick_before = True 63 | self.last_count = 0 64 | self.max_convergence_time_file = open("stats/max_conv_time.txt", "w") 65 | self.num_broadcast = {} 66 | 67 | def init_params(self): 68 | if self.new_route: 69 | self.route_manager.init_routes() 70 | 71 | traci.start(self.sumoCmd) 72 | self.deltaT = traci.simulation.getDeltaT() 73 | 74 | def start_simulation(self): 75 | self.init_params() 76 | while traci.simulation.getMinExpectedNumber() > 0 and traci.simulation.getTime() <= 200: 77 | self.step() 78 | self.time += self.deltaT 79 | 80 | self.end_simulation() 81 | 82 | def get_count(self, type_msg): 83 | return self.num_broadcast.get(type_msg, 0) 84 | 85 | def get_valid_time(self): 86 | if self.num_valid_step == 0: 87 | return math.inf 88 | return (self.num_only_one_leader / self.num_valid_step) 89 | 90 | def get_avg_cvg_time(self): 91 | if self.num_of_picks == 0: 92 | return math.inf 93 | return self.avg_convergence_time / self.num_of_picks 94 | 95 | def get_max_cvg_time(self): 96 | return self.max_convergence_time 97 | 98 | def get_leader_swicth_count(self): 99 | return self.algorithm_module.get_leader_swicth_count() 100 | 101 | def get_nbr_leader_changes(self): 102 | return self.num_of_picks 103 | 104 | def end_simulation(self): 105 | traci.close() 106 | 107 | # FILE I/0 num_of_messages 108 | # self.num_msg_file.write(str(count)) 109 | # self.only_one_leader_file.write(str(self.num_only_one_leader / self.num_valid_step)) 110 | # self.max_convergence_time_file.write(str(self.max_convergence_time)) 111 | 112 | 113 | def step(self): 114 | traci.simulationStep() 115 | self.route_manager.step() 116 | self.maintain_vehicle_list() 117 | # Refresh parameters. 118 | for vid in self.vehicle_list: 119 | self.vehicle_list[vid].update_position() 120 | self.vehicle_list[vid].get_lane_position() 121 | self.vehicle_list[vid].connection_manager.step() 122 | for vid in self.vehicle_list: 123 | self.vehicle_list[vid].pre_step() 124 | for vid in self.vehicle_list: 125 | self.vehicle_list[vid].step() 126 | 127 | #self.print_vehicle() 128 | # Do actions 129 | for vid in self.vehicle_list: 130 | self.vehicle_list[vid].post_step() 131 | 132 | self.create_stats() 133 | 134 | 135 | # stats and useful color to distingish vehicle 136 | def create_stats(self): 137 | #count the number of leader at a given distance of the intersection center 138 | distance = 30 139 | number_of_leader = 0 140 | number_of_car = 0 141 | for vid in self.vehicle_list: 142 | if self.vehicle_list[vid].lane_position < distance: 143 | number_of_car += 1 144 | if self.vehicle_list[vid].is_leader(): 145 | number_of_leader += 1 146 | 147 | if self.vehicle_list[vid].is_leader(): 148 | # leader color 149 | traci.vehicle.setColor(vid, (255, 255, 255)) 150 | else: 151 | # choose a color for the leader 152 | h = hash(self.vehicle_list[vid].get_leader() + 10*"padding") 153 | # take a bright color 154 | h0 = h % 128 + 128 155 | h1 = (h >> 8) % 128 + 128 156 | h2 = (h >> 16) % 128 + 128 157 | traci.vehicle.setColor(vid, (h0 , h1 , h2)) 158 | 159 | count = self.get_count("leader_msg") 160 | 161 | self.csvwriter_msg.writerow([self.time, count - self.last_count]) #number_of_msg 162 | 163 | self.last_count = count 164 | 165 | self.csvwriter_leader.writerow([self.time, 1 if number_of_leader == 1 else 0]) #number_of_leader 166 | 167 | 168 | self.csvwriter_cvg.writerow([self.time, 0 if number_of_car >= 1 and number_of_leader != 1 else 1]) #number_of_leader 169 | 170 | 171 | #number of time there is only one leader 172 | if number_of_car >= 1: 173 | self.num_valid_step += 1 174 | if number_of_leader == 1: 175 | self.num_only_one_leader += 1 176 | 177 | #compute convergence time 178 | if number_of_car >= 1 and number_of_leader != 1: 179 | self.cur_convergence_time += self.deltaT 180 | self.pick_before = False 181 | else: 182 | self.avg_convergence_time += self.cur_convergence_time 183 | self.max_convergence_time = max(self.max_convergence_time, self.cur_convergence_time) 184 | ## if no pick before... 185 | if not self.pick_before: 186 | self.num_of_picks += 1 187 | self.pick_before = True 188 | self.cur_convergence_time = 0 189 | 190 | 191 | def maintain_vehicle_list(self): 192 | departed_id_list = traci.simulation.getDepartedIDList() 193 | for id in departed_id_list: 194 | if not id in self.vehicle_list.keys(): 195 | self.vehicle_list[id] = Vehicle(id) 196 | v = self.vehicle_list[id] 197 | v.bind_simulator(self) 198 | v.bind_connection_manager(self.connection_module(v)) 199 | v.bind_algorithm(self.algorithm_module(v)) 200 | arrived_id_list = traci.simulation.getArrivedIDList() 201 | 202 | for id in arrived_id_list: 203 | if id in self.vehicle_list.keys(): 204 | self.vehicle_list[id].leave_intersection() 205 | self.vehicle_list.pop(id) 206 | # color of the car out of the intersection 207 | traci.vehicle.setColor(id, (128, 0, 0)) 208 | for id in list(self.vehicle_list): 209 | v = self.vehicle_list[id] 210 | if traci.vehicle.getLaneID(id) != v.original_lane: 211 | self.vehicle_list[id].leave_intersection() 212 | self.vehicle_list.pop(id) 213 | # color of the car out of the intersection 214 | traci.vehicle.setColor(id, (128, 0, 0)) 215 | 216 | def print_vehicle(self): 217 | for _ in range(100): 218 | self.log.write("-") 219 | self.log.write("\n") 220 | self.log.write("time:" + str(self.time) + 221 | " traffic_light:" + traci.trafficlight.getRedYellowGreenState("0") + "\n") 222 | for vid in self.vehicle_list: 223 | v = self.vehicle_list[vid] 224 | self.log.write("vid:" + v.id + 225 | " original_lane:" + v.original_lane + 226 | " direction:" + v.direction + 227 | " lane_position:" + str(v.lane_position) + "\n") 228 | self.log.write(" connected_list:" + str(v.connection_manager.connected_list) + 229 | " curr_msg_buffer:" + str(v.connection_manager.curr_msg_buffer) + "\n") 230 | self.log.write(" leader:" + v.algorithm_manager.leader + 231 | " leader_time:" + str(v.algorithm_manager.leader_time) + "\n") 232 | self.log.write(" is_group_leader:" + str(v.algorithm_manager.is_group_leader) + 233 | " silent_time:" + str(v.algorithm_manager.silent_time) + 234 | " latest_control_msg_time:" + str(v.connection_manager.latest_control_msg_time) + "\n") 235 | self.log.write(" is_proposer:" + str(v.algorithm_manager.is_proposer) + 236 | " selection_time:" + str(v.algorithm_manager.selection_time) + "\n") 237 | for _ in range(100): 238 | self.log.write("-") 239 | self.log.write("\n") 240 | 241 | class Vehicle(): 242 | def __init__(self, id): 243 | self.id = id 244 | self.original_lane = traci.vehicle.getLaneID(self.id) 245 | self.direction = self.get_direction() 246 | self.lane_position = traci.lane.getLength(self.original_lane) 247 | self.position = traci.vehicle.getPosition(self.id) 248 | 249 | def bind_algorithm(self, algorithm_manager): 250 | self.algorithm_manager = algorithm_manager 251 | 252 | def bind_connection_manager(self, connection_manager): 253 | self.connection_manager = connection_manager 254 | 255 | def bind_simulator(self, simulator): 256 | self.simulator = simulator 257 | 258 | def get_direction(self): 259 | list = re.split("_", self.original_lane) 260 | if list[0] == "east" or list[0] == "west": 261 | return "east-west" 262 | elif list[0] == "north" or list[0] == "south": 263 | return "north-south" 264 | 265 | def same_lane(self, veh): 266 | return self.original_lane == veh.original_lane 267 | 268 | def same_dir(self, veh): 269 | return self.direction == veh.direction 270 | 271 | 272 | #return distance toward the next intersection 273 | def get_lane_position(self): 274 | from_origin = traci.vehicle.getLanePosition(self.id) 275 | lane_length = traci.lane.getLength(self.original_lane) 276 | self.lane_position = lane_length - from_origin 277 | 278 | def update_position(self): 279 | self.position = traci.vehicle.getPosition(self.id) 280 | 281 | def pre_step(self): 282 | self.algorithm_manager.pre_step() 283 | 284 | def step(self): 285 | self.algorithm_manager.step() 286 | 287 | def post_step(self): 288 | self.algorithm_manager.post_step() 289 | 290 | def leave_intersection(self): 291 | self.algorithm_manager.leave_intersection() 292 | 293 | def is_leader(self): 294 | return self.algorithm_manager.is_leader() 295 | 296 | def get_leader(self): 297 | return self.algorithm_manager.leader -------------------------------------------------------------------------------- /BapRuAlgorithmManager.py: -------------------------------------------------------------------------------- 1 | try: 2 | import traci 3 | except Exception: 4 | import os, sys 5 | if 'SUMO_HOME' in os.environ: 6 | tools = os.path.join(os.environ['SUMO_HOME'], 'tools') 7 | sys.path.append(tools) 8 | import traci 9 | else: 10 | sys.exit("please declare environment variable 'SUMO_HOME'") 11 | import re 12 | 13 | from algorithm import AlgorithmManager 14 | import math 15 | 16 | 17 | # todo name the constants properly 18 | class BapRuAlgorithmManager(AlgorithmManager): 19 | 20 | # static variable 21 | ACTIVATE_SWITCH = True 22 | REDUCE_BACKWARD_MESSAGE = True 23 | PERIOD_LEADER_STABLE = 0.1 # when the agent is not receiving conflict leader messages, then it set the period to a lower frequency 24 | # e.g., if original period is 100ms , then the broadcast period after convergence will be PERIOD_LEADER_STABLE seconds 25 | PERIOD_LEADER_NOT_STABLE = 0.1 26 | HEARTBEAT_FACTOR = 2 27 | THRESHOLD_DEC_FREQ_MSG = 0.5 # after this time being alone, the leader decrease the period of broadcasting 28 | def __init__(self, vehicle): 29 | super(BapRuAlgorithmManager, self).__init__(vehicle) 30 | 31 | self.counter = 0 32 | self.last_received_leader_message_time = self.simulator.time 33 | self.num_spam = 0 34 | self.max_spam_number = 3 35 | self.leader_switch_count = 0 36 | 37 | self.max_dis_switch_leader = 30 38 | self.max_leader_force_time = 3 39 | 40 | self.init_leader() 41 | 42 | def pre_step(self): 43 | return 44 | 45 | def post_step(self): 46 | return 47 | 48 | def init_leader(self, pos_vehicles = {}, force_time = 0): 49 | self.leader = self.id 50 | self.last_lead_msg_sent = 0 51 | self.time_leader = 0 52 | self.time_alone = 0 53 | self.lead_msg_dt = BapRuAlgorithmManager.PERIOD_LEADER_NOT_STABLE 54 | self.last_msg_received = self.create_leader_msg() 55 | self.dis_to_leader = 0 56 | 57 | self.pos_vehicles = pos_vehicles 58 | self.force_time = force_time 59 | 60 | # when a car follow a leader, then it must addapt his silent 61 | # time according to the lead msg freq 62 | def silentTime(self): 63 | return self.last_msg_received["lead_msg_dt"] * BapRuAlgorithmManager.HEARTBEAT_FACTOR 64 | 65 | 66 | def handle_leader_msg(self, msg): 67 | 68 | if msg["leader_id"] == self.id and self.is_leader(): 69 | return 70 | 71 | if msg["leader_id"] != self.id and self.is_leader(): 72 | self.time_alone = 0 73 | 74 | if msg["leader_id"] != self.leader: 75 | need_to_change_leader = self.selfCompare(msg) 76 | 77 | # in case two car can't decide to be a leader 78 | # this condition is not probable in real life 79 | if self.is_leader() and abs(msg["lane_position"] - self.vehicle.lane_position) < 0.5: 80 | if self.num_spam >= self.max_spam_number: 81 | self.num_spam = 0 82 | need_to_change_leader = msg["leader_id"] < self.last_msg_received["leader_id"] 83 | 84 | if not need_to_change_leader: 85 | self.num_spam += 1 86 | 87 | if msg["force_leader"]: 88 | need_to_change_leader = True 89 | 90 | # this is the only place we can change the leader 91 | if need_to_change_leader: 92 | self.leader = msg["leader_id"] 93 | self.last_received_leader_message_time = self.simulator.time 94 | self.last_msg_received = msg 95 | 96 | return 97 | 98 | if not self.is_leader() \ 99 | and msg["leader_id"] == self.leader: 100 | 101 | # decide if the message have to be relayed 102 | if msg["sequence_number"] > self.last_msg_received["sequence_number"]: 103 | 104 | if(not self.neighbors().issubset(self.last_msg_received["visited"])): 105 | # broadcast the message 106 | self.add_neighbors_to_visited() 107 | self.broadcast(self.last_msg_received) 108 | 109 | self.dis_to_leader = min(msg["dis_to_leader"] + 1, self.dis_to_leader) 110 | self.last_received_leader_message_time = self.simulator.time 111 | self.last_msg_received = msg 112 | else: 113 | self.add_new_visited(msg) 114 | self.dis_to_leader = msg["dis_to_leader"] + 1 115 | 116 | # replace with the function you want 117 | # you may need to modify create leader msg if you want to have more information on the leader 118 | def selfCompare(self, msg): 119 | return self.compare(msg, self.last_msg_received) 120 | 121 | def broadcast(self, msg): 122 | # keep up with simulation stats 123 | type_msg = msg["type_msg"] 124 | self.simulator.num_broadcast[type_msg] = self.simulator.num_broadcast.get(type_msg, 0) + 1 125 | if type_msg == "leader_msg" and self.is_leader(): 126 | self.simulator.num_broadcast["original_leader_msg"] = self.simulator.num_broadcast.get("original_leader_msg", 0) + 1 127 | 128 | self.connection_manager.broadcast(msg) 129 | 130 | # replace with the function you want 131 | # you may need to modify create leader msg if you want to have more information on the leader 132 | def compare(self, msg1, msg2): 133 | return msg1["lane_position"] < msg2["lane_position"] 134 | 135 | def neighbors(self): 136 | return set(self.connection_manager.connected_list) 137 | 138 | def create_leader_msg(self): 139 | # [message_type, leader id, distance to intersecrion center, set of visited car, 140 | # id of leader message, frequence between lead messages, 141 | # dis from the leader in leader graph, force the leader to stay leader] 142 | return { 143 | "type_msg": "leader_msg", 144 | "leader_id": self.id, 145 | "lane_position": self.vehicle.lane_position, 146 | "visited": set(self.connection_manager.connected_list), 147 | "sequence_number": self.counter, 148 | "lead_msg_dt": self.lead_msg_dt, 149 | "dis_to_leader": 0, 150 | "force_leader": False 151 | } 152 | 153 | # the 2 next function are used to change the set of visited car in leader message 154 | 155 | def add_new_visited(self, msg): 156 | self.last_msg_received["visited"] = msg["visited"].union(self.last_msg_received["visited"]) 157 | 158 | def add_neighbors_to_visited(self): 159 | self.last_msg_received["visited"] = self.neighbors().union(self.last_msg_received["visited"]) 160 | self.last_msg_received["dis_to_leader"] += 1 161 | 162 | # handle position messages 163 | def handle_pos_msg(self, msg): 164 | if msg["leader_id"] != self.leader: 165 | return 166 | 167 | if self.is_leader(): 168 | # record the pos of all car 169 | self.pos_vehicles[msg["vehicle_id"]] = msg 170 | 171 | if not BapRuAlgorithmManager.REDUCE_BACKWARD_MESSAGE: 172 | if self.id not in msg["visited"]: 173 | msg["visited"].add(self.id) 174 | self.broadcast(msg) 175 | # relay it only if the car which received is closer to the leader 176 | elif msg["dis_to_leader"] < self.dis_to_leader: 177 | msg["dis_to_leader"] = self.dis_to_leader 178 | self.broadcast(msg) 179 | 180 | 181 | # postion messages 182 | def create_pos_msg(self): 183 | # [type message, vehicle id, leader id, distance from the center, original lane 184 | #, dis to leader in position message graph, direction, time message sent] 185 | return { 186 | "type_msg": "pos_msg", 187 | "vehicle_id": self.id, 188 | "leader_id": self.leader, 189 | "lane_position": self.vehicle.lane_position, 190 | "original_lane": self.vehicle.original_lane, 191 | "visited": set(), 192 | "dis_to_leader": self.dis_to_leader, 193 | "direction": self.vehicle.get_direction(), 194 | "time": self.simulator.time 195 | } 196 | 197 | def leader_step(self): 198 | self.time_leader += self.simulator.deltaT 199 | self.time_alone += self.simulator.deltaT 200 | 201 | if self.last_lead_msg_sent >= self.lead_msg_dt: 202 | 203 | # the time alone is the time that have passed since the leader have not received any messages from 204 | # another leader 205 | 206 | if self.time_alone < BapRuAlgorithmManager.THRESHOLD_DEC_FREQ_MSG: 207 | # time alone too short, the leader is not stable 208 | self.lead_msg_dt = BapRuAlgorithmManager.PERIOD_LEADER_NOT_STABLE 209 | 210 | if self.time_alone >= BapRuAlgorithmManager.THRESHOLD_DEC_FREQ_MSG: 211 | # time alone long enouth, we can increase lead message period 212 | self.lead_msg_dt = BapRuAlgorithmManager.PERIOD_LEADER_STABLE 213 | 214 | 215 | 216 | self.last_msg_received = self.create_leader_msg() 217 | 218 | #force him to be the leader 219 | if self.time_leader < self.force_time: 220 | self.last_msg_received["lane_position"] = 0 221 | 222 | 223 | self.last_received_leader_message_time = self.simulator.time 224 | self.broadcast(self.last_msg_received) 225 | self.counter += 1 226 | self.last_lead_msg_sent = 0 227 | else: 228 | self.last_lead_msg_sent += self.simulator.deltaT 229 | 230 | self.pos_vehicles[self.id] = self.create_pos_msg() 231 | 232 | def step(self): 233 | 234 | for msg in self.connection_manager.curr_msg_buffer: 235 | # todo change force leader 236 | if msg["type_msg"] == "leader_msg" and msg["force_leader"] and msg["leader_id"] == self.id: 237 | self.init_leader(msg["pos_vehicles"], self.max_leader_force_time) 238 | break 239 | 240 | # if the car is his own leader 241 | if self.is_leader(): 242 | self.leader_step() 243 | else: 244 | self.broadcast(self.create_pos_msg()) 245 | 246 | for msg in self.connection_manager.curr_msg_buffer: 247 | if msg["type_msg"] == "leader_msg": 248 | self.handle_leader_msg(msg) 249 | 250 | if msg["type_msg"] == "pos_msg": 251 | self.handle_pos_msg(msg) 252 | 253 | # there is a timeout for receiving leader messages 254 | if not self.is_leader() \ 255 | and abs(self.last_received_leader_message_time - self.simulator.time) > self.silentTime(): 256 | # become a leader 257 | self.init_leader() 258 | 259 | def create_next_leader_msg(self, next_leader): 260 | # [message_type, leader id, distance to intersecrion center, set of visited car, 261 | # id of leader message, frequence between lead messages, 262 | # dis from the leader in leader graph, force the leader to stay leader] 263 | return { 264 | "type_msg": "leader_msg", 265 | "leader_id": next_leader, 266 | "lane_position": 0, 267 | "visited": set(self.connection_manager.connected_list), # todo what should we put here 268 | "sequence_number": self.counter, 269 | "lead_msg_dt": self.lead_msg_dt, 270 | "dis_to_leader": 0, 271 | "force_leader": True, 272 | "pos_vehicles": self.pos_vehicles 273 | } 274 | 275 | # get the closest on the given lane 276 | def get_next_leader_on_lane(self, pred): 277 | best_msg_pos = None 278 | 279 | for id_car, pos_msg in self.pos_vehicles.items(): 280 | 281 | # the car is not on the i,tersection anymore 282 | if abs(self.simulator.time - pos_msg["time"]) > 1: 283 | continue 284 | 285 | # take the best on a different lane 286 | if id_car != self.id and pos_msg["lane_position"] < self.max_dis_switch_leader \ 287 | and pred(best_msg_pos, pos_msg): 288 | best_msg_pos = pos_msg 289 | 290 | return None if best_msg_pos is None else best_msg_pos["vehicle_id"] 291 | 292 | # to select the next leader: 293 | # - we take the closest car in a different direction (n-s or e-w) in a 30m radius 294 | # - if there is none, then take the farthest car on the same direction (deacrese leader switch number) 295 | def get_next_leader(self): 296 | 297 | farthest_same_dir = \ 298 | lambda best_msg, msg: msg["direction"] == self.vehicle.direction \ 299 | and (best_msg is None or msg["lane_position"] > best_msg["lane_position"]) 300 | 301 | closest_diff_dir = \ 302 | lambda best_msg, msg: msg["direction"] != self.vehicle.direction \ 303 | and (best_msg is None or msg["lane_position"] < best_msg["lane_position"]) 304 | 305 | new_leader = self.get_next_leader_on_lane(closest_diff_dir) 306 | if not new_leader is None: 307 | return new_leader 308 | return self.get_next_leader_on_lane(farthest_same_dir) 309 | 310 | 311 | #this method is called when the car leave the intersection 312 | def leave_intersection(self): 313 | 314 | if not self.is_leader(): 315 | return 316 | 317 | new_leader = self.get_next_leader() 318 | 319 | if new_leader is None: 320 | return 321 | 322 | msg = self.create_next_leader_msg(new_leader) 323 | if BapRuAlgorithmManager.ACTIVATE_SWITCH: 324 | self.broadcast(msg) 325 | self.leader_switch_count+=1 326 | 327 | def get_leader_switch_count(self): 328 | return self.leader_switch_count -------------------------------------------------------------------------------- /algorithm.py: -------------------------------------------------------------------------------- 1 | try: 2 | import traci 3 | except Exception: 4 | import os, sys 5 | if 'SUMO_HOME' in os.environ: 6 | tools = os.path.join(os.environ['SUMO_HOME'], 'tools') 7 | sys.path.append(tools) 8 | import traci 9 | else: 10 | sys.exit("please declare environment variable 'SUMO_HOME'") 11 | import re 12 | 13 | # message type: traffic control message (1), selection initialization message (2), selection response message (3), lane position message (4) 14 | # traffic control message: 1,