10.6 Triggers

An ASAM OpenSCENARIO 1.2.0 trigger is mapped to the boolean expression that evaluates to the equivalent logical function as the original trigger. Such boolean expression can then be used to trigger new behavior in a few different ways:

  1. Event object

  2. Wait statement

  3. Until modifier

The most direct conversion to this version of ASAM OpenSCENARIO is to create an event which has a different meaning than an ASAM OpenSCENARIO 1.2.0 Event). In this case, the expression can be named and referred to from other parts of the scenario. Conversion of an ASAM OpenSCENARIO 1.2.0 Event is possible if the action that is triggered by it is instant, meaning a duration of 0 s simulation time.

This is how it looks using an event:

Code 1. Perform logging when a condition for car1 is met, depending on the distance between car1 and the ego.
    car1: car
    do:
        car1.drive() with:
            speed(50kph)

    on car1.object_distance(ego, direction: longitudinal, mode: bounding_box) < 10 m:
        call log_info("Too near.")
Within an event handler of this version of ASAM OpenSCENARIO (on) only instant actions can be used.

If the motivation for defining an Event in ASAM OpenSCENARIO 1.2.0 is primarily to compose a sequence of events, it can be easier formulated using a serial operator combined with wait or until:

Code 2. Composing a sequence of events using a serial operator and wait
    serial:
        wait car.object_distance(ego, direction: longitudinal, mode: bounding_box) < 10 m
        car.drive() with:
            speed(0kph, at:end)
Code 3. Composing a sequence of events using a serial operator and until
    serial:
        car.drive() with:
            speed(50kph)
            until(car.object_distance(ego, direction: longitudinal, mode: bounding_box) < 10 m)
        car.drive() with:
            speed(0kph, at:end)

Here, as soon as the condition is met, we are free to use actions with non-zero duration.

10.6.1 Condition

Condition has the following attributes:

  • name

  • delay
    Can be mapped with wait.

  • conditionEdge
    Can be mapped with the following constructs:

    • rise

    • fall

10.6.1.1 ParameterCondition

ParameterCondition works in conjunction with ParameterAction and allows for checking of parameter values. The same functionality can be achieved using the variable fields, like counter.

Code 4. ParameterCondition
actor my_car:
    var counter: int = 0

on object_distance(ego, direction: longitudinal, mode: bounding_box) < 10 m:
    call increment_counter()

    do serial:
        my_car.drive() with:
            speed([40kph..60kph])
            until(my_car.counter >= 3)
        my_car.reach_speed(0kph)
In ASAM OpenSCENARIO 1.2.0 all runtime variables are of type string, but comparison and rules allow for comparison to a numeric value.

10.6.1.2 TimeOfDayCondition

environment.datetime >= environment.local_to_unix_time(year: 2021, month: 12, day: 17, hour: 18 minute: 59, second: 22)

Date and time in the scenario are greater than given local time. In this version of ASAM OpenSCENARIO this condition can be used for a static check of the date and time. Value of environment.datetime is not updated during the scenario run, so it cannot trigger the dynamic behavior.

10.6.1.3 UserDefinedValueCondition

This condition allows for creating triggers based on values provided by the simulation environment. Such values are accessible in this version of ASAM OpenSCENARIO using external methods.

scenario dut.test_drive:
    def external_value() -> float is external cpp("my_new_kpi.cpp")

    serial:
        wait external_value() > 4.0
        drive() with:
            speed(0kph, at:end)

10.6.1.4 StoryboardElementStateCondition

Scenarios and actions in this version of ASAM OpenSCENARIO provide the default .start and .end events. StoryBoardElementStateCondition can be mapped to the start and end events of an appropriate scenario. Simple composition of scenario elements, especially when performed by the same actor, is more easily expressed with serial and parallel temporal operators.

scenario storyboard_element_state:

    ego: car
    car_1: car
    car_2: car

    do parallel:
            ego.drive() with:
                lane(left_of: car_1)
                speed([40kph..80kph])
            maneuver_group_1: car_1.drive() with:
                    position([30m..50m], ahead_of: ego)
                    lane(same_as: ego, at: end)
            serial:
                car_2.drive() until (@maneuver_group_1.end)
                car_2.reach_speed(0kph)

10.6.2 ByEntityConditions

ByEntityCondition defines a condition, which entities can trigger the evaluation of this condition as well as a TriggeringEntitiesRule that specifies if all listed entities must fulfill the condition or whether just one entity is sufficient to fulfill the condition.

10.6.2.1 CollisionCondition

