10.7 Actions

ASAM OpenSCENARIO 1.2.0 actions can be represented in and converted to this version of ASAM OpenSCENARIO in two ways.

  1. drive() with modifiers.

  2. Specialized action, defined in the domain model of this version of ASAM OpenSCENARIO.

Both approaches are valid for converting ASAM OpenSCENARIO 1.2.0 scenarios. There are some exceptions to this approach. Where applicable, these are detailed in the specific section of the action and in Section 10.8, "Elements from 1.2.0 not yet covered".

In the scope of the conversion of ASAM OpenSCENARIO 1.2.0 scenarios, for most actions both options can be used. Exceptions are listed through the section text and in the appendix.

Using drive() can in some cases result in a scenario specification that is less constrained than when using the equivalent specialized action. Depending on the use case you might start with specialized action conversion and move to drive variant as you move the scenario up the abstraction levels.

10.7.1 Action duration

General instructions for handling ASAM OpenSCENARIO 1.2.0 actions with regards to action duration.

  • Durable/Continuous

  • Instantaneous

Actions with the flag continuous can be represented using two drive actions:

  1. A drive action to reach the required condition.

  2. A request to continue driving with a condition satisfied from that moment until the next action on the same control axis (longitudinal/lateral) cancels it.

Specialized actions where the duration is relevant have the duration argument. The argument specifies the time in which the action needs to complete. In contrast to ASAM OpenSCENARIO 1.2.0 there is no direct way to specify that the action is to complete within specific covered distance (distance dynamics specification for SpeedChange and LaneChange actions).

10.7.2 Private actions

Private actions are executed by a list of actors that are specified in the parent ManeuverGroup. If multiple actors are executing the same action, an action is considered finished when the last actor finishes executing it.

10.7.2.1 SpeedAction

Example 1. Setting the initial speed
Code 1. ASAM OpenSCENARIO 1.2.0
<Private entityRef="Ego">
    <PrivateAction>
        <LongitudinalAction>
            <SpeedAction>
                <SpeedActionDynamics dynamicsShape="step" dynamicsDimension="time" value="0" />
                <SpeedActionTarget>
                    <AbsoluteTargetSpeed value="$Ego_InitSpeed_Ve0" />
                </SpeedActionTarget>
             </SpeedAction>
        </LongitudinalAction>
    </PrivateAction>
</Private>
Code 2. ASAM OpenSCENARIO 2.0.0
# Drive

ego.drive() with: along(s_ego); speed(ego_init_speed_ve0)

# Specialized action

ego.change_speed(target: target_ego_init_speed_ve0, rate_profile: asap)
Code 3. ASAM OpenSCENARIO 2.0.0
# Drive

do serial:
    car1.drive() with: speed(50kph)
    car1.drive() with: speed(100kph, at:end)

# Specialized action

do serial:
    car1.reach_speed(50kph, as_soon_as_possible)
    car1.reach_speed(100kph, cubic_accel)

10.7.2.2 LongitudinalDistanceAction and LateralDistanceAction

Example 2. continuous:`false`, freespace:`true`
Code 4. ASAM OpenSCENARIO 1.2.0
<ManeuverGroup maximumExecutionCount="1" name="Group 1">
    <Actors selectTriggeringEntities="false">
        <EntityRef entityRef="Car 1"/>
    </Actors>
    <Maneuver name="Maneuver 1">
        <ParameterDeclarations/>
        <Event priority="overwrite" maximumExecutionCount="1" name="Event 1">
            <Action name="Longitudinal Distance">
                <PrivateAction>
                    <LongitudinalAction>
                        <LongitudinalDistanceAction freespace="true" continuous="false" entityRef="Ego" distance="20.0">
                            <DynamicConstraints maxAcceleration="0.0" maxDeceleration="0.0" maxSpeed="0.0"/>
                        </LongitudinalDistanceAction>
                    </LongitudinalAction>

                </PrivateAction>
            </Action>
            <StartTrigger>
                <ConditionGroup>
                    <Condition delay="0.0" conditionEdge="none" name="Simulation Time">
                        <ByValueCondition>
                            <SimulationTimeCondition rule="equalTo" value="2.0"/>
                        </ByValueCondition>
                    </Condition>
                </ConditionGroup>
            </StartTrigger>
        </Event>
    </Maneuver>
  </ManeuverGroup>
...
Code 5. ASAM OpenSCENARIO 2.0.0
serial:
    parallel(duration: 2s):
        ego.drive()
        car1.drive() with:
            position(behind: ego, 50m)

    parallel:
        ego.drive()
        car1.drive() with:
            position(at:end, behind: ego, 20m)

