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