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.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.SortedSet;
16 import java.util.TreeMap;
17
18 import org.djunits.unit.AccelerationUnit;
19 import org.djunits.unit.LengthUnit;
20 import org.djunits.unit.TimeUnit;
21 import org.djunits.value.vdouble.scalar.Acceleration;
22 import org.djunits.value.vdouble.scalar.Duration;
23 import org.djunits.value.vdouble.scalar.Length;
24 import org.djunits.value.vdouble.scalar.Speed;
25 import org.djunits.value.vdouble.scalar.Time;
26 import org.opentrafficsim.core.gtu.GTUException;
27 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
28 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
29 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDouble;
30 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDuration;
31 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeLength;
32 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
33 import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
34 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
35 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
36 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
37 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
38
39 import nl.tudelft.simulation.language.Throw;
40
41
42
43
44
45
46
47
48
49
50
51
52 public final class ConflictUtil
53 {
54
55
56 public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts.",
57 new Duration(1.0, TimeUnit.SECOND), POSITIVE);
58
59
60 public static final ParameterTypeDouble TIME_FACTOR = new ParameterTypeDouble("timeFactor",
61 "Safety factor on estimated time.", 1.25, ATLEASTONE);
62
63
64 public static final ParameterTypeLength STOP_AREA = new ParameterTypeLength("stopArea",
65 "Area before stop line where one is considered arrived at the intersection.", new Length(4, LengthUnit.METER),
66 POSITIVE);
67
68
69
70
71 private ConflictUtil()
72 {
73
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 @SuppressWarnings("checkstyle:parameternumber")
94 public static Acceleration approachConflicts(final BehavioralCharacteristics behavioralCharacteristics,
95 final SortedSet<HeadwayConflict> conflicts, final SortedSet<AbstractHeadwayGTU> leaders,
96 final CarFollowingModel carFollowingModel, final Length vehicleLength, final Speed speed,
97 final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans) throws GTUException, ParameterException
98 {
99
100 Length stopLength = behavioralCharacteristics.getParameter(ParameterTypes.S0).plus(vehicleLength);
101 List<Length> prevStarts = new ArrayList<>();
102 List<Length> prevEnds = new ArrayList<>();
103 Acceleration a = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
104 conflictPlans.cleanYieldPlans();
105
106 for (HeadwayConflict conflict : conflicts)
107 {
108
109
110 if (conflict.isCrossing())
111 {
112
113 a =
114 Acceleration.min(a, avoidCrossingCollision(behavioralCharacteristics, conflict, carFollowingModel,
115 speed, speedLimitInfo));
116 }
117 else
118 {
119
120 a =
121 Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, behavioralCharacteristics,
122 carFollowingModel, speed, speedLimitInfo));
123 }
124
125
126 boolean stop;
127 switch (conflict.getConflictRule())
128 {
129 case PRIORITY:
130 {
131 stop =
132 stopForPriorityConflict(conflict, leaders, speed, stopLength, vehicleLength,
133 behavioralCharacteristics, conflictPlans);
134 break;
135 }
136 case GIVE_WAY:
137 {
138 stop =
139 stopForGiveWayConflict(conflict, leaders, speed, stopLength, vehicleLength,
140 behavioralCharacteristics, speedLimitInfo, carFollowingModel);
141 break;
142 }
143 case STOP:
144 {
145 stop =
146 stopForStopConflict(conflict, leaders, speed, stopLength, vehicleLength, behavioralCharacteristics,
147 speedLimitInfo, carFollowingModel);
148 break;
149 }
150 case ALL_STOP:
151 {
152 stop = stopForAllStopConflict(conflict, conflictPlans);
153 break;
154 }
155 default:
156 {
157 throw new GTUException("Unsupported conflict rule encountered while approaching conflicts.");
158 }
159 }
160
161
162 prevStarts.add(conflict.getDistance());
163 if (stop)
164 {
165
166 int j = 0;
167 for (int i = prevEnds.size() - 1; i >= 0; i--)
168 {
169
170 if (prevStarts.get(i + 1).minus(prevEnds.get(i)).gt(stopLength))
171 {
172 j = i + 1;
173 break;
174 }
175 }
176
177 return Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
178 speedLimitInfo, prevStarts.get(j)));
179 }
180 prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
181
182 }
183
184 return a;
185 }
186
187
188
189
190
191
192
193
194
195
196
197 private static Acceleration followConflictingLeaderOnMergeOrSplit(final HeadwayConflict conflict,
198 final BehavioralCharacteristics behavioralCharacteristics, final CarFollowingModel carFollowingModel,
199 final Speed speed, final SpeedLimitInfo speedLimitInfo) throws ParameterException
200 {
201
202 if (conflict.getDownstreamConflictingGTUs().isEmpty())
203 {
204 return new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
205 }
206
207 AbstractHeadwayGTU c = conflict.getDownstreamConflictingGTUs().first();
208 if (c.isAhead())
209 {
210
211 return new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
212 }
213
214
215
216
217
218
219
220
221
222
223 Length virtualHeadway = conflict.getDistance().plus(c.getOverlapRear());
224
225 SortedMap<Length, Speed> leaders = new TreeMap<>();
226 leaders.put(virtualHeadway, c.getSpeed());
227 Acceleration a = carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
228
229
230 if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
231 {
232
233
234
235
236
237
238
239
240
241
242 Acceleration aStop =
243 CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed, speedLimitInfo, conflict
244 .getDistance());
245 a = Acceleration.max(a, aStop);
246 }
247 return a;
248 }
249
250
251
252
253
254
255
256
257
258
259
260 private static Acceleration avoidCrossingCollision(final BehavioralCharacteristics behavioralCharacteristics,
261 final HeadwayConflict conflict, final CarFollowingModel carFollowingModel, final Speed speed,
262 final SpeedLimitInfo speedLimitInfo) throws ParameterException
263 {
264 if (!conflict.getDownstreamConflictingGTUs().isEmpty())
265 {
266 AbstractHeadwayGTU conflictingGTU = conflict.getDownstreamConflictingGTUs().first();
267 if (conflictingGTU.isParallel())
268 {
269 Length distance =
270 conflictingGTU.getOverlapRear().abs().plus(conflictingGTU.getOverlap()).plus(
271 conflictingGTU.getOverlapFront().abs());
272 AnticipationInfo ttcC =
273 AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
274 AnticipationInfo tteO =
275 AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
276 behavioralCharacteristics, carFollowingModel, speedLimitInfo, new Duration(.5, TimeUnit.SI));
277
278 if (tteO.getDuration().lt(ttcC.getDuration()))
279 {
280 if (!conflictingGTU.getSpeed().eq(Speed.ZERO))
281 {
282
283 double acc =
284 2 * (conflict.getDistance().si - speed.si * ttcC.getDuration().si)
285 / (ttcC.getDuration().si * ttcC.getDuration().si);
286
287 if (speed.si / -acc > ttcC.getDuration().si)
288 {
289 return new Acceleration(acc, AccelerationUnit.SI);
290 }
291 }
292
293 return CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed, speedLimitInfo,
294 conflict.getDistance());
295 }
296 }
297 }
298 return new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 private static boolean stopForPriorityConflict(final HeadwayConflict conflict,
315 final SortedSet<AbstractHeadwayGTU> leaders, final Speed speed, final Length stopLength, final Length vehicleLength,
316 final BehavioralCharacteristics behavioralCharacteristics, final ConflictPlans yieldPlans) throws ParameterException
317 {
318 if (leaders.isEmpty() || conflict.getUpstreamConflictingGTUs().isEmpty()
319 || conflict.getUpstreamConflictingGTUs().first().getSpeed().eq(Speed.ZERO))
320 {
321
322 return false;
323 }
324
325
326
327 Length distance = conflict.getDistance().plus(vehicleLength);
328 if (conflict.isCrossing())
329 {
330
331 distance = distance.plus(conflict.getLength());
332 }
333 AnticipationInfo ttcC = AnticipationInfo.anticipateMovement(distance, speed, Acceleration.ZERO);
334
335 distance = distance.minus(leaders.first().getDistance()).minus(vehicleLength).plus(stopLength);
336 AnticipationInfo ttpD = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
337
338 if (ttpD.getDuration().ge(ttcC.getDuration()))
339 {
340
341
342
343
344 if (yieldPlans.isYieldPlan(conflict, leaders.first()))
345 {
346 return false;
347 }
348
349
350 if (!yieldPlans.isYieldPlan(conflict, conflict.getUpstreamConflictingGTUs().first())
351 && conflict.getUpstreamConflictingGTUs().first().getSpeed().equals(Speed.ZERO))
352 {
353 Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B);
354 Acceleration bReq =
355 new Acceleration(.5 * speed.si * speed.si / conflict.getDistance().si, AccelerationUnit.SI);
356 if (bReq.gt(b))
357 {
358
359 return false;
360 }
361 }
362
363 yieldPlans.setYieldPlan(conflict, conflict.getUpstreamConflictingGTUs().first());
364 return true;
365 }
366
367 return false;
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 @SuppressWarnings("checkstyle:parameternumber")
384 private static boolean stopForGiveWayConflict(final HeadwayConflict conflict,
385 final SortedSet<AbstractHeadwayGTU> leaders, final Speed speed, final Length stopLength, final Length vehicleLength,
386 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
387 final CarFollowingModel carFollowingModel) throws ParameterException
388 {
389
390
391
392
393
394 Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B).multiplyBy(-1.0);
395 double f = behavioralCharacteristics.getParameter(TIME_FACTOR);
396 Duration gap = behavioralCharacteristics.getParameter(MIN_GAP);
397
398 Length distance = conflict.getDistance().plus(vehicleLength);
399 if (conflict.isCrossing())
400 {
401
402 distance = distance.plus(conflict.getLength());
403 }
404 AnticipationInfo ttcO =
405 AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, behavioralCharacteristics,
406 carFollowingModel, speedLimitInfo, new Duration(.5, TimeUnit.SI));
407
408 AnticipationInfo ttpDz = null;
409 AnticipationInfo ttpDs = null;
410 if (conflict.isCrossing())
411 {
412 if (!leaders.isEmpty())
413 {
414 distance =
415 conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength()).plus(stopLength);
416 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
417 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
418 }
419 else
420 {
421
422 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
423 ttpDs = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
424 }
425 }
426
427
428 ArrayList<Length> confDistance = new ArrayList<>();
429 ArrayList<Speed> confSpeed = new ArrayList<>();
430 ArrayList<Acceleration> confAcceleration = new ArrayList<>();
431 if (!conflict.getUpstreamConflictingGTUs().isEmpty())
432 {
433 for (AbstractHeadwayGTU conflictingVehicle : conflict.getUpstreamConflictingGTUs())
434 {
435 confDistance.add(conflictingVehicle.getDistance());
436 confSpeed.add(conflictingVehicle.getSpeed());
437 confAcceleration.add(conflictingVehicle.getAcceleration());
438 }
439 }
440 else
441 {
442
443 confDistance.add(conflict.getConflictingVisibility());
444 confSpeed.add(conflict.getConflictingSpeedLimit());
445 confAcceleration.add(Acceleration.ZERO);
446 }
447
448
449 for (int i = 0; i < confDistance.size(); i++)
450 {
451
452
453 AnticipationInfo tteCc =
454 AnticipationInfo.anticipateMovement(confDistance.get(i), confSpeed.get(i), confAcceleration.get(i));
455 AnticipationInfo tteCs = AnticipationInfo.anticipateMovement(confDistance.get(i), confSpeed.get(i), b);
456
457
458 if (conflict.isMerge())
459 {
460
461
462 double vConflicting = confSpeed.get(i).si + b.si * ttcO.getDuration().si;
463 double vSelf = ttcO.getEndSpeed().si;
464 double speedDiff = vConflicting - vSelf;
465 speedDiff = speedDiff > 0 ? speedDiff : 0;
466 Duration additionalTime = new Duration(speedDiff / -b.si, TimeUnit.SI);
467
468
469 if (ttcO.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
470 || ttcO.getDuration().plus(additionalTime).multiplyBy(f).plus(gap).gt(tteCs.getDuration()))
471 {
472 return true;
473 }
474
475 }
476 else if (conflict.isCrossing())
477 {
478
479
480
481
482
483 if (ttpDz.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
484 || ttcO.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
485 || ttpDs.getDuration().multiplyBy(f).plus(gap).gt(tteCs.getDuration()))
486 {
487 return true;
488 }
489
490 }
491 else
492 {
493 throw new RuntimeException("Conflict is of unknown type " + conflict.getConflictType()
494 + ", which is not merge nor crossing.");
495 }
496 }
497
498
499 return false;
500
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 @SuppressWarnings("checkstyle:parameternumber")
517 private static boolean stopForStopConflict(final HeadwayConflict conflict, final SortedSet<AbstractHeadwayGTU> leaders,
518 final Speed speed, final Length stopLength, final Length vehicleLength,
519 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
520 final CarFollowingModel carFollowingModel) throws ParameterException
521 {
522 return stopForGiveWayConflict(conflict, leaders, speed, stopLength, vehicleLength, behavioralCharacteristics,
523 speedLimitInfo, carFollowingModel);
524 }
525
526
527
528
529
530
531
532 private static boolean stopForAllStopConflict(final HeadwayConflict conflict, final ConflictPlans conflictPlans)
533 {
534
535
536 if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
537 {
538 return false;
539 }
540
541 return false;
542 }
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560 public static final class ConflictPlans implements Serializable
561 {
562
563
564 private static final long serialVersionUID = 20160811L;
565
566
567 private final Map<String, String> yieldPlans = new HashMap<>();
568
569
570 private final Set<String> activeYieldPlans = new HashSet<>();
571
572
573 private final HashMap<String, StopPhase> stopPhases = new HashMap<>();
574
575
576 private final HashMap<String, Time> arrivalTimes = new HashMap<>();
577
578
579
580
581
582
583
584 boolean isYieldPlan(final HeadwayConflict conflict, final AbstractHeadwayGTU gtu)
585 {
586 return this.yieldPlans.containsKey(conflict.getId())
587 && this.yieldPlans.get(conflict.getId()).equals(gtu.getId());
588 }
589
590
591
592
593
594
595 void setYieldPlan(final HeadwayConflict conflict, final AbstractHeadwayGTU gtu)
596 {
597 this.yieldPlans.put(conflict.getId(), gtu.getId());
598 this.activeYieldPlans.add(conflict.getId());
599 }
600
601
602
603
604 void cleanYieldPlans()
605 {
606
607 Iterator<String> iterator = this.yieldPlans.keySet().iterator();
608 while (iterator.hasNext())
609 {
610 String conflictId = iterator.next();
611 if (!this.activeYieldPlans.contains(conflictId))
612 {
613 iterator.remove();
614 }
615 }
616
617 this.activeYieldPlans.clear();
618 }
619
620
621
622
623
624
625 void setArrivalTime(final AbstractHeadwayGTU gtu, final Time time)
626 {
627 this.arrivalTimes.put(gtu.getId(), time);
628 }
629
630
631
632
633
634
635 Time getArrivalTime(final AbstractHeadwayGTU gtu)
636 {
637 return this.arrivalTimes.get(gtu.getId());
638 }
639
640
641
642
643
644 void setStopPhaseApproach(final HeadwayStopLine stopLine)
645 {
646 this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
647 }
648
649
650
651
652
653
654 void setStopPhaseYield(final HeadwayStopLine stopLine)
655 {
656 Throw.when(!this.stopPhases.containsKey(stopLine.getId())
657 || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH), RuntimeException.class,
658 "Yield stop phase is set for stop line that was not approached.");
659 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
660 }
661
662
663
664
665
666
667 void setStopPhaseRun(final HeadwayStopLine stopLine)
668 {
669 Throw.when(!this.stopPhases.containsKey(stopLine.getId()), RuntimeException.class,
670 "Run stop phase is set for stop line that was not approached.");
671 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
672 }
673
674
675
676
677
678 boolean isStopPhaseApproach(final HeadwayStopLine stopLine)
679 {
680 return this.stopPhases.containsKey(stopLine.getId())
681 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
682 }
683
684
685
686
687
688 boolean isStopPhaseYield(final HeadwayStopLine stopLine)
689 {
690 return this.stopPhases.containsKey(stopLine.getId())
691 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
692 }
693
694
695
696
697
698 boolean isStopPhaseRun(final HeadwayStopLine stopLine)
699 {
700 return this.stopPhases.containsKey(stopLine.getId())
701 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
702 }
703
704
705 @Override
706 public String toString()
707 {
708 return "ConflictPlans";
709 }
710
711 }
712
713
714
715
716
717
718
719
720
721
722
723
724 private static enum StopPhase
725 {
726
727 APPROACH,
728
729
730 YIELD,
731
732
733 RUN;
734 }
735
736 }