Welcome to Scenic’s documentation!

Scenic is a domain-specific probabilistic programming language for modeling the environments of cyber-physical systems like robots and autonomous cars. A Scenic program defines a distribution over scenes, configurations of physical objects and agents; sampling from this distribution yields concrete scenes which can be simulated to produce training or testing data.

Scenic was designed and implemented by Daniel J. Fremont, Tommaso Dreossi, Shromona Ghosh, Xiangyu Yue, Alberto L. Sangiovanni-Vincentelli, and Sanjit A. Seshia. For a description of the language and some of its applications, see our PLDI 2019 paper; a more in-depth discussion is in Chapters 5 and 8 of this thesis. Our publications page lists additional papers using Scenic.

If you have any problems using Scenic, please submit an issue to our GitHub repository or contact Daniel at dfremont@ucsc.edu.

Table of Contents

Getting Started with Scenic

Installation

Scenic requires Python 3.6 or newer. You can install Scenic from PyPI by simply running:

pip install scenic

Alternatively, you can download or clone the Scenic repository, which contains examples we’ll use below. Install Poetry, activate the virtual environment in which you would like to run Scenic, and then run:

poetry install

If you will be developing Scenic, add the -E dev option when invoking Poetry.

Note

If you are not already using a virtual environment, poetry install will create one. You can then run poetry shell to create a terminal inside the environment for running the commands below.

Either of the options above should install all of the dependencies which are required to run Scenic. Scenarios using the scenic.simulators.webots.guideways model also require the pyproj package, and will prompt you if you don’t have it.

Note

For Windows, we recommend using bashonwindows (the Windows subsystem for Linux) on Windows 10. Instructions for installing poetry on bashonwindows can be found here.

In the past, the shapely package did not install properly on Windows. If you encounter this issue, try installing it manually following the instructions here.

Note

You may also want to install the Polygon3 package (pip install Polygon3) to get faster and more robust polygon triangulation. However, this package is based on the GPC library, which is only free for non-commercial use.

Trying Some Examples

The Scenic repository contains many example scenarios, found in the examples directory. They are organized by the simulator they are written for, e.g. GTA or Webots. Each simulator has a specialized Scenic interface which requires additional setup (see Supported Simulators); however, for convenience Scenic provides an easy way to visualize scenarios without running a simulator. Simply run the scenic module as a script, giving a path to a Scenic file:

python -m scenic examples/gta/badlyParkedCar2.scenic

This will compile the Scenic program and sample from it, displaying a schematic of the resulting scene. Since this is the badly-parked car example from our GTA case study, you should get something like this:

_images/badlyParkedCar2.png

Here the circled rectangle is the ego car; its view cone extends to the right, where we see another car parked rather poorly at the side of the road (the white lines are curbs). If you close the window, Scenic will sample another scene from the same scenario and display it. This will repeat until you kill the generator (Control-c in Linux; right-clicking on the Dock icon and selecting Quit on OS X).

Scenarios for the other simulators can be viewed in the same way. Here are a few for Webots:

python -m scenic examples/webots/mars/narrowGoal.scenic
python -m scenic examples/webots/road/crossing.scenic
python -m scenic examples/webots/guideways/uberCrash.scenic
_images/narrowGoal.png _images/crossing.png _images/uberCrash.png

Learning More

Depending on what you’d like to do with Scenic, different parts of the documentation may be helpful:

  • If you want to learn how to write Scenic programs, see the tutorial.

  • If you want to use Scenic with a simulator, see the Supported Simulators page (which also describes how to interface Scenic to a new simulator, if the one you want isn’t listed).

  • If you want to add a feature to the language or otherwise need to understand Scenic’s inner workings, see our page on Scenic Internals.

Scenic Tutorial

This tutorial motivates and illustrates the main features of Scenic, focusing on aspects of the language that make it particularly well-suited for describing geometric scenarios. Throughout, we use examples from our case study using Scenic to generate traffic scenes in GTA V to test and train autonomous cars [F19].

Classes, Objects, and Geometry

To start, suppose we want scenes of one car viewed from another on the road. We can write this very concisely in Scenic:

1
2
3
from scenic.simulators.gta.model import Car
ego = Car
Car

Line 1 imports the GTA world model, a Scenic library defining everything specific to our GTA interface. This includes the definition of the class Car, as well as information about the road geometry that we’ll see later. We’ll suppress this import statement in subsequent examples.

Line 2 then creates a Car and assigns it to the special variable ego specifying the ego object. This is the reference point for the scenario: our simulator interfaces typically use it as the viewpoint for rendering images, and many of Scenic’s geometric operators use ego by default when a position is left implicit (we’ll see an example momentarily).

