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