IncentiveSpeedWithCourtesy.java
package org.opentrafficsim.road.gtu.tactical.lmrs;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.Speed;
import org.djutils.immutablecollections.ImmutableLinkedHashMap;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.base.parameters.ParameterTypeAcceleration;
import org.opentrafficsim.base.parameters.ParameterTypeSpeed;
import org.opentrafficsim.base.parameters.ParameterTypes;
import org.opentrafficsim.core.gtu.Stateless;
import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
import org.opentrafficsim.core.network.LateralDirectionality;
import org.opentrafficsim.road.gtu.perception.RelativeLane;
import org.opentrafficsim.road.gtu.perception.categories.InfrastructurePerception;
import org.opentrafficsim.road.gtu.perception.categories.TrafficPerception;
import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
import org.opentrafficsim.road.gtu.tactical.util.lmrs.Desire;
import org.opentrafficsim.road.gtu.tactical.util.lmrs.LmrsParameters;
import org.opentrafficsim.road.gtu.tactical.util.lmrs.VoluntaryIncentive;
/**
* Determines lane change desire for speed. The anticipation speed in the current and adjacent lanes are compared. The larger
* the difference, the larger the lane change desire. For negative differences, negative desire results. Anticipation speed
* involves the most critical vehicle considered to be in a lane. Vehicles are more critical if their speed is lower, and if
* they are closer. The set of vehicles considered to be on a lane includes drivers on adjacent lanes of the considered lane,
* with a lane change desire towards the considered lane above a certain threshold. If such vehicles have low speeds (i.e.
* vehicle accelerating to merge), this may result in a courtesy lane change, or in not changing lane out of courtesy from the
* 2nd lane of the mainline. Vehicles on the current lane of the driver, are not considered on adjacent lanes. This would
* maintain a large speed difference between the lanes where all drivers do not change lane as they consider leading vehicles to
* be on the adjacent lane, lowering the anticipation speed on the adjacent lane. The desire for speed is reduced as
* acceleration is larger, preventing over-assertive lane changes as acceleration out of congestion in the adjacent lane has
* progressed more.<br>
* <br>
* <b>Note:</b> This incentive includes speed, and a form of courtesy. It should therefore not be combined with incentives
* solely for speed, or solely for courtesy.
* <p>
* Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author Wouter Schakel
*/
public final class IncentiveSpeedWithCourtesy implements VoluntaryIncentive, Stateless<IncentiveSpeedWithCourtesy>
{
/** Acceleration parameter type. */
protected static final ParameterTypeAcceleration A = ParameterTypes.A;
/** Anticipation speed difference parameter type. */
protected static final ParameterTypeSpeed VGAIN = LmrsParameters.VGAIN;
/** Speed threshold below which traffic is considered congested. */
protected static final ParameterTypeSpeed VCONG = ParameterTypes.VCONG;
/** Singleton instance. */
public static final IncentiveSpeedWithCourtesy SINGLETON = new IncentiveSpeedWithCourtesy();
@Override
public IncentiveSpeedWithCourtesy get()
{
return SINGLETON;
}
/**
* Constructor.
*/
private IncentiveSpeedWithCourtesy()
{
//
}
@Override
public Desire determineDesire(final TacticalContextEgo context, final Desire mandatoryDesire,
final ImmutableLinkedHashMap<Class<? extends VoluntaryIncentive>, Desire> voluntaryDesire)
throws ParameterException, OperationalPlanException
{
// zero if no lane change is possible
InfrastructurePerception infra = context.getPerception().getPerceptionCategory(InfrastructurePerception.class);
TrafficPerception traffic = context.getPerception().getPerceptionCategory(TrafficPerception.class);
double leftDist = infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.LEFT).si;
double rightDist = infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, LateralDirectionality.RIGHT).si;
// gather some info
Speed vCur = traffic.getSpeed(RelativeLane.CURRENT, context.getDesiredSpeed());
Speed vGain = context.getParameters().getParameter(VGAIN);
// calculate aGain (default 1; lower as acceleration is higher than 0)
double aGain;
Acceleration aCur = context.getCarFollowingAcceleration();
if (aCur.si > 0)
{
Acceleration a = context.getParameters().getParameter(A);
aGain = (a.si - aCur.si) / a.si;
}
else
{
aGain = 1.0;
}
// left desire
double dLeft;
if (leftDist > 0.0 && infra.getCrossSection().contains(RelativeLane.LEFT))
{
Speed vLeft = traffic.getSpeed(RelativeLane.LEFT, context.getDesiredSpeed());
dLeft = aGain * (vLeft.si - vCur.si) / vGain.si;
}
else
{
dLeft = 0.0;
}
// right desire
double dRight;
if (rightDist > 0.0 && infra.getCrossSection().contains(RelativeLane.RIGHT))
{
Speed vRight = traffic.getSpeed(RelativeLane.RIGHT, context.getDesiredSpeed());
if (vCur.si >= context.getParameters().getParameter(VCONG).si)
{
dRight = aGain * Math.min(vRight.si - vCur.si, 0) / vGain.si;
}
else
{
dRight = aGain * (vRight.si - vCur.si) / vGain.si;
}
}
else
{
dRight = 0.0;
}
// return desire
return new Desire(dLeft, dRight);
}
@Override
public String toString()
{
return "IncentiveSpeedWithCourtesy";
}
}