背景

自动驾驶算法的调试和效果评测首先要在仿真环境中去做,因此,一个强大、灵活的仿真环境是开发、测试过程中必不可少的要素。我在查找可用的仿真工具时主要关注以下几个特性:

  • 开源,免费
  • 包含高速场景
  • 可以便捷的控制、切换场景的环境,场景、环境尽可能的丰富与真实
  • 可以便捷的控制、切换场景内移动物体(如车辆、行人等)的行为模式,行为模式尽可能的丰富与真实

CARLA是一个主要由英特尔实验室和巴塞罗那的计算机视觉中心开发的开源项目。包含了自动驾驶系统的3种方法:

  • 经典模块化方法,包含基于视觉的感知模块,基于规则的规划器,还有行为控制器。
  • 端到端的模仿学习方法。
  • 端到端的强化学习方法。

特点:

  • 针对3D的城市场景(urban driving),开源免费,支持感知、规划、控制。
  • 基于Unreal Engine 4,server-client结构。
  • 可安装于Linux和Windows。
  • Python API,没有C++ API。

架构

Carla 是一个开源的模拟器,可以模拟真实的交通环境,行人行为,汽车传感器信号等等。如下图所示,模拟器使用C++ 和虚幻 (Unreal) 引擎构成,使用者可以通过 Python API 使用 Python 脚本代码对模拟器的环境进行操作和控制。

在这里插入图片描述

Carla主要分为Server与Client两个模块,Server端用来建立这个仿真世界,而Client端则是由用户控制,用来调整、变化这个仿真世界。

  • Server: Server端负责任何与仿真本身相关的事情:从3D渲染汽车、街道、建筑,传感器模型的构建,到物理计算等等。它就像一个造物主, 将整个世界建造出来,并且根据Client 的外来指令更新这个世界。它本身是基于UnrealEnigne做出的3D渲染。
  • Client: 如果server构造了整个世界,那么这个世界不同时刻到底该如何运转(比如天气是什么样,有多少辆车在跑,速度是多少)则是由Client端控制的。用户通过书写Python脚本(最新版本C++ 也可以)来向Server端输送指令指导世界的变化,Server根据用户的指令去执行。Client端也可以接受Server端的信息,譬如某个照相机拍到的路面图片。

核心模块

  1. Traffic Manager: 自动驾驶之所以难搞,很核心的一个原因就是现实世界车太多了!因此,Carla专门构造了Traffic Manager这个模块来模拟类似现实世界负责的交通环境。通过这个模块,用户可以定义N多不同车型、不同行为模式、不同速度的车辆在路上愉快地与你的自动驾驶汽车(Ego-Vehicle)一起玩耍。
  2. Sensors: Carla里面有各种各样模拟真实世界的传感器模型,包括相机、激光雷达、声波雷达、IMU、GNSS等等。为了让仿真更接近真实世界,它里面的相机拍出的照片甚至还有畸变和动态模糊效果。用户一般将这些Sensor attach到不同的车辆上来收集各种数据。
  3. Recorder: 俗话说的好,不能复现的仿真不是好仿真。这个模块就是用来记录仿真每一个时刻(Step)的状态,可以用来回顾、复现等等。
  4. ROS bridge: 这个模块可以让Carla与ROS还有Autoware交互,正是这个模块的存在使得在仿真里测试你的自动驾驶系统变得可能,十分重要,后面也会详细讲解。
  5. Open Assest:这个模块可以允许你为仿真世界添加customized的物体库,比如你可以在默认的汽车蓝图里再加一个真实世界不存在、外形酷炫的小飞汽车,用来给Client端调用。

环境

创建城市环境的3个步骤:

  1. 排布道路和人行道
  2. 放置房子、绿植、地形、交通基础设施
  3. 指定动态物体可以出现的地点

传感器

有camera,分别提供RGB图像、深度信息和语义分割信息,语义包含物体的12种分类。

提供有GPS坐标、朝向、速度、加速度、碰撞等数据,以及交通规则评估数据,如行驶过的轨迹占据错误的车道比例等,还提供了所有动态物体的准确位置和bounding boxes。

经典模块

局部规划仅依赖于感知探测到的环境。内部有状态机:车道跟随,左转,右转,路口前进,停车。

PID控制。

感知的语义分割基于RefineNet。

判断是否处于路口基于AlexNet二分类。

