想试验英伟达的Jetson序列套件或者其他公司的类似边缘计算开发板能否跑我们的模型并部署到机器人上,于是买了块今年上市的Jetson Nano板子和一张64G的SD卡。
首先要到英伟达的网站https://developer.nvidia.com/embedded/downloads点击Jetson Nano Developer Kit SD Card Image 然后点Image下载Jetson Nano使用的image,我下载的是jetson-nano-sd-r32.1.1-2019-05-31.zip,5G多下载到本地后,使用工具烧写到SD卡中,有好几款常用工具看个人喜好,比如Etcher、USB Image Tool,我使用的USB Image Tool,在https://usb-image-tool.en.softonic.com/这里下载,无需安装解压后即可以使用,需要Windows .NET框架支持,如果你的Windows没有安装.NET框架的话,运行USB Image Tool.exe会弹出提示要你安装,把SDK卡插入读卡器连到电脑上,然后在USB Image Tool里左侧点击SD卡对应的移动盘,然后点击下方的restore并在弹出窗口里选择刚下载的jetson-nano-sd-r32.1.1-2019-05-31.zip,确认后写入这个Image到SD卡。在Ubuntu系统下,使用系统自带Disks应用程序,点击右上角的More actions图标,选择Restore Disk Image然后选择image按提示操作即可。把Image写入SD卡后把SD卡插入靠CPU散热片这边的板子上一个有点隐蔽的插槽里(这个你不注意看一下可能还发现不了),接下来选择合适电源加电。
可恶的是这种板子没有配带配套电源的,这里就是个大坑,光是折腾电源问题就浪费了半天时间!从网上文章看来很多人中招了!英伟达声称可以使用5V 2A USB电源或DC电源(但实际板子上DC电源标的是5V 4A !!!),于是先找了个5V 2A的USB电源,加电后屏幕上出现了NVIDIA图标后就黑了,板子上电源旁的指示灯也灭了,但是风扇(风扇是另配的,线接在CPU散热架附近的一个四针插头上,接其中右侧三针(以下图为准))一直在响,说明电源没断,问客服怎么回事,说是这板子有高低压保护,电压低于5V时可能会黑掉,就像掉电了一样。看来我手头的USB电源输出不行,标了5V/2A的USB电源不见得能稳定输出符合要求的电压,但一时找不到合适的正规5V/4A电源,于是找了个可变电源,把输出电压调到5V,插上电源线后板子没反映,同事帮助查了下网上资料,晕,原来有个J48两针插头需要跳接后DC电源才能使用,找了个套帽(就是下图中的黄色套帽)跳接了J48,再加电起来了,可变电源显示屏显示输出电流在0.35A-1.0A波动,进入Ubuntu 18.04后,在用Chrome浏览器访问外部网站时突然又黑屏了,发现可变电源显示屏上显示的输出电流降到了0.09A左右,当时输出电压当时低于5V,可能是电压低了,于是把可变电源输出电源调到5.1V左右,再加电,后面使用基本上没发生这情况了,于是接下来开始正式干活安装环境。
另外输出视频有两个口:HDMI和DP,我试了一下,使用HMDI转VGA线没用,一直黑屏,转换线本身没问题,连接我笔记本和独立的大显示器没问题,使用HMDI转DIV(24+1)就可以,使用DP转DIV也可以。
首先,关于Ubuntu软件更新下载源是否需要改成国内的源呢?比如阿里、清华、中科大等,把/etc/apt/source.list备份后,我试验了一下在source.list中增加国内清华和科大源后下载py-faster-rcnn所需的包,结果是没用(其实执行sudo apt-get update和sudo apt-get upgrade时就可以看出来),为什么呢,因为Jetson Nano使用的是ARM64而不是X86架构的芯片,国内源缺乏aarch64对应的软件,所以有些包还得老老实实使用官方的main源下载,对于非常用的包则可以从国内源快速下载,所以增加国内源不是对所有下载都起很大作用,爱加就加吧。
确认一下英伟达在image里给安装好了的CUDA10,检查一下这些目录和文件路径是否存在:
/usr/local/cuda (实际是指向/usr/local/cuda-10.0的链接)
/usr/local/cuda-10.0
并且在~/.bashrc里加入:
export CUDA_HOME=/usr/local/cuda
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
export PATH=/usr/local/cuda/bin:$PATH
然后执行source ~/.bashrc 让设置在当前终端里立即生效,如果不嫌事多,还可执行下列命令检查一下:
(1)执行nvcc -V 看一下输出结果。
(2)执行pkg-config opencv --modversion看输出的OpenCV版本是否是3.3.1
(3)执行下列命令检查cudnn是否正常:
cd /usr/src/cudnn_samples_v7/mnistCUDNN
sudo make
sudo chmod +x mnistCUDNN
./mnistCUDNN
最后看到 Test passed就是正常。
如果你对那些TensorRT、CUDA、cuDNN、OpenCV samples之类的感兴趣,检查一下下列路径应该存在了的:
/usr/src/tensorrt/samples/
CUDA /usr/local/cuda-/samples/
cuDNN /usr/src/cudnn_samples_v7/
OpenCV /usr/share/OpenCV/samples/
然后开始干正经主题活:
先安装各种支持包(模型使用python2.7,以下是基于python2.7的安装):
sudo apt-get install git cmake
sudo apt install cmake-qt-gui
sudo apt-get install libprotobuf-dev
sudo apt-get install libleveldb-dev
sudo apt-get install libsnappy-dev
sudo apt-get install libopencv-dev
sudo apt-get install libhdf5-serial-dev
sudo apt-get install protobuf-compiler
sudo apt-get install libgflags-dev
sudo apt-get install libgoogle-glog-dev
sudo apt-get install liblmdb-dev
sudo apt-get install libatlas-base-dev
sudo apt-get install gfortran
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install libopenblas-dev
sudo apt-get install python-pip
sudo apt-get install python-dev
#sudo pip install opencv-python --default-timeout=600
sudo pip install python-dateutil
sudo apt-get install python-opencv python-numpy python-scipy python-matplotlib python-sklearn python-skimage
python-h5py python-protobuf python-leveldb python-networkx python-nose python-pandas python-gflags cython ipython python-yaml python-paramiko python-thrift
注意 numpy不能是Ubuntu16.04系统默认安装的numpy=1.7.x。上面用命令sudo apt-get install python-numpy安装的python-numpy版本是当前最新的1:1.13.3-2ubuntu1,对于只是跑训练出来的模型没问题,对于模型的训练可能出错,改用pip安装指定版本号 numpy==1.16.0(这个版本对于模型训练没问题,但是跑模型时可能出错,说某些类的属性不存在,这时用pip uninstall numpy卸载掉numpy1.1.6.0,只保留python-numpy 1:1.13.3-2ubuntu1即可,当然,最好是把模型训练和跑模型不要共用同一环境,使用conda env或者docker为各自创建一个运行环境最好),保证numpy版本正确 (1.11.0在便于和运行训练py-faster-rcnn时没问题,但是在跑部署模型的环境下gpu_nms.so 使用的CUDA10.1编译出来的,而不是编译和训练模型时使用的CUDA10.0,会导致下面的错误,这个错误估计使用CUDA10.0下编译出来的gpu_nms.so 应该没有下面这个错误,没时间琢磨,只好先使用numpy1.16.0版绕过去这个问题:
from nms.gpu_nms import gpu_nms
File "__init__.pxd", line 918, in init nms.gpu_nms
ValueError: numpy.ufunc size changed, may indicate binary incompatibility. Expected 216 from C header, got 192 from PyObject
)
sudo pip install -U numpy==1.16.0
使用numpy >1.11.0 版本时又会造成py-faster-rcnn在运行时报错,具体什么错误和这个问题怎么解决最后面说。
#sudo pip install paramiko #等同于sudo apt-get install python-paramiko
sudo pip install easydict Pillow six #cython?
#sudo pip install thrift #等同于sudo apt-get install python-thrift
上面有的包不是用于支持跑模型的,而是支持调用模型的代码的,比如paramiko用于scp远程拷贝文件,thrift用于C/S网络通讯模式封装调用模型,所以以上并非必须全部安装,可根据情况选择安装。
注意:
对于caffe依赖的包的安装,还可以进入py-faster-rcnn/caffe-fast-rcnn/python
目录,然后执行:
for req in $(cat requirements.txt); do pip install $req; done
这个和上面使用sudo apt-get install安装那些支持包差不多是重复的,但是还是有差异,有些包的版本在使用sudo apt-get install python-<package>安装和使用pip install <package>安装得到的版本是不一样的,所以如果只是跑模型进行识别,使用sudo apt-get install python-<package>安装完那些支持包可能就能跑起来了,训练模型可能还得使用pip install -U <package>把出错的包重新安装一下合适的版本,比如numpy。
依赖包都安装好后,下载源码:
执行下面命令下载py-faster-rcnn的caffe版的源码:
git clone - -recursive https://github.com/rbgirshick/py-faster-rcnn.git
https://developer.nvidia.com/cuda-gpus这里列出了各种GPU的算力,Jetson Nano的算力是5.3:
修改py-faster-rcnn/lib/setup.y:
如果发现编译lib时,include numpy的路径不对,增加下面这句:
try:
numpy_include = np.get_include()
except AttributeError:
numpy_include = np.get_numpy_include()
numpy_include='/usr/local/lib/python2.7/dist-packages/numpy/core/include'
修改算力为jetson nano的53 :
'nvcc': ['-arch=sm_35',
为
'nvcc': ['-arch=sm_53',
然后编译Cython,执行:
cd py-faster-rcnn/lib
make
然后执行:
cd py-faster-rcnn/caffe-fast-rcnn
cp Makefile.config.example Makefile.config
然后对Makefile.config做下面的修改(模型使用的python2.7,因此无需修改除下面内容的其他项):
USE_CUDNN := 1
#jetson nano上默认安装了OpenCV3.4。对于PC上/usr/lib下安装了OpenCV2.4.9版本的也可以不打开注释:
OPENCV_VERSION := 3
#jetson nano的image里默认只安装了CUDA10.0,如果你自己安装了多个版本的CUDA,那么还需指定一下路径:
CUDA_DIR := /usr/local/cuda-10.0
WITH_PYTHON_LAYER := 1
将CUDA_ARCH的值做修改,删除算力为20和21的那两句:
CUDA_ARCH := -gencode arch=compute_30,code=sm_30 \
-gencode arch=compute_35,code=sm_35 \
-gencode arch=compute_50,code=sm_50 \
-gencode arch=compute_52,code=sm_52 \
-gencode arch=compute_53,code=sm_53 \
-gencode arch=compute_60,code=sm_60 \
-gencode arch=compute_61,code=sm_61 \
-gencode arch=compute_61,code=compute_61
PYTHON_INCLUDE := /usr/include/python2.7 \
/usr/local/lib/python2.7/dist-packages/numpy/core/include
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/aarch64-linux-gnu /usr/lib/aarch64-linux-gnu/hdf5/serial
(插一句:如果是在X86芯的PC上训练模型或跑模型,把上面的相关路径/usr/lib/aarch64-linux-gnu改成/usr/lib/x86_64-linux-gnu,如果使用的RTX2070 RTX2080之类的GPU(算力7.5)的话,需要增加:
-gencode=arch=compute_75,code=sm_75 \
-gencode arch=compute_75,code=compute_75
)
然后对Makefile做如下修改:
NVCCFLAGS += -D_FORCE_INLINES -ccbin=$(CXX) -Xcompiler -fPIC $(COMMON_FLAGS)
LIBRARIES += glog gflags protobuf boost_system boost_filesystem m hdf5_serial_hl hdf5_serial
然后执行make过一会儿会报错:
cudnn.hpp:127:41: error: too few arguments to function ‘cudnnStatus_t cudnnSetPooling2dDescriptor
或者
这个是由于pycaffe里的cudnn*相关文件相对于cuDNN7来说太老了,解决办法是下载个最新版的caffe源码:
git clone https://github.com/BVLC/caffe.git
然后在pycaffe目录下执行
find . -name cudnn*
查找出所有以cudnn开头的文件:
py-faster-rcnn/caffe-fast-rcnn/include/caffe/util/cudnn.hpp
py-faster-rcnn/caffe-fast-rcnn/src/caffe/util/cudnn.cpp
py-faster-rcnn/caffe-fast-rcnn/src/caffe/layers/cudnn_*
py-faster-rcnn/caffe-fast-rcnn/include/caffe/layers/cudnn_*
将BVLC caffe里的对于文件拷贝过去替换掉即可
然后执行
make
make pycaffe
编译完成后,把自己在PC上已训练好的模型和此处编译出来的相关封装调用文件拷贝部署到jetson nano上即可运行。
在PC或服务器上编译pycaffe完后,为了让caffe module能在python下用起来,还需在/etc/bash.bashrc里最后加一句:
export PYTHONPATH=/home/<username>/py-faster-rcnn/caffe-fast-rcnn/python:$PYTHONPATH
保存后执行 source /etc/bash.bashrc
然后运行python,然后在python下import caffe,不报错即可。
如果你是使用类似如下命令
conda create -n <env> pip python=2.7.16
创建的一个隔离环境下做的以上安装,那么需要先执行
conda activate <env>
激活环境,再运行python。
上面要注意的是:要先编译Cython和用make编译caffe遇到类似上面的函数参数不对的错误后再替换cudnn*相关文件,然后再编译就才可以顺利编译完成,如果你下载源码后就立即替换cudnn*相关文件,那么编译Cython时很快就结束了也不报错,但其实失败了,会导致后面执行make编译caffe时接连出现这样那样的错误,原因就是cudnn*文件过早替换使得Cython其实没有编译成功有些文件.hpp和.cpp文件没有生成!这是个大坑哦,害我浪费了半天时间!
另外注意:在Ubuntu16.04下编译出来的libcaffe.so*和_caffe.so在Ubuntu18.04下不能使用,需要使用源码在Ubuntu18.04下重新编译出来的才能使用,否则运行时会报很多Ubuntu16.04下的so文件找不到,比如libboost_python-py27.so.1.58.0,libboost_system.so.1.58.0,libboost_thread.so.1.58.0,libprotobuf.so.9,libgflags.so.2,libhdf5_serial_hl.so.10,libopencv_imgcodecs3.so.3.3。
Jetson Nano的镜像里默认给你安装好了OpenCV3的库,如果在其他环境,比如机器人的使用Ubuntu+ROS的板子上部署模型,可以使用ROS kinetic自带的OpenCV3的库(默认/opt/ros/...下),或者自己下载源码编译OpenCV3,不过前面文章讲过,编译OpenCV不是个轻松活,没配置好或者编译的支持环境没安装好会遇到很多这样那样的坑,所以环境中有了OpenCV库就最好使用现成的。
numpy >1.11.0时造成py-faster-rcnn训练时报错:
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self.run()
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "./tools/train_faster_rcnn_alt_opt.py", line 195, in train_fast_rcnn
max_iters=max_iters)
File "/home/<user>/py-faster-rcnn/tools/../lib/fast_rcnn/train.py", line 160, in train_net
model_paths = sw.train_model(max_iters)
File "/home/<user>AI/py-faster-rcnn/tools/../lib/fast_rcnn/train.py", line 101, in train_model
self.solver.step(1)
File "/home/<user>/py-faster-rcnn/tools/../lib/roi_data_layer/layer.py", line 144, in forward
blobs = self._get_next_minibatch()
File "/home/<user>/py-faster-rcnn/tools/../lib/roi_data_layer/layer.py", line 63, in _get_next_minibatch
return get_minibatch(minibatch_db, self._num_classes)
File "/home/<user>/py-faster-rcnn/tools/../lib/roi_data_layer/minibatch.py", line 55, in get_minibatch
num_classes)
File "/home/<user>/py-faster-rcnn/tools/../lib/roi_data_layer/minibatch.py", line 100, in _sample_rois
fg_inds, size=fg_rois_per_this_image, replace=False)
File "mtrand.pyx", line 1191, in mtrand.RandomState.choice
TypeError: 'numpy.float64' object cannot be interpreted as an index
解决办法:
按下面红字部分修改py-faster-rcnn/lib/roi_data_layer/minibatch.py:
def get_minibatch(roidb, num_classes):
"""Given a roidb, construct a minibatch sampled from it."""
num_images = len(roidb)
# Sample random scales to use for each image in this batch
random_scale_inds = npr.randint(0, high=len(cfg.TRAIN.SCALES),
size=num_images)
assert(cfg.TRAIN.BATCH_SIZE % num_images == 0), \
'num_images ({}) must divide BATCH_SIZE ({})'. \
format(num_images, cfg.TRAIN.BATCH_SIZE)
rois_per_image = cfg.TRAIN.BATCH_SIZE / num_images
fg_rois_per_image = int( np.round(cfg.TRAIN.FG_FRACTION * rois_per_image))
def _get_bbox_regression_labels(bbox_target_data, num_classes):
"""Bounding-box regression targets are stored in a compact form in the
roidb.
This function expands those targets into the 4-of-4*K representation used
by the network (i.e. only one class has non-zero targets). The loss weights
are similarly expanded.
Returns:
bbox_target_data (ndarray): N x 4K blob of regression targets
bbox_loss_weights (ndarray): N x 4K blob of loss weights
"""
clss = bbox_target_data[:, 0]
bbox_targets = np.zeros((clss.size, 4 * num_classes), dtype=np.float32)
bbox_loss_weights = np.zeros(bbox_targets.shape, dtype=np.float32)
inds = np.where(clss > 0)[0]
for ind in inds:
cls = clss[ind]
start = int( 4 * cls )