1 package org.opentrafficsim.road.gtu.lane.tactical.util;
2
3 import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.ATLEASTONE;
4 import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.POSITIVE;
5
6 import java.io.Serializable;
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.SortedMap;
11 import java.util.SortedSet;
12 import java.util.TreeMap;
13 import java.util.UUID;
14
15 import org.djunits.unit.AccelerationUnit;
16 import org.djunits.unit.DurationUnit;
17 import org.djunits.unit.LengthUnit;
18 import org.djunits.value.vdouble.scalar.Acceleration;
19 import org.djunits.value.vdouble.scalar.Duration;
20 import org.djunits.value.vdouble.scalar.Length;
21 import org.djunits.value.vdouble.scalar.Speed;
22 import org.djunits.value.vdouble.scalar.Time;
23 import org.opentrafficsim.core.gtu.GTUException;
24 import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
25 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
26 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
27 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDouble;
28 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDuration;
29 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeLength;
30 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
31 import org.opentrafficsim.core.network.Node;
32 import org.opentrafficsim.core.network.route.Route;
33 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
34 import org.opentrafficsim.road.gtu.lane.RoadGTUTypes;
35 import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
36 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
37 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
38 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
39 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
40 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
41 import org.opentrafficsim.road.gtu.lane.tactical.pt.BusSchedule;
42 import org.opentrafficsim.road.network.lane.CrossSectionLink;
43 import org.opentrafficsim.road.network.lane.conflict.BusStopConflictRule;
44 import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
45 import org.opentrafficsim.road.network.lane.conflict.ConflictType;
46 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
47
48 import nl.tudelft.simulation.language.Throw;
49
50 /**
51 * This class implements default behavior for intersection conflicts for use in tactical planners.
52 * <p>
53 * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
54 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
55 * <p>
56 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 3, 2016 <br>
57 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
58 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
59 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
60 */
61 // TODO do not ignore vehicles upstream of conflict if they have green
62 // TODO conflict over multiple lanes (longitudinal in own direction)
63 // TODO a) yielding while having priority happens only when leaders is standing still on conflict (then its useless...)
64 // b) two vehicles can remain upstream of merge if vehicle stands on merge but leaves some space to move
65 // probably 1 is yielding, and 1 is courtesy yielding as the other stands still
66 // c) they might start moving together and collide further down (no response to negative headway on merge)
67 public final class ConflictUtil
68 {
69
70 /** Minimum time gap between events. */
71 public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts.",
72 new Duration(0.000001, DurationUnit.SECOND), POSITIVE);
73
74 /** Stopping distance at conflicts. */
75 public static final ParameterTypeLength S0_CONF =
76 new ParameterTypeLength("s0 conf", "Stopping distance at conflicts.", new Length(1.5, LengthUnit.METER), POSITIVE);
77
78 /** Multiplication factor on time for conservative assessment. */
79 public static final ParameterTypeDouble TIME_FACTOR =
80 new ParameterTypeDouble("timeFactor", "Safety factor on estimated time.", 1.25, ATLEASTONE);
81
82 /** Area before stop line where one is considered arrived at the intersection. */
83 public static final ParameterTypeLength STOP_AREA =
84 new ParameterTypeLength("stopArea", "Area before stop line where one is considered arrived at the intersection.",
85 new Length(4, LengthUnit.METER), POSITIVE);
86
87 /** Parameter of how much time before departure a bus indicates its departure to get priority. */
88 public static final ParameterTypeDuration TI =
89 new ParameterTypeDuration("ti", "Indicator time before departure.", Duration.createSI(3.0), POSITIVE);
90
91 /** Time step for free acceleration anticipation. */
92 private static final Duration TIME_STEP = new Duration(0.5, DurationUnit.SI);
93
94 /**
95 * Do not instantiate.
96 */
97 private ConflictUtil()
98 {
99 //
100 }
101
102 /**
103 * Approach conflicts by applying appropriate acceleration (or deceleration). The model may yield for a vehicle even while
104 * having priority. Such a 'yield plan' is remembered in <tt>YieldPlans</tt>. By forwarding the same <tt>YieldPlans</tt> for
105 * a GTU consistency of such plans is provided. If any conflict is not accepted to pass, stopping before a more upstream
106 * conflict is applied if there not sufficient stopping length in between conflicts.
107 * @param behavioralCharacteristics behavioral characteristics
108 * @param conflicts set of conflicts to approach
109 * @param leaders leading vehicles
110 * @param carFollowingModel car-following model
111 * @param vehicleLength length of vehicle
112 * @param speed current speed
113 * @param acceleration current acceleration
114 * @param speedLimitInfo speed limit info
115 * @param conflictPlans set of plans for conflict
116 * @param gtu gtu
117 * @return acceleration appropriate for approaching the conflicts
118 * @throws GTUException in case of an unsupported conflict rule
119 * @throws ParameterException if a parameter is not defined or out of bounds
120 */
121 @SuppressWarnings("checkstyle:parameternumber")
122 public static Acceleration approachConflicts(final BehavioralCharacteristics behavioralCharacteristics,
123 final SortedSet<HeadwayConflict> conflicts, final SortedSet<HeadwayGTU> leaders,
124 final CarFollowingModel carFollowingModel, final Length vehicleLength, final Speed speed,
125 final Acceleration acceleration, final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans,
126 final LaneBasedGTU gtu) throws GTUException, ParameterException
127 {
128
129 conflictPlans.cleanPlans();
130
131 Acceleration a = Acceleration.POS_MAXVALUE;
132 if (conflicts.isEmpty())
133 {
134 return a;
135 }
136
137 List<Length> prevStarts = new ArrayList<>();
138 List<Length> prevEnds = new ArrayList<>();
139 List<Class<? extends ConflictRule>> conflictRuleTypes = new ArrayList<>();
140
141 for (HeadwayConflict conflict : conflicts)
142 {
143
144 // adjust acceleration for situations where stopping might not be required
145 if (conflict.isCrossing())
146 {
147 // avoid collision if crossing is occupied
148 a = Acceleration.min(a,
149 avoidCrossingCollision(behavioralCharacteristics, conflict, carFollowingModel, speed, speedLimitInfo));
150 }
151 else
152 {
153 // follow leading GTUs on merge or split
154 a = Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, behavioralCharacteristics,
155 carFollowingModel, speed, speedLimitInfo));
156 }
157 if (conflict.getDistance().lt0())
158 {
159 // ignore conflicts we are on (i.e. negative distance to start of conflict)
160 continue;
161 }
162
163 // indicator if bus
164 if (gtu.getStrategicalPlanner().getRoute() instanceof BusSchedule && gtu.getGTUType().isOfType(RoadGTUTypes.BUS)
165 && conflict.getConflictRuleType().equals(BusStopConflictRule.class))
166 {
167 BusSchedule busSchedule = (BusSchedule) gtu.getStrategicalPlanner().getRoute();
168 Time actualDeparture = busSchedule.getActualDepartureConflict(conflict.getId());
169 if (actualDeparture != null && actualDeparture.si < gtu.getSimulator().getSimulatorTime().getTime().si
170 + behavioralCharacteristics.getParameter(TI).si)
171 {
172 // TODO depending on left/right-hand traffic
173 conflictPlans.setIndicatorIntent(TurnIndicatorIntent.LEFT, conflict.getDistance());
174 }
175 }
176
177 // determine if we need to stop
178 boolean stop;
179 switch (conflict.getConflictPriority())
180 {
181 case PRIORITY:
182 {
183 stop = stopForPriorityConflict(conflict, leaders, speed, vehicleLength, behavioralCharacteristics,
184 conflictPlans);
185 break;
186 }
187 case GIVE_WAY:
188 {
189 stop = stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength,
190 behavioralCharacteristics, speedLimitInfo, carFollowingModel);
191 break;
192 }
193 case STOP:
194 {
195 stop = stopForStopConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
196 speedLimitInfo, carFollowingModel);
197 break;
198 }
199 case ALL_STOP:
200 {
201 stop = stopForAllStopConflict(conflict, conflictPlans);
202 break;
203 }
204 case SPLIT:
205 {
206 stop = false; // skipped anyway
207 break;
208 }
209 default:
210 {
211 throw new GTUException("Unsupported conflict rule encountered while approaching conflicts.");
212 }
213 }
214
215 // stop if required, account for upstream conflicts to keep clear
216 if (!conflict.getConflictType().equals(ConflictType.SPLIT))
217 {
218
219 if (stop)
220 {
221 prevStarts.add(conflict.getDistance());
222 conflictRuleTypes.add(conflict.getConflictRuleType());
223 // stop for first conflict looking upstream of this blocked conflict that allows sufficient space
224 int j = 0; // most upstream conflict if not in between conflicts
225 for (int i = prevEnds.size() - 1; i >= 0; i--) // downstream to upstream
226 {
227 // note, at this point prevStarts contains one more conflict than prevEnds
228 if (prevStarts.get(i + 1).minus(prevEnds.get(i))
229 .gt(passableDistance(vehicleLength, behavioralCharacteristics)))
230 {
231 j = i + 1;
232 break;
233 }
234 }
235
236 // TODO
237 // if this lowers our acceleration, we need to check if we are able to pass upstream conflicts still in time
238
239 // stop for j'th conflict, if deceleration is too strong, for next one
240 behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
241 Acceleration aCF = new Acceleration(-Double.MAX_VALUE, AccelerationUnit.SI);
242 while (aCF.si < -6.0 && j < prevStarts.size())
243 {
244 if (prevStarts.get(j).lt(behavioralCharacteristics.getParameter(S0_CONF)))
245 {
246 // TODO what to do when we happen to be in the stopping distance? Stopping might be reasonable,
247 // while car-following might give strong deceleration due to s < s0.
248 aCF = Acceleration.max(aCF, new Acceleration(-6.0, AccelerationUnit.SI));
249 }
250 else
251 {
252 Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
253 speedLimitInfo, prevStarts.get(j));
254 if (conflictRuleTypes.get(j).equals(BusStopConflictRule.class)
255 && aStop.lt(behavioralCharacteristics.getParameter(ParameterTypes.BCRIT).neg()))
256 {
257 aStop = Acceleration.POS_MAXVALUE;
258 }
259 aCF = Acceleration.max(aCF, aStop);
260 }
261 j++;
262 }
263 behavioralCharacteristics.resetParameter(ParameterTypes.S0);
264 a = Acceleration.min(a, aCF);
265 break;
266 }
267
268 // keep conflict clear (when stopping for another conflict), if there are conflicting vehicles
269 if (!conflict.getUpstreamConflictingGTUs().isEmpty())
270 {
271 prevStarts.add(conflict.getDistance());
272 conflictRuleTypes.add(conflict.getConflictRuleType());
273 prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
274 }
275 }
276
277 }
278
279 if (a.si < -6.0)
280 {
281 System.err.println("Deceleration from conflict util stronger than 6m/s^2.");
282 // return IGNORE;
283 }
284 return a;
285 }
286
287 /**
288 * Determines acceleration for following conflicting vehicles <i>on</i> a merge or split conflict.
289 * @param conflict merge or split conflict
290 * @param behavioralCharacteristics behavioral characteristics
291 * @param carFollowingModel car-following model
292 * @param speed current speed
293 * @param speedLimitInfo speed limit info
294 * @return acceleration for following conflicting vehicles <i>on</i> a merge or split conflict
295 * @throws ParameterException if a parameter is not given or out of bounds
296 */
297 private static Acceleration followConflictingLeaderOnMergeOrSplit(final HeadwayConflict conflict,
298 final BehavioralCharacteristics behavioralCharacteristics, final CarFollowingModel carFollowingModel,
299 final Speed speed, final SpeedLimitInfo speedLimitInfo) throws ParameterException
300 {
301 // ignore if no conflicting GTU's
302 if (conflict.getDownstreamConflictingGTUs().isEmpty())
303 {
304 return Acceleration.POS_MAXVALUE;
305 }
306 // get the most upstream GTU to consider
307 HeadwayGTU c = conflict.getDownstreamConflictingGTUs().first();
308 if (c.isAhead())
309 {
310 // conflict GTU completely downstream of conflict (i.e. regular car-following, ignore here)
311 return Acceleration.POS_MAXVALUE;
312 }
313 // conflict GTU (partially) on the conflict
314 // {@formatter:off}
315 // ______________________________________________
316 // ___ virtual headway | ___ |
317 // |___|(-----------------------)|___|(vehicle from south, on lane from south)
318 // _____________________________|_______|________
319 // / /
320 // / /
321 // {@formatter:on}
322 Length virtualHeadway = conflict.getDistance().plus(c.getOverlapRear());
323 if (virtualHeadway.le0() && conflict.getDistance().le0())
324 {
325 // TODO what about long conflicts where we need to follow the second conflicting downstream vehicle?
326 // conflict GTU downstream of start of conflict, but upstream of us
327 return Acceleration.POS_MAXVALUE;
328 }
329 // follow leader
330 SortedMap<Length, Speed> leaders = new TreeMap<>();
331 leaders.put(virtualHeadway, c.getSpeed());
332 Acceleration a = carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
333 // if conflicting GTU is partially upstream of the conflict and at (near) stand-still, stop for the conflict rather than
334 // following the tail of the conflicting GTU
335 if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
336 {
337 // {@formatter:off}
338 /*
339 * ______________________________________________
340 * ___ stop for conflict | |
341 * |___|(--------------------)| ___ |
342 * _____________________________|__/ /_|________
343 * / /__/ /
344 * / /
345 */
346 // {@formatter:on}
347 behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
348 Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed, speedLimitInfo,
349 conflict.getDistance());
350 behavioralCharacteristics.resetParameter(ParameterTypes.S0);
351 a = Acceleration.max(a, aStop); // max, which ever allows the largest acceleration
352 }
353 return a;
354 }
355
356 /**
357 * Determines an acceleration required to avoid a collision with GTUs <i>on</i> a crossing conflict.
358 * @param behavioralCharacteristics behavioral characteristics
359 * @param conflict conflict
360 * @param carFollowingModel car-following model
361 * @param speed current speed
362 * @param speedLimitInfo speed limit info
363 * @return acceleration required to avoid a collision
364 * @throws ParameterException if parameter is not defined
365 */
366 private static Acceleration avoidCrossingCollision(final BehavioralCharacteristics behavioralCharacteristics,
367 final HeadwayConflict conflict, final CarFollowingModel carFollowingModel, final Speed speed,
368 final SpeedLimitInfo speedLimitInfo) throws ParameterException
369 {
370
371 // TODO only within visibility
372 List<HeadwayGTU> conflictingGTUs = new ArrayList<>();
373 for (HeadwayGTU gtu : conflict.getUpstreamConflictingGTUs())
374 {
375 if (isOnRoute(conflict.getConflictingLink(), gtu))
376 {
377 // first upstream vehicle on route to this conflict
378 conflictingGTUs.add(gtu);
379 break;
380 }
381 }
382 for (HeadwayGTU gtu : conflict.getDownstreamConflictingGTUs())
383 {
384 if (gtu.isParallel())
385 {
386 conflictingGTUs.add(gtu);
387 }
388 else
389 {
390 // vehicles beyond conflict are not a thread
391 break;
392 }
393 }
394
395 if (conflictingGTUs.isEmpty())
396 {
397 return Acceleration.POS_MAXVALUE;
398 }
399
400 Acceleration a = Acceleration.POS_MAXVALUE;
401 for (HeadwayGTU conflictingGTU : conflictingGTUs)
402 {
403 AnticipationInfo tteC;
404 Length distance;
405 if (conflictingGTU.isParallel())
406 {
407 tteC = new AnticipationInfo(Duration.ZERO, conflictingGTU.getSpeed());
408 distance = conflictingGTU.getOverlapRear().abs().plus(conflictingGTU.getOverlap())
409 .plus(conflictingGTU.getOverlapFront().abs());
410 }
411 else
412 {
413 tteC = AnticipationInfo.anticipateMovement(conflictingGTU.getDistance(), conflictingGTU.getSpeed(),
414 Acceleration.ZERO);
415 distance = conflictingGTU.getDistance().plus(conflict.getLength()).plus(conflictingGTU.getLength());
416 }
417 AnticipationInfo ttcC = AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
418 AnticipationInfo tteO = AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
419 behavioralCharacteristics, carFollowingModel, speedLimitInfo, TIME_STEP);
420 // enter before cleared
421 // TODO safety factor?
422 if (tteC.getDuration().lt(tteO.getDuration()) && tteO.getDuration().lt(ttcC.getDuration()))
423 {
424 if (!conflictingGTU.getSpeed().eq0())
425 {
426 // solve parabolic speed profile s = v*t + .5*a*t*t, a =
427 double acc = 2 * (conflict.getDistance().si - speed.si * ttcC.getDuration().si)
428 / (ttcC.getDuration().si * ttcC.getDuration().si);
429 // time till zero speed > time to avoid conflict?
430 if (speed.si / -acc > ttcC.getDuration().si)
431 {
432 a = Acceleration.min(a, new Acceleration(acc, AccelerationUnit.SI));
433 }
434 else
435 {
436 // will reach zero speed ourselves
437 a = Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
438 speedLimitInfo, conflict.getDistance()));
439 }
440 }
441 // conflicting vehicle stand-still, ignore even at conflict
442 }
443 }
444 return a;
445 }
446
447 /**
448 * Approach a priority conflict. Stopping is applied to give way to conflicting traffic in case congestion is present on the
449 * own lane. This is courtesy yielding.
450 * @param conflict conflict to approach
451 * @param leaders leading vehicles in own lane
452 * @param speed current speed
453 * @param vehicleLength vehicle length
454 * @param behavioralCharacteristics behavioral characteristics
455 * @param yieldPlans set of plans for yielding with priority
456 * @return whether to stop for this conflict
457 * @throws ParameterException if parameter B is not defined
458 */
459 public static boolean stopForPriorityConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
460 final Speed speed, final Length vehicleLength, final BehavioralCharacteristics behavioralCharacteristics,
461 final ConflictPlans yieldPlans) throws ParameterException
462 {
463
464 if (leaders.isEmpty() || conflict.getUpstreamConflictingGTUs().isEmpty())
465 {
466 // no leader, or no conflicting vehicle
467 return false;
468 }
469
470 // Stop as long as some leader is standing still, and leader is not leaving sufficient space yet
471 // use start of conflict on merge, end of conflict on crossing
472 Length typeCorrection = conflict.isCrossing() ? conflict.getLength() : Length.ZERO;
473 // distance leader has to cover before we can pass the conflict
474 Length distance = conflict.getDistance().minus(leaders.first().getDistance())
475 .plus(passableDistance(vehicleLength, behavioralCharacteristics)).plus(typeCorrection);
476 if (distance.gt0())
477 {
478 Length required = conflict.getDistance().plus(typeCorrection)
479 .plus(passableDistance(vehicleLength, behavioralCharacteristics)); // for ourselves
480 for (HeadwayGTU leader : leaders)
481 {
482 if (leader.getSpeed().eq0())
483 {
484 // first stand-still leader is not fully upstream of the conflict (in that case, ignore), and does not
485 // allow sufficient space for all vehicles in between
486 return leader.getDistance().ge(conflict.getDistance()) && required.ge(leader.getDistance());
487 }
488 required = required // add required distance for leaders
489 .plus(passableDistance(leader.getLength(), leader.getBehavioralCharacteristics()));
490 }
491 }
492 return false;
493
494 }
495
496 /**
497 * Approach a give-way conflict.
498 * @param conflict conflict
499 * @param leaders leaders
500 * @param speed current speed
501 * @param acceleration current acceleration
502 * @param vehicleLength vehicle length
503 * @param behavioralCharacteristics behavioral characteristics
504 * @param speedLimitInfo speed limit info
505 * @param carFollowingModel car-following model
506 * @return whether to stop for this conflict
507 * @throws ParameterException if a parameter is not defined
508 */
509 @SuppressWarnings("checkstyle:parameternumber")
510 public static boolean stopForGiveWayConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
511 final Speed speed, final Acceleration acceleration, final Length vehicleLength,
512 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
513 final CarFollowingModel carFollowingModel) throws ParameterException
514 {
515
516 // TODO conflicting vehicle on crossing conflict, but will leave sooner then we enter, so no problem?
517 // TODO more generally, also upstream conflicting vehicles at crossings may leave the conflict before we enter
518 if (conflict.getConflictType().equals(ConflictType.CROSSING) && !conflict.getDownstreamConflictingGTUs().isEmpty()
519 && conflict.getDownstreamConflictingGTUs().first().isParallel())
520 {
521 // vehicle on the conflict
522 return true;
523 }
524
525 // Get data independent of conflicting vehicle
526 // parameters
527 Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B).neg();
528 double f = behavioralCharacteristics.getParameter(TIME_FACTOR);
529 Duration gap = behavioralCharacteristics.getParameter(MIN_GAP);
530 // time till conflict is cleared
531 Length distance = conflict.getDistance().plus(vehicleLength);
532 if (conflict.isCrossing())
533 {
534 // merge is cleared at start, crossing at end
535 distance = distance.plus(conflict.getLength());
536 }
537 // based on acceleration, limited by free acceleration
538 AnticipationInfo ttcOa = AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, behavioralCharacteristics,
539 carFollowingModel, speedLimitInfo, TIME_STEP);
540 // time till downstream vehicle will make the conflict passible, under constant speed or safe deceleration
541 AnticipationInfo ttpDz = null;
542 AnticipationInfo ttpDs = null;
543 if (conflict.isCrossing())
544 {
545 if (!leaders.isEmpty())
546 {
547 distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength())
548 .plus(passableDistance(vehicleLength, behavioralCharacteristics));
549 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
550 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
551 }
552 else
553 {
554 // no leader so conflict is passible within a duration of 0
555 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
556 ttpDs = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
557 }
558 }
559
560 List<HeadwayGTU> conflictingVehicles = new ArrayList<>();
561 if (!conflict.getUpstreamConflictingGTUs().isEmpty())
562 {
563 for (HeadwayGTU vehicle : conflict.getUpstreamConflictingGTUs())
564 {
565 if (conflict.getConflictingTrafficLightDistance() != null && !conflict.isPermitted()
566 && vehicle.getDistance().gt(conflict.getConflictingTrafficLightDistance()))
567 {
568 break;
569 }
570 if (isOnRoute(conflict.getConflictingLink(), vehicle))
571 {
572 conflictingVehicles.add(vehicle);
573 }
574 }
575 }
576 else if (conflict.getConflictingTrafficLightDistance() == null)
577 {
578 // none within visibility, assume a conflicting vehicle just outside of visibility driving at speed limit
579 try
580 {
581 HeadwayGTUSimple conflictGtu = new HeadwayGTUSimple("virtual " + UUID.randomUUID().toString(), RoadGTUTypes.CAR,
582 conflict.getConflictingVisibility(), new Length(4.0, LengthUnit.SI),
583 conflict.getConflictingSpeedLimit(), Acceleration.ZERO);
584 conflictingVehicles.add(conflictGtu);
585 }
586 catch (GTUException exception)
587 {
588 throw new RuntimeException("Could not create a virtual conflicting vehicle at visibility range.", exception);
589 }
590 }
591
592 // Do not stop if conflicting vehicle is standing still
593 if (conflictingVehicles.isEmpty() || conflictingVehicles.get(0).getSpeed().eq0())
594 {
595 return false;
596 }
597
598 // Loop over conflicting vehicles
599 for (HeadwayGTU conflictingVehicle : conflictingVehicles)
600 {
601
602 // time till conflict vehicle will enter, under free acceleration and safe deceleration
603 AnticipationInfo tteCa;
604 if (conflictingVehicle instanceof HeadwayGTUSimple)
605 {
606 tteCa = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
607 conflictingVehicle.getAcceleration());
608 }
609 else
610 {
611 BehavioralCharacteristics bc = conflictingVehicle.getBehavioralCharacteristics();
612 SpeedLimitInfo sli = conflictingVehicle.getSpeedLimitInfo();
613 CarFollowingModel cfm = conflictingVehicle.getCarFollowingModel();
614 // Constant acceleration creates inf at stand still, triggering passing trough a congested stream
615 tteCa = AnticipationInfo.anticipateMovementFreeAcceleration(conflictingVehicle.getDistance(),
616 conflictingVehicle.getSpeed(), bc, cfm, sli, TIME_STEP);
617 }
618 AnticipationInfo tteCs =
619 AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(), b);
620
621 // check gap
622 if (conflict.isMerge())
623 {
624
625 // Merge, will be each others followers, add time to overcome speed difference
626 double vSelf = ttcOa.getEndSpeed().si;
627 double speedDiff = conflictingVehicle.getSpeed().si - vSelf;
628 speedDiff = speedDiff > 0 ? speedDiff : 0;
629 Duration additionalTime = new Duration(speedDiff / -b.si, DurationUnit.SI);
630 // check if conflict vehicle will be upstream after that time, position beyond conflict after additional time
631 double followerFront = conflictingVehicle.getSpeed().si * ttcOa.getDuration().si
632 - conflictingVehicle.getDistance().si + (conflictingVehicle.getSpeed().si * additionalTime.si
633 + 0.5 * b.si * additionalTime.si * additionalTime.si);
634 double ownRear = vSelf * additionalTime.si; // constant speed after clearing
635 Duration tMax = behavioralCharacteristics.getParameter(ParameterTypes.TMAX);
636 Length s0 = behavioralCharacteristics.getParameter(ParameterTypes.S0);
637 // 1) will clear the conflict after the conflict vehicle enters
638 // 2) not sufficient time to overcome speed difference
639 // 3) conflict vehicle will be too near after adjusting speed
640 if (ttcOa.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
641 || ttcOa.getDuration().plus(additionalTime).multiplyBy(f).plus(gap).gt(tteCs.getDuration())
642 || ownRear < (followerFront + (tMax.si + gap.si) * vSelf + s0.si) * f)
643 {
644 return true;
645 }
646
647 }
648 else if (conflict.isCrossing())
649 {
650
651 // Crossing, stop if order of events is not ok
652 // Should go before the conflict vehicle
653 // 1) downstream vehicle must supply sufficient space before conflict vehicle will enter
654 // 2) must clear the conflict before the conflict vehicle will enter
655 // 3) if leader decelerates with b, conflict vehicle should be able to safely delay entering conflict
656 if (ttpDz.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
657 || ttcOa.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
658 || ttpDs.getDuration().multiplyBy(f).plus(gap).gt(tteCs.getDuration()))
659 {
660 return true;
661 }
662
663 }
664 else
665 {
666 throw new RuntimeException(
667 "Conflict is of unknown type " + conflict.getConflictType() + ", which is not merge nor a crossing.");
668 }
669 }
670
671 // No conflict vehicle triggered stopping
672 return false;
673
674 }
675
676 /**
677 * Approach a stop conflict. Currently this is equal to approaching a give-way conflict.
678 * @param conflict conflict
679 * @param leaders leaders
680 * @param speed current speed
681 * @param acceleration current acceleration
682 * @param vehicleLength vehicle length
683 * @param behavioralCharacteristics behavioral characteristics
684 * @param speedLimitInfo speed limit info
685 * @param carFollowingModel car-following model
686 * @return whether to stop for this conflict
687 * @throws ParameterException if a parameter is not defined
688 */
689 @SuppressWarnings("checkstyle:parameternumber")
690 public static boolean stopForStopConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
691 final Speed speed, final Acceleration acceleration, final Length vehicleLength,
692 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
693 final CarFollowingModel carFollowingModel) throws ParameterException
694 {
695 return stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
696 speedLimitInfo, carFollowingModel);
697 }
698
699 /**
700 * Approach an all-stop conflict.
701 * @param conflict conflict to approach
702 * @param conflictPlans set of plans for conflict
703 * @return whether to stop for this conflict
704 */
705 public static boolean stopForAllStopConflict(final HeadwayConflict conflict, final ConflictPlans conflictPlans)
706 {
707 // TODO all-stop behavior
708
709 if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
710 {
711 return false;
712 }
713
714 return false;
715 }
716
717 /**
718 * Returns whether the conflicting link is on the route of the given gtu.
719 * @param conflictingLink CrossSectionLink; conflicting link
720 * @param gtu HeadwayGTU; gtu
721 * @return whether the conflict is on the route of the given gtu
722 */
723 private static boolean isOnRoute(final CrossSectionLink conflictingLink, final HeadwayGTU gtu)
724 {
725 try
726 {
727 Route route = gtu.getRoute();
728 if (route == null)
729 {
730 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
731 return true;
732 }
733 Node startNode = conflictingLink.getStartNode();
734 Node endNode = conflictingLink.getEndNode();
735 return route.contains(startNode) && route.contains(endNode)
736 && Math.abs(route.indexOf(endNode) - route.indexOf(startNode)) == 1;
737 }
738 catch (@SuppressWarnings("unused") UnsupportedOperationException uoe)
739 {
740 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
741 return true;
742 }
743 }
744
745 /**
746 * Returns a speed dependent distance needed behind the leader to completely pass the conflict.
747 * @param vehicleLength vehicle length
748 * @param behavioralCharacteristics behavioral characteristics
749 * @return speed dependent distance needed behind the leader to completely pass the conflict
750 * @throws ParameterException if parameter is not available
751 */
752 private static Length passableDistance(final Length vehicleLength,
753 final BehavioralCharacteristics behavioralCharacteristics) throws ParameterException
754 {
755 return behavioralCharacteristics.getParameter(ParameterTypes.S0).plus(vehicleLength);
756 }
757
758 /**
759 * Holds the tactical plans of a driver considering conflicts. These are remembered for consistency. For instance, if the
760 * decision is made to yield as current deceleration suggests it's safe to do so, but the trajectory for stopping in front
761 * of the conflict results in deceleration slightly above what is considered safe deceleration, the plan should not be
762 * abandoned. Decelerations above what is considered safe deceleration may result due to numerical overshoot or other factor
763 * coming into play in car-following models. Many other examples exist where a driver sticks to a certain plan.
764 * <p>
765 * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
766 * <br>
767 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
768 * <p>
769 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 7, 2016 <br>
770 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
771 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
772 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
773 */
774 public static final class ConflictPlans implements Serializable
775 {
776
777 /** */
778 private static final long serialVersionUID = 20160811L;
779
780 /** Phases of navigating an all-stop intersection per intersection. */
781 private final HashMap<String, StopPhase> stopPhases = new HashMap<>();
782
783 /** Estimated arrival times of vehicles at all-stop intersection. */
784 private final HashMap<String, Time> arrivalTimes = new HashMap<>();
785
786 /** Indicator intent. */
787 private TurnIndicatorIntent indicatorIntent = TurnIndicatorIntent.NONE;
788
789 /** Distance to object causing turn indicator intent. */
790 private Length indicatorObjectDistance = null;
791
792 /**
793 * Clean any yield plan that was no longer kept active in the last evaluation of conflicts.
794 */
795 void cleanPlans()
796 {
797 this.indicatorIntent = TurnIndicatorIntent.NONE;
798 this.indicatorObjectDistance = null;
799 }
800
801 /**
802 * Sets the estimated arrival time of a GTU.
803 * @param gtu GTU
804 * @param time estimated arrival time
805 */
806 void setArrivalTime(final AbstractHeadwayGTU gtu, final Time time)
807 {
808 this.arrivalTimes.put(gtu.getId(), time);
809 }
810
811 /**
812 * Returns the estimated arrival time of given GTU.
813 * @param gtu GTU
814 * @return estimated arrival time of given GTU
815 */
816 Time getArrivalTime(final AbstractHeadwayGTU gtu)
817 {
818 return this.arrivalTimes.get(gtu.getId());
819 }
820
821 /**
822 * Sets the current phase to 'approach' for the given stop line.
823 * @param stopLine stop line
824 */
825 void setStopPhaseApproach(final HeadwayStopLine stopLine)
826 {
827 this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
828 }
829
830 /**
831 * Sets the current phase to 'yield' for the given stop line.
832 * @param stopLine stop line
833 * @throws RuntimeException if the phase was not set to approach before
834 */
835 void setStopPhaseYield(final HeadwayStopLine stopLine)
836 {
837 Throw.when(
838 !this.stopPhases.containsKey(stopLine.getId())
839 || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH),
840 RuntimeException.class, "Yield stop phase is set for stop line that was not approached.");
841 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
842 }
843
844 /**
845 * Sets the current phase to 'run' for the given stop line.
846 * @param stopLine stop line
847 * @throws RuntimeException if the phase was not set to approach before
848 */
849 void setStopPhaseRun(final HeadwayStopLine stopLine)
850 {
851 Throw.when(!this.stopPhases.containsKey(stopLine.getId()), RuntimeException.class,
852 "Run stop phase is set for stop line that was not approached.");
853 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
854 }
855
856 /**
857 * @param stopLine stop line
858 * @return whether the current phase is 'approach' for the given stop line
859 */
860 boolean isStopPhaseApproach(final HeadwayStopLine stopLine)
861 {
862 return this.stopPhases.containsKey(stopLine.getId())
863 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
864 }
865
866 /**
867 * @param stopLine stop line
868 * @return whether the current phase is 'yield' for the given stop line
869 */
870 boolean isStopPhaseYield(final HeadwayStopLine stopLine)
871 {
872 return this.stopPhases.containsKey(stopLine.getId())
873 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
874 }
875
876 /**
877 * @param stopLine stop line
878 * @return whether the current phase is 'run' for the given stop line
879 */
880 boolean isStopPhaseRun(final HeadwayStopLine stopLine)
881 {
882 return this.stopPhases.containsKey(stopLine.getId()) && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
883 }
884
885 /** {@inheritDoc} */
886 @Override
887 public String toString()
888 {
889 return "ConflictPlans";
890 }
891
892 /**
893 * @return indicatorIntent.
894 */
895 public TurnIndicatorIntent getIndicatorIntent()
896 {
897 return this.indicatorIntent;
898 }
899
900 /**
901 * @return indicatorObjectDistance.
902 */
903 public Length getIndicatorObjectDistance()
904 {
905 return this.indicatorObjectDistance;
906 }
907
908 /**
909 * @param intent indicator intent
910 * @param distance distance to object pertaining to the turn indicator intent
911 */
912 public void setIndicatorIntent(final TurnIndicatorIntent intent, final Length distance)
913 {
914 if (this.indicatorObjectDistance == null || this.indicatorObjectDistance.gt(distance))
915 {
916 this.indicatorIntent = intent;
917 this.indicatorObjectDistance = distance;
918 }
919 }
920
921 }
922
923 /**
924 * Phases of navigating an all-stop intersection.
925 * <p>
926 * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
927 * <br>
928 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
929 * <p>
930 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 30, 2016 <br>
931 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
932 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
933 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
934 */
935 private enum StopPhase
936 {
937 /** Approaching stop intersection. */
938 APPROACH,
939
940 /** Yielding for stop intersection. */
941 YIELD,
942
943 /** Running over stop intersection. */
944 RUN;
945 }
946
947 }