Finally, line 3 creates a second Car. Compiling this scenario with Scenic, sampling a scene from it, and importing the scene into GTA V yields an image like this:

Simple car scenario image.

A scene sampled from the simple car scenario, rendered in GTA V.

Note that both the ego car (where the camera is located) and the second car are both located on the road and facing along it, despite the fact that the code above does not specify the position or any other properties of the two cars. This is because in Scenic, any unspecified properties take on the default values inherited from the object’s class. Slightly simplified, the definition of the class Car begins:

1
2
3
4
5
6
class Car:
    position: Point on road
    heading: roadDirection at self.position
    width: self.model.width
    height: self.model.height
    model: CarModel.defaultModel()      # a distribution over several car models

Here road is a region, one of Scenic’s primitive types, defined in the gta model to specify which points in the workspace are on a road. Similarly, roadDirection is a vector field specifying the nominal traffic direction at such points. The operator F at X simply gets the direction of the field F at point X, so line 3 sets a Car’s default heading to be the road direction at its position. The default position, in turn, is a Point on road (we will explain this syntax shortly), which means a uniformly random point on the road. Thus, in our simple scenario above both cars will be placed on the road facing a reasonable direction, without our having to specify this explicitly.

We can of course override the class-provided defaults and define the position of an object more specifically. For example,

1
Car offset by (-10, 10) @ (20, 40)

creates a car that is 20–40 meters ahead of the camera (the ego), and up to 10 meters to the left or right, while still using the default heading (namely, being aligned with the road). Here the interval notation (X, Y) creates a uniform distribution on the interval, and X @ Y creates a vector from xy coordinates (as in Smalltalk [GR83]).

Local Coordinate Systems

Scenic provides a number of constructs for working with local coordinate systems, which are often helpful when building a scene incrementally out of component parts. Above, we saw how offset by could be used to position an object in the coordinate system of the ego, for instance placing a car a certain distance away from the camera 1.

It is equally easy in Scenic to use local coordinate systems around other objects or even arbitrary points. For example, suppose we want to make the scenario above more realistic by not requiring the car to be exactly aligned with the road, but to be within say 5°. We could write

1
2
Car offset by (-10, 10) @ (20, 40),
    facing (-5, 5) deg

but this is not quite what we want, since this sets the orientation of the car in global coordinates. Thus the car will end up facing within 5° of North, rather than within 5° of the road direction. Instead, we can use Scenic’s general operator X relative to Y, which can interpret vectors and headings as being in a variety of local coordinate systems:

If instead we want the heading to be relative to that of the ego car, so that the two cars are (roughly) aligned, we can simply write (-5, 5) deg relative to ego.

Notice that since roadDirection is a vector field, it defines a different local coordinate system at each point in space: at different points on the map, roads point different directions! Thus an expression like 15 deg relative to field does not define a unique heading. The example above works because Scenic knows that the expression (-5, 5) deg relative to roadDirection depends on a reference position, and automatically uses the position of the Car being defined. This is a feature of Scenic’s system of specifiers, which we explain next.

Readable, Flexible Specifiers

The syntax offset by X and facing Y for specifying positions and orientations may seem unusual compared to typical constructors in object-oriented languages. There are two reasons why Scenic uses this kind of syntax: first, readability. The second is more subtle and based on the fact that in natural language there are many ways to specify positions and other properties, some of which interact with each other. Consider the following ways one might describe the location of an object:

  1. “is at position X” (an absolute position)

  2. “is just left of position X” (a position based on orientation)

  3. “is 3 m West of the taxi” (a relative position)

  4. “is 3 m left of the taxi” (a local coordinate system)

  5. “is one lane left of the taxi” (another local coordinate system)

  6. “appears to be 10 m behind the taxi” (relative to the line of sight)

  7. “is 10 m along the road from the taxi” (following a potentially-curving vector field)

These are all fundamentally different from each other: for example, (4) and (5) differ if the taxi is not parallel to the lane.

Furthermore, these specifications combine other properties of the object in different ways: to place the object “just left of” a position, we must first know the object’s heading; whereas if we wanted to face the object “towards” a location, we must instead know its position. There can be chains of such dependencies: for example, the description “the car is 0.5 m left of the curb” means that the right edge of the car is 0.5 m away from the curb, not its center, which is what the car’s position property stores. So the car’s position depends on its width, which in turn depends on its model. In a typical object-oriented language, these dependencies might be handled by first computing values for position and all other properties, then passing them to a constructor. For “a car is 0.5 m left of the curb” we might write something like:

# hypothetical Python-like language
model = Car.defaultModelDistribution.sample()
pos = curb.offsetLeft(0.5 + model.width / 2)
car = Car(pos, model=model)

