1 package org.opentrafficsim.core.gtu.lane.changing;
2
3 import static org.junit.Assert.assertEquals;
4
5 import java.rmi.RemoteException;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.Map;
10
11 import javax.naming.NamingException;
12
13 import nl.tudelft.simulation.dsol.SimRuntimeException;
14 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
15
16 import org.junit.Test;
17 import org.opentrafficsim.core.car.LaneBasedIndividualCar;
18 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
19 import org.opentrafficsim.core.dsol.OTSModelInterface;
20 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
21 import org.opentrafficsim.core.gtu.GTUException;
22 import org.opentrafficsim.core.gtu.GTUType;
23 import org.opentrafficsim.core.gtu.RelativePosition;
24 import org.opentrafficsim.core.gtu.following.HeadwayGTU;
25 import org.opentrafficsim.core.gtu.following.IDMPlus;
26 import org.opentrafficsim.core.network.LateralDirectionality;
27 import org.opentrafficsim.core.network.LongitudinalDirectionality;
28 import org.opentrafficsim.core.network.NetworkException;
29 import org.opentrafficsim.core.network.geotools.LinearGeometry;
30 import org.opentrafficsim.core.network.geotools.NodeGeotools;
31 import org.opentrafficsim.core.network.lane.CrossSectionLink;
32 import org.opentrafficsim.core.network.lane.Lane;
33 import org.opentrafficsim.core.network.lane.LaneType;
34 import org.opentrafficsim.core.unit.AccelerationUnit;
35 import org.opentrafficsim.core.unit.FrequencyUnit;
36 import org.opentrafficsim.core.unit.LengthUnit;
37 import org.opentrafficsim.core.unit.SpeedUnit;
38 import org.opentrafficsim.core.unit.TimeUnit;
39 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
40 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
41 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
42 import org.opentrafficsim.simulationengine.SimpleSimulator;
43
44 import com.vividsolutions.jts.geom.Coordinate;
45 import com.vividsolutions.jts.geom.GeometryFactory;
46 import com.vividsolutions.jts.geom.LineString;
47
48
49
50
51
52
53
54
55
56
57
58 public class LaneChangeModelTest implements OTSModelInterface
59 {
60
61 private static final long serialVersionUID = 20150313;
62
63
64
65
66
67
68
69
70
71 private static CrossSectionLink<String, String> makeLink(final String name, final NodeGeotools.STR from,
72 final NodeGeotools.STR to, final DoubleScalar.Rel<LengthUnit> width)
73 {
74
75
76 CrossSectionLink<String, String> link =
77 new CrossSectionLink<String, String>(name, from, to, new DoubleScalar.Rel<LengthUnit>(from.getPoint()
78 .distance(to.getPoint()), LengthUnit.METER));
79 GeometryFactory factory = new GeometryFactory();
80 Coordinate[] coordinates =
81 new Coordinate[]{new Coordinate(from.getPoint().x, from.getPoint().y, 0),
82 new Coordinate(to.getPoint().x, to.getPoint().y, 0)};
83 LineString line = factory.createLineString(coordinates);
84 try
85 {
86 new LinearGeometry(link, line, null);
87 }
88 catch (NetworkException exception)
89 {
90 throw new Error("This network is probably too simple for this to happen...");
91 }
92 return link;
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107 private static Lane makeLane(final CrossSectionLink<String, String> link, final LaneType<String> laneType,
108 final DoubleScalar.Rel<LengthUnit> latPos, final DoubleScalar.Rel<LengthUnit> width)
109 throws RemoteException, NamingException, NetworkException
110 {
111 DoubleScalar.Abs<FrequencyUnit> f2000 = new DoubleScalar.Abs<FrequencyUnit>(2000.0, FrequencyUnit.PER_HOUR);
112 Lane result = new Lane(link, latPos, latPos, width, width, laneType, LongitudinalDirectionality.FORWARD, f2000);
113 return result;
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127
128 public static Lane[] makeMultiLane(final String name, final NodeGeotools.STR from, final NodeGeotools.STR to,
129 final LaneType<String> laneType, final int laneCount) throws RemoteException, NamingException,
130 NetworkException
131 {
132 DoubleScalar.Rel<LengthUnit> width = new DoubleScalar.Rel<LengthUnit>(laneCount * 4.0, LengthUnit.METER);
133 final CrossSectionLink<String, String> link = makeLink(name, from, to, width);
134 Lane[] result = new Lane[laneCount];
135 width = new DoubleScalar.Rel<LengthUnit>(4.0, LengthUnit.METER);
136 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
137 {
138 DoubleScalar.Rel<LengthUnit> latPos =
139 new DoubleScalar.Rel<LengthUnit>((-0.5 - laneIndex) * width.getSI(), LengthUnit.METER);
140 result[laneIndex] = makeLane(link, laneType, latPos, width);
141 }
142 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
143 {
144 if (laneIndex < laneCount - 1)
145 {
146 result[laneIndex].addAccessibleAdjacentLane(result[laneIndex + 1], LateralDirectionality.RIGHT);
147 }
148 if (laneIndex > 0)
149 {
150 result[laneIndex].addAccessibleAdjacentLane(result[laneIndex - 1], LateralDirectionality.LEFT);
151 }
152 }
153 return result;
154 }
155
156
157
158
159
160
161
162
163
164 @Test
165 public final void changeRight() throws RemoteException, NamingException, SimRuntimeException, NetworkException,
166 GTUException
167 {
168 GTUType<String> gtuType = new GTUType<String>("car");
169 LaneType<String> laneType = new LaneType<String>("CarLane");
170 laneType.addPermeability(gtuType);
171 Lane[] lanes =
172 makeMultiLane("Road with two lanes", new NodeGeotools.STR("From", new Coordinate(0, 0, 0)),
173 new NodeGeotools.STR("To", new Coordinate(200, 0, 0)), laneType, 2);
174 Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions =
175 new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
176 initialLongitudinalPositions.put(lanes[0], new DoubleScalar.Rel<LengthUnit>(100, LengthUnit.METER));
177 SimpleSimulator simpleSimulator =
178 new SimpleSimulator(new DoubleScalar.Abs<TimeUnit>(0, TimeUnit.SECOND),
179 new DoubleScalar.Rel<TimeUnit>(0, TimeUnit.SECOND), new DoubleScalar.Rel<TimeUnit>(3600,
180 TimeUnit.SECOND), this );
181 AbstractLaneChangeModel laneChangeModel = new Egoistic();
182 LaneBasedIndividualCar<String> car =
183 new LaneBasedIndividualCar<String>("ReferenceCar", gtuType, new IDMPlus(
184 new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
185 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
186 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
187 TimeUnit.SECOND), 1d), laneChangeModel, initialLongitudinalPositions,
188 new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), new DoubleScalar.Rel<LengthUnit>(
189 4, LengthUnit.METER), new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER),
190 new DoubleScalar.Abs<SpeedUnit>(150, SpeedUnit.KM_PER_HOUR),
191 (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
192 Collection<HeadwayGTU> sameLaneGTUs = new HashSet<HeadwayGTU>();
193 sameLaneGTUs.add(new HeadwayGTU(car, 0));
194 Collection<HeadwayGTU> preferredLaneGTUs = new HashSet<HeadwayGTU>();
195 Collection<HeadwayGTU> nonPreferredLaneGTUs = new HashSet<HeadwayGTU>();
196 LaneMovementStep laneChangeModelResult =
197 laneChangeModel.computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
198 nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
199 new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
200 new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
201 new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
202
203 assertEquals("Vehicle want to change to the right lane", LateralDirectionality.RIGHT,
204 laneChangeModelResult.getLaneChange());
205 DoubleScalar.Rel<LengthUnit> rear = car.position(lanes[0], car.getRear());
206 DoubleScalar.Rel<LengthUnit> front = car.position(lanes[0], car.getFront());
207 DoubleScalar.Rel<LengthUnit> reference = car.position(lanes[0], RelativePosition.REFERENCE_POSITION);
208
209
210
211 DoubleScalar.Rel<LengthUnit> vehicleLength = DoubleScalar.minus(front, rear).immutable();
212 DoubleScalar.Rel<LengthUnit> collisionStart = DoubleScalar.minus(reference, vehicleLength).immutable();
213 DoubleScalar.Rel<LengthUnit> collisionEnd = DoubleScalar.plus(reference, vehicleLength).immutable();
214 for (double pos = collisionStart.getSI() + 0.01; pos < collisionEnd.getSI() - 0.01; pos += 0.1)
215 {
216 Map<Lane, DoubleScalar.Rel<LengthUnit>> otherLongitudinalPositions =
217 new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
218 otherLongitudinalPositions.put(lanes[1], new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
219 LaneBasedIndividualCar<String> collisionCar =
220 new LaneBasedIndividualCar<String>("LaneChangeBlockingCar", gtuType, new IDMPlus(
221 new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
222 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
223 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
224 TimeUnit.SECOND), 1d), laneChangeModel, otherLongitudinalPositions,
225 new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), vehicleLength,
226 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(150,
227 SpeedUnit.KM_PER_HOUR), (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
228 preferredLaneGTUs.clear();
229 HeadwayGTU collisionHWGTU = new HeadwayGTU(collisionCar, pos - reference.getSI());
230 preferredLaneGTUs.add(collisionHWGTU);
231 laneChangeModelResult =
232 new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
233 nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
234 new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
235 new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
236 new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
237
238 assertEquals(
239 "Vehicle cannot to change to the right lane because that would result in an immediate collision",
240 null, laneChangeModelResult.getLaneChange());
241 }
242 for (double pos = 0; pos < 200; pos += 5)
243 {
244 Map<Lane, DoubleScalar.Rel<LengthUnit>> otherLongitudinalPositions =
245 new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
246 otherLongitudinalPositions.put(lanes[1], new DoubleScalar.Rel<LengthUnit>(pos, LengthUnit.METER));
247 LaneBasedIndividualCar<String> otherCar =
248 new LaneBasedIndividualCar<String>("OtherCar", gtuType, new IDMPlus(
249 new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
250 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
251 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
252 TimeUnit.SECOND), 1d), laneChangeModel, otherLongitudinalPositions,
253 new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR), vehicleLength,
254 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(150,
255 SpeedUnit.KM_PER_HOUR), (OTSDEVSSimulatorInterface) simpleSimulator.getSimulator());
256 preferredLaneGTUs.clear();
257 HeadwayGTU collisionHWGTU =
258 new HeadwayGTU(otherCar, pos - car.position(lanes[0], car.getReference()).getSI());
259 preferredLaneGTUs.add(collisionHWGTU);
260 laneChangeModelResult =
261 new Egoistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
262 nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
263 new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
264 new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
265 new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
266
267 laneChangeModelResult =
268 new Altruistic().computeLaneChangeAndAcceleration(car, sameLaneGTUs, preferredLaneGTUs,
269 nonPreferredLaneGTUs, new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR),
270 new DoubleScalar.Rel<AccelerationUnit>(0.3, AccelerationUnit.METER_PER_SECOND_2),
271 new DoubleScalar.Rel<AccelerationUnit>(0.1, AccelerationUnit.METER_PER_SECOND_2),
272 new DoubleScalar.Rel<AccelerationUnit>(-0.3, AccelerationUnit.METER_PER_SECOND_2));
273
274
275
276
277 }
278 }
279
280
281
282
283
284 @Override
285 public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
286 throws SimRuntimeException, RemoteException
287 {
288 }
289
290
291 @Override
292 public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
293 {
294 return null;
295 }
296
297 }