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