Notice how model must be used twice, because model determines both the model of the car and (indirectly) its position. This is inelegant, and breaks encapsulation because the default model distribution is used outside of the Car constructor. The latter problem could be fixed by having a specialized constructor or factory function:

# hypothetical Python-like language
car = CarLeftOfBy(curb, 0.5)

However, such functions would proliferate since we would need to handle all possible combinations of ways to specify different properties (e.g. do we want to require a specific model? Are we overriding the width provided by the model for this specific car?). Instead of having a multitude of such monolithic constructors, Scenic factors the definition of objects into potentially-interacting but syntactically-indepdendent parts:

1
2
Car left of spot by 0.5,
    with model CarModel.models['BUS']

Here left of X by D and with model M are specifiers which do not have an order, but which together specify the properties of the car. Scenic works out the dependencies between properties (here, position is provided by left of, which depends on width, whose default value depends on model) and evaluates them in the correct order. To use the default model distribution we would simply omit line 2; keeping it affects the position of the car appropriately without having to specify BUS more than once.

Specifying Multiple Properties Together

Recall that we defined the default position for a Car to be a Point on road: this is an example of another specifier, on region, which specifies position to be a uniformly random point in the given region. This specifier illustrates another feature of Scenic, namely that specifiers can specify multiple properties simultaneously. Consider the following scenario, which creates a parked car given a region curb (also defined in the scenic.simulators.gta.model library):

1
2
spot = OrientedPoint on visible curb
Car left of spot by 0.25

The function visible region returns the part of the region that is visible from the ego object. The specifier on visible curb with then set position to be a uniformly random visible point on the curb. We create spot as an OrientedPoint, which is a built-in class that defines a local coordinate system by having both a position and a heading. The on region specifier can also specify heading if the region has a preferred orientation (a vector field) associated with it: in our example, curb is oriented by roadDirection. So spot is, in fact, a uniformly random visible point on the curb, oriented along the road. That orientation then causes the Car to be placed 0.25 m left of spot in spot’s local coordinate system, i.e. 0.25 m away from the curb, as desired.

In fact, Scenic makes it easy to elaborate this scenario without needing to alter the code above. Most simply, we could specify a particular model or non-default distribution over models by just adding with model M to the definition of the Car. More interestingly, we could produce a scenario for badly-parked cars by adding two lines:

1
2
3
4
spot = OrientedPoint on visible curb
badAngle = Uniform(1, -1) * (10, 20) deg
Car left of spot by 0.25,
    facing badAngle relative to roadDirection

This will yield cars parked 10-20° off from the direction of the curb, as seen in the image below. This example illustrates how specifiers greatly enhance Scenic’s flexibility and modularity.

Badly-parked car image.

A scene sampled from the badly-parked car scenario, rendered in GTA V.

Declarative Hard and Soft Constraints

Notice that in the scenarios above we never explicitly ensured that two cars will not intersect each other. Despite this, Scenic will never generate such scenes. This is because Scenic enforces several default requirements:

  • All objects must be contained in the workspace, or a particular specified region. For example, we can define the Car class so that all of its instances must be contained in the region road by default.

  • Objects must not intersect each other (unless explicitly allowed).

  • Objects must be visible from the ego object (so that they affect the rendered image; this requirement can also be disabled, for example for dynamic scenarios).

Scenic also allows the user to define custom requirements checking arbitrary conditions built from various geometric predicates. For example, the following scenario produces a car headed roughly towards the camera, while still facing the nominal road direction:

1
2
3
ego = Car on road
car2 = Car offset by (-10, 10) @ (20, 40), with viewAngle 30 deg
require car2 can see ego

Here we have used the X can see Y predicate, which in this case is checking that the ego car is inside the 30° view cone of the second car.

Requirements, called observations in other probabilistic programming languages, are very convenient for defining scenarios because they make it easy to restrict attention to particular cases of interest. Note how difficult it would be to write the scenario above without the require statement: when defining the ego car, we would have to somehow specify those positions where it is possible to put a roughly-oncoming car 20–40 meters ahead (for example, this is not possible on a one-way road). Instead, we can simply place ego uniformly over all roads and let Scenic work out how to condition the distribution so that the requirement is satisfied 2. As this example illustrates, the ability to declaratively impose constraints gives Scenic greater versatility than purely-generative formalisms. Requirements also improve encapsulation by allowing us to restrict an existing scenario without altering it. For example:

1
2
3
import genericTaxiScenario    # import another Scenic scenario
fifthAvenue = ...             # extract a Region from a map here
require genericTaxiScenario.taxi on fifthAvenue

The constraints in our examples above are hard requirements which must always be satisfied. Scenic also allows imposing soft requirements that need only be true with some minimum probability:

