├── 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,