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