1
require[0.5] car2 can see ego   # condition only needs to hold with prob. >= 0.5

Such requirements can be useful, for example, in ensuring adequate representation of a particular condition when generating a training set: for instance, we could require that at least 90% of generated images have a car driving on the right side of the road.

Mutations

A common testing paradigm is to randomly generate variations of existing tests. Scenic supports this paradigm by providing syntax for performing mutations in a compositional manner, adding variety to a scenario without changing its code. For example, given a complex scenario involving a taxi, we can add one additional line:

1
2
from bigScenario import taxi
mutate taxi

The mutate statement will add Gaussian noise to the position and heading properties of taxi, while still enforcing all built-in and custom requirements. The standard deviation of the noise can be scaled by writing, for example, mutate taxi by 2 (which adds twice as much noise), and in fact can be controlled separately for position and heading (see scenic.core.object_types.Mutator).

A Worked Example

We conclude with a larger example of a Scenic program which also illustrates the language’s utility across domains and simulators. Specifically, we consider the problem of testing a motion planning algorithm for a Mars rover able to climb over rocks. Such robots can have very complex dynamics, with the feasibility of a motion plan depending on exact details of the robot’s hardware and the geometry of the terrain. We can use Scenic to write a scenario generating challenging cases for a planner to solve in simulation.

We will write a scenario representing a rubble field of rocks and piples with a bottleneck between the rover and its goal that forces the path planner to consider climbing over a rock. First, we import a small Scenic library for the Webots robotics simulator (scenic.simulators.webots.mars.model) which defines the (empty) workspace and several types of objects: the Rover itself, the Goal (represented by a flag), and debris classes Rock, BigRock, and Pipe. Rock and BigRock have fixed sizes, and the rover can climb over them; Pipe cannot be climbed over, and can represent a pipe of arbitrary length, controlled by the height property (which corresponds to Scenic’s y axis).

1
from scenic.simulators.webots.mars.model import *

Then we create the rover at a fixed position and the goal at a random position on the other side of the workspace:

2
3
ego = Rover at 0 @ -2
goal = Goal at (-2, 2) @ (2, 2.5)

Next we pick a position for the bottleneck, requiring it to lie roughly on the way from the robot to its goal, and place a rock there.

4
5
6
7
bottleneck = OrientedPoint offset by (-1.5, 1.5) @ (0.5, 1.5),
                           facing (-30, 30) deg
require abs((angle to goal) - (angle to bottleneck)) <= 10 deg
BigRock at bottleneck

Note how we define bottleneck as an OrientedPoint, with a range of possible orientations: this is to set up a local coordinate system for positioning the pipes making up the bottleneck. Specifically, we position two pipes of varying lengths on either side of the bottleneck, with their ends far enough apart for the robot to be able to pass between:

 8
 9
10
11
12
13
14
halfGapWidth = (1.2 * ego.width) / 2
leftEnd = OrientedPoint left of bottleneck by halfGapWidth,
                        facing (60, 120) deg relative to bottleneck
rightEnd = OrientedPoint right of bottleneck by halfGapWidth,
                         facing (-120, -60) deg relative to bottleneck
Pipe ahead of leftEnd, with height (1, 2)
Pipe ahead of rightEnd, with height (1, 2)

Finally, to make the scenario slightly more interesting, we add several additional obstacles, positioned either on the far side of the bottleneck or anywhere at random (recalling that Scenic automatically ensures that no objects will overlap).

15
16
17
18
19
20
BigRock beyond bottleneck by (-0.5, 0.5) @ (0.5, 1)
BigRock beyond bottleneck by (-0.5, 0.5) @ (0.5, 1)
Pipe
Rock
Rock
Rock

This completes the scenario, which can also be found in the Scenic repository under examples/webots/mars/narrowGoal.scenic. Several scenes generated from the scenario and visualized in Webots are shown below.

Mars rover scenario image.

A scene sampled from the Mars rover scenario, rendered in Webots.

_images/mars3.jpg _images/mars4.jpg _images/mars5.jpg

Further Reading

This tutorial illustrated the syntax of Scenic through several simple examples. Much more complex scenarios are possible, such as the platoon and bumper-to-bumper traffic GTA V scenarios shown below. For many further examples using a variety of simulators, see the examples folder, as well as the links in the Supported Simulators page.

_images/platoon2.jpg _images/platoon3.jpg _images/platoon4.jpg _images/btb1.jpg _images/btb3.jpg _images/btb4.jpg

For a comprehensive overview of Scenic’s syntax, including details on all specifiers, operators, distributions, statements, and built-in classes, see the Scenic Syntax Reference. Our Guide to Scenic Syntax summarizes all of these language constructs in convenient tables with links to the detailed documentation.

