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