View Javadoc
1   package org.opentrafficsim.road.gtu.tactical.lmrs;
2   
3   import java.util.SortedSet;
4   
5   import org.djunits.value.vdouble.scalar.Duration;
6   import org.djunits.value.vdouble.scalar.Length;
7   import org.djunits.value.vdouble.scalar.Speed;
8   import org.djutils.immutablecollections.ImmutableLinkedHashMap;
9   import org.opentrafficsim.base.parameters.ParameterException;
10  import org.opentrafficsim.base.parameters.ParameterTypeDuration;
11  import org.opentrafficsim.base.parameters.ParameterTypeLength;
12  import org.opentrafficsim.base.parameters.ParameterTypes;
13  import org.opentrafficsim.core.gtu.Stateless;
14  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
15  import org.opentrafficsim.core.network.LateralDirectionality;
16  import org.opentrafficsim.road.gtu.perception.RelativeLane;
17  import org.opentrafficsim.road.gtu.perception.categories.InfrastructurePerception;
18  import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
19  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Desire;
20  import org.opentrafficsim.road.gtu.tactical.util.lmrs.MandatoryIncentive;
21  import org.opentrafficsim.road.network.LaneChangeInfo;
22  
23  /**
24   * Determines desire by assessing the number of required lane change to be performed and the distance within which these have to
25   * be performed. Desire starts to increase from 0 linearly over a distance of x0 per required lane change, or per v*t0 per
26   * required lane change. For v>x0/t0 this gives that remaining time is critical, while for v<x0/t0 remaining space is
27   * critical. The desire is set towards the adjacent lane with a better situation. Negative desire towards the other lane, the
28   * extent of which pertains to the other adjacent lane, is also set.
29   * <p>
30   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
31   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
32   * </p>
33   * @author Wouter Schakel
34   */
35  public final class IncentiveRoute implements MandatoryIncentive, Stateless<IncentiveRoute>
36  {
37  
38      /** Look ahead parameter type. */
39      protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
40  
41      /** Look-ahead time for mandatory lane changes parameter type. */
42      public static final ParameterTypeDuration T0 = ParameterTypes.T0;
43  
44      /** Singleton instance. */
45      public static final IncentiveRoute SINGLETON = new IncentiveRoute();
46  
47      @Override
48      public IncentiveRoute get()
49      {
50          return SINGLETON;
51      }
52  
53      /**
54       * Constructor.
55       */
56      private IncentiveRoute()
57      {
58          //
59      }
60  
61      @Override
62      public Desire determineDesire(final TacticalContextEgo context,
63              final ImmutableLinkedHashMap<Class<? extends MandatoryIncentive>, Desire> mandatoryDesire)
64              throws ParameterException, OperationalPlanException
65      {
66          // desire to leave current lane
67          InfrastructurePerception infra = context.getPerception().getPerceptionCategory(InfrastructurePerception.class);
68          SortedSet<LaneChangeInfo> currentInfo = infra.getLegalLaneChangeInfo(RelativeLane.CURRENT);
69          Length currentFirst = currentInfo.isEmpty() || currentInfo.first().numberOfLaneChanges() == 0 ? Length.POSITIVE_INFINITY
70                  : currentInfo.first().remainingDistance();
71          double dCurr = getDesireToLeave(context, RelativeLane.CURRENT);
72  
73          // left
74          double dLeft = 0.0;
75          if (context.getPerception().getLaneStructure().exists(RelativeLane.LEFT)
76                  && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.LEFT).neg().lt(currentFirst))
77          {
78              // desire to leave left lane
79              dLeft = getDesireToLeave(context, RelativeLane.LEFT);
80              // desire to leave from current to left lane
81              dLeft = dLeft < dCurr ? dCurr : dLeft > dCurr ? -dLeft : 0.0;
82          }
83  
84          // right
85          double dRigh = 0.0;
86          if (context.getPerception().getLaneStructure().exists(RelativeLane.RIGHT) && infra
87                  .getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.RIGHT).neg().lt(currentFirst))
88          {
89              // desire to leave right lane
90              dRigh = getDesireToLeave(context, RelativeLane.RIGHT);
91              // desire to leave from current to right lane
92              dRigh = dRigh < dCurr ? dCurr : dRigh > dCurr ? -dRigh : 0.0;
93          }
94  
95          return new Desire(dLeft, dRigh);
96      }
97  
98      /**
99       * Calculates desire to leave a lane.
100      * @param context tactical information such as parameters and car-following model
101      * @param lane relative lane to evaluate
102      * @return desire to leave a lane
103      * @throws ParameterException in case of a parameter exception
104      * @throws OperationalPlanException in case of perception exceptions
105      */
106     public static double getDesireToLeave(final TacticalContextEgo context, final RelativeLane lane)
107             throws ParameterException, OperationalPlanException
108     {
109         InfrastructurePerception infra = context.getPerception().getPerceptionCategory(InfrastructurePerception.class);
110         Length x0 = context.getParameters().getParameter(LOOKAHEAD);
111         Duration t0 = context.getParameters().getParameter(T0);
112         double dOut = 0.0;
113         if (infra.getCrossSection().contains(lane))
114         {
115             for (LaneChangeInfo info : infra.getLegalLaneChangeInfo(lane))
116             {
117                 double d = info.remainingDistance().lt0() ? info.numberOfLaneChanges()
118                         : getDesireToLeave(x0, t0, info.remainingDistance(), info.numberOfLaneChanges(), context.getSpeed());
119                 dOut = d > dOut ? d : dOut;
120             }
121         }
122         return dOut;
123     }
124 
125     /**
126      * Calculates desire to leave a lane for a single infrastructure info.
127      * @param x0 relevant distance per lane change
128      * @param t0 relevant time per lane change
129      * @param x remaining distance for lane changes
130      * @param n number of required lane changes
131      * @param v current speed
132      * @return desire to leave a lane for a single infrastructure info
133      * @throws ParameterException in case of a parameter exception
134      */
135     public static double getDesireToLeave(final Length x0, final Duration t0, final Length x, final int n, final Speed v)
136             throws ParameterException
137     {
138         double d1 = 1 - x.si / (n * x0.si);
139         double d2 = 1 - (x.si / v.si) / (n * t0.si);
140         d1 = d2 > d1 ? d2 : d1;
141         return d1 < 0 ? 0 : d1;
142     }
143 
144     @Override
145     public String toString()
146     {
147         return "IncentiveRoute";
148     }
149 
150 }