Skip to content

Commit

Permalink
[traffic] Re-enable the safety check when crossing intersections
Browse files Browse the repository at this point in the history
  • Loading branch information
minhduc0711 committed Sep 22, 2021
1 parent 2cd38bc commit f2f5a12
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 48 deletions.
@@ -0,0 +1,227 @@
/**
* Name: Mix Drive City
* Description: Vehicles driving in a road graph
* Author: Duc Pham and Patrick Taillandier
* Tags: gis, shapefile, graph, agent_movement, skill, transport
*/

model simple_intersection


global {
float size_environment <- 1#km;

geometry shape <- envelope(size_environment);

//the typical step for the advanced driving skill
float step <- 0.5 #s;

//use only for display purpose
float lane_width <- 2.0;

//number of cars
int num_cars <- 300;

float proba_block_node_car <- 1.0;

//graph used for the shortest path computation
graph road_network;

init {
create intersection with: (location: {10,size_environment/2});
create intersection with: (location: {size_environment/2,size_environment/2});
create intersection with: (location: {size_environment / 2 + 30,size_environment/2}, is_traffic_signal: true);
create intersection with: (location: {size_environment - 10,size_environment/2});

create intersection with: (location: {size_environment/2, 10});
create intersection with: (location: {size_environment/2,size_environment - 10});

create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[0],intersection[1]]));
create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[1],intersection[2]]));
create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[2],intersection[3]]));
create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[4],intersection[1]]));
create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[1],intersection[5]]));


//build the graph from the roads and intersections
road_network <- as_driving_graph(road,intersection);

//for traffic light, initialize their counter value (synchronization of traffic lights)
ask intersection where each.is_traffic_signal {
do initialize;
}

}

reflex add_car {
create car with: (location: intersection[0].location, target: intersection[3]);
create car with: (location: intersection[4].location, target: intersection[5]);
}
}


//road species
species road skills: [skill_road]{
string type;
string oneway;

aspect base_ligne {
draw shape color: #white end_arrow:5;
}

}

//intersection species
species intersection skills: [skill_road_node] {
bool is_traffic_signal;
float time_to_change <- 60#s ;
float counter <- rnd(time_to_change);

//take into consideration the roads coming from both direction (for traffic light)
list<road> ways1;
list<road> ways2;

//if the traffic light is green
bool is_green;
rgb color <- #yellow;

//initialize the traffic light
action initialize {
do compute_crossing;
stop << [];
if (flip(0.5)) {
do to_green;
} else {
do to_red;
}
}

action compute_crossing {
if (length(roads_in) >= 2) {
road rd0 <- road(roads_in[0]);
list<point> pts <- rd0.shape.points;
float ref_angle <- last(pts) direction_to rd0.location;
loop rd over: roads_in {
list<point> pts2 <- road(rd).shape.points;
float angle_dest <- last(pts2) direction_to rd.location;
float ang <- abs(angle_dest - ref_angle);
if (ang > 45 and ang < 135) or (ang > 225 and ang < 315) {
ways2 << road(rd);
}
}
}

loop rd over: roads_in {
if not (rd in ways2) {
ways1 << road(rd);
}
}
}

//shift the traffic light to green
action to_green {
stop[0] <- ways2;
color <- #green;
is_green <- true;
}

//shift the traffic light to red
action to_red {
stop[0] <- ways1;
color <- #red;
is_green <- false;
}

//update the state of the traffic light
reflex dynamic_node when: is_traffic_signal {
counter <- counter + step;
if (counter >= time_to_change) {
counter <- 0.0;
if is_green {
do to_red;
} else {
do to_green;
}
}
}

aspect base {
draw circle(1) color: color;
}
}


