最近组里想在诸如NVIDIA TX2之类的硬件上搞自动驾驶,用以研究自动驾驶框架和算法性能。真车是真的搞不起,因为购置和维护成本太高,场地搭建不现实,测试安全性风险也太高,只能用软件模拟搭个沙盘才能过得了生活。自动驾驶模拟器自然应该在服务器上跑,自动驾驶软件又应该在板子上跑;模拟器就相当是个赛车游戏,板子就是个车手。问题来了:如何将一个硬件接入模拟器?鉴于机器人项目一般使用ROS作为中间件,而ROS本身是分布式的,只要模拟器和自动驾驶框架支持ROS或者有ROS的转接口就可以了。

自动驾驶软件方面,在目前开源的两大框架Apolloautoware之中,由于阿波罗太过沉重,板子上跑不动,我选择用autoware。autoware按使用的中间件不同分两版本,其中autoware.ai使用老版的ROS1,autoware.auto使用新的ROS2;由于后者还需要大量折腾,我选择旧版autoware。

模拟器方面,目前流行的开源方案有lgsvlcarla,它们功能都相当强大:可以模拟自然环境、道路建筑、交通信号、行人车辆、各种天气情况等,还有诸如GPS、IMU、摄像头、激光雷达等传感器,并提供ROS接口供软件接入。

  • lgsvl是LG开源的模拟器,使用unity引擎,有许多官方企业合作,图形界面做的很完整,自带有一键傻瓜式的连接Apollo或者autoware的例程,自带了一个几条道路的小地图;
  • carla是开源社区开发的,使用Unreal4引擎,没啥界面,基本上需要用python调它API才能操作,例程比较充分,自带了七八个小村镇的地图模型。

个人感觉二者功能都可比,不过lgsvl太官方了,不够自由,开放的模型和地图没carla多,所以我选择用carla模拟器。

simulators
simulators

实验设置:

  • 局域网:千兆以太网。
  • 服务器:CPU选用i7-6700,内存32GB;至于显卡,我最开始用P106-100矿卡,性能略低于1060,结果无论是单独跑模拟器还是驾驶软件都非常勉强,还严重连累了图形界面的响应速度;后来升级到泰坦X(12GB)之后模拟器和自动驾驶都可以同时实时跑。
  • 操作系统:服务器上Ubuntu 18.04或者20.04都亲测能跑,嵌入式端最好用Ubuntu 18.04。
  • 嵌入式:我首先试了4G内存版的树莓派4,它启动autoware时候直接爆内存;又尝试TX2,8GB内存加开zram swap之后惊险地跑起来了,但是目测处理速度只有一两帧每秒,经常因为localization模块处理不过来而撞车;最后我升级到NVIDIA AGX Xavier,32GB内存,相传它比TX2高20倍的算力;但是最后发现网络通信成了瓶颈

carla提供了一个carla-autoware的仿真组合,使用docker一键编译和部署,在本机上开carla,而在docker上开autoware。这就好办了,把它docker构建过程依葫芦画瓢在嵌入式端部署一遍就好了。

carla模拟器搭建

直接参考carla quick start guide。由于国内git访问速度相当糟糕,我尽量选择下载release压缩包。

安装

先安装一些依赖包:

# ubuntu20.04已经没有python-pip包了。。
sudo apt install -y python3-pip
pip3 install --user pygame numpy

release中下载CARLA_xx.tar.gz(或者CARLA_xx_RSS.tar.gz,二选一),还有AdditionalMaps_xx.tar.gz。推荐使用最新的0.9.11版本。将CARLA解压,再将AdditionalMaps解压到Import目录中:

mkdir carla-simulator
tar xf CARLA_0.9.11.tar.gz -C carla-simulator
tar xf AdditionalMaps_0.9.11.tar.gz -C carla-simulator/Import

然后一键启动carla:

cd carla-simulator
./CarlaUE4.sh

等上一会儿就会启动carla的world视图,在这里可以用WASD和鼠标来游玩。

carla-world
carla-world

配置操作

