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.dsol.SimRuntimeException;
10 import nl.tudelft.simulation.language.d3.DirectedPoint;
11
12 import org.djunits.unit.AccelerationUnit;
13 import org.djunits.unit.SpeedUnit;
14 import org.djunits.unit.TimeUnit;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Speed;
18 import org.djunits.value.vdouble.scalar.Time;
19 import org.opentrafficsim.core.geometry.OTSGeometryException;
20 import org.opentrafficsim.core.geometry.OTSLine3D;
21 import org.opentrafficsim.core.geometry.OTSPoint3D;
22 import org.opentrafficsim.core.gtu.GTU;
23 import org.opentrafficsim.core.gtu.GTUDirectionality;
24 import org.opentrafficsim.core.gtu.GTUException;
25 import org.opentrafficsim.core.gtu.GTUType;
26 import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
28 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
29 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
30 import org.opentrafficsim.core.network.LateralDirectionality;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
33 import org.opentrafficsim.road.gtu.lane.driver.LaneBasedBehavioralCharacteristics;
34 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
35 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedAltruistic;
36 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedEgoistic;
37 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneChangeModel;
38 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneMovementStep;
39 import org.opentrafficsim.road.gtu.lane.tactical.following.AccelerationStep;
40 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
41 import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
42 import org.opentrafficsim.road.network.lane.Lane;
43 import org.opentrafficsim.road.network.lane.LaneDirection;
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
72
73
74
75
76 public class LaneBasedGTUFollowingLaneChangeTacticalPlanner extends AbstractLaneBasedTacticalPlanner
77 {
78
79 private static final long serialVersionUID = 20160129L;
80
81
82 private static final double LANECHANGETIME = 2.0;
83
84
85 private Time.Abs earliestNexLaneChangeTime = Time.Abs.ZERO;
86
87
88 private static final double[] SCURVE;
89
90 static
91 {
92 SCURVE = new double[65];
93 for (int i = 0; i <= 64; i++)
94 {
95 double t = i / 64.0;
96 double ot = 1.0 - t;
97 double t3 = t * t * t;
98 SCURVE[i] = 10.0 * t3 * ot * ot + 5.0 * t3 * t * ot + t3 * t * t;
99 }
100 }
101
102
103
104
105 public LaneBasedGTUFollowingLaneChangeTacticalPlanner()
106 {
107 super();
108 }
109
110
111 @Override
112 public OperationalPlan generateOperationalPlan(final GTU gtu, final Time.Abs startTime,
113 final DirectedPoint locationAtStartTime) throws OperationalPlanException, NetworkException, GTUException
114 {
115
116 LaneBasedGTU laneBasedGTU = (LaneBasedGTU) gtu;
117 LanePerception perception = laneBasedGTU.getPerception();
118 LaneBasedBehavioralCharacteristics drivingCharacteristics = laneBasedGTU.getBehavioralCharacteristics();
119
120
121 laneBasedGTU.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
122
123
124 if (laneBasedGTU.getMaximumVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
125 {
126 return new OperationalPlan(gtu, locationAtStartTime, startTime, new Time.Rel(1.0, TimeUnit.SECOND));
127 }
128
129
130 perception.updateForwardHeadwayGTU();
131 perception.updateAccessibleAdjacentLanesLeft();
132 perception.updateAccessibleAdjacentLanesRight();
133 perception.updateSpeedLimit();
134
135
136 Length.Rel forwardHeadway = drivingCharacteristics.getForwardHeadwayDistance();
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 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT
151 : TurnIndicatorStatus.RIGHT);
152 OperationalPlan laneChangePlan =
153 makeLaneChangePlanMobil(laneBasedGTU, perception, lanePathInfo, direction);
154 if (laneChangePlan != null)
155 {
156 return laneChangePlan;
157 }
158 }
159 }
160 }
161
162
163 if (gtu.getSimulator().getSimulatorTime().getTime().lt(this.earliestNexLaneChangeTime))
164 {
165 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
166 }
167
168
169
170 Set<Lane> leftLanes = perception.getAccessibleAdjacentLanesLeft().get(lanePathInfo.getReferenceLane());
171 if (nextSplitInfo.isSplit())
172 {
173 leftLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
174 }
175 if (!leftLanes.isEmpty() && laneBasedGTU.getVelocity().si > 4.0)
176 {
177 perception.updateBackwardHeadwayGTU();
178 perception.updateParallelGTUsLeft();
179 perception.updateLaneTrafficLeft();
180 if (perception.getParallelGTUsLeft().isEmpty())
181 {
182 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
183 if (perception.getForwardHeadwayGTU() != null && perception.getForwardHeadwayGTU().getGtuId() != null)
184 {
185 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
186 }
187 if (perception.getBackwardHeadwayGTU() != null && perception.getBackwardHeadwayGTU().getGtuId() != null)
188 {
189 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
190 }
191 DirectedLaneChangeModel dlcm = new DirectedAltruistic();
192 DirectedLaneMovementStep dlms =
193 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.LEFT, sameLaneTraffic,
194 perception.getNeighboringGTUsLeft(), laneBasedGTU.getBehavioralCharacteristics()
195 .getForwardHeadwayDistance(), perception.getSpeedLimit(), new Acceleration(1.0,
196 AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI), new Time.Rel(
197 LANECHANGETIME, TimeUnit.SECOND));
198 if (dlms.getLaneChange() != null)
199 {
200 gtu.setTurnIndicatorStatus(TurnIndicatorStatus.LEFT);
201 OperationalPlan laneChangePlan =
202 makeLaneChangePlanMobil(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.LEFT);
203 if (laneChangePlan != null)
204 {
205 return laneChangePlan;
206 }
207 }
208 }
209 }
210
211
212 Set<Lane> rightLanes = perception.getAccessibleAdjacentLanesRight().get(lanePathInfo.getReferenceLane());
213 if (nextSplitInfo.isSplit())
214 {
215 rightLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
216 }
217 if (!rightLanes.isEmpty() && laneBasedGTU.getVelocity().si > 4.0)
218 {
219 perception.updateBackwardHeadwayGTU();
220 perception.updateParallelGTUsRight();
221 perception.updateLaneTrafficRight();
222 if (perception.getParallelGTUsRight().isEmpty())
223 {
224 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
225 if (perception.getForwardHeadwayGTU() != null && perception.getForwardHeadwayGTU().getGtuId() != null)
226 {
227 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
228 }
229 if (perception.getBackwardHeadwayGTU() != null && perception.getBackwardHeadwayGTU().getGtuId() != null)
230 {
231 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
232 }
233 DirectedLaneChangeModel dlcm = new DirectedAltruistic();
234 DirectedLaneMovementStep dlms =
235 dlcm.computeLaneChangeAndAcceleration(laneBasedGTU, LateralDirectionality.RIGHT, sameLaneTraffic,
236 perception.getNeighboringGTUsRight(), laneBasedGTU.getBehavioralCharacteristics()
237 .getForwardHeadwayDistance(), perception.getSpeedLimit(), new Acceleration(1.0,
238 AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI), new Time.Rel(
239 LANECHANGETIME, TimeUnit.SECOND));
240 if (dlms.getLaneChange() != null)
241 {
242 gtu.setTurnIndicatorStatus(TurnIndicatorStatus.RIGHT);
243 OperationalPlan laneChangePlan =
244 makeLaneChangePlanMobil(laneBasedGTU, perception, lanePathInfo, LateralDirectionality.RIGHT);
245 if (laneChangePlan != null)
246 {
247 return laneChangePlan;
248 }
249 }
250 }
251 }
252
253 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
254 }
255
256
257
258
259
260
261
262
263
264
265
266 private OperationalPlan currentLanePlan(final LaneBasedGTU laneBasedGTU, final Time.Abs startTime,
267 final DirectedPoint locationAtStartTime, final LanePathInfo lanePathInfo) throws OperationalPlanException,
268 GTUException
269 {
270 LanePerception perception = laneBasedGTU.getPerception();
271 GTUFollowingModelOld gfm = laneBasedGTU.getBehavioralCharacteristics().getGTUFollowingModel();
272
273
274 AccelerationStep accelerationStep;
275 HeadwayGTU headwayGTU = perception.getForwardHeadwayGTU();
276 Length.Rel maxDistance = lanePathInfo.getPath().getLength().minus(laneBasedGTU.getLength().multiplyBy(2.0));
277 if (headwayGTU.getGtuId() == null)
278 {
279 headwayGTU = new HeadwayGTU("ENDPATH", Speed.ZERO, maxDistance, GTUType.NONE);
280 }
281 accelerationStep =
282 gfm.computeAccelerationStep(laneBasedGTU, headwayGTU.getGtuSpeed(), headwayGTU.getDistance(), maxDistance,
283 perception.getSpeedLimit());
284
285
286 if (accelerationStep.getAcceleration().si < 1E-6
287 && laneBasedGTU.getVelocity().si < OperationalPlan.DRIFTING_SPEED_SI)
288 {
289 return new OperationalPlan(laneBasedGTU, locationAtStartTime, startTime, accelerationStep.getDuration());
290 }
291
292
293 List<Segment> operationalPlanSegmentList = new ArrayList<>();
294 if (accelerationStep.getAcceleration().si == 0.0)
295 {
296 Segment segment = new OperationalPlan.SpeedSegment(accelerationStep.getDuration());
297 operationalPlanSegmentList.add(segment);
298 }
299 else
300 {
301 Segment segment =
302 new OperationalPlan.AccelerationSegment(accelerationStep.getDuration(),
303 accelerationStep.getAcceleration());
304 operationalPlanSegmentList.add(segment);
305 }
306 OperationalPlan op =
307 new OperationalPlan(laneBasedGTU, lanePathInfo.getPath(), startTime, laneBasedGTU.getVelocity(),
308 operationalPlanSegmentList);
309 return op;
310 }
311
312
313
314
315
316
317
318 private LateralDirectionality
319 determineLeftRight(final LaneBasedGTU laneBasedGTU, final NextSplitInfo nextSplitInfo)
320 {
321
322 for (Lane correctLane : nextSplitInfo.getCorrectCurrentLanes())
323 {
324 for (Lane currentLane : laneBasedGTU.getLanes().keySet())
325 {
326 if (correctLane.getParentLink().equals(currentLane.getParentLink()))
327 {
328 double deltaOffset =
329 correctLane.getDesignLineOffsetAtBegin().si - currentLane.getDesignLineOffsetAtBegin().si;
330 if (laneBasedGTU.getLanes().get(currentLane).equals(GTUDirectionality.DIR_PLUS))
331 {
332 return deltaOffset > 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
333 }
334 else
335 {
336 return deltaOffset < 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
337 }
338 }
339 }
340 }
341 return null;
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355 private OperationalPlan makeLaneChangePlanMobil(final LaneBasedGTU gtu, final LanePerception perception,
356 final LanePathInfo lanePathInfo, final LateralDirectionality direction) throws GTUException, NetworkException
357 {
358 Collection<HeadwayGTU> otherLaneTraffic;
359 perception.updateForwardHeadwayGTU();
360 perception.updateBackwardHeadwayGTU();
361 if (direction.isLeft())
362 {
363 perception.updateParallelGTUsLeft();
364 perception.updateLaneTrafficLeft();
365 otherLaneTraffic = perception.getNeighboringGTUsLeft();
366 }
367 else
368 {
369 perception.updateParallelGTUsRight();
370 perception.updateLaneTrafficRight();
371 otherLaneTraffic = perception.getNeighboringGTUsRight();
372 }
373 if (!perception.parallelGTUs(direction).isEmpty())
374 {
375 return null;
376 }
377
378 Collection<HeadwayGTU> sameLaneTraffic = new HashSet<>();
379 if (perception.getForwardHeadwayGTU() != null && perception.getForwardHeadwayGTU().getGtuId() != null)
380 {
381 sameLaneTraffic.add(perception.getForwardHeadwayGTU());
382 }
383 if (perception.getBackwardHeadwayGTU() != null && perception.getBackwardHeadwayGTU().getGtuId() != null)
384 {
385 sameLaneTraffic.add(perception.getBackwardHeadwayGTU());
386 }
387 Length.Rel maxDistance = lanePathInfo.getPath().getLength().minus(gtu.getLength().multiplyBy(2.0));
388 sameLaneTraffic.add(new HeadwayGTU("ENDPATH", Speed.ZERO, maxDistance, GTUType.NONE));
389 otherLaneTraffic.add(new HeadwayGTU("ENDPATH", Speed.ZERO, maxDistance, GTUType.NONE));
390
391
392
393 DirectedLaneChangeModel dlcm = new DirectedEgoistic();
394
395 DirectedLaneMovementStep dlms =
396 dlcm.computeLaneChangeAndAcceleration(gtu, direction, sameLaneTraffic, otherLaneTraffic, gtu
397 .getBehavioralCharacteristics().getForwardHeadwayDistance(), perception.getSpeedLimit(),
398 new Acceleration(2.0, AccelerationUnit.SI), new Acceleration(0.1, AccelerationUnit.SI), new Time.Rel(
399 LANECHANGETIME, TimeUnit.SECOND));
400 if (dlms.getLaneChange() == null)
401 {
402 return null;
403 }
404
405 Lane startLane = getReferenceLane(gtu);
406 Set<Lane> adjacentLanes = startLane.accessibleAdjacentLanes(direction, gtu.getGTUType());
407
408 Lane adjacentLane = adjacentLanes.iterator().next();
409 Length.Rel startPosition = gtu.position(startLane, gtu.getReference());
410 double fraction2 = startLane.fraction(startPosition);
411 LanePathInfo lanePathInfo2 =
412 buildLanePathInfo(gtu, gtu.getBehavioralCharacteristics().getForwardHeadwayDistance(), adjacentLane,
413 fraction2, gtu.getLanes().get(startLane));
414
415
416 AccelerationStep accelerationStep = dlms.getGfmr();
417 Speed v0 = gtu.getVelocity();
418 double t = accelerationStep.getDuration().si;
419 double distanceSI = v0.si * t + 0.5 * accelerationStep.getAcceleration().si * t * t;
420 Speed vt = v0.plus(accelerationStep.getAcceleration().multiplyBy(accelerationStep.getDuration()));
421
422
423
424 if (distanceSI < 2.0)
425 {
426 return null;
427 }
428
429 if (perception.getForwardHeadwayGTU() == null
430 || (perception.getForwardHeadwayGTU() != null && perception.getForwardHeadwayGTU().getDistance().si < 5.0))
431 {
432 return null;
433 }
434
435 OTSLine3D path;
436 try
437 {
438 path = interpolateScurve(lanePathInfo.getPath(), lanePathInfo2.getPath(), distanceSI);
439 }
440 catch (OTSGeometryException exception)
441 {
442 System.err.println("GTU : " + gtu);
443 System.err.println("LanePathInfo : " + lanePathInfo.getPath());
444 System.err.println("LanePathInfo2: " + lanePathInfo2.getPath());
445 System.err.println("distanceSI : " + distanceSI);
446 System.err.println("v0, t, vt, a : " + v0 + ", " + t + ", " + vt + ", "
447 + accelerationStep.getAcceleration());
448 throw new GTUException(exception);
449 }
450
451 try
452 {
453 double a = accelerationStep.getAcceleration().si;
454
455 if (path.getLengthSI() > distanceSI * 1.5)
456 {
457 a = (path.getLengthSI() - v0.si) / LANECHANGETIME;
458 vt = new Speed(v0.si + LANECHANGETIME * a, SpeedUnit.SI);
459 }
460
461
462
463 for (Lane lane : gtu.getLanes().keySet())
464 {
465 gtu.getSimulator().scheduleEventRel(new Time.Rel(LANECHANGETIME - 0.001, TimeUnit.SI), this, gtu,
466 "leaveLane", new Object[]{lane});
467 }
468
469
470 for (LaneDirection laneDirection : lanePathInfo.getLaneDirectionList())
471 {
472 if (!gtu.getLanes().keySet().contains(laneDirection.getLane()))
473 {
474 gtu.getSimulator().scheduleEventRel(new Time.Rel(LANECHANGETIME - 0.001, TimeUnit.SI), this, gtu,
475 "leaveLane", new Object[]{laneDirection.getLane()});
476 }
477 }
478
479 gtu.enterLane(adjacentLane, adjacentLane.getLength().multiplyBy(fraction2), gtu.getLanes().get(startLane));
480 System.out.println("gtu " + gtu.getId() + " entered lane " + adjacentLane + " at pos "
481 + adjacentLane.getLength().multiplyBy(fraction2));
482
483 List<Segment> operationalPlanSegmentList = new ArrayList<>();
484 if (a == 0.0)
485 {
486 Segment segment = new OperationalPlan.SpeedSegment(new Time.Rel(LANECHANGETIME, TimeUnit.SI));
487 operationalPlanSegmentList.add(segment);
488 }
489 else
490 {
491 Segment segment =
492 new OperationalPlan.AccelerationSegment(new Time.Rel(LANECHANGETIME, TimeUnit.SI),
493 new Acceleration(a, AccelerationUnit.SI));
494 operationalPlanSegmentList.add(segment);
495 }
496 OperationalPlan op =
497 new OperationalPlan(gtu, path, gtu.getSimulator().getSimulatorTime().getTime(), v0,
498 operationalPlanSegmentList);
499 this.earliestNexLaneChangeTime =
500 gtu.getSimulator().getSimulatorTime().getTime().plus(new Time.Rel(17, TimeUnit.SECOND));
501
502
503 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
504
505 return op;
506 }
507 catch (OperationalPlanException | SimRuntimeException exception)
508 {
509 throw new GTUException(exception);
510 }
511 }
512
513
514
515
516
517
518
519
520
521 private static OTSLine3D interpolateLinear(OTSLine3D line1, OTSLine3D line2, final double lengthSI)
522 throws OTSGeometryException
523 {
524 OTSLine3D l1 = line1.extract(0, lengthSI);
525 OTSLine3D l2 = line2.extract(0, lengthSI);
526 List<OTSPoint3D> line = new ArrayList<>();
527 int num = 32;
528 for (int i = 0; i <= num; i++)
529 {
530 double f0 = 1.0 * i / num;
531 double f1 = 1.0 - f0;
532 DirectedPoint p1 = l1.getLocationFraction(f0);
533 DirectedPoint p2 = l2.getLocationFraction(f0);
534 line.add(new OTSPoint3D(p1.x * f1 + p2.x * f0, p1.y * f1 + p2.y * f0, p1.z * f1 + p2.z * f0));
535 }
536 return new OTSLine3D(line);
537 }
538
539
540
541
542
543
544
545
546
547 private static OTSLine3D interpolateScurve(OTSLine3D line1, OTSLine3D line2, final double lengthSI)
548 throws OTSGeometryException
549 {
550 OTSLine3D l1 = line1.extract(0, lengthSI);
551 OTSLine3D l2 = line2.extract(0, lengthSI);
552 List<OTSPoint3D> line = new ArrayList<>();
553 int num = 64;
554 for (int i = 0; i <= num; i++)
555 {
556 double factor = SCURVE[i];
557 DirectedPoint p1 = l1.getLocationFraction(i / 64.0);
558 DirectedPoint p2 = l2.getLocationFraction(i / 64.0);
559 line.add(new OTSPoint3D(p1.x + factor * (p2.x - p1.x), p1.y + factor * (p2.y - p1.y), p1.z + factor
560 * (p2.z - p1.z)));
561 }
562 return new OTSLine3D(line);
563 }
564 }