This is the source code accompanying the master thesis from Sindre Johansen (@sindreij) and Andreas Løvland (@AndrLov) spring of 2015. This repository should contain everything needed to run the simulation. However, no warranties are given. The following code might work, or it might crash your machine (probably both). The code is given away under apache license for inspiration, but this is not a complete product.
For an overview over the project, take a look at https://highwayflocking.github.io/.
PS: Note that the textures in this project is a bit different from the one presented in the video and the thesis. We needed to change the textures because of licensing reasons.
About the Source Code
This code consists of two components, the Highway Simulator, and the Controller. The Simulator is written in C++, using Unreal Engine. The Controller is written in python. To test the Simulator, you should probably download the release with pre-compiled code, the rest of this documentation is about compiling from source. The following is a list of the top folders and their content.
- Source/ All the C++ source code for the simulator.
- Content/ All the assets for the simulator.
- blender/ The blender files for the assets created by us.
- controller/ The Controller code.
- Config/ Configuration of the simulator. Used by Unreal Editor.
- Build/ Some build files for the simulator, for now just the icon.
- NPRA_Data_Plot/ Code to plot data from NPRA. Data not included.
- README.md - The file you are reading
- HighwayFlocking.uproject - The Unreal Project.
- gui_setup.iss - Inno Setup project file
Installing the requirements
First and foremost, this simulator is written using Windows. It could probably run on Linux and Mac OSX with some tweaking, however it is only tested using Windows, and a few parts of the code is written using windows only SDKs (mainly the replay runner). However, getting it to run on other OSes should only be a few lines of code (pull requests are welcome).
The main requirement is Unreal Engine. You need to register at https://www.unrealengine.com/ to download it, and it is free for non-commercial use. Installing should be simple, and this documentation assumes that you have Unreal Engine working.
Since we are using C++ code, you also need to have a source editor and compiler compatible with Unreal Engine. On Windows, this is Visual Studio.
For the controller to work, you need to have Python 2.7 installed and correctly set up. You also need to install a few requirements, controller/requirements.txt contains a list that should include all requirements needed. To install them, something like the following might work. However, installing matplotlib and numpy might be impossible using the following method, depending on your setup. Installation files for both numpy and matplotlib should be acquirable on the internet.
pip install -r controller/requirements.txt
Compiling and Running the simulator
First, you need to create the Visual Studio project. To do this, right click HighwayFlocking.uproject, and choose Generate Visual Studio project files.
Start Unreal Editor by doubleclicking HighwayFlocking.uproject. You will probably get a messsage saying that UE4Editor-HighwayFlocking.dll is missing. Click Yes to compile the project. When this is done, you should be able to run the project in Unreal Editor. To view the source code in Visual Studio, press File | Open Visual Studio after opening the project in Unreal Editor.
When the simulation have started, try clicking O to start the spawners. Press ESC to close the simulator. When running the simulator, there are lots of keyboard shortcuts that can be used. These are the most important:
- N - Toggle road markings.
- F4 - Toggle HUD.
- ESC - Exit the simulator.
- Left Click - Follow a vehicle.
- As spectator:
- W - Move forward.
- S - Move backward.
- A - Move Left.
- D - Move Right.
- O - Toggle Spawners.
- T - Teleport.
- Shift + Mouse Wheel - Zoom.
- Following a flocking vehicle:
- 1 - Return to spectator.
- Mouse Wheel - Change distance to vehicle.
When the simulator is running in Unreal Editor, you need to package the project for use with the controller. In Unreal Editor, press File | Package Project | Windows | Windows (64 bit) (if you cannot choose 64 bit, make sure you are using the development configuration, or choose 32 bit windows. Note that the 32 bit windows can not play replays larger than 2 GB). Choose the folder controller/ in the project.
Running the controller
When the simulator is packaged, you can run the controller. Make sure that all the required dependencies is installed (see a previous section). The controller consist of four top level files that can be run, gui.py, remote.py, worker.py, and server.py. gui.py is the simplest one, and is a simple GUI where you can try the simulator using the different configs. This is the recommended way of checking if the simulator is working. To start the gui, just doubleclick gui.py.
A Distributed System
The three files remote.py, worker.py and server.py are three parts of a distributed system to run experiments. worker.py is the worker, which gets commands from a server running server.py. remote.py communicates with the server, giving it commands. remote.py can also be used for tasks not requring communications with the server. For an overview over what it can do, run.
python remote.py --help
The system is configured in config.py, to create a local override, create the file local_config.py, use local_config.py.dist as a template.
Before the server can be run, you need to create the required tables. You need to have postgres setup correctly, at least version 9.4. Make sure that the config variable DB_CONNECTION_STRING is correct. Run the following:
python server.py create_tables
The server is just a plain HTTP server using flask. It can be deployed as a wsgi application, or run locally using
python server.py run
To deploy a worker, copy the controller folder (including the packaged simulator) to the worker machine (or use the same machine where the server is running). Make sure that the config variable SERVER_URL is pointing to the server. Doubleclick worker.py to start the worker.
Make sure that SERVER_URL is correct. When everything is running, you should be able to run the following:
$ python remote.py workers 2015-06-08 18:15:08,243 - INFO - _new_conn - Starting new HTTP connection (1): its-015-06.idi.ntnu.no NAME VERSION TIME SINCE SEEN DOING ========== ======== =============== =============== worker1 0.2.1 00:00:03 get_task worker2 0.2.1 00:00:04 get_task worker3 0.2.1 00:00:03 get_task
This should give a list of all the workers you have deployed. To add some tasks to the queue, run for example following.
$ python remote.py add_tasks ONCOMING_ONRAMP_BUS 4000 15000 400 2015-06-08 18:13:29,569 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(4000) 2015-06-08 18:13:29,569 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(4400) 2015-06-08 18:13:29,569 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(4800) 2015-06-08 18:13:29,569 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(5200) ... 2015-06-08 18:13:29,576 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(14000) 2015-06-08 18:13:29,578 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(14400) 2015-06-08 18:13:29,578 - INFO - add_tasks - Adding tasks oncoming_onramp_bus+througput(14800) 2015-06-08 18:13:29,862 - INFO - _new_conn - Starting new HTTP connection (1): its-015-06.idi.ntnu.no
This will add the same tasks that were repeated 30 times for each configuration in the thesis. The first number is that starting throughput, the second number is the stop throughput, and the third is the step. This will create a tasks for each throughput from 4000 to, but not including 15000, with a step of 400 between every throughput.
The workers should now start working on the tasks. To see the uncomplete tasks, run the following:
$ remote.py tasks 2015-06-08 18:19:06,039 - INFO - _new_conn - Starting new HTTP connection (1): its-015-06.idi.ntnu.no ID REQUEST_TIME STARTED_TIME STATUS_TIME %% DONE COMPLETED WORKER CONFIG_NAME ==== ================ ================ ================ ======= ========= ========== ========================== 12024 21.05.2015 15:36 Not started 21.05.2015 17:35 0.0 % False None symetric+througput(10000) 12025 21.05.2015 15:36 Not started 25.05.2015 12:48 0.0 % False None symetric+througput(12000) 12294 08.06.2015 18:13 08.06.2015 18:17 08.06.2015 18:18 16.3 % False worker1 oncoming_onramp_bus+througput(4000) 12296 08.06.2015 18:13 08.06.2015 18:17 08.06.2015 18:18 9.8 % False worker2 oncoming_onramp_bus+througput(4800) 12297 08.06.2015 18:13 08.06.2015 18:18 08.06.2015 18:18 3.3 % False worker3 oncoming_onramp_bus+througput(5200) 12298 08.06.2015 18:13 Not started 08.06.2015 18:13 0.0 % False None oncoming_onramp_bus+througput(5600) 12299 08.06.2015 18:13 Not started 08.06.2015 18:13 0.0 % False None oncoming_onramp_bus+througput(6000) ...
Note that only uncomplete tasks are shown. You can see which tasks the workers are working at, and how many percent done they are. To see a list of the complete results, run: (note how I specify the worker-version I want results from).
$ remote.py results -v 0.2.1 2015-06-08 18:20:49,336 - INFO - _new_conn - Starting new HTTP connection (1): its-015-06.idi.ntnu.no ID TASKID VERSION COMPLETED_TIME WORKER CONFIG_NAME INCIDENTS THROUGPUT === ====== ======= ================ ========== ================================================== ========= ========= 9668 12061 0.2.1 21.05.2015 16:33 its-015-03 oncoming_onramp_bus+througput(5200) 0 5150 9667 12049 0.2.1 21.05.2015 16:32 its-015-15 oncoming_onramp_bus+througput(10800) 0 10078 9666 12060 0.2.1 21.05.2015 16:31 its-015-20 oncoming_onramp_bus+througput(4800) 0 4772 9670 12051 0.2.1 21.05.2015 16:34 its-015-22 oncoming_onramp_bus+througput(11600) 4 10704 9671 12052 0.2.1 21.05.2015 16:35 its-015-09 oncoming_onramp_bus+througput(12000) 0 11018 9669 12050 0.2.1 21.05.2015 16:33 its-015-26 oncoming_onramp_bus+througput(11200) 0 10376 9673 12063 0.2.1 21.05.2015 16:37 its-015-04 oncoming_onramp_bus+througput(6000) 0 5917 9674 12064 0.2.1 21.05.2015 16:38 its-015-08 oncoming_onramp_bus+througput(6400) 0 6229 9672 12062 0.2.1 21.05.2015 16:36 its-015-16 oncoming_onramp_bus+througput(5600) 0 5519 9677 12066 0.2.1 21.05.2015 16:40 its-015-12 oncoming_onramp_bus+througput(7200) 0 6990 9676 12055 0.2.1 21.05.2015 16:39 its-015-18 oncoming_onramp_bus+througput(13200) 6 11876
Note how the number of incidents and the average measured throughput is reported. You can easily get a plot of all runs, you need to specify the configuration you want a plot of, and the workerversion.
$ remote.py plot oncoming_onramp_bus tp -v 0.2.1
To get a plot using R (the plots used in the thesis), you need to append -r. You need to have R installed for this to work.
To get a more thorough look at a single run, you can look at its replay. Take for instance the 5200-throughput run above, note that this has a resultID of 9668. To play the replay, run.
$ remote.py play_replay 9668 2015-06-08 18:26:35,640 - INFO - _new_conn - Starting new HTTP connection (1): its-015-06.idi.ntnu.no 2015-06-08 18:26:54,084 - INFO - download_replay - GUnzipping the replay
This will first download the replay from the server, and then unzip it. This takes some time, however, the replay is cached so that running the command a second time is much faster. The replay will start. The following keyboard shortcuts can be used to control the replay.
- Space - Pause / Unpause the replay
- Arrow Left - Play Reversed (Paused) / Slow down time (Running forwards) / Accelerate time (Running backwards)
- Arrow Right - Play (Paused) / Accelerate time (Running forwards) / Slow down time (Running backwards)
- Arrow Up - Go to next accident (slow, and bug-prone, use caution)
- Arrow Down - Go to previous accident (slow, and bug-prone, use caution)
- F3 - Show behavior arrows
- F5 - Show/Hide HUD.
- F6 - Show vehicle IDs (useful for movie recording)
The remote command have a lot of options, this is a short guide. To get a list of options, run:
Add one task.
python remote.py add_task ONCOMING_ONRAMP_BUS --throughput 5000
Add multiple tasks
python remote.py add_tasks ONCOMING_ONRAMP_BUS 4000 15000 400 -n 2
Show non-completed or all tasks.
Show data for a result
Show all workers
Remove tasks where the worker have abanded the task
Play a replay from a result
Play a replay from a local file (does not connect to the server)
Set the task to not started (and lets a new worker try the task)
Create a movie from a replay, multiple commands to specify the camera to use.
Runs the same task as add_task, but instead of having a worker run the task, the task is run locally. The results are not saved to the server. Useful for testing.
Start the engine in game modus, and record to the replay specified. Useful to create a replay from a specific scenario.