Footnotes

1

In fact, ego is a variable and can be reassigned, so we can set ego to one object, build a part of the scene around it, then reassign ego and build another part of the scene.

2

On the other hand, Scenic may have to work hard to satisfy difficult constraints. Ultimately Scenic falls back on rejection sampling, which in the worst case will run forever if the constraints are inconsistent (although we impose a limit on the number of iterations: see Scenario.generate).

References

F19

Fremont et al., Scenic: A Language for Scenario Specification and Scene Generation, PLDI 2019.

GR83

Goldberg and Robson, Smalltalk-80: The Language and its Implementation, Addison-Wesley, 1983. [PDF]

Guide to Scenic Syntax

This page summarizes the syntax of Scenic (excluding syntax inherited from Python). For more details, click the links for individual language constructs to go to the corresponding section of the Scenic Syntax Reference.

Primitive Data Types

Booleans

expressing truth values

Scalars

representing distances, angles, etc. as floating-point numbers

Vectors

representing positions and offsets in space

Headings

representing orientations in space

Vector Fields

associating an orientation (i.e. a heading) to each point in space

Regions

representing sets of points in space

Distributions

(low, high)

uniformly distributed in the interval

Normal(mean, stdDev)

normal distribution with the given mean and standard deviation

Uniform(value, …)

uniform over a finite set of values

Discrete({value: weight, … })

discrete with given values and weights

Objects

Property

Default

Meaning

position

0 @ 0

position in global coordinates

viewDistance

50

distance for the ‘can see’ operator

mutationScale

0

overall scale of mutations

positionStdDev

1

mutation standard deviation for position

heading

0

heading in global coordinates

viewAngle

360 degrees

angle for the ‘can see’ operator

headingStdDev

5 degrees

mutation standard deviation for heading

width

1

width of bounding box (X axis)

height

1

height of bounding box (Y axis)

regionContainedIn

workspace

Region the object must lie within

allowCollisions

false

whether collisions are allowed

requireVisible

true

whether object must be visible from ego

Specifiers

Diagram illustrating several specifiers.

Illustration of the beyond, behind, and offset by specifiers. Each OrientedPoint (e.g. P) is shown as a bold arrow.

Specifier for Position

Meaning

at vector

Positions the object at the given global coordinates

offset by vector

Positions the object at the given coordinates in the local coordinate system of ego (which must already be defined)

offset along direction by vector

Positions the object at the given coordinates, in a local coordinate system centered at ego and oriented along the given direction

(left | right) of vector [by scalar]

Positions the object further to the left/right by the given scalar distance

(ahead of | behind) vector [by scalar]

As above, except placing the object ahead of or behind the given position

beyond vector by vector [from vector]

Positions the object at coordinates given by the second vector, centered at the first vector and oriented along the line of sight from the ego

visible [from (Point | OrientedPoint)]

Positions the object uniformly at random in the visible region of the ego, or of the given Point/OrientedPoint if given

Specifiers for position and optionally heading

Meaning

(in | on) region

Positions the object uniformly at random in the given Region

(left | right) of (OrientedPoint | Object) [by scalar]

Positions the object to the left/right of the given OrientedPoint, depending on the object’s width

(ahead of | behind) (OrientedPoint | Object) [by scalar ]

As above, except positioning the object ahead of or behind the given OrientedPoint, thereby depending on height

following vectorField [from vector ] for scalar

Positions the object at a point obtained by following the given vector field for the given distance starting from ego

Specifiers for heading

Meaning

facing heading

Orients the object along the given heading in global coordinates

facing vectorField

Orients the object along the given vector field at the object’s position

facing (toward | away from) vector

Orients the object toward/away from the given position (thereby depending on the object’s position)

apparently facing heading [from vector]

Orients the object so that it has the given heading with respect to the line of sight from ego (or from the position given by the optional from vector)

Operators

Diagram illustrating several operators.

Illustration of several operators. Each OrientedPoint (e.g. P) is shown as a bold arrow.

Scalar Operators

Meaning

relative heading of heading [from heading]

The relative heading of the given heading with respect to ego (or the heading provided with the optional from heading)

apparent heading of OrientedPoint [from vector ]

The apparent heading of the OrientedPoint, with respect to the line of sight from ego (or the position provided with the optional from vector )

distance [from vector ] to vector

The distance to the given position from ego (or the position provided with the optional from vector )

angle [from vector ] to vector

The heading to the given position from ego (or the position provided with the optional from vector)

Boolean Operators

Meaning

(Point | OrientedPoint) can see (vector | Object)

Whether or not a position or Objectis visible from a Point or OrientedPoint. V

(vector | Object) in region

Whether a position or Object lies in the region

