View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2   
3   import java.util.SortedSet;
4   
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.opentrafficsim.base.parameters.ParameterException;
9   import org.opentrafficsim.base.parameters.ParameterTypes;
10  import org.opentrafficsim.base.parameters.Parameters;
11  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
12  import org.opentrafficsim.core.network.LateralDirectionality;
13  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
14  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
15  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
16  import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu;
17  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
18  import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
19  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
20  
21  /**
22   * Interface for LMRS gap-acceptance models.
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/peter-knoppers">Peter Knoppers</a>
29   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
30   */
31  public interface GapAcceptance
32  {
33  
34      /** Being informed of the model and parameters of other drivers (default LMRS). */
35      GapAcceptance INFORMED = new GapAcceptance()
36      {
37          @Override
38          public boolean acceptGap(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
39                  final CarFollowingModel cfm, final double desire, final Speed ownSpeed, final Acceleration ownAcceleration,
40                  final LateralDirectionality lat) throws ParameterException, OperationalPlanException
41          {
42              NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
43              if (neighbors.isGtuAlongside(lat))
44              {
45                  // gtu alongside
46                  return false;
47              }
48  
49              Acceleration threshold = params.getParameter(ParameterTypes.B).times(-desire);
50              if (threshold.gt(ownAcceleration))
51              {
52                  return false;
53              }
54  
55              if (!acceptEgoAcceleration(perception, params, sli, cfm, desire, ownSpeed, lat, threshold))
56              {
57                  return false;
58              }
59  
60              // TODO
61              /*-
62               * Followers and are accepted if the acceleration and speed is 0, a leader is accepted if the ego speed is 0. This
63               * is in place as vehicles that provide courtesy, will decelerate for us and overshoot the stand-still distance. As
64               * a consequence, they will cease cooperation as they are too close. A pattern will arise where followers slow down
65               * to (near) stand-still, and accelerate again, before we could ever accept the gap.
66               *
67               * By accepting the gap in the moment that they reach stand-still, this vehicle can at least accept the gap at some
68               * point. All of this is only a problem if the own vehicle is standing still. Otherwise the stand-still distance is
69               * not important and movement of our own will create an acceptable situation.
70               *
71               * What needs to be done, is to find a better way to deal with the cooperation and gap-acceptance, such that this
72               * hack is not required.
73               */
74              for (PerceivedGtu follower : neighbors.getFirstFollowers(lat))
75              {
76                  if (follower.getSpeed().gt0() || follower.getAcceleration().gt0() || follower.getDistance().si < 1.0)
77                  {
78                      Acceleration aFollow = LmrsUtil.singleAcceleration(follower.getDistance(), follower.getSpeed(), ownSpeed,
79                              desire, follower.getBehavior().getParameters(), follower.getBehavior().getSpeedLimitInfo(),
80                              follower.getBehavior().getCarFollowingModel());
81                      if (threshold.gt(aFollow))
82                      {
83                          return false;
84                      }
85                  }
86              }
87  
88              if (!acceptLaneChangers(perception, params, sli, cfm, ownSpeed, lat, threshold))
89              {
90                  return false;
91              }
92  
93              return true;
94          }
95  
96          @Override
97          public String toString()
98          {
99              return "INFORMED";
100         }
101     };
102 
103     /** Being informed of the model and parameters of other drivers, but applying own headway value. */
104     GapAcceptance EGO_HEADWAY = new GapAcceptance()
105     {
106         @Override
107         public boolean acceptGap(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
108                 final CarFollowingModel cfm, final double desire, final Speed ownSpeed, final Acceleration ownAcceleration,
109                 final LateralDirectionality lat) throws ParameterException, OperationalPlanException
110         {
111             NeighborsPerception neigbors = perception.getPerceptionCategory(NeighborsPerception.class);
112             if (neigbors.isGtuAlongside(lat))
113             {
114                 // gtu alongside
115                 return false;
116             }
117 
118             Acceleration threshold = params.getParameter(ParameterTypes.B).times(-desire);
119             if (!acceptEgoAcceleration(perception, params, sli, cfm, desire, ownSpeed, lat, threshold))
120             {
121                 return false;
122             }
123 
124             for (PerceivedGtu follower : neigbors.getFirstFollowers(lat))
125             {
126                 if (follower.getSpeed().gt0() || follower.getAcceleration().gt0())
127                 {
128                     // Change headway parameter
129                     Parameters folParams = follower.getBehavior().getParameters();
130                     // safe copy, so no need to set/reset
131                     folParams.setParameter(ParameterTypes.TMIN, params.getParameter(ParameterTypes.TMIN));
132                     folParams.setParameter(ParameterTypes.TMAX, params.getParameter(ParameterTypes.TMAX));
133                     Acceleration aFollow = LmrsUtil.singleAcceleration(follower.getDistance(), follower.getSpeed(), ownSpeed,
134                             desire, folParams, follower.getBehavior().getSpeedLimitInfo(),
135                             follower.getBehavior().getCarFollowingModel());
136                     // folParams.resetParameter(ParameterTypes.TMIN);
137                     // folParams.resetParameter(ParameterTypes.TMAX);
138                     if (threshold.gt(aFollow))
139                     {
140                         return false;
141                     }
142                 }
143             }
144 
145             if (!acceptLaneChangers(perception, params, sli, cfm, ownSpeed, lat, threshold))
146             {
147                 return false;
148             }
149 
150             return true;
151         }
152 
153         @Override
154         public String toString()
155         {
156             return "EGO_HEADWAY";
157         }
158     };
159 
160     /**
161      * Determine whether a gap is acceptable.
162      * @param perception perception
163      * @param params parameters
164      * @param sli speed limit info
165      * @param cfm car-following model
166      * @param desire level of lane change desire
167      * @param ownSpeed own speed
168      * @param lat lateral direction for synchronization
169      * @param threshold threshold value
170      * @return whether a gap is acceptable
171      * @throws ParameterException if a parameter is not defined
172      * @throws OperationalPlanException perception exception
173      */
174     private static boolean acceptEgoAcceleration(final LanePerception perception, final Parameters params,
175             final SpeedLimitInfo sli, final CarFollowingModel cfm, final double desire, final Speed ownSpeed,
176             final LateralDirectionality lat, final Acceleration threshold) throws ParameterException, OperationalPlanException
177     {
178         if (ownSpeed.gt0())
179         {
180             for (PerceivedGtu leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
181             {
182                 Acceleration a = LmrsUtil.singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, params,
183                         sli, cfm);
184                 if (threshold.gt(a))
185                 {
186                     return false;
187                 }
188             }
189         }
190         return true;
191     }
192 
193     /**
194      * Determine whether a gap is acceptable regarding lane changers from the second adjacent lane to the first adjacent lane.
195      * @param perception perception
196      * @param params parameters
197      * @param sli speed limit info
198      * @param cfm car-following model
199      * @param ownSpeed own speed
200      * @param lat lateral direction for synchronization
201      * @param threshold threshold value
202      * @return whether a gap is acceptable
203      * @throws ParameterException if a parameter is not defined
204      * @throws OperationalPlanException perception exception
205      */
206     private static boolean acceptLaneChangers(final LanePerception perception, final Parameters params,
207             final SpeedLimitInfo sli, final CarFollowingModel cfm, final Speed ownSpeed, final LateralDirectionality lat,
208             final Acceleration threshold) throws ParameterException, OperationalPlanException
209     {
210         if (ownSpeed.gt0())
211         {
212             NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
213             // Only potential lane changers in the gap to the leader in the target lane are relevant
214             SortedSet<PerceivedGtu> firstLeaders = neighbors.getFirstLeaders(lat);
215             Length range = Length.POS_MAXVALUE;
216             if (!firstLeaders.isEmpty())
217             {
218                 range = Length.ZERO;
219                 for (PerceivedGtu leader : firstLeaders)
220                 {
221                     range = Length.max(range, leader.getDistance());
222                 }
223             }
224             for (PerceivedGtu leader : neighbors.getLeaders(new RelativeLane(lat, 2)))
225             {
226                 if (leader.getDistance().gt(range))
227                 {
228                     return true;
229                 }
230                 if (leader.getManeuver().isChangingLane(lat.flip()))
231                 {
232                     Acceleration a = CarFollowingUtil.followSingleLeader(cfm, params, ownSpeed, sli, leader.getDistance(),
233                             leader.getSpeed());
234                     return a.ge(threshold);
235                 }
236             }
237         }
238         return true;
239     }
240 
241     /**
242      * Determine whether a gap is acceptable.
243      * @param perception perception
244      * @param params parameters
245      * @param sli speed limit info
246      * @param cfm car-following model
247      * @param desire level of lane change desire
248      * @param ownSpeed own speed
249      * @param ownAcceleration current car-following acceleration
250      * @param lat lateral direction for synchronization
251      * @return whether a gap is acceptable
252      * @throws ParameterException if a parameter is not defined
253      * @throws OperationalPlanException perception exception
254      */
255     boolean acceptGap(LanePerception perception, Parameters params, SpeedLimitInfo sli, CarFollowingModel cfm, double desire,
256             Speed ownSpeed, Acceleration ownAcceleration, LateralDirectionality lat)
257             throws ParameterException, OperationalPlanException;
258 
259 }