1 package org.opentrafficsim.road.gtu.lane.tactical.util; 2 3 import org.djunits.unit.AccelerationUnit; 4 import org.djunits.unit.SpeedUnit; 5 import org.djunits.value.vdouble.scalar.Acceleration; 6 import org.djunits.value.vdouble.scalar.Length; 7 import org.djunits.value.vdouble.scalar.Speed; 8 import org.djutils.exceptions.Throw; 9 import org.djutils.exceptions.Try; 10 import org.opentrafficsim.base.parameters.ParameterException; 11 import org.opentrafficsim.base.parameters.Parameters; 12 import org.opentrafficsim.core.gtu.GtuException; 13 import org.opentrafficsim.road.gtu.lane.perception.PerceptionIterable; 14 import org.opentrafficsim.road.gtu.lane.perception.PerceptionIterableSet; 15 import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadway; 16 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway; 17 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu; 18 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel; 19 import org.opentrafficsim.road.network.speed.SpeedLimitInfo; 20 21 /** 22 * Static methods regarding car-following for composition in tactical planners. 23 * <p> 24 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br> 25 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>. 26 * </p> 27 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a> 28 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a> 29 */ 30 public final class CarFollowingUtil 31 { 32 33 /** 34 * Do not instantiate. 35 */ 36 private CarFollowingUtil() 37 { 38 // 39 } 40 41 /** 42 * Follow a set of headway GTUs. 43 * @param carFollowingModel CarFollowingModel; car-following model 44 * @param parameters Parameters; parameters 45 * @param speed Speed; current speed 46 * @param speedLimitInfo SpeedLimitInfo; speed limit info 47 * @param distance Length; distance 48 * @param leaderSpeed Speed; speed of the leader 49 * @return acceleration for following the leader 50 * @throws ParameterException if a parameter is not given or out of bounds 51 */ 52 public static Acceleration followSingleLeader(final CarFollowingModel carFollowingModel, final Parameters parameters, 53 final Speed speed, final SpeedLimitInfo speedLimitInfo, final Length distance, final Speed leaderSpeed) 54 throws ParameterException 55 { 56 return carFollowingModel.followingAcceleration(parameters, speed, speedLimitInfo, createLeader(distance, leaderSpeed)); 57 } 58 59 /** 60 * Follow a set of headway GTUs. 61 * @param carFollowingModel CarFollowingModel; car-following model 62 * @param parameters Parameters; parameters 63 * @param speed Speed; current speed 64 * @param speedLimitInfo SpeedLimitInfo; speed limit info 65 * @param leader HeadwayGtu; leader 66 * @return acceleration for following the leader 67 * @throws ParameterException if a parameter is not given or out of bounds 68 */ 69 public static Acceleration followSingleLeader(final CarFollowingModel carFollowingModel, final Parameters parameters, 70 final Speed speed, final SpeedLimitInfo speedLimitInfo, final HeadwayGtu leader) throws ParameterException 71 { 72 return carFollowingModel.followingAcceleration(parameters, speed, speedLimitInfo, new PerceptionIterableSet<>(leader)); 73 } 74 75 /** 76 * Stop within given distance. 77 * @param carFollowingModel CarFollowingModel; car-following model 78 * @param parameters Parameters; parameters 79 * @param speed Speed; current speed 80 * @param speedLimitInfo SpeedLimitInfo; speed limit info 81 * @param distance Length; distance to stop over 82 * @return acceleration to stop over distance 83 * @throws ParameterException if a parameter is not given or out of bounds 84 */ 85 public static Acceleration stop(final CarFollowingModel carFollowingModel, final Parameters parameters, final Speed speed, 86 final SpeedLimitInfo speedLimitInfo, final Length distance) throws ParameterException 87 { 88 return carFollowingModel.followingAcceleration(parameters, speed, speedLimitInfo, createLeader(distance, Speed.ZERO)); 89 } 90 91 /** 92 * Return constant acceleration in order to stop in specified distance. The car-following model is used to determine the 93 * stopping distance (i.e. distance remaining at stand still, e.g. 1-3m). 94 * @param carFollowingModel CarFollowingModel; car-following model 95 * @param parameters Parameters; parameters 96 * @param speed Speed; current speed 97 * @param distance Length; distance to stop over 98 * @return constant acceleration in order to stop in specified distance 99 * @throws ParameterException on missing parameter 100 */ 101 public static Acceleration constantAccelerationStop(final CarFollowingModel carFollowingModel, final Parameters parameters, 102 final Speed speed, final Length distance) throws ParameterException 103 { 104 Length s0 = carFollowingModel.desiredHeadway(parameters, Speed.ZERO); 105 return new Acceleration(-0.5 * speed.si * speed.si / (distance.si - s0.si), AccelerationUnit.SI); 106 } 107 108 /** 109 * Calculate free acceleration. 110 * @param carFollowingModel CarFollowingModel; car-following model 111 * @param parameters Parameters; parameters 112 * @param speed Speed; current speed 113 * @param speedLimitInfo SpeedLimitInfo; speed limit info 114 * @return acceleration free acceleration 115 * @throws ParameterException if a parameter is not given or out of bounds 116 */ 117 public static Acceleration freeAcceleration(final CarFollowingModel carFollowingModel, final Parameters parameters, 118 final Speed speed, final SpeedLimitInfo speedLimitInfo) throws ParameterException 119 { 120 PerceptionIterableSet<Headway> leaders = new PerceptionIterableSet<>(); 121 return carFollowingModel.followingAcceleration(parameters, speed, speedLimitInfo, leaders); 122 } 123 124 /** 125 * Returns an acceleration based on the car-following model in order to adjust the speed to a given value at some location 126 * ahead. This is done by placing a virtual vehicle somewhere near the location. Both the location and speed of this virtual 127 * vehicle are dynamically adjusted to resemble a car-following situation. To explain, first consider the situation where a 128 * virtual vehicle is placed at the target speed and such that the equilibrium headway is in line with the location: 129 * 130 * <pre> 131 * 132 * ___ location of target speed --)| ___ 133 * |___|(--------------s--------------) (--h--)|___| ))) vTar 134 * </pre> 135 * 136 * Here, {@code s} is the distance to the target speed, and {@code h} is the desired headway if the vehicle would drive at 137 * the target speed {@code vTar}.<br> 138 * <br> 139 * In this way car-following models will first underestimate the required deceleration, as the virtual vehicle is actually 140 * stationary and does not move with {@code vTar} at all. Because of this underestimation, strong deceleration is required 141 * later. This behavior is not in line with the sensitivity parameters of the car-following model.<br> 142 * <br> 143 * To correct for the fact that the virtual vehicle is actually not moving, the speed difference should be larger, i.e. the 144 * speed of the virtual vehicle {@code vTar'} should be lower. We require: 145 * <ul> 146 * <li>if {@code v = vTar} then {@code vTar' = vTar}, otherwise there is an incentive to accelerate or decelerate for no 147 * good reason</li> 148 * <li>if {@code vTar ~ 0} then {@code vTar' ~ 0}, as car-following models are suitable for stopping and need no additional 149 * incentive to decelerate in such cases</li> 150 * <li>if {@code 0 < vTar < v} then {@code vTar' < vTar}, introducing additional deceleration to compensate for the fact 151 * that the virtual vehicle does not move 152 * </ul> 153 * These requirements are met by {@code vTar' = vTar * (vTar/v) = vTar^2/v}.<br> 154 * <br> 155 * Furthermore, if {@code v < vTar} we get {@code vTar' > vTar} leading to additional acceleration. Acceleration is then 156 * appropriate, and possibly limited by a free term in the car-following model.<br> 157 * <br> 158 * The virtual vehicle is thus placed with speed {@code vTar'} at a distance {@code s + h'} where {@code h'} is the desired 159 * headway if the vehicle would drive at speed {@code vTar'}. Both {@code vTar'} and {@code h'} depend on the current speed 160 * of the vehicle, so the virtual vehicle in this case actually moves, but not with {@code vTar}.<br> 161 * <br> 162 * This approach has been tested with the IDM+ to deliver decelerations in line with the parameters. On a plane with initial 163 * speed ranging from 0 to 33.33m/s and a target speed in 300m also ranging from 0 to 33.33m/s, strongest deceleration is 164 * equal to the car-following model stopping from 33.33m/s to a stand-still vehicle in 300m (+ stopping distance of 3m). 165 * Throughout the plane the maximum deceleration of each scenario is close to this value, unless the initial speed is so 166 * low, and the target speed is so high, that such levels of deceleration are never required.<br> 167 * <br> 168 * @param carFollowingModel CarFollowingModel; car-following model to use 169 * @param parameters Parameters; parameters 170 * @param speed Speed; current speed 171 * @param speedLimitInfo SpeedLimitInfo; info regarding the desired speed for car-following 172 * @param distance Length; distance to the location of the target speed 173 * @param targetSpeed Speed; target speed 174 * @return acceleration acceleration based on the car-following model in order to adjust the speed 175 * @throws ParameterException if parameter exception occurs 176 * @throws NullPointerException if any input is null 177 * @throws IllegalArgumentException if the distance or target speed is not at least 0 178 */ 179 public static Acceleration approachTargetSpeed(final CarFollowingModel carFollowingModel, final Parameters parameters, 180 final Speed speed, final SpeedLimitInfo speedLimitInfo, final Length distance, final Speed targetSpeed) 181 throws ParameterException 182 { 183 Throw.whenNull(parameters, "Parameters may not be null."); 184 Throw.whenNull(speed, "Speed may not be null."); 185 Throw.whenNull(speedLimitInfo, "Speed limit info may not be null."); 186 Throw.whenNull(distance, "Distance may not be null"); 187 Throw.whenNull(targetSpeed, "Target speed may not be null"); 188 Throw.when(distance.si < 0, IllegalArgumentException.class, "Distance must be at least 0."); 189 Throw.when(targetSpeed.si < 0, IllegalArgumentException.class, "Target speed must be at least 0."); 190 // adjust speed of virtual vehicle to add deceleration incentive as the virtual vehicle does not move 191 Speed virtualSpeed; 192 if (speed.si > 0) 193 { 194 virtualSpeed = new Speed(targetSpeed.si * targetSpeed.si / speed.si, SpeedUnit.SI); 195 } 196 else 197 { 198 virtualSpeed = new Speed(Double.MAX_VALUE, SpeedUnit.SI); 199 } 200 // set distance in line with equilibrium headway at virtual speed 201 Length virtualDistance = distance.plus(carFollowingModel.desiredHeadway(parameters, virtualSpeed)); 202 // calculate acceleration towards virtual vehicle with car-following model 203 return carFollowingModel.followingAcceleration(parameters, speed, speedLimitInfo, 204 createLeader(virtualDistance, virtualSpeed)); 205 } 206 207 /** 208 * Create a single leader set. 209 * @param headway Length; distance to the leader 210 * @param speed Speed; leader speed 211 * @return Set; set with a single leader 212 */ 213 private static PerceptionIterable<Headway> createLeader(final Length headway, final Speed speed) 214 { 215 PerceptionIterable<Headway> leaders = 216 Try.assign(() -> new PerceptionIterableSet<>(new CarFollowingHeadway(headway, speed)), 217 "Exception during headway creation."); 218 return leaders; 219 } 220 221 /** 222 * Simple headway implementation for minimum car-following information. 223 * <p> 224 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. 225 * <br> 226 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>. 227 * </p> 228 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a> 229 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a> 230 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a> 231 */ 232 public static class CarFollowingHeadway extends AbstractHeadway 233 { 234 /** */ 235 private static final long serialVersionUID = 20180226L; 236 237 /** Speed of the leader. */ 238 private final Speed speed; 239 240 /** 241 * Constructor. 242 * @param headway Length; distance to the leader 243 * @param speed Speed; leader speed 244 * @throws GtuException on exception 245 */ 246 public CarFollowingHeadway(final Length headway, final Speed speed) throws GtuException 247 { 248 super(headway); 249 this.speed = speed; 250 } 251 252 /** {@inheritDoc} */ 253 @Override 254 public String getId() 255 { 256 return null; 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 public Length getLength() 262 { 263 return null; 264 } 265 266 /** {@inheritDoc} */ 267 @Override 268 public Speed getSpeed() 269 { 270 return this.speed; 271 } 272 273 /** {@inheritDoc} */ 274 @Override 275 public ObjectType getObjectType() 276 { 277 return null; 278 } 279 280 /** {@inheritDoc} */ 281 @Override 282 public Acceleration getAcceleration() 283 { 284 return null; 285 } 286 } 287 288 }