+1 (972) 883-2091
ECSS 4.220, 800 W Campbell Rd, Richardson, TX 75083–0688, US

1. Package Description

The “planning” package implements the planning module which serves as the brain of a vehicle agent. As illustrated in the figure below, to implement a vehicle agent’s planning module, it’s necessary to extend the classes: “AbstractPlanningModule”, “AbstractPlanGenerator”, “Plan”, and “AbstractPlanExecutor” to provide concrete implementations. Next, we describe the process of extetnding these classes to implement the concrete traffic simulation classes.

stimulus

2. Implementing the “DrivePlan” class

In this example, a drive plan describes the vehicle agent’s intention to drive on roads by moving from its current position to the next intended position. To implement this class:

  1. Inside the package “edu.utdallas.mavs.traffic.simulation.sim.agent.planning”, create a class named “DrivePlan” that extends the class “edu.utdallas.mavs.divas.core.sim.agnt.planning.Plan”.
  2. The implementation should specify the agent plan to move too the next intended position (“nextPosition”) during the next simulation cycle. Write the following code to implement the “DrivePlan” class:
package edu.utdallas.mavs.traffic.simulation.sim.agent.planning;

import com.jme3.math.Vector3f;

import edu.utdallas.mavs.divas.core.sim.agent.planning.Plan;
import edu.utdallas.mavs.divas.core.sim.agent.task.Task;

public class DrivePlan extends Plan
{
    private static final long serialVersionUID = 1L;

    Vector3f                  nextPosition;

    public DrivePlan()
    {
        super();
    }

    /**
     * Format plan for printing
     */
    @Override
    public String toString()
    {
        String str = "";
        for(Task t : tasks)
        {
            if(t != null && tasks != null)
            {
                str += " " + t.toString();
            }
        }

        if(str == "")
        {
            str += "Idle";
        }

        return str;
    }
}

3. Implementing the “VehiclePlanExecutor” class

A “VehiclePlanExecutor” executes the “DrivePlan” to generate a set of vehicle agent’s stimuli that are sent to the environment at the end of the agent phase. To implement this class:

  1. Inside the package “edu.utdallas.mavs.traffic.simulation.agent.planning” create a class named “VehiclePlanExecutor” that extends the class “edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanExecutor”.
  2. The class “VehiclePlanExecutor” must provide a concrete implementation for the method “executePlan” that specifies the translation of the drive plan into a set of agent vehicle’s stimuli. The following code provides an implementation of “VehiclePlanExecutor”:
package edu.utdallas.mavs.traffic.simulation.sim.agent.planning;

import java.io.Serializable;

import edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanExecutor;
import edu.utdallas.mavs.divas.core.sim.agent.planning.Plan;
import edu.utdallas.mavs.divas.core.sim.agent.task.Task;
import edu.utdallas.mavs.divas.core.sim.common.stimulus.Stimuli;
import edu.utdallas.mavs.traffic.simulation.sim.agent.knowledge.VehicleKnowledgeModule;
import edu.utdallas.mavs.traffic.simulation.sim.agent.tasks.VehicleTaskModule;

public class VehiclePlanExecutor extends AbstractPlanExecutor<VehicleKnowledgeModule, VehicleTaskModule> implements Serializable
{
    private static final long serialVersionUID = 1L;

    public VehiclePlanExecutor(VehicleKnowledgeModule knowledgeModule)
    {
        super(knowledgeModule);
    }

    @Override
    public Stimuli executePlan(Plan plan)
    {
        Stimuli sm = new Stimuli();

        for(Task task : plan.getTasks())
        {
            if(task != null && task.isEnabled())
            {
                sm.addAll(task.execute(knowledgeModule.getId()));
            }
        }

        return sm;

    }

}

4. Implementing the “VehiclePlanGenerator” class

