Skip to content

Commit 39cfcde

Browse files
authored
Merge pull request #282 from physycom/autoAssignTrafficLightsParams
Add `initTrafficLights` function to RoadNetwork class
2 parents 78ac475 + 10e3100 commit 39cfcde

File tree

4 files changed

+206
-5
lines changed

4 files changed

+206
-5
lines changed

src/dsm/headers/RoadNetwork.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ namespace dsm {
8080
/// @brief Adjust the nodes' transport capacity
8181
/// @details The nodes' capacity is adjusted using the graph's streets transport capacity, which may vary basing on the number of lanes. The node capacity will be set to the sum of the incoming streets' transport capacity.
8282
void adjustNodeCapacities();
83+
/// @brief Initialize the traffic lights with random parameters
84+
/// @details Traffic Lights with no parameters set are initialized with random parameters.
85+
/// Street priorities are assigned considering the number of lanes and the speed limit.
86+
/// Traffic Lights with an input degree lower than 3 are converted to standard intersections.
87+
void initTrafficLights();
8388

8489
/// @brief Import the graph's adjacency matrix from a file.
8590
/// If the file is not of a supported format, it will read the file as a matrix with the first two elements being

src/dsm/sources/RoadNetwork.cpp

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,162 @@ namespace dsm {
8888
}
8989
}
9090

