View Javadoc
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 }