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.