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 car-following model
44 * @param parameters parameters
45 * @param speed current speed
46 * @param speedLimitInfo speed limit info
47 * @param distance distance
48 * @param leaderSpeed 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 car-following model
62 * @param parameters parameters
63 * @param speed current speed
64 * @param speedLimitInfo speed limit info
65 * @param leader 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 car-following model
78 * @param parameters parameters
79 * @param speed current speed
80 * @param speedLimitInfo speed limit info
81 * @param distance 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 car-following model
95 * @param parameters parameters
96 * @param speed current speed
97 * @param distance 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 car-following model
111 * @param parameters parameters
112 * @param speed current speed
113 * @param 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 car-following model to use
169 * @param parameters parameters
170 * @param speed current speed
171 * @param speedLimitInfo info regarding the desired speed for car-following
172 * @param distance distance to the location of the target speed
173 * @param targetSpeed 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 distance to the leader
210 * @param speed leader speed
211 * @return 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://github.com/peter-knoppers">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 distance to the leader
243 * @param 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 @Override
253 public String getId()
254 {
255 return null;
256 }
257
258 @Override
259 public Length getLength()
260 {
261 return null;
262 }
263
264 @Override
265 public Speed getSpeed()
266 {
267 return this.speed;
268 }
269
270 @Override
271 public ObjectType getObjectType()
272 {
273 return null;
274 }
275
276 @Override
277 public Acceleration getAcceleration()
278 {
279 return null;
280 }
281 }
282
283 }