First steps¶
This tutorial connects to the server, moves the spectator to W 125th St and Lenox Ave in Harlem, steps the world in synchronous mode, and renders one view at four times of day and in rain.
Connect¶
client = valdrada.Client(a.host, a.port) # retries until the server accepts the connection
client.set_timeout(120.0)
world = client.get_world() # waits until the simulator has booted
Client retries until the server accepts the connection, and get_world() waits until the simulator has booted,
which takes a minute or two on the first start while the shaders compile. set_timeout bounds how long each call
waits for its answer.
Synchronous stepping¶
original = world.get_settings()
world.apply_settings(valdrada.WorldSettings(synchronous_mode=True, fixed_delta_seconds=0.05))
In synchronous mode, the default, the simulation advances by fixed_delta_seconds on each world.tick() and waits
otherwise, so a script can take as long as it needs between steps. tick() returns the new frame number once every
listening sensor has delivered its data for that step. The script restores the original settings before it exits.
The spectator and streaming¶
here = m.geolocation_to_location(*LENOX_125)
world.get_spectator().set_transform(Transform(here + Location(0, 0, 30)))
print("loaded:", world.wait_until_loaded())
junction = m.get_junctions(center=here, radius=40)[0]
The city streams in around the spectator, and renders and map queries see only what has loaded. wait_until_loaded
renders with time frozen until the tiles and buildings around the spectator are in. get_junctions then returns the
intersection from the loaded street network, with its location, its arms and whether it is signalized.
A camera, the time of day and the weather¶
camera = world.spawn_actor(bp, Transform(junction.location + Location(-22, -20, 1.8), Rotation(pitch=2, yaw=45)))
latest = {}
camera.listen(lambda image: latest.update(image=image)) # runs once per tick, before tick() returns
A camera spawned without a parent keeps its world pose. This one stands at eye height on the south-west corner of the
intersection and looks up Lenox Ave with a horizontal field of view of 80°. listen registers a callback that
receives the camera's image on every step.
world.set_weather takes a time of day (day, golden, dusk or night) and an amount of rain between 0 and 1;
the presets of WeatherParameters combine the two. After each change the script runs 60 steps (3 s of simulated
time) so that the lighting and the renderer's temporal history settle, then saves the last image. Rain wets the
streets gradually, and they dry the same way once it stops, so the script restores the weather it found and runs
another 200 steps before it exits; the next script then starts on dry streets.
The script prints:
server 0.1.0, 1280x720, ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Laptop GPU (0x00002520) Direct3D11 vs_5_0 ps_5_0, D3D11)
loaded: {'loaded': True, 'seconds': 57.8, 'tiles': 26, 'macro': 84}
Junction(2160_-2741, Location(x=2167.500, y=2740.000, z=3.385), signalized=True, arms=8) arm headings: [-120, -119, -29, 62, 151]
day frame 60, sim time 3.00 s -> _out/first_steps/day.png
golden frame 120, sim time 6.00 s -> _out/first_steps/golden.png
dusk frame 180, sim time 9.00 s -> _out/first_steps/dusk.png
night frame 240, sim time 12.00 s -> _out/first_steps/night.png
rain frame 300, sim time 15.00 s -> _out/first_steps/rain.png
The complete script¶
"""First steps: connect to the server, place the spectator, step the world in synchronous mode, and change the time
of day and the weather. Saves one RGB image per condition at W 125th St and Lenox Ave.
python first_steps.py [--host 127.0.0.1] [--port 2000] [--out _out/first_steps]
"""
import argparse
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")) # run from a source checkout
import valdrada # noqa: E402
from valdrada import Location, Rotation, Transform, WeatherParameters # noqa: E402
LENOX_125 = (40.80776, -73.94549) # W 125th St & Lenox Ave, Harlem
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", type=int, default=2000)
ap.add_argument("--out", default="_out/first_steps")
a = ap.parse_args()
client = valdrada.Client(a.host, a.port) # retries until the server accepts the connection
client.set_timeout(120.0)
world = client.get_world() # waits until the simulator has booted
info = client.get_server_info()
print(f"server {client.get_server_version()}, {info['resolution'][0]}x{info['resolution'][1]}, {info['gpu']}")
# synchronous mode: the world advances one fixed step per world.tick() and waits in between
original, original_weather = world.get_settings(), world.get_weather()
world.apply_settings(valdrada.WorldSettings(synchronous_mode=True, fixed_delta_seconds=0.05))
# the city streams in around the spectator: move it to the intersection and wait for the tiles
m = world.get_map()
here = m.geolocation_to_location(*LENOX_125)
world.get_spectator().set_transform(Transform(here + Location(0, 0, 30)))
print("loaded:", world.wait_until_loaded())
junction = m.get_junctions(center=here, radius=40)[0]
print(junction, "arm headings:", sorted({round(arm.heading) for arm in junction.arms}))
# an RGB camera at eye height on the south-west corner, looking up Lenox Ave
bp = world.get_blueprint_library().find("sensor.camera.rgb")
bp.set_attribute("fov", 80)
camera = world.spawn_actor(bp, Transform(junction.location + Location(-22, -20, 1.8), Rotation(pitch=2, yaw=45)))
latest = {}
camera.listen(lambda image: latest.update(image=image)) # runs once per tick, before tick() returns
conditions = [("day", WeatherParameters.Day), ("golden", WeatherParameters.Golden),
("dusk", WeatherParameters.Dusk), ("night", WeatherParameters.Night),
("rain", WeatherParameters.RainyDay)]
try:
for name, weather in conditions:
world.set_weather(weather)
for _ in range(60): # 3 s of simulated time: lighting and image history settle
frame = world.tick()
path = latest["image"].save_to_disk(f"{a.out}/{name}.png")
print(f"{name:7s} frame {frame}, sim time {world.get_snapshot().timestamp:6.2f} s -> {path}")
# rain leaves the streets wet, and they dry over a few seconds of simulated time: dry them for the next script
camera.stop()
world.set_weather(original_weather)
for _ in range(200):
world.tick()
finally:
camera.destroy()
world.set_weather(original_weather)
world.apply_settings(original)
if __name__ == "__main__":
main()