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