View Javadoc
1   package org.opentrafficsim.road.gtu.tactical.lmrs;
2   
3   import org.djunits.value.vdouble.scalar.Duration;
4   import org.djunits.value.vdouble.scalar.Length;
5   import org.djunits.value.vdouble.scalar.Speed;
6   import org.djutils.draw.point.DirectedPoint2d;
7   import org.opentrafficsim.base.DistancedObject;
8   import org.opentrafficsim.base.parameters.ParameterException;
9   import org.opentrafficsim.base.parameters.ParameterTypes;
10  import org.opentrafficsim.core.gtu.GtuException;
11  import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
12  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
13  import org.opentrafficsim.core.network.NetworkException;
14  import org.opentrafficsim.road.gtu.LaneBasedGtu;
15  import org.opentrafficsim.road.gtu.operational.LaneOperationalPlanBuilder;
16  import org.opentrafficsim.road.gtu.operational.SimpleOperationalPlan;
17  import org.opentrafficsim.road.gtu.perception.LanePerception;
18  import org.opentrafficsim.road.gtu.perception.RelativeLane;
19  import org.opentrafficsim.road.gtu.tactical.Blockable;
20  import org.opentrafficsim.road.gtu.tactical.Synchronizable;
21  import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
22  import org.opentrafficsim.road.gtu.tactical.WithDesiredSpeed;
23  import org.opentrafficsim.road.gtu.tactical.following.CarFollowingModel;
24  import org.opentrafficsim.road.gtu.tactical.util.DeadEndUtil;
25  import org.opentrafficsim.road.gtu.tactical.util.LaneChangeNotAllowedUtil;
26  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Cooperation;
27  import org.opentrafficsim.road.gtu.tactical.util.lmrs.GapAcceptance;
28  import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsData;
29  import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsParameters;
30  import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsUtil;
31  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Synchronization;
32  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Tailgating;
33  
34  /**
35   * Implementation of the LMRS (Lane change Model with Relaxation and Synchronization). See Schakel, W.J., Knoop, V.L., and Van
36   * Arem, B. (2012), <a href="http://victorknoop.eu/research/papers/TRB2012_LMRS_reviewed.pdf">LMRS: Integrated Lane Change Model
37   * with Relaxation and Synchronization</a>, Transportation Research Records: Journal of the Transportation Research Board, No.
38   * 2316, pp. 47-57. Note in the official versions of TRB and TRR some errors appeared due to the typesetting of the papers (not
39   * in the preprint provided here). A list of errata for the official versions is found
40   * <a href="http://victorknoop.eu/research/papers/Erratum_LMRS.pdf">here</a>.
41   * <p>
42   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
43   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
44   * </p>
45   * @author Wouter Schakel
46   */
47  public class Lmrs extends AbstractIncentivesTacticalPlanner implements Synchronizable, Blockable, WithDesiredSpeed
48  {
49  
50      /** Deviation object in case of no desired deviation. */
51      private static final DistancedObject<Length> NO_DEVIATION = new DistancedObject<>(Length.ZERO, Length.ZERO);
52  
53      /** LMRS data. */
54      private final LmrsData lmrsData;
55  
56      /**
57       * Constructor setting the car-following model.
58       * @param carFollowingModel car-following model
59       * @param gtu GTU
60       * @param lanePerception perception
61       * @param synchronization type of synchronization
62       * @param cooperation type of cooperation
63       * @param gapAcceptance gap-acceptance
64       * @param tailgating tailgating
65       */
66      public Lmrs(final CarFollowingModel carFollowingModel, final LaneBasedGtu gtu, final LanePerception lanePerception,
67              final Synchronization synchronization, final Cooperation cooperation, final GapAcceptance gapAcceptance,
68              final Tailgating tailgating)
69      {
70          super(carFollowingModel, gtu, lanePerception);
71          this.lmrsData = new LmrsData(synchronization, cooperation, gapAcceptance, tailgating);
72      }
73  
74      @Override
75      public OperationalPlan generateOperationalPlan(final Duration startTime, final DirectedPoint2d locationAtStartTime)
76              throws GtuException, NetworkException, ParameterException
77      {
78          // create tactical context
79          TacticalContextEgo context = new TacticalContextEgo(getGtu());
80  
81          // LMRS
82          SimpleOperationalPlan simplePlan = LmrsUtil.determinePlan(context, this.lmrsData, this);
83  
84          // lower acceleration from additional sources, consider adjacent lane when changing lane or synchronizing
85          RelativeLane[] lanes;
86          double dLeft = context.getParameters().getParameter(LmrsParameters.DLEFT);
87          double dRight = context.getParameters().getParameter(LmrsParameters.DRIGHT);
88          double dSync = context.getParameters().getParameter(LmrsParameters.DSYNC);
89          if (dLeft >= dSync && dLeft >= dRight)
90          {
91              lanes = new RelativeLane[] {RelativeLane.CURRENT, RelativeLane.LEFT};
92          }
93          else if (dRight >= dSync)
94          {
95              lanes = new RelativeLane[] {RelativeLane.CURRENT, RelativeLane.RIGHT};
96          }
97          else
98          {
99              lanes = new RelativeLane[] {RelativeLane.CURRENT};
100         }
101         for (RelativeLane lane : lanes)
102         {
103             if (getPerception().getLaneStructure().getRootCrossSection().contains(lane))
104             {
105                 // On the current lane, consider all incentives. On adjacent lanes only consider incentives beyond the distance
106                 // over which a lane change is not yet possible, i.e. the merge distance.
107                 // TODO: consider route in incentives (only if not on current lane?)
108                 Length mergeDistance = lane.isCurrent() ? Length.ZERO
109                         : Synchronization.getMergeDistance(getPerception(), lane.getLateralDirectionality());
110                 simplePlan.minimizeAcceleration(getAcceleration(context, lane, mergeDistance));
111             }
112         }
113 
114         // deal with dead-end situations and lane changes that are not allowed
115         simplePlan = LaneChangeNotAllowedUtil.preventLaneChange(context, DeadEndUtil.dealWithDeadEnd(context, simplePlan));
116 
117         // set turn indicator
118         context.getIntent(TurnIndicatorStatus.class).ifPresentOrElse((d) -> getGtu().setTurnIndicatorStatus(d.object()),
119                 () -> getGtu().setTurnIndicatorStatus(TurnIndicatorStatus.NONE));
120 
121         // create plan
122         return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), simplePlan,
123                 getGtu().getParameters().getParameter(ParameterTypes.LCDUR),
124                 context.getIntent(Length.class).orElse(NO_DEVIATION));
125 
126     }
127 
128     @Override
129     public Synchronizable.State getSynchronizationState()
130     {
131         return this.lmrsData.getSynchronizationState();
132     }
133 
134     @Override
135     public boolean isBlocking()
136     {
137         for (AccelerationIncentive acc : getAccelerationIncentives())
138         {
139             if (acc instanceof AccelerationConflicts)
140             {
141                 return ((AccelerationConflicts) acc).isBlocking();
142             }
143         }
144         return false;
145     }
146 
147     @Override
148     public Speed getDesiredSpeed()
149     {
150         return this.lmrsData.getDesiredSpeed();
151     }
152 
153     @Override
154     public String toString()
155     {
156         return "LMRS [mandatoryIncentives=" + getMandatoryIncentives() + ", voluntaryIncentives=" + getVoluntaryIncentives()
157                 + ", accelerationIncentives = " + getAccelerationIncentives() + "]";
158     }
159 
160 }