Top-down Renderer
2D Top-down renderer is widely used in this documentation for rendering the results, as it is lightweight and can run on any platforms without GPU requirement. If your system has poor support for OpenGL like Apple M1/M2 chips, a good choice is to use top-down renderer. And the simulation results are exactly the same using either 3D renderer or top-down renderer.
Lifetime
You are free to launch this renderer at any timestep by calling env.render(mode="topdown")
. The renderer will be created and work until the env.reset()
is called. It will shutdown the top_down_renderer
and destroy it. Thus the lifetime of a renderer is the period between calling env.render
for the first time and executing next env.reset
.
The following example running an environment for 100 steps. It launches the renderer when episode_step=50, and thus the generated gif only records the last 50 frames. Also, it demonstrate how to record screen and generate gif.
from metadrive.envs import MetaDriveEnv
from IPython.display import Image
from metadrive.utils import print_source, get_source
import cv2
env = MetaDriveEnv(dict(log_level=50))
env.reset()
for i in range(100):
env.step([0,0])
if i>=50:
env.render(mode="topdown",
window=False,
screen_size=(400, 200),
screen_record=True,
text={"Step": i})
env.top_down_renderer.generate_gif()
print("Before reset the renderer is", env.top_down_renderer)
env.reset()
print("After reset the renderer is", env.top_down_renderer)
env.close()
Image(open("demo.gif", 'rb').read())
error: XDG_RUNTIME_DIR not set in the environment.
Before reset the renderer is <metadrive.engine.top_down_renderer.TopDownRenderer object at 0x7efee2657910>
After reset the renderer is None
Configs
The env.render()
accepts parameters like screen_size
, window
and so on as input which defines the behavior of the top-down renderer. Note that these parameters only take effect when you call env.render
for the first time in one episode.
All accepted arguments for creating the top-down renderer are as follows.
from metadrive.engine.top_down_renderer import TopDownRenderer
from metadrive.utils import CONFIG, FUNC_2
print_source(TopDownRenderer.__init__, ["def", "# doc-end"], colorscheme=FUNC_2)
def __init__(
self,
film_size=(2000, 2000), # draw map in size = film_size/scaling. By default, it is set to 400m
scaling=5, # None for auto-scale
screen_size=(800, 800),
num_stack=15,
history_smooth=0,
show_agent_name=False,
camera_position=None,
target_agent_heading_up=False,
target_vehicle_heading_up=None,
draw_target_vehicle_trajectory=False,
semantic_map=False,
semantic_broken_line=True,
draw_contour=True,
window=True,
screen_record=False,
):
"""
Launch a top-down renderer for current episode. Usually, it is launched by env.render(mode="topdown") and will
be closed when next env.reset() is called or next episode starts.
Args:
film_size: The size of the film used to draw the map. The unit is pixel. It should cover the whole map to
ensure it is complete in the rendered result. It works with the argument scaling to select the region
to draw. For example, (2000, 2000) film size with scaling=5 can draw any maps whose width and height
less than 2000/5 = 400 meters.
scaling: The scaling determines how many pixels are used to draw one meter.
screen_size: The size of the window popped up. It shows a region with width and length = screen_size/scaling
num_stack: How many history steps to keep. History trajectory will show in faded color. It should be > 1
history_smooth: Smoothing the trajectory by drawing less history positions. This value determines the sample
rate. By default, this value is 0, meaning positions in previous num_stack steps will be shown.
show_agent_name: Draw the name of the agent.
camera_position: Set the (x,y) position of the top_down camera. If it is not specified, the camera will move
with the ego car.
target_agent_heading_up: Whether to rotate the camera according to the ego agent's heading. When enabled,
The ego car always faces upwards.
target_vehicle_heading_up: Deprecated, use target_agent_heading_up instead!
draw_target_vehicle_trajectory: Whether to draw the ego car's whole trajectory without faded color
semantic_map: Whether to draw semantic color for each object. The color scheme is in TopDownSemanticColor.
semantic_broken_line: Whether to draw broken line for semantic map
draw_contour: Whether to draw a counter for objects
window: Whether to pop up the window. Setting it to 'False' enables off-screen rendering
screen_record: Whether to record the episode. The recorded result can be accessed by
env.top_down_renderer.screen_frames or env.top_down_renderer.generate_gif(file_name, fps)
"""
#
Region Size in Screen
If you wanna adjust the region size shown on the screen/window, change scaling
to a reasonable value. The region size in meter is determined by screen_size[0]/scaling
and screen_size[1]/scaling
. For example, if your screen size is (1200, 800) and scaling is 5, then it draws a 240m x 160m region.
To demonstrate this, The following example draws exactly the same region with different screen_size
and scaling
.
env = MetaDriveEnv(dict(log_level=50, num_scenarios=1, map="X"))
env.reset()
frame_1 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(400, 200), scaling=4)
env.reset()
frame_2 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(200, 100), scaling=2)
env.reset()
frame_3 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(100, 50), scaling=1)
env.reset()
frame_4 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(200, 100), scaling=1)
env.close()
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 5)) # You can adjust the figsize as needed
axes[0][0].imshow(frame_1)
axes[0][0].axis('off') # Turn off axis
axes[0][0].set_title("screen_size=(400, 200), scaling=4")
axes[0][1].imshow(frame_2)
axes[0][1].axis('off') # Turn off axis
axes[0][1].set_title("screen_size=(200, 100), scaling=2")
axes[1][0].imshow(frame_3)
axes[1][0].axis('off') # Turn off axis
axes[1][0].set_title("screen_size=(100, 50), scaling=1")
axes[1][1].imshow(frame_4)
axes[1][1].axis('off') # Turn off axis
axes[1][1].set_title("screen_size=(200, 100), scaling=1")
plt.subplots_adjust(wspace=0.05)
plt.show()
Map Region Size
The Map region size is determined by film_size
and scaling
like how to determine the region shown in window. Users have to make sure the map region size exceeds the actual map size to make sure the map is shown complete. Usually, maps in MetaDrive are smaller than 400m x 400m. Thus the default film_size=(2000, 2000)
and scaling=5
are able to handle most cases.
If you find the map in top-down rendering is incomplete, consider increase the film_size
or decrease the scaling
. The following example shows what will happen if the film_size is too small.
env = MetaDriveEnv(dict(log_level=50, num_scenarios=1, map="X"))
env.reset()
frame_1 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(800, 400), scaling=4, film_size=(200, 200))
map_1 = env.top_down_renderer.get_map()
env.reset()
frame_2 = env.render(mode="topdown", window=False, camera_position=(50, 7.5),
screen_size=(800, 400), scaling=4, film_size=(400, 400))
map_2 = env.top_down_renderer.get_map()
env.close()
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 5))
axes[0][0].imshow(map_1)
axes[0][0].axis('off') # Turn off axis
axes[0][0].set_title("Map region 50m x 50m")
axes[0][1].imshow(map_2)
axes[0][1].axis('off') # Turn off axis
axes[0][1].set_title("Map region 100m x 100m")
axes[1][0].imshow(frame_1)
axes[1][0].axis('off') # Turn off axis
axes[1][0].set_title("Rendering result")
axes[1][1].imshow(frame_2)
axes[1][1].axis('off') # Turn off axis
axes[1][1].set_title("Rendering result")
plt.subplots_adjust(wspace=0.05)
plt.show()
Semantic Top-down View
The top-down view can be changed to semantic view by adding semantic_map=True
when creating the renderer.
from metadrive.envs import ScenarioEnv
env = ScenarioEnv(dict(log_level=50,
num_scenarios=2))
env.reset(seed=0)
frame_1 = env.render(mode="topdown", window=False,
screen_size=(800, 800), scaling=5)
env.reset(seed=0)
frame_2 = env.render(mode="topdown", window=False,
screen_size=(800, 800), scaling=5, semantic_map=True)
env.reset(seed=1)
frame_3 = env.render(mode="topdown", window=False,
screen_size=(800, 800), scaling=5)
env.reset(seed=1)
frame_4 = env.render(mode="topdown", window=False,
screen_size=(800, 800), scaling=5, semantic_map=True)
env.close()
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 10)) # You can adjust the figsize as needed
axes[0][0].imshow(frame_1)
axes[0][0].axis('off') # Turn off axis
axes[0][0].set_title("Seed: 0, Normal")
axes[0][1].imshow(frame_2)
axes[0][1].axis('off') # Turn off axis
axes[0][1].set_title("Seed: 0, Semantic View")
axes[1][0].imshow(frame_3)
axes[1][0].axis('off') # Turn off axis
axes[1][0].set_title("Seed: 1, Normal")
axes[1][1].imshow(frame_4)
axes[1][1].axis('off') # Turn off axis
axes[1][1].set_title("Seed: 1, Semantic View")
plt.subplots_adjust(wspace=0.)
plt.show()