View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.cacc;
2   
3   import java.util.SortedSet;
4   
5   import org.djunits.value.vdouble.scalar.Acceleration;
6   import org.djunits.value.vdouble.scalar.Duration;
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djunits.value.vdouble.scalar.Speed;
9   import org.djunits.value.vdouble.scalar.Time;
10  import org.opentrafficsim.base.parameters.ParameterException;
11  import org.opentrafficsim.base.parameters.ParameterTypeAcceleration;
12  import org.opentrafficsim.base.parameters.ParameterTypeDuration;
13  import org.opentrafficsim.base.parameters.ParameterTypeLength;
14  import org.opentrafficsim.base.parameters.ParameterTypes;
15  import org.opentrafficsim.base.parameters.Parameters;
16  import org.opentrafficsim.core.gtu.GTUException;
17  import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
18  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
19  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
20  import org.opentrafficsim.core.network.LateralDirectionality;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
23  import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
24  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
25  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
26  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
27  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
28  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
29  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
30  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
31  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
32  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
33  import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
34  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Desire;
35  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
36  import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
37  
38  import nl.tudelft.simulation.language.d3.DirectedPoint;
39  
40  /**
41   * <p>
42   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
43   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
44   * <p>
45   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 27 sep. 2018 <br>
46   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
47   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
48   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
49   */
50  @SuppressWarnings("serial")
51  public class CaccTacticalPlanner extends AbstractLaneBasedTacticalPlanner
52  {
53  
54      /** Lane change info. */
55      private final LaneChange laneChange;
56  
57      /** Longitudinal controller. */
58      private final CaccController controller;
59  
60      /** Platoon. */
61      private Platoon platoon;
62  
63      /** Look ahead parameter type. */
64      protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
65  
66      /** ... */
67      public static final ParameterTypeDuration T_GAP = CaccParameters.T_GAP;
68  
69      /** Look-ahead time for mandatory lane changes parameter type. */
70      public static final ParameterTypeDuration T0 = ParameterTypes.T0;
71  
72      /** Fixed model time step. */
73      public static final ParameterTypeDuration DT = ParameterTypes.DT;
74  
75      /** Synchronization by platoon leader. */
76      public static final ParameterTypeAcceleration A_REDUCED = CaccParameters.A_REDUCED;
77  
78      /**
79       * @param carFollowingModel
80       * @param gtu
81       * @param lanePerception
82       * @param controller
83       */
84      public CaccTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu,
85              final LanePerception lanePerception, final CaccController controller)
86      {
87          super(carFollowingModel, gtu, lanePerception);
88          this.controller = controller;
89          this.laneChange = new LaneChange(gtu);
90      }
91  
92      /**
93       * Sets the platoon.
94       * @param platoon Platoon; platoon
95       */
96      public void setPlatoon(final Platoon platoon)
97      {
98          this.platoon = platoon;
99          this.controller.setPlatoon(platoon);
100     }
101 
102     /**
103      * Generate operational plan.
104      */
105     @Override
106     public OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
107             throws OperationalPlanException, GTUException, NetworkException, ParameterException
108     {
109 
110         // Perception objects
111         LanePerception perception = getPerception();
112         getGtu().getTacticalPlanner().getPerception().perceive(); // update perception
113         ControllerPerceptionCategory sensors = getPerception().getPerceptionCategory(ControllerPerceptionCategory.class);
114         InfrastructurePerception infra = getPerception().getPerceptionCategory(InfrastructurePerception.class);
115 
116         // Current GTU
117         LaneBasedGTU gtu = getGtu();
118         Speed speed = gtu.getSpeed(); // Actual speed, not perceived
119         Parameters parameters = gtu.getParameters();
120 
121         // Speed limit
122         SpeedLimitProspect slp = getPerception().getPerceptionCategory(InfrastructurePerception.class)
123                 .getSpeedLimitProspect(RelativeLane.CURRENT);
124         SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
125 
126         // Platoon list
127         String gtuId = gtu.getId();
128 
129         // Initialize operational plan
130         SimpleOperationalPlan/plan/operational/SimpleOperationalPlan.html#SimpleOperationalPlan">SimpleOperationalPlan plan = new SimpleOperationalPlan(Acceleration.ZERO, Duration.ZERO);
131 
132         // Determine desire to change lanes (current, left, right)
133         SortedSet<InfrastructureLaneChangeInfo> currentInfo = infra.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT);
134         Length currentFirst = currentInfo.isEmpty() || currentInfo.first().getRequiredNumberOfLaneChanges() == 0
135                 ? Length.POSITIVE_INFINITY : currentInfo.first().getRemainingDistance();
136 
137         double dCurr = 0.0;
138         double dLeft = 0.0;
139         double dRigh = 0.0;
140 
141         // Desire for current lane
142         if (infra.getCrossSection().contains(RelativeLane.CURRENT))
143         {
144             dCurr = determineDesire(infra, parameters, speed, RelativeLane.CURRENT);
145         }
146 
147         // Desire for left lane
148         if (perception.getLaneStructure().getExtendedCrossSection().contains(RelativeLane.LEFT)
149                 && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.LEFT).neg().lt(currentFirst))
150         {
151             // Desire to leave left lane
152             dLeft = determineDesire(infra, parameters, speed, RelativeLane.LEFT);
153             // desire to leave from current to left lane
154             dLeft = dLeft < dCurr ? dCurr : dLeft > dCurr ? -dLeft : 0;
155         }
156 
157         // Desire for right lane
158         if (perception.getLaneStructure().getExtendedCrossSection().contains(RelativeLane.RIGHT) && infra
159                 .getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.RIGHT).neg().lt(currentFirst))
160         {
161             // Desire to leave right lane
162             dRigh = determineDesire(infra, parameters, speed, RelativeLane.RIGHT);
163             // desire to leave from current to right lane
164             dRigh = dRigh < dCurr ? dCurr : dRigh > dCurr ? -dRigh : 0;
165         }
166         // Offset to ensure keep right policy
167         dRigh = dRigh + 0.01;
168         Desireroad/gtu/lane/tactical/util/lmrs/Desire.html#Desire">Desire desire = new Desire(dLeft, dRigh);
169 
170         // Determine and set direction of lane change, based on desire and if not already changing lanes
171         LateralDirectionality laneChangeDirection;
172         laneChangeDirection = LateralDirectionality.NONE;
173         double thresholdLeft = 0.1; // Threshold for changing to the left
174         double thresholdRigh = 0.0; // Threshold for changing to the right
175 
176         // By default; direction is straight
177         LateralDirectionality direction = LateralDirectionality.NONE;
178         TurnIndicatorStatus turndirection;
179 
180         if (this.platoon != null)
181         {
182             if (desire.leftIsLargerOrEqual() && desire.getLeft() > thresholdLeft
183                     && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.LEFT).gt0())
184             {
185                 // Direction is desired and possible (set blinker accordingly)
186                 turndirection = TurnIndicatorStatus.LEFT;
187 
188                 if (this.platoon.canInitiateLaneChangeProcess())
189                 {
190                     laneChangeDirection = LateralDirectionality.LEFT;
191                     this.platoon.initiateLaneChange(laneChangeDirection);
192                 }
193             }
194             else if (!desire.leftIsLargerOrEqual() && desire.getRight() > thresholdRigh
195                     && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.RIGHT).gt0())
196             {
197                 // Direction is desired and possible (set blinker accordingly)
198                 turndirection = TurnIndicatorStatus.RIGHT;
199 
200                 if (this.platoon.canInitiateLaneChangeProcess())
201                 {
202                     laneChangeDirection = LateralDirectionality.RIGHT;
203                     this.platoon.initiateLaneChange(laneChangeDirection);
204                 }
205             }
206             else
207             {
208                 turndirection = TurnIndicatorStatus.NONE;
209             }
210 
211             // Direction of initiated lane change, returns NONE if truck should not change lanes (yet)
212             direction = this.platoon.shouldChangeLane(gtuId);
213 
214         }
215         else
216         {
217             turndirection = TurnIndicatorStatus.NONE;
218         }
219 
220         // Leader and follower (adjacent lane) based on desired lane change direction
221         HeadwayGTU adjacentLeader = sensors.getLeader(direction);
222         HeadwayGTU adjacentFollower = sensors.getFollower(direction);
223 
224         // By default; merging is not allowed in the desired direction
225         LateralDirectionality allowedDirection = LateralDirectionality.NONE;
226 
227         // Car-following acceleration in case of lane-change
228         CarFollowingModel cfEgo = this.getCarFollowingModel();
229         Acceleration b0 = parameters.getParameter(ParameterTypes.B0);
230 
231         // Check if the desire to change lanes is there and if it is legally possible to change lanes
232         if (!direction.isNone() && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, direction).gt0())
233         {
234 
235             gtu.setTurnIndicatorStatus(turndirection);
236 
237             // Only if a lane change has occurred already -> do the following
238             if (this.platoon.laneChangeInProgress())
239             {
240                 if (adjacentLeader == null || adjacentFollower == null
241                         || (adjacentLeader.getOverlap() == null && adjacentFollower.getOverlap() == null
242                                 && CarFollowingUtil.followSingleLeader(cfEgo, parameters, gtu.getSpeed(), sli,
243                                         adjacentLeader.getDistance(), adjacentLeader.getSpeed()).ge(b0.neg())
244                                 || (this.platoon.isInPlatoon(adjacentLeader.getId()) && adjacentLeader.getOverlap() == null
245                                         && adjacentFollower.getOverlap() == null)))
246                 {
247                     if (this.platoon.getIndex(gtuId) == (this.platoon.numberOfChanged() - 1))
248                     {
249                         // Current gtu is the leader of the platoon
250                         if (this.platoon.isInPlatoon(adjacentFollower.getId()))
251                         {
252                             // Space till rear of platoon is free
253                             allowedDirection = direction;
254                             this.platoon.addLaneChange(getGtu());
255                             // this.laneChange.setDesiredLaneChangeDuration(getGtu().getParameters().getParameter(ParameterTypes.LCDUR));
256                             gtu.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
257 
258                         }
259                     }
260                 }
261             }
262             else if (adjacentLeader == null || adjacentFollower == null
263                     || (adjacentLeader.getOverlap() == null && adjacentFollower.getOverlap() == null
264                             && CarFollowingUtil.followSingleLeader(cfEgo, parameters, gtu.getSpeed(), sli,
265                                     adjacentLeader.getDistance(), adjacentLeader.getSpeed()).ge(b0.neg())))
266             {
267                 // No overlap with leader AND follower (can be the same gtu)
268                 if (adjacentFollower == null
269                         || CarFollowingUtil
270                                 .followSingleLeader(adjacentFollower.getCarFollowingModel(), parameters,
271                                         adjacentFollower.getSpeed(), sli, adjacentFollower.getDistance(), gtu.getSpeed())
272                                 .ge(b0.neg()))
273                 {
274                     // Gap is acceptable based on acceleration adjustment; merging is allowed in desired direction
275                     allowedDirection = direction;
276                     this.platoon.addLaneChange(getGtu());
277                     gtu.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
278                 }
279             }
280         }
281         // Break.on(gtu, "10", 0.0, true);
282         // Decrease acceleration to create gap (only leading vehicle in platoon)
283         // Also checks whether there are platoon vehicles behind (otherwise do not decrease speed)
284         // && this.platoon.laneChangeInProgress() -> changed to when direction is on
285         // String leaderID = (platoon.getId(platoon.size()-1));
286 
287         if (this.platoon != null && this.platoon.getIndex(gtuId) == 0 && !this.platoon.canInitiateLaneChangeProcess()
288         // && turndirection != TurnIndicatorStatus.NONE
289         // && this.platoon.laneChangeInProgress()
290         // && this.platoon.isInPlatoon(sensors.getFollower(LateralDirectionality.NONE).getId())
291                 && allowedDirection.isNone() && !direction.isNone()
292                 && infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, direction).gt0())
293         {
294             // We are the leading vehicle, we want to change to a direction but are not allowed (no space)
295             // Now only if the last vehicle has changed already
296             Acceleration atemp = this.controller.calculateAcceleration(gtu);
297             Acceleration reduction = parameters.getParameter(A_REDUCED);
298             Acceleration amax = parameters.getParameter(CaccParameters.A_MAX);
299             Acceleration amin = parameters.getParameter(CaccParameters.A_MIN);
300             Acceleration areduced = atemp.minus(reduction);
301             // Limit deceleration by upper and lower limits from parameters
302             Acceleration aredLimit = Acceleration
303                     .instantiateSI(areduced.si < amax.si ? (areduced.si > amin.si ? areduced.si : amin.si) : amax.si);
304             plan = new SimpleOperationalPlan(aredLimit, parameters.getParameter(DT), allowedDirection);
305 
306         }
307         else
308         {
309             plan = new SimpleOperationalPlan(this.controller.calculateAcceleration(gtu), parameters.getParameter(DT),
310                     allowedDirection);
311         }
312 
313         return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), startTime, plan, this.laneChange);
314 
315     }
316 
317     /** ... 
318      * @param infra InfrastructurePerception
319      * @param parameters Parameters
320      * @param speed Speed
321      * @param laneDirection RelativeLane
322      * @return Double determined desire
323      * @throws ParameterException */
324     private Double determineDesire(final InfrastructurePerception infra, final Parameters parameters, final Speed speed,
325             final RelativeLane laneDirection) throws ParameterException
326     {
327         double dCurr;
328 
329         double dOut = 0.0; // reset dOut
330 
331         for (InfrastructureLaneChangeInfo info : infra.getInfrastructureLaneChangeInfo(laneDirection))
332         {
333             double d;
334 
335             // Desire to leave current lane based on remaining distance
336             double d1 = 1 - info.getRemainingDistance().si
337                     / (info.getRequiredNumberOfLaneChanges() * parameters.getParameter(LOOKAHEAD).si);
338             double d2 = 1 - (info.getRemainingDistance().si / speed.si)
339                     / (info.getRequiredNumberOfLaneChanges() * parameters.getParameter(T0).si);
340             d1 = d2 > d1 ? d2 : d1;
341 
342             d = d1 < 0 ? 0 : d1;
343 
344             dOut = d > dOut ? d : dOut;
345         }
346 
347         dCurr = dOut;
348 
349         return dCurr;
350     }
351 
352 }