1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Set;
8
9 import nl.tudelft.simulation.language.d3.DirectedPoint;
10
11 import org.djunits.unit.AccelerationUnit;
12 import org.djunits.unit.TimeUnit;
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.Time;
17 import org.opentrafficsim.core.gtu.GTUDirectionality;
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.ParameterTypes;
23 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
24 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
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.categories.DefaultAlexander;
31 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
32 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedAltruistic;
33 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedEgoistic;
34 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneChangeModel;
35 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneMovementStep;
36 import org.opentrafficsim.road.gtu.lane.tactical.following.AccelerationStep;
37 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
38 import org.opentrafficsim.road.network.lane.Lane;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class LaneBasedGTUFollowingChange0TacticalPlanner extends AbstractLaneBasedTacticalPlanner
72 {
73
74 private static final long serialVersionUID = 20160129L;
75
76
77 private Time earliestNexLaneChangeTime = Time.ZERO;
78
79
80 private Lane referenceLane = null;
81
82
83 private Length referencePos = null;
84
85
86 private boolean destroyGtuOnFailure = false;
87
88
89
90
91
92
93 public LaneBasedGTUFollowingChange0TacticalPlanner(final GTUFollowingModelOld carFollowingModel, final LaneBasedGTU gtu)
94 {
95 super(carFollowingModel, gtu);
96 }
97
98
99
100
101
102 public GTUFollowingModelOld getCarFollowingModelOld()
103 {
104 return (GTUFollowingModelOld) super.getCarFollowingModel();
105 }
106
107
108 @Override
109 public OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
110 throws OperationalPlanException, NetworkException, GTUException, ParameterException
111 {
112 try
113 {
114
115 LaneBasedGTU laneBasedGTU = getGtu();
116 LanePerception perception = getPerception();
117 BehavioralCharacteristics behavioralCharacteristics = laneBasedGTU.getBehavioralCharacteristics();
118 behavioralCharacteristics.setParameter(ParameterTypes.LOOKAHEAD, ParameterTypes.LOOKAHEAD.getDefaultValue());
119
120
121 laneBasedGTU.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
122
123
124 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
125 {
126 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
127 }
128
129
130 perception.getPerceptionCategory(DefaultAlexander.class).updateForwardHeadway();
131 perception.getPerceptionCategory(DefaultAlexander.class).updateAccessibleAdjacentLanesLeft();
132 perception.getPerceptionCategory(DefaultAlexander.class).updateAccessibleAdjacentLanesRight();
133 perception.getPerceptionCategory(DefaultAlexander.class).updateSpeedLimit();
134
135
136 Length forwardHeadway = behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD);
137 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway);
138 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, forwardHeadway);
139 Set<Lane> correctLanes = laneBasedGTU.getLanes().keySet();
140 correctLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
141
142
143 if (lanePathInfo.getPath().getLength().lt(forwardHeadway))
144 {
145 if (correctLanes.isEmpty())
146 {
147 LateralDirectionality direction = determineLeftRight(laneBasedGTU, nextSplitInfo);
148 if (direction != null)
149 {
150 getGtu().setTurnIndicatorStatus(
151 direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
152 if (canChange(laneBasedGTU, perception, lanePathInfo, direction))
153 {
154 DirectedPoint newLocation = changeLane(laneBasedGTU, direction);
155 lanePathInfo =
156 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos, forwardHeadway);
157 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
158 }
159 }
160 }
161 }
162
163
164
165 if (getGtu().getSimulator().getSimulatorTime().getTime().lt(this.earliestNexLaneChangeTime))
166 {
167 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
168 }
169
170
171
172 Set<Lane> leftLanes =
173 perception.getPerceptionCategory(DefaultAlexander.class).getAccessibleAdjacentLanesLeft().get(
174 lanePathInfo.getReferenceLane());
175 if (nextSplitInfo.isSplit())
176 {
177 leftLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
178 }
179 if (!leftLanes.isEmpty() && laneBasedGTU.getSpeed().si > 4.0)
180 {
181 perception.getPerceptionCategory(DefaultAlexander.class).updateBackwardHeadway();
182 perception.getPerceptionCategory(DefaultAlexander.class).updateParallelHeadwaysLeft();
183 perception.getPerceptionCategory(DefaultAlexander.class).updateNeighboringHeadwaysLeft();
184 if (perception.getPerceptionCategory(DefaultAlexander.class).getParallelHeadwaysLeft().isEmpty())
185 {
186 Collection<Headway> sameLaneTraffic = new HashSet<>();
187
188 if (perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway() != null
189 && perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway().getObjectType()
190 .isGtu())
191 {
192 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway());
193 }
194 if (perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway() != null
195 && perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway().getObjectType()
196 .isGtu())
197 {
198 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway());
199 }
200 DirectedLaneChangeModel dlcm = new DirectedAltruistic(getPerception());
201 DirectedLaneMovementStep dlms =
202 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.LEFT, sameLaneTraffic,
203 perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysLeft(),
204 behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD), perception
205 .getPerceptionCategory(DefaultAlexander.class).getSpeedLimit(), new Acceleration(1.0,
206 AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI), new Duration(0.5,
207 TimeUnit.SECOND));
208 if (dlms.getLaneChange() != null)
209 {
210 getGtu().setTurnIndicatorStatus(TurnIndicatorStatus.LEFT);
211 if (canChange(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.LEFT))
212 {
213 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.LEFT);
214 lanePathInfo =
215 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos, forwardHeadway);
216 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
217 }
218 }
219 }
220 }
221
222
223 Set<Lane> rightLanes =
224 perception.getPerceptionCategory(DefaultAlexander.class).getAccessibleAdjacentLanesRight().get(
225 lanePathInfo.getReferenceLane());
226 if (nextSplitInfo.isSplit())
227 {
228 rightLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
229 }
230 if (!rightLanes.isEmpty() && laneBasedGTU.getSpeed().si > 4.0)
231 {
232 perception.getPerceptionCategory(DefaultAlexander.class).updateBackwardHeadway();
233 perception.getPerceptionCategory(DefaultAlexander.class).updateParallelHeadwaysRight();
234 perception.getPerceptionCategory(DefaultAlexander.class).updateNeighboringHeadwaysRight();
235 if (perception.getPerceptionCategory(DefaultAlexander.class).getParallelHeadwaysRight().isEmpty())
236 {
237 Collection<Headway> sameLaneTraffic = new HashSet<>();
238
239 if (perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway() != null
240 && perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway().getObjectType()
241 .isGtu())
242 {
243 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway());
244 }
245 if (perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway() != null
246 && perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway().getObjectType()
247 .isGtu())
248 {
249 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway());
250 }
251 DirectedLaneChangeModel dlcm = new DirectedAltruistic(getPerception());
252 DirectedLaneMovementStep dlms =
253 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.RIGHT, sameLaneTraffic,
254 perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysRight(),
255 behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD), perception
256 .getPerceptionCategory(DefaultAlexander.class).getSpeedLimit(), new Acceleration(1.0,
257 AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI), new Duration(0.5,
258 TimeUnit.SECOND));
259 if (dlms.getLaneChange() != null)
260 {
261 getGtu().setTurnIndicatorStatus(TurnIndicatorStatus.RIGHT);
262 if (canChange(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.RIGHT))
263 {
264 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.RIGHT);
265 lanePathInfo =
266 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos, forwardHeadway);
267 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
268 }
269 }
270 }
271 }
272
273 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
274 }
275 catch (GTUException | NetworkException | OperationalPlanException exception)
276 {
277 if (isDestroyGtuOnFailure())
278 {
279 System.err.println("LaneBasedGTUFollowingChange0TacticalPlanner.generateOperationalPlan() failed for "
280 + getGtu() + " because of " + exception.getMessage() + " -- GTU destroyed");
281 getGtu().destroy();
282 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
283 }
284 throw exception;
285 }
286 }
287
288
289
290
291
292
293
294
295
296
297
298 private OperationalPlan currentLanePlan(final LaneBasedGTU laneBasedGTU, final Time startTime,
299 final DirectedPoint locationAtStartTime, final LanePathInfo lanePathInfo) throws OperationalPlanException,
300 GTUException
301 {
302 LanePerception perception = getPerception();
303
304
305 AccelerationStep accelerationStep;
306 accelerationStep =
307 this.getCarFollowingModelOld().computeAccelerationStep(laneBasedGTU,
308 perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway().getSpeed(),
309 perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway().getDistance(),
310 lanePathInfo.getPath().getLength().minus(laneBasedGTU.getLength().multiplyBy(2.0)),
311 perception.getPerceptionCategory(DefaultAlexander.class).getSpeedLimit());
312
313
314 if (accelerationStep.getAcceleration().si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
315 {
316 return new OperationalPlan(laneBasedGTU, locationAtStartTime, startTime, accelerationStep.getDuration());
317 }
318
319
320 List<Segment> operationalPlanSegmentList = new ArrayList<>();
321 if (accelerationStep.getAcceleration().si == 0.0)
322 {
323 Segment segment = new OperationalPlan.SpeedSegment(accelerationStep.getDuration());
324 operationalPlanSegmentList.add(segment);
325 }
326 else
327 {
328 Segment segment =
329 new OperationalPlan.AccelerationSegment(accelerationStep.getDuration(), accelerationStep.getAcceleration());
330 operationalPlanSegmentList.add(segment);
331 }
332 OperationalPlan op =
333 new OperationalPlan(laneBasedGTU, lanePathInfo.getPath(), startTime, laneBasedGTU.getSpeed(),
334 operationalPlanSegmentList);
335 return op;
336 }
337
338
339
340
341
342
343
344 private LateralDirectionality determineLeftRight(final LaneBasedGTU laneBasedGTU, final NextSplitInfo nextSplitInfo)
345 {
346
347 for (Lane correctLane : nextSplitInfo.getCorrectCurrentLanes())
348 {
349 for (Lane currentLane : laneBasedGTU.getLanes().keySet())
350 {
351 if (correctLane.getParentLink().equals(currentLane.getParentLink()))
352 {
353 double deltaOffset =
354 correctLane.getDesignLineOffsetAtBegin().si - currentLane.getDesignLineOffsetAtBegin().si;
355 if (laneBasedGTU.getLanes().get(currentLane).equals(GTUDirectionality.DIR_PLUS))
356 {
357 return deltaOffset > 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
358 }
359 else
360 {
361 return deltaOffset < 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
362 }
363 }
364 }
365 }
366 return null;
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381 private boolean canChange(final LaneBasedGTU gtu, final LanePerception perception, final LanePathInfo lanePathInfo,
382 final LateralDirectionality direction) throws GTUException, NetworkException, ParameterException,
383 OperationalPlanException
384 {
385 Collection<Headway> otherLaneTraffic;
386 perception.getPerceptionCategory(DefaultAlexander.class).updateForwardHeadway();
387 perception.getPerceptionCategory(DefaultAlexander.class).updateBackwardHeadway();
388 if (direction.isLeft())
389 {
390 perception.getPerceptionCategory(DefaultAlexander.class).updateParallelHeadwaysLeft();
391 perception.getPerceptionCategory(DefaultAlexander.class).updateNeighboringHeadwaysLeft();
392 otherLaneTraffic = perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysLeft();
393 }
394 else if (direction.isRight())
395 {
396 perception.getPerceptionCategory(DefaultAlexander.class).updateParallelHeadwaysRight();
397 perception.getPerceptionCategory(DefaultAlexander.class).updateNeighboringHeadwaysRight();
398 otherLaneTraffic = perception.getPerceptionCategory(DefaultAlexander.class).getNeighboringHeadwaysRight();
399 }
400 else
401 {
402 throw new GTUException("Lateral direction is neither LEFT nor RIGHT during a lane change");
403 }
404 if (!perception.getPerceptionCategory(DefaultAlexander.class).getParallelHeadways(direction).isEmpty())
405 {
406 return false;
407 }
408
409 Collection<Headway> sameLaneTraffic = new HashSet<>();
410
411 if (perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway() != null
412 && perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway().getObjectType().isGtu())
413 {
414 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getForwardHeadway());
415 }
416 if (perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway() != null
417 && perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway().getObjectType().isGtu())
418 {
419 sameLaneTraffic.add(perception.getPerceptionCategory(DefaultAlexander.class).getBackwardHeadway());
420 }
421
422
423 DirectedLaneChangeModel dlcm = new DirectedEgoistic(getPerception());
424
425 DirectedLaneMovementStep dlms =
426 dlcm.computeLaneChangeAndAcceleration(gtu, direction, sameLaneTraffic, otherLaneTraffic, gtu
427 .getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD), perception.getPerceptionCategory(
428 DefaultAlexander.class).getSpeedLimit(), new Acceleration(2.0, AccelerationUnit.SI), new Acceleration(0.1,
429 AccelerationUnit.SI), new Duration(0.5, TimeUnit.SECOND));
430 if (dlms.getLaneChange() == null)
431 {
432 return false;
433 }
434
435 return true;
436 }
437
438
439
440
441
442
443
444
445 private DirectedPoint changeLane(final LaneBasedGTU gtu, final LateralDirectionality direction) throws GTUException
446 {
447 DirectedPoint p = null;
448 Set<Lane> lanes = new HashSet<>(gtu.getLanes().keySet());
449 for (Lane lane : lanes)
450 {
451 Set<Lane> adjacentLanes = lane.accessibleAdjacentLanes(direction, gtu.getGTUType());
452 double fraction = gtu.fractionalPosition(lane, gtu.getReference());
453 for (Lane adjacentLane : adjacentLanes)
454 {
455 gtu.enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fraction), gtu.getLanes().get(lane));
456 if (fraction >= 0.0 && fraction <= 1.0)
457 {
458 p = adjacentLane.getCenterLine().getLocationFractionExtended(fraction);
459 this.referenceLane = adjacentLane;
460 this.referencePos = adjacentLane.getLength().multiplyBy(fraction);
461 }
462 System.out.println("gtu " + gtu.getId() + " entered lane " + adjacentLane + " at pos "
463 + adjacentLane.getLength().multiplyBy(fraction));
464 }
465 gtu.leaveLane(lane);
466 }
467
468 if (p == null)
469 {
470 System.err.println("Warning: " + gtu + " not with its reference point on any lane. Came from " + lanes
471 + ", now at " + gtu.getLanes().keySet());
472 Lane l = gtu.getLanes().keySet().iterator().next();
473 double fraction = gtu.fractionalPosition(l, gtu.getReference());
474 p = l.getCenterLine().getLocationFractionExtended(fraction);
475 this.referenceLane = l;
476 this.referencePos = l.getLength().multiplyBy(fraction);
477 }
478
479
480 this.earliestNexLaneChangeTime =
481 gtu.getSimulator().getSimulatorTime().getTime().plus(new Duration(15, TimeUnit.SECOND));
482
483
484 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
485
486 return p;
487 }
488
489
490
491
492
493 public final boolean isDestroyGtuOnFailure()
494 {
495 return this.destroyGtuOnFailure;
496 }
497
498
499
500
501
502 public final void setDestroyGtuOnFailure(final boolean destroyGtuOnFailure)
503 {
504 this.destroyGtuOnFailure = destroyGtuOnFailure;
505 }
506
507
508 @Override
509 public final String toString()
510 {
511 return "LaneBasedGTUFollowingChange0TacticalPlanner [earliestNexLaneChangeTime=" + this.earliestNexLaneChangeTime
512 + ", referenceLane=" + this.referenceLane + ", referencePos=" + this.referencePos + ", destroyGtuOnFailure="
513 + this.destroyGtuOnFailure + "]";
514 }
515
516 }