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