基于FastDeploy将PicoDet部署到Jetson Nano上之C++版

基于 FastDeploy

将 PicoDet 部署到 Jetson Nano 上

之 C++ 版

本文于2022年9月底发布,感谢飞桨达人创造营!!感谢 FastDeploy!!请大家到 FD主页 点star支持!

本文将从Jetson Nano 2G(创乐博套件)的开箱到目标检测模型的部署,做一个全流程的详解,力求为 Jetson Nano 新手提供一站式的手把手教程,如有不全面的地方,欢迎大家随时补充。

全文包括如下几个部分,最好按顺序操作:

  • 一、极简开箱照
  • 二、组装
  • 三、系统烧录
  • 四、扩充磁盘空间
  • 五、增加虚拟内存 swapfile
  • 六、开发环境搭建
  • 七、获取导出后的 PicoDet 模型
  • 八、基于 FastDeploy 的 C++ 部署
  • 九、使用 MIPI CSI 摄像头的 Demo
  • 十、总结和展望

其中部分内容会直接给出大佬的链接,以避免重复造轮子。(再次感谢巨人前辈提供肩膀)

一、极简开箱照

套件包括:主板、散热器、风扇、USB口wifi、保护壳、电源、micro usb数据线、HDMI线、网线、跳线、sd卡、读卡器、资料U盘。








二、组装

这步很简单,先把风扇装好就可以了。

【注意】:烧录成功之前不要装亚克力保护壳,因为装完之后会挡住插跳线的针脚。也可以先不装跳线针脚的那一面。

风扇电源线连接位置如图,注意方向不要插错了,错了也插不进去。




三、烧录

请注意,本文针对的是创乐博的套件。

如果是原厂套件,可以参考 此文

同时,也建议使用创乐博的同学按照资料U盘里的视频教程操作。

总体来说,烧录分为两大部分:

  • 第一部分为烧录eMMC引导;
  • 第二部分是把Jetson Nano的系统镜像烧录到 SD 卡里。

这两部分缺一不可。

第1步:启动虚拟机

在你的PC机上,安装VMware,并载入资料U盘内提供的虚拟机文件:YourPath\2.虚拟机镜像\clb_jetson_ubuntu1804\clb_jetson.vmx

这个虚拟机镜像里已经准备好了烧录的环境,无需额外安装其他工具即可直接可以使用。

第2步:设置烧写模式

用跳线连接Nano主板的2、3针脚,既设置烧写模式,如下图:




第3步:连接数据线

连接PC机的USB口和Nano的Micro USB口,如下图:



第4步:上电

上电。



此时虚拟机会自动弹出检测到USB设备。选择连接到虚拟机。



第5步:开始烧录eMMC

在PC端的虚拟机内,进入到 Linux_for_Tegra 目录,找到 1.txt文件,将其中的命令复制粘贴到命令行,运行,并输入开机密码,即可开始烧录。



等待几分钟,看到如下提示,则烧录成功,此时可以退出虚拟机:




第6步:烧录SD卡

在PC上安装 Balena Etcher 烧录工具。

然后,将SD卡放入读卡器,并插入PC。

此后的SD卡烧录方法与原厂套件的烧录方法相同,此处不再赘述,直接按 这篇文章 里的步骤操作即可。

【注意】写入 SD 卡的数据以 Linux 的 ext4 格式存在,因此在 Windows 下面是无法识别的,请 Windows 使用者无需惊慌(有遇到不少用户在 Windows 下看不到数据,还以为自己没有安装成功)。

第7步:启动前的准备

【注意】一定要先拔掉Nano的电源,然后再把SD卡插入到Nano上。

【还要注意】再次上电之前,需要将跳线拔掉。




第8步:启动Nano

把Nano连接到显示器,再接上鼠标键盘,就可以上电启动了。



如果没有多余的显示器、鼠标、键盘,可以参考 此文 ,通过远程桌面或者SSH的方式连接到Nano,此处不再赘述。

烧录问题汇总

  • 开机显示如下画面:



这是因为只做了SD卡的烧录,没有对eMMC进行烧录。

请按照eMMC烧录步骤操作,即可解决问题。

  • (更多问题会持续汇总更新,请大家在评论区留言,我会持续补充。)

四、扩充磁盘空间

在SD卡写入系统后,默认会有一部分可用磁盘空间是被隐藏的,因此我们需要把它释放出来。

