View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.lmrs;
2   
3   import java.util.SortedSet;
4   
5   import org.djunits.unit.DimensionlessUnit;
6   import org.djunits.value.vdouble.scalar.Acceleration;
7   import org.djunits.value.vdouble.scalar.Dimensionless;
8   import org.djunits.value.vdouble.scalar.Length;
9   import org.djunits.value.vdouble.scalar.Speed;
10  import org.opentrafficsim.core.gtu.GTUException;
11  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
12  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
13  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
14  import org.opentrafficsim.core.gtu.perception.EgoPerception;
15  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
16  import org.opentrafficsim.core.network.LateralDirectionality;
17  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
18  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
19  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
20  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
21  import org.opentrafficsim.road.gtu.lane.perception.categories.IntersectionPerception;
22  import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
23  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
24  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
25  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
26  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
27  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
28  import org.opentrafficsim.road.gtu.lane.tactical.util.ConflictUtil;
29  import org.opentrafficsim.road.gtu.lane.tactical.util.ConflictUtil.ConflictPlans;
30  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Desire;
31  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
32  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.VoluntaryIncentive;
33  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
34  
35  /**
36   * Determines lane change desire for speed. The anticipation speed in the current and adjacent lanes are compared. The larger
37   * the difference, the larger the lane change desire. For negative differences, negative desire results. Anticipation speed
38   * involves the the most critical vehicle considered to be in a lane. Vehicles are more critical if their speed is lower, and if
39   * they are closer. The set of vehicles considered to be on a lane includes drivers on adjacent lanes of the considered lane,
40   * with a lane change desire towards the considered lane above a certain certain threshold. If such vehicles have low speeds
41   * (i.e. vehicle accelerating to merge), this may result in a courtesy lane change, or in not changing lane out of courtesy from
42   * the 2nd lane of the mainline. Vehicle on the current lane of the driver, are not considered on adjacent lanes. This would
43   * maintain a large speed difference between the lanes where all drivers do not change lane as they consider leading vehicles to
44   * be on the adjacent lane, lowering the anticipation speed on the adjacent lane. The desire for speed is reduced as
45   * acceleration is larger, preventing over-assertive lane changes as acceleration out of congestion in the adjacent lane has
46   * progressed more.<br>
47   * <br>
48   * <b>Note:</b> This incentive includes speed, and a form of courtesy. It should therefore not be combined with incentives
49   * solely for speed, or solely for courtesy.
50   * <p>
51   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
52   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
53   * <p>
54   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 13, 2016 <br>
55   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
56   */
57  public class IncentiveSpeedWithCourtesy implements VoluntaryIncentive
58  {
59  
60      /** {@inheritDoc} */
61      @Override
62      public final Desire determineDesire(final BehavioralCharacteristics behavioralCharacteristics,
63              final LanePerception perception, final CarFollowingModel carFollowingModel, final Desire mandatoryDesire,
64              final Desire voluntaryDesire) throws ParameterException, OperationalPlanException
65      {
66  
67          // zero if no lane change is possible
68          if (perception.getPerceptionCategory(InfrastructurePerception.class).getLegalLaneChangePossibility(RelativeLane.CURRENT,
69                  LateralDirectionality.LEFT).si == 0
70                  && perception.getPerceptionCategory(InfrastructurePerception.class)
71                          .getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.RIGHT).si == 0)
72          {
73              return new Desire(0, 0);
74          }
75  
76          // gather some info
77          Speed vCur = anticipationSpeed(RelativeLane.CURRENT, behavioralCharacteristics, perception, carFollowingModel);
78          Speed vGain = behavioralCharacteristics.getParameter(LmrsParameters.VGAIN);
79  
80          // calculate aGain (default 1; lower as acceleration is higher than 0)
81          Dimensionless aGain;
82          /*
83           * Instead of instantaneous car-following acceleration, use current acceleration; only then is the acceleration factor
84           * consistent with possible lane change incentives pertaining to speed (which used to be only vehicles in the original
85           * LMRS, but can be any number of reason here. E.g. traffic lights, conflicts, etc.)
86           */
87          Acceleration aCur = perception.getPerceptionCategory(EgoPerception.class).getAcceleration();
88          // SortedSet<AbstractHeadwayGTU> leaders =
89          // perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT);
90          // SpeedLimitInfo speedLimitInfo = perception.getPerceptionCategory(InfrastructurePerception.class)
91          // .getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
92          // Acceleration aCur = CarFollowingUtil.followLeaders(carFollowingModel, behavioralCharacteristics,
93          // perception.getPerceptionCategory(EgoPerception.class).getSpeed(), speedLimitInfo, leaders);
94          if (aCur.si > 0)
95          {
96              Acceleration a = behavioralCharacteristics.getParameter(ParameterTypes.A);
97              aGain = a.minus(aCur).divideBy(a);
98          }
99          else
100         {
101             aGain = new Dimensionless(1, DimensionlessUnit.SI);
102         }
103 
104         // left desire
105         Dimensionless dLeft;
106         if (perception.getPerceptionCategory(InfrastructurePerception.class).getCrossSection().contains(RelativeLane.LEFT))
107         {
108             Speed vLeft = anticipationSpeed(RelativeLane.LEFT, behavioralCharacteristics, perception, carFollowingModel);
109             dLeft = aGain.multiplyBy(vLeft.minus(vCur)).divideBy(vGain);
110         }
111         else
112         {
113             dLeft = Dimensionless.ZERO;
114         }
115 
116         // right desire
117         Dimensionless dRight;
118         if (perception.getPerceptionCategory(InfrastructurePerception.class).getCrossSection().contains(RelativeLane.RIGHT))
119         {
120             Speed vRight = anticipationSpeed(RelativeLane.RIGHT, behavioralCharacteristics, perception, carFollowingModel);
121             dRight = aGain.multiplyBy(vRight.minus(vCur)).divideBy(vGain);
122         }
123         else
124         {
125             dRight = Dimensionless.ZERO;
126         }
127 
128         // return desire
129         return new Desire(dLeft, dRight);
130     }
131 
132     /**
133      * Determine the anticipation speed on the given lane. This depends on leading vehicles, leading vehicles in adjacent lanes
134      * with their indicator to this lane, and conflicts.
135      * @param lane lane to anticipate the speed on
136      * @param bc behavioral characteristics
137      * @param perception perception
138      * @param cfm car-following model, used for the desired speed
139      * @return anticipation speed on lane
140      * @throws ParameterException if a parameter is not defined
141      * @throws OperationalPlanException perception exception
142      */
143     private Speed anticipationSpeed(final RelativeLane lane, final BehavioralCharacteristics bc,
144             final LanePerception perception, final CarFollowingModel cfm) throws ParameterException, OperationalPlanException
145     {
146 
147         SpeedLimitInfo sli = perception.getPerceptionCategory(InfrastructurePerception.class).getSpeedLimitProspect(lane)
148                 .getSpeedLimitInfo(Length.ZERO);
149         Speed anticipationSpeed = cfm.desiredSpeed(bc, sli);
150         Speed desiredSpeed = new Speed(anticipationSpeed);
151         Length x0 = bc.getParameter(ParameterTypes.LOOKAHEAD);
152 
153         // leaders with right indicators on left lane of considered lane
154         if (perception.getPerceptionCategory(InfrastructurePerception.class).getCrossSection().contains(lane.getLeft()))
155         {
156             for (HeadwayGTU headwayGTU : perception.getPerceptionCategory(NeighborsPerception.class)
157                     .getLeaders(lane.getLeft()))
158             {
159                 // leaders on the current lane with indicator to an adjacent lane are not considered
160                 if (headwayGTU.isRightTurnIndicatorOn() && !lane.getLeft().equals(RelativeLane.CURRENT))
161                 {
162                     anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayGTU);
163                 }
164             }
165         }
166 
167         // leaders with left indicators on right lane of considered lane
168         if (perception.getPerceptionCategory(InfrastructurePerception.class).getCrossSection().contains(lane.getRight()))
169         {
170             for (HeadwayGTU headwayGTU : perception.getPerceptionCategory(NeighborsPerception.class)
171                     .getLeaders(lane.getRight()))
172             {
173                 // leaders on the current lane with indicator to an adjacent lane are not considered
174                 if (headwayGTU.isLeftTurnIndicatorOn() && !lane.getRight().equals(RelativeLane.CURRENT))
175                 {
176                     anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayGTU);
177                 }
178             }
179         }
180 
181         // leaders in the considered lane
182         for (HeadwayGTU headwayGTU : perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(lane))
183         {
184             anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayGTU);
185         }
186 
187         // conflicts in the considered lane
188         if (perception.contains(DirectIntersectionPerception.class))
189         {
190             for (HeadwayConflict headwayConflict : perception.getPerceptionCategory(IntersectionPerception.class)
191                     .getConflicts(lane))
192             {
193                 Length vehicleLength = perception.getPerceptionCategory(EgoPerception.class).getLength();
194                 Speed speed = perception.getPerceptionCategory(EgoPerception.class).getSpeed();
195                 SortedSet<HeadwayGTU> leaders =
196                         perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(lane);
197                 if (!headwayConflict.getConflictType().isCrossing())
198                 {
199                     // consider first downstream vehicle on split or merge (ignore others)
200                     SortedSet<HeadwayGTU> conflictVehicles = headwayConflict.getDownstreamConflictingGTUs();
201                     if (!conflictVehicles.isEmpty() && conflictVehicles.first().isParallel())
202                     {
203                         HeadwayGTU conflictingGtu = conflictVehicles.first();
204                         Length distance = headwayConflict.getDistance().plus(headwayConflict.getLength())
205                                 .plus(conflictingGtu.getOverlapRear());
206                         HeadwayGTU leadingGtu;
207                         try
208                         {
209                             leadingGtu = new HeadwayGTUSimple(conflictingGtu.getId(), conflictingGtu.getGtuType(), distance,
210                                     conflictingGtu.getLength());
211                         }
212                         catch (GTUException exception)
213                         {
214                             throw new OperationalPlanException("Could not create HeadwayGTUSimple for GTU on split.",
215                                     exception);
216                         }
217                         anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, leadingGtu);
218                     }
219                 }
220                 switch (headwayConflict.getConflictPriority())
221                 {
222                     case SPLIT:
223                     {
224                         // nothing
225                         break;
226                     }
227                     case PRIORITY:
228                     {
229                         if (ConflictUtil.stopForPriorityConflict(headwayConflict, leaders, speed, vehicleLength, bc,
230                                 new ConflictPlans()))
231                         {
232                             anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayConflict);
233                         }
234                         break;
235                     }
236                     case GIVE_WAY:
237                     {
238                         Acceleration acceleration = perception.getPerceptionCategory(EgoPerception.class).getAcceleration();
239                         if (ConflictUtil.stopForGiveWayConflict(headwayConflict, leaders, speed, acceleration, vehicleLength,
240                                 bc, sli, cfm))
241                         {
242                             anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayConflict);
243                         }
244                         break;
245                     }
246                     case STOP:
247                     {
248                         Acceleration acceleration = perception.getPerceptionCategory(EgoPerception.class).getAcceleration();
249                         if (ConflictUtil.stopForStopConflict(headwayConflict, leaders, speed, acceleration, vehicleLength, bc,
250                                 sli, cfm))
251                         {
252                             anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayConflict);
253                         }
254                         break;
255                     }
256                     case ALL_STOP:
257                     {
258                         if (ConflictUtil.stopForAllStopConflict(headwayConflict, new ConflictPlans()))
259                         {
260                             anticipationSpeed = anticipateSingle(anticipationSpeed, desiredSpeed, x0, headwayConflict);
261                         }
262                         break;
263                     }
264                     default:
265                         throw new RuntimeException(
266                                 "Conflict priority " + headwayConflict.getConflictPriority() + " is unknown.");
267                 }
268             }
269         }
270 
271         return anticipationSpeed;
272     }
273 
274     /**
275      * Anticipate a single leader by possibly lowering the anticipation speed.
276      * @param anticipationSpeed anticipation speed
277      * @param desiredSpeed desired speed on anticipated lane
278      * @param x0 look-ahead distance
279      * @param headway leader/object to anticipate
280      * @return possibly lowered anticipation speed
281      */
282     private Speed anticipateSingle(final Speed anticipationSpeed, final Speed desiredSpeed, final Length x0,
283             final Headway headway)
284     {
285         Speed speed = headway.getSpeed() == null ? Speed.ZERO : headway.getSpeed();
286         if (speed.gt(anticipationSpeed) || headway.getDistance().gt(x0))
287         {
288             return anticipationSpeed;
289         }
290         Speed vSingle = Speed.interpolate(speed, desiredSpeed, headway.getDistance().si / x0.si);
291         return anticipationSpeed.lt(vSingle) ? anticipationSpeed : vSingle;
292     }
293 
294     /** {@inheritDoc} */
295     @Override
296     public final String toString()
297     {
298         return "IncentiveSpeedWithCourtesy";
299     }
300 
301 }