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