Highway elements
highway, highway_entry and highway_exit
The highway, highway_entry, and highway_exit route elements classify occurrences of one_way_roads. This means that the route associated with a highway occurrence is the same as that of a one_way_road occurrence, and the same applies to highway_entry and highway_exit occurrences.
- The
highwayelement represents a section of a highway between two interchanges. - The
highway_entryelement represents a highway on-ramp. - The
highway_exitelement represents a highway off-ramp.
Foretify detects these highway elements based on the topology and geometry of road intersections and interchanges, as well as speed limits. Detection does not rely on any explicit classification in the source map.
The image below shows highway occurrences, highlighted in pink, a highway_entry highlighted in blue, and a highway_exit highlighted in yellow.
OSC definition of highway, highway_entry, and highway_exit
Since these three elements refer to the one_way_road they classify, they all inherit from one base struct called highway_base, which holds the ID of the associated one_way_road. There are no occurrences of highway_base itself, only of the derived types highway, highway_entry, and highway_exit.
# highway_base - a base type for highway elements 'highway', 'highway_entry', and 'highway_exit'.
# A highway_base element covers exactly (is a classification of) a one_way_road.
# There are *no* actual occurrences of this type, only of its sub-types
#
struct highway_base inherits road_element:
one_way_road_id: int
# highway - a highway 'one_way_road'
#
struct highway inherits highway_base:
# The minimum driving speed limit along the highway
#
legal_speed:speed
# The minimum number of driving lanes along the highway
#
lanes:int
# highway_entry - a highway_entry 'one_way_road'
#
struct highway_entry inherits highway_base
# highway_entry - a highway_entry 'one_way_road'
#
struct highway_exit inherits highway_base
Example using highway, highway_entry, and highway_exit
In the following scenario, the NPC enters the highway that the SUT is driving on and then the SUT exits the same highway, as illustrated in the following image. In this example, we use the route elements highway, highway_entry, and highway_exit.
The SUT and its planned path are green, the NPC and its planned path are blue:
Example scenario using highway, highway_entry, and highway_exit
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/highway.xodr"
set min_test_time = 0second
scenario sut.highway_drive:
npc: vehicle
# Elements 'highway1', 'highway2' and 'highway3' below compose a highway route along which there are:
# - A highway on-ramp 'on_ramp' connecting to 'highway2'.
# - A highway off-ramp 'off_ramp' exiting from the 'highway2'.
#
highway1: highway
highway2: highway
# connection1 - internal_road connecting highway1 and highway2
#
connection1: internal_road with:
keep(it.in_one_way_road_id == highway1.one_way_road_id)
keep(it.out_one_way_road_id == highway2.one_way_road_id)
highway3: highway
# connection2 - internal_road connecting highway2 and highway3
#
connection2: internal_road with:
keep(it.in_one_way_road_id == highway2.one_way_road_id)
keep(it.out_one_way_road_id == highway3.one_way_road_id)
on_ramp: highway_entry
# connection3 - internal_road connecting on_ramp and highway2
#
connection3: internal_road with:
keep(it.in_one_way_road_id == on_ramp.one_way_road_id)
keep(it.out_one_way_road_id == highway2.one_way_road_id)
off_ramp: highway_exit
# connection4 - internal_road connecting highway2 and off_ramp
#
connection4: internal_road with:
keep(it.in_one_way_road_id == highway2.one_way_road_id)
keep(it.out_one_way_road_id == off_ramp.one_way_road_id)
do parallel(overlap: equal, duration: [10..20]s):
serial:
sut.car.drive() with:
along(highway1, at: start)
sut.car.drive()
sut.car.drive() with:
along(off_ramp, at: end, run_mode: best_effort)
serial:
npc.drive() with:
along(on_ramp, at: start)
npc.drive()
npc.drive() with:
along(highway3, at: end, run_mode: best_effort)
extend top.main:
do sut.highway_drive()
merge_from_entry and merge_from_highway structs
In the previous example, we used constraints to define the drive routes:
-
For SUT:
highway1,connection1,highway2,connection4,off_ramp -
For NPC:
on_ramp,connection3,highway2,connection2,highway3
Since it is common for vehicles to drive from one highway_entry occurrence to the next highway, or from a highway occurrence to the next highway, the two structs merge_from_entry and merge_from_highway are used to wrap the relevant elements and constraints.
OSC definition of merge_from_entry and merge_from_highway structs
struct merge_from_entry:
internal_road: internal_road with:
keep(it.junction_kind == highway)
in_road: highway_entry with:
keep(it.one_way_road_id == internal_road.in_one_way_road_id)
out_road: highway with:
keep(it.one_way_road_id == internal_road.out_one_way_road_id)
struct merge_from_highway:
internal_road: internal_road with:
keep(it.junction_kind == highway)
in_road: highway with:
keep(it.one_way_road_id == internal_road.in_one_way_road_id)
out_road: highway with:
keep(it.one_way_road_id == internal_road.out_one_way_road_id)
Example scenario using merge_from_entry and merge_from_highway
The following scenario is the same as the previous scenario, using merge_from_entry and merge_from_highway structs. The NPC enters the highway that the SUT is driving on and then the SUT exits the same highway.
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/highway.xodr"
set min_test_time = 0second
scenario sut.highway_drive:
npc: vehicle
# ramp_to_hw - a highway_entry connected via an internal_road to a highway.
#
ramp_to_hw: merge_from_entry
# hw_to_hw1 - two connected highway elements. The second one is constrained to be the same as
# the highway of 'ramp_to_hw'.
#
hw_to_hw1: merge_from_highway with:
keep(it.out_road == ramp_to_hw.out_road)
# hw_to_hw2- wo connected highway elements. The first one is the second highway of 'hw_to_hw1'.
#
hw_to_hw2: merge_from_highway with:
keep(it.in_road == hw_to_hw1.out_road)
off_ramp: highway_exit
# connection4 - internal_road connecting highway2 and off_ramp
#
connection4: internal_road with:
keep(it.in_one_way_road_id == hw_to_hw1.out_road.one_way_road_id)
keep(it.out_one_way_road_id == off_ramp.one_way_road_id)
do parallel(overlap: equal, duration: [10..20]s):
serial:
sut.car.drive() with:
along(hw_to_hw1.in_road, at: start)
sut.car.drive()
sut.car.drive() with:
along(off_ramp, at: end, run_mode: best_effort)
serial:
npc.drive() with:
along(ramp_to_hw.in_road, at: start)
npc.drive()
npc.drive() with:
along(hw_to_hw2.out_road, at: end, run_mode: best_effort)
extend top.main:
do sut.highway_drive()
highway_acceleration_lane
The highway_acceleration_lane route element represents an acceleration lane for merging in highway traffic when entering from a highway on-ramp.
Formally the acceleration_lane is one of the side-most lanes of a highway route element occurrence that meets the following criteria:
- It has a preceding lane in an incoming highway_entry occurrence.
- It does not have a preceding lane in an incoming highway occurrence.
- It has no subsequent lane and gets narrower towards its end.
- It is shorter than
config.map.highway_acceleration_lane_max_length, which is 1000 meters by default.
Two longitudinal offsets are marked on the acceleration lane:
-
start_merge_offset- This is a forward offset from the beginning of the occurrence, where the width of the acceleration lane starts getting narrower, encouraging vehicles to merge into the highway. -
force_merge_offset_from_end- This is a backward offset from the end of the occurrence to the place where the acceleration lane width is too narrow and forces vehicles to merge. This value is configurable viaconfig.map.force_merge_width_threshold, and is 2.7 meters by default.
In the image below, highway_entry occurrences are highlighted in yellow, highway occurrences are highlighted in red, and highway_acceleration_lane occurrences are highlighted in blue, but seem purple in the image because every highway_acceleration_lane occurrence (blue) overlaps a highway occurrence (red). The start_merge_offset and force_merge_offset_from_end are demonstrated for one of the highway_acceleration_lane occurrences.
OSC definition of highway_acceleration_lane
# highway_acceleration_lane - acceleration lane at the beginning of a highway element
# Please pay attention that highway_acceleration_lane might cover multiple instances of highway
# (for example, in case of merges from both sides of the road)
#
struct highway_acceleration_lane inherits road_element:
# id of the one_way_road that overlaps with the start of the acceleration lane
#
start_one_way_road_id: int
# id of the one_way_road that overlaps with the end of the acceleration lane
#
end_one_way_road_id: int
# Distance from the start of the acceleration lane to where the width of the lane starts shrinking
#
start_merge_offset: length
# Distance from the end of the acceleration lane to where the width of the lane is below the
# configured minimum (configured in top.config.map.force_merge_width_threshold)
#
force_merge_offset_from_end: length
Example using highway_acceleration_lane
In this example the SUT merges onto the highway from an acceleration lane while being challenged by a NPC driving in the lane it is merging into. A possible result is illustrated in the following image. The SUT and its planned path are green, the NPC and its planned path are blue.
Example scenario using highway_acceleration_lane
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/highway_2.xodr"
scenario sut.merge_from_acceleration_lane:
npc: vehicle
acceleration_lane: highway_acceleration_lane
hw: highway
do parallel(overlap: equal, duration: [5..20]s):
serial:
# drive along both 'hw' and 'acceleration_lane' - this implicitly constrains
# 'acceleration_lane' to be on 'hw'.
#
sut.car.drive() with:
along(acceleration_lane, start_offset: 0m, at: start)
# implicitly constraint acceleration_lane on hw
along(hw, at: start)
# drive past the end of he acceleration lane
along(hw, start_offset: [acceleration_lane.length..hw.length], at: end)
serial:
npc.drive() with:
along(hw, start_offset: 0m, at: start)
lane(1, left_of: sut.car, at: start)
extend top.main:
do sut.merge_from_acceleration_lane()