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.DurationUnit;
15 import org.djunits.unit.LengthUnit;
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.AbstractOTSModel;
26 import org.opentrafficsim.core.dsol.OTSSimulator;
27 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
28 import org.opentrafficsim.core.geometry.OTSGeometryException;
29 import org.opentrafficsim.core.geometry.OTSLine3D;
30 import org.opentrafficsim.core.geometry.OTSPoint3D;
31 import org.opentrafficsim.core.gtu.GTUDirectionality;
32 import org.opentrafficsim.core.gtu.GTUType;
33 import org.opentrafficsim.core.gtu.RelativePosition;
34 import org.opentrafficsim.core.network.LateralDirectionality;
35 import org.opentrafficsim.core.network.LinkType;
36 import org.opentrafficsim.core.network.Network;
37 import org.opentrafficsim.core.network.NetworkException;
38 import org.opentrafficsim.core.network.OTSNetwork;
39 import org.opentrafficsim.core.network.OTSNode;
40 import org.opentrafficsim.road.DefaultTestParameters;
41 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
42 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
43 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
44 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
45 import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
46 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
47 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
48 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
49 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
50 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
51 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
52 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
53 import org.opentrafficsim.road.network.lane.CrossSectionLink;
54 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
55 import org.opentrafficsim.road.network.lane.Lane;
56 import org.opentrafficsim.road.network.lane.LaneType;
57 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
58 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
59
60 import nl.tudelft.simulation.dsol.SimRuntimeException;
61
62
63
64
65
66
67
68
69
70
71
72 public class LaneChangeModelTest extends AbstractOTSModel implements UNITS
73 {
74
75 private static final long serialVersionUID = 20150313;
76
77
78 private OTSNetwork network = new OTSNetwork("lane change model test network");
79
80
81
82 public LaneChangeModelTest()
83 {
84 super(new OTSSimulator());
85 }
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 private static CrossSectionLink makeLink(final Network network, final String name, final OTSNode from, final OTSNode to,
101 final Length width, final OTSSimulatorInterface simulator) throws OTSGeometryException, NetworkException
102 {
103
104
105 OTSPoint3D[] coordinates = new OTSPoint3D[] { new OTSPoint3D(from.getPoint().x, from.getPoint().y, 0),
106 new OTSPoint3D(to.getPoint().x, to.getPoint().y, 0) };
107 OTSLine3D line = new OTSLine3D(coordinates);
108 CrossSectionLink link =
109 new CrossSectionLink(network, name, from, to, LinkType.ROAD, line, simulator, LaneKeepingPolicy.KEEP_RIGHT);
110 return link;
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124
125 private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType, final Length latPos,
126 final Length width) throws NamingException, NetworkException, OTSGeometryException
127 {
128 Map<GTUType, Speed> speedMap = new LinkedHashMap<>();
129 speedMap.put(GTUType.VEHICLE, new Speed(100, KM_PER_HOUR));
130
131 Lane result =
132 new Lane(link, id, latPos, latPos, width, width, laneType, speedMap, new OvertakingConditions.LeftAndRight());
133 return result;
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148 public static Lane[] makeMultiLane(final Network network, final String name, final OTSNode from, final OTSNode to,
149 final LaneType laneType, final int laneCount, final OTSSimulatorInterface simulator) throws Exception
150 {
151 Length width = new Length(laneCount * 4.0, METER);
152 final CrossSectionLink link = makeLink(network, name, from, to, width, simulator);
153 Lane[] result = new Lane[laneCount];
154 width = new Length(4.0, METER);
155 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
156 {
157
158 Length latPos = new Length((-0.5 - laneIndex) * width.getSI(), METER);
159 result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPos, width);
160 }
161 return result;
162 }
163
164
165
166
167
168 @Test
169 public final void changeRight() throws Exception
170 {
171 GTUType gtuType = CAR;
172 LaneType laneType = LaneType.TWO_WAY_LANE;
173 int laneCount = 2;
174 this.simulator.initialize(Time.ZERO, Duration.ZERO, new Duration(3600.0, DurationUnit.SECOND), this);
175 Lane[] lanes =
176 makeMultiLane(this.network, "Road with two lanes", new OTSNode(this.network, "From", new OTSPoint3D(0, 0, 0)),
177 new OTSNode(this.network, "To", new OTSPoint3D(200, 0, 0)), laneType, laneCount, this.simulator);
178
179
180
181 assertEquals("Leftmost lane should not have accessible adjacent lanes on the LEFT side", 0,
182 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType, GTUDirectionality.DIR_PLUS).size());
183 assertEquals("Leftmost lane should have one accessible adjacent lane on the RIGHT side", 1,
184 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType, GTUDirectionality.DIR_PLUS).size());
185 assertEquals("Rightmost lane should have one accessible adjacent lane on the LEFT side", 1,
186 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType, GTUDirectionality.DIR_PLUS).size());
187 assertEquals("Rightmost lane should not have accessible adjacent lanes on the RIGHT side", 0,
188 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType, GTUDirectionality.DIR_PLUS).size());
189
190 Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
191 initialLongitudinalPositions
192 .add(new DirectedLanePosition(lanes[1], new Length(100, METER), GTUDirectionality.DIR_PLUS));
193 AbstractLaneChangeModel laneChangeModel = new Egoistic();
194 ParameterSet parameters = DefaultTestParameters.create();
195
196
197
198 LaneBasedIndividualGTU car = new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER),
199 new Length(2, METER), new Speed(150, KM_PER_HOUR), Length.createSI(2.0), this.simulator, this.network);
200 LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
201 new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, car), car);
202 car.setParameters(parameters);
203 car.init(strategicalPlanner, initialLongitudinalPositions, new Speed(100, KM_PER_HOUR));
204 car.getTacticalPlanner().getPerception().perceive();
205 Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
206 sameLaneGTUs.add(new HeadwayGTUSimple(car.getId(), car.getGTUType(), Length.ZERO, Length.ZERO, car.getLength(),
207 car.getSpeed(), car.getAcceleration(), null));
208 Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
209 Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
210 LaneMovementStep laneChangeModelResult = laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs,
211 preferredLaneGTUs, nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
212 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
213
214 assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
215 laneChangeModelResult.getLaneChangeDirection());
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
220
221
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
229 .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
230
231 parameters = DefaultTestParameters.create();
232
233
234
235
236
237
238
239
240
241
242
243 LaneBasedIndividualGTU collisionCar =
244 new LaneBasedIndividualGTU("LaneChangeBlockingCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
245 new Speed(150, KM_PER_HOUR), vehicleLength.multiplyBy(0.5), this.simulator, this.network);
246 strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
247 new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, collisionCar), collisionCar);
248 collisionCar.setParameters(parameters);
249 collisionCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
250 preferredLaneGTUs.clear();
251 HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(collisionCar.getId(), collisionCar.getGTUType(),
252 new Length(pos - reference.getSI(), LengthUnit.SI), collisionCar.getLength(), collisionCar.getWidth(),
253 collisionCar.getSpeed(), collisionCar.getAcceleration(), null);
254 preferredLaneGTUs.add(collisionHWGTU);
255 laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
256 nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
257 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
258
259 assertEquals("Vehicle cannot to change to the right lane because that would result in an immediate collision", null,
260 laneChangeModelResult.getLaneChangeDirection());
261 }
262 for (double pos = 0; pos < 180; pos += 5)
263 {
264 Set<DirectedLanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
265 otherLongitudinalPositions
266 .add(new DirectedLanePosition(lanes[1], new Length(pos, METER), GTUDirectionality.DIR_PLUS));
267
268 parameters = new ParameterSet();
269 parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
270 parameters.setParameter(ParameterTypes.B, new Acceleration(1.5, METER_PER_SECOND_2));
271 parameters.setParameter(ParameterTypes.S0, new Length(2, METER));
272 parameters.setParameter(ParameterTypes.T, new Duration(1, SECOND));
273 parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
274 parameters.setDefaultParameter(ParameterTypes.LOOKAHEAD);
275 parameters.setDefaultParameter(ParameterTypes.LOOKBACKOLD);
276 parameters.setParameter(AbstractIDM.DELTA, 1d);
277
278
279
280
281 LaneBasedIndividualGTU otherCar =
282 new LaneBasedIndividualGTU("OtherCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
283 new Speed(150, KM_PER_HOUR), vehicleLength.multiplyBy(0.5), this.simulator, this.network);
284 strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
285 new LaneBasedCFLCTacticalPlanner(new IDMPlusOld(), laneChangeModel, otherCar), otherCar);
286 otherCar.setParameters(parameters);
287 otherCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
288 preferredLaneGTUs.clear();
289 HeadwayGTUSimple collisionHWGTU = new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(),
290 new Length(pos - car.position(lanes[0], car.getReference()).getSI(), LengthUnit.SI), otherCar.getLength(),
291 otherCar.getWidth(), otherCar.getSpeed(), otherCar.getAcceleration(), null);
292 preferredLaneGTUs.add(collisionHWGTU);
293 laneChangeModelResult = new Egoistic().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
297 laneChangeModelResult = new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
298 nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
299 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
300
301
302
303
304 }
305 }
306
307
308
309
310
311
312 @Override
313 public void constructModel() throws SimRuntimeException
314 {
315
316 }
317
318
319 @Override
320 public final OTSNetwork getNetwork()
321 {
322 return this.network;
323 }
324
325 }