View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.lmrs;
2   
3   import java.util.ArrayList;
4   
5   import nl.tudelft.simulation.language.d3.DirectedPoint;
6   
7   import org.djunits.unit.AccelerationUnit;
8   import org.djunits.unit.SpeedUnit;
9   import org.djunits.value.vdouble.scalar.Acceleration;
10  import org.djunits.value.vdouble.scalar.Speed;
11  import org.djunits.value.vdouble.scalar.Time.Abs;
12  import org.opentrafficsim.core.gtu.GTU;
13  import org.opentrafficsim.core.gtu.GTUException;
14  import org.opentrafficsim.core.gtu.drivercharacteristics.ParameterException;
15  import org.opentrafficsim.core.gtu.drivercharacteristics.ParameterTypeDouble;
16  import org.opentrafficsim.core.gtu.drivercharacteristics.ParameterTypes;
17  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
18  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
19  import org.opentrafficsim.core.network.NetworkException;
20  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
21  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
22  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
23  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
24  import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
25  
26  /**
27   * Implementation of the LMRS (Lane change Model with Relaxation and Synchronization).
28   * See Schakel, W.J., Knoop, V.L., and Van Arem, B. (2012), 
29   * <a href="http://victorknoop.eu/research/papers/TRB2012_LMRS_reviewed.pdf">LMRS: Integrated Lane Change Model with 
30   * Relaxation and Synchronization</a>, Transportation Research Records: Journal of the Transportation Research Board, 
31   * No. 2316, pp. 47-57. Note in the official versions of TRB and TRR some errors appeared due to the typesetting of the 
32   * papers (not in the preprint provided here). A list of errata for the official versions is found 
33   * <a href="http://victorknoop.eu/research/papers/Erratum_LMRS.pdf">here</a>.
34   * @author Wouter Schakel
35   */
36  public class LMRS extends AbstractLaneBasedTacticalPlanner {
37  
38  	private static final long serialVersionUID = 20160803L;
39  	
40  	/** Free lane change desire threshold. */
41  	public static final ParameterTypeDouble DFREE = new ParameterTypeDouble("dFree", 
42  			"Free lane change desire threshold.", 0.365) {
43  		public void check(double value) throws ParameterException {
44  			ParameterException.failIf(value<=0, "Parameter of type dFree may not have a negative or zero value.");
45  			ParameterException.failIf(value>1, "Parameter of type dFree may not have a value greater than 1.");
46  		}		
47  	};
48  	
49  	/** Synchronized lane change desire threshold. */
50  	public static final ParameterTypeDouble DSYNC = new ParameterTypeDouble("dSync", 
51  			"Synchronized lane change desire threshold.", 0.577) {
52  		public void check(double value) throws ParameterException {
53  			ParameterException.failIf(value<=0, "Parameter of type dSync may not have a negative or zero value.");
54  			ParameterException.failIf(value>1, "Parameter of type dSync may not have a value greater than 1.");
55  		}		
56  	};
57  	
58  	/** Cooperative lane change desire threshold. */
59  	public static final ParameterTypeDouble DCOOP = new ParameterTypeDouble("dCoop", 
60  			"Cooperative lane change desire threshold.", 0.788) {
61  		public void check(double value) throws ParameterException {
62  			ParameterException.failIf(value<=0, "Parameter of type dCoop may not have a negative or zero value.");
63  			ParameterException.failIf(value>1, "Parameter of type dCoop may not have a value greater than 1.");
64  		}		
65  	};
66  	
67  	/** Minimum acceleration for current plan. */
68  	protected Acceleration minimumAcceleration;
69  	
70  	/** List of mandatory lane change incentives. */
71  	protected ArrayList<MandatoryIncentive> mandatoryIncentives;
72  	
73  	/** List of voluntary lane change incentives. */
74  	protected ArrayList<VoluntaryIncentive> voluntaryIncentives;
75  	
76  	/**
77  	 * Adds a mandatory incentive.
78  	 * @param incentive Incentive to add.
79  	 */
80  	public void addMandatoryIncentive(MandatoryIncentive incentive) {
81  		this.mandatoryIncentives.add(incentive);
82  	}
83  	
84  	/**
85  	 * Adds a voluntary incentive.
86  	 * @param incentive Incentive to add.
87  	 */
88  	public void addVoluntaryIncentive(VoluntaryIncentive incentive) {
89  		this.voluntaryIncentives.add(incentive);
90  	}
91  	
92  	/**
93  	 * Sets the default lane change incentives. These are the mandatory route incentive, and the voluntary speed and 
94  	 * keep incentives. Any existing incentives are removed.
95  	 */
96  	public void setDefaultIncentives() {
97  		this.mandatoryIncentives.clear();
98  		this.voluntaryIncentives.clear();
99  		this.mandatoryIncentives.add(new IncentiveRoute());
100 		this.voluntaryIncentives.add(new IncentiveSpeedWithCourtesy());
101 		this.voluntaryIncentives.add(new IncentiveKeep());
102 	}
103 	
104 	/**
105 	 * Disables lane changes by clearing all incentives and setting a dummy incentive as mandatory incentive.
106 	 */
107 	public void disableLaneChanges() {
108 		this.mandatoryIncentives.clear();
109 		this.voluntaryIncentives.clear();
110 		this.mandatoryIncentives.add(new IncentiveDummy());
111 	}
112 	
113 	@Override
114 	public OperationalPlan generateOperationalPlan(GTU gtu, Abs startTime, DirectedPoint locationAtStartTime) 
115 			throws OperationalPlanException, GTUException, NetworkException {
116 		
117 		// TODO: Remove this when other todo's are done, it is used as a placeholder where some acceleration needs to be
118 		// determined.
119 		Acceleration dummy = new Acceleration(0, AccelerationUnit.SI);
120 		
121 		// Check existence of mandatory incentive 
122 		if (mandatoryIncentives.isEmpty()) {
123 			throw new OperationalPlanException("At the least the LMRS requires 1 mandatory lane change incentive.");
124 		}
125 
126 		// Obtain objects to get info
127 		LaneBasedGTU gtuLane = (LaneBasedGTU) gtu;
128 		LanePerception perception = (LanePerception) gtu.getPerception();
129 		CarFollowingModel dfm = (CarFollowingModel) gtuLane.getStrategicalPlanner().getDrivingCharacteristics().getGTUFollowingModel();
130 				
131 		
132 		// TODO: Throw ParameterException
133 		Acceleration b;
134 		double dFree;
135 		double dSync;
136 		double dCoop;
137 		try {
138 			b = gtuLane.getBehavioralCharacteristics().getAccelerationParameter(ParameterTypes.B);
139 			dFree = gtuLane.getBehavioralCharacteristics().getParameter(DFREE);
140 			dSync = gtuLane.getBehavioralCharacteristics().getParameter(DSYNC);
141 			dCoop = gtuLane.getBehavioralCharacteristics().getParameter(DCOOP);
142 		} catch (ParameterException pe) {
143 			throw new RuntimeException(pe);
144 		}
145 		
146 		// Reset stuff that is used in creating a plan
147 		minimumAcceleration = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
148 		
149 		// Determine tactical plan
150 		if (Math.random()>.5) { // TODO: if changing lane (rand to disable annoying warnings)
151 			
152 			/*
153 			 * During a lane change, both leaders are followed.
154 			 */
155 			lowerAcceleration(dummy);
156 			lowerAcceleration(dummy);
157 			
158 			/*
159     		 * Operational plan.
160     		 */
161 			// TODO: Build the operational plan using minimum acceleration
162 			//LaneOperationalPlanBuilder
163 			return null;
164 			
165 		}
166 		
167 		/*
168 		 * Relaxation
169 		 */
170 		// TODO: relaxation
171 			
172 		/*
173 		 * Determine desire
174 		 * Mandatory is deduced as the maximum of a set of mandatory incentives, while voluntary desires are added.
175 		 * Depending on the level of mandatory lane change desire, voluntary desire may be included partially. If 
176 		 * both are positive or negative, voluntary desire is fully included. Otherwise, voluntary desire is less
177 		 * considered within the range dSync < |mandatory| < dCoop. The absolute value is used as large negative
178 		 * mandatory desire may also dominate voluntary desire.
179 		 */
180 		// Mandatory desire
181 		double dLeftMandatory = Double.NEGATIVE_INFINITY;
182 		double dRightMandatory = Double.NEGATIVE_INFINITY;
183 		for (MandatoryIncentive incentive : this.mandatoryIncentives) {
184 			Desire d = incentive.determineDesire(gtuLane, perception);
185 			dLeftMandatory = d.getLeft()>dLeftMandatory ? d.getLeft() : dLeftMandatory;
186 			dRightMandatory = d.getRight()>dRightMandatory ? d.getRight() : dRightMandatory;
187 		}
188 		Desire mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
189 		// Voluntary desire
190 		double dLeftVoluntary = 0;
191 		double dRightVoluntary = 0;
192 		for (VoluntaryIncentive incentive : this.voluntaryIncentives) {
193 			Desire d = incentive.determineDesire(gtuLane, perception, mandatoryDesire);
194 			dLeftVoluntary += d.getLeft();
195 			dRightVoluntary += d.getRight();
196 		}
197 		// Total desire
198 		double thetaLeft = 0;
199 		if (dLeftMandatory<=dSync || dLeftMandatory*dLeftVoluntary>=0) {
200 			// low mandatory desire, or same sign
201 			thetaLeft = 1;
202 		} else if (dSync<dLeftMandatory && dLeftMandatory<dCoop && dLeftMandatory*dLeftVoluntary<0) {
203 			// linear from 1 at dSync to 0 at dCoop
204 			thetaLeft = (dCoop-Math.abs(dLeftMandatory)) / (dCoop-dSync);
205 		}
206 		double thetaRight = 0;
207 		if (dRightMandatory<=dSync || dRightMandatory*dRightVoluntary>=0) {
208 			// low mandatory desire, or same sign
209 			thetaRight = 1;
210 		} else if (dSync<dRightMandatory && dRightMandatory<dCoop && dRightMandatory*dRightVoluntary<0) {
211 			// linear from 1 at dSync to 0 at dCoop
212 			thetaRight = (dCoop-Math.abs(dRightMandatory)) / (dCoop-dSync);
213 		}
214 		Desire totalDesire = new Desire(dLeftMandatory + thetaLeft*dLeftVoluntary, 
215 				dRightMandatory + thetaRight*dRightVoluntary);
216 		
217 		
218 		/*
219 		 * Gap acceptance
220 		 * The adjacent gap is accepted if acceleration is safe for the potential follower and for this driver.
221 		 */
222 		// TODO: get neighboring vehicles, use their (perceived) car-following model with altered headway
223 		Acceleration aFollow = dummy;
224 		Acceleration aSelf = dummy;
225 		boolean leftAllowed = true; // TODO: get from perception / infrastructure, with relaxation
226 		boolean acceptLeft = aSelf.getSI()>=-b.si*totalDesire.getLeft() && 
227                 aFollow.getSI()>=-b.si*totalDesire.getLeft() && leftAllowed;
228         aFollow = dummy;
229 		aSelf = dummy;
230 		boolean rightAllowed = true; // TODO: get from perception / infrastructure, with relaxation
231 		boolean acceptRight = aSelf.getSI()>=-b.si*totalDesire.getRight() && 
232 				aFollow.getSI()>=-b.si*totalDesire.getRight() && rightAllowed;
233 		
234         /*
235          * Lane change decision
236          * A lane change is initiated for the largest desire if this is above the threshold and the gap is accepted.
237          * Otherwise, the indicator may be switched on.
238          */
239 		// TODO: switch on indicators (and off)?
240         boolean changeLeft = false;
241         boolean changeRight = false;
242         if (totalDesire.leftIsLargerOrEqual() && totalDesire.getLeft()>=dFree && acceptLeft) {
243         	// change left
244         	changeLeft = true;
245         } else if (!totalDesire.leftIsLargerOrEqual() && totalDesire.getRight()>=dFree && acceptRight) {
246         	// change right
247         	changeRight = true;
248         } else if (totalDesire.leftIsLargerOrEqual() && totalDesire.getLeft()>=dCoop) {
249         	// switch on left indicator
250         	
251         } else if (!totalDesire.leftIsLargerOrEqual() && totalDesire.getRight()>=dCoop) {
252         	// switch on right indicator
253         	
254         }
255         
256         /*
257          * Acceleration
258          * Acceleration is determined by the leader, and possibly adjacent vehicles for synchronization and 
259          * cooperation.
260          */
261         // follow leader
262         lowerAcceleration(dummy);
263         // synchronize
264         // TODO: get neighboring vehicles, use car-following model with altered headway
265         if (totalDesire.leftIsLargerOrEqual() && totalDesire.getLeft()>=dSync) {
266         	// sync left
267         	lowerAcceleration(safe(dummy, b));
268         } else if (!totalDesire.leftIsLargerOrEqual() && totalDesire.getRight()>=dSync) {
269         	// sync right
270         	lowerAcceleration(safe(dummy, b));
271         }
272         // cooperate
273         // TODO: get neighboring vehicles, their indicators, use car-following model with altered headway
274         if (Math.random()>0) {
275         	// cooperate left
276         	lowerAcceleration(safe(dummy, b));
277         }
278         if (Math.random()>0) {
279         	// cooperate right
280         	lowerAcceleration(safe(dummy, b));
281         }
282         
283         /*
284 		 * Operational plan.
285 		 */
286 		// TODO: Build the operational plan using minimum acceleration and including a possible lane change using 
287         // changeLeft/changeRight
288 		//LaneOperationalPlanBuilder
289         return null;
290 		
291 	}
292 	
293 	protected Acceleration calculateAcceleration(LaneBasedGTU follower, HeadwayGTU leader, double d) {
294 		// TODO: adjust desired headway based on desire
295 		// set T
296 		Acceleration a = calculateAcceleration(follower, leader);
297 		// reset T
298 		return a;
299 	}
300 	
301 	protected Acceleration calculateAcceleration(LaneBasedGTU follower, HeadwayGTU leader) {
302 		// TODO: speed limit
303 		// TODO: follower != self
304 		Speed speedLimit = new Speed(0, SpeedUnit.SI);
305 		return follower.getStrategicalPlanner().getDrivingCharacteristics().getGTUFollowingModel().computeAcceleration(follower.getVelocity(), follower.getMaximumVelocity(), leader.getGtuSpeed(), leader.getDistance(), speedLimit);
306 	}
307 
308 	/**
309 	 * Remembers the lowest acceleration per tactical plan.
310 	 * @param a Acceleration to remember if lower than any previous acceleration.
311 	 */
312 	protected void lowerAcceleration(Acceleration a) {
313 		if (a.getSI()<this.minimumAcceleration.si) {
314 			this.minimumAcceleration = a;
315 		}
316 	}
317 	
318 	/**
319 	 * Limits the supplied acceleration to safe values, i.e. above or equal to -b.
320 	 * @param a Acceleration to limit.
321 	 * @param b Deceleration to limit to.
322 	 * @return Limited acceleration.
323 	 */
324 	protected Acceleration safe(Acceleration a, Acceleration b) {
325 		if (a.si>=-b.si) {
326 			return a;
327 		}
328 		return new Acceleration(-b.si, AccelerationUnit.SI);
329 	}
330 
331 }