1 package org.opentrafficsim.road.gtu.lane.plan.operational;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import nl.tudelft.simulation.language.d3.DirectedPoint;
8
9 import org.djunits.unit.AccelerationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.unit.SpeedUnit;
12 import org.djunits.unit.TimeUnit;
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.opentrafficsim.core.Throw;
20 import org.opentrafficsim.core.geometry.OTSGeometryException;
21 import org.opentrafficsim.core.geometry.OTSLine3D;
22 import org.opentrafficsim.core.geometry.OTSPoint3D;
23 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
24 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
25 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
26 import org.opentrafficsim.core.math.Solver;
27 import org.opentrafficsim.core.network.LateralDirectionality;
28 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
29 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
30 import org.opentrafficsim.road.network.lane.Lane;
31
32 /**
33 * Builder for several often used operational plans, based on a list of lanes. E.g., decelerate to come to a full stop at the
34 * end of a shape; accelerate to reach a certain speed at the end of a curve; drive constant on a curve; decelerate or
35 * accelerate to reach a given end speed at the end of a curve, etc.<br>
36 * TODO driving with negative speeds (backward driving) is not yet supported.
37 * <p>
38 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
39 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
40 * </p>
41 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
42 * initial version Nov 15, 2015 <br>
43 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
44 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
45 */
46 public final class LaneOperationalPlanBuilder
47 {
48 /** Maximum acceleration for unbounded accelerations: 1E12 m/s2. */
49 private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);
50
51 /** Maximum deceleration for unbounded accelerations: -1E12 m/s2. */
52 private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);
53
54 /** Private constructor. */
55 private LaneOperationalPlanBuilder()
56 {
57 // class should not be instantiated
58 }
59
60 /**
61 * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
62 * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
63 * that the end speed is actually reached by this plan.
64 * @param gtu the GTU for debugging purposes
65 * @param lanes a list of connected Lanes to do the driving on
66 * @param firstLanePosition position on the first lane with the reference point of the GTU
67 * @param distance distance to drive for reaching the end speed
68 * @param startTime the current time or a time in the future when the plan should start
69 * @param startSpeed the speed at the start of the path
70 * @param endSpeed the required end speed
71 * @param maxAcceleration the maximum acceleration that can be applied, provided as a POSITIVE number
72 * @param maxDeceleration the maximum deceleration that can be applied, provided as a NEGATIVE number
73 * @return the operational plan to accomplish the given end speed
74 * @throws OperationalPlanException when the plan cannot be generated, e.g. because of a path that is too short
75 * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
76 * constructed segment list differ more than a given threshold
77 * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
78 * first lane
79 */
80 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
81 final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
82 final Speed endSpeed, final Acceleration maxAcceleration, final Acceleration maxDeceleration)
83 throws OperationalPlanException, OTSGeometryException
84 {
85 OTSLine3D path = makePath(lanes, firstLanePosition, distance);
86 OperationalPlan.Segment segment;
87 if (startSpeed.eq(endSpeed))
88 {
89 segment = new SpeedSegment(distance.divideBy(startSpeed));
90 }
91 else
92 {
93 try
94 {
95 // t = 2x / (vt + v0); a = (vt - v0) / t
96 Duration duration = distance.multiplyBy(2.0).divideBy(endSpeed.plus(startSpeed));
97 Acceleration acceleration = endSpeed.minus(startSpeed).divideBy(duration);
98 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
99 {
100 acceleration = maxDeceleration;
101 duration =
102 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
103 TimeUnit.SI);
104 }
105 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
106 {
107 acceleration = maxAcceleration;
108 duration =
109 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
110 TimeUnit.SI);
111 }
112 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
113 }
114 catch (ValueException ve)
115 {
116 throw new OperationalPlanException(ve);
117 }
118 }
119 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
120 segmentList.add(segment);
121 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
122 }
123
124 /**
125 * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
126 * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
127 * that the end speed is actually reached by this plan.
128 * @param lanes a list of connected Lanes to do the driving on
129 * @param firstLanePosition position on the first lane with the reference point of the GTU
130 * @param distance distance to drive for reaching the end speed
131 * @return the driving path as a line
132 * @throws OperationalPlanException when the length of the lanes is less than the distance when we start at the
133 * firstLanePosition on the first lane, or when the lanes list contains no elements
134 * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
135 * first lane
136 */
137 public static OTSLine3D makePath(final List<Lane> lanes, final Length firstLanePosition, final Length distance)
138 throws OperationalPlanException, OTSGeometryException
139 {
140 if (lanes.size() == 0)
141 {
142 throw new OperationalPlanException("LaneOperationalPlanBuilder.makePath got a lanes list with size = 0");
143 }
144 OTSLine3D path = lanes.get(0).getCenterLine().extract(firstLanePosition, lanes.get(0).getLength());
145 for (int i = 1; i < lanes.size(); i++)
146 {
147 path = OTSLine3D.concatenate(0.15, path, lanes.get(i).getCenterLine());
148 }
149 return path.extract(0.0, distance.si);
150 }
151
152 /**
153 * Build a plan with a path and a given start speed to reach a provided end speed, exactly at the end of the curve.
154 * Acceleration and deceleration are virtually unbounded (1E12 m/s2) to reach the end speed (e.g., to come to a complete
155 * stop).
156 * @param gtu the GTU for debugging purposes
157 * @param lanes a list of connected Lanes to do the driving on
158 * @param firstLanePosition position on the first lane with the reference point of the GTU
159 * @param distance distance to drive for reaching the end speed
160 * @param startTime the current time or a time in the future when the plan should start
161 * @param startSpeed the speed at the start of the path
162 * @param endSpeed the required end speed
163 * @return the operational plan to accomplish the given end speed
164 * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
165 * constructed segment list differ more than a given threshold
166 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
167 * first lane
168 */
169 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
170 final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
171 final Speed endSpeed) throws OperationalPlanException, OTSGeometryException
172 {
173 return buildGradualAccelerationPlan(gtu, lanes, firstLanePosition, distance, startTime, startSpeed, endSpeed,
174 MAX_ACCELERATION, MAX_DECELERATION);
175 }
176
177 /**
178 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
179 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
180 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
181 * before completing the path, a truncated path that ends where the GTU stops is used instead.
182 * @param gtu the GTU for debugging purposes
183 * @param lanes a list of connected Lanes to do the driving on
184 * @param firstLanePosition position on the first lane with the reference point of the GTU
185 * @param distance distance to drive for reaching the end speed
186 * @param startTime the current time or a time in the future when the plan should start
187 * @param startSpeed the speed at the start of the path
188 * @param endSpeed the required end speed
189 * @param acceleration the acceleration to use if endSpeed > startSpeed, provided as a POSITIVE number
190 * @param deceleration the deceleration to use if endSpeed < startSpeed, provided as a NEGATIVE number
191 * @return the operational plan to accomplish the given end speed
192 * @throws OperationalPlanException when the construction of the operational path fails
193 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
194 * first lane
195 */
196 public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
197 final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
198 final Speed endSpeed, final Acceleration acceleration, final Acceleration deceleration)
199 throws OperationalPlanException, OTSGeometryException
200 {
201 OTSLine3D path = makePath(lanes, firstLanePosition, distance);
202 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
203 if (startSpeed.eq(endSpeed))
204 {
205 segmentList.add(new OperationalPlan.SpeedSegment(distance.divideBy(startSpeed)));
206 }
207 else
208 {
209 try
210 {
211 if (endSpeed.gt(startSpeed))
212 {
213 Duration t = endSpeed.minus(startSpeed).divideBy(acceleration);
214 Length x = startSpeed.multiplyBy(t).plus(acceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
215 if (x.ge(distance))
216 {
217 // we cannot reach the end speed in the given distance with the given acceleration
218 Duration duration =
219 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
220 TimeUnit.SI);
221 segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
222 }
223 else
224 {
225 // we reach the (higher) end speed before the end of the segment. Make two segments.
226 segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
227 Duration duration = distance.minus(x).divideBy(endSpeed);
228 segmentList.add(new OperationalPlan.SpeedSegment(duration));
229 }
230 }
231 else
232 {
233 Duration t = endSpeed.minus(startSpeed).divideBy(deceleration);
234 Length x = startSpeed.multiplyBy(t).plus(deceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
235 if (x.ge(distance))
236 {
237 // we cannot reach the end speed in the given distance with the given deceleration
238 Duration duration =
239 new Duration(Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si),
240 TimeUnit.SI);
241 segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
242 }
243 else
244 {
245 if (endSpeed.si == 0.0)
246 {
247 // if endSpeed == 0, we cannot reach the end of the path. Therefore, build a partial path.
248 OTSLine3D partialPath = path.truncate(x.si);
249 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
250 return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, lanes);
251 }
252 // we reach the (lower) end speed, larger than zero, before the end of the segment. Make two segments.
253 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
254 Duration duration = distance.minus(x).divideBy(endSpeed);
255 segmentList.add(new OperationalPlan.SpeedSegment(duration));
256 }
257 }
258 }
259 catch (ValueException ve)
260 {
261 throw new OperationalPlanException(ve);
262 }
263
264 }
265 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
266 }
267
268 /**
269 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
270 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
271 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
272 * before completing the path, a truncated path that ends where the GTU stops is used instead.
273 * @param gtu the GTU for debugging purposes
274 * @param lanes a list of connected Lanes to do the driving on
275 * @param firstLanePosition position on the first lane with the reference point of the GTU
276 * @param startTime the current time or a time in the future when the plan should start
277 * @param startSpeed the speed at the start of the path
278 * @param acceleration the acceleration to use
279 * @param timeStep time step for the plan
280 * @return the operational plan to accomplish the given end speed
281 * @throws OperationalPlanException when the construction of the operational path fails
282 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
283 * first lane
284 */
285 public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
286 final Length firstLanePosition, final Time startTime, final Speed startSpeed, final Acceleration acceleration,
287 final Duration timeStep) throws OperationalPlanException, OTSGeometryException
288 {
289
290 if (startSpeed.eq(Speed.ZERO) && acceleration.le(Acceleration.ZERO))
291 {
292 // stand-still
293 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, lanes.get(0));
294 }
295 Length distance;
296 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
297 if (startSpeed.plus(acceleration.multiplyBy(timeStep)).lt(Speed.ZERO))
298 {
299 // will reach stand-still within time step
300 Duration brakingTime = startSpeed.divideBy(acceleration.multiplyBy(-1.0));
301 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
302 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
303 distance =
304 new Length(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si,
305 LengthUnit.SI);
306 }
307 else
308 {
309 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
310 distance =
311 new Length(startSpeed.si * timeStep.si + .5 * acceleration.si * timeStep.si * timeStep.si, LengthUnit.SI);
312 }
313 OTSLine3D path;
314 try
315 {
316 path = makePath(lanes, firstLanePosition, distance);
317 }
318 catch (Exception e)
319 {
320 path = makePath(lanes, firstLanePosition, distance);
321 throw new Error("Bad!");
322 }
323 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, lanes);
324 }
325
326 /**
327 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
328 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
329 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
330 * before completing the path, a truncated path that ends where the GTU stops is used instead.
331 * @param gtu the GTU for debugging purposes
332 * @param fromLanes lanes where the GTU changes from
333 * @param laneChangeDirectionality direction of lane change
334 * @param startPosition current position
335 * @param startTime the current time or a time in the future when the plan should start
336 * @param startSpeed the speed at the start of the path
337 * @param acceleration the acceleration to use
338 * @param timeStep time step for the plan
339 * @param laneChange lane change status
340 * @return the operational plan to accomplish the given end speed
341 * @throws OperationalPlanException when the construction of the operational path fails
342 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
343 * first lane
344 */
345 @SuppressWarnings("checkstyle:parameternumber")
346 public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
347 final List<Lane> fromLanes, final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition,
348 final Time startTime, final Speed startSpeed, final Acceleration acceleration, final Duration timeStep,
349 final LaneChange laneChange) throws OperationalPlanException, OTSGeometryException
350 {
351 Length fromLaneDistance =
352 new Length(startSpeed.si * timeStep.si + .5 * acceleration.si * timeStep.si * timeStep.si, LengthUnit.SI);
353 // TODO also for other driving directions, additional arguments in projectFractional?
354 double fractionalLinkPositionFirst =
355 fromLanes.get(0).getCenterLine().projectFractional(
356 fromLanes.get(0).getParentLink().getStartNode().getDirection(),
357 fromLanes.get(0).getParentLink().getEndNode().getDirection(), startPosition.x, startPosition.y);
358 Length fromLaneFirstPosition = fromLanes.get(0).position(fractionalLinkPositionFirst);
359 Length cumulDistance = fromLanes.get(0).getLength().minus(fromLaneFirstPosition);
360 int lastLaneIndex = 0;
361 while (cumulDistance.lt(fromLaneDistance))
362 {
363 lastLaneIndex++;
364 cumulDistance = cumulDistance.plus(fromLanes.get(lastLaneIndex).getLength());
365 }
366 double fractionalLinkPositionLast =
367 fromLanes.get(lastLaneIndex).getLength().minus(cumulDistance.minus(fromLaneDistance)).si
368 / fromLanes.get(lastLaneIndex).getLength().si;
369
370 List<Lane> toLanes = new ArrayList<>();
371 for (Lane lane : fromLanes)
372 {
373 if (!lane.accessibleAdjacentLanes(laneChangeDirectionality, gtu.getGTUType()).isEmpty())
374 {
375 toLanes.add(lane.accessibleAdjacentLanes(laneChangeDirectionality, gtu.getGTUType()).iterator().next());
376 }
377 else
378 {
379 new Exception().printStackTrace();
380 System.exit(-1);
381 }
382 }
383
384 Length toLaneFirstPosition = toLanes.get(0).position(fractionalLinkPositionFirst);
385 Length fromLaneLastPosition = fromLanes.get(lastLaneIndex).position(fractionalLinkPositionLast);
386 Length toLaneLastPosition = toLanes.get(lastLaneIndex).position(fractionalLinkPositionLast);
387
388 DirectedPoint fromFirst = fromLanes.get(0).getCenterLine().getLocation(fromLaneFirstPosition);
389 DirectedPoint toFirst = toLanes.get(0).getCenterLine().getLocation(toLaneFirstPosition);
390 DirectedPoint fromLast = fromLanes.get(lastLaneIndex).getCenterLine().getLocation(fromLaneLastPosition);
391 DirectedPoint toLast = toLanes.get(lastLaneIndex).getCenterLine().getLocation(toLaneLastPosition);
392
393 double lastFraction = laneChange.updateAndGetFraction(timeStep, laneChangeDirectionality);
394 OTSPoint3D lastPoint =
395 new OTSPoint3D(fromLast.x * (1 - lastFraction) + toLast.x * lastFraction, fromLast.y * (1 - lastFraction)
396 + toLast.y * lastFraction, fromLast.z * (1 - lastFraction) + toLast.z * lastFraction);
397 OTSPoint3D firstPoint = new OTSPoint3D(startPosition);
398 OTSLine3D path = new OTSLine3D(firstPoint, lastPoint);
399
400 double t = timeStep.si;
401 Acceleration a = new Acceleration((2.0 * (path.getLength().si - startSpeed.si * t)) / (t * t), AccelerationUnit.SI);
402 Speed endSpeed = startSpeed.plus(a.multiplyBy(timeStep));
403 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>();
404 if (endSpeed.lt(Speed.ZERO))
405 {
406 Duration brakingTime = startSpeed.divideBy(acceleration.multiplyBy(-1.0));
407 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
408 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
409 }
410 else
411 {
412 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
413 }
414 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, fromLanes);
415 }
416
417 /**
418 * Lane change status across operational plans.
419 * <p>
420 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
421 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
422 * <p>
423 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br>
424 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
425 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
426 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
427 */
428 public static class LaneChange implements Serializable
429 {
430
431 /** */
432 private static final long serialVersionUID = 20160811L;
433
434 /** Lane change progress. */
435 private Duration laneChangeProgress;
436
437 /** Total lane change duration. */
438 private Duration laneChangeDuration;
439
440 /** Whether the GTU is changing lane. */
441 private LateralDirectionality laneChangeDirectionality = null;
442
443 /**
444 * Return whether the GTU is changing lane.
445 * @return whether the GTU is changing lane
446 */
447 public final boolean isChangingLane()
448 {
449 return this.laneChangeDirectionality != null;
450 }
451
452 /**
453 * Return whether the GTU is changing left.
454 * @return whether the GTU is changing left
455 */
456 public final boolean isChangingLeft()
457 {
458 return LateralDirectionality.LEFT.equals(this.laneChangeDirectionality);
459 }
460
461 /**
462 * Return whether the GTU is changing right.
463 * @return whether the GTU is changing right
464 */
465 public final boolean isChangingRight()
466 {
467 return LateralDirectionality.RIGHT.equals(this.laneChangeDirectionality);
468 }
469
470 /**
471 * Target lane of lane change.
472 * @return target lane of lane change
473 * @throws OperationalPlanException If no lane change is being performed.
474 */
475 public final RelativeLane getTargetLane() throws OperationalPlanException
476 {
477 Throw.when(!isChangingLane(), OperationalPlanException.class,
478 "Target lane is requested, but no lane change is being performed.");
479 return isChangingLeft() ? RelativeLane.LEFT : RelativeLane.RIGHT;
480 }
481
482 /**
483 * Sets the duration for a lane change. May be set during a lane change. Whenever the lane change progress exceeds the
484 * lane change duration, the lane change is finalized in the next time step.
485 * @param laneChangeDuration total lane change duration
486 */
487 public final void setLaneChangeDuration(final Duration laneChangeDuration)
488 {
489 this.laneChangeDuration = laneChangeDuration;
490 }
491
492 /**
493 * Update the lane change and return the lateral fraction for the end of the coming time step. This method is for use by
494 * the operational plan builder. It adds the time step duration to the lane change progress. Lane changes are
495 * automatically initiated and finalized.
496 * @param timeStep coming time step duration
497 * @param laneChangeDirection direction of lane change
498 * @return lateral fraction for the end of the coming time step
499 */
500 final double updateAndGetFraction(final Duration timeStep, final LateralDirectionality laneChangeDirection)
501 {
502 if (!isChangingLane())
503 {
504 // initiate lane change
505 this.laneChangeProgress = Duration.ZERO;
506 this.laneChangeDirectionality = laneChangeDirection;
507 }
508 // add current time step
509 this.laneChangeProgress.plus(timeStep);
510 // get lateral fraction at end of current time step
511 double fraction = this.laneChangeProgress.divideBy(this.laneChangeDuration).si;
512 if (fraction >= 1.0)
513 {
514 // limit by 1 and finalize lane change
515 this.laneChangeDirectionality = null;
516 return 1.0;
517 }
518 return fraction;
519 }
520
521 /** {@inheritDoc} */
522 public final String toString()
523 {
524 return "LaneChange [" + this.laneChangeDuration + " of " + this.laneChangeDuration + " to "
525 + this.laneChangeDirectionality + "]";
526 }
527
528 }
529
530 /**
531 * Build a plan with a path and a given start speed to try to come to a stop with a given deceleration. If the GTU can stop
532 * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee
533 * that the OperationalPlan will lead to a complete stop.
534 * @param gtu the GTU for debugging purposes
535 * @param lanes a list of connected Lanes to do the driving on
536 * @param firstLanePosition position on the first lane with the reference point of the GTU
537 * @param distance distance to drive for reaching the end speed
538 * @param startTime the current time or a time in the future when the plan should start
539 * @param startSpeed the speed at the start of the path
540 * @param deceleration the deceleration to use if endSpeed < startSpeed, provided as a NEGATIVE number
541 * @return the operational plan to accomplish the given end speed
542 * @throws OperationalPlanException when construction of the operational path fails
543 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
544 * first lane
545 */
546 public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final List<Lane> lanes,
547 final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed,
548 final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
549 {
550 return buildMaximumAccelerationPlan(gtu, lanes, firstLanePosition, distance, startTime, startSpeed, new Speed(0.0,
551 SpeedUnit.SI), new Acceleration(1.0, AccelerationUnit.SI), deceleration);
552 }
553
554 /*-
555 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime,
556 final Acceleration startAcceleration, final Speed maxSpeed, final Duration duration,
557 final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType,
558 final double laneChangeProgress, final DirectedPoint endPoint) throws OperationalPlanException
559 {
560 return buildSpatialPlan(gtu, startTime, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, duration,
561 fromLanes, toLanes, curvatureType, laneChangeProgress, endPoint);
562 }
563
564 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime,
565 final DirectedPoint startPoint, final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed,
566 final Duration duration, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes,
567 final CurvatureType curvatureType, final double laneChangeProgress, final DirectedPoint endPoint)
568 throws OperationalPlanException
569 {
570
571 // check
572 checkLaneDirections(fromLanes, toLanes);
573
574 // get start fractional position on link
575 final CrossSectionLink startLink = fromLanes.get(0).getLane().getParentLink();
576 Direction start;
577 Direction end;
578 if (fromLanes.get(0).getDirection() == GTUDirectionality.DIR_PLUS)
579 {
580 start = startLink.getStartNode().getDirection();
581 end = startLink.getEndNode().getDirection();
582 }
583 else
584 {
585 start = startLink.getEndNode().getDirection();
586 end = startLink.getStartNode().getDirection();
587 }
588 double fStart = startLink.getDesignLine().projectFractional(start, end, startPoint.x, startPoint.y);
589
590 // get end fractional position on link, and end link
591 double fEnd = 0;
592 CrossSectionLink endLink = null;
593 for (int i = 0; i < toLanes.size(); i++)
594 {
595 CrossSectionLink l = toLanes.get(i).getLane().getParentLink();
596 if (toLanes.get(i).getDirection() == GTUDirectionality.DIR_PLUS)
597 {
598 start = l.getStartNode().getDirection();
599 end = l.getEndNode().getDirection();
600 }
601 else
602 {
603 start = l.getEndNode().getDirection();
604 end = l.getStartNode().getDirection();
605 }
606 fEnd = l.getDesignLine().projectFractional(start, end, endPoint.x, endPoint.y);
607 if (fEnd > 0 && fEnd < 1)
608 {
609 endLink = l;
610 break;
611 }
612 }
613 Throw.when(endLink == null, OperationalPlanException.class, "End point cannot be projected to to-lanes.");
614
615 // build from-line and to-line
616 OTSLine3D from = null;
617 OTSLine3D to = null;
618 for (int i = 0; i < toLanes.size(); i++)
619 {
620 CrossSectionLink l = toLanes.get(i).getLane().getParentLink();
621 try
622 {
623 if (l == startLink)
624 {
625 from = fromLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1);
626 to = toLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1);
627 }
628 else if (l == endLink)
629 {
630 from =
631 OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd));
632 to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd));
633 break;
634 }
635 from = OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine());
636 to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine());
637 }
638 catch (OTSGeometryException exception)
639 {
640 throw new RuntimeException("Bug in buildSpatialPlan method.");
641 }
642 }
643
644 // interpolate path
645 List<OTSPoint3D> line = new ArrayList<>();
646 line.add(new OTSPoint3D(startPoint.x, startPoint.y, startPoint.z));
647 if (curvatureType.equals(CurvatureType.LINEAR))
648 {
649 int n = (int) Math.ceil(32 * (1.0 - laneChangeProgress));
650 for (int i = 1; i < n; i++)
651 {
652 double fraction = 1.0 * i / n;
653 double f0 = laneChangeProgress + (1.0 - laneChangeProgress) * fraction;
654 double f1 = 1.0 - f0;
655 DirectedPoint p1;
656 DirectedPoint p2;
657 try
658 {
659 p1 = from.getLocationFraction(fraction);
660 p2 = to.getLocationFraction(fraction);
661 }
662 catch (OTSGeometryException exception)
663 {
664 throw new RuntimeException("Bug in buildSpatialPlan method.");
665 }
666 line.add(new OTSPoint3D(p1.x * f1 + p2.x * f0, p1.y * f1 + p2.y * f0, p1.z * f1 + p2.z * f0));
667 }
668 }
669 OTSLine3D path;
670 try
671 {
672 path = new OTSLine3D(line);
673 }
674 catch (OTSGeometryException exception)
675 {
676 throw new RuntimeException("Bug in buildSpatialPlan method.");
677 }
678
679 // acceleration segments
680 List<Segment> segmentList = new ArrayList<>();
681 Acceleration b = startAcceleration.multiplyBy(-1.0);
682 if (startSpeed.lt(b.multiplyBy(duration)))
683 {
684 // will reach zero speed within duration
685 Duration d = startSpeed.divideBy(b);
686 segmentList.add(new AccelerationSegment(d, startAcceleration)); // decelerate to zero
687 segmentList.add(new SpeedSegment(duration.minus(d))); // stay at zero for the remainder of duration
688 }
689 else
690 {
691 segmentList.add(new AccelerationSegment(duration, startAcceleration));
692 }
693
694 return new OperationalPlan(gtu, path, startTime, startSpeed, segmentList);
695 }
696
697 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Acceleration startAcceleration,
698 final Speed maxSpeed, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes,
699 final CurvatureType curvatureType, final Duration duration) throws OperationalPlanException
700 {
701 return buildSpatialPlan(gtu, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, fromLanes, toLanes,
702 curvatureType, duration);
703 }
704
705 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final DirectedPoint startPoint,
706 final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed,
707 final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType,
708 final Duration duration) throws OperationalPlanException
709 {
710 checkLaneDirections(fromLanes, toLanes);
711
712 return null;
713 }
714
715 private final static void checkLaneDirections(final List<LaneDirection> fromLanes, List<LaneDirection> toLanes)
716 throws OperationalPlanException
717 {
718 Throw.when(fromLanes == null || toLanes == null, OperationalPlanException.class, "Lane lists may not be null.");
719 Throw.when(fromLanes.isEmpty(), OperationalPlanException.class, "Lane lists may not be empty.");
720 Throw.when(fromLanes.size() != toLanes.size(), OperationalPlanException.class,
721 "Set of from lanes has different length than set of to lanes.");
722 for (int i = 0; i < fromLanes.size(); i++)
723 {
724 Throw.when(!fromLanes.get(i).getLane().getParentLink().equals(toLanes.get(i).getLane().getParentLink()),
725 OperationalPlanException.class,
726 "A lane in the from-lanes list is not on the same link as the lane at equal index in the to-lanes list.");
727 }
728 }
729
730 /**
731 * Defines curvature.
732 * <p>
733 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
734 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
735 * <p>
736 * @version $Revision$, $LastChangedDate$, by $Author$, initial version May 27, 2016 <br>
737 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
738 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
739 */
740 /*-
741 public enum CurvatureType
742 {
743 /** Linear lateral movement. */
744 /*-
745 LINEAR
746 {
747 public double[] getFractions(final double strartFraction)
748 {
749 return new double[1];
750 }
751 },
752
753 /** */
754 /*-
755 SCURVE
756 {
757 public double[] getFractions(final double strartFraction)
758 {
759 return new double[1];
760 }
761 };
762
763 public abstract double[] getFractions(double startFraction);
764 }
765 */
766
767 }