Eclipse SUMO - Simulation of Urban MObility
MSActuatedTrafficLightLogic.cpp
Go to the documentation of this file.
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 /****************************************************************************/
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>
35 #include <microsim/MSGlobals.h>
36 #include <microsim/MSNet.h>
37 #include "MSTrafficLightLogic.h"
39 #include <microsim/MSLane.h>
40 #include <microsim/MSEdge.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 // ===========================================================================
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 
71  myShowDetectors = StringUtils::toBool(getParameter("show-detectors", toString(OptionsCont::getOptions().getBool("tls.actuated.show-detectors"))));
72  myFile = FileHelpers::checkForRelativity(getParameter("file", "NUL"), basePath);
74  myVehicleTypes = getParameter("vTypes", "");
75 }
76 
78 
79 void
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 distance 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  std::map<MSInductLoop*, const MSLane*> inductLoopLaneMap; // in case loops are placed further upstream
111  double maxDetectorGap = 0;
112  for (i2 = myLanes.begin(); i2 != myLanes.end(); ++i2) {
113  const LaneVector& lanes = *i2;
114  for (i = lanes.begin(); i != lanes.end(); i++) {
115  MSLane* lane = (*i);
116  if (noVehicles(lane->getPermissions())) {
117  // do not build detectors on green verges or sidewalks
118  continue;
119  }
120  if (laneInductLoopMap.find(lane) != laneInductLoopMap.end()) {
121  // only build one detector per lane
122  continue;
123  }
124  const SUMOTime minDur = getMinimumMinDuration(lane);
125  if (minDur == std::numeric_limits<SUMOTime>::max()) {
126  // only build detector if this lane is relevant for an actuated phase
127  continue;
128  }
129  double length = lane->getLength();
130  double speed = lane->getSpeedLimit();
131  double inductLoopPosition = MIN2(
132  myDetectorGap * speed,
133  (STEPS2TIME(minDur) / myPassingTime + 0.5) * DEFAULT_LENGTH_WITH_GAP);
134 
135  // check whether the lane is long enough
136  double ilpos = length - inductLoopPosition;
137  MSLane* placementLane = lane;
138  while (ilpos < 0 && placementLane->getIncomingLanes().size() == 1) {
139  placementLane = placementLane->getLogicalPredecessorLane();
140  ilpos += placementLane->getLength();
141  }
142  if (ilpos < 0) {
143  ilpos = 0;
144  }
145  // Build the induct loop and set it into the container
146  std::string id = "TLS" + myID + "_" + myProgramID + "_InductLoopOn_" + lane->getID();
147  MSInductLoop* loop = static_cast<MSInductLoop*>(nb.createInductLoop(id, placementLane, ilpos, myVehicleTypes, myShowDetectors));
148  laneInductLoopMap[lane] = loop;
149  inductLoopLaneMap[loop] = lane;
150  myInductLoops.push_back(loop);
152  maxDetectorGap = MAX2(maxDetectorGap, length - ilpos);
153 
154  if (warn && floor(floor(inductLoopPosition / DEFAULT_LENGTH_WITH_GAP) * myPassingTime) > STEPS2TIME(minDur)) {
155  // warn if the minGap is insufficient to clear vehicles between stop line and detector
156  WRITE_WARNING("At actuated tlLogic '" + getID() + "', minDur " + time2string(minDur) + " is too short for a detector gap of " + toString(inductLoopPosition) + "m.");
157  warn = false;
158  }
159  }
160  }
161  // assign loops to phase index (myInductLoopsForPhase)
162  // 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
163  // greenMinor is ambiguous as vehicles may not be able to drive
164  // Under the following condition we allow actuation from minor link:
165  // check1a : the minor link is minor in all phases
166  // check1b : there is another major link from the same lane in the current phase
167  // (Under these conditions we assume that the minor link is unimportant and traffic is mostly for the major link)
168  //
169  // check1c: when the lane has only one edge, we treat greenMinor as green as there would be no actuation otherwise
170  //
171  // 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
172 
173  // also assign loops to link index for validation:
174  // check if all links from actuated phases (minDur != maxDur) have an inductionloop in at least one phase
175  const SVCPermissions motorized = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
176  std::map<int, std::set<MSInductLoop*> > linkToLoops;
177  std::set<int> actuatedLinks;
178 
179  std::vector<bool> neverMajor(numLinks, true);
180  for (const MSPhaseDefinition* phase : myPhases) {
181  const std::string& state = phase->getState();
182  for (int i = 0; i < numLinks; i++) {
183  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
184  neverMajor[i] = false;
185  }
186  }
187  }
188  std::vector<bool> oneLane(numLinks, false);
189  for (int i = 0; i < numLinks; i++) {
190  for (MSLane* lane : getLanesAt(i)) {
191  // only count motorized vehicle lanes
192  int numMotorized = 0;
193  for (MSLane* l : lane->getEdge().getLanes()) {
194  if ((l->getPermissions() & motorized) != 0) {
195  numMotorized++;
196  }
197  }
198  if (numMotorized == 1) {
199  oneLane[i] = true;
200  break;
201  }
202  }
203  }
204 
205 
206  for (const MSPhaseDefinition* phase : myPhases) {
207  std::set<MSInductLoop*> loops;
208  if (phase->minDuration != phase->maxDuration) {
209  // actuated phase
210  const std::string& state = phase->getState();
211  // collect indices of all green links for the phase
212  std::set<int> greenLinks;
213  // collect green links for each induction loops (in this phase)
214  std::map<MSInductLoop*, std::set<int> > loopLinks;
215 
216  for (int i = 0; i < numLinks; i++) {
217  if (state[i] == LINKSTATE_TL_GREEN_MAJOR
218  || (state[i] == LINKSTATE_TL_GREEN_MINOR
219  && ((neverMajor[i] // check1a
220  && hasMajor(state, getLanesAt(i))) // check1b
221  || oneLane[i])) // check1c
222  ) {
223  greenLinks.insert(i);
224  actuatedLinks.insert(i);
225  }
226 #ifdef DEBUG_DETECTORS
227  //if (DEBUG_COND) {
228  // std::cout << " phase=" << myInductLoopsForPhase.size() << " i=" << i << " state=" << state[i] << " green=" << greenLinks.count(i) << " oneLane=" << oneLane[i]
229  // << " loopLanes=";
230  // for (MSLane* lane: getLanesAt(i)) {
231  // if (laneInductLoopMap.count(lane) != 0) {
232  // std::cout << lane->getID() << " ";
233  // }
234  // }
235  // std::cout << "\n";
236  //}
237 #endif
238  for (MSLane* lane : getLanesAt(i)) {
239  if (laneInductLoopMap.count(lane) != 0) {
240  loopLinks[laneInductLoopMap[lane]].insert(i);
241  }
242  }
243  }
244  for (auto& item : loopLinks) {
245  MSInductLoop* loop = item.first;
246  const MSLane* loopLane = inductLoopLaneMap[loop];
247  bool usable = true;
248  // check1
249  for (int j : item.second) {
250  if (greenLinks.count(j) == 0) {
251  usable = false;
252 #ifdef DEBUG_DETECTORS
253  if (DEBUG_COND) {
254  std::cout << " phase=" << myInductLoopsForPhase.size() << " check1: loopLane=" << loopLane->getID() << " notGreen=" << j << " oneLane[j]=" << oneLane[j] << "\n";
255  }
256 #endif
257  break;
258  }
259  }
260  // check2
261  if (usable) {
262  for (MSLink* link : loopLane->getLinkCont()) {
263  const MSLane* next = link->getLane();
264  if (laneInductLoopMap.count(next) != 0) {
265  MSInductLoop* nextLoop = laneInductLoopMap[next];
266  for (int j : loopLinks[nextLoop]) {
267  if (greenLinks.count(j) == 0) {
268  usable = false;
269 #ifdef DEBUG_DETECTORS
270  if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " check2: loopLane=" << loopLane->getID()
271  << " nextLane=" << next->getID() << " nextLink=" << j << " nextState=" << state[j] << "\n";
272 #endif
273  break;
274  }
275  }
276  }
277  }
278  }
279 
280  if (usable) {
281  loops.insert(item.first);
282 #ifdef DEBUG_DETECTORS
283  //if (DEBUG_COND) std::cout << " phase=" << myInductLoopsForPhase.size() << " usableLoops=" << item.first->getID() << " links=" << joinToString(item.second, " ") << "\n";
284 #endif
285  for (int j : item.second) {
286  linkToLoops[j].insert(item.first);
287  }
288  }
289  }
290  if (loops.size() == 0) {
291  WRITE_WARNING("At actuated tlLogic '" + getID() + "', actuated phase " + toString(myInductLoopsForPhase.size()) + " has no controlling detector");
292  }
293  }
294 #ifdef DEBUG_DETECTORS
295  if (DEBUG_COND) {
296  std::cout << " phase=" << myInductLoopsForPhase.size() << " loops=" << joinNamedToString(loops, " ") << "\n";
297  }
298  //if (DEBUG_COND) {
299  // std::cout << " linkToLoops:\n";
300  // for (auto item : linkToLoops) {
301  // std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
302  // }
303  //}
304 #endif
305  myInductLoopsForPhase.push_back(std::vector<MSInductLoop*>(loops.begin(), loops.end()));
306  }
307 #ifdef DEBUG_DETECTORS
308  //if (DEBUG_COND) {
309  // std::cout << "final linkToLoops:\n";
310  // for (auto item : linkToLoops) {
311  // std::cout << " link=" << item.first << " loops=" << joinNamedToString(item.second, " ") << "\n";
312  // }
313  //}
314 #endif
315  for (int i : actuatedLinks) {
316  if (linkToLoops[i].size() == 0 && myLinks[i].size() > 0
317  && (myLinks[i].front()->getLaneBefore()->getPermissions() & motorized) != 0) {
318  WRITE_WARNING("At actuated tlLogic '" + getID() + "', linkIndex " + toString(i) + " has no controlling detector");
319  }
320  }
321 }
322 
323 
324 SUMOTime
326  SUMOTime result = std::numeric_limits<SUMOTime>::max();
327  for (const MSPhaseDefinition* phase : myPhases) {
328  const std::string& state = phase->getState();
329  for (int i = 0; i < (int)state.size(); i++) {
330  if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
331  for (MSLane* cand : getLanesAt(i)) {
332  if (lane == cand) {
333  if (phase->minDuration != phase->maxDuration) {
334  result = MIN2(result, phase->minDuration);
335  }
336  }
337  }
338  }
339  }
340  }
341  return result;
342 }
343 
344 bool
345 MSActuatedTrafficLightLogic::hasMajor(const std::string& state, const LaneVector& lanes) const {
346  for (int i = 0; i < (int)state.size(); i++) {
347  if (state[i] == LINKSTATE_TL_GREEN_MAJOR) {
348  for (MSLane* cand : getLanesAt(i)) {
349  for (MSLane* lane : lanes) {
350  if (lane == cand) {
351  return true;
352  }
353  }
354  }
355  }
356  }
357  return false;
358 }
359 
360 
361 // ------------ Switching and setting current rows
362 SUMOTime
364  // checks if the actual phase should be continued
365  // @note any vehicles which arrived during the previous phases which are now waiting between the detector and the stop line are not
366  // considere here. RiLSA recommends to set minDuration in a way that lets all vehicles pass the detector
367  const double detectionGap = gapControl();
368  if (detectionGap < std::numeric_limits<double>::max()) {
369  return duration(detectionGap);
370  }
371  // increment the index to the current phase
372  myStep++;
373  assert(myStep <= (int)myPhases.size());
374  if (myStep == (int)myPhases.size()) {
375  myStep = 0;
376  }
377  //stores the time the phase started
379  // activate coloring
380  if (myShowDetectors && getCurrentPhaseDef().isGreenPhase()) {
381  for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
382  loop->setSpecialColor(&RGBColor::GREEN);
383  }
384  }
385 
386  // set the next event
388 }
389 
390 
391 // ------------ "actuated" algorithm methods
392 SUMOTime
393 MSActuatedTrafficLightLogic::duration(const double detectionGap) const {
394  assert(getCurrentPhaseDef().isGreenPhase());
395  assert((int)myPhases.size() > myStep);
396  const SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
397  // ensure that minimum duration is kept
398  SUMOTime newDuration = getCurrentPhaseDef().minDuration - actDuration;
399  // try to let the last detected vehicle pass the intersection (duration must be positive)
400  newDuration = MAX3(newDuration, TIME2STEPS(myDetectorGap - detectionGap), SUMOTime(1));
401  // cut the decimal places to ensure that phases always have integer duration
402  if (newDuration % 1000 != 0) {
403  const SUMOTime totalDur = newDuration + actDuration;
404  newDuration = (totalDur / 1000 + 1) * 1000 - actDuration;
405  }
406  // ensure that the maximum duration is not exceeded
407  newDuration = MIN2(newDuration, getCurrentPhaseDef().maxDuration - actDuration);
408  return newDuration;
409 }
410 
411 
412 double
414  //intergreen times should not be lenghtend
415  assert((int)myPhases.size() > myStep);
416  double result = std::numeric_limits<double>::max();
418  return result;
419  }
420  // switch off active colors
421  if (myShowDetectors) {
422  for (MSInductLoop* loop : myInductLoops) {
423  loop->setSpecialColor(nullptr);
424  }
425  }
426  if (!getCurrentPhaseDef().isGreenPhase()) {
427  return result; // end current phase
428  }
429 
430  // Checks, if the maxDuration is kept. No phase should longer send than maxDuration.
431  SUMOTime actDuration = MSNet::getInstance()->getCurrentTimeStep() - myPhases[myStep]->myLastSwitch;
432  if (actDuration >= getCurrentPhaseDef().maxDuration) {
433  return result; // end current phase
434  }
435 
436  // now the gapcontrol starts
437  for (MSInductLoop* loop : myInductLoopsForPhase[myStep]) {
438  loop->setSpecialColor(&RGBColor::GREEN);
439  const double actualGap = loop->getTimeSinceLastDetection();
440  if (actualGap < myMaxGap) {
441  result = MIN2(result, actualGap);
442  }
443  }
444  return result;
445 }
446 
447 
448 void
450  myShowDetectors = show;
451  for (MSInductLoop* loop : myInductLoops) {
452  loop->setVisible(myShowDetectors);
453  }
454 }
455 
456 /****************************************************************************/
457 
The link has green light, may pass.
MSLane * getLogicalPredecessorLane() const
get the most likely precedecessor lane (sorted using by_connections_to_sorter). The result is cached ...
Definition: MSLane.cpp:2532
Builds detectors for microsim.
MSEdge & getEdge() const
Returns the lane&#39;s edge.
Definition: MSLane.h:670
long long int SUMOTime
Definition: SUMOTime.h:35
alternative tag for e1 detector
std::string joinNamedToString(const std::set< T *, C > &ns, const T_BETWEEN &between)
Definition: ToString.h:281
#define DEBUG_COND
The link has green light, has to brake.
void init(NLDetectorBuilder &nb)
Initialises the tls with information about incoming lanes.
vehicle is a bicycle
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
Phases myPhases
The list of phases this logic uses.
std::string time2string(SUMOTime t)
Definition: SUMOTime.cpp:65
const std::vector< MSLane * > & getLanes() const
Returns this edge&#39;s lanes.
Definition: MSEdge.h:165
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:168
T MAX2(T a, T b)
Definition: StdDefs.h:80
std::string myFile
The output file for generated detectors.
double getLength() const
Returns the lane&#39;s length.
Definition: MSLane.h:541
#define DEFAULT_DETECTOR_GAP
const std::string & getID() const
Returns the id.
Definition: Named.h:77
#define TIME2STEPS(x)
Definition: SUMOTime.h:59
T MAX3(T a, T b, T c)
Definition: StdDefs.h:94
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:239
SUMOTime duration(const double detectionGap) const
Returns the minimum duration of the current phase.
const MSPhaseDefinition & getCurrentPhaseDef() const
Returns the definition of the current phase.
A fixed traffic light logic.
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:58
LaneVectorVector myLanes
The list of LaneVectors; each vector contains the incoming lanes that belong to the same link index...
InductLoopMap myInductLoopsForPhase
A map from phase to induction loops to be used for gap control.
const LaneVector & getLanesAt(int i) const
Returns the list of lanes that are controlled by the signals at the given position.
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter ...
A class that stores and controls tls and switching of their programs.
std::string myVehicleTypes
Whether detector output separates by vType.
virtual MSDetectorFileOutput * createInductLoop(const std::string &id, MSLane *lane, double pos, const std::string &vTypes, bool show=true)
Creates an instance of an e1 detector using the given values.
static const RGBColor GREEN
Definition: RGBColor.h:191
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:48
bool hasMajor(const std::string &state, const LaneVector &lanes) const
return whether there is a major link from the given lane in the given phase
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter ...
virtual void init(NLDetectorBuilder &nb)
Initialises the tls with information about incoming lanes.
SUMOTime getMinimumMinDuration(MSLane *lane) const
get the minimum min duration for all stretchable phases that affect the given lane ...
#define DEFAULT_PASSING_TIME
SVCPermissions getPermissions() const
Returns the vehicle class permissions for this lane.
Definition: MSLane.h:549
double getSpeedLimit() const
Returns the lane&#39;s maximum allowed speed.
Definition: MSLane.h:533
#define STEPS2TIME(x)
Definition: SUMOTime.h:57
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:284
T MIN2(T a, T b)
Definition: StdDefs.h:74
double myMaxGap
The maximum gap to check in seconds.
void add(SumoXMLTag type, MSDetectorFileOutput *d, const std::string &device, SUMOTime splInterval, SUMOTime begin=-1)
Adds a detector/output combination into the containers.
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition: MSNet.h:400
#define DEFAULT_LENGTH_WITH_GAP
const std::string myProgramID
The id of the logic.
std::vector< MSPhaseDefinition * > Phases
Definition of a list of phases, being the junction logic.
LinkVectorVector myLinks
The list of LinkVectors; each vector contains the links that belong to the same link index...
std::string myID
The name of the object.
Definition: Named.h:134
std::vector< MSLane * > LaneVector
Definition of the list of arrival lanes subjected to this tls.
double gapControl()
Return the minimum detection gap of all detectors if the current phase should be extended and double:...
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory...
bool noVehicles(SVCPermissions permissions)
Returns whether an edge with the given permission forbids vehicles.
const std::string getParameter(const std::string &key, const std::string &defaultValue="") const
Returns the value for a given key.
SUMOTime minDuration
The minimum duration of the phase.
double myDetectorGap
The detector distance in seconds.
double myPassingTime
The passing time used in seconds.
const MSLinkCont & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.cpp:2099
MSActuatedTrafficLightLogic(MSTLLogicControl &tlcontrol, const std::string &id, const std::string &programID, const MSSimpleTrafficLightLogic::Phases &phases, int step, SUMOTime delay, const std::map< std::string, std::string > &parameter, const std::string &basePath)
Constructor.
std::vector< MSInductLoop * > myInductLoops
static bool gUseMesoSim
Definition: MSGlobals.h:91
SUMOTime myFreq
The frequency for aggregating detector output.
The definition of a single phase of a tls logic.
Representation of a lane in the micro simulation.
Definition: MSLane.h:83
#define DEFAULT_MAX_GAP
bool myShowDetectors
Whether the detectors shall be shown in the GUI.
SUMOTime trySwitch()
Switches to the next phase.
An unextended detector measuring at a fixed position on a fixed lane.
Definition: MSInductLoop.h:64