1 package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2
3 import java.util.LinkedHashSet;
4 import java.util.SortedMap;
5 import java.util.SortedSet;
6 import java.util.TreeMap;
7
8 import org.djunits.unit.AccelerationUnit;
9 import org.djunits.unit.TimeUnit;
10 import org.djunits.value.vdouble.scalar.Acceleration;
11 import org.djunits.value.vdouble.scalar.Duration;
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djunits.value.vdouble.scalar.Speed;
14 import org.djunits.value.vdouble.scalar.Time;
15 import org.opentrafficsim.core.gtu.GTUException;
16 import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
17 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
18 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
19 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
20 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
21 import org.opentrafficsim.core.network.LateralDirectionality;
22 import org.opentrafficsim.core.network.NetworkException;
23 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
24 import org.opentrafficsim.road.gtu.lane.Break;
25 import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
26 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
27 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
28 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
29 import org.opentrafficsim.road.gtu.lane.perception.categories.IntersectionPerception;
30 import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
31 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
32 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
33 import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
34 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
35 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveGetInLane;
36 import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
37 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
38 import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
39
40
41
42
43
44
45
46
47
48
49
50 public final class LmrsUtil implements LmrsParameters
51 {
52
53
54
55
56 private LmrsUtil()
57 {
58
59 }
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 @SuppressWarnings("checkstyle:parameternumber")
78 public static SimpleOperationalPlan determinePlan(final LaneBasedGTU gtu, final Time startTime,
79 final CarFollowingModel carFollowingModel, final LaneChange laneChange, final LmrsData lmrsData,
80 final LanePerception perception, final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
81 final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
82 throws GTUException, NetworkException, ParameterException, OperationalPlanException
83 {
84
85
86 if (startTime.si == 0.0)
87 {
88 return new SimpleOperationalPlan(Acceleration.ZERO, LateralDirectionality.NONE);
89 }
90
91
92 SpeedLimitProspect slp =
93 perception.getPerceptionCategory(InfrastructurePerception.class).getSpeedLimitProspect(RelativeLane.CURRENT);
94 SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
95 BehavioralCharacteristics bc = gtu.getBehavioralCharacteristics();
96
97
98 Speed speed = gtu.getSpeed();
99 SortedSet<HeadwayGTU> leaders =
100 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT);
101 if (!leaders.isEmpty() && lmrsData.isNewLeader(leaders.first()))
102 {
103 initHeadwayRelaxation(bc, leaders.first());
104 }
105 Acceleration a;
106 CarFollowingModel regularFollowing = carFollowingModel;
107 SortedSet<HeadwayGTU> followers =
108 perception.getPerceptionCategory(NeighborsPerception.class).getFollowers(RelativeLane.CURRENT);
109 if (!followers.isEmpty())
110 {
111 HeadwayGTU follower = followers.first();
112 Speed desiredSpeedFollower = follower.getCarFollowingModel().desiredSpeed(follower.getBehavioralCharacteristics(),
113 follower.getSpeedLimitInfo());
114 Speed desiredSpeed = carFollowingModel.desiredSpeed(bc, sli);
115 if (desiredSpeed.lt(desiredSpeedFollower))
116 {
117
118 CarFollowingModel carFollowingModelWrapped = new CarFollowingModelWrapper(carFollowingModel,
119 Speed.interpolate(desiredSpeed, desiredSpeedFollower, bc.getParameter(HIERARCHY)));
120 a = CarFollowingUtil.followLeaders(carFollowingModelWrapped, bc, speed, sli, leaders);
121
122 regularFollowing = carFollowingModelWrapped;
123 }
124 else
125 {
126 a = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli, leaders);
127 }
128 }
129 else
130 {
131 a = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli, leaders);
132 }
133
134
135
136 Length remainingDist = null;
137 for (InfrastructureLaneChangeInfo ili : perception.getPerceptionCategory(InfrastructurePerception.class)
138 .getInfrastructureLaneChangeInfo(RelativeLane.CURRENT))
139 {
140 if (remainingDist == null || remainingDist.gt(ili.getRemainingDistance()))
141 {
142 remainingDist = ili.getRemainingDistance();
143 }
144 }
145 if (remainingDist != null)
146 {
147 Acceleration bCrit = bc.getParameter(ParameterTypes.BCRIT);
148 remainingDist = remainingDist.minus(bc.getParameter(ParameterTypes.S0)).minus(gtu.getFront().getDx());
149 if (remainingDist.le0())
150 {
151 if (speed.gt0())
152 {
153 a = Acceleration.min(a, bCrit.neg());
154 }
155 else
156 {
157 a = Acceleration.min(a, Acceleration.ZERO);
158 }
159 }
160 else
161 {
162 Acceleration bMin = new Acceleration(.5 * speed.si * speed.si / remainingDist.si, AccelerationUnit.SI);
163 if (bMin.ge(bCrit))
164 {
165 a = Acceleration.min(a, bMin.neg());
166 }
167 }
168 }
169
170
171 LateralDirectionality initiatedLaneChange;
172 TurnIndicatorIntent turnIndicatorStatus = TurnIndicatorIntent.NONE;
173 if (laneChange.isChangingLane())
174 {
175 RelativeLane secondLane = laneChange.getSecondLane(gtu);
176 initiatedLaneChange = LateralDirectionality.NONE;
177 SortedSet<HeadwayGTU> secondLeaders =
178 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(secondLane);
179 Acceleration aSecond = CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli, secondLeaders);
180 if (!secondLeaders.isEmpty() && lmrsData.isNewLeader(secondLeaders.first()))
181 {
182 initHeadwayRelaxation(bc, secondLeaders.first());
183 }
184 a = Acceleration.min(a, aSecond);
185 }
186 else
187 {
188
189
190 Desire desire = getLaneChangeDesire(bc, perception, carFollowingModel, mandatoryIncentives, voluntaryIncentives);
191
192
193 boolean acceptLeft =
194 acceptGap(perception, bc, sli, carFollowingModel, desire.getLeft(), speed, LateralDirectionality.LEFT);
195 boolean acceptRight =
196 acceptGap(perception, bc, sli, carFollowingModel, desire.getRight(), speed, LateralDirectionality.RIGHT);
197
198
199 double dFree = bc.getParameter(DFREE);
200 double dSync = bc.getParameter(DSYNC);
201 double dCoop = bc.getParameter(DCOOP);
202
203
204 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dFree && acceptLeft)
205 {
206
207 initiatedLaneChange = LateralDirectionality.LEFT;
208 turnIndicatorStatus = TurnIndicatorIntent.LEFT;
209 bc.setParameter(DLC, desire.getLeft());
210 setDesiredHeadway(bc, desire.getLeft());
211 leaders = perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.LEFT);
212 if (!leaders.isEmpty())
213 {
214
215 lmrsData.isNewLeader(leaders.first());
216 }
217 a = Acceleration.min(a, CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli,
218 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.LEFT)));
219 }
220 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dFree && acceptRight)
221 {
222
223 initiatedLaneChange = LateralDirectionality.RIGHT;
224 turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
225 bc.setParameter(DLC, desire.getRight());
226 setDesiredHeadway(bc, desire.getRight());
227 leaders = perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.RIGHT);
228 if (!leaders.isEmpty())
229 {
230
231 lmrsData.isNewLeader(leaders.first());
232 }
233 a = Acceleration.min(a, CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli,
234 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.RIGHT)));
235 }
236 else
237 {
238 initiatedLaneChange = LateralDirectionality.NONE;
239 turnIndicatorStatus = TurnIndicatorIntent.NONE;
240 }
241 laneChange.setLaneChangeDuration(gtu.getBehavioralCharacteristics().getParameter(ParameterTypes.LCDUR));
242
243
244 Acceleration aSync;
245 if (initiatedLaneChange.equals(LateralDirectionality.NONE))
246 {
247
248 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dSync)
249 {
250 aSync = lmrsData.getSynchronization().synchronize(perception, bc, sli, carFollowingModel, desire.getLeft(),
251 LateralDirectionality.LEFT, lmrsData);
252 a = Acceleration.min(a, aSync);
253 }
254 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dSync)
255 {
256 aSync = lmrsData.getSynchronization().synchronize(perception, bc, sli, carFollowingModel, desire.getRight(),
257 LateralDirectionality.RIGHT, lmrsData);
258 a = Acceleration.min(a, aSync);
259 }
260
261 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dCoop)
262 {
263
264 turnIndicatorStatus = TurnIndicatorIntent.LEFT;
265 }
266 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dCoop)
267 {
268
269 turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
270 }
271 bc.setParameter(DLEFT, desire.getLeft());
272 bc.setParameter(DRIGHT, desire.getRight());
273 }
274 else
275 {
276 bc.setParameter(DLEFT, 0.0);
277 bc.setParameter(DRIGHT, 0.0);
278 }
279
280
281 aSync = lmrsData.getSynchronization().cooperate(perception, bc, sli, carFollowingModel, LateralDirectionality.LEFT);
282 a = Acceleration.min(a, aSync);
283 aSync = lmrsData.getSynchronization().cooperate(perception, bc, sli, carFollowingModel,
284 LateralDirectionality.RIGHT);
285 a = Acceleration.min(a, aSync);
286
287
288 exponentialHeadwayRelaxation(bc);
289
290 }
291 lmrsData.finalizeStep();
292
293 SimpleOperationalPlan simplePlan = new SimpleOperationalPlan(a, initiatedLaneChange);
294 if (turnIndicatorStatus.isLeft())
295 {
296 simplePlan.setIndicatorIntentLeft();
297 }
298 else if (turnIndicatorStatus.isRight())
299 {
300 simplePlan.setIndicatorIntentRight();
301 }
302 return simplePlan;
303
304 }
305
306
307
308
309
310
311
312 private static void initHeadwayRelaxation(final BehavioralCharacteristics bc, final HeadwayGTU leader)
313 throws ParameterException
314 {
315 if (leader.getBehavioralCharacteristics().contains(DLC))
316 {
317 setDesiredHeadway(bc, leader.getBehavioralCharacteristics().getParameter(DLC));
318 }
319
320 }
321
322
323
324
325
326
327 private static void exponentialHeadwayRelaxation(final BehavioralCharacteristics bc) throws ParameterException
328 {
329 double ratio = bc.getParameter(ParameterTypes.DT).si / bc.getParameter(ParameterTypes.TAU).si;
330 bc.setParameter(ParameterTypes.T, Duration.interpolate(bc.getParameter(ParameterTypes.T),
331 bc.getParameter(ParameterTypes.TMAX), ratio <= 1.0 ? ratio : 1.0));
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 private static Desire getLaneChangeDesire(final BehavioralCharacteristics behavioralCharacteristics,
351 final LanePerception perception, final CarFollowingModel carFollowingModel,
352 final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
353 final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
354 throws ParameterException, GTUException, OperationalPlanException
355 {
356
357 double dSync = behavioralCharacteristics.getParameter(DSYNC);
358 double dCoop = behavioralCharacteristics.getParameter(DCOOP);
359
360
361 double dLeftMandatory = 0.0;
362 double dRightMandatory = 0.0;
363 Desire mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
364 for (MandatoryIncentive incentive : mandatoryIncentives)
365 {
366 Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire);
367 if (incentive instanceof IncentiveGetInLane && d.getLeft() >= 1.0 && d.getRight() >= 1.0)
368 {
369 incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire);
370 }
371 dLeftMandatory = Math.abs(d.getLeft()) > Math.abs(dLeftMandatory) ? d.getLeft() : dLeftMandatory;
372 dRightMandatory = Math.abs(d.getRight()) > Math.abs(dRightMandatory) ? d.getRight() : dRightMandatory;
373 mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
374 }
375
376
377 double dLeftVoluntary = 0;
378 double dRightVoluntary = 0;
379 Desire voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
380 for (VoluntaryIncentive incentive : voluntaryIncentives)
381 {
382 Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire,
383 voluntaryDesire);
384 dLeftVoluntary += d.getLeft();
385 dRightVoluntary += d.getRight();
386 voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
387 }
388
389
390 double thetaLeft = 0;
391 if (dLeftMandatory <= dSync || dLeftMandatory * dLeftVoluntary >= 0)
392 {
393
394 thetaLeft = 1;
395 }
396 else if (dSync < dLeftMandatory && dLeftMandatory < dCoop && dLeftMandatory * dLeftVoluntary < 0)
397 {
398
399 thetaLeft = (dCoop - Math.abs(dLeftMandatory)) / (dCoop - dSync);
400 }
401 double thetaRight = 0;
402 if (dRightMandatory <= dSync || dRightMandatory * dRightVoluntary >= 0)
403 {
404
405 thetaRight = 1;
406 }
407 else if (dSync < dRightMandatory && dRightMandatory < dCoop && dRightMandatory * dRightVoluntary < 0)
408 {
409
410 thetaRight = (dCoop - Math.abs(dRightMandatory)) / (dCoop - dSync);
411 }
412
413 return new Desire(dLeftMandatory + thetaLeft * dLeftVoluntary, dRightMandatory + thetaRight * dRightVoluntary);
414
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 private static boolean acceptGap(final LanePerception perception, final BehavioralCharacteristics bc,
431 final SpeedLimitInfo sli, final CarFollowingModel cfm, final double desire, final Speed ownSpeed,
432 final LateralDirectionality lat) throws ParameterException, OperationalPlanException
433 {
434
435
436 if (!perception.getLaneStructure().canChange(lat, perception))
437 {
438 return false;
439 }
440
441
442 if (perception.getPerceptionCategory(InfrastructurePerception.class).getLegalLaneChangePossibility(RelativeLane.CURRENT,
443 lat).si <= 0.0)
444 {
445 return false;
446 }
447
448
449 if (perception.contains(IntersectionPerception.class))
450 {
451 if ((lat.isLeft() && perception.getPerceptionCategory(IntersectionPerception.class).isAlongsideConflictLeft())
452 || (lat.isRight()
453 && perception.getPerceptionCategory(IntersectionPerception.class).isAlongsideConflictRight()))
454 {
455 return false;
456 }
457 }
458
459
460 if (perception.getPerceptionCategory(NeighborsPerception.class).isGtuAlongside(lat))
461 {
462
463 return false;
464 }
465
466 Acceleration b = bc.getParameter(ParameterTypes.B);
467 Acceleration aFollow = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
468 for (
469
470 HeadwayGTU follower : perception.getPerceptionCategory(NeighborsPerception.class).getFirstFollowers(lat))
471 {
472 if (follower.getSpeed().gt0())
473 {
474 Acceleration a = singleAcceleration(follower.getDistance(), follower.getSpeed(), ownSpeed, desire,
475 follower.getBehavioralCharacteristics(), follower.getSpeedLimitInfo(), follower.getCarFollowingModel());
476 aFollow = Acceleration.min(aFollow, a);
477 }
478 }
479
480 Acceleration aSelf = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
481 if (ownSpeed.gt0())
482 {
483 for (
484
485 HeadwayGTU leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
486 {
487 Acceleration a = singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, bc, sli, cfm);
488 aSelf = Acceleration.min(aSelf, a);
489 }
490 }
491
492 Acceleration threshold = b.multiplyBy(-desire);
493 return aFollow.ge(threshold) && aSelf.ge(threshold);
494 }
495
496
497
498
499
500
501
502 static void setDesiredHeadway(final BehavioralCharacteristics bc, final double desire) throws ParameterException
503 {
504 double limitedDesire = desire < 0 ? 0 : desire > 1 ? 1 : desire;
505 double tDes = limitedDesire * bc.getParameter(ParameterTypes.TMIN).si
506 + (1 - limitedDesire) * bc.getParameter(ParameterTypes.TMAX).si;
507 double t = bc.getParameter(ParameterTypes.T).si;
508 bc.setParameter(ParameterTypes.T, new Duration(tDes < t ? tDes : t, TimeUnit.SI));
509 }
510
511
512
513
514
515
516 static void resetDesiredHeadway(final BehavioralCharacteristics bc) throws ParameterException
517 {
518 bc.resetParameter(ParameterTypes.T);
519 }
520
521
522
523
524
525
526
527
528
529
530
531
532
533 static Acceleration singleAcceleration(final Length distance, final Speed followerSpeed, final Speed leaderSpeed,
534 final double desire, final BehavioralCharacteristics bc, final SpeedLimitInfo sli, final CarFollowingModel cfm)
535 throws ParameterException
536 {
537
538 setDesiredHeadway(bc, desire);
539
540 SortedMap<Length, Speed> leaders = new TreeMap<>();
541 leaders.put(distance, leaderSpeed);
542 Acceleration a = cfm.followingAcceleration(bc, followerSpeed, sli, leaders);
543
544 resetDesiredHeadway(bc);
545 return a;
546 }
547
548
549
550
551
552
553
554
555
556
557
558
559
560 private static final class CarFollowingModelWrapper implements CarFollowingModel
561 {
562
563
564 private final CarFollowingModel carFollowingModel;
565
566
567 private final Speed desiredSpeed;
568
569
570
571
572
573 CarFollowingModelWrapper(final CarFollowingModel carFollowingModel, final Speed desiredSpeed)
574 {
575 this.carFollowingModel = carFollowingModel;
576 this.desiredSpeed = desiredSpeed;
577 }
578
579
580 @Override
581 public Speed desiredSpeed(final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedInfo)
582 throws ParameterException
583 {
584 return this.desiredSpeed;
585 }
586
587
588 @Override
589 public Length desiredHeadway(final BehavioralCharacteristics behavioralCharacteristics, final Speed speed)
590 throws ParameterException
591 {
592 return this.carFollowingModel.desiredHeadway(behavioralCharacteristics, speed);
593 }
594
595
596 @Override
597 public Acceleration followingAcceleration(final BehavioralCharacteristics behavioralCharacteristics, final Speed speed,
598 final SpeedLimitInfo speedLimitInfo, final SortedMap<Length, Speed> leaders) throws ParameterException
599 {
600 return this.carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
601 }
602
603
604 @Override
605 public String getName()
606 {
607 return this.carFollowingModel.getName();
608 }
609
610
611 @Override
612 public String getLongName()
613 {
614 return this.carFollowingModel.getLongName();
615 }
616
617 }
618
619 }