Gym-Duckietown is a simulator for the Duckietown Universe, written in pure Python/OpenGL (Pyglet). It places your agent, a Duckiebot, inside of an instance of a Duckietown: a loop of roads with turns, intersections, obstacles, Duckie pedestrians, and other Duckiebots.
There are multiple registered gym environments, each corresponding to a different map file.
You can find the map files here.
The simulator supports a YAML-based file format which is designed to be easy to hand edit.
Each map file has two main sections:
tiles: represents the map itself, i.e., defines the grid layout. It is composed by several rows where each row is an array of tiles descriptions.
objects: listing of 3D objects placed on the map. Each object has 3 parameters: pos, height, and rotate.
For example, take a look at the following map file.
tiles:
- [floor , floor , floor , floor , floor , floor , floor , floor]
- [floor , curve_left/W , straight/W, 3way_left/W , straight/W, straight/W , curve_left/N , asphalt]
- [floor , straight/S , grass , straight/N , asphalt , asphalt , straight/N , asphalt]
- [floor , 3way_left/S , straight/W, 3way_left/N , asphalt , asphalt , straight/N , asphalt]
- [floor , straight/S , grass , straight/N , asphalt , curve_right/N , curve_left/E , asphalt]
- [floor , curve_left/S , straight/E, 3way_left/E , straight/E, curve_left/E , asphalt , asphalt]
- [floor , floor , floor , floor , floor , floor , floor , floor]
objects:
- kind: tree
pos: [2.5, 4.5]
rotate: 180
height: 0.25
optional: true
- kind: duckie
pos: [2.5, 2.9]
rotate: -90
height: 0.08
optional: true
- kind: sign_stop
pos: [2.08, 4.05]
rotate: 90
height: 0.18
tile_size: 0.585
The top-left tile in this file maps to coordinates (0, 0) in the world. The x-coordinate increases as we go through in each row, and the y-coordinate increases as we go through the columns, that is, the lines in the map files. Thus, at coordinate (0, 0) in the world it has floor, and at coordinate (6, 4) in the world it is curve_left/E.
Each tile contains a string with the kind of tile and its orientation (North, South, East, West). This is the direction in which the agent is expected to drive when going onto this tile.
The available tile types are:
empty
straight
curve_left
curve_right
3way_left (3-way intersection)
3way_right
4way (4-way intersection)
asphalt
grass
floor (office floor)
The several kinds of tiles are divided into drivable tiles ("straight", "curve_left", "curve_right", "3way_left", "3way_right" and "4way") and non-drivable ("empty", "floor", "asphalt", "grass").
The available object types are:
barrier
cone (traffic cone)
duckie
duckiebot (model of a Duckietown robot)
tree
house
truck (delivery-style truck)
bus
building (multi-floor building)
sign_stop, sign_T_intersect, sign_yield, etc. (see meshes subdirectory for the other supported objects)
Although the environment is rendered in 3D, the map is essentially two-dimensional. As such, objects coordinates are specified along two axes.
The pos parameter represents the object's position. The object positions are scaled according to the tile size, so that (1.5, 2.5) would be exactly in the middle of tile (1, 2), that is, in middle of the second column of tiles, middle of the third row.
The height parameter represents the object's height, and serves as a scaling factor for the objects. Objects can be scaled according to their real-world height in meters (for example, 0.25 meters).
The rotate parameter represents the object's orientation, expressed by the rotation angle. A positive rotation angle corresponds to a counter-clockwise (leftward) rotation, whereas a negative rotation corresponds to a clockwise rotation. The angles are in degrees. Note that, models should, at angle 0, face towards the positive X axis.
Objects can have an optional flag set, which means that they randomly may or may not appear during training, as a form of domain randomization.
Regarding the size of the tiles, we can define it using the tile_size parameter (mandatory).
Additionally, there are other (optional) parameters we can define in a map's file, such the initial position where the agent starts (start_tile parameter) and the initial starting pose (start_pose parameter).
The observations are single camera images, as numpy arrays of size (120, 160, 3). These arrays contain unsigned 8-bit integer values in the [0, 255] range.
This image size was chosen because it is exactly one quarter of the 640x480 image resolution provided by the camera, which makes it fast and easy to scale down the images. The choice of 8-bit integer values over floating-point values was made because the resulting images are smaller if stored on disk and faster to send over a networked connection.
The simulator uses continuous actions by default. Actions passed to the step() function should be numpy arrays containing two numbers between -1 and 1 (could also be a Python list). These two numbers correspond to forward velocity, and a steering angle, respectively.
A positive velocity makes the robot go forward, and a positive steering angle makes the robot turn left.
There is also a Gym wrapper class named DiscreteWrapper that allows you to use discrete actions (turn left, move forward, turn right) instead of continuous actions if you prefer.
The default reward function tries to encourage the agent to drive forward along the right lane in each tile. Each tile has an associated bezier curve defining the path the agent is expected to follow. The agent is rewarded for being as close to the curve as possible, and also for facing the same direction as the curve's tangent. The episode is terminated if the agent gets too far outside of a drivable tile, or if the max_steps parameter is exceeded. See the step function in this source file.