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