1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file MSActuatedTrafficLightLogic.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Julia Ringel
13 /// @author Jakob Erdmann
14 /// @author Michael Behrisch
15 /// @author Laura Bieker
16 /// @date Sept 2002
17 /// @version $Id$
18 ///
19 // An actuated (adaptive) traffic light logic
20 /****************************************************************************/
21
22
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27
28 #include <cassert>
29 #include <utility>
30 #include <vector>
31 #include <bitset>
32 #include <microsim/MSEventControl.h>
33 #include <microsim/output/MSDetectorControl.h>
34 #include <microsim/output/MSInductLoop.h>
35 #include <microsim/MSGlobals.h>
36 #include <microsim/MSNet.h>
37 #include "MSTrafficLightLogic.h"
38 #include "MSActuatedTrafficLightLogic.h"
39 #include <microsim/MSLane.h>
40 #include <microsim/MSEdge.h>
41 #include <netload/NLDetectorBuilder.h>
42 #include <utils/common/StringUtils.h>
43
44 //#define DEBUG_DETECTORS
45 #define DEBUG_COND (getID()=="26121229")
46
47 // ===========================================================================
48 // parameter defaults definitions
49 // ===========================================================================
50 #define DEFAULT_MAX_GAP "3.0"
51 #define DEFAULT_PASSING_TIME "1.9"
52 #define DEFAULT_DETECTOR_GAP "2.0"
53
54 #define DEFAULT_LENGTH_WITH_GAP 7.5
55
56
57 // ===========================================================================
58 // method definitions
59 // ===========================================================================
MSActuatedTrafficLightLogic(MSTLLogicControl & tlcontrol,const std::string & id,const std::string & programID,const Phases & phases,int step,SUMOTime delay,const std::map<std::string,std::string> & parameter,const std::string & basePath)60 MSActuatedTrafficLightLogic::MSActuatedTrafficLightLogic(MSTLLogicControl& tlcontrol,
61 const std::string& id, const std::string& programID,
62 const Phases& phases,
63 int step, SUMOTime delay,
64 const std::map<std::string, std::string>& parameter,
65 const std::string& basePath) :
66 MSSimpleTrafficLightLogic(tlcontrol, id, programID, TLTYPE_ACTUATED, phases, step, delay, parameter) {
67
68 myMaxGap = StringUtils::toDouble(getParameter("max-gap", DEFAULT_MAX_GAP));
69 myPassingTime = StringUtils::toDouble(getParameter("passing-time", DEFAULT_PASSING_TIME)); // passing-time seems obsolete... (Leo)
70 myDetectorGap = StringUtils::toDouble(getParameter("detector-gap", DEFAULT_DETECTOR_GAP));
71 myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
72 myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
73 myFreq = TIME2STEPS(StringUtils::toDouble(getParameter("freq", "300")));
74 myVehicleTypes = getParameter("vTypes", "");
75 }
76
~MSActuatedTrafficLightLogic()77 MSActuatedTrafficLightLogic::~MSActuatedTrafficLightLogic() { }
78
79 void
init(NLDetectorBuilder & nb)80 MSActuatedTrafficLightLogic::init(NLDetectorBuilder& nb) {
81 MSTrafficLightLogic::init(nb);
82 assert(myLanes.size() > 0);
83 bool warn = true; // warn only once
84 const int numLinks = (int)myLinks.size();
85
86 // Detector position should be computed based on road speed. If the position
87 // is quite far away and the minDur is short this may cause the following
88 // problems:
89 //
90 // 1) high flow failure:
91 // In a standing queue, no vehicle touches the detector.
92 // By the time the queue advances, the detector gap has been exceeded and the phase terminates prematurely
93 //
94 // 2) low flow failure
95 // The standing queue is fully between stop line and detector and there are no further vehicles.
96 // The minDur is too short to let all vehicles pass
97 //
98 // Problem 2) is not so critical because there is less potential for
99 // jamming in a low-flow situation. In contrast, problem 1) should be
100 // avoided as it has big jamming potential. We compute an upper bound for the
101 // detector disatnce to avoid it
102
103
104 // change values for setting the loops and lanestate-detectors, here
105 //SUMOTime inductLoopInterval = 1; //
106 LaneVectorVector::const_iterator i2;
107 LaneVector::const_iterator i;
108 // build the induct loops
109 std::map<const MSLane*, MSInductLoop*> laneInductLoopMap;
110 double maxDetectorGap = 0;
111 for (i2 = myLanes.begin(); i2 != myLanes.end(); ++i2) {
112 const LaneVector& lanes = *i2;
113 for (i = lanes.begin(); i != lanes.end(); i++) {
114 MSLane* lane = (*i);
115 if (noVehicles(lane->getPermissions())) {
116 // do not build detectors on green verges or sidewalks
117 continue;
118 }
119 if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
120 // only build one detector per lane
121 continue;
122 }
123 const SUMOTime minDur = getMinimumMinDuration(lane);
124 if (minDur == std::numeric_limits<SUMOTime>::max()) {
125 // only build detector if this lane is relevant for an actuated phase
126 continue;
127 }
128 double length = lane->getLength();
129 double speed = lane->getSpeedLimit();
130 double inductLoopPosition = MIN2(
131 myDetectorGap * speed,
132 MAX2(1.0, (STEPS2TIME(minDur) - 1)) * DEFAULT_LENGTH_WITH_GAP);
133
134 // check whether the lane is long enough
135 double ilpos = length - inductLoopPosition;
136 if (ilpos < 0) {
137 ilpos = 0;
138 }
139 // Build the induct loop and set it into the container
140 std::string id = "TLS" + myID + "_" + myProgramID + "_InductLoopOn_" + lane->getID();
141 MSInductLoop* loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, lane, ilpos, myVehicleTypes, myShowDetectors));
142 laneInductLoopMap[lane] = loop;
143 myInductLoops.push_back(loop);
144 MSNet::getInstance()->getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, myFile, myFreq);
145 maxDetectorGap = MAX2(maxDetectorGap, length - ilpos);
146
147 if (warn && floor(floor(inductLoopPosition / DEFAULT_LENGTH_WITH_GAP) * myPassingTime) > STEPS2TIME(minDur)) {
148 // warn if the minGap is insufficient to clear vehicles between stop line and detector
149 WRITE_WARNING("At actuated tlLogic '" + getID() + "', minDur " + time2string(minDur) + " is too short for a detector gap of " + toString(inductLoopPosition) + "m.");
150 warn = false;
151 }
152 }
153 }
154 // assign loops to phase index (myInductLoopsForPhase)
155 // check1: loops may not be used for a phase if there are other connections from the same lane that may not drive in that phase
156 // greenMinor is ambiguous as vehicles may not be able to drive
157 // Under the following condition we allow actuation from minor link:
158 // check1a : the minor link is minor in all phases
159 // check1b : there is another major link from the same lane in the current phase
160 // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
161 //
162 // check1c: when the lane has only one edge, we treat greenMinor as green as there would be no actuation otherwise
163 //
164 // check2: if there are two loops on subsequent lanes (joined tls) and the second one has a red link, the first loop may not be used
165
166 // also assign loops to link index for validation:
167 // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
168 const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
169 std::map<int, std::set<MSInductLoop*> > linkToLoops;
170 std::set<int> actuatedLinks;
171
172 std::vector<bool> neverMajor(numLinks, true);
173 for (const MSPhaseDefinition* phase : myPhases) {
174 const std::string& state = phase->getState();
175 for (int i = 0; i < numLinks; i++) {
176 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
177 neverMajor[i] = false;
178 }
179 }
180 }
181 std::vector<bool> oneLane(numLinks, false);
182 for (int i = 0; i < numLinks; i++) {
183 for (MSLane* lane : getLanesAt(i)) {
184 // only count motorized vehicle lanes
185 int numMotorized = 0;
186 for (MSLane* l : lane->getEdge().getLanes()) {
187 if ((l->getPermissions() & motorized) != 0) {
188 numMotorized++;
189 }
190 }
191 if (numMotorized == 1) {
192 oneLane[i] = true;
193 break;
194 }
195 }
196 }
197
198
199 for (const MSPhaseDefinition* phase : myPhases) {
200 std::set<MSInductLoop*> loops;
201 if (phase->minDuration != phase->maxDuration) {
202 // actuated phase
203 const std::string& state = phase->getState();
204 // collect indices of all green links for the phase
205 std::set<int> greenLinks;
206 // collect green links for each induction loops (in this phase)
207 std::map<MSInductLoop*, std::set<int> > loopLinks;
208
209 for (int i = 0; i < numLinks; i++) {
210 if (state[i] == LINKSTATE_TL_GREEN_MAJOR
211 || (state[i] == LINKSTATE_TL_GREEN_MINOR
212 && ((neverMajor[i] // check1a
213 && hasMajor(state, getLanesAt(i))) // check1b
214 || oneLane[i])) // check1c
215 ) {
216 greenLinks.insert(i);
217 actuatedLinks.insert(i);
218 }
219 #ifdef DEBUG_DETECTORS
220 //if (DEBUG_COND) {
221 // std::cout << " phase=" << myInductLoopsForPhase.size() << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
222 // << " loopLanes=";
223 // for (MSLane* lane: getLanesAt(i)) {
224 // if (laneInductLoopMap.count(lane) != 0) {
225 // std::cout << lane->getID() << " ";
226 // }
227 // }
228 // std::cout << "\n";
229 //}
230 #endif
231 for (MSLane* lane : getLanesAt(i)) {
232 if (laneInductLoopMap.count(lane) != 0) {
233 loopLinks[laneInductLoopMap[lane]].insert(i);
234 }
235 }
236 }
237 for (auto& item : loopLinks) {
238 bool usable = true;
239 // check1
240 for (int j : item.second) {
241 if (greenLinks.count(j) == 0) {
242 usable = false;
243 #ifdef DEBUG_DETECTORS
244 if (DEBUG_COND) {
245 std::cout << " phase=" << myInductLoopsForPhase.size() << " check1: loopLane=" << item.first->getLane()->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
246 }
247 #endif
248 break;
249 }
250 }
251 // check2
252 if (usable) {
253 const MSLane* loopLane = item.first->getLane();
254 for (MSLink* link : loopLane->getLinkCont()) {
255 const MSLane* next = link->getLane();
256 if (laneInductLoopMap.count(next) != 0) {
257 MSInductLoop* nextLoop = laneInductLoopMap[next];
258 for (int j : loopLinks[nextLoop]) {
259 if (greenLinks.count(j) == 0) {
260 usable = false;
261 #ifdef DEBUG_DETECTORS
262 if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " check2: loopLane=" << item.first->getLane()->getID()
263 << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
264 #endif
265 break;
266 }
267 }
268 }
269 }
270 }
271
272 if (usable) {
273 loops.insert(item.first);
274 #ifdef DEBUG_DETECTORS
275 //if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
276 #endif
277 for (int j : item.second) {
278 linkToLoops[j].insert(item.first);
279 }
280 }
281 }
282 if (loops.size() == 0) {
283 WRITE_WARNING("At actuated tlLogic '" + getID() + "', actuated phase " + toString(myInductLoopsForPhase.size()) + " has no controlling detector");
284 }
285 }
286 #ifdef DEBUG_DETECTORS
287 if (DEBUG_COND) {
288 std::cout << " phase=" << myInductLoopsForPhase.size() << " loops=" << joinNamedToString(loops, " ") << "\n";
289 }
290 //if (DEBUG_COND) {
291 // std::cout << " linkToLoops:\n";
292 // for (auto item : linkToLoops) {
293 // std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
294 // }
295 //}
296 #endif
297 myInductLoopsForPhase.push_back(std::vector<MSInductLoop*>(loops.begin(), loops.end()));
298 }
299 #ifdef DEBUG_DETECTORS
300 //if (DEBUG_COND) {
301 // std::cout << "final linkToLoops:\n";
302 // for (auto item : linkToLoops) {
303 // std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
304 // }
305 //}
306 #endif
307 for (int i : actuatedLinks) {
308 if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
309 && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
310 WRITE_WARNING("At actuated tlLogic '" + getID() + "', linkIndex " + toString(i) + " has no controlling detector");
311 }
312 }
313 }
314
315
316 SUMOTime
getMinimumMinDuration(MSLane * lane) const317 MSActuatedTrafficLightLogic::getMinimumMinDuration(MSLane* lane) const {
318 SUMOTime result = std::numeric_limits<SUMOTime>::max();
319 for (const MSPhaseDefinition* phase : myPhases) {
320 const std::string& state = phase->getState();
321 for (int i = 0; i < (int)state.size(); i++) {
322 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
323 for (MSLane* cand : getLanesAt(i)) {
324 if (lane == cand) {
325 if (phase->minDuration != phase->maxDuration) {
326 result = MIN2(result, phase->minDuration);
327 }
328 }
329 }
330 }
331 }
332 }
333 return result;
334 }
335
336 bool
hasMajor(const std::string & state,const LaneVector & lanes) const337 MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
338 for (int i = 0; i < (int)state.size(); i++) {
339 if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
340 for (MSLane* cand : getLanesAt(i)) {
341 for (MSLane* lane : lanes) {
342 if (lane == cand) {
343 return true;
344 }
345 }
346 }
347 }
348 }
349 return false;
350 }
351
352
353 // ------------ Switching and setting current rows
354 SUMOTime
trySwitch()355 MSActuatedTrafficLightLogic::trySwitch() {
356 // checks if the actual phase should be continued
357 // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
358 // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
359 const double detectionGap = gapControl();
360 if (detectionGap < std::numeric_limits<double>::max()) {
361 return duration(detectionGap);
362 }
363 // increment the index to the current phase
364 myStep++;
365 assert(myStep <= (int)myPhases.size());
366 if (myStep == (int)myPhases.size()) {
367 myStep = 0;
368 }
369 //stores the time the phase started
370 myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
371 // activate coloring
372 if (myShowDetectors && getCurrentPhaseDef().isGreenPhase()) {
373 for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
374 loop->setSpecialColor(&RGBColor::GREEN);
375 }
376 }
377
378 // set the next event
379 return getCurrentPhaseDef().minDuration;
380 }
381
382
383 // ------------ "actuated" algorithm methods
384 SUMOTime
duration(const double detectionGap) const385 MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
386 assert(getCurrentPhaseDef().isGreenPhase());
387 assert((int)myPhases.size() > myStep);
388 const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
389 // ensure that minimum duration is kept
390 SUMOTime newDuration = getCurrentPhaseDef().minDuration - actDuration;
391 // try to let the last detected vehicle pass the intersection (duration must be positive)
392 newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
393 // cut the decimal places to ensure that phases always have integer duration
394 if (newDuration % 1000 != 0) {
395 const SUMOTime totalDur = newDuration + actDuration;
396 newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
397 }
398 // ensure that the maximum duration is not exceeded
399 newDuration = MIN2(newDuration, getCurrentPhaseDef().maxDuration - actDuration);
400 return newDuration;
401 }
402
403
404 double
gapControl()405 MSActuatedTrafficLightLogic::gapControl() {
406 //intergreen times should not be lenghtend
407 assert((int)myPhases.size() > myStep);
408 double result = std::numeric_limits<double>::max();
409 if (MSGlobals::gUseMesoSim) {
410 return result;
411 }
412 // switch off active colors
413 if (myShowDetectors) {
414 for (MSInductLoop* loop : myInductLoops) {
415 loop->setSpecialColor(nullptr);
416 }
417 }
418 if (!getCurrentPhaseDef().isGreenPhase()) {
419 return result; // end current phase
420 }
421
422 // Checks, if the maxDuration is kept. No phase should longer send than maxDuration.
423 SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
424 if (actDuration >= getCurrentPhaseDef().maxDuration) {
425 return result; // end current phase
426 }
427
428 // now the gapcontrol starts
429 for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
430 loop->setSpecialColor(&RGBColor::GREEN);
431 const double actualGap = loop->getTimeSinceLastDetection();
432 if (actualGap < myMaxGap) {
433 result = MIN2(result, actualGap);
434 }
435 }
436 return result;
437 }
438
439
440
441 /****************************************************************************/
442
443