对carla的操作全凭它的python API。。可以在另外的电脑上对它操作,不过得把carla-simulator下的PythonAPI文件夹拷贝过去,然后使用PythonAPI/utilPythonAPI/examples里面的例程,它们在import carla时候会引用PythonAPI/carla/dist/的egg包,默认只提供了python2.7和3.7的版本,如果python版本不对还会报错。这时可以做一些hack:如果你用ubuntu18.04自带的python3.6,就需要将carla-0.9.11-py3.7-linux-x86_64.egg拷贝一份为carla-0.9.11-py3.6-linux-x86_64.egg;或者你用的是ubuntu20.04的python3.8,就将它拷贝为carla-0.9.11-py3.8-linux-x86_64.egg,以此类推。

还需要注意,跑这些例程时候得先cd到它们的目录中。。下面列举一些常用操作:

PythonAPI/util/test_connection.py用于测试模拟器的连接情况。需要使用--host指明服务器IP:

$ python3 ./test_connection.py --host 192.168.xxx.xxx
CARLA 61caeef4 connected at 192.168.xxx.xxx:2000.

PythonAPI/util/config.py对carla进行操作。首先使用-l列举天气和地图的配置:

$ python3 ./config.py --host 192.168.xxx.xxx -l
weather presets:

    ClearNoon, ClearSunset, CloudyNoon, CloudySunset, Default,
    HardRainNoon, HardRainSunset, MidRainSunset, MidRainyNoon,
    SoftRainNoon, SoftRainSunset, WetCloudyNoon, WetCloudySunset,
    WetNoon, WetSunset.

available maps:

    Town01, Town01_Opt, Town02, Town02_Opt, Town03, Town03_Opt,
    Town04, Town04_Opt, Town05, Town05_Opt.

随后可以使用--weather换天气,或者--map换地图。参考自带的地图配置。目前默认地图Town03。

不同天气:HardRainSunset, CloudyNoon, ClearSunset, ClearNoon
不同天气:HardRainSunset, CloudyNoon, ClearSunset, ClearNoon
不同地图:Town01, Town02, Town04, Town05
不同地图:Town01, Town02, Town04, Town05

开车

PythonAPI/examples下面有manual_control.pymanual_control_steeringwheel.py可供玩耍。前者是鼠标键盘控制,后者用pygame接口,可以用手柄控制。玩手柄的时候,需要在本文件夹下面新建wheel_config.ini文件映射键位,配置诸如方向盘、油门、刹车的按键编号。可以使用jstest-gtk来确认按键配置:

jstest-gtk
jstest-gtk

安排好按键之后,可以写如下配置文件:

[G29 Racing Wheel]
steering_wheel = 3
throttle = 2
brake = 5
reverse = 0
handbrake = 1

然后就可以愉快的玩耍了。

pygame
pygame

嵌入式端

刷机,安装依赖包

刷机工具问题:在主机上,用NVIDIA sdkmanager来给板子刷机。目前(2021-1)这个刷机工具拒绝在Ubuntu 20.04上运行,如果要强行运行,就要欺骗它:将/etc/os-release改为Ubuntu 18.04的!当然这可能会给别的工具造成一些困扰,到时候再改回去即可。。以下是一个例子:

NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.4 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

Jetpack版本问题:目前autoware工程里面定死CUDA版本要小于等于10.0,凡是10.1、10.2通通报错,如果想省点心的话就给板子刷Jetpack 4.3,它带的是CUDA 10.0。

添加SSD:工程代码体积很大,板子自带的32GB闪存根本不够用。我给AGX Xavier加了个512G的SN750,不过测试读写速度只有1500MB左右,看样子其实可以用更低档次的SSD。可以将整个rootfs都移到SSD上,参考https://github.com/jetsonhacks/rootOnNVMe,这个代码的原理是先从32G的闪存上启动(uboot和内核都没动),然后利用一个systemd service将文件系统chroot到SSD上。首先对SSD分区:可以用fdisk、gparted等,分一个区就行了;接着格式化为ext4文件系统,可以用mkfs.ext4等工具;这两步可以在板子上做,也可以先弄好再装到板子上;然后依次运行这个代码里的copy-rootfs-ssd.shsetup-service.sh;最后重启即可。

改为国内软件源:刷机完成之后,将/etc/apt/sources.list里面的URL改为国内的源,推荐清华源:https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/,注意Ubuntu的非x86的源的URL一般是xxxxx/ubuntu-ports/,而不是xxxxx/ubuntu/