91+
void RoadNetwork::initTrafficLights() {
92+
for (auto const& [id, pNode] : m_nodes) {
93+
if (!pNode->isTrafficLight()) {
94+
continue;
95+
}
96+
auto& tl = dynamic_cast<TrafficLight&>(*pNode);
97+
if (!tl.streetPriorities().empty() || !tl.cycles().empty()) {
98+
continue;
99+
}
100+
auto const& inNeighbours = m_adjacencyMatrix.getCol(id);
101+
std::map<Id, int, std::greater<int>> capacities;
102+
std::unordered_map<Id, double> streetAngles;
103+
std::unordered_map<Id, double> maxSpeeds;
104+
std::unordered_map<Id, int> nLanes;
105+
double higherSpeed{0.}, lowerSpeed{std::numeric_limits<double>::max()};
106+
int higherNLanes{0}, lowerNLanes{std::numeric_limits<int>::max()};
107+
if (inNeighbours.size() < 3) {
108+
Logger::warning(std::format(
109+
"Not enough in neighbours {} for Traffic Light {}", inNeighbours.size(), id));
110+
// Replace with a normal intersection
111+
auto const& coordinates{pNode->coords()};
112+
if (coordinates.has_value()) {
113+
m_nodes[id] = std::make_unique<Intersection>(id, *coordinates);
114+
} else {
115+
m_nodes[id] = std::make_unique<Intersection>(id);
116+
}
117+
continue;
118+
}
119+
for (auto const& inId : inNeighbours) {
120+
auto const streetId{inId * nNodes() + id};
121+
auto const& pStreet{m_edges.at(streetId)};
122+
123+
double const speed{pStreet->maxSpeed()};
124+
int const nLan{pStreet->nLanes()};
125+
auto const cap{pStreet->capacity()};
126+
Logger::debug(std::format("Street {} with capacity {}", streetId, cap));
127+
capacities.emplace(streetId, cap);
128+
streetAngles.emplace(streetId, pStreet->angle());
129+
130+
maxSpeeds.emplace(streetId, speed);
131+
nLanes.emplace(streetId, nLan);
132+
133+
higherSpeed = std::max(higherSpeed, speed);
134+
lowerSpeed = std::min(lowerSpeed, speed);
135+
136+
higherNLanes = std::max(higherNLanes, nLan);
137+
lowerNLanes = std::min(lowerNLanes, nLan);
138+
}
139+
if (higherSpeed != lowerSpeed) {
140+
// Assign streets with higher speed to priority
141+
for (auto const& [sid, speed] : maxSpeeds) {
142+
if (speed == higherSpeed) {
143+
tl.addStreetPriority(sid);
144+
}
145+
}
146+
// continue;
147+
} else if (higherNLanes != lowerNLanes) {
148+
for (auto const& [sid, nLan] : nLanes) {
149+
if (nLan == higherNLanes) {
150+
tl.addStreetPriority(sid);
151+
}
152+
}
153+
// continue;
154+
}
155+
// Set first two elements of capacities to street priorities
156+
// auto it{capacities.begin()};
157+
// tl.addStreetPriority(it->first);
158+
// ++it;
159+
// if (it != capacities.end()) {
160+
// tl.addStreetPriority(it->first);
161+
// continue;
162+
// }
163+
// Id firstStreetId{streetAngles.begin()->first};
164+
// tl.addStreetPriority(firstStreetId);
165+
// for (auto const& [streetId, angle] : streetAngles) {
166+
// if (streetId == firstStreetId) {
167+
// continue;
168+
// }
169+
// if (angle == streetAngles.begin()->second) {
170+
// tl.addStreetPriority(streetId);
171+
// }
172+
// }
173+
// if (tl.streetPriorities().size() > 1) {
174+
// continue;
175+
// }
176+
if (tl.streetPriorities().empty()) {
177+
Logger::warning(std::format("Failed to auto-init Traffic Light {}", id));
178+
continue;
179+
}
180+
181+
// Assign cycles
182+
std::pair<Delay, Delay> greenTimes;
183+
{
184+
auto capPriority{0.}, capNoPriority{0.};
185+
std::unordered_map<Id, double> normCapacities;
186+
auto sum{0.};
187+
for (auto const& [streetId, cap] : capacities) {
188+
sum += cap;
189+
}
190+
for (auto const& [streetId, cap] : capacities) {
191+
normCapacities.emplace(streetId, cap / sum);
192+
}
193+
for (auto const& [streetId, normCap] : normCapacities) {
194+
if (tl.streetPriorities().contains(streetId)) {
195+
capPriority += normCap;
196+
} else {
197+
capNoPriority += normCap;
198+
}
199+
}
200+
Logger::debug(
201+
std::format("Capacities for Traffic Light {}: priority {} no priority {}",
202+
id,
203+
capPriority,
204+
capNoPriority));
205+
greenTimes = std::make_pair(static_cast<Delay>(capPriority * tl.cycleTime()),
206+
static_cast<Delay>(capNoPriority * tl.cycleTime()));
207+
}
208+
std::for_each(inNeighbours.begin(), inNeighbours.end(), [&](Id const inId) {
209+
auto const streetId{inId * nNodes() + id};
210+
auto const nLane{nLanes.at(streetId)};
211+
Delay greenTime{greenTimes.first};
212+
Delay phase{0};
213+
if (!tl.streetPriorities().contains(streetId)) {
214+
phase = greenTime;
215+
greenTime = greenTimes.second;
216+
}
217+
Logger::debug(
218+
std::format("Setting cycle for street {} with green time {} and phase {}",
219+
streetId,
220+
greenTime,
221+
phase));
222+
switch (nLane) {
223+
case 3:
224+
tl.setCycle(streetId,
225+
dsm::Direction::RIGHTANDSTRAIGHT,
226+
TrafficLightCycle{static_cast<Delay>(greenTime * 2. / 3), phase});
227+
tl.setCycle(
228+
streetId,
229+
dsm::Direction::LEFT,
230+
TrafficLightCycle{
231+
static_cast<Delay>(greenTime / 3.),
232+
static_cast<Delay>(phase + static_cast<Delay>(greenTime * 2. / 3))});
233+
break;
234+
default:
235+
tl.setCycle(
236+
streetId, dsm::Direction::ANY, TrafficLightCycle{greenTime, phase});
237+
break;
238+
}
239+
});
240+
}
241+
}
242+
91243
void RoadNetwork::buildAdj() {
92244
// find max values in streets node pairs
93245
m_maxAgentCapacity = 0;
94-
for (const auto& [streetId, pStreet] : m_edges) {
246+
for (auto const& [streetId, pStreet] : m_edges) {
95247
m_maxAgentCapacity += pStreet->capacity();
96248
if (pStreet->geometry().empty()) {
97249
std::vector<std::pair<double, double>> coords;
@@ -319,7 +471,7 @@ namespace dsm {
319471
std::getline(iss, highway, ';');
320472
if (highway.find("traffic_signals") != std::string::npos) {
321473
addNode<TrafficLight>(
322-
nodeIndex, 60, std::make_pair(std::stod(lat), std::stod(lon)));
474+
nodeIndex, 120, std::make_pair(std::stod(lat), std::stod(lon)));
323475
} else if (highway.find("roundabout") != std::string::npos) {
324476
addNode<Roundabout>(nodeIndex, std::make_pair(std::stod(lat), std::stod(lon)));
325477
} else {

test/Test_dynamics.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,10 @@ TEST_CASE("FirstOrderDynamics") {
604604
RoadNetwork graph2;
605605
graph2.addNode<TrafficLight>(1, 4);
606606
graph2.addStreets(s1, s2, s3, s4);
607-
graph2.buildAdj();
608607
auto& tl = graph2.node<TrafficLight>(1);
609608
tl.setCycle(1, dsm::Direction::RIGHT, {2, 0});
610-
tl.setCycle(7, dsm::Direction::RIGHT, {2, 0});
611609
tl.setCycle(16, dsm::Direction::RIGHT, {2, 2});
612-
tl.setCycle(9, dsm::Direction::RIGHT, {2, 2});
610+
graph2.buildAdj();
613611
FirstOrderDynamics dynamics{
614612
graph2, false, 69, 0., dsm::weight_functions::streetLength, 1.};
615613
dynamics.addItinerary(2, 2);

test/Test_graph.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,52 @@ TEST_CASE("RoadNetwork") {
268268
CHECK_FALSE(graph.street(1, 0));
269269
}
270270
SUBCASE("make trafficlight") {
271+
GIVEN("A graph with a traffic light with no parameters") {
272+
Street s1{1, std::make_pair(0, 1), 30., 15., 3};
273+
Street s2{11, std::make_pair(2, 1), 30., 15., 3};
274+
Street s3{16, std::make_pair(3, 1), 30., 15., 1};
275+
Street s4{21, std::make_pair(4, 1), 30., 15., 2};
276+
RoadNetwork graph2;
277+
graph2.addNode<dsm::TrafficLight>(1, 120);
278+
graph2.addStreets(s1, s2, s3, s4);
279+
for (auto const& pair : graph2.edges()) {
280+
pair.second->setCapacity(2 * pair.second->nLanes());
281+
}
282+
graph2.buildAdj();
283+
WHEN("We auto-init Traffic Lights") {
284+
graph2.initTrafficLights();
285+
THEN("Parameters are correctly set") {
286+
auto& tl{graph2.node<dsm::TrafficLight>(1)};
287+
CHECK_EQ(tl.cycleTime(), 120);
288+
auto const& cycles{tl.cycles()};
289+
CHECK_EQ(cycles.size(), 4);
290+
CHECK_EQ(cycles.at(1)[0].greenTime(), 53);
291+
CHECK_EQ(cycles.at(1)[1].greenTime(), 53);
292+
CHECK_EQ(cycles.at(1)[2].greenTime(), 26);
293+
CHECK_EQ(cycles.at(1)[0].phase(), 0);
294+
CHECK_EQ(cycles.at(1)[1].phase(), 0);
295+
CHECK_EQ(cycles.at(1)[2].phase(), 53);
296+
CHECK_EQ(cycles.at(11)[0].greenTime(), 53);
297+
CHECK_EQ(cycles.at(11)[1].greenTime(), 53);
298+
CHECK_EQ(cycles.at(11)[2].greenTime(), 26);
299+
CHECK_EQ(cycles.at(11)[0].phase(), 0);
300+
CHECK_EQ(cycles.at(11)[1].phase(), 0);
301+
CHECK_EQ(cycles.at(11)[2].phase(), 53);
302+
CHECK_EQ(cycles.at(16)[0].greenTime(), 40);
303+
CHECK_EQ(cycles.at(16)[1].greenTime(), 40);
304+
CHECK_EQ(cycles.at(16)[2].greenTime(), 40);
305+
CHECK_EQ(cycles.at(16)[0].phase(), 80);
306+
CHECK_EQ(cycles.at(16)[1].phase(), 80);
307+
CHECK_EQ(cycles.at(16)[2].phase(), 80);
308+
CHECK_EQ(cycles.at(21)[0].greenTime(), 40);
309+
CHECK_EQ(cycles.at(21)[1].greenTime(), 40);
310+
CHECK_EQ(cycles.at(21)[2].greenTime(), 40);
311+
CHECK_EQ(cycles.at(21)[0].phase(), 80);
312+
CHECK_EQ(cycles.at(21)[1].phase(), 80);
313+
CHECK_EQ(cycles.at(21)[2].phase(), 80);
314+
}
315+
}
316+
}
271317
GIVEN("A graph object with two nodes and one street") {
272318
RoadNetwork graph{};
273319
graph.addStreet(Street{1, std::make_pair(0, 1)});

0 commit comments

Comments
 (0)