Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scenario Caching #1764

Merged
merged 24 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
48bcb8f
Implement partial build and caching for scenarios
saulfield Nov 8, 2022
fea9f07
Refactor scenario caching, use SMARTS version in hash function
saulfield Nov 18, 2022
d245741
Add a build/ directory for all scenario artifacts
saulfield Nov 21, 2022
9c7d973
Type fix
saulfield Nov 22, 2022
eed10f0
Type fix
saulfield Nov 22, 2022
222f696
Cache glb file, track map file hash, refactoring
saulfield Nov 22, 2022
56e5df3
Test fixes
saulfield Nov 22, 2022
cbd1a73
Test fixes
saulfield Nov 22, 2022
df77b32
Merge with develop and fix conflicts
saulfield Nov 28, 2022
0565333
Update changelog, make gen_ functions private, and update scenarios
saulfield Nov 29, 2022
74d5f49
Warn if gen_* functions called externally. Type fix.
saulfield Nov 29, 2022
44a683a
Update docs
saulfield Nov 29, 2022
9a1e250
Merge with develop and fix conflicts
saulfield Nov 29, 2022
c480192
Remove ultra files
saulfield Nov 29, 2022
afda06f
Fix overwrite issue
saulfield Nov 30, 2022
804efba
Merge branch 'develop' into saul/scenario-caching
saulfield Dec 9, 2022
27aeb54
Apply suggestions from code review
saulfield Dec 9, 2022
4b604e7
Merge branch 'saul/scenario-caching' of github.com:huawei-noah/SMARTS…
saulfield Dec 9, 2022
19cd6bb
Update changelog
saulfield Dec 9, 2022
31d9634
Update scenarios
saulfield Dec 9, 2022
85f72dd
Changes from review
saulfield Dec 13, 2022
23fb55e
Merge branch 'develop' into saul/scenario-caching
saulfield Dec 14, 2022
84b6daa
Removed unused seed params. Made gen_* functions public.
saulfield Dec 15, 2022
28c8b8b
Remove map offset param in build_scenarios, add test for scenario cac…
saulfield Dec 16, 2022
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ shifted_map-AUTOGEN.net.xml
*.pkl
*.shf
*.tfrecord*
build.db

