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