OpenCV版本问题:nvidia的jetson源里面画蛇添足地提供了opencv-4.1.1的包(而且这个包都没开cuda),然而ubuntu源是opencv-3.2.0,而且ros里面很多包都依赖这个版本的opencv。opencv3到4之间有大量接口更改,比如有很多CV_xxx宏定义改成了cv::xxx,又比如配置文件读写通通只认流式接口而不认以前的节点对象;而autoware代码用的是opencv3,要编译通过就有两种选择:改代码(亲测,修完接口之后没问题),不想改代码就降级opencv:

首先使用apt-cache policy libopencv-dev查看都有哪些版本的包:

$ apt-cache policy libopencv-dev
libopencv-dev:
  Installed: 3.2.0+dfsg-4ubuntu0.1
  Candidate: 4.1.1-2-gd5a58aa75
  Version table:
 *** 4.1.1-2-gd5a58aa75 500
        500 https://repo.download.nvidia.com/jetson/common r32/main arm64 Packages
     3.2.0+dfsg-4ubuntu0.1 500
        500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports bionic-updates/universe arm64 Packages
        500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports bionic-security/universe arm64 Packages
        100 /var/lib/dpkg/status
     3.2.0+dfsg-4build2 500
        500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports bionic/universe arm64 Packages

这里我选择3.2.0+dfsg-4ubuntu0.1版本的,重新安装libopencv-dev,并且用apt-mark hold锁死它

sudo apt install --reinstall libopencv-dev=3.2.0+dfsg-4ubuntu0.1
sudo apt-mark hold libopencv-dev

然后安装ros,参考ros ubuntu安装文档。这里我还是选用清华源:

sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
sudo apt update
sudo apt install ros-melodic-desktop-full

rosdep初始化ROS依赖项。注意国内访问github raw可能会出问题,因此最好买一个梯子。可以使用proxychains4进行代理:

source /opt/ros/melodic/setup.sh
sudo rosdep init
rosdep update
# 这两步会访问https://raw.githubusercontent.com/,可能需要代理:
# sudo apt install proxychains4
# 装完之后记得修改/etc/proxychains4.conf配置代理,记得要开启quiet_mode
# 然后使用proxychains4代理这两个操作:
#   sudo proxychains4 rosdep init
#   proxychains4 rosdep update

接着就可以开始部署autoware了。

编译autoware和bridge

玩嵌入式端之前,先在主机上搭建carla-autoware的docker,确认能跑通了,再将里面的程序从docker里面拷贝出来到板子上部署。在主机上我没碰到什么问题,只是国内访问github相当慢,最好搭个代理;编译也要花上一段时间,耐心等待。

在板子上,为了加快编译速度,我将板子CPU性能拉到最高(8核@2.265GHz),并且开风扇散热:

sudo nvpmodel -m 0
sudo sh -c 'echo 100 > /sys/devices/pwm-fan/target_pwm'

在板子上,先建立工程目录,并启动ROS环境:

mkdir ~/autoware.ai/src -p
source /opt/ros/melodic/setup.sh

在主机上,将carla-autoware的docker里面~/Autoware/src打包出来,拷贝到板子的~/autoware.ai/src里。

以下配置均在板子上操作,参考autoware源码编译carla-autoware docker例程

安装依赖项:

sudo apt install -y python-catkin-pkg python-rosdep \
  ros-$ROS_DISTRO-catkin python3-pip \
  python3-colcon-common-extensions \
  python3-setuptools python3-vcstool
pip3 install -U setuptools

升级eigen库以支持cuda:

cd && wget http://bitbucket.org/eigen/eigen/get/3.3.7.tar.gz #Download Eigen
mkdir eigen && tar xf 3.3.7.tar.gz -C eigen #Decompress
cd eigen && mkdir build && cd build && cmake .. && make && make install #Build and install
cd && rm -rf 3.3.7.tar.gz && rm -rf eigen #Remove downloaded and temporary files

安装autoware的依赖库:

cd ~/autoware.ai
rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO

最后开始编译:

AUTOWARE_COMPILE_WITH_CUDA=1 colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release

编译carla的PythonAPI

板子作为client,还需要carla的python包以和carla服务器交互,这需要在板子上编译carla的client部分。参考carla的linux编译流程,首先下载完整的源代码。我们新建文件夹carla-0.9.11并将代码解压进去。

