diff --git a/.gitignore b/.gitignore index 6ef363893..8efdd286c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,5 @@ Util/Build .vs __pycache__ _benchmarks_results -_images +_images* core - diff --git a/CHANGELOG.md b/CHANGELOG.md index f52b32893..0b631ea12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## CARLA 0.7.1 + + * New Python API module: Benchmark + - Defines a set of tasks and conditions to test a certain agent + - Contains a starting benchmark, CoRL2017 + - Contains Agent Class: Interface for benchmarking AIs + * New Python API module: Basic Planner (Temporary Hack) + - Provide routes for the agent + - Contains AStar module to find the shortest route + * Other Python API improvements + - Converter class to convert between Unreal world and map units + - Metrics module to summarize benchmark results + * Send vehicle's roll, pitch, and yaw to client (orientation is now deprecated) + * New RoutePlanner class for assigning fixed routes to autopilot (IntersectionEntrance has been removed) + * Create a random engine for each vehicle, which greatly improves repeatability + * Add option to skip content download in Setup.sh + * Few small fixes to the city assets + ## CARLA 0.7.0 * New Python client API @@ -9,7 +27,7 @@ - Better documentation - Protocol: renamed "ai_control" to "autopilot_control" - Merged testing client - - Added the maps for both cities, the client can now access the car position within the lane. + - Added the maps for both cities, the client can now access the car position within the lane * Make CARLA start without client by default * Added wind effect to some trees and plants * Improvements to the existing weather presets diff --git a/Docs/Example.CarlaSettings.ini b/Docs/Example.CarlaSettings.ini index 8de855982..a9e904672 100644 --- a/Docs/Example.CarlaSettings.ini +++ b/Docs/Example.CarlaSettings.ini @@ -1,4 +1,6 @@ ; Example of settings file for CARLA. +; +; Use it with `./CarlaUE4.sh -carla-settings=Path/To/This/File`. [CARLA/Server] ; If set to false, a mock controller will be used instead of waiting for a real diff --git a/Package.sh b/Package.sh new file mode 100755 index 000000000..72b62e664 --- /dev/null +++ b/Package.sh @@ -0,0 +1,156 @@ +#! /bin/bash + +################################################################################ +# Packages a CARLA build. +################################################################################ + +set -e + +DOC_STRING="Makes a packaged version of CARLA for distribution. +Please make sure to run Rebuild.sh before!" + +USAGE_STRING="Usage: $0 [-h|--help] [--skip-packaging]" + +# ============================================================================== +# -- Parse arguments ----------------------------------------------------------- +# ============================================================================== + +DO_PACKAGE=true +DO_COPY_FILES=true +DO_TARBALL=true + +OPTS=`getopt -o h --long help,skip-packaging -n 'parse-options' -- "$@"` + +if [ $? != 0 ] ; then echo "$USAGE_STRING" ; exit 2 ; fi + +eval set -- "$OPTS" + +while true; do + case "$1" in + --skip-packaging ) + DO_PACKAGE=false + shift ;; + -h | --help ) + echo "$DOC_STRING" + echo "$USAGE_STRING" + exit 1 + ;; + * ) + break ;; + esac +done + +# ============================================================================== +# -- Set up environment -------------------------------------------------------- +# ============================================================================== + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +pushd "$SCRIPT_DIR" >/dev/null + +REPOSITORY_TAG=`git describe --tags --dirty --always` + +echo "Packaging version '$REPOSITORY_TAG'." + +UNREAL_PROJECT_FOLDER=${PWD}/Unreal/CarlaUE4 +DIST_FOLDER=${PWD}/Dist +BUILD_FOLDER=${DIST_FOLDER}/${REPOSITORY_TAG} + +function fatal_error { + echo -e "\033[0;31mERROR: $1\033[0m" + exit 1 +} + +function log { + echo -e "\033[0;33m$1\033[0m" +} + +# ============================================================================== +# -- Package project ----------------------------------------------------------- +# ============================================================================== + +if $DO_PACKAGE ; then + + pushd "$UNREAL_PROJECT_FOLDER" >/dev/null + + log "Packaging the project..." + + if [ ! -d "${UE4_ROOT}" ]; then + fatal_error "UE4_ROOT is not defined, or points to a non-existant directory, please set this environment variable." + fi + + rm -Rf ${BUILD_FOLDER} + mkdir ${BUILD_FOLDER} + + ${UE4_ROOT}/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \ + -project="${PWD}/CarlaUE4.uproject" \ + -noP4 -platform=Linux \ + -clientconfig=Development -serverconfig=Development \ + -cook -compile -build -stage -pak -archive \ + -archivedirectory="${BUILD_FOLDER}" + + popd >/dev/null + +fi + +if [[ ! -d ${BUILD_FOLDER}/LinuxNoEditor ]] ; then + fatal_error "Failed to package the project!" +fi + +# ============================================================================== +# -- Copy files (Python server, README, etc) ----------------------------------- +# ============================================================================== + +if $DO_COPY_FILES ; then + + DESTINATION=${BUILD_FOLDER}/LinuxNoEditor + + log "Copying extra files..." + + cp -v ./LICENSE ${DESTINATION}/LICENSE + cp -v ./CHANGELOG.md ${DESTINATION}/CHANGELOG + cp -v ./Docs/release_readme.md ${DESTINATION}/README + cp -v ./Docs/Example.CarlaSettings.ini ${DESTINATION}/Example.CarlaSettings.ini + + rsync -vhr --delete --delete-excluded \ + --exclude "__pycache__" \ + --exclude "*.pyc" \ + --exclude ".*" \ + PythonClient/ ${DESTINATION}/PythonClient + + echo + +fi + +# ============================================================================== +# -- Zip the project ----------------------------------------------------------- +# ============================================================================== + +if $DO_TARBALL ; then + + DESTINATION=${DIST_FOLDER}/CARLA_${REPOSITORY_TAG}.tar.gz + SOURCE=${BUILD_FOLDER}/LinuxNoEditor + + pushd "$SOURCE" >/dev/null + + log "Packaging build..." + + rm -f ./Manifest_NonUFSFiles_Linux.txt + rm -Rf ./CarlaUE4/Saved + rm -Rf ./Engine/Saved + + tar -czvf ${DESTINATION} * + + popd >/dev/null + +fi + +# ============================================================================== +# -- ...and we are done -------------------------------------------------------- +# ============================================================================== + +echo "" +echo "****************" +echo "*** Success! ***" +echo "****************" + +popd >/dev/null diff --git a/PythonClient/test/test_repeatability.py b/PythonClient/test/test_repeatability.py new file mode 100755 index 000000000..a37d83a3c --- /dev/null +++ b/PythonClient/test/test_repeatability.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de +# Barcelona (UAB), and the INTEL Visual Computing Lab. +# +# This work is licensed under the terms of the MIT license. +# For a copy, see . + +"""Client that runs two servers simultaneously to test repeatability.""" + +import argparse +import logging +import os +import random +import sys +import time + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + +from carla.client import make_carla_client +from carla.sensor import Camera, Image +from carla.settings import CarlaSettings +from carla.tcp import TCPConnectionError + + +def run_carla_clients(args): + filename = '_images_repeatability/server{:d}/{:0>6d}.png' + with make_carla_client(args.host1, args.port1) as client1: + logging.info('1st client connected') + with make_carla_client(args.host2, args.port2) as client2: + logging.info('2nd client connected') + + settings = CarlaSettings() + settings.set( + SynchronousMode=True, + SendNonPlayerAgentsInfo=True, + NumberOfVehicles=50, + NumberOfPedestrians=50, + WeatherId=random.choice([1, 3, 7, 8, 14])) + settings.randomize_seeds() + + if args.images_to_disk: + camera = Camera('DefaultCamera') + camera.set_image_size(800, 600) + settings.add_sensor(camera) + + scene1 = client1.load_settings(settings) + scene2 = client2.load_settings(settings) + + number_of_player_starts = len(scene1.player_start_spots) + assert number_of_player_starts == len(scene2.player_start_spots) + player_start = random.randint(0, max(0, number_of_player_starts - 1)) + logging.info( + 'start episode at %d/%d player start (run forever, press ctrl+c to cancel)', + player_start, + number_of_player_starts) + + client1.start_episode(player_start) + client2.start_episode(player_start) + + frame = 0 + while True: + frame += 1 + + meas1, sensor_data1 = client1.read_data() + meas2, sensor_data2 = client2.read_data() + + player1 = meas1.player_measurements + player2 = meas2.player_measurements + + images1 = [x for x in sensor_data1.values() if isinstance(x, Image)] + images2 = [x for x in sensor_data2.values() if isinstance(x, Image)] + + control1 = player1.autopilot_control + control2 = player2.autopilot_control + + try: + assert len(images1) == len(images2) + assert len(meas1.non_player_agents) == len(meas2.non_player_agents) + assert player1.transform.location.x == player2.transform.location.x + assert player1.transform.location.y == player2.transform.location.y + assert player1.transform.location.z == player2.transform.location.z + assert control1.steer == control2.steer + assert control1.throttle == control2.throttle + assert control1.brake == control2.brake + assert control1.hand_brake == control2.hand_brake + assert control1.reverse == control2.reverse + except AssertionError: + logging.exception('assertion failed') + + if args.images_to_disk: + assert len(images1) == 1 + images1[0].save_to_disk(filename.format(1, frame)) + images2[0].save_to_disk(filename.format(2, frame)) + + client1.send_control(control1) + client2.send_control(control2) + + +def main(): + argparser = argparse.ArgumentParser(description=__doc__) + argparser.add_argument( + '-v', '--verbose', + action='store_true', + dest='debug', + help='print debug information') + argparser.add_argument( + '--log', + metavar='LOG_FILE', + default=None, + help='print output to file') + argparser.add_argument( + '--host1', + metavar='H', + default='127.0.0.1', + help='IP of the first host server (default: 127.0.0.1)') + argparser.add_argument( + '-p1', '--port1', + metavar='P', + default=2000, + type=int, + help='TCP port to listen to the first server (default: 2000)') + argparser.add_argument( + '--host2', + metavar='H', + default='127.0.0.1', + help='IP of the second host server (default: 127.0.0.1)') + argparser.add_argument( + '-p2', '--port2', + metavar='P', + default=3000, + type=int, + help='TCP port to listen to the second server (default: 3000)') + argparser.add_argument( + '-i', '--images-to-disk', + action='store_true', + help='save images to disk') + + args = argparser.parse_args() + + logging_config = { + 'format': '%(levelname)s: %(message)s', + 'level': logging.DEBUG if args.debug else logging.INFO + } + if args.log: + logging_config['filename'] = args.log + logging_config['filemode'] = 'w+' + logging.basicConfig(**logging_config) + + logging.info('listening to 1st server at %s:%s', args.host1, args.port1) + logging.info('listening to 2nd server at %s:%s', args.host2, args.port2) + + while True: + try: + + run_carla_clients(args) + + except TCPConnectionError as error: + logging.error(error) + time.sleep(1) + + +if __name__ == '__main__': + + try: + main() + except KeyboardInterrupt: + print('\nCancelled by user. Bye!') diff --git a/README.md b/README.md index f2a803b6d..4cdce0fef 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ environmental conditions. For instructions on how to use and compile CARLA, check out [CARLA Documentation](http://carla.readthedocs.io). -If you want to benchmark your model in the same conditions as in our CoRL’17 paper, check out [Benchmarking](https://github.com/carla-simulator/carla/blob/benchmark_branch/Docs/benchmark.md). +If you want to benchmark your model in the same conditions as in our CoRL’17 +paper, check out +[Benchmarking](http://carla.readthedocs.io/en/latest/benchmark/). News ---- diff --git a/Rebuild.sh b/Rebuild.sh index 82dffb79d..09db805e2 100755 --- a/Rebuild.sh +++ b/Rebuild.sh @@ -1,6 +1,46 @@ #!/bin/bash + +################################################################################ +# Updates CARLA content. +################################################################################ + set -e +DOC_STRING="Update CARLA content to the latest version, to be run after 'git pull'." + +USAGE_STRING="Usage: $0 [-h|--help] [--no-editor]" + +# ============================================================================== +# -- Parse arguments ----------------------------------------------------------- +# ============================================================================== + +LAUNCH_UE4_EDITOR=true + +OPTS=`getopt -o h --long help,no-editor -n 'parse-options' -- "$@"` + +if [ $? != 0 ] ; then echo "$USAGE_STRING" ; exit 2 ; fi + +eval set -- "$OPTS" + +while true; do + case "$1" in + --no-editor ) + LAUNCH_UE4_EDITOR=false; + shift ;; + -h | --help ) + echo "$DOC_STRING" + echo "$USAGE_STRING" + exit 1 + ;; + * ) + break ;; + esac +done + +# ============================================================================== +# -- Set up environment -------------------------------------------------------- +# ============================================================================== + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" pushd "$SCRIPT_DIR" >/dev/null @@ -60,8 +100,15 @@ set -e log "Build CarlaUE4 project..." make CarlaUE4Editor -log "Launching UE4Editor..." -${UE4_ROOT}/Engine/Binaries/Linux/UE4Editor "${PWD}/CarlaUE4.uproject" +if $LAUNCH_UE4_EDITOR ; then + log "Launching UE4Editor..." + ${UE4_ROOT}/Engine/Binaries/Linux/UE4Editor "${PWD}/CarlaUE4.uproject" +else + echo "" + echo "****************" + echo "*** Success! ***" + echo "****************" +fi popd >/dev/null diff --git a/Unreal/CarlaUE4/Config/DefaultGame.ini b/Unreal/CarlaUE4/Config/DefaultGame.ini index 8a71b87ee..4c9370430 100644 --- a/Unreal/CarlaUE4/Config/DefaultGame.ini +++ b/Unreal/CarlaUE4/Config/DefaultGame.ini @@ -3,7 +3,7 @@ ProjectID=675BF8694238308FA9368292CC440350 ProjectName=CARLA UE4 CompanyName=CVC CopyrightNotice="Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB). This work is licensed under the terms of the MIT license. For a copy, see ." -ProjectVersion=0.7.0 +ProjectVersion=0.7.1 [/Script/UnrealEd.ProjectPackagingSettings] BuildConfiguration=PPBC_Development diff --git a/Unreal/CarlaUE4/Plugins/Carla/Carla.uplugin b/Unreal/CarlaUE4/Plugins/Carla/Carla.uplugin index 4bef00a01..678cec47d 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Carla.uplugin +++ b/Unreal/CarlaUE4/Plugins/Carla/Carla.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "0.7.0", + "VersionName": "0.7.1", "FriendlyName": "CARLA", "Description": "", "Category": "Science", diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/VehicleSpawnerBase.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/VehicleSpawnerBase.cpp index 198e7b199..f71401893 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/VehicleSpawnerBase.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/VehicleSpawnerBase.cpp @@ -104,7 +104,7 @@ void AVehicleSpawnerBase::SpawnVehicleAtSpawnPoint( Vehicle->SpawnDefaultController(); auto Controller = GetController(Vehicle); if (Controller != nullptr) { // Sometimes fails... - Controller->SetRandomEngine(GetRandomEngine()); + Controller->GetRandomEngine()->Seed(GetRandomEngine()->GenerateSeed()); Controller->SetRoadMap(GetRoadMap()); Controller->SetAutopilot(true); Vehicles.Add(Vehicle); diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.cpp index 795a656af..b2b887941 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.cpp @@ -73,6 +73,8 @@ static void ClearQueue(std::queue &Queue) AWheeledVehicleAIController::AWheeledVehicleAIController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + RandomEngine = CreateDefaultSubobject(TEXT("RandomEngine")); + PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.TickGroup = TG_PrePhysics; } diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.h index 02d9972f2..fa623f08e 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/AI/WheeledVehicleAIController.h @@ -98,14 +98,10 @@ public: /// @{ public: - void SetRandomEngine(URandomEngine *InRandomEngine) - { - RandomEngine = InRandomEngine; - } - UFUNCTION(Category = "Random Engine", BlueprintCallable) URandomEngine *GetRandomEngine() { + check(RandomEngine != nullptr); return RandomEngine; } @@ -221,13 +217,13 @@ private: private: UPROPERTY() - ACarlaWheeledVehicle *Vehicle; + ACarlaWheeledVehicle *Vehicle = nullptr; UPROPERTY() - URoadMap *RoadMap; + URoadMap *RoadMap = nullptr; UPROPERTY() - URandomEngine *RandomEngine; + URandomEngine *RandomEngine = nullptr; UPROPERTY(VisibleAnywhere) bool bAutopilotEnabled = false; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp index 71d326f6b..7afbb80be 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp @@ -176,7 +176,8 @@ void ACarlaGameModeBase::BeginPlay() VehicleSpawner->SetSeed(CarlaSettings.SeedVehicles); VehicleSpawner->SetRoadMap(RoadMap); if (PlayerController != nullptr) { - PlayerController->SetRandomEngine(VehicleSpawner->GetRandomEngine()); + PlayerController->GetRandomEngine()->Seed( + VehicleSpawner->GetRandomEngine()->GenerateSeed()); } } else { UE_LOG(LogCarla, Error, TEXT("Missing vehicle spawner actor!")); diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.cpp index a823cd793..09f2bebf9 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.cpp @@ -17,3 +17,10 @@ int32 URandomEngine::GenerateRandomSeed() std::numeric_limits::max()); return Distribution(RandomDevice); } + +int32 URandomEngine::GenerateSeed() +{ + return GetUniformIntInRange( + std::numeric_limits::lowest(), + std::numeric_limits::max()); +} diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.h index db786fade..e3539ca69 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/RandomEngine.h @@ -18,13 +18,19 @@ class URandomEngine : public UObject public: // =========================================================================== - /// @name Set and get seed + /// @name Seed // =========================================================================== /// @{ + /// Generate a non-deterministic random seed. UFUNCTION(BlueprintCallable) static int32 GenerateRandomSeed(); + /// Generate a seed derived from previous seed. + UFUNCTION(BlueprintCallable) + int32 GenerateSeed(); + + /// Seed the random engine. UFUNCTION(BlueprintCallable) void Seed(int32 InSeed) { diff --git a/Update.sh b/Update.sh index 61f0807ad..35043f193 100755 --- a/Update.sh +++ b/Update.sh @@ -1,17 +1,19 @@ #! /bin/bash ################################################################################ -# Updates CARLA contents. +# Updates CARLA content. ################################################################################ set -e +DOC_STRING="Update CARLA content to the latest version, to be run after 'git pull'." + +USAGE_STRING="Usage: $0 [-h|--help] [-s|--skip-download]" + # ============================================================================== # -- Parse arguments ----------------------------------------------------------- # ============================================================================== -USAGE_STRING="Usage: $0 [-h|--help] [-s|--skip-download]" - SKIP_DOWNLOAD=false OPTS=`getopt -o hs --long help,skip-download -n 'parse-options' -- "$@"` @@ -26,6 +28,7 @@ while true; do SKIP_DOWNLOAD=true; shift ;; -h | --help ) + echo "$DOC_STRING" echo "$USAGE_STRING" exit 1 ;; diff --git a/Util/ContentVersions.txt b/Util/ContentVersions.txt index 160075361..3a6553be4 100644 --- a/Util/ContentVersions.txt +++ b/Util/ContentVersions.txt @@ -7,3 +7,4 @@ 0.5.4: 0B2HFV-VuKn3PYUFFanlmZ2VMTW8 0.6.0: 1Gw8sw01hDEV4FtpHEZZwvS-6XN0jmaLT 0.7.0: 11PKC_lG7L80L1ZxiMlzV17utU45UcRnT +0.7.1: 1gf3YaFIjwr1EaK6qxq2V-a510Brt9Imq