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