1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7
8 import org.djunits.unit.AccelerationUnit;
9 import org.djunits.unit.DurationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.value.ValueRuntimeException;
12 import org.djunits.value.storage.StorageType;
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.djunits.value.vdouble.vector.AccelerationVector;
19 import org.djutils.draw.point.OrientedPoint2d;
20 import org.opentrafficsim.base.parameters.ParameterException;
21 import org.opentrafficsim.base.parameters.ParameterTypeLength;
22 import org.opentrafficsim.base.parameters.ParameterTypes;
23 import org.opentrafficsim.core.geometry.OtsLine2d;
24 import org.opentrafficsim.core.gtu.GtuException;
25 import org.opentrafficsim.core.gtu.GtuType;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
28 import org.opentrafficsim.core.gtu.plan.operational.Segments;
29 import org.opentrafficsim.core.network.LateralDirectionality;
30 import org.opentrafficsim.core.network.Link;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.core.network.Node;
33 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
34 import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
35 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
36 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
37 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectDefaultSimplePerception;
38 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
39 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
40 import org.opentrafficsim.road.gtu.lane.tactical.following.GtuFollowingModelOld;
41 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
42 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
43 import org.opentrafficsim.road.network.lane.CrossSectionElement;
44 import org.opentrafficsim.road.network.lane.CrossSectionLink;
45 import org.opentrafficsim.road.network.lane.Lane;
46 import org.opentrafficsim.road.network.lane.LanePosition;
47 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
48 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
49
50
51
52
53
54
55
56
57
58
59
60
61 public class LaneBasedCfLcTacticalPlanner extends AbstractLaneBasedTacticalPlanner
62 {
63
64 private static final long serialVersionUID = 20151125L;
65
66
67 protected static final ParameterTypeLength LOOKBACKOLD = ParameterTypes.LOOKBACKOLD;
68
69
70 private static final Acceleration STAYINCURRENTLANEINCENTIVE = new Acceleration(0.1, AccelerationUnit.METER_PER_SECOND_2);
71
72
73 private static final Acceleration PREFERREDLANEINCENTIVE = new Acceleration(0.3, AccelerationUnit.METER_PER_SECOND_2);
74
75
76 private static final Acceleration NONPREFERREDLANEINCENTIVE = new Acceleration(-0.3, AccelerationUnit.METER_PER_SECOND_2);
77
78
79 public static final Length NOLANECHANGENEEDED = new Length(Double.MAX_VALUE, LengthUnit.SI);
80
81
82 public static final Length GETOFFTHISLANENOW = Length.ZERO;
83
84
85 private static final Duration TIMEHORIZON = new Duration(90, DurationUnit.SECOND);
86
87
88 private LaneChangeModel laneChangeModel;
89
90
91
92
93
94
95
96 public LaneBasedCfLcTacticalPlanner(final GtuFollowingModelOld carFollowingModel, final LaneChangeModel laneChangeModel,
97 final LaneBasedGtu gtu)
98 {
99 super(carFollowingModel, gtu, new CategoricalLanePerception(gtu));
100 this.laneChangeModel = laneChangeModel;
101 getPerception().addPerceptionCategory(new DirectDefaultSimplePerception(getPerception()));
102 }
103
104
105 @Override
106 public final OperationalPlan generateOperationalPlan(final Time startTime, final OrientedPoint2d locationAtStartTime)
107 throws OperationalPlanException, NetworkException, GtuException, ParameterException
108 {
109 try
110 {
111
112 LaneBasedGtu laneBasedGTU = getGtu();
113 LanePerception perception = getPerception();
114
115
116 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
117 {
118 return OperationalPlan.standStill(getGtu(), getGtu().getLocation(), startTime, Duration.ONE);
119 }
120
121 Length maximumForwardHeadway = laneBasedGTU.getParameters().getParameter(LOOKAHEAD);
122 DefaultSimplePerception simplePerception = perception.getPerceptionCategory(DefaultSimplePerception.class);
123 Speed speedLimit = simplePerception.getSpeedLimit();
124
125
126 Headway sameLaneLeader = simplePerception.getForwardHeadwayGtu();
127
128 Headway sameLaneFollower = simplePerception.getBackwardHeadway();
129 Collection<Headway> sameLaneTraffic = new ArrayList<>();
130 if (sameLaneLeader.getObjectType().isGtu())
131 {
132 sameLaneTraffic.add(sameLaneLeader);
133 }
134 if (sameLaneFollower.getObjectType().isGtu())
135 {
136 sameLaneTraffic.add(sameLaneFollower);
137 }
138
139
140 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
141
142
143 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, maximumForwardHeadway);
144 boolean currentLaneFine = nextSplitInfo.correctCurrentLanes().contains(lanePathInfo.getReferenceLane());
145
146
147
148
149
150 Collection<Headway> leftLaneTraffic = simplePerception.getNeighboringHeadwaysLeft();
151 Collection<Headway> rightLaneTraffic = simplePerception.getNeighboringHeadwaysRight();
152
153
154 final LateralDirectionality preferred = LateralDirectionality.RIGHT;
155 final Acceleration defaultLeftLaneIncentive =
156 preferred.isLeft() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
157 final Acceleration defaultRightLaneIncentive =
158 preferred.isRight() ? PREFERREDLANEINCENTIVE : NONPREFERREDLANEINCENTIVE;
159
160 AccelerationVector defaultLaneIncentives = new AccelerationVector(new double[] {defaultLeftLaneIncentive.getSI(),
161 STAYINCURRENTLANEINCENTIVE.getSI(), defaultRightLaneIncentive.getSI()}, AccelerationUnit.SI);
162 AccelerationVector laneIncentives = laneIncentives(laneBasedGTU, defaultLaneIncentives);
163 LaneMovementStep lcmr = this.laneChangeModel.computeLaneChangeAndAcceleration(laneBasedGTU, sameLaneTraffic,
164 rightLaneTraffic, leftLaneTraffic, speedLimit,
165 new Acceleration(laneIncentives.get(preferred.isRight() ? 2 : 0)), new Acceleration(laneIncentives.get(1)),
166 new Acceleration(laneIncentives.get(preferred.isRight() ? 0 : 2)));
167 Duration duration = lcmr.getGfmr().getValidUntil().minus(getGtu().getSimulator().getSimulatorAbsTime());
168 if (lcmr.getLaneChangeDirection() != null)
169 {
170 laneBasedGTU.changeLaneInstantaneously(lcmr.getLaneChangeDirection());
171
172
173 lanePathInfo = buildLanePathInfo(laneBasedGTU, maximumForwardHeadway);
174 }
175
176
177 Headway object = simplePerception.getForwardHeadwayObject();
178 Acceleration a = lcmr.getGfmr().getAcceleration();
179 if (object instanceof HeadwayTrafficLight)
180 {
181
182 a = Acceleration.min(a, ((GtuFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
183 getGtu().getMaximumSpeed(), Speed.ZERO, object.getDistance(), speedLimit));
184 }
185
186
187 Length dist = lanePathInfo.path().getLength().minus(getGtu().getFront().dx());
188 a = Acceleration.min(a, ((GtuFollowingModelOld) getCarFollowingModel()).computeAcceleration(getGtu().getSpeed(),
189 getGtu().getMaximumSpeed(), Speed.ZERO, dist, speedLimit));
190
191
192 if (a.si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
193 {
194 return OperationalPlan.standStill(getGtu(), getGtu().getLocation(), startTime, Duration.ONE);
195 }
196 OtsLine2d path = lanePathInfo.path();
197 OperationalPlan op = new OperationalPlan(getGtu(), path, startTime, Segments.off(getGtu().getSpeed(), duration, a));
198 return op;
199 }
200 catch (ValueRuntimeException exception)
201 {
202 throw new GtuException(exception);
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218 private AccelerationVector laneIncentives(final LaneBasedGtu gtu, final AccelerationVector defaultLaneIncentives)
219 throws NetworkException, ValueRuntimeException, GtuException, OperationalPlanException
220 {
221 Length leftSuitability = suitability(gtu, LateralDirectionality.LEFT);
222 Length currentSuitability = suitability(gtu, null);
223 Length rightSuitability = suitability(gtu, LateralDirectionality.RIGHT);
224 if (leftSuitability == NOLANECHANGENEEDED && currentSuitability == NOLANECHANGENEEDED
225 && rightSuitability == NOLANECHANGENEEDED)
226 {
227 return checkLaneDrops(gtu, defaultLaneIncentives);
228 }
229 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
230 && currentSuitability == NOLANECHANGENEEDED
231 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
232 {
233 return checkLaneDrops(gtu, new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
234 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI));
235 }
236 if (currentSuitability == NOLANECHANGENEEDED)
237 {
238 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
239 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
240 }
241 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
242 acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257 private AccelerationVector checkLaneDrops(final LaneBasedGtu gtu, final AccelerationVector defaultLaneIncentives)
258 throws NetworkException, ValueRuntimeException, GtuException, OperationalPlanException
259 {
260
261 Length leftSuitability = Double.isNaN(defaultLaneIncentives.get(0).si) || defaultLaneIncentives.get(0).si < -10
262 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.LEFT);
263 Length currentSuitability = laneDrop(gtu, null);
264 Length rightSuitability = Double.isNaN(defaultLaneIncentives.get(2).si) || defaultLaneIncentives.get(2).si < -10
265 ? GETOFFTHISLANENOW : laneDrop(gtu, LateralDirectionality.RIGHT);
266
267 if ((leftSuitability == NOLANECHANGENEEDED || leftSuitability == GETOFFTHISLANENOW)
268 && currentSuitability == NOLANECHANGENEEDED
269 && (rightSuitability == NOLANECHANGENEEDED || rightSuitability == GETOFFTHISLANENOW))
270 {
271 return defaultLaneIncentives;
272 }
273
274 if (currentSuitability == NOLANECHANGENEEDED)
275 {
276 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability),
277 defaultLaneIncentives.get(1).getSI(), acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
278 }
279 if (currentSuitability.le(leftSuitability))
280 {
281 return new AccelerationVector(
282 new double[] {PREFERREDLANEINCENTIVE.getSI(), NONPREFERREDLANEINCENTIVE.getSI(), GETOFFTHISLANENOW.getSI()},
283 AccelerationUnit.SI);
284 }
285 if (currentSuitability.le(rightSuitability))
286 {
287 return new AccelerationVector(
288 new double[] {GETOFFTHISLANENOW.getSI(), NONPREFERREDLANEINCENTIVE.getSI(), PREFERREDLANEINCENTIVE.getSI()},
289 AccelerationUnit.SI, StorageType.DENSE);
290 }
291 return new AccelerationVector(new double[] {acceleration(gtu, leftSuitability), acceleration(gtu, currentSuitability),
292 acceleration(gtu, rightSuitability)}, AccelerationUnit.SI);
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 private Length laneDrop(final LaneBasedGtu gtu, final LateralDirectionality direction)
309 throws NetworkException, GtuException, OperationalPlanException
310 {
311 LanePosition dlp = gtu.getReferencePosition();
312 Lane lane = dlp.lane();
313 Length longitudinalPosition = dlp.position();
314 if (null != direction)
315 {
316 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
317 direction, longitudinalPosition);
318 }
319 if (null == lane)
320 {
321 return GETOFFTHISLANENOW;
322 }
323 double remainingLength = lane.getLength().getSI() - longitudinalPosition.getSI();
324 double remainingTimeSI = TIMEHORIZON.getSI() - remainingLength / lane.getSpeedLimit(gtu.getType()).getSI();
325 while (remainingTimeSI >= 0)
326 {
327 for (LaneDetector detector : lane.getDetectors())
328 {
329 if (detector instanceof SinkDetector)
330 {
331 return NOLANECHANGENEEDED;
332 }
333 }
334 int branching = lane.nextLanes(gtu.getType()).size();
335 if (branching == 0)
336 {
337 return new Length(remainingLength, LengthUnit.SI);
338 }
339 if (branching > 1)
340 {
341 return NOLANECHANGENEEDED;
342 }
343 lane = lane.nextLanes(gtu.getType()).iterator().next();
344 remainingTimeSI -= lane.getLength().getSI() / lane.getSpeedLimit(gtu.getType()).getSI();
345 remainingLength += lane.getLength().getSI();
346 }
347 return NOLANECHANGENEEDED;
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361 private Length suitability(final LaneBasedGtu gtu, final LateralDirectionality direction)
362 throws NetworkException, GtuException, OperationalPlanException
363 {
364 LanePosition dlp = gtu.getReferencePosition();
365 Lane lane = dlp.lane();
366 Length longitudinalPosition = dlp.position().plus(gtu.getFront().dx());
367 if (null != direction)
368 {
369 lane = getPerception().getPerceptionCategory(DefaultSimplePerception.class).bestAccessibleAdjacentLane(lane,
370 direction, longitudinalPosition);
371 }
372 if (null == lane)
373 {
374 return GETOFFTHISLANENOW;
375 }
376 try
377 {
378 return suitability(lane, longitudinalPosition, gtu, TIMEHORIZON);
379
380 }
381 catch (NetworkException ne)
382 {
383 System.err.println(gtu + " has a route problem in suitability: " + ne.getMessage());
384 return NOLANECHANGENEEDED;
385 }
386 }
387
388
389
390
391
392
393
394 private double acceleration(final LaneBasedGtu gtu, final Length stopDistance)
395 {
396
397
398 double v = gtu.getSpeed().getSI();
399 double a = -v * v / 2 / stopDistance.getSI();
400 return a;
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414 private Length suitability(final Lane lane, final Length longitudinalPosition, final LaneBasedGtu gtu,
415 final Duration timeHorizon) throws NetworkException
416 {
417 double remainingDistance = lane.getLength().getSI() - longitudinalPosition.getSI();
418 double spareTime = timeHorizon.getSI() - remainingDistance / lane.getSpeedLimit(gtu.getType()).getSI();
419
420 Node nextNode = lane.getLink().getEndNode();
421 Link lastLink = lane.getLink();
422 Node nextSplitNode = null;
423 Lane currentLane = lane;
424 CrossSectionLink linkBeforeBranch = lane.getLink();
425 while (null != nextNode)
426 {
427 if (spareTime <= 0)
428 {
429 return NOLANECHANGENEEDED;
430 }
431 int laneCount = countCompatibleLanes(linkBeforeBranch, gtu.getType());
432 if (0 == laneCount)
433 {
434 throw new NetworkException("No compatible Lanes on Link " + linkBeforeBranch);
435 }
436 if (1 == laneCount)
437 {
438 return NOLANECHANGENEEDED;
439
440 }
441 int branching = nextNode.getLinks().size();
442 if (branching > 2)
443 {
444 nextSplitNode = nextNode;
445 break;
446 }
447 else if (1 == branching)
448 {
449 return NOLANECHANGENEEDED;
450 }
451 else
452 {
453 Link nextLink = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
454 if (nextLink instanceof CrossSectionLink)
455 {
456 nextNode = nextLink.getEndNode();
457
458 remainingDistance += nextLink.getLength().getSI();
459 linkBeforeBranch = (CrossSectionLink) nextLink;
460
461 if (currentLane.nextLanes(gtu.getType()).size() == 0)
462 {
463
464
465 if (currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtu.getType()).size() > 0)
466 {
467 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT,
468 gtu.getType()))
469 {
470 if (adjacentLane.nextLanes(gtu.getType()).size() > 0)
471 {
472 currentLane = adjacentLane;
473 break;
474 }
475
476
477 }
478 }
479 for (Lane adjacentLane : currentLane.accessibleAdjacentLanesLegal(LateralDirectionality.LEFT,
480 gtu.getType()))
481 {
482 if (adjacentLane.nextLanes(gtu.getType()).size() > 0)
483 {
484 currentLane = adjacentLane;
485 break;
486 }
487
488
489 }
490 if (currentLane.nextLanes(gtu.getType()).size() == 0)
491 {
492 throw new NetworkException(
493 "Lane ends and there is not a compatible adjacent lane that does " + "not end");
494 }
495 }
496
497 for (Lane nextLane : currentLane.nextLanes(gtu.getType()))
498 {
499 currentLane = currentLane.nextLanes(gtu.getType()).iterator().next();
500 break;
501 }
502 spareTime -= currentLane.getLength().getSI() / currentLane.getSpeedLimit(gtu.getType()).getSI();
503 }
504 else
505 {
506
507
508 return NOLANECHANGENEEDED;
509 }
510 lastLink = nextLink;
511 }
512 }
513 if (null == nextNode)
514 {
515 throw new NetworkException("Cannot find the next branch or sink node");
516 }
517
518
519 Map<Lane, Length> suitabilityOfLanesBeforeBranch = new LinkedHashMap<>();
520 Link linkAfterBranch = gtu.getStrategicalPlanner().nextLink(lastLink, gtu.getType());
521 for (CrossSectionElement cse : linkBeforeBranch.getCrossSectionElementList())
522 {
523 if (cse instanceof Lane)
524 {
525 Lane l = (Lane) cse;
526 if (l.getType().isCompatible(gtu.getType()))
527 {
528 for (Lane connectingLane : l.nextLanes(gtu.getType()))
529 {
530 if (connectingLane.getLink() == linkAfterBranch && connectingLane.getType().isCompatible(gtu.getType()))
531 {
532 Length currentValue = suitabilityOfLanesBeforeBranch.get(l);
533
534
535
536 Length value = suitability(connectingLane, new Length(0, LengthUnit.SI), gtu,
537 new Duration(spareTime, DurationUnit.SI));
538
539 value = value.plus(new Length(remainingDistance, LengthUnit.SI));
540
541
542 suitabilityOfLanesBeforeBranch.put(l,
543 null == currentValue || value.le(currentValue) ? value : currentValue);
544 }
545 }
546 }
547 }
548 }
549 if (suitabilityOfLanesBeforeBranch.size() == 0)
550 {
551 throw new NetworkException("No lanes available on Link " + linkBeforeBranch);
552 }
553 Length currentLaneSuitability = suitabilityOfLanesBeforeBranch.get(currentLane);
554 if (null != currentLaneSuitability)
555 {
556 return currentLaneSuitability;
557 }
558
559 int totalLanes = countCompatibleLanes(currentLane.getLink(), gtu.getType());
560 Length leftSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
561 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.LEFT, gtu.getType());
562 Length rightSuitability = computeSuitabilityWithLaneChanges(currentLane, remainingDistance,
563 suitabilityOfLanesBeforeBranch, totalLanes, LateralDirectionality.RIGHT, gtu.getType());
564 if (leftSuitability.ge(rightSuitability))
565 {
566 return leftSuitability;
567 }
568 else if (rightSuitability.ge(leftSuitability))
569 {
570
571 return rightSuitability;
572 }
573 if (leftSuitability.le(GETOFFTHISLANENOW))
574 {
575 throw new NetworkException("Changing lanes in any direction does not get the GTU on a suitable lane");
576 }
577 return leftSuitability;
578 }
579
580
581
582
583
584
585
586
587
588
589
590
591
592 protected final Length computeSuitabilityWithLaneChanges(final Lane startLane, final double remainingDistance,
593 final Map<Lane, Length> suitabilities, final int totalLanes, final LateralDirectionality direction,
594 final GtuType gtuType)
595 {
596
597
598
599
600
601
602
603
604
605 int laneChangesUsed = 0;
606 Lane currentLane = startLane;
607 Length currentSuitability = null;
608 while (null == currentSuitability)
609 {
610 laneChangesUsed++;
611 if (currentLane.accessibleAdjacentLanesLegal(direction, gtuType).size() == 0)
612 {
613 return GETOFFTHISLANENOW;
614 }
615 currentLane = currentLane.accessibleAdjacentLanesLegal(direction, gtuType).iterator().next();
616 currentSuitability = suitabilities.get(currentLane);
617 }
618 double fraction = currentSuitability == NOLANECHANGENEEDED ? 0 : 0.5;
619 int notSuitableLaneCount = totalLanes - suitabilities.size();
620 return new Length(
621 remainingDistance * (notSuitableLaneCount - laneChangesUsed + 1 + fraction) / (notSuitableLaneCount + fraction),
622 LengthUnit.SI);
623 }
624
625
626
627
628
629
630
631
632 protected final int countCompatibleLanes(final CrossSectionLink link, final GtuType gtuType)
633 {
634 int result = 0;
635 for (CrossSectionElement cse : link.getCrossSectionElementList())
636 {
637 if (cse instanceof Lane)
638 {
639 Lane l = (Lane) cse;
640 if (l.getType().isCompatible(gtuType))
641 {
642 result++;
643 }
644 }
645 }
646 return result;
647 }
648
649
650 @Override
651 public final String toString()
652 {
653 return "LaneBasedCFLCTacticalPlanner [laneChangeModel=" + this.laneChangeModel + "]";
654 }
655
656 }