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 import java.util.Map; 7 import java.util.Set; 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.geometry.OTSGeometryException; 20 import org.opentrafficsim.core.geometry.OTSLine3D; 21 import org.opentrafficsim.core.geometry.OTSPoint3D; 22 import org.opentrafficsim.core.gtu.GTUException; 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.AbstractLaneBasedGTU; 29 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU; 30 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane; 31 import org.opentrafficsim.road.network.lane.DirectedLanePosition; 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 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, lanes.get(0)); 295 } 296 Length distance; 297 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>(); 298 if (startSpeed.plus(acceleration.multiplyBy(timeStep)).lt(Speed.ZERO)) 299 { 300 // will reach stand-still within time step 301 Duration brakingTime = startSpeed.divideBy(acceleration.neg()); 302 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration)); 303 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime))); 304 distance = 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, final List<Lane> fromLanes, 347 final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime, 348 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange) 349 throws OperationalPlanException, OTSGeometryException 350 { 351 // on first call during lane change, use laneChangeDirectionality as laneChange.getDirection() is probably NONE 352 LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality; 353 Length fromLaneDistance = 354 new Length(startSpeed.si * timeStep.si + .5 * acceleration.si * timeStep.si * timeStep.si, LengthUnit.SI); 355 // TODO also for other driving directions, additional arguments in projectFractional? 356 double firstFractionalPosition = fromLanes.get(0).getCenterLine().projectFractional( 357 fromLanes.get(0).getParentLink().getStartNode().getDirection(), 358 fromLanes.get(0).getParentLink().getEndNode().getDirection(), startPosition.x, startPosition.y); 359 Length fromLaneFirstPosition = fromLanes.get(0).position(firstFractionalPosition); 360 Length cumulDistance = fromLanes.get(0).getLength().minus(fromLaneFirstPosition); 361 int lastLaneIndex = 0; 362 while (cumulDistance.lt(fromLaneDistance)) 363 { 364 lastLaneIndex++; 365 cumulDistance = cumulDistance.plus(fromLanes.get(lastLaneIndex).getLength()); 366 } 367 double lastFractionalPosition = 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(direction, gtu.getGTUType()).isEmpty()) 374 { 375 toLanes.add(lane.accessibleAdjacentLanes(direction, 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(firstFractionalPosition); 385 Length fromLaneLastPosition = fromLanes.get(lastLaneIndex).position(lastFractionalPosition); 386 Length toLaneLastPosition = toLanes.get(lastLaneIndex).position(lastFractionalPosition); 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, direction, gtu); 394 OTSPoint3D lastPoint = new OTSPoint3D(fromLast.x * (1 - lastFraction) + toLast.x * lastFraction, 395 fromLast.y * (1 - lastFraction) + toLast.y * lastFraction, 396 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 // why was this used? 402 // Acceleration a = new Acceleration((2.0 * (path.getLength().si - startSpeed.si * t)) / (t * t), AccelerationUnit.SI); 403 Speed endSpeed = startSpeed.plus(acceleration.multiplyBy(timeStep)); 404 ArrayList<OperationalPlan.Segment> segmentList = new ArrayList<>(); 405 if (endSpeed.lt(Speed.ZERO)) 406 { 407 Duration brakingTime = startSpeed.divideBy(acceleration.neg()); 408 if (brakingTime.si > 0.0) 409 { 410 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration)); 411 } 412 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime))); 413 } 414 else 415 { 416 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration)); 417 } 418 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, fromLanes, toLanes, lastLaneIndex, 419 lastFractionalPosition); 420 } 421 422 /** 423 * Lane change status across operational plans. 424 * <p> 425 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. 426 * <br> 427 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>. 428 * <p> 429 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br> 430 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a> 431 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a> 432 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a> 433 */ 434 public static class LaneChange implements Serializable 435 { 436 437 /** */ 438 private static final long serialVersionUID = 20160811L; 439 440 /** Lane change progress. */ 441 private Duration laneChangeProgress; 442 443 /** Total lane change duration. */ 444 private Duration laneChangeDuration; 445 446 /** Whether the GTU is changing lane. */ 447 private LateralDirectionality laneChangeDirectionality = null; 448 449 /** 450 * Return whether the GTU is changing lane. 451 * @return whether the GTU is changing lane 452 */ 453 public final boolean isChangingLane() 454 { 455 return this.laneChangeDirectionality != null; 456 } 457 458 /** 459 * Return whether the GTU is changing left. 460 * @return whether the GTU is changing left 461 */ 462 public final boolean isChangingLeft() 463 { 464 return LateralDirectionality.LEFT.equals(this.laneChangeDirectionality); 465 } 466 467 /** 468 * Return whether the GTU is changing right. 469 * @return whether the GTU is changing right 470 */ 471 public final boolean isChangingRight() 472 { 473 return LateralDirectionality.RIGHT.equals(this.laneChangeDirectionality); 474 } 475 476 /** 477 * Return lateral lane change direction. 478 * @return LateralDirectionality; lateral lane change direction 479 */ 480 public final LateralDirectionality getDirection() 481 { 482 return this.laneChangeDirectionality; 483 } 484 485 /** 486 * @param laneChangeDirectionality set laneChangeDirectionality. 487 */ 488 public final void setLaneChangeDirectionality(final LateralDirectionality laneChangeDirectionality) 489 { 490 this.laneChangeDirectionality = laneChangeDirectionality; 491 } 492 493 /** 494 * Second lane of lane change relative to the reference lane. Note that the reference lane may either be the source or 495 * the target lane. Thus, the second lane during a lane change may either be the left or right lane, regardless of the 496 * lane change direction. 497 * @param gtu LaneBasedGTU; gtu 498 * @return target lane of lane change 499 * @throws OperationalPlanException If no lane change is being performed. 500 */ 501 public final RelativeLane getSecondLane(final LaneBasedGTU gtu) throws OperationalPlanException 502 { 503 Throw.when(!isChangingLane(), OperationalPlanException.class, 504 "Target lane is requested, but no lane change is being performed."); 505 Map<Lane, Length> map; 506 DirectedLanePosition dlp; 507 try 508 { 509 map = gtu.positions(gtu.getReference()); 510 dlp = gtu.getReferencePosition(); 511 } 512 catch (GTUException exception) 513 { 514 throw new OperationalPlanException("Second lane of lane change could not be determined.", exception); 515 } 516 Set<Lane> accessibleLanes = dlp.getLane().accessibleAdjacentLanes(this.laneChangeDirectionality, gtu.getGTUType()); 517 if (!accessibleLanes.isEmpty() && map.containsKey(accessibleLanes.iterator().next())) 518 { 519 return isChangingLeft() ? RelativeLane.LEFT : RelativeLane.RIGHT; 520 } 521 return isChangingLeft() ? RelativeLane.RIGHT : RelativeLane.LEFT; 522 } 523 524 /** 525 * Sets the duration for a lane change. May be set during a lane change. Whenever the lane change progress exceeds the 526 * lane change duration, the lane change is finalized in the next time step. 527 * @param laneChangeDuration total lane change duration 528 */ 529 public final void setLaneChangeDuration(final Duration laneChangeDuration) 530 { 531 this.laneChangeDuration = laneChangeDuration; 532 } 533 534 /** 535 * Update the lane change and return the lateral fraction for the end of the coming time step. This method is for use by 536 * the operational plan builder. It adds the time step duration to the lane change progress. Lane changes are 537 * automatically initiated and finalized. 538 * @param timeStep coming time step duration 539 * @param laneChangeDirection direction of lane change 540 * @param gtu GTU 541 * @return lateral fraction for the end of the coming time step 542 */ 543 // TODO remove GTU argument (only needed for instantaneous lane change hack) 544 final double updateAndGetFraction(final Duration timeStep, final LateralDirectionality laneChangeDirection, 545 final LaneBasedGTU gtu) 546 { 547 if (!isChangingLane()) 548 { 549 // initiate lane change 550 this.laneChangeProgress = Duration.ZERO; 551 this.laneChangeDirectionality = laneChangeDirection; 552 try 553 { 554 ((AbstractLaneBasedGTU) gtu).initLaneChange(laneChangeDirection); 555 } 556 catch (GTUException exception) 557 { 558 throw new RuntimeException("Error during lane change initialization.", exception); 559 } 560 } 561 // add current time step 562 this.laneChangeProgress = this.laneChangeProgress.plus(timeStep); 563 // get lateral fraction at end of current time step 564 double fraction = this.laneChangeProgress.divideBy(this.laneChangeDuration).si; 565 if (fraction >= 1.0) 566 { 567 // TODO this elsewhere based on path 568 try 569 { 570 ((AbstractLaneBasedGTU) gtu).finalizeLaneChange(laneChangeDirection, 571 gtu.getSimulator().getSimulatorTime().getTime().plus(timeStep)); 572 } 573 catch (GTUException exception) 574 { 575 throw new RuntimeException("Error during lane change finalization.", exception); 576 } 577 // limit by 1 and finalize lane change 578 this.laneChangeDirectionality = null; 579 return 1.0; 580 } 581 return fraction; 582 } 583 584 /** {@inheritDoc} */ 585 public final String toString() 586 { 587 return "LaneChange [" + this.laneChangeProgress + " of " + this.laneChangeDuration + " to " 588 + this.laneChangeDirectionality + "]"; 589 } 590 591 } 592 593 /** 594 * 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 595 * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee 596 * that the OperationalPlan will lead to a complete stop. 597 * @param gtu the GTU for debugging purposes 598 * @param lanes a list of connected Lanes to do the driving on 599 * @param firstLanePosition position on the first lane with the reference point of the GTU 600 * @param distance distance to drive for reaching the end speed 601 * @param startTime the current time or a time in the future when the plan should start 602 * @param startSpeed the speed at the start of the path 603 * @param deceleration the deceleration to use if endSpeed < startSpeed, provided as a NEGATIVE number 604 * @return the operational plan to accomplish the given end speed 605 * @throws OperationalPlanException when construction of the operational path fails 606 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the 607 * first lane 608 */ 609 public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final List<Lane> lanes, 610 final Length firstLanePosition, final Length distance, final Time startTime, final Speed startSpeed, 611 final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException 612 { 613 return buildMaximumAccelerationPlan(gtu, lanes, firstLanePosition, distance, startTime, startSpeed, 614 new Speed(0.0, SpeedUnit.SI), new Acceleration(1.0, AccelerationUnit.SI), deceleration); 615 } 616 617 /*- 618 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime, 619 final Acceleration startAcceleration, final Speed maxSpeed, final Duration duration, 620 final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType, 621 final double laneChangeProgress, final DirectedPoint endPoint) throws OperationalPlanException 622 { 623 return buildSpatialPlan(gtu, startTime, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, duration, 624 fromLanes, toLanes, curvatureType, laneChangeProgress, endPoint); 625 } 626 627 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Time startTime, 628 final DirectedPoint startPoint, final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed, 629 final Duration duration, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, 630 final CurvatureType curvatureType, final double laneChangeProgress, final DirectedPoint endPoint) 631 throws OperationalPlanException 632 { 633 634 // check 635 checkLaneDirections(fromLanes, toLanes); 636 637 // get start fractional position on link 638 final CrossSectionLink startLink = fromLanes.get(0).getLane().getParentLink(); 639 Direction start; 640 Direction end; 641 if (fromLanes.get(0).getDirection() == GTUDirectionality.DIR_PLUS) 642 { 643 start = startLink.getStartNode().getDirection(); 644 end = startLink.getEndNode().getDirection(); 645 } 646 else 647 { 648 start = startLink.getEndNode().getDirection(); 649 end = startLink.getStartNode().getDirection(); 650 } 651 double fStart = startLink.getDesignLine().projectFractional(start, end, startPoint.x, startPoint.y); 652 653 // get end fractional position on link, and end link 654 double fEnd = 0; 655 CrossSectionLink endLink = null; 656 for (int i = 0; i < toLanes.size(); i++) 657 { 658 CrossSectionLink l = toLanes.get(i).getLane().getParentLink(); 659 if (toLanes.get(i).getDirection() == GTUDirectionality.DIR_PLUS) 660 { 661 start = l.getStartNode().getDirection(); 662 end = l.getEndNode().getDirection(); 663 } 664 else 665 { 666 start = l.getEndNode().getDirection(); 667 end = l.getStartNode().getDirection(); 668 } 669 fEnd = l.getDesignLine().projectFractional(start, end, endPoint.x, endPoint.y); 670 if (fEnd > 0 && fEnd < 1) 671 { 672 endLink = l; 673 break; 674 } 675 } 676 Throw.when(endLink == null, OperationalPlanException.class, "End point cannot be projected to to-lanes."); 677 678 // build from-line and to-line 679 OTSLine3D from = null; 680 OTSLine3D to = null; 681 for (int i = 0; i < toLanes.size(); i++) 682 { 683 CrossSectionLink l = toLanes.get(i).getLane().getParentLink(); 684 try 685 { 686 if (l == startLink) 687 { 688 from = fromLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1); 689 to = toLanes.get(i).getLane().getCenterLine().extractFractional(fStart, 1); 690 } 691 else if (l == endLink) 692 { 693 from = 694 OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd)); 695 to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine().extractFractional(0, fEnd)); 696 break; 697 } 698 from = OTSLine3D.concatenate(from, fromLanes.get(i).getLane().getCenterLine()); 699 to = OTSLine3D.concatenate(to, toLanes.get(i).getLane().getCenterLine()); 700 } 701 catch (OTSGeometryException exception) 702 { 703 throw new RuntimeException("Bug in buildSpatialPlan method."); 704 } 705 } 706 707 // interpolate path 708 List<OTSPoint3D> line = new ArrayList<>(); 709 line.add(new OTSPoint3D(startPoint.x, startPoint.y, startPoint.z)); 710 if (curvatureType.equals(CurvatureType.LINEAR)) 711 { 712 int n = (int) Math.ceil(32 * (1.0 - laneChangeProgress)); 713 for (int i = 1; i < n; i++) 714 { 715 double fraction = 1.0 * i / n; 716 double f0 = laneChangeProgress + (1.0 - laneChangeProgress) * fraction; 717 double f1 = 1.0 - f0; 718 DirectedPoint p1; 719 DirectedPoint p2; 720 try 721 { 722 p1 = from.getLocationFraction(fraction); 723 p2 = to.getLocationFraction(fraction); 724 } 725 catch (OTSGeometryException exception) 726 { 727 throw new RuntimeException("Bug in buildSpatialPlan method."); 728 } 729 line.add(new OTSPoint3D(p1.x * f1 + p2.x * f0, p1.y * f1 + p2.y * f0, p1.z * f1 + p2.z * f0)); 730 } 731 } 732 OTSLine3D path; 733 try 734 { 735 path = new OTSLine3D(line); 736 } 737 catch (OTSGeometryException exception) 738 { 739 throw new RuntimeException("Bug in buildSpatialPlan method."); 740 } 741 742 // acceleration segments 743 List<Segment> segmentList = new ArrayList<>(); 744 Acceleration b = startAcceleration.neg(); 745 if (startSpeed.lt(b.multiplyBy(duration))) 746 { 747 // will reach zero speed within duration 748 Duration d = startSpeed.divideBy(b); 749 segmentList.add(new AccelerationSegment(d, startAcceleration)); // decelerate to zero 750 segmentList.add(new SpeedSegment(duration.minus(d))); // stay at zero for the remainder of duration 751 } 752 else 753 { 754 segmentList.add(new AccelerationSegment(duration, startAcceleration)); 755 } 756 757 return new OperationalPlan(gtu, path, startTime, startSpeed, segmentList); 758 } 759 760 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final Acceleration startAcceleration, 761 final Speed maxSpeed, final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, 762 final CurvatureType curvatureType, final Duration duration) throws OperationalPlanException 763 { 764 return buildSpatialPlan(gtu, gtu.getLocation(), gtu.getSpeed(), startAcceleration, maxSpeed, fromLanes, toLanes, 765 curvatureType, duration); 766 } 767 768 public static OperationalPlan buildSpatialPlan(final LaneBasedGTU gtu, final DirectedPoint startPoint, 769 final Speed startSpeed, final Acceleration startAcceleration, final Speed maxSpeed, 770 final List<LaneDirection> fromLanes, List<LaneDirection> toLanes, final CurvatureType curvatureType, 771 final Duration duration) throws OperationalPlanException 772 { 773 checkLaneDirections(fromLanes, toLanes); 774 775 return null; 776 } 777 778 private final static void checkLaneDirections(final List<LaneDirection> fromLanes, List<LaneDirection> toLanes) 779 throws OperationalPlanException 780 { 781 Throw.when(fromLanes == null || toLanes == null, OperationalPlanException.class, "Lane lists may not be null."); 782 Throw.when(fromLanes.isEmpty(), OperationalPlanException.class, "Lane lists may not be empty."); 783 Throw.when(fromLanes.size() != toLanes.size(), OperationalPlanException.class, 784 "Set of from lanes has different length than set of to lanes."); 785 for (int i = 0; i < fromLanes.size(); i++) 786 { 787 Throw.when(!fromLanes.get(i).getLane().getParentLink().equals(toLanes.get(i).getLane().getParentLink()), 788 OperationalPlanException.class, 789 "A lane in the from-lanes list is not on the same link as the lane at equal index in the to-lanes list."); 790 } 791 } 792 793 /** 794 * Defines curvature. 795 * <p> 796 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br> 797 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>. 798 * <p> 799 * @version $Revision$, $LastChangedDate$, by $Author$, initial version May 27, 2016 <br> 800 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a> 801 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a> 802 */ 803 /*- 804 public enum CurvatureType 805 { 806 /** Linear lateral movement. */ 807 /*- 808 LINEAR 809 { 810 public double[] getFractions(final double strartFraction) 811 { 812 return new double[1]; 813 } 814 }, 815 816 /** */ 817 /*- 818 SCURVE 819 { 820 public double[] getFractions(final double strartFraction) 821 { 822 return new double[1]; 823 } 824 }; 825 826 public abstract double[] getFractions(double startFraction); 827 } 828 */ 829 830 }