物体检测EdgeBoard(FZ)专用SDK集成文档
简介
本文档介绍 EasyEdge/EasyDL在EdgeBoard®边缘计算盒/Lite计算卡上的专用软件的使用流程。
EdgeBoard系列硬件可直接应用于AI项目研发与部署,具有高性能、易携带、通用性强、开发简单等四大优点。
详细硬件参数请在AI市场浏览。
EdgeBoard产品使用手册:https://ai.baidu.com/ai-doc/HWCE/Yk3b86gvp
软核版本
CPP-SDK版本 | 对应软核 |
---|---|
1.3.2、1.3.4、1.3.5 | 1.8.1 |
1.3.0、1.3.1、1.3.2、1.3.4 | 1.8 |
0.5.7-1.2.1 | 1.5 |
0.5.2+ | 1.4 |
SDK升级需配合EdgeBoard硬件软核升级,建议升级软核为SDK对应版本,否则可能出现结果错误或者其他异常。
可以通过
dmesg | grep "DRIVER Version"
命令获取EdgeBoard当前的软核版本
Release Notes
注意*:升级完成相应的软核之后需要重启机器生效。
sdk对应的软核说明: 如果客户使用的软核是mobile版本的,需要使用1.4的SDK;如果不是mobile 版本,可以选择1.5+(目前最高版本更新至1.8.1)版本的SDK使用。
1.5+版本的软核以及sdk更新情况如下表所示:
时间 | 版本 | 说明 | EdgeBoard非mobile对应的软核以及特性 | |
---|---|---|---|---|
2021.12.20 | 1.3.5 | 升预测引擎为PaddleLite 1.8.1,推理库支持了Ubuntu18.04文件系统 | https://ai.baidu.com/ai-doc/HWCE/Fkuqounlk(含有EB升级Ubuntu18.04系统的步骤) | |
2021.10.15 | 1.3.2、1.3.4、1.3.4 | 推理库支持了Ubuntu18.04文件系统 | https://ai.baidu.com/ai-doc/HWCE/Fkuqounlk | |
2021.06.29 | 1.3.1 | 视频流解析支持分辨率调整;预测引擎升级; | https://ai.baidu.com/ai-doc/HWCE/Ikqgcqt5x | |
2021.05.14 | 1.3.0 | 新增视频流接入支持;展示已发布模型性能评估报告 | https://ai.baidu.com/ai-doc/HWCE/Ikqgcqt5x | |
2021.05.14 | 1.2.1 | 功能无更新 | https://ai.baidu.com/ai-doc/HWCE/okqiwkm32 | |
2020.10.29 | 0.5.7 | 预测引擎切换为PaddleLite 1.5 | - | |
2019.12.27 | 0.4.5 | 引擎升级,支持zu5/zu3,支持EasyDL 高精度检测模型 | - | |
2019.07.25 | 0.4.0 | EdgeBoard SDK Release! | - |
mobile软核以及sdk更新情况如下表所示: | 时间 | 版本 | 说明 |EdgeBoard mobile对应的软核以及特性 | | --- | ---- | ---- |---- |---- | |2021.05.14|1.2.1|功能无更新|https://ai.baidu.com/ai-doc/HWCE/okqiwkm32|https://ai.baidu.com/ai-doc/HWCE/Lkqiwlziw||
快速开始
开发者从EasyEdge/EasyDL下载的软件部署包中,包含了简单易用的SDK和Demo。只需简单的几个步骤,即可快速部署运行EdgeBoard计算盒。
部署包中包含多版本SDK:
- baidueasyedge_linux_cpp_aarch64_EdgeBoardFZ1.8*:适用于EdgeBoard 1.5+软核
- baidueasyedge_linux_cpp_aarch64_EdgeBoardFZ1.4*:适用于EdgeBoard 1.4软核
SDK文件结构
1baidu_easyedge_linux_cpp_aarch64_EdgeBoardFZ1.5_*
2├── ReadMe.txt
3├── bin
4│ ├── easyedge_image_inference
5│ ├── easyedge_serving
6│ └── easyedge_video_inference
7├── include
8│ └── easyedge
9├── lib
10│ ├── libeasyedge.so -> libeasyedge.so.1
11│ ├── libeasyedge.so.1 -> libeasyedge.so.1.3.1
12│ ├── libeasyedge.so.1.3.1
13│ ├── libeasyedge_static.a
14│ ├── libeasyedge_videoio.so -> libeasyedge_videoio.so.1
15│ ├── libeasyedge_videoio.so.1 -> libeasyedge_videoio.so.1.3.1
16│ ├── libeasyedge_videoio.so.1.3.1
17│ ├── libeasyedge_videoio_static.a
18│ ├── libpaddle_full_api_shared.so -> libpaddle_full_api_shared.so.1.8.0
19│ ├── libpaddle_full_api_shared.so.1.8.0
20│ ├── libverify.so -> libverify.so.1
21│ ├── libverify.so.1 -> libverify.so.1.0.0
22│ └── libverify.so.1.0.0
23├── now_sre.log
24├── src
25│ ├── CMakeLists.txt
26│ ├── cmake
27│ ├── common
28│ ├── demo_image_inference
29│ ├── demo_serving
30│ └── demo_video_inference
31└── thirdparty
32 └── opencv
1.1.0+的SDK自带OpenCV,src编译的时候会引用thirdparty/opencv路径下的头文件和库文件。
Demo使用流程
用户在AI市场购买计算盒之后,请参考以下步骤进行集成和试用。
1. 将计算盒连接电源
指示灯亮起,等待约1分钟。
- 参考EdgeBoard使用文档配置网口或串口连接。登录EdgeBoard计算盒。
- 加载驱动(开机加载一次即可)。
1insmod /home/root/workspace/driver/{zu9|zu5|zu3}/fpgadrv.ko
根据购买的版本,选择合适的驱动。若未加载驱动,可能报错:
1Failed to to fpga device: -1
- 设置系统时间(系统时间必须正确)
1date --set "2019-5-18 20:48:00"
2. (可选)启动HTTP服务
部署包中附带了HTTP服务功能,开发者可以进入SDK根目录,运行easyedge_serving
程序启动HTTP服务。
1# ./easyedge_serving {RES目录} "" {绑定的host,默认0.0.0.0} {绑定的端口,默认24401}
2cd ${SDK_ROOT}
3export LD_LIBRARY_PATH=./lib
4./demo/easyedge_serving ../../../RES ""
日志显示
12019-07-18 13:27:05,941 INFO [EasyEdge] [http_server.cpp:136] 547974369280 Serving at 0.0.0.0:24401
则启动成功。此时可直接在浏览器中输入http://{EdgeBoard计算盒ip地址}:24401/
,在h5中测试模型效果。
同时,可以调用HTTP接口来访问盒子。具体参考下文接口说明。
EdgeBoard HTTP Server 目前使用的是单线程处理请求。
3. 编译运行Demo
编译:
1cd src
2mkdir build && cd build
3cmake .. && make
运行
1./easyedge_image_inference {RES资源文件夹路径} {测试图片路径}
便可看到识别结果。
使用说明
使用流程
激活成功之后,有效期内可离线使用。
- 配置
PaddleFluidConfig
- 新建
Predictor
:global_controller()->CreateEdgePredictor(config);
- 初始化
predictor->init()
- 传入图片开始识别
predictor->infer(img, ...);
目前EdgeBoard暂不支持并行多模型计算。
接口说明
预测图片
1 /**
2 * @brief 同步预测接口
3 * inference synchronous
4 * Supported by most chip and engine
5 * @param image: must be BGR , HWC format (opencv default)
6 * @param result
7 * @param threshold
8 * @return
9 */
10 virtual int infer(
11 cv::Mat &image, std::vector<EdgeResultData> &result, float threshold = 0.1
12 ) = 0;
识别结果说明
EdgeResultData
中可以获取对应的分类信息、位置信息。
1struct EdgeResultData {
2 int index; // 分类结果的index
3 std::string label; // 分类结果的label
4 float prob; // 置信度
5
6 // object detection field
7 float x1, y1, x2, y2; // (x1, y1): 左上角, (x2, y2): 右下角; 均为0~1的长宽比例值。
8};
关于矩形坐标
x1 * 图片宽度 = 检测框的左上角的横坐标
y1 * 图片高度 = 检测框的左上角的纵坐标
x2 * 图片宽度 = 检测框的右下角的横坐标
y2 * 图片高度 = 检测框的右下角的纵坐标
可以参考demo文件中使用opencv绘制矩形的逻辑。
HTTP 私有服务请求说明
http 请求参数
URL中的get参数:
参数 | 说明 | 默认值 |
---|---|---|
threshold | 阈值过滤, 0~1 | 0.1 |
HTTP POST Body即为图片的二进制内容(无需base64, 无需json)
Python
1import requests
2
3with open('./1.jpg', 'rb') as f:
4 img = f.read()
5 result = requests.post(
6 'http://127.0.0.1:24401/',
7 params={'threshold': 0.1},
8 data=img).json()
Cpp label=C#
1FileStream fs = new FileStream("./img.jpg", FileMode.Open);
2BinaryReader br = new BinaryReader(fs);
3byte[] img = br.ReadBytes((int)fs.Length);
4br.Close();
5fs.Close();
6string url = "http://127.0.0.1:8402?threshold=0.1";
7HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
8request.Method = "POST";
9Stream stream = request.GetRequestStream();
10stream.Write(img, 0, img.Length);
11stream.Close();
12
13WebResponse response = request.GetResponse();
14StreamReader sr = new StreamReader(response.GetResponseStream());
15Console.WriteLine(sr.ReadToEnd());
16sr.Close();
17response.Close();
Cpp label=C++ 需要安装curl
1#include <sys/stat.h>
2 #include <curl/curl.h>
3 #include <iostream>
4 #include <string>
5 #define S_ISREG(m) (((m) & 0170000) == (0100000))
6 #define S_ISDIR(m) (((m) & 0170000) == (0040000))
7
8 size_t write_callback(void *ptr, size_t size, size_t num, void *data) {
9 std::string *str = dynamic_cast<std::string *>((std::string *)data);
10 str->append((char *)ptr, size*num);
11 return size*num;
12 }
13
14 int main(int argc, char *argv[]) {
15 const char *post_data_filename = "./img.jpg";
16 FILE *fp = NULL;
17 std::string response;
18 struct stat stbuf = { 0, };
19 fp = fopen(post_data_filename, "rb");
20 if (!fp) {
21 fprintf(stderr, "Error: failed to open file "%s"
22 ", post_data_filename);
23 return -1;
24 }
25 if (fstat(fileno(fp), &stbuf) || !S_ISREG(stbuf.st_mode)) {
26 fprintf(stderr, "Error: unknown file size "%s"
27 ", post_data_filename);
28 return -1;
29 }
30 CURL *curl;
31 CURLcode res;
32 curl_global_init(CURL_GLOBAL_ALL);
33 curl = curl_easy_init();
34 if (curl != NULL) {
35 curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:24401?threshold=0.1");
36 curl_easy_setopt(curl, CURLOPT_POST, 1L);
37 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE,(curl_off_t)stbuf.st_size);
38 curl_easy_setopt(curl, CURLOPT_READDATA, (void *)fp);
39 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
40 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
41 res = curl_easy_perform(curl);
42
43 if (res != CURLE_OK) {
44 fprintf(stderr, "curl_easy_perform() failed: %s
45 ", curl_easy_strerror(res));
46 }
47 std::cout << response << std::endl; // response即为返回的json数据
48 curl_easy_cleanup(curl);
49 }
50 curl_global_cleanup();
51 fclose(fp);
52 return 0;
53 }
http 返回数据
字段 | 类型说明 | 其他 |
---|---|---|
error_code | Number | 0为成功,非0参考message获得具体错误信息 |
results | Array | 内容为具体的识别结果。其中字段的具体含义请参考预测图像-返回格式 一节 |
cost_ms | Number | 预测耗时ms,不含网络交互时间 |
返回示例
1{
2 "cost_ms": 52,
3 "error_code": 0,
4 "results": [
5 {
6 "confidence": 0.94482421875,
7 "index": 1,
8 "label": "IronMan",
9 "x1": 0.059185408055782318,
10 "x2": 0.18795496225357056,
11 "y1": 0.14762254059314728,
12 "y2": 0.52510076761245728
13 },
14 {
15 "confidence": 0.94091796875,
16 "index": 1,
17 "label": "IronMan",
18 "x1": 0.79151463508605957,
19 "x2": 0.92310667037963867,
20 "y1": 0.045728668570518494,
21 "y2": 0.42920106649398804
22 }
23 ]
24}
预测视频
SDK 提供了支持摄像头读取、视频文件和网络视频流的解析工具类VideoDecoding
,此类提供了获取视频帧数据的便利函数。通过VideoConfig
结构体可以控制视频/摄像头的解析策略、抽帧策略、分辨率调整、结果视频存储等功能。对于抽取到的视频帧可以直接作为SDK infer 接口的参数进行预测。
- 接口
classVideoDecoding
:
1 /**
2 * @brief 获取输入源的下一帧
3 * @param frame_tensor
4 * @return
5 */
6 virtual int next(FrameTensor &frame_tensor) = 0;
7
8 /**
9 * @brief 显示当前frame_tensor中的视频帧
10 * @param frame_tensor
11 * @return
12 */
13 virtual int display(const FrameTensor &frame_tensor) = 0;
14
15 /**
16 * @brief 将当前frame_tensor中的视频帧写为本地视频文件
17 * @param frame_tensor
18 * @return
19 */
20 virtual int save(FrameTensor &frame_tensor) = 0;
21
22 /**
23 * @brief 获取视频的fps属性
24 * @return
25 */
26 virtual int get_fps() = 0;
27 /**
28 * @brief 获取视频的width属性
29 * @return
30 */
31 virtual int get_width() = 0;
32
33 /**
34 * @brief 获取视频的height属性
35 * @return
36 */
37 virtual int get_height() = 0;
struct VideoConfig
1/**
2 * @brief 视频源、抽帧策略、存储策略的设置选项
3 */
4struct VideoConfig {
5 SourceType source_type; // 输入源类型
6 std::string source_value; // 输入源地址,如视频文件路径、摄像头index、网络流地址
7 int skip_frames{0}; // 设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true
8 int retrieve_all{false}; // 是否抽取所有frame以便于作为显示和存储,对于不满足skip_frames策略的frame,把所抽取帧的is_needed置为false
9 int input_fps{0}; // 在采取抽帧之前设置视频的fps
10 Resolution resolution{Resolution::kAuto}; // 采样分辨率,只对camera有效
11
12 bool enable_display{false};
13 std::string window_name{"EasyEdge"};
14 bool display_all{false}; // 是否显示所有frame,若为false,仅显示根据skip_frames抽取的frame
15
16 bool enable_save{false};
17 std::string save_path; // frame存储为视频文件的路径
18 bool save_all{false}; // 是否存储所有frame,若为false,仅存储根据skip_frames抽取的frame
19
20 std::map::string, std::string> conf;
21};
source_type
:输入源类型,支持视频文件、摄像头、网络视频流三种,值分别为1、2、3。
source_value
: 若source_type
为视频文件,该值为指向视频文件的完整路径;若source_type
为摄像头,该值为摄像头的index,如对于/dev/video0
的摄像头,则index为0;若source_type
为网络视频流,则为该视频流的完整地址。
skip_frames
:设置跳帧,每隔skip_frames帧抽取一帧,并把该抽取帧的is_needed置为true,标记为is_needed的帧是用来做预测的帧。反之,直接跳过该帧,不经过预测。
retrieve_all
:若置该项为true,则无论是否设置跳帧,所有的帧都会被抽取返回,以作为显示或存储用。
input_fps
:用于抽帧前设置fps。
resolution
:设置摄像头采样的分辨率,其值请参考easyedge_video.h
中的定义,注意该分辨率调整仅对输入源为摄像头时有效。
conf
:高级选项。部分配置会通过该map来设置。
注意:
- 如果使用
VideoConfig
的display
功能,需要自行编译带有GTK选项的opencv,默认打包的opencv不包含此项。 - 使用摄像头抽帧时,如果通过
resolution
设置了分辨率调整,但是不起作用,请添加如下选项:
1video_config.conf["backend"] = "2";
3.部分设备上的CSI摄像头尚未兼容,如遇到问题,可以通过工单、QQ交流群或微信交流群反馈。
具体接口调用流程,可以参考SDK中的demo_video_inference
。
错误说明
SDK所有主动报出的错误,均覆盖在EdgeStatus
枚举中。同时SDK会有详细的错误日志,开发者可以打开Debug日志查看额外说明:
1EdgeLogConfig log_config;
2log_config.enable_debug = true;
3global_controller()->set_log_config(log_config);
FAQ
1. 如何处理一些 undefined reference?
如:undefined reference to `curl_easy_setopt@CURL_OPENSSL_3'
可以通过安装libcurl3 libcurl-openssl1.0-dev
来解决。
如果开发者想不想使用低版本的openssl(如Ubuntu 18.04), 可以link静态库easyedge_static.a
,自己指定需要的Library的版本。
示例:修改CMakeList.txt
1find_package(CURL REQUIRED)
2target_link_libraries(easyedge_demo ${OpenCV_LIBS} easyedge_static pthread ${CURL_LIBRARIES} paddle-mobile)
2. error while loading shared libraries: libeasyedge.so.0.4.0: cannot open shared object file: No such file or directory
类似错误包括libpaddle-mobile.so
找不到。
直接运行SDK自带的二进制可能会有这个问题,设置LD_LIBRARY_PATH为SDK部署包中的lib目录即可。 开发者自行使用CMake编译的二进制可以有效管理.so的依赖。
3. 使用libcurl请求http服务时,速度明显变慢
这是因为libcurl请求continue导致server等待数据的问题,添加空的header即可
1headers = curl_slist_append(headers, "Expect:");
4. 预测过程中报内存不足“Killed”
此问题仅出现在ZU5,因为FZ5A带vcu,给他预留的内存过大导致,如果用不到VCU可以把这部分改小。修改/run/media/mmcblk1p1/uEnv.txt:
1ethaddr=00:0a:35:00:00:09
2uenvcmd=fatload mmc 1 0x3000000 image.ub && bootm 0x3000000
3
4bootargs=earlycon console=ttyPS0,115200 clk_ignore_unused cpuidle.off=1 root=/dev/mmcblk1p2 rw rootwait cma=128M
注意中间空行要保留。
5. 预测结果异常
如果购买的计算盒较早,驱动文件较旧,而SDK比较新(或SDK比较旧,但是计算盒较新),可能出现结果异常,如结果均为空或者nan
。
请参考“软核版本”小节更新软核和驱动版本。
6. 编译过程报错file format not recognized
1libeasyedge.so: file format not recognized; treating as linker script
下载的SDK zip包需要放到板子内部后,再解压、编译。
7. 提示 driver_version(1.4.0) not match paddle_lite_version(1.5.1)
需更新驱动,否则可能导致结果异常。参考“软核版本”小节。