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