特性

  • 通过服务器多客户端体系结构实现的可扩展性:同一节点或不同节点中的多个客户端可以控制不同的参与者。
  • 灵活的 API: CARLA 提供了一个强大的 API,允许用户控制与模拟相关的所有方面,包括交通生成、行人行为、天气、传感器等等。
  • 自动驾驶传感器套件支持:用户可以配置各种传感器套件,包括激光雷达、多摄像头、深度传感器和GPS等。
  • 规划和控制的快速模拟:此模式禁用渲染,以提供不需要图形的交通模拟和道路行为的快速执行。
  • 地图创建功能:用户可以通过 RoadRunner等工具轻松创建自己的符合 OpenDrive 标准的地图。
  • 交通场景仿真:Engine ScenarioRunner 工具允许用户基于模块化行为定义和执行不同的交通场景的仿真。
  • ROS集成:通过我们的ROS-bridge为Carla提供与ROS的集成。
  • 自动驾驶基线 baselines:我们在CARLA中提供作为可运行代理的自动驾驶基线,包括AutoWare代理和条件模仿学习代理。

安装

ubuntu环境下安装流程:

基础软件安装

1
2
3
4
5
6
sudo apt-get update &&
sudo apt-get install wget software-properties-common &&
sudo add-apt-repository ppa:ubuntu-toolchain-r/test &&
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - &&
sudo apt-add-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main" &&
sudo apt-get update

Ubuntu 20.04:

1
2
3
4
sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
sudo apt-get install build-essential clang-10 lld-10 g++-7 cmake ninja-build libvulkan1 python python-dev python3-dev python3-pip libpng-dev libtiff5-dev libjpeg-dev tzdata sed curl unzip autoconf libtool rsync libxml2-dev git
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-10/bin/clang++ 180 &&
sudo update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-10/bin/clang 180

安装Unreal Engine:

1
2
3
4
5
git clone --depth 1 -b carla https://github.com/CarlaUnreal/UnrealEngine.git ~/UnrealEngine_4.26
cd ~/UnrealEngine_4.26
./Setup.sh && ./GenerateProjectFiles.sh && make
cd ~/UnrealEngine_4.26/Engine/Binaries/Linux && ./UE4Editor

编译Carla:

1
2
git clone https://github.com/carla-simulator/carla
./Update.sh

设置UE环境变量,直接写文件 gedit ~/.bashrc

1
export UE4_ROOT=~/UnrealEngine_4.26

编译Carla客户端,可指定版本

1
make PythonAPI ARGS="--python-version=3.7, 3.8"

生成两种不同的文件 egg文件和whl文件,egg免安装,whl和系统有关

1
pip3 install <path/to/wheel>.whl 

编译Carla服务端,并启动:

1
make launch

点击Play或执行测试脚本:

1
2
3
4
5
6
7
cd PythonAPI/examples
python3 -m pip install -r requirements.txt
python3 generate_traffic.py

# Terminal B
cd PythonAPI/examples
python3 dynamic_weather.py

使用

可以使用Python来实现一个客户端来跟Carla仿真环境进行交互,而在Carla Python库中,是以 carla.Client 类来实现的

1
2
client = carla.Client("localhost", 2000)
client.set_timeout(10.0)

client对象是跟Carla环境交互的唯一的入口,有了client对象以后,我们就可以获取到Carla环境中的世界(World)了,World对象确确实实的代表Carla环境中的世界,你想要在世界中创建任何东西,都是往这个World对象中添加

1
world = client.load_world('Town02')

客户端获取到了World对象,并使用了Town02这张由Carla官方提供的内置地图,Carla还提供了其他一些地图,我们可以通过更改load_world的参数来加载不同的地图.

有了world对象以后,我们来直观感受下,我们可以做什么样的交互,比如我们想控制世界的天气和时间(太阳的位置)。

1
2
3
4
5
weather = carla.WeatherParameters(
cloudiness=0.0,
precipitation=0.0,
sun_altitude_angle=50.0)
world.set_weather(weather)

使用carla.WeatherParameters创建了一种天气,万里无云,没有降雨,太阳的角度为50,也就是说是一个大晴天,然后通过世界的set_weather方法修改了世界的天气。

Actors:

Actors我们可以称之为演员,在Carla世界中任何可以通过客户端创建的物体都称为Actors(演员),包括:车辆,行人,传感器等等

Blueprints:

创建一个Actor需要知道这些信息,也就是类似于需要使用一个模板,这个东西在Carla中叫做蓝图,即Blueprints

Carla中已经内置了一个蓝图库,里边包含了许多不同的Actors,比如我们可以创建一台特斯拉的Model 3,我们也可以创建一台BWM,或者雪铁龙等等。

创建一个白色的特斯拉蓝图(模板):

1
model3_bp = world.get_blueprint_library().find('vehicle.tesla.model3') model3_bp.set_attribute('color', '255,255,255') 

world.get_blueprint_library()可以获取到Carla内置的蓝图库,通过find方法找到model 3,我们也可以使用filter方法然后给定模糊匹配的方法获取到多个蓝图,比如,可以用world.get_blueprint_library().filter(‘vehicle.bmw.*’)获取到所有BMW的车型蓝图。每一个蓝图都可以使用set_attribute更改自身的属性,这里我们将颜色设置成了白色。

Actor的生命周期:

