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