边缘设备使用记录--阿加犀AIBox 6490
- 设备介绍
- 设备连接
- glog && gflags
- onnx2tflite
- AidLite SDK for C++
- 模型输入输出的shape
- 执行推断
- OpenCV使用
设备介绍
阿加犀AIBox 6490是一款基于高通QCS6490平台的高性价比智能边缘计算终端,具有14TOPS AI算力,可独立运行Android、Linux、Ubuntu操作系统,或选择Android+Linux双系统原生融合并行,带来更加高效的使用体验。 详情可以参考官网链接,我手中的这块板子是单独的Ubuntu 20.04系统。
设备连接
6490自己是有HDMI out输出的,所以最简单的办法就是直接插上鼠标键盘还有显示器,当正常的电脑用,不过这里的桌面应该是简化过的,只有左上角有个终端的按钮,别的什么都没有
单纯这么使用的话应该是有点麻烦的,所以这里还是选择和之前树莓派,以及Nvidia Orin nx一样的方法,使用SSH远程开发。 然后这里就有新的问题了,这个目前似乎是没有开机自动WiFi连接的方法的,按之前给树莓派配WiFi的方法试了下也没成功,所以这里就退而求其次,选择用网线和主机连接,再把主机的网络共享给6490板子,实现的效果也是一样的。在连上网线之后,可以在网络设备里看到刚刚插入的网口
这个未识别的网络指的就是刚刚连上的了,接下来只需要再设置下WiFi的网络共享以及对应以太网的IP地址就可以了
然后再根据arp -a命令可以查到当前设备的IP地址,之后只需要用自己习惯的方式连上就可以,我这里配的是tabby和vscode
之后就可以进行愉快的coding了
- 这里有一个小问题,就是板子每次重启后,虽然我连的网口没变,但IP还是会发生改变,这样其实倒也没什么问题。。就是后边用的次数多了可能会增加找到这个IP的难度。目前还没想到什么好办法解决,之后看看有没有办法能解决吧
glog && gflags
因为个人的习惯问题,还是把glog,gflags日志库安装了一下,这里还是源码编译,之前也源码编译安装了很多次了,首先要注意一定要先安装gflags
git clone https://github.com/gflags/gflags.git
cd gflags
mkdir build && cd build
cmake .. -DGFLAGS_NAMESPACE=google -DCMAKE_CXX_FLAGS=-fPIC ..
make -j4
sudo make install
然后再安装glog,不过这里直接拉最新的代码是不行的,主要报错是在CMake文件里,虽然感觉应该不难改,但这里还不如降低一下版本。。所以直接换成了0.5.0的版本,安装没什么问题
git clone -b v0.5.0 https://github.com/google/glog
cd glog
mkdir build && cd build
cmake -DGFLAGS_NAMESPACE=google -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_SHARED_LIBS=ON ..
make -j4
sudo make install
然后就在自己的程序里链接到这两个库就能愉快的使用了
onnx2tflite
我比较常用的模型都是onnx,之前在看autoware的时候有了解过TensorRT和tvm两种部署方式,里面是通过加载onnx然后生成engine引擎文件实现的。在Aidlux自带的AI包中,支持的模型种类没有onnx,所以这里是稍微搜了下,发现了onnx2tflite这个库,所以这里把这个库先安装一下。第一步还是经典的依赖问题,直接pip install网络估计还是会报错,所以这里直接指定清华源了
sudo pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
安装之后其实报了版本冲突的警告,但是不知道后边会不会有影响,先运行下试试
- 然后果然就出问题了,运行的时候一直会提示The TensorFlow library was compiled to use AVX instructions, but these aren’t available on your machine.这个错误,然后直接段错误,上网搜了下我的CPU应该是不支持AVX,所以要使用SSE版本的Tensorflow才行。。这里参考Tensorflow针对CPU的编译优化加速这篇文章进行了Tensorflow的重新安装
这里也是废了半天劲,最后转换得到了一个yolox_nano.tflite文件,而且还不知道能不能用。。先继续往下看吧(后边试了一下,加载模型的时候会提示GATHER不支持GPU加速,但好像也没什么大问题?)
- 又在网上搜了半天,发现了YOLOX-ti-lite Object Detector in TFLite这样一个仓库,里面是一些在边缘设备部署YOLOx的代码,而且也有几个训练好的模型,这里也是下了几个别人训练好的模型准备后边拿来测试一下,至少创建模型是可以的
AidLite SDK for C++
本来还想着要装tvm呢,后来搜了搜发现阿加犀自己有对应的模型推理的一个SDK,而且使用文档写的相对还挺全的,这个文档的地址在AidLite SDK for C++,这里就参考这个文档依次来学习一下
模型输入输出的shape
在模型创建的代码中,我们发现里面对input_shape
和output_shape
进行了设置
// 创建Model实例对象,并设置模型相关参数
Model* model = Model::create_instance("./inceptionv3_float32.dlc");
if(model == nullptr){printf("Create model failed !\n");return EXIT_FAILURE;
}
std::vector<std::vector<uint32_t>> input_shapes = {{1,299,299,3}};
std::vector<std::vector<uint32_t>> output_shapes = {{1,1001}};
model->set_model_properties(input_shapes, DataType::TYPE_FLOAT32, output_shapes, DataType::TYPE_FLOAT32);
像这里,我个人觉得第一个1代表的就是批次,然后输入后边对应的就是2992993的图像,下面输出的1000应该对应着1001种类别。 OK,因为我对各种网络其实不是很熟悉,所以这里还是以YOLOx为例,之前也有在autoware.universe源码略读(3.1)–perception:yolo初识这篇文章总结过,这里的输出应该是85*8400,所以设置的话应该设置成[1, 8400,85]?这里我暂时是这么设置的
- 这里又看了下,YOLOx-nano和tiny应该是只支持416*416一种输入的,所以这里的输入输出都得跟着改成下面这样
std::vector<std::vector<uint32_t>> input_shapes = {{1, 416, 416, 3}};
std::vector<std::vector<uint32_t>> output_shapes = {{1, 3549, 85}};
model->set_model_properties(input_shapes, Aidlux::Aidlite::DataType::TYPE_FLOAT32,output_shapes, Aidlux::Aidlite::DataType::TYPE_FLOAT32);
还不知道这样对不对,还是一边写一边测试吧。然后是创建 Config 类型的对象,此对象用于记录模型推理相关的配置信息,推理框架运行过程中会使用到这些配置信息。
执行推断
这里其实照着文档来就可以了,不过值得注意的是,我本来以为这个板子是NPU,结果发现还是GPU(其实我也不是很了解两者的区别),因为在设置Config提示我tflite模型不能用NPU我才发现这个问题。还有和之前用到的TensorRT不同的一点是,因为这里指定了输入和输出的shapes
,所以保存成cv::Mat就可以了
cv::Mat input_data;
preprocess(FLAGS_input_image, input_data);
result = fast_interpreter->set_input_tensor(0, static_cast<void*>(input_data.data));
if (result != 0)
{LOG(FATAL) << "interpreter set input tensor false!\n";
}
然后执行推理过程,稍微计时了一下,在我使用yolox_nano_ti_lite_float32.tflite模型的时候每次推理的耗时在20-30ms,不是很清楚这个算什么水平。然后我们在这里直接把输出再转成一个vector,这样应该会好处理一些
result = fast_interpreter->invoke();
if (result != 0)
{LOG(FATAL) << "invoke failed!\n";
}
time_end = std::chrono::system_clock::now();
auto time_cost = std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_begin);
LOG(INFO) << "invoke cost: " << time_cost.count() << "ms.\n";
float* out_data = nullptr;
uint32_t output_tensor_length = 0;
result = fast_interpreter->get_output_tensor(0, (void**)&out_data, &output_tensor_length);
assert(result == 0);
std::vector<float> out_data_vec(out_data, out_data + output_tensor_length / 4);
LOG(INFO) << "out data vec size: " << out_data_vec.size() << std::endl;
最后当然还有一步骤是输出的后处理,这个就要针对不同的模型进行不同的操作了,就不在这里说了
OpenCV使用
在板子里是有预先安装好OpenCV的,但是可以看到里面有不止一个
这里是有两个版本,4.9.0和4.2.0,其实我也不太清楚这两个版本具体有什么区别,但是因为之前自己用的都是4.2.0的,所以这里还是用4.2.0的进行后续的开发。在CMake里直接find_package
的找到的是4.9.0的版本,所以这里只需要制定以下cmake文件的路径就好
find_package(OpenCV REQUIREDPATHS /usr/local/lib/aidlux_opencv/lib/cmake/opencv4NO_DEFAULT_PATH)
include_directories(${OpenCV_INCLUDE_DIRS})
# message(STATUS "OpenCV include path: ${OpenCV_INCLUDE_DIRS}")
其实不需要消息输出,因为会提示找到的OpenCV的版本