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 }