操作方法很简单,在Nano上直接安装 gparted 工具(sudo apt-get install gparted),运行工具后找到SD卡(一般为/dev/sda1) 右击已分配的分区,resize,拖到很靠右又不是全部在右边的地方,然后就可以开始了。

这里 有官方视频教程,请大家参考。

五、增加虚拟内存 swapfile

Jetson Nano 2GB 的内存相对弱势,所幸 Linux 提供了一种 SWAP 技术,能将存储设备空间作为虚拟内存使用,性能虽然不如物理内存,但也能支撑更多深度学习的计算。

官方提供的操作方法在 这里 的“4.1增加交换空间大小”章节。如法炮制即可,此处不再赘述。

六、开发环境搭建

1. 建议移除LibreOffice

这会为系统省很多空间,而且这个软件对做深度学习和计算机视觉算法也没什么用。

sudo apt-get purge libreoffice
sudo apt-get clean

2. 安装必要的包

sudo apt-get install build-essential make cmake
sudo apt-get install git g++ pkg-config curl

此外,建议将cmake升级一下,参考 这里 的方法.

3. 安装jetson-stats工具包

该工具包的jtop工具可以实时显示系统资源情况、检测nano温度等。

按照 这里 的步骤操作即可。

通过 jtop 查得本文的主要硬件及软件版本如下:

  • Jetpack = 4.6.1
  • opencv = 4.1.1 compiled CUDA:NO
  • CUDA = 10.2.300
  • cuDNN = 8.2.1.32
  • TensorRT = 8.2.1.8



七、获取导出后的 PicoDet 模型

本文将采用 PicoDet-s-416 检测模型,实现对垃圾的检测。

关于模型的生产过程,请移步这里: 基于PP-PicoDet v2 的路面垃圾检测

其中,导出好的模型,保存在本项目的此目录下:

~/work/PicoDet-s-416-DHQ-exported

同时,在此目录下也提供了一张测试图片:

trash01.png

需要说明的是,由于此模型是使用Demo数据训练的,因此精度不高。感兴趣的同学可以自行收集更多数据,参考原文重新训练。

八、基于 FastDeploy 的 C++ 部署

1. FastDeploy 在Nano + Linux下的环境要求

  • cmake >= 3.12
  • gcc/g++ >= 8.2
  • python >= 3.6
  • cuda >= 11.0 (Linux默认安装路径在/usr/local/cuda下)
  • cudnn >= 8.0
  • TensorRT、Paddle Inference、ONNXruntime等推理引擎,会在SDK中包含,不需要单独安装。

2. 在 Nano 上直接编译 FastDeploy 的 C++ SDK

  • 编译前,需安装patchelf:
sudo apt-get install patchelf
  • 然后开始拉取 FastDeploy的代码,并编译:
git clone https://github.com/PaddlePaddle/FastDeploy
cd FastDeploy
mkdir build && cd build
cmake .. -DBUILD_ON_JETSON=ON -DENABLE_VISION=ON -DCMAKE_INSTALL_PREFIX=${PWD}/fastdeploy_cpp_sdk
make -j8
make install




整个编译过程在Nano上进行,大概3-5分钟可以编译完成。之后在FastDeploy/build/fastdeploy_cpp_sdk目录下的即为编译产出的C++部署库。包括C++的实例代码也在里面。




【注意】FastDeploy在编译时会依赖第三方库Eigen,因此会自动拉取Gitlab上源码,如遇编译时拉取Eigen源码问题,可先使用如下命令配置git:

git config --global http.sslverify false

【还要注意】在Jetson上编译FastDeploy,当打开开关BUILD_ON_JETSON时,会默认开启ENABLE_ORT_BACKEND和ENABLE_TRT_BACKEND,即当前仅支持ONNXRuntime CPU或TensorRT两种后端分别用于在CPU和GPU上的推理。这一点,在后文演示推理时,还会提到。

3. PicoDet 的 C++ 部署示例

  • 配置依赖库的搜索路径

FastDeploy 非常贴心的准备了把依赖库导入环境变量的脚本:fastdeploy_init.sh,避免大家手动添加导致错误。

脚本位于:

YourPathTo/fastdeploy_cpp_sdk/

在此目录下,直接执行此命令,即可完成导入:

source fastdeploy_init.sh

导入结果如图:




  • FastDeploy 提供的示例代码位于:
YourPathTo/fastdeploy_cpp_sdk/examples/vision/detection/paddledetection/cpp/infer_picodet.cc
  • 进入以上的目录后,依次运行如下编译命令:
mkdir build && cd build
cmake .. -DFASTDEPLOY_INSTALL_DIR=YourPathTo/fastdeploy_cpp_sdk
make -j
  • 进入到 YourPathTo/fastdeploy_cpp_sdk/examples/vision/detection/paddledetection/cpp/build 目录,可找到编译后的可执行文件: infer_picodet_demo
  • 将导出后的 PicoDet 模型和测试图片拷贝到当前build目录下
  • 可尝试用以下3种方法推理(其实只有2种可用)。命令行的最后一个参数可以是[0, 1, 2],用于设置推理的方式。
    • CPU推理

./infer_picodet_demo ./PicoDet-s-416-DHQ-exported trash01.png 0


CPU推理耗时 380ms,推理结果的图片,会保存在当前可执行文件目录下。


推理框上的text内容为 {类别ID,置信度}

    • GPU推理

./infer_picodet_demo ./PicoDet-s-416-DHQ-exported trash01.png 1



【再次注意】
编译FastDeploy时,当打开开关BUILD_ON_JETSON时,会默认开启ENABLE_ORT_BACKEND和ENABLE_TRT_BACKEND,即当前仅支持ONNXRuntime CPU或TensorRT两种后端分别用于在CPU和GPU上的推理。因此,这里的GPU推理并不会生效,而是会自动转成CPU推理。

    • GPU上TensorRT推理

./infer_picodet_demo ./PicoDet-s-416-DHQ-exported trash01.png 2
FastDeploy 默认采用TRT-FP32的推理。如果需要使用TRT-FP16的推理,设置的方法很简单,只需要在代码中加入一行 option.EnableTrtFP16() 即可。


细心的朋友会发现,在使用TRT推理时,每次初始化非常耗时,感觉是一直卡在这里:
[INFO] fastdeploy/backends/tensorrt/ trt_backend.cc(416) ::BuildTrtEngine Start to building TensorRT Engine...
实际上,FP32的初始化需要3分钟左右,FP16的初始化需要10分钟!!
那么,如何提速呢?
方法也很简单,在代码中加入一行:option.SetTrtCacheFile("./picodet.trt")



这样,第一次初始化完成之后,就会在当前目录下保存TRT的缓存文件 picodet.trt,



这样以后每次运行就直接读取该文件了,避免重复初始化。


【格外注意】当需要在TRT-FP32和TRT-FP16之间切换时,别忘了先删除保存的 picodet.trt 缓存文件。

九、使用 MIPI CSI 摄像头的 Demo

1. CSI 摄像头的安装

【注意】一定要先断电!!!

这里放几张示意图:



详情请参考 大佬的安装步骤

安装成功后,先看一下是否可以被系统识别到:

ls /dev/vid*




直接运行命令测试一下:

gst-launch-1.0 nvarguscamerasrc ! 'video/x-raw(memory:NVMM),width=3820, height=2464, framerate=21/1, format=NV12' ! nvvidconv flip-method=0 ! 'video/x-raw,width=960, height=616' ! nvvidconv ! nvegltransform ! nveglglessink -e

2. CSI 摄像头的调用

CSI接口的摄像头调用,比USB的要复杂,但并不难。

C++版本的调用方法如下:

// 定义gstreamer pipeline
std::string gstreamer_pipeline (int capture_width, int capture_height, int display_width, int display_height, int framerate, int flip_method)
{ // DHQ added 20220927
    return "nvarguscamerasrc ! video/x-raw(memory:NVMM), width=(int)" + std::to_string(capture_width) + ", height=(int)" +
           std::to_string(capture_height) + ", format=(string)NV12, framerate=(fraction)" + std::to_string(framerate) +
           "/1 ! nvvidconv flip-method=" + std::to_string(flip_method) + " ! video/x-raw, width=(int)" + std::to_string(display_width) + ", height=(int)" +
           std::to_string(display_height) + ", format=(string)BGRx ! videoconvert ! video/x-raw, format=(string)BGR ! appsink";
// 在主函数中设置参数,并调用gstreamer管道
int capture_width = 1280 ;
int capture_height = 720 ;
int display_width = 1280 ;
int display_height = 720 ;
int framerate = 60 ;
int flip_method = 0 ;
//创建管道
std::string pipeline = gstreamer_pipeline(capture_width,
capture_height,
display_width,
display_height,
framerate,
flip_method);
std::cout << "使用gstreamer管道: \n\t" << pipeline << "\n";