species car skills: [advanced_driving] {
rgb color <- rnd_color(255);
intersection target;

init {
vehicle_length <- 3.8 #m;
//car occupies 2 lanes
num_lanes_occupied <-1;
max_speed <-150 #km / #h;

proba_block_node <- proba_block_node_car;
proba_respect_priorities <- 1.0;
proba_respect_stops <- [1.0];
proba_use_linked_road <- 0.0;

lane_change_limit <- 2;
linked_lane_limit <- 0;

}
//choose a random target and compute the path to it
reflex choose_path when: final_target = nil {
do compute_path graph: road_network target: target;
}
reflex move when: final_target != nil {
do drive;
//if arrived at target, kill it and create a new car
if (final_target = nil) {
do unregister;
do die;
}
}

// Just use for display purpose
// Shifts the position of the vehicle perpendicularly to the road,
// in order to visualize different lanes
point compute_position {
if (current_road != nil) {
float dist <- (road(current_road).num_lanes - current_lane -
mean(range(num_lanes_occupied - 1)) - 0.5) * lane_width;
if violating_oneway {
dist <- -dist;
}
point shift_pt <- {cos(heading + 90) * dist, sin(heading + 90) * dist};

return location + shift_pt;
} else {
return {0, 0};
}
}


aspect default {
if (current_road != nil) {
point pos <- compute_position();
draw rectangle(vehicle_length, lane_width * num_lanes_occupied)
at: pos color: color rotate: heading border: #black;
draw triangle(lane_width * num_lanes_occupied)
at: pos color: #white rotate: heading + 90 ;
}
}

}


experiment simple_intersection type: gui {

output {
display city type: opengl background: #black synchronized: true draw_env: false{
species road aspect: base_ligne;
species intersection aspect: base;
species car ;
}
}
}
Expand Up @@ -18,9 +18,12 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.apache.commons.collections4.OrderedBidiMap;
import org.apache.commons.lang3.tuple.Pair;
import org.locationtech.jts.geom.Coordinate;

import com.google.common.collect.Iterables;

