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