Heading Operators

Meaning

scalar deg

The given heading, interpreted as being in degrees

vectorField at vector

The heading specified by the vector field at the given position

direction relative to direction

The first direction, interpreted as an offset relative to the second direction

Vector Operators

Meaning

vector (relative to | offset by) vector

The first vector, interpreted as an offset relative to the second vector (or vice versa)

vector offset along direction by vector

The second vector, interpreted in a local coordinate system centered at the first vector and oriented along the given direction

Region Operators

Meaning

visible region

The part of the given region visible from ego

region visible from (Point | OrientedPoint)

The part of the given region visible from the given Point/OrientedPoint

OrientedPoint Operators

Meaning

vector relative to OrientedPoint

The given vector, interpreted in the local coordinate system of the OrientedPoint

OrientedPoint offset by vector

Equivalent to vector relative to OrientedPoint above

(front | back | left | right) of Object

The midpoint of the corresponding edge of the bounding box of the Object, oriented along its heading

(front | back) (left | right) of Object

The corresponding corner of the Object’s bounding box, also oriented along its heading

Statements

Syntax

Meaning

import module

Imports a Scenic or Python module

param identifier = value, …

Defines global parameters of the scenario

require boolean

Defines a hard requirement

mutate identifier, … [by number ]

Enables mutation of the given list of objects

Scenic Syntax Reference

Primitive Data Types

Scalars

representing distances, angles, etc. as floating-point numbers, which can be sampled from various distributions

Vectors

representing positions and offsets in space, constructed from coordinates with the syntax X @ Y (inspired by Smalltalk). By convention, coordinates are in meters, although the semantics of Scenic does not depend on this. More significantly, the vector syntax is specialized for 2-dimensional space. The 2D assumption dramatically simplifies much of Scenic’s syntax (particularly that dealing with orientations, as we will see below), while still being adequate for a variety of applications. However, it is important to note that the fundamental ideas of Scenic are not specific to 2D, and it would be easy to extend our implementation of the language to support 3D space.

Headings

representing orientations in space. Conveniently, in 2D these can be expressed using a single angle (rather than Euler angles or a quaternion). Scenic represents headings in radians, measured anticlockwise from North, so that a heading of 0 is due North and a heading of π/2 is due West. We use the convention that the heading of a local coordinate system is the heading of its y-axis, so that, for example, -2 @ 3 means 2 meters left and 3 ahead.

Vector Fields

associating an orientation (i.e. a heading) to each point in space. For example, a vector field could represent the shortest paths to a destination, or the nominal traffic direction on a road

Regions

representing sets of points in space. Scenic provides a variety of ways to define Regions: rectangles, circular sectors, line segments, polygons, occupancy grids, and explicit lists of points. Regions can have an associated vector field giving points in the region preferred orientations. For example, a Region representing a lane of traffic could have a preferred orientation aligned with the lane, so that we can easily talk about distances along the lane, even if it curves. Another possible use of preferred orientations is to give the surface of an object normal vectors, so that other objects placed on the surface face outward by default.

Position Specifiers

offset along direction by vector

Positions the object at the given coordinates, in a local coordinate system centered at ego and oriented along the given direction (which, if a vector field, is evaluated at ego to obtain a heading)

(left | right) of vector [by scalar]

Depends on heading and width. Without the optional by scalar, positions the object immediately to the left/right of the given position; i.e., so that the midpoint of the object’s right/left edge is at that position. If by scalar is used, the object is placed further to the left/right by the given distance.

(ahead of | behind) vector [by scalar]

As above, except placing the object ahead of or behind the given position (so that the midpoint of the object’s back/front edge is at that position); thereby depending on heading and height.

beyond vector by vector [from vector]

Positions the object at coordinates given by the second vector, in a local coordinate system centered at the first vector and oriented along the line of sight from the ego. For example, beyond taxi by 0 @ 3 means 3 meters directly behind the taxi as viewed by the camera.

(in | on) region

Positions the object uniformly at random in the given Region. If the Region has a preferred orientation (a vector field), also optionally specifies heading to be equal to that orientation at the object’s position.

(left | right) of (OrientedPoint | Object) [by scalar]

Positions the object to the left/right of the given OrientedPoint, depending on the object’s width. Also optionally specifies heading to be the same as that of the OrientedPoint. If the OrientedPoint is in fact an Object, the object being constructed is positioned to the left/right of its left/right edge.

following vectorField [from vector ] for scalar

Positions the object at a point obtained by following the given vector field for the given distance starting from ego (or the position optionally provided with from vector ). Optionally specifies heading to be the heading of the vector field at the resulting point. Uses a forward Euler approximation of the continuous vector field

Heading Specifiers