In this example, a “VehiclePlanGenerator” executes a planning strategy to generate a drive plan for the next simulation cycle. A drive plan describes a vehicle intention to move to the next position on a road segment while trying to reach its destination goal. At the end of the plan generation process, a “VehiclePlanGenerator” gnerates a “DrivePlan” that consists of a combination of: “MoveForwardTask”, “TurnRightTask”, and “TurnLeftTask”.

For the sake of simplicity, we assume that our traffic network has a grid-like street topology or similar to the well-known street geography of Manhattan. Consequently, a vehicle agent either: 1) drives on horizontal or vertical path (that is, along the grid lines); or 2) makes left/right turns on an intersection to change its path from horizontal to vertical or vice versa. Based on the aforementioned assumptions, a driving plan can be generated according to three cases as illustrated in the figure below:

stimulus

Case 1: A vehicle is driving in parallel with the x-axis or the z-axis.

In this case, the generated drive plan consists of a “MoveForwardTask” to the next position on the vehicle current road segment. The computation of the next position depends on the current vehicle position, heading, and speed. For instance, in the example illustrated in the above figure, the heading of V1 is (-1, 0, 0). This means that the driving direction of V1 is in parallel to the x-axis. As the position of V1 is (11, 0, 3) and its speed is 5, the vehicle will drive for 5 points on its heading direction. As such, V1’s position on the x-axis will be 6, while its position on the z-axis will be unchanged. By the end of the plan generation process, V1 will generate a driving plan that consists of a “MoveForwardTask” to the position (6, 0, 3). In addition, the vehicle wheels’ animation posture will be set to “forward_fast”.

While driving in parallel to the x-axis/z-axis, the vehicle plan generator keeps generating plans to move forward on its direction until it reach an intersection. After that, the vehicle agent will consider its goal as achieved. Then, it will select a neighbor intersection and set it to be its net goal.

Case 2: A vehicle is making left/right turn.

In case of making turns, the generated drive plan consists of either a “TurnLeftTask” or “TurnRightTask” that describes the turn start and end positions. Once a “TurnTask” is created, the vehicle interpolated positions and headings are computed depending on the number of steps needed by the vehicle to finish its turn. The detailed discussion of this process is given earlier in the description of the task module. As the turn task spans several simulation cycles, the vehicle plan generator sets the turn task to be ongoing to indicate its incompleteness. During an ongoing turn task, the next position is selected from the set of the pre-computed interpolated positions. The vehicle plan generator keeps generating ongoing tasks until completing the predefined number of steps in a turn task.

Case 3: A vehicle is waiting on an intersection.

Once a vehicle reach the end of a road segment attached to an intersection, it has to stop and wait for 10 simulation cycles before proceeding to travel to its next destination goal. During the waiting period, there is no driving plan to be generated. In addition, the vehicle wheels’ animation posture is set to “zero_position” to indicate that the wheels is not rolling.

To implement the “VehiclePlanGenerator” class:

  1. Inside the package “edu.utdallas.mavs.traffic.simulation.agent.planning” create a class named “VehiclePlanGenerator” that extends the class “edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanGenerator”.
  2. Write the following code that implements the driving plan which includes the aforementioned three cases.
package edu.utdallas.mavs.traffic.simulation.sim.agent.planning;

import com.jme3.math.Vector3f;

import edu.utdallas.mavs.divas.core.sim.agent.knowledge.internal.Goal;
import edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanGenerator;
import edu.utdallas.mavs.divas.core.sim.agent.planning.Plan;
import edu.utdallas.mavs.divas.core.sim.agent.task.AbstractTask;
import edu.utdallas.mavs.divas.core.sim.agent.task.Task;
import edu.utdallas.mavs.traffic.simulation.sim.agent.knowledge.VehicleKnowledgeModule;
import edu.utdallas.mavs.traffic.simulation.sim.agent.tasks.MoveForwardTask;
import edu.utdallas.mavs.traffic.simulation.sim.agent.tasks.TurnTask;
import edu.utdallas.mavs.traffic.simulation.sim.agent.tasks.VehicleTaskModule;
import edu.utdallas.mavs.traffic.simulation.sim.common.state.Posture;

