diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1e23c1..398c01b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,3 +48,26 @@ jobs: cd scenarionet/ pytest --cov=./ --cov-config=.coveragerc --cov-report=xml -sv tests + + test_ipynb: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Blackbox tests + run: | + pip install cython + pip install numpy + pip install -e . + pip install pytest + pip install pytest-cov + pip install ray + git clone https://github.com/metadriverse/metadrive.git + cd metadrive + pip install -e . + cd ../ + pip install nbmake pytest-xdist + pytest --nbmake -n=auto ./tutorial/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3c6d605..08b068d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ dataset/* **/TEST/ **/experiment/ **/wandb/ +/tutorial/*.gif +/tutorial/.ipynb_checkpoints/ diff --git a/README.md b/README.md index 45db9d8..e2909c1 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # ScenarioNet -**Open-Source Platform for Large-Scale Traffic Scenario Simulation and Modeling** +**Open-Source Platform for Large-Scale Traffic Scenario Simulation and Modeling** ![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg) [ [**Webpage**](https://metadriverse.github.io/scenarionet/) | [**Code**](https://github.com/metadriverse/scenarionet) | [**Video**](https://youtu.be/3bOqswXP6OA) | [**Paper**](http://arxiv.org/abs/2306.12241) | -[**Documentation**](https://scenarionet.readthedocs.io/en/latest/) +[**Documentation**](https://scenarionet.readthedocs.io/en/latest/) | +[**Colab Example**](https://colab.research.google.com/github/metadriverse/scenarionet/blob/main/tutorial/simulation.ipynb) ] ScenarioNet allows users to load scenarios from real-world dataset like Waymo, nuPlan, nuScenes, l5 and synthetic diff --git a/tutorial/simulation.ipynb b/tutorial/simulation.ipynb new file mode 100644 index 0000000..67b416e --- /dev/null +++ b/tutorial/simulation.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "J1w-zrxAUr4l", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Quick Start Tutorial of Scenario Simulation\n", + "\n", + "Welcome to try out MetaDrive & ScenarioNet!\n", + "\n", + "The simulation supports two running modes:\n", + "\n", + "1. **With 3D rendering functionality**: MetaDrive can easily install and run in personal computer, but may need special treatments for 3D rendering in headless machine and cloud servers.\n", + "\n", + "2. **Without 3D rendering functionality**: MetaDrive can easily install and run in any machine. In this Colab notebook, we demonstrate MetaDrive in this mode and the renderer will be the **2D** **Pygame** renderer.\n", + "\n", + "In this tutorial, we will navigate you through the installation and some basic functionality of the simulator!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2efvTXdHVptN", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Installation\n", + "\n", + "You can install MetaDrive easily." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "65J2iQKpUQ1B", + "outputId": "60411b22-c699-4db0-bf23-13b4c83c4d4f", + "pycharm": { + "name": "#%%\n" + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "#@title Collect the MetaDrive & ScenarioNet\n", + "# NOTE: If you are running this notebook locally with installtion finished, this step is not required.\n", + "RunningInCOLAB = 'google.colab' in str(get_ipython()) # Detect if it is running in Colab\n", + "if RunningInCOLAB:\n", + " %pip install git+https://github.com/metadriverse/metadrive.git\n", + " %pip install git+https://github.com/metadriverse/scenarionet.git" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's create a 2D visualization tool for recording the scenario in GIF." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# visualization\n", + "from IPython.display import Image as IImage\n", + "import pygame\n", + "import numpy as np\n", + "from PIL import Image\n", + "\n", + "def make_GIF(frames, name=\"demo.gif\"):\n", + " print(\"Generate gif...\")\n", + " imgs = [pygame.surfarray.array3d(frame) for frame in frames]\n", + " imgs = [Image.fromarray(img) for img in imgs]\n", + " imgs[0].save(name, save_all=True, append_images=imgs[1:], duration=50, loop=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration\n", + "\n", + "Let's import some modules and specify the dataset directory.\n", + "**Note: if your machine supports 3D OpenGL rendering, you can turn on the *threeD_render* flag in the following cell. It will render both the 2D results and 3D results.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#@title Make some configurations and import some modules\n", + "from metadrive.engine.engine_utils import close_engine\n", + "close_engine()\n", + "from metadrive.pull_asset import pull_asset\n", + "pull_asset(False)\n", + "# NOTE: usually you don't need the above lines. It is only for avoiding a potential bug when running on colab\n", + "\n", + "from metadrive.engine.asset_loader import AssetLoader\n", + "from metadrive.policy.replay_policy import ReplayEgoCarPolicy\n", + "from metadrive.envs.scenario_env import ScenarioEnv\n", + "import os\n", + "\n", + "threeD_render=False # turn on this to enable 3D render. It only works when you have a screen and not running on Colab.\n", + "threeD_render=threeD_render and not RunningInCOLAB\n", + "os.environ[\"SDL_VIDEODRIVER\"] = \"dummy\" # Hide the pygame window\n", + "waymo_data = AssetLoader.file_path(AssetLoader.asset_path, \"waymo\", return_raw_style=False) # Use the built-in datasets with simulator\n", + "nuscenes_data = AssetLoader.file_path(AssetLoader.asset_path, \"nuscenes\", return_raw_style=False) # Use the built-in datasets with simulator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "os.listdir(waymo_data) # there are 3 waymo scenario file with a 'dataset_summary.pkl'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "os.listdir(nuscenes_data) # there are 10 nuscenes scenario file with a 'dataset_summary.pkl' and a 'dataset_summary.pkl'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulate one Waymo scenario\n", + "The simulation interface is in gym-style and let's create a environment first. \n", + "By specifying the *data_directory*, we can load the Waymo dataset to simulation. *num_scenarios* is used to determine how many scenarios are loaded from the datasets. Here we only load one scenario from the Waymo dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = ScenarioEnv(\n", + " {\n", + " \"manual_control\": False,\n", + " \"reactive_traffic\": False,\n", + " \"use_render\": threeD_render,\n", + " \"agent_policy\": ReplayEgoCarPolicy,\n", + " \"data_directory\": waymo_data,\n", + " \"num_scenarios\": 1\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the simulation can run with *env.step()* and *env.reset(seed=scenario-index)*. Their functions are as follows.\n", + "\n", + "- The *env.reset(seed=scenario-index)* tells the simulator to remove all existing objects created in last episode, create a new scenario with index *scenario-index* and start a new episode.\n", + "**As we only have one scenario loaded to the simulator, the *scenario-index* can only be 1 in this example.**\n", + "\n", + "\n", + "- *env.step()* will progress the simulation by one step (0.1 second) and return the new observation, reward and termination flag. It takes an action as input which is a 2-dim vector representing the throttle and steering angle for the ego car.\n", + "As we are using the *ReplayEgoCarPolicy* here, the ego car will not take the external action accepted from *env.step()*.\n", + "**Instead, the ego car will follow the recorded trajectory. Thus the following code is for playing one recorded scenario including maps and trajetcories.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# @title Run Simulation\n", + "\n", + "o, _ = env.reset(seed=0)\n", + "frames = []\n", + "for i in range(1, 100000):\n", + " o, r, tm, tc, info = env.step([1.0, 0.])\n", + " frames.append(env.render(mode=\"top_down\",film_size=(1200, 1200)))\n", + " if tm or tc:\n", + " break\n", + "env.close()\n", + "\n", + "make_GIF(frames)\n", + "# visualization\n", + "IImage(open(\"demo.gif\", 'rb').read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulate Multiple nuScenes scenarios\n", + "For simulating multiple scenarios, just modify the *num_scenarios* and use *env.reset(seed=target_index)* to select the scenario of interest.\n", + "This example loading all scenarios into simulator but only simulate and visualize 2 of them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = ScenarioEnv(\n", + " {\n", + " \"manual_control\": False,\n", + " \"reactive_traffic\": False,\n", + " \"use_render\": False,\n", + " \"agent_policy\": ReplayEgoCarPolicy,\n", + " \"data_directory\": nuscenes_data, # use nuscenes data\n", + " \"num_scenarios\": 10, # load 10 scenarios\n", + " }\n", + ")\n", + "\n", + "for seed in range(2): # only simulate the first 2 scenarios\n", + " print(\"\\nSimulate Scenario: {}\".format(seed))\n", + " o, _ = env.reset(seed=seed)\n", + " frames = []\n", + " for i in range(1, 100000):\n", + " o, r, tm, tc, info = env.step([1.0, 0.])\n", + " frames.append(env.render(mode=\"top_down\",film_size=(4000, 4000), screen_size=(500, 500)))\n", + " if tm or tc:\n", + " make_GIF(frames, name=\"scenario_{}.gif\".format(seed))\n", + " break\n", + "\n", + "env.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install imageio\n", + "\n", + "import imageio\n", + "import numpy as np \n", + "\n", + "#Create reader object for the gif\n", + "gif1 = imageio.get_reader('scenario_0.gif')\n", + "gif2 = imageio.get_reader('scenario_1.gif')\n", + "\n", + "\n", + "#If they don't have the same number of frame take the shorter\n", + "number_of_frames = min(gif1.get_length(), gif2.get_length())-1\n", + "\n", + "#Create writer object\n", + "new_gif = imageio.get_writer('output.gif')\n", + "\n", + "for frame_number in range(number_of_frames):\n", + " img1 = gif1.get_next_data()\n", + " img2 = gif2.get_next_data()\n", + " #here is the magic\n", + " new_image = np.hstack((img1, img2))\n", + " new_gif.append_data(new_image)\n", + "\n", + "gif1.close()\n", + "gif2.close() \n", + "new_gif.close()\n", + "\n", + "# visulization\n", + "IImage(open(\"output.gif\", 'rb').read())" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}