View Javadoc
1   package org.opentrafficsim.road.gtu.lane.changing;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.opentrafficsim.road.gtu.lane.RoadGTUTypes.CAR;
5   
6   import java.util.Collection;
7   import java.util.HashSet;
8   import java.util.LinkedHashMap;
9   import java.util.LinkedHashSet;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import javax.naming.NamingException;
14  
15  import org.djunits.unit.LengthUnit;
16  import org.djunits.unit.TimeUnit;
17  import org.djunits.unit.UNITS;
18  import org.djunits.value.vdouble.scalar.Acceleration;
19  import org.djunits.value.vdouble.scalar.Duration;
20  import org.djunits.value.vdouble.scalar.Length;
21  import org.djunits.value.vdouble.scalar.Speed;
22  import org.djunits.value.vdouble.scalar.Time;
23  import org.junit.Test;
24  import org.opentrafficsim.core.dsol.OTSModelInterface;
25  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
26  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
27  import org.opentrafficsim.core.geometry.OTSGeometryException;
28  import org.opentrafficsim.core.geometry.OTSLine3D;
29  import org.opentrafficsim.core.geometry.OTSPoint3D;
30  import org.opentrafficsim.core.gtu.GTUDirectionality;
31  import org.opentrafficsim.core.gtu.GTUType;
32  import org.opentrafficsim.core.gtu.RelativePosition;
33  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
34  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
35  import org.opentrafficsim.core.network.LateralDirectionality;
36  import org.opentrafficsim.core.network.LinkType;
37  import org.opentrafficsim.core.network.LongitudinalDirectionality;
38  import org.opentrafficsim.core.network.Network;
39  import org.opentrafficsim.core.network.NetworkException;
40  import org.opentrafficsim.core.network.OTSNetwork;
41  import org.opentrafficsim.core.network.OTSNode;
42  import org.opentrafficsim.road.DefaultTestParameters;
43  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
44  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
45  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
46  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
47  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
48  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
49  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
50  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
51  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
52  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
53  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
54  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
55  import org.opentrafficsim.road.network.lane.CrossSectionLink;
56  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
57  import org.opentrafficsim.road.network.lane.Lane;
58  import org.opentrafficsim.road.network.lane.LaneType;
59  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
60  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
61  import org.opentrafficsim.simulationengine.SimpleSimulator;
62  
63  import mockit.MockUp;
64  import nl.tudelft.simulation.dsol.SimRuntimeException;
65  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
66  
67  /**
68   * Test some very basic properties of lane change models.
69   * <p>
70   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
71   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
72   * <p>
73   * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
74   * initial version 14 nov. 2014 <br>
75   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
76   */
77  public class LaneChangeModelTest implements OTSModelInterface, UNITS
78  {
79      /** */
80      private static final long serialVersionUID = 20150313;
81  
82      /** The network. */
83      private OTSNetwork network = new OTSNetwork("lane change model test network");
84  
85      /**
86       * Create a Link.
87       * @param network Network; the network
88       * @param name String; name of the new Link
89       * @param from Node; start node of the new Link
90       * @param to Node; end node of the new Link
91       * @param width Length; the width of the new Link
92       * @return Link
93       * @throws OTSGeometryException when coordinates cannot be calculated
94       * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
95       *             or the end node of the link are not registered in the network
96       */
97      private static CrossSectionLink makeLink(final Network network, final String name, final OTSNode from, final OTSNode to,
98              final Length width) throws OTSGeometryException, NetworkException
99      {
100         // TODO create a LinkAnimation if the simulator is compatible with that.
101         // FIXME The current LinkAnimation is too bad to use...
102         // Mock the simulator. Alternatively, provide the simulator from the constructModel.
103         OTSSimulatorInterface simulator = new MockUp<OTSSimulatorInterface>()
104         {
105             // no implementation needed.
106         }.getMockInstance();
107         OTSPoint3D[] coordinates = new OTSPoint3D[] { new OTSPoint3D(from.getPoint().x, from.getPoint().y, 0),
108                 new OTSPoint3D(to.getPoint().x, to.getPoint().y, 0) };
109         OTSLine3D line = new OTSLine3D(coordinates);
110         CrossSectionLink link = new CrossSectionLink(network, name, from, to, LinkType.ALL, line, simulator,
111                 LongitudinalDirectionality.DIR_PLUS, LaneKeepingPolicy.KEEP_RIGHT);
112         return link;
113     }
114 
115     /**
116      * Create one Lane.
117      * @param link Link; the link that owns the new Lane
118      * @param id String; the id of the lane, has to be unique within the link
119      * @param laneType LaneType&lt;String&gt;; the type of the new Lane
120      * @param latPos Length; the lateral position of the new Lane with respect to the design line of the link
121      * @param width Length; the width of the new Lane
122      * @return Lane
123      * @throws NamingException on ???
124      * @throws NetworkException on ??
125      * @throws OTSGeometryException when center line or contour of a link or lane cannot be generated
126      */
127     private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType, final Length latPos,
128             final Length width) throws NamingException, NetworkException, OTSGeometryException
129     {
130         Map<GTUType, LongitudinalDirectionality> directionalityMap = new LinkedHashMap<>();
131         directionalityMap.put(GTUType.ALL, LongitudinalDirectionality.DIR_PLUS);
132         Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
133         speedMap.put(GTUType.ALL, new Speed(100, KM_PER_HOUR));
134         // XXX Decide what type of overtaking conditions we want in this test
135         Lane result = new Lane(link, id, latPos, latPos, width, width, laneType, directionalityMap, speedMap,
136                 new OvertakingConditions.LeftAndRight());
137         return result;
138     }
139 
140     /**
141      * Create a simple straight road with the specified number of Lanes.
142      * @param network Network; the network
143      * @param name String; name of the Link
144      * @param from Node; starting node of the new Lane
145      * @param to Node; ending node of the new Lane
146      * @param laneType LaneType&lt;String&gt;; the type of GTU that can use the lanes
147      * @param laneCount int; number of lanes in the road
148      * @return Lane&lt;String, String&gt;[]; array containing the new Lanes
149      * @throws Exception when something goes wrong (should not happen)
150      */
151     public static Lane[] makeMultiLane(final Network network, final String name, final OTSNode from, final OTSNode to,
152             final LaneType laneType, final int laneCount) throws Exception
153     {
154         Length width = new Length(laneCount * 4.0, METER);
155         final CrossSectionLink link = makeLink(network, name, from, to, width);
156         Lane[] result = new Lane[laneCount];
157         width = new Length(4.0, METER);
158         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
159         {
160             // successive lanes have a more negative offset => more to the RIGHT
161             Length latPos = new Length((-0.5 - laneIndex) * width.getSI(), METER);
162             result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPos, width);
163         }
164         return result;
165     }
166 
167     /**
168      * Test that a vehicle in the left lane changes to the right lane if that is empty, or there is enough room.
169      * @throws Exception when something goes wrong (should not happen)
170      */
171     @Test
172     public final void changeRight() throws Exception
173     {
174         GTUType gtuType = CAR;
175         Set<GTUType> compatibility = new HashSet<GTUType>();
176         compatibility.add(gtuType);
177         LaneType laneType = new LaneType("CarLane", compatibility);
178         int laneCount = 2;
179         Lane[] lanes =
180                 makeMultiLane(this.network, "Road with two lanes", new OTSNode(this.network, "From", new OTSPoint3D(0, 0, 0)),
181                         new OTSNode(this.network, "To", new OTSPoint3D(200, 0, 0)), laneType, laneCount);
182 
183         // Let's see if adjacent lanes are accessible
184         // lanes: | 0 : 1 : 2 | in case of three lanes
185         lanes[0].accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType);
186         assertEquals("Leftmost lane should not have accessible adjacent lanes on the LEFT side", 0,
187                 lanes[0].accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).size());
188         assertEquals("Leftmost lane should have one accessible adjacent lane on the RIGHT side", 1,
189                 lanes[0].accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).size());
190         assertEquals("Rightmost lane should have one accessible adjacent lane on the LEFT side", 1,
191                 lanes[1].accessibleAdjacentLanes(LateralDirectionality.LEFT, gtuType).size());
192         assertEquals("Rightmost lane should not have accessible adjacent lanes on the RIGHT side", 0,
193                 lanes[1].accessibleAdjacentLanes(LateralDirectionality.RIGHT, gtuType).size());
194 
195         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
196         initialLongitudinalPositions
197                 .add(new DirectedLanePosition(lanes[1], new Length(100, METER), GTUDirectionality.DIR_PLUS));
198         SimpleSimulator simpleSimulator = new SimpleSimulator(new Time(0, TimeUnit.BASE_SECOND), new Duration(0, SECOND),
199                 new Duration(3600, SECOND), this);
200         AbstractLaneChangeModel laneChangeModel = new Egoistic();
201         BehavioralCharacteristics behavioralCharacteristics = DefaultTestParameters.create();
202         // LaneBasedBehavioralCharacteristics drivingCharacteristics =
203         // new LaneBasedBehavioralCharacteristics(new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2), new Acceleration(
204         // 1.5, METER_PER_SECOND_2), new Length(2, METER), new Duration(1, SECOND), 1d), laneChangeModel);
205         LaneBasedIndividualGTU car = new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER),
206                 new Length(2, METER), new Speed(150, KM_PER_HOUR), simpleSimulator, (OTSNetwork) this.network);
207         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics,
208                 new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, car), car);
209         car.init(strategicalPlanner, initialLongitudinalPositions, new Speed(100, KM_PER_HOUR));
210         car.getTacticalPlanner().getPerception().perceive();
211         Collection<Headway> sameLaneGTUs = new LinkedHashSet<Headway>();
212         sameLaneGTUs
213                 .add(new HeadwayGTUSimple(car.getId(), car.getGTUType(), Length.ZERO, car.getLength(), car.getSpeed(), null));
214         Collection<Headway> preferredLaneGTUs = new LinkedHashSet<Headway>();
215         Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<Headway>();
216         LaneMovementStep laneChangeModelResult = laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs,
217                 preferredLaneGTUs, nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
218                 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
219         // System.out.println(laneChangeModelResult.toString());
220         assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
221                 laneChangeModelResult.getLaneChangeDirection());
222         Length rear = car.position(lanes[0], car.getRear());
223         Length front = car.position(lanes[0], car.getFront());
224         Length reference = car.position(lanes[0], RelativePosition.REFERENCE_POSITION);
225         // System.out.println("rear: " + rear);
226         // System.out.println("front: " + front);
227         // System.out.println("reference: " + reference);
228         Length vehicleLength = front.minus(rear);
229         Length collisionStart = reference.minus(vehicleLength);
230         Length collisionEnd = reference.plus(vehicleLength);
231         for (double pos = collisionStart.getSI() + 0.01; pos < collisionEnd.getSI() - 0.01; pos += 0.1)
232         {
233             Set<DirectedLanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
234             otherLongitudinalPositions
235                     .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
236 
237             behavioralCharacteristics = DefaultTestParameters.create();
238             // behavioralCharacteristics = new BehavioralCharacteristics();
239             // behavioralCharacteristics.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
240             // behavioralCharacteristics.setParameter(ParameterTypes.B, new Acceleration(1.5, METER_PER_SECOND_2));
241             // behavioralCharacteristics.setParameter(ParameterTypes.S0, new Length(2, METER));
242             // behavioralCharacteristics.setParameter(ParameterTypes.T, new Duration(1, SECOND));
243             // behavioralCharacteristics.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
244             // behavioralCharacteristics.setParameter(AbstractIDM.DELTA, 1d);
245             // drivingCharacteristics =
246             // new LaneBasedBehavioralCharacteristics(new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2),
247             // new Acceleration(1.5, METER_PER_SECOND_2), new Length(2, METER), new Duration(1, SECOND), 1d),
248             // laneChangeModel);
249             LaneBasedIndividualGTU collisionCar =
250                     new LaneBasedIndividualGTU("LaneChangeBlockingCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
251                             new Speed(150, KM_PER_HOUR), simpleSimulator, (OTSNetwork) this.network);
252             strategicalPlanner = new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics,
253                     new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, collisionCar), collisionCar);
254             collisionCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
255             preferredLaneGTUs.clear();
256             HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(collisionCar.getId(), collisionCar.getGTUType(),
257                     new Length(pos - reference.getSI(), LengthUnit.SI), collisionCar.getLength(), collisionCar.getSpeed(),
258                     null);
259             preferredLaneGTUs.add(collisionHWGTU);
260             laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
261                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
262                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
263             // System.out.println(laneChangeModelResult.toString());
264             assertEquals("Vehicle cannot to change to the right lane because that would result in an immediate collision", null,
265                     laneChangeModelResult.getLaneChangeDirection());
266         }
267         for (double pos = 0; pos < 180; pos += 5) // beyond 180m, a GTU gets a plan beyond the 200m long network
268         {
269             Set<DirectedLanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
270             otherLongitudinalPositions
271                     .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
272 
273             behavioralCharacteristics = new BehavioralCharacteristics();
274             behavioralCharacteristics.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
275             behavioralCharacteristics.setParameter(ParameterTypes.B, new Acceleration(1.5, METER_PER_SECOND_2));
276             behavioralCharacteristics.setParameter(ParameterTypes.S0, new Length(2, METER));
277             behavioralCharacteristics.setParameter(ParameterTypes.T, new Duration(1, SECOND));
278             behavioralCharacteristics.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
279             behavioralCharacteristics.setDefaultParameter(ParameterTypes.LOOKAHEAD);
280             behavioralCharacteristics.setDefaultParameter(ParameterTypes.LOOKBACKOLD);
281             behavioralCharacteristics.setParameter(AbstractIDM.DELTA, 1d);
282             // drivingCharacteristics =
283             // new LaneBasedBehavioralCharacteristics(new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2),
284             // new Acceleration(1.5, METER_PER_SECOND_2), new Length(2, METER), new Duration(1, SECOND), 1d),
285             // laneChangeModel);
286             LaneBasedIndividualGTU otherCar = new LaneBasedIndividualGTU("OtherCarAt" + pos, gtuType, vehicleLength,
287                     new Length(2, METER), new Speed(150, KM_PER_HOUR), simpleSimulator, (OTSNetwork) this.network);
288             strategicalPlanner = new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics,
289                     new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, otherCar), otherCar);
290             otherCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
291             preferredLaneGTUs.clear();
292             HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(),
293                     new Length(pos - car.position(lanes[0], car.getReference()).getSI(), LengthUnit.SI), otherCar.getLength(),
294                     otherCar.getSpeed(), null);
295             preferredLaneGTUs.add(collisionHWGTU);
296             laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
297                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
298                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
299             // System.out.println(String.format("pos=%5fm Egoistic: %s", pos, laneChangeModelResult.toString()));
300             laneChangeModelResult = new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
301                     nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
302                     new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
303             // System.out.println(String.format("pos=%5fm Altruistic: %s", pos, laneChangeModelResult.toString()));
304             // assertEquals(
305             // "Vehicle cannot to change to the right lane because that would result in an immediate collision",
306             // null, laneChangeModelResult.getLaneChange());
307         }
308     }
309 
310     // TODO test/prove the expected differences between Egoistic and Altruistic
311     // TODO prove that the most restrictive car in the other lane determines what happens
312     // TODO test merge into overtaking lane
313 
314     /** {@inheritDoc} */
315     @Override
316     public void constructModel(SimulatorInterface<Time, Duration, OTSSimTimeDouble> simulator) throws SimRuntimeException
317     {
318         // DO NOTHING
319     }
320 
321     /** {@inheritDoc} */
322     @Override
323     public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator()
324 
325     {
326         return null;
327     }
328 
329     /** {@inheritDoc} */
330     @Override
331     public OTSNetwork getNetwork()
332     {
333         return this.network;
334     }
335 
336 }