Alternatively, for LongitudinalDistanceAction and LateralDistanceAction, the specialized actions change_space_gap and keep_space_gap can be used, with specification ahead/behind or left/right (see Code 6). For LateralDistanceAction use values left_of and right_of for the direction argument of position modifier.

In this case distance is calculated along a specified axis, which corresponds to the ASAM OpenSCENARIO 1.2.0 freespace: true variant.
Code 6. Use of specialized actions to change longitudinal and lateral distances.
serial:
    parallel:
    # Reach specified distance
        car1.change_space_gap(target: 20m, direction: behind, reference: ego)
        car2.change_space_gap(target: 3m, direction: left, reference: ego)
    parallel:
    # Keep the attained distance
        car1.keep_space_gap(direction: longitudinal, reference: ego)
        car2.keep_space_gap(direction: lateral, reference: ego)
Code 7. ASAM OpenSCENARIO 1.2.0
<ManeuverGroup maximumExecutionCount="1" name="Group 1">
    <Actors selectTriggeringEntities="false">
        <EntityRef entityRef="Car 1"/>
    </Actors>
    <Maneuver name="Maneuver 1">
        <ParameterDeclarations/>
        <Event priority="overwrite" maximumExecutionCount="1" name="Event 1">
            <Action name="Longitudinal Distance">
                <PrivateAction>
                    <LongitudinalAction>
                        <LongitudinalDistanceAction freespace="true" continuous="true" entityRef="Ego" distance="20.0">
                            <DynamicConstraints maxAcceleration="9.0" maxDeceleration="0.0" maxSpeed="25.0"/>
                        </LongitudinalDistanceAction>
                    </LongitudinalAction>
                </PrivateAction>
            </Action>
            <StartTrigger>
                <ConditionGroup>
                    <Condition delay="0.0" conditionEdge="none" name="Simulation Time">
                        <ByValueCondition>
                            <SimulationTimeCondition rule="equalTo" value="2.0"/>
                        </ByValueCondition>
                    </Condition>
                </ConditionGroup>
            </StartTrigger>
        </Event>
    </Maneuver>
  </ManeuverGroup>
...
Code 8. ASAM OpenSCENARIO 2.0.0
serial:
    parallel:
        ego.drive()
        car1.drive() with:
            position(behind_of: ego, 50m)

    parallel:
        ego.drive()
        car1.drive() with:
            position(at:end, behind_of: ego, 20m)
            speed([0kph..90kph])
            acceleration([0mpsps..9mpsps])

    parallel:
        ego.drive()
        car1.drive() with:
            position(behind_of: ego, 20m)
            speed([0kph..90kph])
        keep(car1.acceleration.translational.norm() < 9mpsps)

10.7.2.3 LaneChangeAction

Example 3. LaneChangeAction
Code 9. ASAM OpenSCENARIO 1.2.0
<ManeuverGroup maximumExecutionCount="1" name="CutOutManeuverGroup">
    <Actors selectTriggeringEntities="false">
        <EntityRef entityRef="LeadVehicle" />
    </Actors>
    <Maneuver name="CutOutManeuver">
        <Event name="CutOutEvent" priority="overwrite">
            <Action name="CutOutAction">
                <PrivateAction>
                    <LateralAction>
                        <LaneChangeAction>
                            <LaneChangeActionDynamics dynamicsShape="cubic" value="$Lateral_Velocity_Vy" dynamicsDimension="rate" />
                                <LaneChangeTarget>
                                    <RelativeTargetLane entityRef="TargetBlocking" value="1" />
                                </LaneChangeTarget>
                          </LaneChangeAction>
                      </LateralAction>
                </PrivateAction>
          ...
          ...
</ManeuverGroup>
Code 10. ASAM OpenSCENARIO 2.0.0
lead_vehicle.change_lane(target: lane(left_of: target_blocking), rate_profile: smooth, rate_peak: lateral_velocity_vy, duration: 5s)

=== LaneOffsetAction

LaneOffSetAction can be converted using change_lane action or with a follow_lane action. If a more detailed definition of an action shape is needed, change_lane with the same lane modifier (same_as parameter) is preferred. If the exact shape is not so important, follow_lane is less constrained.

Code 11. ASAM OpenSCENARIO 1.2.0
<ManeuverGroup maximumExecutionCount="1" name="SwerveSequence">
    <Actors selectTriggeringEntities="false">
        <EntityRef entityRef="SideVehicle" />
    </Actors>
    <Maneuver name="SwerveManeuver">
        <Event name="SwerveEvent" priority="overwrite">
            <Action name="SwerveAction">
                <PrivateAction>
                    <LateralAction>
                        <LaneOffsetAction continuous="true">
                            <LaneOffsetActionDynamics maxLateralAcc="$Swerve_MaxLateralAcc" dynamicsShape="sinusoidal" />
                            <LaneOffsetTarget>
                                <AbsoluteTargetLaneOffset value="$Swerve_Offset_Left" />
                            </LaneOffsetTarget>
                        </LaneOffsetAction>
                    </LateralAction>
                </PrivateAction>
            </Action>
        ...
        ...
