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