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.Length;
15 import org.djunits.value.vdouble.scalar.Time;
16 import org.opentrafficsim.core.gtu.GTU;
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.plan.operational.OperationalPlan;
21 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
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.lane.LaneBasedGTU;
26 import org.opentrafficsim.road.gtu.lane.driver.LaneBasedBehavioralCharacteristics;
27 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
28 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedAltruistic;
29 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedEgoistic;
30 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneChangeModel;
31 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneMovementStep;
32 import org.opentrafficsim.road.gtu.lane.tactical.following.AccelerationStep;
33 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
34 import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
35 import org.opentrafficsim.road.network.lane.Lane;
36
37
38
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 public class LaneBasedGTUFollowingChange0TacticalPlanner extends AbstractLaneBasedTacticalPlanner
69 {
70
71 private static final long serialVersionUID = 20160129L;
72
73
74 private Time.Abs earliestNexLaneChangeTime = Time.Abs.ZERO;
75
76
77 private Lane referenceLane = null;
78
79
80 private Length.Rel referencePos = null;
81
82
83 private boolean destroyGtuOnFailure = false;
84
85
86
87
88 public LaneBasedGTUFollowingChange0TacticalPlanner()
89 {
90 super();
91 }
92
93
94 @Override
95 public OperationalPlan generateOperationalPlan(final GTU gtu, final Time.Abs startTime,
96 final DirectedPoint locationAtStartTime) throws OperationalPlanException, NetworkException, GTUException
97 {
98 try
99 {
100
101 LaneBasedGTU laneBasedGTU = (LaneBasedGTU) gtu;
102 LanePerception perception = laneBasedGTU.getPerception();
103 LaneBasedBehavioralCharacteristics drivingCharacteristics = laneBasedGTU.getBehavioralCharacteristics();
104
105
106 laneBasedGTU.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
107
108
109 if (laneBasedGTU.getMaximumVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
110 {
111 return new OperationalPlan(gtu, locationAtStartTime, startTime, new Time.Rel(1.0, TimeUnit.SECOND));
112 }
113
114
115 perception.updateForwardHeadwayGTU();
116 perception.updateAccessibleAdjacentLanesLeft();
117 perception.updateAccessibleAdjacentLanesRight();
118 perception.updateSpeedLimit();
119
120
121 Length.Rel forwardHeadway = drivingCharacteristics.getForwardHeadwayDistance();
122 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway);
123 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, forwardHeadway);
124 Set<Lane> correctLanes = laneBasedGTU.getLanes().keySet();
125 correctLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
126
127
128 if (lanePathInfo.getPath().getLength().lt(forwardHeadway))
129 {
130 if (correctLanes.isEmpty())
131 {
132 LateralDirectionality direction = determineLeftRight(laneBasedGTU, nextSplitInfo);
133 if (direction != null)
134 {
135 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
136 if (canChange(laneBasedGTU, perception, lanePathInfo, direction))
137 {
138 DirectedPoint newLocation = changeLane(laneBasedGTU, direction);
139 lanePathInfo =
140 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos,
141 forwardHeadway);
142 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
143 }
144 }
145 }
146 }
147
148
149
150 if (gtu.getSimulator().getSimulatorTime().getTime().lt(this.earliestNexLaneChangeTime))
151 {
152 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
153 }
154
155
156
157 Set<Lane> leftLanes = perception.getAccessibleAdjacentLanesLeft().get(lanePathInfo.getReferenceLane());
158 if (nextSplitInfo.isSplit())
159 {
160 leftLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
161 }
162 if (!leftLanes.isEmpty() && laneBasedGTU.getVelocity().si > 4.0)
163 {
164 perception.updateBackwardHeadwayGTU();
165 perception.updateParallelGTUsLeft();
166 perception.updateLaneTrafficLeft();
167 if (perception.getParallelGTUsLeft().isEmpty())
168 {
169 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
170 if (perception.getForwardHeadwayGTU() != null
171 && perception.getForwardHeadwayGTU().getGtuId() != null)
172 {
173 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
174 }
175 if (perception.getBackwardHeadwayGTU() != null
176 && perception.getBackwardHeadwayGTU().getGtuId() != null)
177 {
178 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
179 }
180 DirectedLaneChangeModel dlcm = new DirectedAltruistic();
181 DirectedLaneMovementStep dlms =
182 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.LEFT,
183 sameLaneTraffic, perception.getNeighboringGTUsLeft(), laneBasedGTU
184 .getBehavioralCharacteristics().getForwardHeadwayDistance(), perception.getSpeedLimit(),
185 new Acceleration(1.0, AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI),
186 new Time.Rel(0.5, TimeUnit.SECOND));
187 if (dlms.getLaneChange() != null)
188 {
189 gtu.setTurnIndicatorStatus(TurnIndicatorStatus.LEFT);
190 if (canChange(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.LEFT))
191 {
192 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.LEFT);
193 lanePathInfo =
194 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos,
195 forwardHeadway);
196 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
197 }
198 }
199 }
200 }
201
202
203 Set<Lane> rightLanes = perception.getAccessibleAdjacentLanesRight().get(lanePathInfo.getReferenceLane());
204 if (nextSplitInfo.isSplit())
205 {
206 rightLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
207 }
208 if (!rightLanes.isEmpty() && laneBasedGTU.getVelocity().si > 4.0)
209 {
210 perception.updateBackwardHeadwayGTU();
211 perception.updateParallelGTUsRight();
212 perception.updateLaneTrafficRight();
213 if (perception.getParallelGTUsRight().isEmpty())
214 {
215 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
216 if (perception.getForwardHeadwayGTU() != null
217 && perception.getForwardHeadwayGTU().getGtuId() != null)
218 {
219 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
220 }
221 if (perception.getBackwardHeadwayGTU() != null
222 && perception.getBackwardHeadwayGTU().getGtuId() != null)
223 {
224 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
225 }
226 DirectedLaneChangeModel dlcm = new DirectedAltruistic();
227 DirectedLaneMovementStep dlms =
228 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.RIGHT,
229 sameLaneTraffic, perception.getNeighboringGTUsRight(), laneBasedGTU
230 .getBehavioralCharacteristics().getForwardHeadwayDistance(), perception.getSpeedLimit(),
231 new Acceleration(1.0, AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI),
232 new Time.Rel(0.5, TimeUnit.SECOND));
233 if (dlms.getLaneChange() != null)
234 {
235 gtu.setTurnIndicatorStatus(TurnIndicatorStatus.RIGHT);
236 if (canChange(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.RIGHT))
237 {
238 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.RIGHT);
239 lanePathInfo =
240 buildLanePathInfo(laneBasedGTU, this.referenceLane, this.referencePos,
241 forwardHeadway);
242 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
243 }
244 }
245 }
246 }
247
248 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
249 }
250 catch (GTUException | NetworkException | OperationalPlanException exception)
251 {
252 if (isDestroyGtuOnFailure())
253 {
254 System.err.println("LaneBasedGTUFollowingChange0TacticalPlanner.generateOperationalPlan() failed for "
255 + gtu + " because of " + exception.getMessage() + " -- GTU destroyed");
256 gtu.destroy();
257 return new OperationalPlan(gtu, locationAtStartTime, startTime, new Time.Rel(1.0, TimeUnit.SECOND));
258 }
259 throw exception;
260 }
261 }
262
263
264
265
266
267
268
269
270
271
272
273 private OperationalPlan currentLanePlan(final LaneBasedGTU laneBasedGTU, final Time.Abs startTime,
274 final DirectedPoint locationAtStartTime, final LanePathInfo lanePathInfo) throws OperationalPlanException,
275 GTUException
276 {
277 LanePerception perception = laneBasedGTU.getPerception();
278 GTUFollowingModelOld gfm = laneBasedGTU.getBehavioralCharacteristics().getGTUFollowingModel();
279
280
281 AccelerationStep accelerationStep;
282 if (perception.getForwardHeadwayGTU().getGtuId() == null)
283 {
284 accelerationStep =
285 gfm.computeAccelerationStepWithNoLeader(laneBasedGTU,
286 lanePathInfo.getPath().getLength().minus(laneBasedGTU.getLength().multiplyBy(2.0)),
287 perception.getSpeedLimit());
288 }
289 else
290 {
291 accelerationStep =
292 gfm.computeAccelerationStep(laneBasedGTU, perception.getForwardHeadwayGTU().getGtuSpeed(), perception
293 .getForwardHeadwayGTU().getDistance(),
294 lanePathInfo.getPath().getLength().minus(laneBasedGTU.getLength().multiplyBy(2.0)), perception
295 .getSpeedLimit());
296 }
297
298
299 if (accelerationStep.getAcceleration().si < 1E-6
300 && laneBasedGTU.getVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
301 {
302 return new OperationalPlan(laneBasedGTU, locationAtStartTime, startTime, accelerationStep.getDuration());
303 }
304
305
306 List<Segment> operationalPlanSegmentList = new ArrayList<>();
307 if (accelerationStep.getAcceleration().si == 0.0)
308 {
309 Segment segment = new OperationalPlan.SpeedSegment(accelerationStep.getDuration());
310 operationalPlanSegmentList.add(segment);
311 }
312 else
313 {
314 Segment segment =
315 new OperationalPlan.AccelerationSegment(accelerationStep.getDuration(),
316 accelerationStep.getAcceleration());
317 operationalPlanSegmentList.add(segment);
318 }
319 OperationalPlan op =
320 new OperationalPlan(laneBasedGTU, lanePathInfo.getPath(), startTime, laneBasedGTU.getVelocity(),
321 operationalPlanSegmentList);
322 return op;
323 }
324
325
326
327
328
329
330
331 private LateralDirectionality
332 determineLeftRight(final LaneBasedGTU laneBasedGTU, final NextSplitInfo nextSplitInfo)
333 {
334
335 for (Lane correctLane : nextSplitInfo.getCorrectCurrentLanes())
336 {
337 for (Lane currentLane : laneBasedGTU.getLanes().keySet())
338 {
339 if (correctLane.getParentLink().equals(currentLane.getParentLink()))
340 {
341 double deltaOffset =
342 correctLane.getDesignLineOffsetAtBegin().si - currentLane.getDesignLineOffsetAtBegin().si;
343 if (laneBasedGTU.getLanes().get(currentLane).equals(GTUDirectionality.DIR_PLUS))
344 {
345 return deltaOffset > 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
346 }
347 else
348 {
349 return deltaOffset < 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
350 }
351 }
352 }
353 }
354 return null;
355 }
356
357
358
359
360
361
362
363
364
365
366
367 private boolean canChange(final LaneBasedGTU gtu, final LanePerception perception, final LanePathInfo lanePathInfo,
368 final LateralDirectionality direction) throws GTUException, NetworkException
369 {
370 Collection<HeadwayGTU> otherLaneTraffic;
371 perception.updateForwardHeadwayGTU();
372 perception.updateBackwardHeadwayGTU();
373 if (direction.isLeft())
374 {
375 perception.updateParallelGTUsLeft();
376 perception.updateLaneTrafficLeft();
377 otherLaneTraffic = perception.getNeighboringGTUsLeft();
378 }
379 else
380 {
381 perception.updateParallelGTUsRight();
382 perception.updateLaneTrafficRight();
383 otherLaneTraffic = perception.getNeighboringGTUsRight();
384 }
385 if (!perception.parallelGTUs(direction).isEmpty())
386 {
387 return false;
388 }
389
390 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
391 if (perception.getForwardHeadwayGTU() != null && perception.getForwardHeadwayGTU().getGtuId() != null)
392 {
393 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
394 }
395 if (perception.getBackwardHeadwayGTU() != null && perception.getBackwardHeadwayGTU().getGtuId() != null)
396 {
397 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
398 }
399
400
401 DirectedLaneChangeModel dlcm = new DirectedEgoistic();
402
403 DirectedLaneMovementStep dlms =
404 dlcm.computeLaneChangeAndAcceleration(gtu, direction, sameLaneTraffic, otherLaneTraffic, gtu
405 .getBehavioralCharacteristics().getForwardHeadwayDistance(), perception.getSpeedLimit(), new Acceleration(
406 2.0, AccelerationUnit.SI), new Acceleration(0.1, AccelerationUnit.SI), new Time.Rel(0.5,
407 TimeUnit.SECOND));
408 if (dlms.getLaneChange() == null)
409 {
410 return false;
411 }
412
413 return true;
414 }
415
416
417
418
419
420
421
422
423 private DirectedPoint changeLane(final LaneBasedGTU gtu, final LateralDirectionality direction) throws GTUException
424 {
425 DirectedPoint p = null;
426 Set<Lane> lanes = new HashSet<>(gtu.getLanes().keySet());
427 for (Lane lane : lanes)
428 {
429 Set<Lane> adjacentLanes = lane.accessibleAdjacentLanes(direction, gtu.getGTUType());
430 double fraction = gtu.fractionalPosition(lane, gtu.getReference());
431 for (Lane adjacentLane : adjacentLanes)
432 {
433 gtu.enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fraction), gtu.getLanes().get(lane));
434 if (fraction >= 0.0 && fraction <= 1.0)
435 {
436 p = adjacentLane.getCenterLine().getLocationFractionExtended(fraction);
437 this.referenceLane = adjacentLane;
438 this.referencePos = adjacentLane.getLength().multiplyBy(fraction);
439 }
440 System.out.println("gtu " + gtu.getId() + " entered lane " + adjacentLane + " at pos "
441 + adjacentLane.getLength().multiplyBy(fraction));
442 }
443 gtu.leaveLane(lane);
444 }
445
446 if (p == null)
447 {
448 System.err.println("Warning: " + gtu + " not with its reference point on any lane. Came from " + lanes
449 + ", now at " + gtu.getLanes().keySet());
450 Lane l = gtu.getLanes().keySet().iterator().next();
451 double fraction = gtu.fractionalPosition(l, gtu.getReference());
452 p = l.getCenterLine().getLocationFractionExtended(fraction);
453 this.referenceLane = l;
454 this.referencePos = l.getLength().multiplyBy(fraction);
455 }
456
457
458 this.earliestNexLaneChangeTime =
459 gtu.getSimulator().getSimulatorTime().getTime().plus(new Time.Rel(15, TimeUnit.SECOND));
460
461
462 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
463
464 return p;
465 }
466
467
468
469
470
471 public final boolean isDestroyGtuOnFailure()
472 {
473 return this.destroyGtuOnFailure;
474 }
475
476
477
478
479
480 public final void setDestroyGtuOnFailure(final boolean destroyGtuOnFailure)
481 {
482 this.destroyGtuOnFailure = destroyGtuOnFailure;
483 }
484
485 }