View Javadoc
1   package org.opentrafficsim.core.gtu.lane.changing;
2   
3   import static org.junit.Assert.assertEquals;
4   
5   import java.rmi.RemoteException;
6   import java.util.Collection;
7   import java.util.HashMap;
8   import java.util.HashSet;
9   import java.util.Map;
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.junit.Test;
17  import org.opentrafficsim.core.car.LaneBasedIndividualCar;
18  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
19  import org.opentrafficsim.core.dsol.OTSModelInterface;
20  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
21  import org.opentrafficsim.core.gtu.GTUException;
22  import org.opentrafficsim.core.gtu.GTUType;
23  import org.opentrafficsim.core.gtu.RelativePosition;
24  import org.opentrafficsim.core.gtu.following.HeadwayGTU;
25  import org.opentrafficsim.core.gtu.following.IDMPlus;
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.geotools.LinearGeometry;
30  import org.opentrafficsim.core.network.geotools.NodeGeotools;
31  import org.opentrafficsim.core.network.lane.CrossSectionLink;
32  import org.opentrafficsim.core.network.lane.Lane;
33  import org.opentrafficsim.core.network.lane.LaneType;
34  import org.opentrafficsim.core.unit.AccelerationUnit;
35  import org.opentrafficsim.core.unit.FrequencyUnit;
36  import org.opentrafficsim.core.unit.LengthUnit;
37  import org.opentrafficsim.core.unit.SpeedUnit;
38  import org.opentrafficsim.core.unit.TimeUnit;
39  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
40  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
41  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
42  import org.opentrafficsim.simulationengine.SimpleSimulator;
43  
44  import com.vividsolutions.jts.geom.Coordinate;
45  import com.vividsolutions.jts.geom.GeometryFactory;
46  import com.vividsolutions.jts.geom.LineString;
47  
48  /**
49   * Test some very basic properties of lane change models.
50   * <p>
51   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
52   * reserved. <br>
53   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
54   * <p>
55   * @version 14 nov. 2014 <br>
56   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
57   */
58  public class LaneChangeModelTest implements OTSModelInterface
59  {
60      /** */
61      private static final long serialVersionUID = 20150313;
62  
63      /**
64       * Create a Link.
65       * @param name String; name of the new Link
66       * @param from Node; start node of the new Link
67       * @param to Node; end node of the new Link
68       * @param width DoubleScalar.Rel&lt;LengthUnit&gt;; the width of the new Link
69       * @return Link
70       */
71      private static CrossSectionLink<String, String> makeLink(final String name, final NodeGeotools.STR from,
72              final NodeGeotools.STR to, final DoubleScalar.Rel<LengthUnit> width)
73      {
74          // TODO create a LinkAnimation if the simulator is compatible with that.
75          // FIXME The current LinkAnimation is too bad to use...
76          CrossSectionLink<String, String> link =
77                  new CrossSectionLink<String, String>(name, from, to, new DoubleScalar.Rel<LengthUnit>(from.getPoint()
78                          .distance(to.getPoint()), LengthUnit.METER));
79          GeometryFactory factory = new GeometryFactory();
80          Coordinate[] coordinates =
81                  new Coordinate[]{new Coordinate(from.getPoint().x, from.getPoint().y, 0),
82                          new Coordinate(to.getPoint().x, to.getPoint().y, 0)};
83          LineString line = factory.createLineString(coordinates);
84          try
85          {
86              new LinearGeometry(link, line, null);
87          }
88          catch (NetworkException exception)
89          {
90              throw new Error("This network is probably too simple for this to happen...");
91          }
92          return link;
93      }
94  
95      /**
96       * Create one Lane.
97       * @param link Link; the link that owns the new Lane
98       * @param laneType LaneType&lt;String&gt;; the type of the new Lane
99       * @param latPos DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral position of the new Lane with respect to the design
100      *            line of the link
101      * @param width DoubleScalar.Rel&lt;LengthUnit&gt;; the width of the new Lane
102      * @return Lane
103      * @throws RemoteException on communications failure
104      * @throws NamingException on ???
105      * @throws NetworkException
106      */
107     private static Lane makeLane(final CrossSectionLink<String, String> link, final LaneType<String> laneType,
108             final DoubleScalar.Rel<LengthUnit> latPos, final DoubleScalar.Rel<LengthUnit> width)
109             throws RemoteException, NamingException, NetworkException
110     {
111         DoubleScalar.Abs<FrequencyUnit> f2000 = new DoubleScalar.Abs<FrequencyUnit>(2000.0, FrequencyUnit.PER_HOUR);
112         Lane result = new Lane(link, latPos, latPos, width, width, laneType, LongitudinalDirectionality.FORWARD, f2000);
113         return result;
114     }
115 
116     /**
117      * Create a simple straight road with the specified number of Lanes.
118      * @param name String; name of the Link
119      * @param from Node; starting node of the new Lane
120      * @param to Node; ending node of the new Lane
121      * @param laneType LaneType&lt;String&gt;; the type of GTU that can use the lanes
122      * @param laneCount int; number of lanes in the road
123      * @return Lane[]; array containing the new Lanes
124      * @throws NamingException when network error occurs
125      * @throws RemoteException when netwprk error occurs
126      * @throws NetworkException
127      */
128     public static Lane[] makeMultiLane(final String name, final NodeGeotools.STR from, final NodeGeotools.STR to,
129             final LaneType<String> laneType, final int laneCount) throws RemoteException, NamingException,
130             NetworkException
131     {
132         DoubleScalar.Rel<LengthUnit> width = new DoubleScalar.Rel<LengthUnit>(laneCount * 4.0, LengthUnit.METER);
133         final CrossSectionLink<String, String> link = makeLink(name, from, to, width);
134         Lane[] result = new Lane[laneCount];
135         width = new DoubleScalar.Rel<LengthUnit>(4.0, LengthUnit.METER);
136         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
137         {
138             DoubleScalar.Rel<LengthUnit> latPos =
139                     new DoubleScalar.Rel<LengthUnit>((-0.5 - laneIndex) * width.getSI(), LengthUnit.METER);
140             result[laneIndex] = makeLane(link, laneType, latPos, width);
141         }
142         for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
143         {
144             if (laneIndex < laneCount - 1)
145             {
146                 result[laneIndex].addAccessibleAdjacentLane(result[laneIndex + 1], LateralDirectionality.RIGHT);
147             }
148             if (laneIndex > 0)
149             {
150                 result[laneIndex].addAccessibleAdjacentLane(result[laneIndex - 1], LateralDirectionality.LEFT);
151             }
152         }
153         return result;
154     }
155 
156     /**
157      * Test that a vehicle in the left lane changes to the right lane if that is empty, or there is enough room.
158      * @throws RemoteException on communications failure
159      * @throws NamingException on ???
160      * @throws SimRuntimeException on ???
161      * @throws NetworkException on Network inconsistency
162      * @throws GTUException
163      */
164     @Test
165     public final void changeRight() throws RemoteException, NamingException, SimRuntimeException, NetworkException,
166             GTUException
167     {
168         GTUType<String> gtuType = new GTUType<String>("car");
169         LaneType<String> laneType = new LaneType<String>("CarLane");
170         laneType.addPermeability(gtuType);
171         Lane[] lanes =
172                 makeMultiLane("Road with two lanes", new NodeGeotools.STR("From", new Coordinate(0, 0, 0)),
173                         new NodeGeotools.STR("To", new Coordinate(200, 0, 0)), laneType, 2);
174         Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions =
175                 new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
176         initialLongitudinalPositions.put(lanes[0], new DoubleScalar.Rel<LengthUnit>(100, LengthUnit.METER));
177         SimpleSimulator simpleSimulator =
178                 new SimpleSimulator(new DoubleScalar.Abs<TimeUnit>(0, TimeUnit.SECOND),
179                         new DoubleScalar.Rel<TimeUnit>(0, TimeUnit.SECOND), new DoubleScalar.Rel<TimeUnit>(3600,
180                                 TimeUnit.SECOND), this /* CRASH - FIXME - will have to wait for Network factory*/);
181         AbstractLaneChangeModel laneChangeModel = new Egoistic();
182         LaneBasedIndividualCar<String> car =
183                 new LaneBasedIndividualCar<String>("ReferenceCar", gtuType, new IDMPlus(
184                         new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
185                         new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
186                         new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
187                                 TimeUnit.SECOND), 1d), laneChangeModel, initialLongitudinalPositions,
188                         new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), new DoubleScalar.Rel<LengthUnit>(
189                                 4, LengthUnit.METER), new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER),
190                         new DoubleScalar.Abs<SpeedUnit>(150, SpeedUnit.KM_PER_HOUR),
191                         (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
192         Collection<HeadwayGTU> sameLaneGTUs = new HashSet<HeadwayGTU>();
193         sameLaneGTUs.add(new HeadwayGTU(car, 0));
194         Collection<HeadwayGTU> preferredLaneGTUs = new HashSet<HeadwayGTU>();
195         Collection<HeadwayGTU> nonPreferredLaneGTUs = new HashSet<HeadwayGTU>();
196         LaneMovementStep laneChangeModelResult =
197                 laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
198                         nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
199                         new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
200                         new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
201                         new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
202         // System.out.println(laneChangeModelResult.toString());
203         assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
204                 laneChangeModelResult.getLaneChange());
205         DoubleScalar.Rel<LengthUnit> rear = car.position(lanes[0], car.getRear());
206         DoubleScalar.Rel<LengthUnit> front = car.position(lanes[0], car.getFront());
207         DoubleScalar.Rel<LengthUnit> reference = car.position(lanes[0], RelativePosition.REFERENCE_POSITION);
208         // System.out.println("rear:      " + rear);
209         // System.out.println("front:     " + front);
210         // System.out.println("reference: " + reference);
211         DoubleScalar.Rel<LengthUnit> vehicleLength = DoubleScalar.minus(front, rear).immutable();
212         DoubleScalar.Rel<LengthUnit> collisionStart = DoubleScalar.minus(reference, vehicleLength).immutable();
213         DoubleScalar.Rel<LengthUnit> collisionEnd = DoubleScalar.plus(reference, vehicleLength).immutable();
214         for (double pos = collisionStart.getSI() + 0.01; pos < collisionEnd.getSI() - 0.01; pos += 0.1)
215         {
216             Map<Lane, DoubleScalar.Rel<LengthUnit>> otherLongitudinalPositions =
217                     new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
218             otherLongitudinalPositions.put(lanes[1], new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
219             LaneBasedIndividualCar<String> collisionCar =
220                     new LaneBasedIndividualCar<String>("LaneChangeBlockingCar", gtuType, new IDMPlus(
221                             new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
222                             new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
223                             new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
224                                     TimeUnit.SECOND), 1d), laneChangeModel, otherLongitudinalPositions,
225                             new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), vehicleLength,
226                             new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(150,
227                                     SpeedUnit.KM_PER_HOUR), (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
228             preferredLaneGTUs.clear();
229             HeadwayGTU collisionHWGTU = new HeadwayGTU(collisionCar, pos - reference.getSI());
230             preferredLaneGTUs.add(collisionHWGTU);
231             laneChangeModelResult =
232                     new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
233                             nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
234                             new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
235                             new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
236                             new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
237             // System.out.println(laneChangeModelResult.toString());
238             assertEquals(
239                     "Vehicle cannot to change to the right lane because that would result in an immediate collision",
240                     null, laneChangeModelResult.getLaneChange());
241         }
242         for (double pos = 0; pos < 200; pos += 5)
243         {
244             Map<Lane, DoubleScalar.Rel<LengthUnit>> otherLongitudinalPositions =
245                     new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
246             otherLongitudinalPositions.put(lanes[1], new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
247             LaneBasedIndividualCar<String> otherCar =
248                     new LaneBasedIndividualCar<String>("OtherCar", gtuType, new IDMPlus(
249                             new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
250                             new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
251                             new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
252                                     TimeUnit.SECOND), 1d), laneChangeModel, otherLongitudinalPositions,
253                             new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), vehicleLength,
254                             new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(150,
255                                     SpeedUnit.KM_PER_HOUR), (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
256             preferredLaneGTUs.clear();
257             HeadwayGTU collisionHWGTU =
258                     new HeadwayGTU(otherCar, pos - car.position(lanes[0], car.getReference()).getSI());
259             preferredLaneGTUs.add(collisionHWGTU);
260             laneChangeModelResult =
261                     new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
262                             nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
263                             new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
264                             new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
265                             new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
266             // System.out.println(String.format("pos=%5fm Egoistic:   %s", pos, laneChangeModelResult.toString()));
267             laneChangeModelResult =
268                     new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
269                             nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
270                             new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
271                             new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
272                             new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
273             // System.out.println(String.format("pos=%5fm Altruistic: %s", pos, laneChangeModelResult.toString()));
274             // assertEquals(
275             // "Vehicle cannot to change to the right lane because that would result in an immediate collision",
276             // null, laneChangeModelResult.getLaneChange());
277         }
278     }
279     // TODO test/prove the expected differences between Egoistic and Altruistic
280     // TODO prove that the most restrictive car in the other lane determines what happens
281     // TODO test merge into overtaking lane
282 
283     /** {@inheritDoc} */
284     @Override
285     public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
286             throws SimRuntimeException, RemoteException
287     {
288     }
289 
290     /** {@inheritDoc} */
291     @Override
292     public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
293     {
294         return null;
295     }
296 
297 }