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