一个Actor可以被生成(Spawning),使用(Handling),以及销毁(Destruction)。

  • 生成:有了蓝图之后,意味着我们有了模板,然后就需要使用这个模板生成一个或者多个演员(Actor),这一过程叫做Spawning,因为Actor是在世界中存在的一个物体,因此在生成的时候,需要告诉环境它的出生点在哪里。
  • 使用:当Actor生成以后,就可以通过客户端来控制它的一些行为,比如可以让车子跑起来,并控制它的油门和转向。
  • 销毁:当 我们不在需要一个Actor的时候可以选择销毁它,这样可以释放仿真环境的资源,让环境运行更顺畅。

使用world对象的spawn_actor或者try_spawn_actor方法就可以生成一个Actor,不过上边提到,在生成Actor的时候,需要告诉世界它的出生点在哪里,出生点在Carla中抽象为carla.Transform,

1
transform = Transform(Location(x=230, y=195, z=40), Rotation(yaw=180)) actor = world.spawn_actor(blueprint, transform) 

物体在世界中是有体积的,所以有可能你指定的地点上已经有其他物体了,比如,该位置已经有台车子,或者该位置是一个建筑物,这种情况下,Actor被生成的时候就会出现碰撞,为了避免这个问题,Carla提供了一个接口可以获取到所有空的出生点,只要在这些空的出生点上生成Actor就不会出现碰撞的问题了

1
spawn_points = world.get_map().get_spawn_points() model3_spawn_point = np.random.choice(spawn_points) model3 = world.spawn_actor(model3_bp, model3_spawn_point) 

Actor被生成以后,生成方法会范围被生成的Actor对象,我们可以使用该对象控制Actor的行为。比如,我们可以让刚刚生成的Model 3动起来,使用下边的代码,让它按照交通规则在世界中行驶。

1
model3.set_autopilot(True)

还可以主动的把车挪动位置

1
2
3
location = model3.get_location() 
location.x += 10.0
model3.set_location(location)

不同的Actor类型,可以控制的参数也不一样

不在需要某个Actor的时候,可以将其销毁,释放计算资源

1
destroyed_sucessfully = actor.destroy()

传感器 Sensors:

Sensor是一种特殊的Actor,它的蓝图也是可以在蓝图库里边找到的,目前Carla已经支持了很多传感器,比如

  • 摄像头: Depth, RGB , Semantic segmentation
  • 探测器: Collision , Lane invasion , Obstacle
  • 其他: GNSS , IMU , LIDAR raycast , Radar

传感器跟其他的Actor最大的不同是,它们需要被安装在车上,因此在生成传感器的时候,需要将其附着到一个车辆类型的Actor上,而出生点是针对于这台车本身的坐标系给定的

1
2
3
camera_bp = world.get_blueprint_library().find('sensor.camera.rgb') 
camera = world.spawn_actor(camera_bp, carla.Transform(carla.Location(x=-5.5, z=2.5), carla.Rotation(pitch=8.0)), model3, carla.AttachmentType.SpringArm )
camera.listen(lambda image:image.save_to_disk('output/%06d.png' % image.frame)

首先从蓝图库中找到RGB摄像头模板,然后利用这个蓝图生成摄像头Actor,并将其附着到前边生成好的Model 3上,我们选择了摄像头附着类型为:carla.AttachmentType.SpringArm,并将其位置设置到后方,这样我们就可以像从一个第三者的角度排到行驶的车辆了。

每一个传感器都有一个listen方法,该方法接收一个callback作为参数,我们可以自定义callback里边的逻辑,callback将会在传感器拿到数据后被调用,并能够获取到这些数据。

观察者 spectator:

使用Python脚本生成了一台Model3 ,并让它行驶在路上,我们的Python脚本并没有能力输出视频,我们怎么来确认我们的 Model 3已经被创建,并在路上行驶呢

Carla给我们提供了一个所谓的观察者 Spectator,你可以将其理解为Carla环境的视角,我们可以通过修改观察者的参数,切换Carla环境的视角。

1
2
3
4
5
6
while running:
spectator = world.get_spectator()
transform = model3.get_transform()
spectator.set_transform(carla.Transform(transform.location + carla.Location(z=50),
carla.Rotation(pitch=-90)))
time.sleep(5)

上边这段代码,将观察者的位置到我们的Model 3的正上方(z轴),并将视角调为-90度,即向下看,每隔5s中跟着Model 3的位置重置一下观察者的位置,这样就可以在Model 3的正上方追踪并观察这台车了。

这个项目最后会得到两个输出

  • 我们设置了观察值来实时追踪Model 3的行驶,因此在Carla环境中可以看到车子在行驶
  • 我们在摄像头的callback中添加了保存摄像头采集到的照片到output文件夹的动作,因此我们会在output文件夹中看到很多摄像头采集回来的照片

在这里插入图片描述