Condition that becomes true when an entity is in collision with the specified TriggeringEntities.

    var speed_on_crash: speed = 0

    on ego.time_to_collision(car_1) == 0s or ego.time_to_collision(car_2) == 0s:
        speed_on_crash = sample(ego.speed)

10.6.2.2 TimeHeadwayCondition

    var low_headway: bool = false

    do serial:
        car_1.drive() with:
            speed([80kph..100kph])
            until(low_headway == true)
        car_1.slow_down()

    on car_1.time_headway(car_2) < 2s:
        record(name: 'headway', expression: car_1.time_headway(car_2), unit: s, text: 'Low headway time')
        call set_headway(true)

Emitted when time headway between car_1 and car_2 becomes less than 2 seconds. Type of distance calculation for headway is the distance between the reference points.

10.6.2.3 TimeToCollisionCondition

    do serial:
        car_1.drive() with:
            speed([80kph..120kph])
            until car_1.time_to_collision(car_2) < 3s:
        car_1.reach_speed(0kph)

Emitted when the time to collision (based on the current motion of entities) between car_1 and car_2 becomes less than 3 seconds. The distance used for calculating the time to collision of the two vehicles is the distance between their bounding boxes (freespace parameter).

10.6.2.4 AccelerationCondition

    do serial(30s):
        leading_vehicle.drive() with:
            speed(0kph, at:start)
            speed(200kph, at:end)
        leading_vehicle.drive() with:
            speed([120kph..150kph], at:end)

    on leading_vehicle.acceleration.translational.norm() > 2mpsps:
        record(leading_vehicle.speed,
            unit: kph,
            text: 'High acceleration at speed')

Emitted when the acceleration of leading_vehicle vehicle becomes greater than 2 m/s^2.

10.6.2.5 SpeedCondition

    do parallel:
        serial:
            car_1.drive() with:
                speed([40kph..60kph])
                lane(same_as: car_2)
                until(car_2.speed < 30kph)
            car_1.drive() with:
                speed(30kph)
                lane(1, left_of: car_2)
        car_2.drive() with:
            speed([25kph..50kph])

Emitted when the speed of car_2 becomes less than 30 kph.

10.6.2.6 RelativeSpeedCondition

    def substract_velocity(v1: translational_velocity_3d, v2: translational_velocity_3d) -> translational_velocity_3d is expression translational_velocity_3d (x: v1.x - v2.x, y: v1.y - v2.y, z: v1.z - v2.z)

    do parallel:
        serial:
            car_1.drive() with:
                speed([40kph..60kph])
                until(substract_velocity(car_1.velocity.translational, car_2.velocity.translational).norm() > 10kph)
            car_1.drive() with:
                speed(car_2.speed, at:end)
        car_2.drive() with:
            speed([25kph..50kph])

Emitted when the relative speed between car_1 and car_2 becomes larger than 10 kph.

10.6.2.7 ReachPositionCondition and DistanceCondition

scenario stop_near_point:

    def substract_position(p1: position_3d, p2: position_3d) -> position_3d is expression position_3d(x: p1.x - p2.x, y: p1.y - p2.y, z: p1.z - p2.z)

    car_1: vehicle
    keep(car_1.vehicle_category == car)
    pos_1: position_3d

    do serial:
        car_1.drive() with:
            speed([40kph..60kph])
            until(substract_position(car_1.pose.position, pos_1).norm() < 5m)
        car_1.drive() with:
            speed(0kph, at:end)

Both conditions can be converted in a similar manner:

  • DistanceCondition
    Is more general and can express greater than or equal distance to the determined position.

  • ReachPosition
    Tolerance is interpreted as less than.

10.6.2.8 RelativeDistanceCondition

scenario move_away:
    car_1: car
    car_2: car

    do parallel:
        serial:
            car_1.drive() with:
                speed([40kph..60kph])
                lane(1, left_of: car_2)
                until(ego.object_distance(car_2, mode: bounding_boxes, direction: lateral) < 1m)
            car_1.drive() with:
                lane(2, left_of: car_2)
        serial(duration: 20s):
            car_2.drive() with:
                speed([-5, 10]kph, faster_than: car_1)
            car_2.drive() with:
                lateral(distance: 0.75m, right_of: car_1, measure_by: right_to_left, at:end)
            car_2.drive() with:
                lateral(distance: 0m, measure_by: center_to_center, at:end)

The lateral distance of car_1 to car_2 becomes less than 1 m, measured between bounding boxes (freespace). If Cartesian distance is required, a conversion similar to DistanceCondition can be used.