Junction elements
internal_road
The internal_road route element represents a route within a junction, that connects one incoming one_way_road to one outgoing one_way_road.
Each road position on the map, specified by a longitudinal-offset on a specific msp_road, belongs to either a single one_way_road or a single internal_road.
For interchange junctions that are modeled without overlapping msp_roads, the area of the junction is artificially defined to extend half a meter from the incoming road(s) and half a meter from the outgoing road(s).
OSC definition of internal_road
# enum junction_type:
# A junction is classified as 'highway' if it has either only one entry or only one exit, and
# speed-limits, road curvatures and entry-exit directions of all incoming, outgoing and internal
# roads match constrains of highway roads. Otherwise it is classified as 'urban'.
#
enum junction_type: [other = 0, urban, highway]
# enum junction_category_type:
# 'merge' - the junction has a single exit.
# 'split' - the junction has a single entry.
# 'complex' - not merge or split
#
enum junction_category_type: [other = 0, merge, split, complex]
# internal_road represents a junction-internal road connecting one inbound road to one outbound road.
#
struct internal_road inherits road_element:
# id of the containing junction. This is also the index of the junction in map.junctions.
#
junction_id: uint
# type of the containing junction, see enum junction_type for available values
#
junction_kind: junction_type
# category of the containing junction, see enum junction_category for available values
#
junction_category: junction_category_type
# direction is determined by the angle diff at start and end of the associated route.
#
direction: direction
# min and max curvature along the internal route, in terms of radius.
# radius is positive for both left and right curvatures.
# The direction of the curvature is known from 'direction' field.
# For a straight road interval 'radius' is MAX_DISTANCE
#
min_radius: length
max_radius: length
# id of the incoming one_way_road connected to this internal_road
#
in_one_way_road_id: int
# id of the outgoing one_way_road connected to this internal_road
#
out_one_way_road_id: int
# id of the logical traffic-light controlling this connection (index in map.traffic_lights).
# -1 if none.
#
traffic_light_id: int
# id of the road_with_traffic_light instance controlling this connection. -1 if none.
#
road_with_traffic_light_id: int
# id of the static-sign controlling this connection (e.g., stop or yield). -1 if none.
#
sign_id: int
# id of the road_with_sign instance controlling this connection. -1 if none.
#
road_with_sign_id: int
# flag to indicate if the vehicle can move on the internal road without conflict. The default value is true, in case there is no conflict.
#
can_be_protected: bool
# flag to indicate if the vehicle can move on the internal road with conflict. The default value is false, in case there is no conflict.
#
can_be_unprotected: bool
# flag to indicate if 'stop' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_stop: bool
# flag to indicate if 'stop_and_yield' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_stop_and_yield: bool
# flag to indicate if 'stop_constant' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_stop_constant: bool
# flag to indicate if 'attention' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_attention: bool
# flag to indicate if 'caution' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_caution: bool
# flag to indicate if 'stop_attention' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_stop_attention: bool
# flag to indicate if 'go' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_go: bool
# flag to indicate if 'go_exclusive' is a valid semantic state for the traffic light controlling this internal road
#
valid_tl_go_exclusive: bool
can_be_protected and can_be_unprotected properties
-
can_be_protected: If this property is true, it means that at least under some circumstances, the rules of the road allow vehicles to cross the junction using this internal road without stopping, slowing down, or yielding to conflicting traffic. -
can_be_unprotected: If this property is true, it means that att least under some circumstances, the rules of the road allow vehicles to cross, but first they must stop, slow down, or yield to conflicting traffic.
For internal_roads that are not controlled by traffic lights
- Either
can_be_protectedandcan_be_unprotectedis true, but not both. - If the internal_road does not have a ‘stop’ or ‘yield’ sign, and all conflicting
internal_roadsare controlled by ‘stop’ or ‘yield’ signs, thencan_be_protectedis true, and can_be_unprotectedis false. - Otherwise
can_be_unprotectedis true, andcan_be_protectedis false.
For internal_roads that are are controlled by traffic lights:
can_be_protectedandcan_be_unprotectedare determined by the possible traffic light states and how those states are interpreted based on the regional settings of the map.- Any combination of values may be valid.
- The ‘valid_tl_XXX’ properties provide more detailed information about the possible control states. For additional information see Traffic lights modeling and control.
Examples using can_be_protected and can_be_unprotected
The following examples show how can_be_protected and can_be_unprotected values are used in traffic light-controlled internal_roads.
In the following image, a vehicle travelling the internal_road marked by the path (going straight) must either stop at a red light, or cross the junction without stopping or yielding at a green light. It will therefore have can_be_protected == true, and can_be_unprotected == false.
In the following image, assuming jurisdiction that allows right turns on red, a vehicle travelling the internal_road corresponding to the marked path may either have a protected junction crossing indicated by a green light, or an unprotected junction crossing that requires stopping and yielding to conflicting traffic indicated by a red light. It will therefore have can_be_protected == true and can_be_unprotected == true.
By US rules, a traffic light-controlled left turn is only considered protected if it has a green left arrow. Otherwise, vehicles must yield to conflicting traffic. In the following image, assuming US rules, a vehicle travelling along the internal_road marked by the path, does not have protected passage because the traffic light does not display a green left arrow. Therefore can_be_protected == false and can_be_unprotected == true.
Example using one_way_road and internal_road
In this example, we use two triplets of <one_way_road, internal_road, one_way_road> to define two paths crossing a junction, one for the SUT and one for an NPC. Then we use the modifier roads_enter_junction_relation to constrain the NPC’s path so it enters the junction from the left of the SUT’s path, as illustrated in the following image. The SUT and its planned path are green, the NPC and its planned path are blue.
Example scenario using one_way_road and internal_road
import "$FTX/env/basic/exe_platforms/model_ssp/config/model_dummy_config.osc"
import "$FTX/env/basic/msp/open_drive.osc"
extend test_config:
set map = "$FTX_PACKAGES/maps/Town04.xodr"
scenario sut.enter_junction_with_vehicle_from_left:
npc: vehicle
sut_entry_road: one_way_road
sut_exit_road: one_way_road
sut_internal_road: internal_road with:
keep(it.in_one_way_road_id == sut_entry_road.id and
it.out_one_way_road_id == sut_exit_road.id)
npc_entry_road: one_way_road
npc_exit_road: one_way_road
npc_internal_road: internal_road with:
keep(it.in_one_way_road_id == npc_entry_road.id and
it.out_one_way_road_id == npc_exit_road.id)
roads_enter_junction_relation(sut_entry_road,
npc_entry_road,
relative_direction: any_left)
do parallel(overlap: equal, duration: [5..10]s):
serial:
sut.car.drive() with:
along(sut_entry_road)
sut.car.drive() with:
along(sut_internal_road)
sut.car.drive() with:
along(sut_exit_road, run_mode: best_effort)
serial:
npc.drive() with:
along(npc_entry_road)
npc.drive() with:
along(npc_internal_road)
npc.drive() with:
along(npc_exit_road, run_mode: best_effort)
extend top.main:
do sut.enter_junction_with_vehicle_from_left()
extend top.info:
# Downgrading a collision error to info
on @top.new_issue:
call issues.curr.modify(collision_in_manual_control, info, "Downgrading collision severity to 'info'")
roads_follow_in_junction structs
Since routes that cross junctions are a common use case, the roads_follow_in_junction struct groups, together with the triplet <one_way_road, internal_road, one_way_road> are used for this type of route.
# This struct is used to bind together a path over a junction
# It contains:
# in_road - Incoming one_way_road
# out_road - Outgoing one_way_road
# internal_road - internal_road connecting the two one_way_roads
#
struct roads_follow_in_junction:
in_road: one_way_road
out_road: one_way_road
internal_road: internal_road with:
keep(it.in_one_way_road_id == in_road.id and
it.out_one_way_road_id == out_road.id)
There are a few derivatives to this class that further constrain the junction-crossing route to be traffic light-controlled, sign-controlled, part of a highway interchange, or part of an urban junction.
roads_follow_in_junction_with_tl
roads_follow_in_junction_with_tl is a junction-crossing route that is controlled by a traffic light.
struct roads_follow_in_junction_with_tl inherits roads_follow_in_junction:
keep(self.internal_road.traffic_light_id != -1)
road_with_traffic_light: road_with_traffic_light with:
keep(it.id == self.internal_road.road_with_traffic_light_id)
roads_follow_in_junction_with_sign
roads_follow_in_junction_with_sign is a junction-crossing route that is controlled by a road sign, such as a ‘yield’ or ‘stop’ sign.
struct roads_follow_in_junction_with_sign inherits roads_follow_in_junction:
keep(self.internal_road.sign_id != -1)
road_with_sign: road_with_sign with:
keep(it.id == self.internal_road.road_with_sign_id)
roads_follow_in_highway_junction
roads_follow_in_highway_junction is a route that crosses a highway junction, meaning that both the in_road and out_road are classified as highway, highway_entry or highway_exit.
struct roads_follow_in_highway_junction inherits roads_follow_in_junction:
keep (self.internal_road.junction_kind == highway)
roads_follow_in_urban_junction
roads_follow_in_urban_junction is a route that crosses a junction that is not a highway-junction.
struct roads_follow_in_urban_junction inherits roads_follow_in_junction:
keep (self.internal_road.junction_kind == urban)
Example scenario using roads_follow_in_junction
In this example, we use roads_follow_in_junction instead of one_way_road and internal_road.
import "$FTX/env/basic/exe_platforms/model_ssp/config/model_dummy_config.osc"
import "$FTX/env/basic/msp/open_drive.osc"
extend test_config:
set map = "$FTX_PACKAGES/maps/Town04.xodr"
scenario sut.enter_junction_with_vehicle_from_left:
npc: vehicle
sut_route_over_junction: roads_follow_in_junction
npc_route_over_junction: roads_follow_in_junction
roads_enter_junction_relation(sut_route_over_junction.in_road,
npc_route_over_junction.in_road,
relative_direction: any_left)
do parallel(overlap: equal, duration: [5..10]s):
serial:
sut.car.drive() with:
along(sut_route_over_junction.in_road)
sut.car.drive() with:
along(sut_route_over_junction.internal_road)
sut.car.drive() with:
along(sut_route_over_junction.out_road, run_mode: best_effort)
serial:
npc.drive() with:
along(npc_route_over_junction.in_road)
npc.drive() with:
along(npc_route_over_junction.internal_road)
npc.drive() with:
along(npc_route_over_junction.out_road, run_mode: best_effort)
extend top.main:
do sut.enter_junction_with_vehicle_from_left()
extend top.info:
# Downgrading a collision error to info
on @top.new_issue:
call issues.curr.modify(collision_in_manual_control, info, "Downgrading collision severity to 'info'")
internal_road_with_conflict
The internal_road_with_conflict route element represents pairs of conflicting internal_road occurrences - internal_roads within the same junction that come from different incoming roads and have some geometrical overlap. Two routes that pass through a pair of conflicting internal_road occurrences pose a potential collision course.
In the following image we see two occurrences of internal_road_with_conflict, each one corresponds to an occurrence of an internal_road, and refers to the other internal_road as the conflicting internal_road.
In the following image, one arbitrary internal_road occurrence is outlined in yellow, and all the internal_road occurrences that conflict with it are outlined in blue. For each pair of yellow-outlined and blue-outlined occurrences, two occurrences of internal_road_with_conflict will be marked - one in which the yellow is the conflicting_road and one in which the blue is the conflicting_road.
OSC definition of internal_road_with_conflict
# internal_road_with_conflict represents a junction internal_road with a overlapping internal_road
# in the junction with a diffrent inbound road
#
struct internal_road_with_conflict inherits road_element:
# id of the internal_road that this internal_road_with_conflict represents
#
internal_road_id: int
# id of the conflicting internal_road
#
conflicting_road_id: int
# the conflicting internal_road
#
conflicting_road: internal_road with:
keep(it.id == conflicting_road_id)
Example using internal_road_with_conflict
In this example the SUT and NPC are driving simultaneously on conflicting internal_road occurrences that are not controlled by traffic lights, stop signs, or yield signs.
A possible result is shown in the following image. The SUT and its planned path are green, the NPC and its planned path are blue.
Example scenario using internal_road_with_conflict
import "$FTX/env/basic/exe_platforms/model_ssp/config/model_dummy_config.osc"
import "$FTX/env/basic/msp/open_drive.osc"
extend test_config:
set map = "$FTX/packages/maps/Town04.xodr"
set min_test_time = 0second
scenario sut.non_signaled_conflict:
npc: vehicle
non_signaled_internal_road: internal_road with:
# constraint this internal_road to have no sign (stop/yield) control
#
keep(it.sign_id == -1)
# constraint this internal_road to have no traffic-light control
#
keep(it.traffic_light_id == -1)
# conflicting_internal_road - 'it.conflicting_road' is an internal_road that conflicts with
# 'non_signaled_internal_road', it is also not controlled by sign or traffic-light
conflicting_internal_road: internal_road_with_conflict with:
keep(it.internal_road_id == non_signaled_internal_road.id)
keep(it.conflicting_road.sign_id == -1)
keep(it.conflicting_road.traffic_light_id == -1)
do parallel(overlap: equal, duration: [5..10]s):
serial:
sut.car.drive()
c1: sut.car.drive() with:
along(conflicting_internal_road, start_offset: 0m, at: start)
s1: speed([10..20]kph, at: start)
override(s1, run_mode: best_effort)
sut.car.drive() with:
along(conflicting_internal_road, end_offset: 0m, at: start)
serial:
npc.drive()
c2: npc.drive() with:
along(conflicting_internal_road.conflicting_road, start_offset: 0m, at: start)
s2: speed([10..20]kph, at: start)
sut.car.drive() with:
along(conflicting_internal_road, end_offset: 0m, at: start)
with: sync: synchronize(c1.start, c2.start, offset: [-2..2]s)
extend top.main:
do sut.non_signaled_conflict()