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.LinkedHashSet;
7 import java.util.Map;
8 import java.util.Set;
9
10 import javax.naming.NamingException;
11
12 import org.djunits.unit.DurationUnit;
13 import org.djunits.unit.LengthUnit;
14 import org.djunits.unit.util.UNITS;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Direction;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.junit.Test;
22 import org.opentrafficsim.base.parameters.ParameterSet;
23 import org.opentrafficsim.base.parameters.ParameterTypes;
24 import org.opentrafficsim.core.definitions.DefaultsNl;
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.GtuType;
32 import org.opentrafficsim.core.gtu.RelativePosition;
33 import org.opentrafficsim.core.network.LateralDirectionality;
34 import org.opentrafficsim.core.network.NetworkException;
35 import org.opentrafficsim.core.network.Node;
36 import org.opentrafficsim.road.DefaultTestParameters;
37 import org.opentrafficsim.road.definitions.DefaultsRoadNl;
38 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
39 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
40 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtuSimple;
41 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCfLcTacticalPlanner;
42 import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIdm;
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.LaneBasedStrategicalRoutePlanner;
50 import org.opentrafficsim.road.network.RoadNetwork;
51 import org.opentrafficsim.road.network.lane.CrossSectionLink;
52 import org.opentrafficsim.road.network.lane.Lane;
53 import org.opentrafficsim.road.network.lane.LanePosition;
54 import org.opentrafficsim.road.network.lane.LaneType;
55 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
56
57 import nl.tudelft.simulation.dsol.SimRuntimeException;
58
59
60
61
62
63
64
65
66
67 public class LaneChangeModelTest extends AbstractOtsModel implements UNITS
68 {
69
70 private static final long serialVersionUID = 20150313;
71
72
73 private RoadNetwork network;
74
75
76
77 public LaneChangeModelTest()
78 {
79 super(new OtsSimulator("LaneChangeModelTest"));
80 this.network = new RoadNetwork("lane change model test network", getSimulator());
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 private static CrossSectionLink makeLink(final RoadNetwork network, final String name, final Node from, final Node to,
97 final Length width, final OtsSimulatorInterface simulator) throws OtsGeometryException, NetworkException
98 {
99
100
101 OtsPoint3d[] coordinates = new OtsPoint3d[] {new OtsPoint3d(from.getPoint().x, from.getPoint().y, 0),
102 new OtsPoint3d(to.getPoint().x, to.getPoint().y, 0)};
103 OtsLine3d line = new OtsLine3d(coordinates);
104 CrossSectionLink link =
105 new CrossSectionLink(network, name, from, to, DefaultsNl.ROAD, line, LaneKeepingPolicy.KEEPRIGHT);
106 return link;
107 }
108
109
110
111
112
113
114
115
116
117
118
119
120
121 private static Lane makeLane(final CrossSectionLink link, final String id, final LaneType laneType, final Length latPos,
122 final Length width) throws NamingException, NetworkException, OtsGeometryException
123 {
124
125 Lane result = new Lane(link, id, latPos, latPos, width, width, laneType,
126 Map.of(DefaultsNl.VEHICLE, new Speed(100, KM_PER_HOUR)), false);
127 return result;
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141
142 public static Lane[] makeMultiLane(final RoadNetwork network, final String name, final Node from, final Node to,
143 final LaneType laneType, final int laneCount, final OtsSimulatorInterface simulator) throws Exception
144 {
145 Length width = new Length(laneCount * 4.0, METER);
146 final CrossSectionLink link = makeLink(network, name, from, to, width, simulator);
147 Lane[] result = new Lane[laneCount];
148 width = new Length(4.0, METER);
149 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
150 {
151
152 Length latPos = new Length((-0.5 - laneIndex) * width.getSI(), METER);
153 result[laneIndex] = makeLane(link, "lane." + laneIndex, laneType, latPos, width);
154 }
155 return result;
156 }
157
158
159
160
161
162 @Test
163 public final void changeRight() throws Exception
164 {
165 GtuType gtuType = DefaultsNl.CAR;
166 LaneType laneType = DefaultsRoadNl.TWO_WAY_LANE;
167 int laneCount = 2;
168 this.simulator.initialize(Time.ZERO, Duration.ZERO, new Duration(3600.0, DurationUnit.SECOND), this);
169 Lane[] lanes = makeMultiLane(this.network, "Road with two lanes",
170 new Node(this.network, "From", new OtsPoint3d(0, 0, 0), Direction.ZERO),
171 new Node(this.network, "To", new OtsPoint3d(200, 0, 0), Direction.ZERO), laneType, laneCount, this.simulator);
172
173
174
175 assertEquals("Leftmost lane should not have accessible adjacent lanes on the LEFT side", 0,
176 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType).size());
177 assertEquals("Leftmost lane should have one accessible adjacent lane on the RIGHT side", 1,
178 lanes[0].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType).size());
179 assertEquals("Rightmost lane should have one accessible adjacent lane on the LEFT side", 1,
180 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.LEFT, gtuType).size());
181 assertEquals("Rightmost lane should not have accessible adjacent lanes on the RIGHT side", 0,
182 lanes[1].accessibleAdjacentLanesLegal(LateralDirectionality.RIGHT, gtuType).size());
183
184 Set<LanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
185 initialLongitudinalPositions.add(new LanePosition(lanes[1], new Length(100, METER)));
186 AbstractLaneChangeModel laneChangeModel = new Egoistic();
187 ParameterSet parameters = DefaultTestParameters.create();
188
189
190
191 LaneBasedGtu car = new LaneBasedGtu("ReferenceCar", gtuType, new Length(4, METER), new Length(2, METER),
192 new Speed(150, KM_PER_HOUR), Length.instantiateSI(2.0), this.network);
193 LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
194 new LaneBasedCfLcTacticalPlanner(new IdmPlusOld(), laneChangeModel, car), car);
195 car.setParameters(parameters);
196 car.init(strategicalPlanner, initialLongitudinalPositions, new Speed(100, KM_PER_HOUR));
197 car.getTacticalPlanner().getPerception().perceive();
198 Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
199 sameLaneGTUs.add(new HeadwayGtuSimple(car.getId(), car.getType(), Length.ZERO, Length.ZERO, car.getLength(),
200 car.getSpeed(), car.getAcceleration(), null));
201 Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
202 Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
203 LaneMovementStep laneChangeModelResult = laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs,
204 preferredLaneGTUs, nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
205 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
206
207 assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
208 laneChangeModelResult.getLaneChangeDirection());
209 Length rear = car.position(lanes[0], car.getRear());
210 Length front = car.position(lanes[0], car.getFront());
211 Length reference = car.position(lanes[0], RelativePosition.REFERENCE_POSITION);
212
213
214
215 Length vehicleLength = front.minus(rear);
216 Length collisionStart = reference.minus(vehicleLength);
217 Length collisionEnd = reference.plus(vehicleLength);
218 for (double pos = collisionStart.getSI() + 0.01; pos < collisionEnd.getSI() - 0.01; pos += 0.1)
219 {
220 Set<LanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
221 otherLongitudinalPositions.add(new LanePosition(lanes[1], new Length(pos, METER)));
222
223 parameters = DefaultTestParameters.create();
224
225
226
227
228
229
230
231
232
233
234
235 LaneBasedGtu collisionCar = new LaneBasedGtu("LaneChangeBlockingCarAt" + pos, gtuType, vehicleLength,
236 new Length(2, METER), new Speed(150, KM_PER_HOUR), vehicleLength.times(0.5), this.network);
237 strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
238 new LaneBasedCfLcTacticalPlanner(new IdmPlusOld(), laneChangeModel, collisionCar), collisionCar);
239 collisionCar.setParameters(parameters);
240 collisionCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
241 preferredLaneGTUs.clear();
242 HeadwayGtuSimple collisionHWGTU = new HeadwayGtuSimple(collisionCar.getId(), collisionCar.getType(),
243 new Length(pos - reference.getSI(), LengthUnit.SI), collisionCar.getLength(), collisionCar.getWidth(),
244 collisionCar.getSpeed(), collisionCar.getAcceleration(), null);
245 preferredLaneGTUs.add(collisionHWGTU);
246 laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
247 nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
248 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
249
250 assertEquals("Vehicle cannot to change to the right lane because that would result in an immediate collision", null,
251 laneChangeModelResult.getLaneChangeDirection());
252 }
253 for (double pos = 0; pos < 180; pos += 5)
254 {
255 Set<LanePosition> otherLongitudinalPositions = new LinkedHashSet<>(1);
256 otherLongitudinalPositions.add(new LanePosition(lanes[1], new Length(pos, METER)));
257
258 parameters = new ParameterSet();
259 parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
260 parameters.setParameter(ParameterTypes.B, new Acceleration(1.5, METER_PER_SECOND_2));
261 parameters.setParameter(ParameterTypes.S0, new Length(2, METER));
262 parameters.setParameter(ParameterTypes.T, new Duration(1, SECOND));
263 parameters.setParameter(ParameterTypes.A, new Acceleration(1, METER_PER_SECOND_2));
264 parameters.setDefaultParameter(ParameterTypes.LOOKAHEAD);
265 parameters.setDefaultParameter(ParameterTypes.LOOKBACKOLD);
266 parameters.setParameter(AbstractIdm.DELTA, 1d);
267
268
269
270
271 LaneBasedGtu otherCar = new LaneBasedGtu("OtherCarAt" + pos, gtuType, vehicleLength, new Length(2, METER),
272 new Speed(150, KM_PER_HOUR), vehicleLength.times(0.5), this.network);
273 strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
274 new LaneBasedCfLcTacticalPlanner(new IdmPlusOld(), laneChangeModel, otherCar), otherCar);
275 otherCar.setParameters(parameters);
276 otherCar.init(strategicalPlanner, otherLongitudinalPositions, new Speed(100, KM_PER_HOUR));
277 preferredLaneGTUs.clear();
278 HeadwayGtuSimple collisionHWGTU = new HeadwayGtuSimple(otherCar.getId(), otherCar.getType(),
279 new Length(pos - car.position(lanes[0], car.getReference()).getSI(), LengthUnit.SI), otherCar.getLength(),
280 otherCar.getWidth(), otherCar.getSpeed(), otherCar.getAcceleration(), null);
281 preferredLaneGTUs.add(collisionHWGTU);
282 laneChangeModelResult = new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
283 nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
284 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
285
286 laneChangeModelResult = new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
287 nonPreferredLaneGTUs, new Speed(100, KM_PER_HOUR), new Acceleration(0.3, METER_PER_SECOND_2),
288 new Acceleration(0.1, METER_PER_SECOND_2), new Acceleration(-0.3, METER_PER_SECOND_2));
289
290
291
292
293 }
294 }
295
296
297
298
299
300
301 @Override
302 public void constructModel() throws SimRuntimeException
303 {
304
305 }
306
307
308 @Override
309 public final RoadNetwork getNetwork()
310 {
311 return this.network;
312 }
313
314 }