安装依赖项:

sudo apt install build-essential \
  clang-8 lld-8 g++-7 cmake \
  ninja-build libvulkan1 python \
  python-pip python-wheel python-dev \
  python3-dev python3-pip libpng-dev \
  libtiff5-dev libjpeg-dev tzdata sed \
  curl unzip autoconf libtool rsync \
  libxml2-dev ros-melodic-ackermann-msgs \
  ros-melodic-derived-object-msgs
pip2 install --user distro
pip3 install --user distro

然后将clang-8拷贝为clang,否则后面会找不到clang:

cd /usr/bin/
sudo ln -s clang-8 clang
sudo ln -s clang++-8 clang++
cd - # cd空格减号回车:切换回到原来的目录

接着修改Util/BuildTools/Setup.sh,将CARLA_VERSION直接定义为"0.9.11"而不是通过git去找,因为这是release代码包,并没有git仓库:

@@ -465,7 +465,8 @@
 # -- Generate Version.h --------------------------------------------------------
 # ==============================================================================

-CARLA_VERSION=$(get_git_repository_version)
+#CARLA_VERSION=$(get_git_repository_version)
+CARLA_VERSION="0.9.11"

 log "CARLA version ${CARLA_VERSION}."

随后开始编译。编译时会下载一堆东西导致很慢,耐心等待。。。

# 编译python2,因为carla-autoware例程只用python2
make PythonAPI ARGS="--python-version=2"

最后在PythonAPI/carla/dist/下就会生成aarch64的python egg包。将它们拷贝出来,比方说放在~/PythonAPI/目录下面,那么就要将PYTHONPATH环境变量指向那里。可以在~/.bashrc里面添加:

export PYTHONPATH=$PYTHONPATH:~/PythonAPI/carla-0.9.11-py2.7-linux-aarch64.egg

编译carla ros bridge

首先安装依赖项:

sudo apt install python-pip python-wheel \
  ros-melodic-ackermann-msgs \
  ros-melodic-derived-object-msgs \
  libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev \
  libsdl2-mixer-dev libsdl2-gfx-dev \
  libsdl2-net-dev libportmidi-dev
pip install simple-pid pygame networkx==2.2

下载代码,姑且放在~/目录:

cd
# bridge代码
git clone -b '0.9.10.1' --recurse-submodules https://github.com/carla-simulator/ros-bridge.git
# 地图配置和点云数据,2.4G,git仓库另占有2.4G
git clone --recurse-submodules https://github.com/carla-simulator/carla-autoware
# 以上两步可以直接从docker里面拷出来,反正它们都已经下载过了。注意它们的.git目录就不需要拷贝了

建立ros工作目录为~/carla_ws

mkdir -p ~/carla_ws/src
cd ~/carla_ws/src
ln -s ~/ros-bridge
ln -s ~/carla-autoware/carla-autoware-agent

编译它:

cd ~/carla_ws
source /opt/ros/melodic/setup.bash
catkin_make

最后在~/.bashrc最后追加设置CARLA_AUTOWARE_CONTENTS环境变量,指向地图目录:

export CARLA_AUTOWARE_CONTENTS=~/carla-autoware/autoware-contents

运行

在服务器上启动carla,即运行CarlaUE4.sh。然后用python API切换地图,详见上文。假设切为Town01。

在板子的图形界面里打开一个终端,初始化autoware环境:

source ~/carla_ws/devel/setup.bash
source ~/autoware.ai/install/setup.bash

最后开始运行例程,需要设置townhost参数:

roslaunch carla_autoware_agent carla_autoware_agent.launch town:=Town01 host:=192.168.xxx.xxx

它启动了一个rviz界面,可以通过它进行操作:

启动rviz
启动rviz
设置目的地,开始自动驾驶
设置目的地,开始自动驾驶

看上去可以跑了,然而在AGX Xavier板子上运行仍未达到实时,转弯之后还是撞车了;CPU和内存利用率都还好,但是千兆网络下行速度80MBps左右,考虑到给板子scp大文件时候也基本上这个速度,可以认为网络IO已经跑满了。想必瓶颈就在ros以及跟服务器的网络通信上了,这将是以后需要优化的目标,可选的方案:数据压缩、减少连接个数、RDMA。