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