1   package org.opentrafficsim.road.gtu.lane.changing;
2   
3   import static org.junit.Assert.assertEquals;
4   
5   import java.util.Collection;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.Map;
9   import java.util.Set;
10  
11  import javax.naming.NamingException;
12  
13  import org.djunits.unit.DurationUnit;
14  import org.djunits.unit.LengthUnit;
15  import org.djunits.unit.util.UNITS;
16  import org.djunits.value.vdouble.scalar.Acceleration;
17  import org.djunits.value.vdouble.scalar.Direction;
18  import org.djunits.value.vdouble.scalar.Duration;
19  import org.djunits.value.vdouble.scalar.Length;
20  import org.djunits.value.vdouble.scalar.Speed;
21  import org.djunits.value.vdouble.scalar.Time;
22  import org.junit.Test;
23  import org.opentrafficsim.base.parameters.ParameterSet;
24  import org.opentrafficsim.base.parameters.ParameterTypes;
25  import org.opentrafficsim.core.dsol.AbstractOTSModel;
26  import org.opentrafficsim.core.dsol.OTSSimulator;
27  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
28  import org.opentrafficsim.core.geometry.OTSGeometryException;
29  import org.opentrafficsim.core.geometry.OTSLine3D;
30  import org.opentrafficsim.core.geometry.OTSPoint3D;
31  import org.opentrafficsim.core.gtu.GTUDirectionality;
32  import org.opentrafficsim.core.gtu.GTUType;
33  import org.opentrafficsim.core.gtu.RelativePosition;
34  import org.opentrafficsim.core.network.LateralDirectionality;
35  import org.opentrafficsim.core.network.LinkType;
36  import org.opentrafficsim.core.network.NetworkException;
37  import org.opentrafficsim.road.DefaultTestParameters;
38  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
39  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
40  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
41  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
42  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
43  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
44  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
45  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
46  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
47  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
48  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
49  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
50  import org.opentrafficsim.road.network.OTSRoadNetwork;
51  import org.opentrafficsim.road.network.RoadNetwork;
52  import org.opentrafficsim.road.network.lane.CrossSectionLink;
53  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
54  import org.opentrafficsim.road.network.lane.Lane;
55  import org.opentrafficsim.road.network.lane.LaneType;
56  import org.opentrafficsim.road.network.lane.OTSRoadNode;
57  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
58  
59  import nl.tudelft.simulation.dsol.SimRuntimeException;
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  public class LaneChangeModelTest extends AbstractOTSModel implements UNITS
72  {
73      
74      private static final long serialVersionUID = 20150313;
75  
76      
77      private OTSRoadNetwork network = new OTSRoadNetwork("lane change model test network", true);
78  
79      
80  
81      public LaneChangeModelTest()
82      {
83          super(new OTSSimulator());
84      }
85  
86      
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99      private static CrossSectionLink makeLink(final RoadNetwork network, final String name, final OTSRoadNode from,
100             final OTSRoadNode to, final Length width, final OTSSimulatorInterface simulator)
101             throws OTSGeometryException, NetworkException
102     {
103         
104         
105         OTSPoint3D[] coordinates = new OTSPoint3D[] { new OTSPoint3D(from.getPoint().x, from.getPoint().y, 0),
106                 new OTSPoint3D(to.getPoint().x, to.getPoint().y, 0) };
107         OTSLine3D line = new OTSLine3D(coordinates);
108         CrossSectionLink link = new CrossSectionLink(network, name, from, to, network.getLinkType(LinkType.DEFAULTS.ROAD), line,
109                 simulator, LaneKeepingPolicy.KEEPRIGHT);
110         return link;
111     }
112 
113     
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125     private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType, final Length latPos,
126             final Length width) throws NamingException, NetworkException, OTSGeometryException
127     {
128         Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
129         speedMap.put(link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), new Speed(100, KM_PER_HOUR));
130         
131         Lane result = new Lane(link, id, latPos, latPos, width, width, laneType, speedMap);
132         return result;
133     }
134 
135     
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147     public static Lane[] makeMultiLane(final RoadNetwork network, final String name, final OTSRoadNode from,
148             final OTSRoadNode to, final LaneType laneType, final int laneCount, final OTSSimulatorInterface simulator)
149             throws Exception
150     {
151         Length width = new Length(laneCount * 4.0, METER);
152         final CrossSectionLink link = makeLink(network, name, from, to, width, simulator);
153         Lane[] result = new Lane[laneCount];
154         width = new Length(4.0, METER);
155         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
156         {
157             
158             Length latPos = new Length((-0.5 - laneIndex) * width.getSI(), METER);
159             result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPos, width);
160         }
161         return result;
162     }
163 
164     
165 
166 
167 
168     @Test
169     public final void changeRight() throws Exception
170     {
171         GTUType gtuType = this.network.getGtuType(GTUType.DEFAULTS.CAR);
172         LaneType laneType = this.network.getLaneType(LaneType.DEFAULTS.TWO_WAY_LANE);
173         int laneCount = 2;
174         this.simulator.initialize(Time.ZERO, Duration.ZERO, new Duration(3600.0, DurationUnit.SECOND), this);
175         Lane[] lanes = makeMultiLane(this.network, "Road with two lanes",
176                 new OTSRoadNode(this.network, "From", new OTSPoint3D(0, 0, 0), Direction.ZERO),
177                 new OTSRoadNode(this.network, "To", new OTSPoint3D(200, 0, 0), Direction.ZERO), laneType, laneCount,
178                 this.simulator);
179 
180         
181         
182         assertEquals("Leftmost lane should not have accessible adjacent lanes on the LEFT side", 0,
183                 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType, GTUDirectionality.DIR_PLUS).size());
184         assertEquals("Leftmost lane should have one accessible adjacent lane on the RIGHT side", 1,
185                 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType, GTUDirectionality.DIR_PLUS).size());
186         assertEquals("Rightmost lane should have one accessible adjacent lane on the LEFT side", 1,
187                 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType, GTUDirectionality.DIR_PLUS).size());
188         assertEquals("Rightmost lane should not have accessible adjacent lanes on the RIGHT side", 0,
189                 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType, GTUDirectionality.DIR_PLUS).size());
190 
191         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
192         initialLongitudinalPositions
193                 .add(new DirectedLanePosition(lanes[1], new Length(100, METER), GTUDirectionality.DIR_PLUS));
194         AbstractLaneChangeModel laneChangeModel = new Egoistic();
195         ParameterSet parameters = DefaultTestParameters.create();
196         
197         
198         
199         LaneBasedIndividualGTU car = new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER),
200                 new Length(2, METER), new Speed(150, KM_PER_HOUR), Length.instantiateSI(2.0), this.simulator, this.network);
201         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
202                 new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, car), car);
203         car.setParameters(parameters);
204         car.init(strategicalPlanner, initialLongitudinalPositions, new Speed(100, KM_PER_HOUR));
205         car.getTacticalPlanner().getPerception().perceive();
206         Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
207         sameLaneGTUs.add(new HeadwayGTUSimple(car.getId(), car.getGTUType(), Length.ZERO, Length.ZERO, car.getLength(),
208                 car.getSpeed(), car.getAcceleration(), null));
209         Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
210         Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
211         LaneMovementStep laneChangeModelResult = laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs,
212                 preferredLaneGTUs, nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
213                 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
214         
215         assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
216                 laneChangeModelResult.getLaneChangeDirection());
217         Length rear = car.position(lanes[0], car.getRear());
218         Length front = car.position(lanes[0], car.getFront());
219         Length reference = car.position(lanes[0], RelativePosition.REFERENCE_POSITION);
220         
221         
222         
223         Length vehicleLength = front.minus(rear);
224         Length collisionStart = reference.minus(vehicleLength);
225         Length collisionEnd = reference.plus(vehicleLength);
226         for (double pos = collisionStart.getSI() + 0.01; pos < collisionEnd.getSI() - 0.01; pos += 0.1)
227         {
228             Set<DirectedLanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
229             otherLongitudinalPositions
230                     .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
231 
232             parameters = DefaultTestParameters.create();
233             
234             
235             
236             
237             
238             
239             
240             
241             
242             
243             
244             LaneBasedIndividualGTU collisionCar =
245                     new LaneBasedIndividualGTU("LaneChangeBlockingCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
246                             new Speed(150, KM_PER_HOUR), vehicleLength.times(0.5), this.simulator, this.network);
247             strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
248                     new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, collisionCar), collisionCar);
249             collisionCar.setParameters(parameters);
250             collisionCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
251             preferredLaneGTUs.clear();
252             HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(collisionCar.getId(), collisionCar.getGTUType(),
253                     new Length(pos - reference.getSI(), LengthUnit.SI), collisionCar.getLength(), collisionCar.getWidth(),
254                     collisionCar.getSpeed(), collisionCar.getAcceleration(), null);
255             preferredLaneGTUs.add(collisionHWGTU);
256             laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
257                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
258                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
259             
260             assertEquals("Vehicle cannot to change to the right lane because that would result in an immediate collision", null,
261                     laneChangeModelResult.getLaneChangeDirection());
262         }
263         for (double pos = 0; pos < 180; pos += 5) 
264         {
265             Set<DirectedLanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
266             otherLongitudinalPositions
267                     .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
268 
269             parameters = new ParameterSet();
270             parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
271             parameters.setParameter(ParameterTypes.B, new Acceleration(1.5, METER_PER_SECOND_2));
272             parameters.setParameter(ParameterTypes.S0, new Length(2, METER));
273             parameters.setParameter(ParameterTypes.T, new Duration(1, SECOND));
274             parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
275             parameters.setDefaultParameter(ParameterTypes.LOOKAHEAD);
276             parameters.setDefaultParameter(ParameterTypes.LOOKBACKOLD);
277             parameters.setParameter(AbstractIDM.DELTA, 1d);
278             
279             
280             
281             
282             LaneBasedIndividualGTU otherCar =
283                     new LaneBasedIndividualGTU("OtherCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
284                             new Speed(150, KM_PER_HOUR), vehicleLength.times(0.5), this.simulator, this.network);
285             strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
286                     new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, otherCar), otherCar);
287             otherCar.setParameters(parameters);
288             otherCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
289             preferredLaneGTUs.clear();
290             HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(),
291                     new Length(pos - car.position(lanes[0], car.getReference()).getSI(), LengthUnit.SI), otherCar.getLength(),
292                     otherCar.getWidth(), otherCar.getSpeed(), otherCar.getAcceleration(), null);
293             preferredLaneGTUs.add(collisionHWGTU);
294             laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
295                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
296                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
297             
298             laneChangeModelResult = new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
299                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
300                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
301             
302             
303             
304             
305         }
306     }
307 
308     
309     
310     
311 
312     
313     @Override
314     public void constructModel() throws SimRuntimeException
315     {
316         
317     }
318 
319     
320     @Override
321     public final OTSRoadNetwork getNetwork()
322     {
323         return this.network;
324     }
325 
326 }