apparently facing heading [from vector]

Orients the object so that it has the given heading with respect to the line of sight from ego (or from the position given by the optional from vector). For example, apparently facing 90 deg orients the object so that the camera views its left side head-on

Scalar Operators

angle [from vector ] to vector

The heading to the given position from ego (or the position provided with the optional from vector ). For example, if angle to taxi is zero, then taxi is due North of ego

Boolean Operators

(Point | OrientedPoint) can see (vector | Object)

Whether or not a position or Objectis visible from a Point or OrientedPoint. Visible regions are defined as follows: a Point can see out to a certain distance, and an OrientedPoint restricts this to the circular sector along its heading with a certain angle. A position is then visible if it lies in the visible region, and an Object is visible if its bounding box intersects the visible region. Note that Scenic’s visibility model does not take into account occlusion, although this would be straightforward to add

(vector | Object) in region

Whether a position or Object lies in the region; for the latter, the Object’s bounding box must be contained in the region. This allows us to use the predicate in two ways

Heading Operators

scalar deg

The given heading, interpreted as being in degrees. For example 90 deg evaluates to π/2

direction relative to direction

The first direction, interpreted as an offset relative to the second direction. For example, -5 deg relative to 90 deg is simply 85 deg. If either direction is a vector field, then this operator yields an expression depending on the position property of the object being specified

Vector Operators

vector (relative to | offset by) vector

The first vector, interpreted as an offset relative to the second vector (or vice versa). For example, 5@5 relative to 100@200 is 105@205. Note that this polymorphic operator has a specialized version for instances of OrientedPoint, defined below (so for example -3@0 relative to taxi will not use this vector version, even though the Object taxi can be coerced to a vector)

vector offset along direction by vector

The second vector, interpreted in a local coordinate system centered at the first vector and oriented along the given direction (which, if a vector field, is evaluated at the first vector to obtain a heading)

vector relative to OrientedPoint

The given vector, interpreted in the local coordinate system of the OrientedPoint. So for example 1 @ 2 relative to ego is 1 meter to the right and 2 meters ahead of ego

Statements

import module

Imports a Scenic or Python module. This statement behaves as in Python, but when importing a Scenic module M it also imports any objects created and requirements imposed in M. Scenic also supports the form from module import identifier, … , which as in Python imports the module plus one or more identifiers from its namespace

param identifier = value, …

Defines global parameters of the scenario. These have no semantics in Scenic, simply having their values included as part of the generated scene, but provide a general-purpose way to encode arbitrary global information

require boolean

Defines a hard requirement, requiring that the given condition hold in all instantiations of the scenario. As noted above, this is equivalent to an observe statement in other probabilistic programming languages

mutate identifier, … [by number ]

Enables mutation of the given list of objects, adding Gaussian noise with the given standard deviation (default 1) to their position and heading properties. If no objects are specified, mutation applies to every Object already created

Supported Simulators

Scenic is designed to be easily interfaced to any simulator (see Interfacing to New Simulators). On this page we list interfaces that we and others have developed; if you have a new interface, let us know and we’ll list it here!

CARLA

Our interface to the CARLA simulator enables using Scenic to describe autonomous driving scenarios. This interface is part of the VerifAI toolkit; documentation and examples can be found in the VerifAI repository (the Scenic repository also has several other example scenarios).

Grand Theft Auto V

The interface to Grand Theft Auto V, used in our PLDI paper, allows Scenic to position cars within the game as well as to control the time of day and weather conditions. Many examples using the interface (including all scenarios from the paper) can be found in examples/gta. See the paper and scenic.simulators.gta for documentation.

Importing scenes into GTA V and capturing rendered images requires a GTA V plugin, which you can find here.

LGSVL (coming soon)

We have developed an interface to the LGSVL Simulator for autonomous driving, used in our ITSC 2020 paper. This interface will be released shortly.

Webots

We have several interfaces to the Webots robotics simulator, for different use cases.

Note

Our interfaces were written for the R2018 version of Webots, which is not free but has lower hardware requirements than R2019. Relatively minor changes would be required to make our interfaces work with the newer open source versions of Webots. We may get around to porting them eventually; we’d also gladly accept a pull request!

X-Plane

Our interface to the X-Plane flight simulator enables using Scenic to describe aircraft taxiing scenarios. This interface is part of the VerifAI toolkit; documentation and examples can be found in the VerifAI repository.

Interfacing to New Simulators

To interface Scenic to a new simulator, there are two steps: using the Scenic API to compile scenarios and generate scenes, and writing a Scenic library defining the virtual world provided by the simulator.

Using the Scenic API

