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 path = ref.getGtuDirection().isPlus() ? ref.getLane().getCenterLine().extractFractional(f, 1.0)
298 : ref.getLane().getCenterLine().extractFractional(0.0, f).reverse();
299 LaneDirection prevFrom = null;
300 LaneDirection from = ref.getLaneDirection();
301 int n = 1;
302 while (path.getLength().si < distance.si + n * Lane.MARGIN.si)
303 {
304 n++;
305 prevFrom = from;
306 from = from.getNextLaneDirection(gtu);
307 try
308 {
309 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, from.getDirection().isPlus()
310 ? from.getLane().getCenterLine() : from.getLane().getCenterLine().reverse());
311 }
312 catch (NullPointerException nas)
313 {
314 prevFrom.getNextLaneDirection(gtu);
315 ref.getLaneDirection().getNextLaneDirection(gtu);
316 }
317 }
318 }
319 catch (GTUException exception)
320 {
321 throw new RuntimeException("Error during creation of path.", exception);
322 }
323 return path;
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 @SuppressWarnings("checkstyle:parameternumber")
346 public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
347 final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
348 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
349 throws OperationalPlanException, OTSGeometryException
350 {
351
352
353
354 LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality;
355
356 Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
357 Length planDistance =
358 Length.createSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
359 List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
360
361 try
362 {
363
364 Map<Lane, Length> positions = gtu.positions(gtu.getReference());
365 DirectedLanePosition ref = gtu.getReferencePosition();
366 Iterator<Lane> iterator = ref.getLane()
367 .accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection()).iterator();
368 Lane adjLane = iterator.hasNext() ? iterator.next() : null;
369 DirectedLanePosition from = null;
370 if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
371 {
372
373 from = ref;
374 }
375 else
376 {
377
378 for (Lane lane : positions.keySet())
379 {
380 if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection())
381 .contains(ref.getLane()))
382 {
383 from = new DirectedLanePosition(lane, positions.get(lane), ref.getGtuDirection());
384 break;
385 }
386 }
387 }
388 Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");
389
390
391 OTSLine3D path = laneChange.getPath(timeStep, gtu, from, startPosition, planDistance, direction);
392 LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, true);
393 return plan;
394 }
395 catch (GTUException exception)
396 {
397 throw new RuntimeException("Error during creation of lane change plan.", exception);
398 }
399 }
400
401
402
403
404
405
406
407 private static Acceleration gtuCapabilities(final Acceleration acceleration, final LaneBasedGTU gtu)
408 {
409 return acceleration;
410
411
412 }
413
414
415
416
417
418
419
420
421 public static Duration brakingTime(final Acceleration acceleration, final Speed startSpeed, final Duration time)
422 {
423 if (acceleration.ge0())
424 {
425 return time;
426 }
427 double t = startSpeed.si / -acceleration.si;
428 if (t >= time.si)
429 {
430 return time;
431 }
432 return Duration.createSI(t);
433 }
434
435
436
437
438
439
440
441
442
443 private static List<Segment> createAccelerationSegments(final Speed startSpeed, final Acceleration acceleration,
444 final Duration brakingTime, final Duration timeStep)
445 {
446 List<Segment> segmentList = new ArrayList<>();
447 if (brakingTime.si < timeStep.si)
448 {
449 if (brakingTime.si > 0.0)
450 {
451 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
452 }
453 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
454 }
455 else
456 {
457 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
458 }
459 return segmentList;
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
475 final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
476 throws ParameterException, GTUException, NetworkException, OperationalPlanException
477 {
478 Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);
479
480 if (INSTANT_LANE_CHANGES)
481 {
482 if (simplePlan.isLaneChange())
483 {
484 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
485 }
486 try
487 {
488 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
489 simplePlan.getDuration());
490 }
491 catch (OTSGeometryException exception)
492 {
493 throw new OperationalPlanException(exception);
494 }
495 }
496
497
498 try
499 {
500 if ((!simplePlan.isLaneChange() && !laneChange.isChangingLane()) || (gtu.getSpeed().si == 0.0 && acc.si <= 0.0))
501 {
502 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
503 simplePlan.getDuration());
504 }
505 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
506 gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
507 }
508 catch (OTSGeometryException exception)
509 {
510 throw new OperationalPlanException(exception);
511 }
512 }
513
514
515
516
517
518
519
520
521
522
523
524 public static void scheduleLaneChangeFinalization(final LaneBasedGTU gtu, final Length distance,
525 final LateralDirectionality laneChangeDirection) throws SimRuntimeException
526 {
527 Time time = gtu.getOperationalPlan().timeAtDistance(distance);
528 if (Double.isNaN(time.si))
529 {
530
531 time = gtu.getOperationalPlan().getEndTime();
532 }
533 SimEventInterface<SimTimeDoubleUnit> event = gtu.getSimulator().scheduleEventAbs(time, (short) 6, gtu, gtu,
534 "finalizeLaneChange", new Object[] { laneChangeDirection });
535 gtu.setFinalizeLaneChangeEvent(event);
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552 public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final Length distance, final Time startTime,
553 final Speed startSpeed, final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
554 {
555 return buildMaximumAccelerationPlan(gtu, distance, startTime, startSpeed, new Speed(0.0, SpeedUnit.SI),
556 new Acceleration(1.0, AccelerationUnit.SI), deceleration);
557 }
558
559 }