10.7 Actions
ASAM OpenSCENARIO 1.2.0 actions can be represented in and converted to this version of ASAM OpenSCENARIO in two ways.
-
drive()with modifiers. -
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:
-
A drive action to reach the required condition.
-
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
<Private entityRef="Ego">
<PrivateAction>
<LongitudinalAction>
<SpeedAction>
<SpeedActionDynamics dynamicsShape="step" dynamicsDimension="time" value="0" />
<SpeedActionTarget>
<AbsoluteTargetSpeed value="$Ego_InitSpeed_Ve0" />
</SpeedActionTarget>
</SpeedAction>
</LongitudinalAction>
</PrivateAction>
</Private>
# 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)
# 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
continuous:`false`, freespace:`true`<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>
...
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.
|
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)
<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>
...
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
<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>
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.
<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>
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
# 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.
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:
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:
TeleportAction where the initial location of an entity is specified.<Private entityRef="Ego">
<PrivateAction>
<TeleportAction>
<Position>
<LanePosition roadId="0" laneId="$Ego_InitPosition_LaneId" offset="0.0" s="500.0">
</LanePosition>
</Position>
</TeleportAction>
</PrivateAction>
</Private>
# 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.
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.
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.
car1.drive() with:
along(map.generate_route([vertex_1, ...]), routing_mode=straight)
car2.follow_path(...)
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.
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])
For a full list of properties, see Section 8.11, "Environment actions" and Section 8.10.1, "Environment".
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.