Compiling a Scenic scenario is easy: just call the scenic.scenarioFromFile function with the path to a Scenic file (there’s also a variant scenic.scenarioFromString which works on strings). This returns a Scenario object representing the scenario; to sample a scene from it, call its generate method. Scenes are represented by Scene objects, from which you can extract the objects and their properties as well as the values of the global parameters (see the Scene documentation for details).

Defining a World Model

To make writing scenarios for your simulator easier, you should write a Scenic library specifying all the relevant information about the simulated world. This “world model” could include:

  • Scenic classes (subclasses of Object) corresponding to types of objects in the simulator;

  • instances of Region corresponding to locations of interest (e.g. one for each road);

  • a Workspace specifying legal locations for objects (and optionally providing methods for schematically rendering scenes);

  • any other information that might be useful in scenarios.

Then any Scenic programs for your simulator can import this world model and make use of the information within.

Each of the simulators natively supported by Scenic has a corresponding model.sc file containing its world model. See the Supported Simulators page for links to the module under scenic.simulators for each simulator, where the world model can be found. The scenic.simulators.webots.mars model is particularly simple and would be a good place to start.

Scenic Internals

This section of the documentation describes the implementation of Scenic. It is not intended for ordinary users of Scenic, and will probably only be useful for people who need to make some change to the language (e.g. adding a new type of distribution).

The documentation is organized by the submodules of the main scenic module:

scenic.core

Scenic’s core types and associated support code.

scenic.simulators

World models and associated code for particular simulators.

scenic.syntax

The Scenic compiler and associated support code.

The scenic module itself provides two functions as the top-level interface to Scenic:

scenarioFromFile(path, cacheImports=False)[source]

Compile a Scenic file into a Scenario.

Parameters
  • path (str) – path to a Scenic file

  • cacheImports (bool) – Whether to cache any imported Scenic modules. The default behavior is to not do this, so that subsequent attempts to import such modules will cause them to be recompiled. If it is safe to cache Scenic modules across multiple compilations, set this argument to True. Then importing a Scenic module will have the same behavior as importing a Python module.

Returns

A Scenario object representing the Scenic scenario.

scenarioFromString(string, filename='<string>', cacheImports=False)[source]

Compile a string of Scenic code into a Scenario.

The optional filename is used for error messages.

Publications Using Scenic

Main Papers

The main paper on Scenic is:

Scenic: A Language for Scenario Specification and Scene Generation.
Fremont, Dreossi, Ghosh, Yue, Sangiovanni-Vincentelli, and Seshia.
PLDI 2019. [full version]

An expanded version of this paper appears as Chapters 5 and 8 of this thesis:

Algorithmic Improvisation. [thesis]
Daniel J. Fremont.
Ph.D. dissertation, 2019 (University of California, Berkeley; Group in Logic and the Methodology of Science).

Scenic is also integrated into the VerifAI toolkit, which is described in another paper:

VerifAI: A Toolkit for the Formal Design and Analysis of Artificial Intelligence-Based Systems.
Dreossi*, Fremont*, Ghosh*, Kim, Ravanbakhsh, Vazquez-Chanlatte, and Seshia.

* Equal contribution.

Case Studies

We have also used Scenic in several industrial case studies:

Formal Analysis and Redesign of a Neural Network-Based Aircraft Taxiing System with VerifAI.
Fremont, Chiu, Margineantu, Osipychev, and Seshia.
Formal Scenario-Based Testing of Autonomous Vehicles: From Simulation to the Real World.
Fremont, Kim, Pant, Seshia, Acharya, Bruso, Wells, Lemke, Lu, and Mehta.

Other Papers Building on Scenic

A Programmatic and Semantic Approach to Explaining and Debugging Neural Network Based Object Detectors.
Kim, Gopinath, Pasareanu, and Seshia.

Credits

If you use Scenic, we request that you cite our PLDI 2019 paper.

Scenic is primarily maintained by Daniel J. Fremont.

The Scenic project was started at UC Berkeley in Sanjit Seshia’s research group.

The language was developed by Daniel J. Fremont, Tommaso Dreossi, Shromona Ghosh, Xiangyu Yue, Alberto L. Sangiovanni-Vincentelli, and Sanjit A. Seshia.

Edward Kim assisted in putting together this documentation.

The Scenic tool has benefitted from code contributions from:

  • Johnathan Chiu

  • Francis Indaheng

  • Martin Jansa (LG Electronics, Inc.)

  • Wilson Wu

Finally, many other people provided helpful advice and discussions, including:

  • Ankush Desai

  • Alastair Donaldson

  • Andrew Gordon

  • Jonathan Ragan-Kelley

  • Sriram Rajamani

  • Marcell Vazquez-Chanlatte

Indices and Tables

License

Scenic is distributed under the 3-Clause BSD License.