1 package org.opentrafficsim.road.gtu.lane.plan.operational;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.djunits.unit.AccelerationUnit;
10 import org.djunits.unit.DurationUnit;
11 import org.djunits.unit.LengthUnit;
12 import org.djunits.unit.SpeedUnit;
13 import org.djunits.value.ValueRuntimeException;
14 import org.djunits.value.vdouble.scalar.Acceleration;
15 import org.djunits.value.vdouble.scalar.Duration;
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.djutils.exceptions.Throw;
20 import org.djutils.logger.CategoryLogger;
21 import org.opentrafficsim.base.parameters.ParameterException;
22 import org.opentrafficsim.core.geometry.OTSGeometryException;
23 import org.opentrafficsim.core.geometry.OTSLine3D;
24 import org.opentrafficsim.core.geometry.OTSPoint3D;
25 import org.opentrafficsim.core.gtu.GTUDirectionality;
26 import org.opentrafficsim.core.gtu.GTUException;
27 import org.opentrafficsim.core.gtu.RelativePosition;
28 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
29 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
30 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
31 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
32 import org.opentrafficsim.core.math.Solver;
33 import org.opentrafficsim.core.network.LateralDirectionality;
34 import org.opentrafficsim.core.network.NetworkException;
35 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
36 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
37 import org.opentrafficsim.road.network.lane.Lane;
38 import org.opentrafficsim.road.network.lane.LaneDirection;
39 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
40 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
41
42 import nl.tudelft.simulation.dsol.SimRuntimeException;
43 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
44 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
45 import nl.tudelft.simulation.language.d3.DirectedPoint;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public final class LaneOperationalPlanBuilder
62 {
63
64
65 private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);
66
67
68 private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);
69
70
71
72
73
74 private static final Length MINIMUM_CREDIBLE_PATH_LENGTH = new Length(0.001, LengthUnit.METER);
75
76
77 LaneOperationalPlanBuilder()
78 {
79
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
101 final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration maxAcceleration,
102 final Acceleration maxDeceleration) throws OperationalPlanException, OTSGeometryException
103 {
104 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
105 Segment segment;
106 if (startSpeed.eq(endSpeed))
107 {
108 segment = new SpeedSegment(distance.divide(startSpeed));
109 }
110 else
111 {
112 try
113 {
114
115 Duration duration = distance.times(2.0).divide(endSpeed.plus(startSpeed));
116 Acceleration acceleration = endSpeed.minus(startSpeed).divide(duration);
117 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
118 {
119 acceleration = maxDeceleration;
120 duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
121 DurationUnit.SI);
122 }
123 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
124 {
125 acceleration = maxAcceleration;
126 duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
127 DurationUnit.SI);
128 }
129 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
130 }
131 catch (ValueRuntimeException ve)
132 {
133 throw new OperationalPlanException(ve);
134 }
135 }
136 ArrayList<Segment> segmentList = new ArrayList<>();
137 segmentList.add(segment);
138 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
157 final Time startTime, final Speed startSpeed, final Speed endSpeed)
158 throws OperationalPlanException, OTSGeometryException
159 {
160 return buildGradualAccelerationPlan(gtu, distance, startTime, startSpeed, endSpeed, MAX_ACCELERATION, MAX_DECELERATION);
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
181 final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration acceleration,
182 final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
183 {
184 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
185 ArrayList<Segment> segmentList = new ArrayList<>();
186 if (startSpeed.eq(endSpeed))
187 {
188 segmentList.add(new OperationalPlan.SpeedSegment(distance.divide(startSpeed)));
189 }
190 else
191 {
192 try
193 {
194 if (endSpeed.gt(startSpeed))
195 {
196 Duration t = endSpeed.minus(startSpeed).divide(acceleration);
197 Length x = startSpeed.times(t).plus(acceleration.times(0.5).times(t).times(t));
198 if (x.ge(distance))
199 {
200
201 Duration duration =
202 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
203 DurationUnit.SI);
204 segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
205 }
206 else
207 {
208
209 segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
210 Duration duration = distance.minus(x).divide(endSpeed);
211 segmentList.add(new OperationalPlan.SpeedSegment(duration));
212 }
213 }
214 else
215 {
216 Duration t = endSpeed.minus(startSpeed).divide(deceleration);
217 Length x = startSpeed.times(t).plus(deceleration.times(0.5).times(t).times(t));
218 if (x.ge(distance))
219 {
220
221 Duration duration =
222 new Duration(Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si),
223 DurationUnit.SI);
224 segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
225 }
226 else
227 {
228 if (endSpeed.si == 0.0)
229 {
230
231 OTSLine3D partialPath = path.truncate(x.si);
232 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
233 return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, false);
234 }
235
236 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
237 Duration duration = distance.minus(x).divide(endSpeed);
238 segmentList.add(new OperationalPlan.SpeedSegment(duration));
239 }
240 }
241 }
242 catch (ValueRuntimeException ve)
243 {
244 throw new OperationalPlanException(ve);
245 }
246
247 }
248 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final Time startTime,
268 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final boolean deviative)
269 throws OperationalPlanException, OTSGeometryException
270 {
271 if (startSpeed.si <= OperationalPlan.DRIFTING_SPEED_SI && acceleration.le(Acceleration.ZERO))
272 {
273 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, deviative);
274 }
275
276 Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
277 Length distance =
278 Length.instantiateSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
279 List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
280 if (distance.le(MINIMUM_CREDIBLE_PATH_LENGTH))
281 {
282 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, deviative);
283 }
284 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
285 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, deviative);
286 }
287
288
289
290
291
292
293
294
295 public static OTSLine3D createPathAlongCenterLine(final LaneBasedGTU gtu, final Length distance) throws OTSGeometryException
296 {
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 OTSLine3D path = null;
316 try
317 {
318 DirectedLanePosition ref = gtu.getReferencePosition();
319 double f = ref.getLane().fraction(ref.getPosition());
320 if (ref.getGtuDirection().isPlus() && f < 1.0)
321 {
322 if (f >= 0.0)
323 {
324 path = ref.getLane().getCenterLine().extractFractional(f, 1.0);
325 }
326 else
327 {
328 path = ref.getLane().getCenterLine().extractFractional(0.0, 1.0);
329 }
330 }
331 else if (ref.getGtuDirection().isMinus() && f > 0.0)
332 {
333 if (f <= 1.0)
334 {
335 path = ref.getLane().getCenterLine().extractFractional(0.0, f).reverse();
336 }
337 else
338 {
339 path = ref.getLane().getCenterLine().extractFractional(0.0, 1.0).reverse();
340 }
341 }
342
343
344
345
346 LaneDirection prevFrom = null;
347 LaneDirection from = ref.getLaneDirection();
348 int n = 1;
349 boolean alternativeTried = false;
350 while (path == null || path.getLength().si < distance.si + n * Lane.MARGIN.si)
351 {
352 n++;
353 prevFrom = from;
354 if (null == from)
355 {
356 CategoryLogger.always().warn("About to die: GTU {} has null from value", gtu.getId());
357 }
358 from = from.getNextLaneDirection(gtu);
359 if (from == null)
360 {
361
362 Length pos = prevFrom.getDirection().isPlus() ? prevFrom.getLength() : Length.ZERO;
363 for (SingleSensor sensor : prevFrom.getLane().getSensors(pos, pos, gtu.getGTUType(),
364 prevFrom.getDirection()))
365 {
366
367 if (sensor instanceof SinkSensor)
368 {
369
370 DirectedPoint end = path.getLocationExtendedSI(distance.si + n * Lane.MARGIN.si);
371 List<OTSPoint3D> points = new ArrayList<>(Arrays.asList(path.getPoints()));
372 points.add(new OTSPoint3D(end));
373 return new OTSLine3D(points);
374 }
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427 CategoryLogger.always().error("GTU {} has nowhere to go and no sink sensor either", gtu);
428
429 gtu.destroy();
430 return path;
431 }
432 if (path == null)
433 {
434 path = from.getDirection().isPlus() ? from.getLane().getCenterLine()
435 : from.getLane().getCenterLine().reverse();
436 }
437 else
438 {
439 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, from.getDirection().isPlus()
440 ? from.getLane().getCenterLine() : from.getLane().getCenterLine().reverse());
441 }
442 }
443 }
444 catch (GTUException exception)
445 {
446 throw new RuntimeException("Error during creation of path.", exception);
447 }
448 return path;
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470 @SuppressWarnings("checkstyle:parameternumber")
471 public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
472 final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
473 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
474 throws OperationalPlanException, OTSGeometryException
475 {
476
477
478
479 LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality;
480
481 Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
482 Length planDistance =
483 Length.instantiateSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
484 List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
485
486 try
487 {
488
489 Map<Lane, Length> positions = gtu.positions(gtu.getReference());
490 DirectedLanePosition ref = gtu.getReferencePosition();
491 Iterator<Lane> iterator = ref.getLane()
492 .accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection()).iterator();
493 Lane adjLane = iterator.hasNext() ? iterator.next() : null;
494 DirectedLanePosition from = null;
495 if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
496 {
497
498 from = ref;
499 }
500 else
501 {
502
503 for (Lane lane : positions.keySet())
504 {
505 if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection())
506 .contains(ref.getLane()))
507 {
508 from = new DirectedLanePosition(lane, positions.get(lane), ref.getGtuDirection());
509 break;
510 }
511 }
512 }
513 Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");
514
515
516 OTSLine3D path = laneChange.getPath(timeStep, gtu, from, startPosition, planDistance, direction);
517 LaneBasedOperationalPlanan/operational/LaneBasedOperationalPlan.html#LaneBasedOperationalPlan">LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, true);
518 return plan;
519 }
520 catch (GTUException exception)
521 {
522 throw new RuntimeException("Error during creation of lane change plan.", exception);
523 }
524 }
525
526
527
528
529
530
531
532
533 public static Duration brakingTime(final Acceleration acceleration, final Speed startSpeed, final Duration time)
534 {
535 if (acceleration.ge0())
536 {
537 return time;
538 }
539 double t = startSpeed.si / -acceleration.si;
540 if (t >= time.si)
541 {
542 return time;
543 }
544 return Duration.instantiateSI(t);
545 }
546
547
548
549
550
551
552
553
554
555 private static List<Segment> createAccelerationSegments(final Speed startSpeed, final Acceleration acceleration,
556 final Duration brakingTime, final Duration timeStep)
557 {
558 List<Segment> segmentList = new ArrayList<>();
559 if (brakingTime.si < timeStep.si)
560 {
561 if (brakingTime.si > 0.0)
562 {
563 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
564 }
565 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
566 }
567 else
568 {
569 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
570 }
571 return segmentList;
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
587 final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
588 throws ParameterException, GTUException, NetworkException, OperationalPlanException
589 {
590 Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);
591
592 if (gtu.isInstantaneousLaneChange())
593 {
594 if (simplePlan.isLaneChange())
595 {
596 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
597 }
598 try
599 {
600 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
601 simplePlan.getDuration(), false);
602 }
603 catch (OTSGeometryException exception)
604 {
605 throw new OperationalPlanException(exception);
606 }
607 }
608
609
610 try
611 {
612 if (!simplePlan.isLaneChange() && !laneChange.isChangingLane())
613 {
614 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
615 simplePlan.getDuration(), true);
616 }
617 if (gtu.getSpeed().si == 0.0 && acc.si <= 0.0)
618 {
619 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
620 simplePlan.getDuration(), false);
621 }
622 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
623 gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
624 }
625 catch (OTSGeometryException exception)
626 {
627 throw new OperationalPlanException(exception);
628 }
629 }
630
631
632
633
634
635
636
637
638
639
640
641 public static void scheduleLaneChangeFinalization(final LaneBasedGTU gtu, final Length distance,
642 final LateralDirectionality laneChangeDirection) throws SimRuntimeException
643 {
644 Time time = gtu.getOperationalPlan().timeAtDistance(distance);
645 if (Double.isNaN(time.si))
646 {
647
648 time = gtu.getOperationalPlan().getEndTime();
649 }
650 SimEventInterface<SimTimeDoubleUnit> event = gtu.getSimulator().scheduleEventAbs(time, (short) 6, gtu, gtu,
651 "finalizeLaneChange", new Object[] { laneChangeDirection });
652 gtu.setFinalizeLaneChangeEvent(event);
653 }
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669 public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final Length distance, final Time startTime,
670 final Speed startSpeed, final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
671 {
672 return buildMaximumAccelerationPlan(gtu, distance, startTime, startSpeed, new Speed(0.0, SpeedUnit.SI),
673 new Acceleration(1.0, AccelerationUnit.SI), deceleration);
674 }
675
676 }