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