119. Introduction to the new scenario-based development methodology (SDM)
Foretellix has introduced a new scenario-based development methodology (SDM), designed to make the scenario library more modular, reusable, scalable and easier to maintain. The new approach includes updated file naming conventions, and a consistent structure for defining scenario logic using intent modifiers and clearly defined phases.
The transition to SDM is in progress and will be implemented across all scenarios.
119.1 Scenario directory structure
The overall scenario directory structure remains unchanged with one key addition. An "_h" suffix is added to scenario files to indicate that they serve as headers for the respective scenarios. These header files define the scenario’s user API, including key parameters and member declarations. Adding the "_h" suffix helps organize the scenario components clearly and makes the structure easier to manage and reuse across the scenario library.
For example, the file vehicle_cut_in_base_h.osc serves as the header for the vehicle_cut_in_base scenario family, and the file vehicle_cut_in_h.osc serves as the header for the vehicle_cut_in scenario.
scenarios
vehicle_cut_in_base
vehicle_cut_in_base_cover.osc
vehicle_cut_in_base_h.osc # This is the scenario family header file
vehicle_cut_in_base_imp.osc
vehicle_cut_in_base_top.osc
vehicle_cut_in
vehicle_cut_in_cover.osc
vehicle_cut_in_h.osc # This is the scenario header file
vehicle_cut_in_imp.osc
vehicle_cut_in_top.osc
119.2 Key concepts in the new scenario structure
To build or update scenarios, understanding these core concepts is essential. To help you get started, here's a quick overview of the key building blocks so you can follow the examples and understand the structure confidently.
-
Scenario phases are used to structure simulations into distinct stages — preparing the Ego vehicle and environment, performing the main driving behavior being tested, and evaluating how the system responds and performs afterwards.
-
Modifiers are used to control behavior during these phases.
-
Naming format conventions are used to help you quickly identify phases and modifiers, and understand what actions and maneuvers are taking place.
119.2.1 Scenario Phases
Scenarios are organized into three main phases, that can be easily identified by the specific phase naming convention:
phase_ego_warm_up– prepares the Ego vehicle before the core scenario beginsphase_essence– contains the core scenario behavior or maneuver being testedphase_post– allows for post-scenario reactions and performance evaluation
If more than one phase is required during the essence phase, the following naming format is used for each sub-phase: phase_essence_<sub_phase_name>.
For example:
In the Aborted cut-in scenario, there are two essence sub-phases:
phase_essence_lane_changewhere the cut-in vehicle performs a cut-in maneuver into the Ego's lane from an adjacent lane.phase_essence_abortwhere the cut_in_vehicle aborts the maneuver and cuts out of the Ego's lane back into the original adjacent lane.
In the Ego drive-by parked vehicles, there are also two phases:
phase_essence_ego_drive_with_parked_vehicleswhere the Ego drives alongside a group of parked vehicles.phase_essence_ego_drive_by_the_parked_vehicleswhere the Ego continues driving until it has passed the parked vehicles.
119.2.2 Behavior in phases
Each actor’s behavior in a phase is defined using drive() in the following naming format: drive_<vehicle_name>_at_<phase_name>.
This convention ensures every drive action is clearly associated with both the vehicle and the scenario phase.
This convention ensures every drive action is clearly associated with both the vehicle and the scenario phase. For example:
drive_ego_at_essenceis for the Ego's behavior during the essence phase.drive_cut_in_vehicle_at_ego_warm_upis for the cut-in vehicle's behavior during the ego_warm_up phase.drive_cut_in_vehicle_at_postis for the cut-in vehicles behavor during the post phase
119.2.3 Modifiers
A scenario intent defines the primary objective or situation being tested. It shapes how the scenario is configured, including actor placement, simulated conditions, and the actions that occur.
For more details, see Scenario intent modifiers.
Note
The prefix 'gen_' which stands for 'generated' in any parameter indicates that the parameter is automatically generated, not manually set.
119.3 Phases deep-dive
Now that you're familiar with phases and modifiers, and their naming conventions, let's explore how they work together and are applied in each phase of a scenario.
119.3.1 phase_ego_warm_up
The Ego warm-up phase is the first phase of a scenario. It ensures that all systems are given enough time to warm-up and initialize their driving systems before encountering dynamic or challenging environments. This reduces the risk of failure due to system faults or incomplete initialization.
Only the keep_lane() maneuver is allowed (with some exceptions), meaning vehicles stay in their current lane and do not perform any behaviors such as lane changes, overtaking, or sudden acceleration. Excluding sudden or complex maneuvers keeps the simulation environment remains predictable and stable, reducing the risk of unsafe situations (e.g., collisions, loss of control) stemming from system or vehicle states that are not yet fully initialized.
In the following example warm-up phase, both the Ego and the cut-in vehicle each have a drive() defined with intent-assist modifier keep_lane.
do phase_ego_warm_up: parallel(overlap: equal):
drive_ego_at_ego_warm_up: sut.car.drive() with:
intent_assist_ego_keep_lane_at_ego_warm_up: keep_lane(run_mode: best_effort)
drive_cut_in_vehicle_at_ego_warm_up: cut_in_vehicle.drive() with:
intent_assist_cut_in_vehicle_keep_lane_at_ego_warm_up: keep_lane(run_mode: best_effort)
119.3.2 phase_essence
The essence phase defines the positioning and primary maneuvers of all actors to create realistic scenarios that match real-world driving conditions. For example, in a cut-in scenario, this is when the cut-in maneuver happens.
Typically and ideally, this phase implements the scenario in a single drive that uses modifiers at its start and end. However, it may require multiple sub-phases.
Example
In a single drive() for each vehicle in a cut-in scenario:
-
At the start of the drive the Ego is expected to drive in the same lane at a constant speed. This is modeled in the essence phase with a
drive()for the Ego with intent assist modifierskeep_lane()andspeed():drive_ego_at_essence: sut.car.drive() with: intent_assist_ego_keep_lane_at_essence: keep_lane(run_mode: best_effort) intent_assist_ego_speed_at_essence: speed(gen_ego_speed_at_start, run_mode: best_effort) -
The cut_in_vehicle is expected to be in the lane next to the Ego at the start of scenario, and to be in front of the Ego in the same lane at the end of the scenario. This is modeled with a
drive()for the cut_in_vehicle with intent modifierslane()andposition()at the start and at the end of the essence phase.# cut-in vehicle performs cut-in drive_cut_in_vehicle_at_essence: cut_in_vehicle.drive() with: # at: start cut-in vehicle must get to the initial lane() and position() # it tries to get to the targeted relative speed and lateral position intent_cut_in_vehicle_side_at_essence_start:\ lane(side_of: sut.car, side: gen_cut_in_vehicle_side_at_essence_start, at: start) intent_cut_in_vehicle_time_gap_at_essence_start:\ position(time: gen_cut_in_vehicle_time_gap_at_essence_start, time_tolerance: gen_cut_in_vehicle_time_gap_at_essence_start_tolerance, ahead_of: sut.car, measure_by: nearest, at: start) intent_assist_cut_in_vehicle_rel_speed_at_essence_start:\ speed(gen_cut_in_vehicle_rel_speed_at_essence_start, faster_than: sut.car, at: start, run_mode: best_effort) control_cut_in_vehicle_lateral_at_essence_start:\ lateral(gen_cut_in_vehicle_lateral_at_essence_start, line: center, at: start, run_mode: best_effort) # at: end cut-in vehicle must get to the final lane() and position() # it tries to get to the targeted relative speed and lateral position intent_cut_in_vehicle_lane_same_as_ego_at_essence_end:\ lane(same_as: sut.car, at: end) intent_cut_in_vehicle_time_gap_at_essence_end:\ position(time: gen_cut_in_vehicle_time_gap_at_essence_end, time_tolerance: gen_cut_in_vehicle_time_gap_at_essence_end_tolerance, ahead_of: sut.car, measure_by: nearest, track: projected, at: end) intent_assist_cut_in_vehicle_rel_speed_at_essence_end:\ speed(gen_cut_in_vehicle_rel_speed_at_essence_end, faster_than: sut.car ,at: end, run_mode: best_effort) control_cut_in_vehicle_lateral_at_essence_end:\ lateral(gen_cut_in_vehicle_lateral_at_essence_end, line: center, at: end, run_mode: best_effort)
119.3.3 phase_post
The post phase defines how the Ego and other actors behave after the essence maneuvers. The main goal of this phase is to allow some time after the essence phase for the Ego to complete it's autonomous response and to define how the other actors in the scenario should behave after the essence of the scenario is completed.
The following cut-in vehicle scenario example shows the post phase drive() of the Ego and the cut-in vehicle, where they both have intent-assist modifiers to keep_lane in best effort mode.
do phase_post: parallel(overlap: equal):
drive_ego_at_post: sut.car.drive() with:
intent_assist_ego_keep_lane_at_post: keep_lane(run_mode: best_effort)
drive_cut_in_vehicle_at_post: cut_in_vehicle.drive() with:
intent_assist_cut_in_vehicle_keep_lane_at_post: keep_lane(run_mode: best_effort)
119.4 Scenario intent modifiers
A scenario intent defines the primary objective or situation being tested. The scenario intent shapes how the test is set up, including the placement of actors, the simulated conditions, and the actions that occur during the test.
To evaluate the scenario intent, specific behaviors are simulated. Modifiers are applied in different phases to control and ensure accuracy of these behaviors.
Modifiers are used to achieve the scenario intent:
intent– strict requirements for non-Ego actorsintent_assist– guidance for actors' behavior, often using best_effortcontrol– offer added flexibility by addressing coverage gaps and enabling fine-tuned scenario adjustments
These modifiers provide additional flexibility and control over the scenario, to enable:
Filling Known Coverage Gaps: Addressing any specific coverage areas that remain untested after intent and intent assist modifiers have been applied.
Additional Control: Offering finer adjustments to ensure the scenario runs smoothly and as intended under more nuanced conditions.
To ensure flexibility, control vehicle modifiers are implemented with run_mode: best_effort. This setting allows these modifiers to act as suggestions rather than strict requirements.
Each modifier is labeled using a consistent naming pattern:
<intent | intent_assist | control>_<actor>_<action>_at_<phase>[_start | _end]
For example:
- The
intent_assist_ego_keep_lane_at_ego_warm_upmodifier defines the Ego's behavior (keep_lane) using an intent-assist modifier during the ego_warm_up phase. - The
intent_cut_in_vehicle_side_at_essence_startmodifier defines the required side from which the cut-in vehicle will cut in during the essence phase.
119.4.1 intent modifier
The intent modifier defines the exact actions that each actor (not the Ego) must perform exactly as defined, without any flexibility. If a vehicle doesn't follow these actions strictly, the scenario is considered incomplete.
The following example ensures that the cut-in vehicle ends up in the same lane as the Ego by the end of the essence phase.
intent_cut_in_vehicle_time_gap_to_ego_at_essence_start:\
position(time: gen_ego_time_gap_to_cut_in_vehicle_at_change_lane_start,
ahead_of: sut.car, measure_by: nearest, at: start,
time_tolerance: gen_cut_in_vehicle_ahead_of_ego_tolerance)
119.4.2 intent-assist modifier
The intent_assist modifier provides guidance about how a vehicle should behave to achieve its intended behavior in a scenario.
The intent_assist modifier is used because we have no control over what it does. For the Ego, this modifier helps the planner to create a reliable plan, based on the expected behavior of the Ego. For non-Ego vehicles, it supports their ability to achieve predefined intents within the scenario.
These modifiers use run_mode: best_effort to allow the vehicles to make decisions autonomously while aiming to follow the intended behavior.
The following examples are from a cut-in scenario where a vehicle must be driving at sufficient speed to be able to pass the Ego, and perform a cut-in maneuver ahead of the Ego.
-
The Ego is expected to drive in the same lane at a constant speed. The two
intent_assistmodifiers,keep_laneandspeed, define this.drive_ego_at_essence: sut.car.drive() with: intent_assist_ego_keep_lane_at_essence: keep_lane(run_mode: best_effort) intent_assist_ego_speed_at_essence: speed(gen_ego_speed_at_start, run_mode: best_effort) -
For the cut-in vehicle, the
intentmodifiers define its initiallane()andposition()at the start of the scenario.-
The
intent_assist_cut_in_vehicle_rel_speed_at_essence_startmodifier makes it possible for the cut-in vehicle to satisfy the intent by ensuring the cut-in vehicle moves at the right speed relative to the Ego, so it can reach the required lane and position on time. -
If the cut-in vehicle speed is too slow, or in the wrong position, the cut-in will not be able to happen.
drive_cut_in_vehicle: cut_in_vehicle.drive() with: intent_cut_in_vehicle_side_at_essence_start:\ lane(side_of: sut.car, side: gen_cut_in_vehicle_side_at_essence_start, at: start) intent_cut_in_vehicle_time_gap_at_essence_start:\ position(time: gen_cut_in_vehicle_time_gap_at_essence_start, time_tolerance: gen_cut_in_vehicle_time_gap_at_essence_start_tolerance, ahead_of: sut.car, measure_by: nearest, at: start) intent_assist_cut_in_vehicle_rel_speed_at_essence_start:\ speed(gen_cut_in_vehicle_rel_speed_at_essence_start, faster_than: sut.car, at: start, run_mode: best_effort) -
119.4.3 control modifier
The control modifier provides additional flexibility and fine-tuning for scenario behavior, especially for test coverage or adjustments.
In the example below, the control modifier refines the vehicle's behavior by defining lateral movement using a generated parameter, ensuring alignment with the center line and activation at the start of the essence phase.
phase_essence: parallel(overlap: equal):
drive_ego: sut.car.drive() with:
control_cut_in_vehicle_lateral_at_essence_start: lateral(gen_cut_in_vehicle_lateral_at_essence_start, line: center, at: start, run_mode: best_effort)
119.4.4 Intent modifier names
To simplify identification, each intent modifier is labeled with a prefix - intent, intent_assist or control - followed by the actor’s name and the specific action. This labelling makes it easy to identify and organize modifiers.
The label format is: <intent | intent_assist | control>_<vehicle_name>_<modifier_name>_at_<phase_name>[_start | _end]
Examples: intent_assist_ego_keep_lane_at_essence, intent_cut_in_vehicle_time_gap_at_essence_start, intent_assist_cut_in_vehicle_rel_speed_at_essence_end, intent_cut_in_vehicle_side_at_essence, intent_assist_ego_speed_at_essence, control_cut_in_vehicle_lateral_at_essence.
119.4.4.1 Fine-tuning behavior with modifier types
Intent modifiers define the overall actions and behaviors of actors. The following modifiers provide the detailed adjustments needed to fine-tune those behaviors and ensure they’re executed accurately across the different phases of the scenario.
-
rel_speed: Defines speed in relation to another vehicle, such as faster or slower than the Ego. This helps simulate more realistic driving behaviors like overtaking, merging, or following.Example
intent_assist_cut_in_vehicle_rel_speed_at_essence_end: <faster|slower_than>:..- Sets the cut-in vehicle's relative speed at the end of the essence phase. -
lane_same_as: Aligns a vehicle with the Ego’s lane, which is useful in merging or overtaking maneuvers.Example
intent_cut_in_vehicle_lane_same_as_at_essence_end- Ensures the cut-in vehicle is in the same lane as the Ego at the end of the essence phase. -
keep_lane: Ensures the Ego stays in the same lane, which is especially useful in scenarios involves lane merges or cut-ins.Example
intent_assist_ego_keep_lane_at_post- Ensures the Ego stays in its lane during the post phase. -
time_gap: Used with theposition()modifier to set a vehicle’s position based on time instead of distance. It ensures that a following vehicle maintains a specific time gap (e.g., 2 seconds) behind another vehicle, rather than following a fixed distance. This is especially useful for safety-critical scenarios, such as car-following or cut-in tests.Example
intent_cut_in_vehicle_time_gap_at_essence_start: time..- Sets the required time gap between the cut-in vehicle and the Ego at the start of the essence phase.
119.5 Scenario test
Every scenario has a corresponding <scenario>_main that includes the ego_warm_up, essence and post phases. For example:
Example
extend sut.vehicle_cut_in_main:
do phase_main: serial():
phase_ego_warm_up: vehicle_cut_in_family_base_ego_warm_up(cut_in_vehicle: cut_in_vehicle)
phase_essence: vehicle_cut_in(cut_in_vehicle: cut_in_vehicle)
phase_post: vehicle_cut_in_family_base_post(cut_in_vehicle: cut_in_vehicle)
You can invoke a test by running do _main. For example:
extend top.main:
do vehicle_cut_in_main_random: serial:
vehicle_cut_in_main: sut.vehicle_cut_in_main()
For more advanced usage, you can combine it with other scenarios. However, only the pre-defined phases will executed.