package org.opentrafficsim.road.gtu.following;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
import org.djunits.unit.TimeUnit;
import org.djunits.unit.UNITS;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.DoubleScalar;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.Time;
import org.junit.Test;
import org.opentrafficsim.core.dsol.OTSModelInterface;
import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
import org.opentrafficsim.core.gtu.GTUDirectionality;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.network.OTSNetwork;
import org.opentrafficsim.road.car.CarTest;
import org.opentrafficsim.road.car.LaneBasedIndividualCar;
import org.opentrafficsim.road.gtu.lane.driver.LaneBasedDrivingCharacteristics;
import org.opentrafficsim.road.gtu.lane.perception.LanePerceptionFull;
import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedGTUFollowingTacticalPlanner;
import org.opentrafficsim.road.gtu.lane.tactical.following.AccelerationStep;
import org.opentrafficsim.road.gtu.lane.tactical.following.FixedAccelerationModel;
import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModel;
import org.opentrafficsim.road.gtu.lane.tactical.following.HeadwayGTU;
import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlus;
import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
import org.opentrafficsim.road.network.lane.DirectedLanePosition;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.LaneType;
import org.opentrafficsim.simulationengine.SimpleSimulator;
/**
*
* Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
* initial version Jul 11, 2014
* @author Peter Knoppers
*/
public class IDMPlusTest implements UNITS
{
/** network. */
private OTSNetwork network = new OTSNetwork("network");
/**
* Test IDMPlus.
* @throws Exception when something goes wrong (should not happen)
*/
@SuppressWarnings({"static-method", "checkstyle:methodlength"})
@Test
public final void computeAcceleration() throws Exception
{
// Test 1. Check a car standing still with no leaders accelerates with maximum acceleration
// cars have #10 and up
SimpleSimulator simulator =
new SimpleSimulator(new Time.Abs(0, SECOND), new Time.Rel(0, SECOND), new Time.Rel(3600, SECOND),
new IDMPlusTestModel());
Length.Rel s0 = new Length.Rel(2, METER);
GTUFollowingModel carFollowingModel =
new IDMPlus(new Acceleration(1.25, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2), s0,
new Time.Rel(1, SECOND), 1d);
GTUType gtuType = GTUType.makeGTUType("Car");
LaneType laneType = new LaneType("CarLane");
laneType.addCompatibility(gtuType);
Lane lane = CarTest.makeLane(laneType);
Time.Abs initialTime = new Time.Abs(0, SECOND);
Length.Rel initialPosition = new Length.Rel(123.456, METER);
Speed initialSpeed = new Speed(0, KM_PER_HOUR);
Length.Rel length = new Length.Rel(5.0, METER);
Length.Rel width = new Length.Rel(2.0, METER);
Set initialLongitudinalPositions = new LinkedHashSet<>(1);
initialLongitudinalPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
Speed maxSpeed = new Speed(120, KM_PER_HOUR);
AbstractLaneChangeModel laneChangeModel = new Egoistic();
LaneBasedDrivingCharacteristics drivingCharacteristicsIDM =
new LaneBasedDrivingCharacteristics(carFollowingModel, laneChangeModel);
LaneBasedStrategicalPlanner strategicalPlannerIDM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsIDM, new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar referenceCar10 =
new LaneBasedIndividualCar("10", gtuType, initialLongitudinalPositions, initialSpeed, length, width,
maxSpeed, simulator, strategicalPlannerIDM, new LanePerceptionFull(), this.network);
referenceCar10.getPerception().perceive();
Speed speedLimit = new Speed(100, KM_PER_HOUR);
AccelerationStep cfmr =
carFollowingModel.computeAccelerationStepWithNoLeader(referenceCar10, referenceCar10
.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit);
assertEquals("Standard time slice in IDM+ is 0.5s", 0.5, cfmr.getValidUntil().getSI(), 0.0001);
assertEquals("Acceleration should be maximum", 1.25, cfmr.getAcceleration().getSI(), 0.0001);
// Create another car at exactly the stationary following distance
// Check that the follower remains stationary
Length.Rel leaderPosition =
new Length.Rel(2 + referenceCar10.getLength().getSI()
+ referenceCar10.position(lane, referenceCar10.getReference(), initialTime).getSI(), METER);
Set leaderPositions = new LinkedHashSet<>(1);
leaderPositions.add(new DirectedLanePosition(lane, leaderPosition, GTUDirectionality.DIR_PLUS));
// The leader gets a car following model that makes it stay in place for a loooong time
FixedAccelerationModel fam =
new FixedAccelerationModel(new Acceleration(0, METER_PER_SECOND_2), new Time.Rel(9999, SECOND));
LaneBasedDrivingCharacteristics drivingCharacteristicsFAM =
new LaneBasedDrivingCharacteristics(fam, laneChangeModel);
LaneBasedStrategicalPlanner strategicalPlannerFAM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsFAM, new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar leaderCar11 =
new LaneBasedIndividualCar("11", gtuType, leaderPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
leaderCar11.getPerception().perceive();
HeadwayGTU leader =
new HeadwayGTU(leaderCar11.getId(), leaderCar11.getVelocity(), leaderPosition.getSI()
- referenceCar10.getLength().getSI() - initialPosition.getSI(), leaderCar11.getGTUType());
cfmr =
carFollowingModel.computeAccelerationStep(referenceCar10, leaderCar11.getVelocity(), leader.getDistance(),
leaderCar11.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit);
assertEquals("Acceleration should be 0", 0, cfmr.getAcceleration().getSI(), 0.0001);
leaderPosition =
new Length.Rel(1000 + (3 + referenceCar10.getLength().getSI() + referenceCar10.position(lane,
referenceCar10.getFront(), initialTime).getSI()), METER);
leaderPositions = new LinkedHashSet<>(1);
leaderPositions.add(new DirectedLanePosition(lane, leaderPosition, GTUDirectionality.DIR_PLUS));
// Exercise the if statement that ignores leaders that are further ahead
drivingCharacteristicsFAM = new LaneBasedDrivingCharacteristics(fam, laneChangeModel);
strategicalPlannerFAM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsFAM, new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar leaderCar12 =
new LaneBasedIndividualCar("12", gtuType, leaderPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
leaderCar12.getPerception().perceive();
// Verify that the result is independent of the order of adding in the Collection
Collection leaders = new ArrayList();
HeadwayGTU leader2 =
new HeadwayGTU(leaderCar12.getId(), leaderCar12.getVelocity(), leaderPosition.getSI()
- referenceCar10.getLength().getSI() - initialPosition.getSI(), leaderCar12.getGTUType());
leaders.add(leader2); // Put the 2nd leader in first place
leaders.add(leader);
cfmr =
carFollowingModel.computeDualAccelerationStep(referenceCar10, leaders,
referenceCar10.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit)
.getLeaderAccelerationStep();
assertEquals("Acceleration should be 0", 0, cfmr.getAcceleration().getSI(), 0.0001);
leaders.clear();
leaders.add(leader); // Put the 1st leader in first place
leaders.add(leader2);
cfmr =
carFollowingModel.computeDualAccelerationStep(referenceCar10, leaders,
referenceCar10.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit)
.getLeaderAccelerationStep();
assertEquals("Acceleration should be 0", 0, cfmr.getAcceleration().getSI(), 0.0001);
referenceCar10.destroy();
leaderCar11.destroy();
leaderCar12.destroy();
// Test 2, cars have #20 and up
LaneBasedIndividualCar referenceCar20 =
new LaneBasedIndividualCar("20", gtuType, initialLongitudinalPositions, initialSpeed, length, width,
maxSpeed, simulator, strategicalPlannerIDM, new LanePerceptionFull(), this.network);
leaders.clear();
leaderPosition =
new Length.Rel(-(3 + referenceCar20.getLength().getSI())
+ referenceCar20.position(lane, referenceCar20.getFront(), initialTime).getSI(), METER);
leaderPositions = new LinkedHashSet<>(1);
leaderPositions.add(new DirectedLanePosition(lane, leaderPosition, GTUDirectionality.DIR_PLUS));
LaneBasedIndividualCar leaderCar21 =
new LaneBasedIndividualCar("21", gtuType, leaderPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
referenceCar20.getPerception().perceive();
leaderCar21.getPerception().perceive();
leader =
new HeadwayGTU(leaderCar21.getId(), leaderCar21.getVelocity(), leaderPosition.getSI()
- referenceCar20.getLength().getSI() - initialPosition.getSI(), leaderCar21.getGTUType());
leaders.add(leader);
cfmr =
carFollowingModel.computeDualAccelerationStep(referenceCar20, leaders,
referenceCar20.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit)
.getLeaderAccelerationStep();
assertEquals("Leader acceleration should be 1.25", 1.25, cfmr.getAcceleration().getSI(), 0.0001);
leaderCar21.destroy();
// Check that the returned acceleration increases with the distance to the leader
double referenceAcceleration = -1;
for (int spareDistance = 0; spareDistance <= 500; spareDistance++)
{
leaders.clear();
leaderPosition =
new Length.Rel(spareDistance
+ (3 + referenceCar20.getLength().getSI() + referenceCar20.position(lane,
referenceCar20.getFront(), initialTime).getSI()), METER);
leaderPositions = new LinkedHashSet<>(1);
leaderPositions.add(new DirectedLanePosition(lane, leaderPosition, GTUDirectionality.DIR_PLUS));
LaneBasedIndividualCar leaderCar22 =
new LaneBasedIndividualCar("0", gtuType, leaderPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
leaderCar22.getPerception().perceive();
leader =
new HeadwayGTU(leaderCar22.getId(), leaderCar22.getVelocity(), leaderPosition.getSI()
- referenceCar20.getLength().getSI() - initialPosition.getSI(), leaderCar22.getGTUType());
leaders.add(leader);
cfmr =
carFollowingModel.computeDualAccelerationStep(referenceCar20, leaders,
referenceCar20.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit)
.getFollowerAccelerationStep();
double acceleration = cfmr.getAcceleration().getSI();
// System.out.println("Acceleration with stationary leader at " + spareDistance + " is " + acceleration);
assertTrue("acceleration should not decrease when distance to leader is increased",
acceleration >= referenceAcceleration);
referenceAcceleration = acceleration;
leaderCar22.destroy();
}
assertTrue("Highest acceleration should be less than max", referenceAcceleration <= 1.25);
referenceCar20.destroy();
// Test 3. Check that the returned acceleration increases with the speed of the leader
// cars have #30 and up
LaneBasedIndividualCar referenceCar30 =
new LaneBasedIndividualCar("30", gtuType, initialLongitudinalPositions, initialSpeed, length, width,
maxSpeed, simulator, strategicalPlannerIDM, new LanePerceptionFull(), this.network);
referenceCar30.getPerception().perceive();
referenceAcceleration = Double.NEGATIVE_INFINITY;
leaderPosition =
new Length.Rel(2 + 3 + referenceCar30.getLength().getSI()
+ referenceCar30.position(lane, referenceCar30.getFront(), initialTime).getSI(), METER);
leaderPositions = new LinkedHashSet<>(1);
leaderPositions.add(new DirectedLanePosition(lane, leaderPosition, GTUDirectionality.DIR_PLUS));
// In IDM+ the reference car must have non-zero speed for the leader speed to have any effect
initialSpeed = new Speed(2, METER_PER_SECOND);
for (int integerLeaderSpeed = 0; integerLeaderSpeed <= 40; integerLeaderSpeed++)
{
Set initialPositions = new LinkedHashSet<>(1);
initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
referenceCar30.destroy();
drivingCharacteristicsIDM = new LaneBasedDrivingCharacteristics(carFollowingModel, laneChangeModel);
strategicalPlannerIDM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsIDM,
new LaneBasedGTUFollowingTacticalPlanner());
referenceCar30 =
new LaneBasedIndividualCar("30", gtuType, initialPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerIDM, new LanePerceptionFull(), this.network);
leaders.clear();
Speed leaderSpeed = new Speed(integerLeaderSpeed, METER_PER_SECOND);
drivingCharacteristicsFAM = new LaneBasedDrivingCharacteristics(fam, laneChangeModel);
strategicalPlannerFAM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsFAM,
new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar leaderCar31 =
new LaneBasedIndividualCar("31", gtuType, leaderPositions, leaderSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
leaderCar31.getPerception().perceive();
leader =
new HeadwayGTU(leaderCar31.getId(), leaderCar31.getVelocity(), leaderPosition.getSI()
- referenceCar30.getLength().getSI() - initialPosition.getSI(), leaderCar31.getGTUType());
leaders.add(leader);
// System.out.println("referenceCar: " + referenceCar);
// System.out.println("leaderCar : " + leaderCar);
cfmr =
carFollowingModel.computeDualAccelerationStep(referenceCar30, leaders,
referenceCar30.getDrivingCharacteristics().getForwardHeadwayDistance(), speedLimit)
.getFollowerAccelerationStep();
double acceleration = cfmr.getAcceleration().getSI();
// System.out.println("Acceleration with leader driving " + integerLeaderSpeed + " m/s is " + acceleration);
assertTrue("acceleration should not decrease when leader speed is increased",
acceleration >= referenceAcceleration);
referenceAcceleration = acceleration;
leaderCar31.destroy();
}
assertTrue("Highest acceleration should be less than max", referenceAcceleration <= 1.25);
referenceCar30.destroy();
// Test 4. Check that a car that is 100m behind a stationary car accelerates, then decelerates and stops at
// the right point. (In IDM+ the car oscillates a while around the final position with pretty good damping.)
// Cars have #40 and up
initialPosition = new Length.Rel(100, METER);
Set initialPositions = new LinkedHashSet<>(1);
initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
initialSpeed = new Speed(0, METER_PER_SECOND);
drivingCharacteristicsIDM = new LaneBasedDrivingCharacteristics(carFollowingModel, laneChangeModel);
strategicalPlannerIDM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsIDM, new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar referenceCar40 =
new LaneBasedIndividualCar("40", gtuType, initialPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerIDM, new LanePerceptionFull(), this.network);
referenceCar40.getPerception().perceive();
leaderPosition =
new Length.Rel(100 + 3 + referenceCar40.getLength().getSI()
+ referenceCar40.position(lane, referenceCar40.getFront(), initialTime).getSI(), METER);
drivingCharacteristicsFAM = new LaneBasedDrivingCharacteristics(fam, laneChangeModel);
strategicalPlannerFAM =
new LaneBasedStrategicalRoutePlanner(drivingCharacteristicsFAM, new LaneBasedGTUFollowingTacticalPlanner());
LaneBasedIndividualCar leaderCar41 =
new LaneBasedIndividualCar("41", gtuType, leaderPositions, initialSpeed, length, width, maxSpeed,
simulator, strategicalPlannerFAM, new LanePerceptionFull(), this.network);
leaderCar41.getPerception().perceive();
for (int timeStep = 0; timeStep < 200; timeStep++)
{
Time.Abs simulateUntil = new Time.Abs(0.1 * timeStep, TimeUnit.SI);
simulator.runUpTo(simulateUntil);
while (simulator.isRunning())
{
try
{
Thread.sleep(1);
System.out.println(referenceCar40 + ", t= " + simulator.getSimulatorTime().get() + ", pos="
+ referenceCar40.getLocation());
}
catch (InterruptedException ie)
{
ie = null; // ignore
}
}
// System.out.println(String.format("step %3d, t=%s, referenceCar: %s, speed %s, leaderCar: %s", timeStep,
// simulateUntil, referenceCar, referenceCar.getVelocity(), leaderCar));
if (timeStep > 120)
{
double position = referenceCar40.position(lane, referenceCar40.getFront()).getSI();
assertEquals("After 20 seconds the referenceCar should now be very close to " + s0
+ " before the rear of the leader", leaderCar41.position(lane, referenceCar40.getRear()).getSI()
- s0.getSI(), position, 0.2);
assertEquals("After 20 seconds the speed of the referenceCar should be almost 0", 0, referenceCar40
.getVelocity().getSI(), 0.2);
}
}
referenceCar40.destroy();
leaderCar41.destroy();
}
}
/**
* Simulation model for IDMPlusTest.
*
* Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
* initial version 0 feb. 2015
* @author Peter Knoppers
*/
class IDMPlusTestModel implements OTSModelInterface
{
/** */
private static final long serialVersionUID = 20150210L;
/** {@inheritDoc} */
@Override
public void constructModel(
final SimulatorInterface, DoubleScalar.Rel, OTSSimTimeDouble> simulator)
throws SimRuntimeException
{
// do nothing.
}
/** {@inheritDoc} */
@Override
public SimulatorInterface, DoubleScalar.Rel, OTSSimTimeDouble> getSimulator()
{
return null;
}
}