</ManeuverGroup>
Code 12. ASAM OpenSCENARIO 2.0.0
    do serial(15s):
        side_vehicle.follow_lane(swerve_offset_left, max_lateral_acc: swerve_max_lateral_acc)
        side_vehicle.follow_lane(0m, max_lateral_acc: swerve_max_lateral_acc)
        side_vehicle.follow_lane(-swerve_offset_left, max_lateral_acc: swerve_max_lateral_acc)
        side_vehicle.follow_lane(0m, max_lateral_acc: swerve_max_lateral_acc)

10.7.2.4 SynchronizeAction

Example 4. SynchronizeAction
Code 13. ASAM OpenSCENARIO 2.0.0
# Initial positions
ego_position: odr_point = map.create_odr_point(road_id: 0, lane_id: ego_init_position_lane_id, s: 0m)
ego_target_position: odr_point = map.create_odr_point(road_id: 0, lane_id: ego_init_position_lane_id, s: 80m)
s_ego: path = map.create_path([ego_position, ego_target_position], smooth)

car1_position: odr_point = map.create_odr_point(road_id: 1, lane_id: car1_init_position_lane_id, s: 20m)
car1_target_position: odr_point = map.create_odr_point(road_id: 1, lane_id: car1_init_position_lane_id, s: 120m)
s_car1: path = map.create_path([car1_position, car1_target_position], smooth)

s_target_speed_car1: speed = 80kph

serial:
    parallel(overlap: equal):
        ego.drive() with:
            along(s_ego)
            along(s_ego, start_offset: 0m, at: start)
            along(s_ego, end_offset: 0m, at: end)
            speed(50kph)
        car1.drive() with:
            along(s_car1)
            along(s_car1, start_offset: 0m, at: start)
            along(s_car1, end_offset: 0m, at: end)
            speed(40kph, at: start)
            speed(s_target_speed_car1, at: end)

=== AssignControllerAction, ActivateControllerAction, OverrideControllerValueAction

In ASAM OpenSCENARIO 1.2.0, the handling of controllers depends in large part on the simulation environment. Here we propose an extension, which the simulation of this version of the ASAM OpenSCENARIO environment should follow, to allow for migration of controller-related actions.

Code 14. Defining the structs
struct osc_1_controller:
    var active_lateral: bool = false
    var active_longitudinal: bool = false

struct osc_1_controller_float_value:
    var active: bool = false
    var value: float = 0.0

struct osc_1_controller_gear_value:
    var active: bool = false
    var value: int = 0

struct osc_1_controller_values:
    var throttle: osc_1_controller_value
    var brake: osc_1_controller_value
    var clutch: osc_1_controller_value
    var parking_brake: osc_1_controller_value
    var steering_wheel_action: osc_1_controller_value
    var gear: osc_1_controller_gear_value

struct osc_1_bm : vehicle_bm
    controller: osc_1_controller
    overrides: osc_1_controller_values

With these struct definitions, the actions can be written as follows:

Code 15. Writing the actions
scenario controller_setup:
    ego: car
    car_1: car

    var my_alks_controller: osc_1_controller
    var my_behavior_model: osc_1_bm
    do:
        # First set up and activate the controller
        my_behavior_model.set_controller(my_alks_controller)
        ego.set_bm(my_behavior_model) # AssignController
        my_behavior_model.set_controller_active(lateral: true) # ActivateController

        ego.drive() with:
            speed(50kph)
        car_1.drive() with:
            position([30m..50m], ahead_of: ego)
            lane(same_as: ego)

    # Under some condition override the controller throttle value
    on ego.object_distance(car_1 direction: longitudinal, mode: bounding_box) < 10 m:
        # OverrideControllerValue
        my_behavior_model.activate_override(control: throttle, value: 0.77)

10.7.2.5 TeleportAction

The TeleportAction is used in two contexts. In the <Init> section it specifies the initial location of the entity. Such a case can be converted as in this example:

Example 5. A TeleportAction where the initial location of an entity is specified.
Code 16. ASAM OpenSCENARIO 1.2.0
<Private entityRef="Ego">
    <PrivateAction>
        <TeleportAction>
            <Position>
                <LanePosition roadId="0" laneId="$Ego_InitPosition_LaneId" offset="0.0" s="500.0">
                </LanePosition>
            </Position>
        </TeleportAction>
    </PrivateAction>