# Manually generated sumo route files
!zoo/evaluation/scenarios/**/traffic/*.rou.xml
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ Copy and pasting the git commit messages is __NOT__ enough.
- Renamed `examples/history_vehicles_replacement_for_imitation_learning.py` to `examples/traffic_histories_vehicle_replacement.py`.
- `SumoTrafficSimulation` will now try to hand-off the vehicles it controls to the new SMARTS background traffic provider by default if the Sumo provider crashes.
- SMARTS now gives an error about a suspected lack of junction edges in sumo maps on loading of them.
- Scenario build artifacts are now cached and built incrementally, meaning that subsequent builds (without the `clean` option) will only build the artifacts that depend on the changed DSL objects
- All build artifacts are now in a local `build/` directory in each scenario's directory
- The `allow_offset_map` option has been removed. This must now be set in a `MapSpec` object in the scenario.py if this option is needed
- All scenarios must have a `scenario.py`, and must call `gen_scenario()`, rather than the individual `gen_` functions, which are now private

### Removed
- Removed support for deprecated json-based and YAML formats for traffic histories.
Expand Down
26 changes: 6 additions & 20 deletions cli/studio.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,25 @@ def scenario_cli():
default=False,
help="Clean previously generated artifacts first",
)
@click.option(
"--allow-offset-map",
is_flag=True,
default=False,
help="Allows road network to be offset from the origin. If not specified, creates a new network file if necessary.",
)
@click.option(
"--seed",
type=int,
default=42,
help="Set the base seed of the scenario.",
)
@click.argument("scenario", type=click.Path(exists=True), metavar="<scenario>")
def build(clean: bool, allow_offset_map: bool, scenario: str, seed: int):
def build(clean: bool, scenario: str, seed: int):
click.echo(f"build-scenario {scenario}")

from smarts.sstudio.scenario_construction import build_scenario

assert seed == None or isinstance(seed, (int))

build_scenario(clean, allow_offset_map, scenario, seed, click.echo)
build_scenario(clean, scenario, seed, click.echo)


def _build_scenario_proc(
clean: bool,
allow_offset_map: bool,
scenario: str,
semaphore: synchronize.Semaphore,
seed: int,
Expand All @@ -76,7 +69,7 @@ def _build_scenario_proc(

semaphore.acquire()
try:
build_scenario(clean, allow_offset_map, scenario, seed, click.echo)
build_scenario(clean, scenario, seed, click.echo)
finally:
semaphore.release()

Expand Down Expand Up @@ -104,26 +97,19 @@ def _is_scenario_folder_to_build(path: str) -> bool:
default=False,
help="Clean previously generated artifacts first",
)
@click.option(
"--allow-offset-maps",
is_flag=True,
default=False,
help="Allows road networks (maps) to be offset from the origin. If not specified, a new network file is created if necessary. Defaults to False except when there's Traffic History data associated with the scenario.",
)
@click.option(
"--seed",
type=int,
default=42,
help="Set the base seed of the scenarios.",
)
@click.argument("scenarios", nargs=-1, metavar="<scenarios>")
def build_all(clean: bool, allow_offset_maps: bool, scenarios: List[str], seed: int):
build_scenarios(clean, allow_offset_maps, scenarios, seed)
def build_all(clean: bool, scenarios: List[str], seed: int):
build_scenarios(clean, scenarios, seed)


def build_scenarios(
clean: bool,
allow_offset_maps: bool,
scenarios: List[str],
seed: Optional[int] = None,
):
Expand All @@ -142,7 +128,7 @@ def build_scenarios(
scenario = f"{scenarios_path}/{p.relative_to(scenarios_path)}"
proc = Process(
target=_build_scenario_proc,
args=(clean, allow_offset_maps, scenario, sema, seed),
args=(clean, scenario, sema, seed),
)
all_processes.append((scenario, proc))
proc.start()
Expand Down
26 changes: 24 additions & 2 deletions cli/tests/test_studio.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import os
import shutil
import tempfile
import time
from xml.etree.ElementTree import ElementTree


Expand Down Expand Up @@ -53,10 +54,10 @@ def test_scenario_generation_unchanged():
assert _hashseed not in (None, "random"), f"PYTHONHASHSEED is {_hashseed}"

shutil.copytree("scenarios/sumo", loc1)
build_scenarios(True, True, [loc1], 42)
build_scenarios(True, [loc1], 42)

shutil.copytree("scenarios/sumo", loc2)
build_scenarios(True, True, [loc2], 42)
build_scenarios(True, [loc2], 42)

for dirpath, dirnames, files in os.walk(loc1):
if "traffic" in dirpath:
Expand All @@ -67,3 +68,24 @@ def test_scenario_generation_unchanged():
number_of_comparisons_greater_than_0 = True

assert number_of_comparisons_greater_than_0


def test_scenario_build_caching():
from cli.studio import build_scenarios

with tempfile.TemporaryDirectory() as temp_dir1:
scenario_dir = temp_dir1 + "/scenario"
shutil.copytree("scenarios/sumo/loop", scenario_dir)

# Clean & build the scenario
start = time.time()
build_scenarios(True, [scenario_dir])
elapsed1 = time.time() - start

# Build again without cleaning
start = time.time()
build_scenarios(False, [scenario_dir])
elapsed2 = time.time() - start

# Second build should be much faster because of caching
assert 2 * elapsed2 < elapsed1
17 changes: 7 additions & 10 deletions docs/minimal_scenario_studio.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from pathlib import Path

from smarts.sstudio import types as t
from smarts.sstudio import gen_traffic, gen_bubbles, gen_missions

scenario = str(Path(__file__).parent)
from smarts.sstudio import gen_scenario

# Definition of a traffic flow
traffic = t.Traffic(
Expand All @@ -22,12 +20,11 @@
]
)

# The call to generate traffic
gen_traffic(scenario, traffic, name="basic")
missions = [
t.Mission(t.Route(begin=("west", 0, 0), end=("east", 0, "max"))),
]

gen_missions(
scenario,
[
t.Mission(t.Route(begin=("west", 0, 0), end=("east", 0, "max"))),
],
gen_scenario(
t.Scenario(traffic={"basic": traffic}, ego_missions=missions),
output_dir=Path(__file__).parent,
)
35 changes: 14 additions & 21 deletions docs/sim/scenario_studio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ Generate traffic
]
)

gen_traffic(scenario_path, traffic, name="all", output_dir=scenario_path, seed=seed_, overwrite=True)

Gamenot marked this conversation as resolved.
Show resolved Hide resolved
Note that the `engine` argument to `Traffic` can either be `"SUMO"` or `"SMARTS"`, with `"SUMO"` being the default.
As `"SUMO"` can only be used on Sumo-format "road networks", if you need
to run SMARTS with another map type you may need to change to the `"SMARTS"` engine.
Expand All @@ -58,7 +56,7 @@ See more config for `TrafficActor` in :class:`smarts.sstudio.types`.

Flow can be used to generate repeated vehicle runs on the same route, you can config vehicle route and depart rate here.

After the `gen_traffic` function is run, a dir named "traffic" containing vehicle config xmls will be created under output_dir.
After `traffic` is provided to the `gen_scenario` function, a dir named "traffic" will be created under output_dir which contains background vehicle and route definitions.


This a short file example of how it works:
Expand All @@ -81,33 +79,30 @@ Generate missions
=================

The Scenario Studio of SMARTS also allows generation of *missions* for ego agents and social agents. These missions are similar
to routes for social vehicles. When we run `gen_missions`, "missions.rou.xml" file will be created under the output dir:
to routes for social vehicles. When we run `gen_scenario`, "missions.rou.xml" file will be created under the output dir:

.. code-block:: python

# agent missions
gen_missions(
scenario,
missions=[Mission(Route(begin=("edge0", 0, "random"), end=("edge1", 0, "max"),)),],
seed=seed_,
)
missions = [
Mission(Route(begin=("edge0", 0, "random"), end=("edge1", 0, "max"))),
]

=====================
Generate friction map
=====================

The Scenario Studio of SMARTS also allows generation of *friction map* which consists of a list of *surface patches* for ego agents and social agents. These surface patches are using PositionalZone as in the case of bubbles. When we run `gen_friction_map`, "friction_map.pkl" file will be created under the output dir:
The Scenario Studio of SMARTS also allows the generation of a *friction map* which consists of a list of *surface patches* for ego agents and social agents. These surface patches are using PositionalZone as in the case of bubbles. When we run `gen_scenario` passing in `friction_maps`, a "friction_map.pkl" file will be created under the output dir:

.. code-block:: python

# friction map
gen_friction_map(
scenario,
surface_patches=[RoadSurfacePatch(PositionalZone(pos=(153, -100), size=(2000, 6000)),
begin_time=0,
end_time=20,
friction_coefficient=0.5)]
)
friction_maps = [
RoadSurfacePatch(
PositionalZone(pos=(153, -100), size=(2000, 6000)),
begin_time=0,
end_time=20,
friction_coefficient=0.5,
),
]

=================
Generate road map
Expand All @@ -134,8 +129,6 @@ define a `MapSpec` object in your `scenario.py`.

map_spec = MapSpec(source="path_or_uri", builder_fn=custom_map_builder)

gen_map(map_spec)


Convert an existing map to SUMO
-------------------------------
Expand Down
2 changes: 1 addition & 1 deletion examples/env/figure_eight_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def entry_point(*args, **kwargs):
## Note: can build the scenario here
from smarts.sstudio.scenario_construction import build_scenario

build_scenario(clean=True, allow_offset_map=True, scenario=scenario)
build_scenario(clean=True, scenario=scenario)
hiwayenv = HiWayEnv(
agent_specs={"agent-007": agent_spec},
scenarios=[scenario],
Expand Down
1 change: 0 additions & 1 deletion examples/rl/racing/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def main(args: argparse.Namespace):
# Build the scenarios.
_build_all_scenarios(
clean=True,
allow_offset_maps=False,
scenarios=[config_env["scenarios_dir"]],
seed=_SEED,
)
Expand Down
1 change: 0 additions & 1 deletion scenarios/open_drive/od_4lane/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
gen_scenario(
Scenario(),
output_dir=str(Path(__file__).parent),
overwrite=True,
)
1 change: 0 additions & 1 deletion scenarios/open_drive/od_merge/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@
ego_missions=ego_missions,
),
output_dir=str(Path(__file__).parent),
overwrite=True,
)
1 change: 0 additions & 1 deletion scenarios/open_drive/od_newmarket/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
gen_scenario(
Scenario(),
output_dir=str(Path(__file__).parent),
overwrite=True,
)
9 changes: 9 additions & 0 deletions scenarios/sumo/figure_eight/scenario.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pathlib import Path

from smarts.sstudio.genscenario import gen_scenario
from smarts.sstudio.types import Scenario

gen_scenario(
scenario=Scenario(),
output_dir=Path(__file__).parent,
)
saulfield marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 17 additions & 6 deletions scenarios/sumo/intersections/2lane/scenario.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import os
from pathlib import Path

from smarts.sstudio import gen_traffic
from smarts.sstudio.genscenario import gen_scenario
from smarts.sstudio.types import (
Distribution,
Flow,
JunctionModel,
LaneChangingModel,
MapSpec,
Route,
Scenario,
Traffic,
TrafficActor,
)

scenario = os.path.dirname(os.path.realpath(__file__))

impatient_car = TrafficActor(
name="car",
speed=Distribution(sigma=0.2, mean=1.0),
Expand Down Expand Up @@ -53,14 +53,16 @@
("east-EW", "north-SN"),
]

traffic = {}

for name, routes in {
"vertical": vertical_routes,
"horizontal": horizontal_routes,
"unprotected_left": turn_left_routes,
"turns": turn_left_routes + turn_right_routes,
"all": vertical_routes + horizontal_routes + turn_left_routes + turn_right_routes,
}.items():
traffic = Traffic(
saulfield marked this conversation as resolved.
Show resolved Hide resolved
traffic[name] = Traffic(
flows=[
Flow(
route=Route(
Expand All @@ -75,4 +77,13 @@
]
)

gen_traffic(scenario, traffic, name=name)
gen_scenario(
scenario=Scenario(
traffic=traffic,
map_spec=MapSpec(
source=Path(__file__).parent,
shift_to_origin=True,
),
),
output_dir=Path(__file__).parent,
)
Loading