import msi.gama.common.interfaces.IKeyword;
import msi.gama.metamodel.agent.AbstractAgent;
import msi.gama.metamodel.agent.IAgent;
Expand All @@ -45,7 +48,9 @@
import msi.gama.util.path.IPath;
import msi.gama.util.path.PathFactory;
import msi.gaml.descriptions.ConstantExpressionDescription;
import msi.gaml.operators.Maths;
import msi.gaml.operators.Random;
import msi.gaml.operators.Spatial.Punctal;
import msi.gaml.operators.Spatial.Queries;
import msi.gaml.skills.MovingSkill;
import msi.gaml.species.ISpecies;
Expand Down Expand Up @@ -1082,13 +1087,14 @@ public static Boolean readyToCross(final IScope scope,
double vehicleLength = getVehicleLength(vehicle);
ISpecies context = vehicle.getSpecies();

// TODO: refactor this
// additional conditions to cross the intersection, defined by the user
// IStatement.WithArgs actionTNR = context.getAction("test_next_road");
// Arguments argsTNR = new Arguments();
// argsTNR.put("new_road", ConstantExpressionDescription.create(newRoad));
// actionTNR.setRuntimeArgs(scope, argsTNR);
// if (!(Boolean) actionTNR.executeOn(scope)) { return false; }
IStatement.WithArgs actionTNR = context.getAction("test_next_road");
Arguments argsTNR = new Arguments();
argsTNR.put("new_road", ConstantExpressionDescription.create(newRoad));
actionTNR.setRuntimeArgs(scope, argsTNR);
if (!(Boolean) actionTNR.executeOn(scope)) {
return false;
}

IAgent currentRoad = (IAgent) vehicle.getAttribute(CURRENT_ROAD);
// Don't need to do these checks if the vehicle was just initialized
Expand Down Expand Up @@ -1130,53 +1136,61 @@ public static Boolean readyToCross(final IScope scope,
return true;
}

// Check for vehicles coming from the rightside road
Boolean rightSide = getRightSideDriving(vehicle);
List<IAgent> priorityRoads = (List<IAgent>) node.getAttribute(RoadNodeSkill.PRIORITY_ROADS);
boolean onPriorityRoad = priorityRoads != null && priorityRoads.contains(currentRoad);

// compute angle between the current & next road
// double angleRef = Punctal.angleInDegreesBetween(scope, (GamaPoint) intersectionNode.getLocation(),
// (GamaPoint) currentRoad.getLocation(), (GamaPoint) newRoad.getLocation());

// TODO: adjust the speed diff condition
// TODO: always return false if vehicle decides to make an U-turn
// double speed = Math.max(0.5, getSpeed(vehicle) + getMaxAcceleration(vehicle));
// double safetyDistCoeff = vehicle.hasAttribute(SAFETY_DISTANCE_COEFF) ? getSafetyDistanceCoeff(vehicle)
// : getSecurityDistanceCoeff(vehicle);

// List<IAgent> roadsIn = (List) intersectionNode.getAttribute(RoadNodeSkill.ROADS_IN);
// for (IAgent otherInRoad : roadsIn) {
// if (otherInRoad == currentRoad) {
// continue;
// }
// double angle = Punctal.angleInDegreesBetween(scope, (GamaPoint) intersectionNode.getLocation(),
// (GamaPoint) currentRoad.getLocation(), (GamaPoint) otherInRoad.getLocation());
// boolean otherRoadIsPriortized = priorityRoads != null && priorityRoads.contains(otherInRoad);
// boolean hasPriority = onPriorityRoad && !otherRoadIsPriortized;
// boolean shouldRespectPriority = !onPriorityRoad && otherRoadIsPriortized;
// // be careful of vehicles coming from the right/left side
// if (!hasPriority
// && (shouldRespectPriority || rightSide && angle > angleRef || !rightSide && angle < angleRef)) {
// List<IAgent> otherVehicles = (List) otherInRoad.getAttribute(RoadSkill.ALL_AGENTS);
// for (IAgent otherVehicle : otherVehicles) {
// if (otherVehicle == null || otherVehicle.dead()) {
// continue;
// }
// double otherVehicleLength = getVehicleLength(otherVehicle);
// double otherSpeed = getSpeed(otherVehicle);
// double dist = otherVehicle.euclidianDistanceTo(vehicle);

// if (Maths.round(getSpeed(otherVehicle), 1) > 0.0 &&
// 0.5 + safetyDistCoeff * Math.max(0, speed - otherSpeed) >
// dist - (vehicleLength / 2 + otherVehicleLength / 2)) {
// return false;
// }
// }
// }
// }
// ab is the line representing the direction of the vehicle
// when moving from the current road to the next road
List<GamaPoint> pts = currentRoad.getGeometry().getPoints();
GamaPoint a = pts.get(pts.size() - 2); // starting point of last segment of current road
GamaPoint b = newRoad.getGeometry().getPoints().get(1); // end point of first segment of new road

// Why is 0.5 a lower bound??
double speed = Math.max(0.5, getSpeed(vehicle));
double safetyDistCoeff = vehicle.hasAttribute(SAFETY_DISTANCE_COEFF) ? getSafetyDistanceCoeff(vehicle)
: getSecurityDistanceCoeff(vehicle);
double distToNode = getDistanceToCurrentTarget(vehicle);
List<IAgent> roadsIn = (List) node.getAttribute(RoadNodeSkill.ROADS_IN);
for (IAgent otherInRoad : roadsIn) {
if (otherInRoad == currentRoad) {
continue;
}
List<GamaPoint> otherPts = otherInRoad.getGeometry().getPoints();
// Starting point of last segment of other incoming road
GamaPoint p = otherPts.get(otherPts.size() - 2);
// Check if this road is on the right or left side of the current vehicle's moving direction
int side = Utils.sideOfPoint(a, b, p);
boolean otherRoadIsPriortized = priorityRoads != null && priorityRoads.contains(otherInRoad);
boolean hasPriority = onPriorityRoad && !otherRoadIsPriortized;
boolean shouldRespectPriority = !onPriorityRoad && otherRoadIsPriortized;
// be careful of vehicles coming from the right/left side
if (!hasPriority
&& (shouldRespectPriority || rightSide && side < 0 || !rightSide && side > 0)) {
for (OrderedBidiMap<IAgent, Double> vehicleOrderMap : RoadSkill.getVehicleOrdering(otherInRoad)) {
// The vehicle closest to the end of the road
OrderedBidiMap<Double, IAgent> distMap = vehicleOrderMap.inverseBidiMap();
double otherDistToNode = distMap.lastKey();
IAgent otherVehicle = distMap.get(otherDistToNode);
if (otherVehicle == null || otherVehicle.dead()) {
continue;
}
double otherVehicleLength = getVehicleLength(otherVehicle);
double otherSpeed = getSpeed(otherVehicle);
if (getCurrentTarget(otherVehicle) != node) {
// Other vehicle is actually going away from the intersection
otherSpeed = -otherSpeed;
}
double gap = distToNode + otherDistToNode - (vehicleLength / 2 + otherVehicleLength / 2);

if (getSpeed(otherVehicle) > 0.0 &&
0.5 + safetyDistCoeff * Math.max(0, speed - otherSpeed) > gap) {
return false;
}
}
}
}
}

return true;
}

Expand Down

0 comments on commit f2f5a12

Please sign in to comment.