基于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/
http://
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";