public class VehiclePlanGenerator extends AbstractPlanGenerator<VehicleKnowledgeModule, VehicleTaskModule> {
	private static final long serialVersionUID = 1L;
	private static final String POSITION = "Position";
	private Task ongoingTask;
	private boolean isIntersection = false;

	public VehiclePlanGenerator(VehicleKnowledgeModule knowledgeModule, VehicleTaskModule taskModule) {
		super(knowledgeModule, taskModule);
	}

	@Override
	public void addGoal(Goal arg0) {

	}

	@Override
	public Plan plan() {
		DrivePlan generatedPlan = new DrivePlan();
		if (hasOngoingTask()) {
			generatedPlan.addTask(ongoingTask);
		} else {
			planMovement(generatedPlan);
		}

		writeDebugInfo(generatedPlan);

		return generatedPlan;
	}

	private boolean hasOngoingTask() {
		return (ongoingTask instanceof TurnTask) && !((TurnTask) ongoingTask).hasCompleted();
	}

	private void planMovement(DrivePlan generatedPlan) {
		if (knowledgeModule.getFinalGoal() == null || knowledgeModule.getFinalGoal().isAchieved()) {
			Vector3f nextGoal = knowledgeModule.getNextGoal();
			knowledgeModule.setFinalGoal(new Goal("Vector", POSITION, nextGoal));
		}

		if (((Vector3f) knowledgeModule.getFinalGoal().getValue()) != null) {
			planNextPosition(generatedPlan);
		}
	}

