Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@
* [CARLA ScenarioRunner 0.9.2](#carla-scenariorunner-092)

## Latest changes
### :rocket: New Features
* Minor improvements to some example scenarios. These include FollowLeadingVehicle, VehicleTurning, DynamicObjectCrossing and SignalizedJunctionRightTurn and RunningRedLight. Their behaviors are now more smooth, robust and some outdated mechanics have been removed
* SignalizedJunctionLeftTurn has been remade. It now has an actor flow on which the ego has to merge into, instead of a single vehicle.
* The BackgroundActivity has been readded to the routes, which the objective of creating the sensation of traffic around the ego

### :bug: Bug Fixes
* Fixed bug at OtherLeadingVehicle scenario causing the vehicles to move faster than intended
* Fixed bug causing some debris at ControlLoss scenario to be floating, instead of at ground level

## CARLA ScenarioRunner 0.9.13
### :rocket: New Features
* OpenSCENARIO support:
- Added support for `ParameterAction`
- Extended `ParameterCondition` support to use as an event trigger condition
- Added basic support for FollowTrajectoryAction. Currently only Polylines are supported

### :bug: Bug Fixes
* Fixed metrics parsing and remade the example recordings
Expand Down
140 changes: 74 additions & 66 deletions Docs/creating_new_scenario.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,86 +7,94 @@ Let us call the new scenario _NewScenario_. To create it, there are only few
steps required.

## Creating an empty Python class

Go to the Scenarios folder and create a new Python class with the name
_NewScenario_ in a new Python file (_new_scenario.py_). The class should be
derived from the _BasicScenario_ class. As a result, the class should look as
follows:

```
class NewScenario(BasicScenario):
"""
Some documentation on NewScenario
:param world is the CARLA world
:param ego_vehicles is a list of ego vehicles for this scenario
:param config is the scenario configuration (ScenarioConfiguration)
:param randomize can be used to select parameters randomly (optional, default=False)
:param debug_mode can be used to provide more comprehensive console output (optional, default=False)
:param criteria_enable can be used to disable/enable scenario evaluation based on test criteria (optional, default=True)
:param timeout is the overall scenario timeout (optional, default=60 seconds)
"""

# some ego vehicle parameters
# some parameters for the other vehicles

def __init__(self, world, ego_vehicles, config, randomize=False, debug_mode=False, criteria_enable=True,
timeout=60):
"""
Initialize all parameters required for NewScenario
"""

# Call constructor of BasicScenario
super(NewScenario, self).__init__(
"NewScenario",
ego_vehicles,
config,
world,
debug_mode,
criteria_enable=criteria_enable)


def _create_behavior(self):
"""
Setup the behavior for NewScenario
"""

def _create_test_criteria(self):
"""
Setup the evaluation criteria for NewScenario
"""
```
`NewScenario` in a new Python file `new_scenario.py`. The class should
be derived from the `BasicScenario` class. As a result, the class should
looks as follows:

```python
class NewScenario(BasicScenario):
"""
Some documentation on NewScenario
:param world is the CARLA world
:param ego_vehicles is a list of ego vehicles for this scenario
:param config is the scenario configuration (ScenarioConfiguration)
:param randomize can be used to select parameters randomly (optional, default=False)
:param debug_mode can be used to provide more comprehensive console output (optional, default=False)
:param criteria_enable can be used to disable/enable scenario evaluation based on test criteria (optional, default=True)
:param timeout is the overall scenario timeout (optional, default=60 seconds)
"""

# some ego vehicle parameters
# some parameters for the other vehicles

def __init__(self, world, ego_vehicles, config, randomize=False, debug_mode=False, criteria_enable=True,
timeout=60):
"""
Initialize all parameters required for NewScenario
"""

# Call constructor of BasicScenario
super(NewScenario, self).__init__(
"NewScenario",
ego_vehicles,
config,
world,
debug_mode,
criteria_enable=criteria_enable)


def _create_behavior(self):
"""
Setup the behavior for NewScenario
"""

def _create_test_criteria(self):
"""
Setup the evaluation criteria for NewScenario
"""
```

## Filling the Python class

In the NewScenario class, you have to define the following methods mentioned
in the code example.
In the `NewScenario` class, you have to define the following methods
mentioned in the code example.

### Initialize Method
The initialize method is intended to setup all parameters required
for the scenario and all vehicles. This includes selecting the correct vehicles,
spawning them at the correct location, etc. To simplify this, you may want to
use the _setup_vehicle()_ function defined in basic_scenario.py

The initialize method is intended to setup all parameters required for
the scenario and all vehicles. This includes selecting the correct
vehicles, spawning them at the correct location, etc. To simplify this,
you may want to use the `setup_vehicle()` method defined in
`basic_scenario.py`.

### CreateBehavior method
This method should setup the behavior tree that contains the behavior of all
non-ego vehicles during the scenario. The behavior tree should use py_trees and
the atomic behaviors defined in _atomic_scenario_behavior.py_

This method should setup the behavior tree that contains the behavior of
all non-ego vehicles during the scenario. The behavior tree should use
`py_trees` and the atomic behaviors defined in
`atomic_scenario_behavior.py`.

### CreateTestCriteria method
This method should setup a list with all evaluation criteria for the scenario.
The criteria should be based on the atomic criteria defined in
_atomic_scenario_criteria.py_.

Note: From this list a parallel py_tree will be created automatically!
This method should setup a list with all evaluation criteria for the
scenario. The criteria should be based on the atomic criteria defined
in `atomic_scenario_criteria.py`.

Note: From this list a parallel `py_tree` will be created automatically!

## Adding the scenario configuration
Finally the scenario configuration should be added to the examples/ folder. If you
extend an already existing scenario module, you can simply extend the corresponding
XML, otherwise add a new XML file. In this case you can use any of the existing
XML files as blueprint.

If you want to add multiple ego vehicles for a scenario, make sure that they use different
role names, e.g.
```
Finally the scenario configuration should be added to the `examples/`
folder. If you extend an already existing scenario module, you can
simply extend the corresponding XML, otherwise add a new XML file. In
this case you can use any of the existing XML files as blueprint.

If you want to add multiple ego vehicles for a scenario, make sure that
they use different role names, e.g.

```xml
<scenario name="MultiEgoTown03" type="FreeRide" town="Town03">
<ego_vehicle x="207" y="59" z="0" yaw="180" model="vehicle.lincoln.mkz_2017" rolename="hero"/>
<ego_vehicle x="237" y="-95.0754252474" z="0" yaw="90" model="vehicle.tesla.model3" rolename="hero2"/>
Expand Down
2 changes: 1 addition & 1 deletion Docs/openscenario_support.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ contains of submodules, which are not listed, the support status applies to all
| `VisibilityAction` | &#10060; | &#10060; | |
| `RoutingAction`<br> `AcquirePositionAction` | &#10060; | &#9989; | |
| `RoutingAction`<br> `AssignRouteAction` | &#10060; | &#9989; | Route Options (shortest/fastest/etc) are supported. Shortests means direct path between A and B, all other will use the shortest path along the road network between A and B |
| `RoutingAction`<br> `FollowTrajectoryAction` | &#10060; | &#10060; | |
| `RoutingAction`<br> `FollowTrajectoryAction` | &#10060; | &#9989; | Currently only Polylines without temporal constraints are supported and the trajectoryFollowingMode is up to the controller |

<br>

Expand Down
171 changes: 171 additions & 0 deletions run_experiment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env python3

import time
import glob
import os
import argparse
import sys
from threading import Thread
from datetime import datetime
import argparse

try:
CARLA_ROOT: str = os.getenv("CARLA_ROOT")
egg_dir = os.path.join(CARLA_ROOT, "PythonAPI", "carla", "dist")
sys.path.extend([glob.glob(os.path.join(egg_dir, f"carla-*.egg"))[0],
os.path.join(CARLA_ROOT, "PythonAPI"),
os.path.join(CARLA_ROOT, "PythonAPI", "carla"),
os.path.join(CARLA_ROOT, "PythonAPI", "examples")])
except IndexError:
print(f"Unable to find Carla PythonAPI file in {egg_dir}")

import carla

from scenario_runner import ScenarioRunner

recorder_file = None


def start_scenario_runner(scenario_runner_instance):
try:
print("Starting scenario runner")
result = scenario_runner_instance.run()
finally:
if scenario_runner_instance is not None:
scenario_runner_instance.destroy()
del scenario_runner_instance
print("Stopped scenario runner, Result:", result)


def wait_until_SR_loaded(scenario_runner_instance, ping_freq_s=0.5):
# wait until scenario_runner world has been loaded
while scenario_runner_instance.world is None:
time.sleep(ping_freq_s)


def run_schematic(argparser, scenario_runner_instance):
wait_until_SR_loaded(scenario_runner_instance)
print(10 * "=" + "schematic mode" + 10 * "=")

# can be completely avoided if --visualize is False
# NOTE: this import uses export PYTHONPATH=$PYTHONPATH:${CARLA_ROOT}/PythonAPI/examples
raise NotImplementedError
from dreyevr.examples.schematic_mode import schematic_run

# for full definitions of these args see no_rendering_mode.py
args = argparser.parse_known_args(
[
"-v",
"--verbose",
"--host",
"-p",
"-port",
"--res",
"--filter",
"--map",
"--no-rendering",
"--show-triggers",
"--show-connections",
"--show-spawn-points",
]
)
# TODO: figure out why this is not working
schematic_run(args)


def start_recording(client, args, scenario_runner_instance):
wait_until_SR_loaded(scenario_runner_instance)
time_str: str = datetime.now().strftime("%m_%d_%Y_%H_%M_%S")
filename: str = f"exp_{args.title}_{time_str}.rec"

global recorder_file # to "return" from this thread
recorder_file = client.start_recorder(filename)
print("Recording on file: %s" % recorder_file)


def stop_recording(client):
global recorder_file
print(f"Stopping recording, file saved to \"{recorder_file}\"")
client.stop_recorder()


def scenario_runner_args(parser):
# NOTE: see scenario_runner.py for a better explanation of these
parser.add_argument("-v", "--version", action="version", version="0.9.13")
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", default=2000)
parser.add_argument("--timeout", default="10.0")
parser.add_argument("--trafficManagerPort", default="8000")
parser.add_argument("--trafficManagerSeed", default="0")
parser.add_argument("--sync", action="store_true")
parser.add_argument("--list", action="store_true")
parser.add_argument("--route", nargs="+", type=str)
parser.add_argument("--agent")
parser.add_argument("--agentConfig", type=str, default="")
parser.add_argument("--output", action="store_true", default=True)
parser.add_argument("--junit", action="store_true")
parser.add_argument("--json", action="store_true")
parser.add_argument("--configFile", default="")
parser.add_argument("--additionalScenario", default="")
parser.add_argument("--debug", action="store_true", default=False)
parser.add_argument("--reloadWorld", action="store_true", default=True)
parser.add_argument("--record", type=str, default="")
parser.add_argument("--randomize", action="store_true")
parser.add_argument("--repetitions", default=1, type=int)
parser.add_argument("--waitForEgo", action="store_true")
parser.add_argument("--outputDir", default="")

# arguments we're ignoring
parser.add_argument("--file", action="store_true")
parser.add_argument("--scenario", default=None)
parser.add_argument("--openscenario", default=None)


def main():
# Define arguments that will be received and parsed

description = (
"DReyeVR Scenario Runner & Experiment Runner\n" "Current version: 0.9.13"
)

argparser = argparse.ArgumentParser(
description=description, formatter_class=argparse.RawTextHelpFormatter
)

argparser.add_argument("-t", "--title", default="anonymous")
argparser.add_argument("--visualize", action="store_true", default=False)

# also add all requisite scenario_runner arguments
scenario_runner_args(argparser)

args = argparser.parse_args()

if not args.route:
print("Please specify the route mode, other modes are not supported\n\n")
argparser.print_help(sys.stdout)
exit(1)

client = carla.Client(args.host, args.port)

scenario_runner_instance = ScenarioRunner(args)

# start the listening recorder thread (background, waits for scenario to begin)
recording_thread = Thread(
target=start_recording, args=(client, args, scenario_runner_instance,)
)
recording_thread.start() # run in background

if args.visualize is True:
schematic_thread = Thread(
target=run_schematic, args=(argparser, scenario_runner_instance,)
)
schematic_thread.start()

start_scenario_runner(scenario_runner_instance)

# finish recording
stop_recording(client)


if __name__ == "__main__":
main()
14 changes: 7 additions & 7 deletions scenario_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@

import carla

from srunner.scenarioconfigs.openscenario_configuration import OpenScenarioConfiguration
from srunner.scenariomanager.carla_data_provider import CarlaDataProvider
from srunner.scenariomanager.scenario_manager import ScenarioManager
from srunner.scenarios.open_scenario import OpenScenario
from srunner.scenarios.route_scenario import RouteScenario
from srunner.tools.scenario_parser import ScenarioConfigurationParser
from srunner.tools.route_parser import RouteParser
from srunner.tools.scenario_parser import ScenarioConfigurationParser
from srunner.scenarios.route_scenario import RouteScenario
from srunner.scenarios.open_scenario import OpenScenario
from srunner.scenariomanager.scenario_manager import ScenarioManager
from srunner.scenariomanager.carla_data_provider import CarlaDataProvider
from srunner.scenarioconfigs.openscenario_configuration import OpenScenarioConfiguration

# Version of scenario_runner
VERSION = '0.9.13'
Expand Down Expand Up @@ -408,7 +408,7 @@ def _load_and_run_scenario(self, config):
self.client.start_recorder(recorder_name, True)

# Load scenario and run it
self.manager.load_scenario(scenario, self.agent_instance)
self.manager.load_scenario(scenario, self.agent_instance, config.scenario_number)
self.manager.run_scenario()

# Provide outputs if required
Expand Down
Loading