</Private>
Code 17. ASAM OpenSCENARIO 2.0.0
# Initial positions
ego_position: create_odr_point(road_id: 0, lane_id: ego_init_position_lane_id, s: 0m)
..
do parallel:
    ego.assign_position(ego_position)
    # ... rest of the position assignments

The second case is when the TeleportAction requests instant location change for an entity, during the scenario run. A simulation environment may or may not be able to fulfill this non-physical request, but it can be converted using the assign_position action.

=== AssignRouteAction

AssignRoute action can be mapped with drive together with the along modifier. In contrast to an ASAM OpenSCENARIO 1.2.0 instantaneous action, such a conversion has a duration. To ensure that the route assignment is valid for the entire scenario, add this action in parallel on the same level as the rest of your Story.

Code 18. ASAM OpenSCENARIO 2.0.0
waypoint_1: route_point = map.odr_to_route_point(create_odr_point(road_id: 0, lane_id: 1, s: 30m))
my_route: route = map.create_route([waypoint_1, ...]
do parallel:
    car1.drive() with:
        along(my_route)
    parallel:
        story_1: .....

10.7.2.6 AcquirePositionAction

AcquirePositionAction can be mapped to specialized action change_position, with the flag on_road_network.

Example 6. AcquirePositionAction
Code 19. ASAM OpenSCENARIO 2.0.0
position_1: odr_point = map.create_odr_point(road_id: 27, lane_id: 2, s: 50m)
do:
    car1.change_position(target: position_1, path_style: on_road_network)

=== FollowTrajectoryAction

If the vertex timing information is not to be used, FollowTrajectoryAction is mapped to replay_path (mode position) or follow_path (mode follow) actions. If the timing information is relevant, use replay_trajectory or follow_trajectory instead.

Code 20. No timing information
    car1.drive() with:
        along(map.generate_route([vertex_1, ...]), routing_mode=straight)
    car2.follow_path(...)
Code 21. ASAM OpenSCENARIO 2.0.0 With timing information
    car1.replay_trajectory(...)

10.7.3 Global actions

These actions affect the entire scenario. They are probably mapped as actions on the top scenario.

10.7.3.1 EnvironmentAction

This action sets the fields in the current simulation environment (weather details and time of day). The action is converted with actions on the instance of the environment actor or with constraints on the fields of environment actor.

Example 7. Set environment details for the duration of the scenario
    environment: environment

    do serial:
        # Initialization of celestial body
        environment.sun_position(azimuth: 135deg, elevation: 33deg, intensity: 100000lx)
        # Continuation of the scenario
        car_1.drive()
    car_1: vehicle
    environment: environment

    scenario weather_change:
        do serial:
            environment.rain(intensity: 0mmph, duration: 3min)
            environment.rain(intensity: 1mmph, duration: 1min)
            environment.rain(intensity: 7mmph, duration: 30s)
            parallel(duration: 3min, overlap: equal):
                environment.fog(visual_range: [100m..200m])
                environment.rain(intensity: 1mmph)

    hour_of_day: int # Any time of day between 3 pm and 5 pm
    keep(hour_of_day >= 15)
    keep(hour_of_day <= 17)

    # Any day between 10th and 20th of December
    min_date: time = environment.local_to_unix_time(2021, 12, 10, hour_of_day, 0, 0, 0)
    max_date: time = environment.local_to_unix_time(2021, 12, 20, hour_of_day, 0, 0, 0))
    keep(environment.datetime >= min_date)
    keep(environment.datetime <= max_date)

    do parallel(overlap: equal):
        weather_change()
        car_1.drive() with:
            speed([40kph..70kph])

10.7.3.2 AddEntityAction and RemoveEntityAction

In the simulation, activate the entity (set position and start tracking it) that was declared before. Remove the currently active entity from the simulation.

10.7.3.3 ParameterSetAction and ParameterModifyAction

Pairs with ParameterCondition. This action assigns or modifies values of the runtime variables.

Variables in this version of ASAM OpenSCENARIO are declared with the var keyword (see Example ParameterCondition).

10.7.3.4 TrafficSourceAction, TrafficSinkAction and TrafficSwarmAction

Actions that set up traffic spawning/destroying mechanisms. Vehicles spawned through this mechanism cannot be referenced directly. They affect conditions such as RelativeDistance if they contain entitySelection by object type. Objects created are of SpawnedObject type. They implement the Entity interface, but a SpawnedObject is never explicitly listed in a scenario.

10.7.4 User defined actions

UserDefinedAction, paired with the UserDefinedValueCondition gives a scenario writer the ability to communicate with the runtime environment. UserDefinedAction sends a request to execute some action. Action is defined with a string denoting the action type and a string denoting the additional content.

Interpretation of the action type and additional content is dependent on the contract between the scenario writer and the simulation environment.