	private void planNextPosition(DrivePlan generatedPlan) {
		float nextXPosition = 0;
		float nextZPosition = 0;
		Vector3f goal = (Vector3f) knowledgeModule.getFinalGoal().getValue();

		if (!knowledgeModule.getHeading().equals(knowledgeModule.getPreviousHeading())) {
			// This is the initial heading when creating vehicle agent. We don't
			// want the vehicle to stop there
			if (knowledgeModule.getPreviousHeading().equals(new Vector3f(0, 0, 0))) {
				knowledgeModule.setWaitTime(1);
			}

			if (knowledgeModule.getWaitTime() == 0) {
				knowledgeModule.setWaitTime(10);
				knowledgeModule.getSelf().setPosture(Posture.zero_position);
				isIntersection = true;
				knowledgeModule.getCurrentStreetTile();
			}

			// Move to the begging of the intersection before starting the turn
			// task
			if (knowledgeModule.getWaitTime() == 2) {
				MoveForwardTask moveForwardTask = taskModule
						.createMoveForwardTask(knowledgeModule.getSelf().getPosition()
								.add(knowledgeModule.getPreviousHeading().mult(VehicleKnowledgeModule.CAR_OFFSET)));
				generatedPlan.addTask(moveForwardTask);
				knowledgeModule.getSelf().setPosture(Posture.forward_fast);
			}

			if (knowledgeModule.getWaitTime() == 1) {

				/*
				 * if(!(ongoingTask instanceof TurnRightTask) &&
				 * knowledgeModule.yieldRightOfWay(intersectionTile)) {
				 * knowledgeModule.setWaitTime(knowledgeModule.getWaitTime() +
				 * 2); } else {
				 */

				isIntersection = false;
				knowledgeModule.setWaitTime(0);
				nextXPosition = goal.getX();
				nextZPosition = goal.getZ();
				knowledgeModule.getFinalGoal().setAchieved(true);
				ongoingTask = getNextTaskDirection();
				generatedPlan.addTask(ongoingTask);
				// }
			} else {
				knowledgeModule.setWaitTime(knowledgeModule.getWaitTime() - 1);
			}

		} else if (knowledgeModule.getWaitTime() == 0) {
			// if the vehicle movement on the X-axis
			if (Math.abs(knowledgeModule.getHeading().getX()) == 1) {
				nextXPosition = knowledgeModule.getSelf().getPosition().getX()
						+ (knowledgeModule.desiredSpeed * knowledgeModule.getHeading().getX());
				nextZPosition = knowledgeModule.getSelf().getPosition().getZ();

				if (((nextXPosition >= (goal.getX())) && (knowledgeModule.getHeading().getX() == 1))
						|| ((nextXPosition <= (goal.getX())) && (knowledgeModule.getHeading().getX() == -1))) {
					nextXPosition = goal.getX();
				}

				// the current goal is reached
				if (nextXPosition == goal.getX()) {
					knowledgeModule.getFinalGoal().setAchieved(true);
					if (isIntersection) {
						isIntersection = false;
						knowledgeModule.setWaitTime(10);
						knowledgeModule.getSelf().setPosture(Posture.zero_position);
					}
				}
			}

			// if the vehicle movement on the Z-axis
			if (Math.abs(knowledgeModule.getHeading().getZ()) == 1) {
				nextZPosition = knowledgeModule.getSelf().getPosition().getZ()
						+ (knowledgeModule.desiredSpeed * knowledgeModule.getHeading().getZ());
				nextXPosition = knowledgeModule.getSelf().getPosition().getX();

				if (((nextZPosition >= (goal.getZ())) && (knowledgeModule.getHeading().getZ() == 1))
						|| ((nextZPosition <= (goal.getZ())) && (knowledgeModule.getHeading().getZ() == -1))) {
					nextZPosition = goal.getZ();
				}
				// the current goal is reached
				if (nextZPosition == goal.getZ()) {
					knowledgeModule.getFinalGoal().setAchieved(true);
					if (isIntersection) {
						isIntersection = false;
						knowledgeModule.setWaitTime(10);
						knowledgeModule.getSelf().setPosture(Posture.zero_position);
					}
				}
			}

			Vector3f nextPosition = new Vector3f(nextXPosition, knowledgeModule.getSelf().getPosition().getY(),
					nextZPosition);

			if (knowledgeModule.collidesWithOtherVehicles(nextPosition)) {
				knowledgeModule.setWaitTime(1);
				knowledgeModule.getSelf().setPosture(Posture.zero_position);
			} else {
				MoveForwardTask moveForwardTask = taskModule.createMoveForwardTask(nextPosition);
				generatedPlan.addTask(moveForwardTask);
				if (knowledgeModule.getWaitTime() != 10) {
					knowledgeModule.getSelf().setPosture(Posture.forward_fast);
				}
			}

			if (knowledgeModule.getCurrentGoalDegree() > 2) {
				isIntersection = true;
				knowledgeModule.getCurrentStreetTile();
			} else {
				isIntersection = false;
			}
		} else {
			knowledgeModule.setWaitTime(knowledgeModule.getWaitTime() - 1);
		}
	}

