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