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