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 }