	/**
	 * Return the type of next task
	 */
	public AbstractTask getNextTaskDirection() {
		if (knowledgeModule.getPreviousHeading().getX() == 1 && knowledgeModule.getHeading().getZ() == 1) {
			// right
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setZ(nextPosition.getZ());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_right);
			return taskModule.createTurnRightTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getX() == 1 && knowledgeModule.getHeading().getZ() == -1) {
			// left
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setZ(nextPosition.getZ());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_left);
			return taskModule.createTurnLeftTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getX() == -1 && knowledgeModule.getHeading().getZ() == 1) {
			// left
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setZ(nextPosition.getZ());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_left);
			return taskModule.createTurnLeftTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getX() == -1 && knowledgeModule.getHeading().getZ() == -1) {
			// right
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setZ(nextPosition.getZ());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_right);
			return taskModule.createTurnRightTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getZ() == 1 && knowledgeModule.getHeading().getX() == 1) {
			// left
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setX(nextPosition.getX());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_left);
			return taskModule.createTurnLeftTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getZ() == 1 && knowledgeModule.getHeading().getX() == -1) {
			// right
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setX(nextPosition.getX());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_right);
			return taskModule.createTurnRightTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getZ() == -1 && knowledgeModule.getHeading().getX() == 1) {
			// right
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setX(nextPosition.getX());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_right);
			return taskModule.createTurnRightTask(nextPosition);
		} else if (knowledgeModule.getPreviousHeading().getZ() == -1 && knowledgeModule.getHeading().getX() == -1) {
			// left
			Vector3f nextPosition = (Vector3f) knowledgeModule.getFinalGoal().getValue();
			nextPosition.setX(nextPosition.getX());
			knowledgeModule.getSelf().setPosture(Posture.forward_mid_left);
			return taskModule.createTurnLeftTask(nextPosition);
		}

		return null;
	}

	private void writeDebugInfo(DrivePlan generatedPlan) {
		if (generatedPlan != null) {
			/*
			 * knowledgeModule.getSelf().setPlanningDetails( String.format("%s",
			 * generatedPlan.toString()));
			 */
			// knowledgeModule.getSelf().setPlanningDetails(String.format("Current
			// Tile id: %d",
			// knowledgeModule.getCurrentStreetTile().getID()));
		}
	}
}

5. Implementing the “VehiclePlanningModule” class

As mentioned earlier, the vehicle planning module serves as the brain of a vehicle agent. Its main role is to generate driving plans that are translated into a set of movement and change heading stimuli. These stimuli describe a vehicle agent’s intention to drive on roads and are sent to the environment at the end of the agent phase. The vehicle planning module aggregates the aforementioned classes to implement the process of generating these stimuli. To implement this class:

  1. Inside the package “edu.utdallas.mavs.traffic.simulation.sim.agent.planning” create a class named “VehiclePlanningModule” that extends the class “edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanningModule”.
  2. The “VehiclePlanningModule” class should implement the abstract method “plan” to specify the process of generating a drive plan and translating it into a set of stimuli. The following code provides an implementation for the class “VehiclePlanningModule”.
package edu.utdallas.mavs.traffic.simulation.sim.agent.planning;

import java.io.Serializable;

import edu.utdallas.mavs.divas.core.sim.agent.knowledge.internal.Goal;
import edu.utdallas.mavs.divas.core.sim.agent.planning.AbstractPlanningModule;
import edu.utdallas.mavs.divas.core.sim.agent.planning.Plan;
import edu.utdallas.mavs.divas.core.sim.common.stimulus.Stimuli;
import edu.utdallas.mavs.traffic.simulation.sim.agent.knowledge.VehicleKnowledgeModule;
import edu.utdallas.mavs.traffic.simulation.sim.agent.tasks.VehicleTaskModule;

public class VehiclePlanningModule extends AbstractPlanningModule<VehicleKnowledgeModule, VehicleTaskModule> implements Serializable
{
    private static final long    serialVersionUID = 1L;

    private VehiclePlanGenerator planGenerator;
    private VehiclePlanExecutor  planExecutor;

    public VehiclePlanningModule(VehiclePlanGenerator planGenerator, VehicleKnowledgeModule knowledgeModule, VehiclePlanExecutor planExecutor)
    {
        super(knowledgeModule);
        this.planGenerator = planGenerator;
        this.planExecutor = planExecutor;
    }

    @Override
    public void addGoal(Goal arg0)
    {
        // TODO Auto-generated method stub
    }

    @Override
    public Stimuli plan()
    {
        Stimuli sm = new Stimuli();
        Plan plan = planGenerator.plan();
        knowledgeModule.getSelf().setCurrentTasks(plan.getTasks());
        sm = planExecutor.executePlan(plan);
        return sm;
    }
}