View Javadoc
1   package org.opentrafficsim.road.gtu.tactical.util.lmrs;
2   
3   import org.djunits.unit.AccelerationUnit;
4   import org.djunits.value.vdouble.scalar.Acceleration;
5   import org.djunits.value.vdouble.scalar.Speed;
6   import org.opentrafficsim.base.NamedConstants;
7   import org.opentrafficsim.base.parameters.ParameterException;
8   import org.opentrafficsim.base.parameters.ParameterTypes;
9   import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
10  import org.opentrafficsim.core.network.LateralDirectionality;
11  import org.opentrafficsim.road.gtu.LaneBasedGtu;
12  import org.opentrafficsim.road.gtu.perception.PerceptionCollectable;
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.Synchronizable;
17  import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
18  
19  /**
20   * Different forms of cooperation.
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 Cooperation extends LmrsParameters, NamedConstants
30  {
31  
32      /** Simple passive cooperation. */
33      Cooperation PASSIVE = new Cooperation()
34      {
35          @Override
36          public Acceleration cooperate(final TacticalContextEgo context, final LateralDirectionality lat,
37                  final LmrsData lmrsData, final Desire ownDesire) throws ParameterException, OperationalPlanException
38          {
39              if (!context.getPerception().getLaneStructure().exists(lat.isRight() ? RelativeLane.RIGHT : RelativeLane.LEFT))
40              {
41                  return new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
42              }
43              Acceleration b = context.getParameters().getParameter(ParameterTypes.B);
44              Acceleration a = new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
45              double dCoop = context.getParameters().getParameter(DCOOP);
46              RelativeLane relativeLane = new RelativeLane(lat, 1);
47              for (PerceivedGtu leader : context.getPerception().getPerceptionCategory(NeighborsPerception.class)
48                      .getLeaders(relativeLane))
49              {
50                  double desire = lat.equals(LateralDirectionality.LEFT) ? leader.getBehavior().rightLaneChangeDesire()
51                          : lat.equals(LateralDirectionality.RIGHT) ? leader.getBehavior().leftLaneChangeDesire() : 0.0;
52                  if (desire >= dCoop && (leader.getSpeed().gt0() || leader.getDistance().gt0()))
53                  {
54                      if (lmrsData != null)
55                      {
56                          lmrsData.setSynchronizationState(Synchronizable.State.COOPERATING);
57                      }
58                      Acceleration aSingle =
59                              LmrsUtil.singleAcceleration(context, leader.getDistance(), leader.getSpeed(), desire);
60                      a = Acceleration.min(a, aSingle);
61                  }
62              }
63              return Acceleration.max(a, b.neg());
64          }
65  
66          @Override
67          public String name()
68          {
69              return "PASSIVE";
70          }
71      };
72  
73      /** Same as passive cooperation, except that cooperation is sometimes ignored at low speed of other vehicles. */
74      Cooperation PASSIVE_MOVING = new Cooperation()
75      {
76          @Override
77          public Acceleration cooperate(final TacticalContextEgo context, final LateralDirectionality lat,
78                  final LmrsData lmrsData, final Desire ownDesire) throws ParameterException, OperationalPlanException
79          {
80              if (!context.getPerception().getLaneStructure().exists(lat.isRight() ? RelativeLane.RIGHT : RelativeLane.LEFT))
81              {
82                  return new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
83              }
84              Acceleration bCrit = context.getParameters().getParameter(ParameterTypes.BCRIT);
85              Acceleration a = new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
86              double dCoop = context.getParameters().getParameter(DCOOP);
87              RelativeLane relativeLane = new RelativeLane(lat, 1);
88              NeighborsPerception neighbours = context.getPerception().getPerceptionCategory(NeighborsPerception.class);
89              PerceptionCollectable<PerceivedGtu, LaneBasedGtu> leaders = neighbours.getLeaders(RelativeLane.CURRENT);
90              Speed thresholdSpeed = Speed.ofSI(6.86); // 295m / 43s
91              boolean leaderInCongestion = leaders.isEmpty() ? false : leaders.first().getSpeed().lt(thresholdSpeed);
92              for (PerceivedGtu leader : neighbours.getLeaders(relativeLane))
93              {
94                  double desire = lat.equals(LateralDirectionality.LEFT) ? leader.getBehavior().rightLaneChangeDesire()
95                          : lat.equals(LateralDirectionality.RIGHT) ? leader.getBehavior().leftLaneChangeDesire() : 0.0;
96                  // TODO: only cooperate if merger still quite fast or there's congestion downstream anyway (which we can better
97                  // estimate than only considering the direct leader
98                  if (desire >= dCoop && (leader.getSpeed().gt0() || leader.getDistance().gt0())
99                          && (leader.getSpeed().ge(thresholdSpeed) || leaderInCongestion))
100                 {
101                     if (lmrsData != null)
102                     {
103                         lmrsData.setSynchronizationState(Synchronizable.State.COOPERATING);
104                     }
105                     Acceleration aSingle =
106                             LmrsUtil.singleAcceleration(context, leader.getDistance(), leader.getSpeed(), desire);
107                     a = Acceleration.min(a, aSingle);
108                 }
109             }
110             return Acceleration.max(a, bCrit.neg());
111         }
112 
113         @Override
114         public String name()
115         {
116             return "PASSIVE_MOVING";
117         }
118     };
119 
120     /** Cooperation similar to the default, except at large adjacent leader deceleration. */
121     Cooperation ACTIVE = new Cooperation()
122     {
123         @Override
124         public Acceleration cooperate(final TacticalContextEgo context, final LateralDirectionality lat,
125                 final LmrsData lmrsData, final Desire ownDesire) throws ParameterException, OperationalPlanException
126         {
127             if (!context.getPerception().getLaneStructure().exists(lat.isRight() ? RelativeLane.RIGHT : RelativeLane.LEFT))
128             {
129                 return new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
130             }
131             Acceleration a = new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
132             double dCoop = context.getParameters().getParameter(DCOOP);
133             RelativeLane relativeLane = new RelativeLane(lat, 1);
134             for (PerceivedGtu leader : context.getPerception().getPerceptionCategory(NeighborsPerception.class)
135                     .getLeaders(relativeLane))
136             {
137                 double desire = lat.equals(LateralDirectionality.LEFT) ? leader.getBehavior().rightLaneChangeDesire()
138                         : lat.equals(LateralDirectionality.RIGHT) ? leader.getBehavior().leftLaneChangeDesire() : 0.0;
139                 if (desire >= dCoop && leader.getDistance().gt0()
140                         && leader.getAcceleration().gt(context.getParameters().getParameter(ParameterTypes.BCRIT).neg()))
141                 {
142                     if (lmrsData != null)
143                     {
144                         lmrsData.setSynchronizationState(Synchronizable.State.COOPERATING);
145                     }
146                     Acceleration aSingle =
147                             LmrsUtil.singleAcceleration(context, leader.getDistance(), leader.getSpeed(), desire);
148                     a = Acceleration.min(a, Synchronization.gentleUrgency(aSingle, desire, context.getParameters()));
149                 }
150             }
151             return a;
152         }
153 
154         @Override
155         public String name()
156         {
157             return "ACTIVE";
158         }
159     };
160 
161     /**
162      * Determine acceleration for cooperation.
163      * @param context tactical information such as parameters and car-following model
164      * @param lat lateral direction for cooperation
165      * @param lmrsData lmrs data to store COOPERATION synchronization state in, may be {@code null}
166      * @param ownDesire own lane change desire
167      * @return acceleration for synchronization
168      * @throws ParameterException if a parameter is not defined
169      * @throws OperationalPlanException perception exception
170      */
171     Acceleration cooperate(TacticalContextEgo context, LateralDirectionality lat, LmrsData lmrsData, Desire ownDesire)
172             throws ParameterException, OperationalPlanException;
173 }