使用deepstream对自己模型进行加速推理以及与ROS通信
背景:deepstream检测到的结果:框位置和目标类型、概率值需要进一步传递到ros节点中分析使用,本例是采用TCP/IP通信协议将这些数据发送出去
来源:使用darknet框架,利用yolov3-tiny模型在nvidia jetson nano上进行目标检测推理的时候,帧率较低,约6ps,不能满足实际任务需求。庆幸的是Nvidia提供了很多加速工具,典型的如tensorRT和deepstream。
文章目录
- 1.deepstream使用过程
- 1.1 安装
- 1.1.1 安装依赖:
- 1.1.2 安装librdkafka
- 1.1.3 安装DeepStream SDK
- 1.2 例子-加速官方模型进行推理
- 1.2.1.2 config_infer_primary_yoloV3_tiny.txt文件
- 1.2.2 加速yolov3并推理:
- 1.3 实践-加速自己的模型完成推理
- 1.3.1 准备必要的文件
- 1.3.2 修改deepstream_app_config_yoloV3_tiny.txt文件
- 1.3.2.1 修改图像来源
- 1.3.2.2 修改图像大小
- 1.3.3 修改config_infer_primary_yoloV3_tiny.txt文件
- 1.3.3.1 指定模型配置文件
- 1.3.3.2 指定模型文件
- 1.3.3.3 指定图片集
- 1.3.3.4 修改类型个数
- 2.与ROS进行集成
- 2.1 修改程序:nvdsinfer_custom_impl_Yolo.cpp
- 2.1.1 添加头文件
- 2.1.2 函数decodeYoloV3Tensor()函数
- 2.1.3 位置3:添加自定义函数int_to_string()
- 2.1.4 位置4:添加自定义函数socket_write()
- 2.2 创建ROS节点:detection_server
1.deepstream使用过程
1.1 安装
如果是使用nvidia的设备如nano ,如在烧录系统时不自定义安装的话,deepstream会一并被安装上。
如果没有安装,可按照如下方式进行安装:
1.1.1 安装依赖:
$ sudo apt install \libssl1.0.0 \libgstreamer1.0-0 \gstreamer1.0-tools \gstreamer1.0-plugins-good \gstreamer1.0-plugins-bad \gstreamer1.0-plugins-ugly \gstreamer1.0-libav \libgstrtspserver-1.0-0 \libjansson4=2.11-1
1.1.2 安装librdkafka
sudo apt-get install librdkafka1=0.11.3-1build1
1.1.3 安装DeepStream SDK
从这里下载,然后通过如下执行解压缩:
$ tar -xpvf deepstream_sdk_v4.0.2_jetson.tbz2
之后依次执行如下指令进行安装:
$ cd deepstream_sdk_v4.0.2_jetson
$ sudo tar -xvpf binaries.tbz2 -C /
$ sudo ./install.sh
$ sudo ldconfig
1.2 例子-加速官方模型进行推理
可以首先运行这个例子,来尝鲜,之后来解释其实际实现过程:
### 1.2.1 加速yolov3-tiny模型并推理:
cd /home/inano/deepstream/sources/objectDetector_Yolo
deepstream-app -c deepstream_app_config_yoloV3_tiny.txt
如果一切运行正常,且是第一次运行(目录中没有deepstream生成的加速模型),deepstream会下载yoloV3_tiny的权重文件,配置参数等文件,然后接着会生成加速模型,这个可能会需要一段时间,之后便会显示实时的图像及检测效果(含检测框)。若非第一次运行,即deepstream检测到目录中已经有加速模型,则直接开始进行推理,耗时也会短很多。
可以看到deepstream使用过程很简单,通过使用deepstream-app来加载配置文件就可以,所以一个关键的点就是这个配置文件:
#### 1.2.1.1 deepstream_app_config_yoloV3_tiny.txt文件
文件的有效部分如下,一般需要修改的参数如注释所示:
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
#gie-kitti-output-dir=streamscl[tiled-display]
enable=1
rows=1
columns=1
width=1280 # 在推理中,要使用的图像大小
height=720
gpu-id=0
#(0): nvbuf-mem-default - Default memory allocated, specific to particular platform
#(1): nvbuf-mem-cuda-pinned - Allocate Pinned/Host cuda memory, applicable for Tesla
#(2): nvbuf-mem-cuda-device - Allocate Device cuda memory, applicable for Tesla
#(3): nvbuf-mem-cuda-unified - Allocate Unified cuda memory, applicable for Tesla
#(4): nvbuf-mem-surface-array - Allocate Surface Array memory, applicable for Jetson
nvbuf-memory-type=0[source0]
enable=1
#Type - 1=Camera V4L2 2=URI 3=MultiURI
type=3 # 如果要用摄像头进行检测,就将type设置为0
uri=file://../../samples/streams/sample_1080p_h264.mp4
num-sources=1
gpu-id=0
# (0): memtype_device - Memory type Device
# (1): memtype_pinned - Memory type Host Pinned
# (2): memtype_unified - Memory type Unified
cudadec-memtype=0[sink0]
enable=1
#Type - 1=FakeSink 2=EglSink 3=File
type=2
sync=0
source-id=0
gpu-id=0
nvbuf-memory-type=0[osd]
enable=1
gpu-id=0
border-width=1
text-size=15
text-color=1;1;1;1;
text-bg-color=0.3;0.3;0.3;1
font=Serif
show-clock=0
clock-x-offset=800
clock-y-offset=820
clock-text-size=12
clock-color=1;0;0;0
nvbuf-memory-type=0[streammux]
gpu-id=0
##Boolean property to inform muxer that sources are live
live-source=0
batch-size=1
##time out in usec, to wait after the first buffer is available
##to push the batch even if the complete batch is not formed
batched-push-timeout=40000
## Set muxer output width and height
width=1920
height=1080
##Enable to maintain aspect ratio wrt source, and allow black borders, works
##along with width, height properties
enable-padding=0
nvbuf-memory-type=0# config-file property is mandatory for any gie section.
# Other properties are optional and if set will override the properties set in
# the infer config file.
[primary-gie]
enable=1
gpu-id=0
#model-engine-file=model_b1_fp32.engine
labelfile-path=labels.txt
batch-size=1
#Required by the app for OSD, not a plugin property
bbox-border-color0=1;0;0;1
bbox-border-color1=0;1;1;1
bbox-border-color2=0;0;1;1
bbox-border-color3=0;1;0;1
gie-unique-id=1
nvbuf-memory-type=0
config-file=config_infer_primary_yoloV3_tiny.txt #加载的配置文件(这个里面要设置我们想要加速的模型,以及模型相关的配置文件)[tests]
file-loop=0
1.2.1.2 config_infer_primary_yoloV3_tiny.txt文件
该文件中要指定的有效信息很多,若模型文件,模型配置文件等。
[property]
gpu-id=0
net-scale-factor=1
#0=RGB, 1=BGR
model-color-format=0
custom-network-config=yolov3-tiny.cfg # 模型配置文件
model-file=yolov3-tiny.weights # 模型权重文件model-engine-file=model_b1_fp32.engine # 这里可以直接给出engine模型,就不用每次浪费大量时间来转换模型了,v3尤其浪费时间。每次改动需要把其屏蔽,执行时打开
labelfile-path=labels.txt # 标签文件,即模型对应的要检测的图片列表
## 0=FP32, 1=INT8, 2=FP16 mode
network-mode=0 # 网络计算要使用的浮点数类型,这个要看你GPU设备是否支持以及你的算力要求,默认为FP32
num-detected-classes=80 # 模型分类的个数
gie-unique-id=1
is-classifier=0
maintain-aspect-ratio=1
parse-bbox-func-name=NvDsInferParseCustomYoloV3Tiny
# 下面加载的是用于检测后处理的一个动态库,是deepstream前端检测和后端后处理之间的一个接口该动态库是将nvdsinfer_custom_impl_Yolo目录下的文件编译得到的,nvdsinfer_custom_impl_Yolo主要是面向后处理的,如检测完后,矩形框绘制,检测类型显示,阈值设置等。如果需要修改这些参数,则修改完后需要重新编译。
custom-lib-path=nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so
1.2.2 加速yolov3并推理:
安装同样的运行方式可以查看yolov3加速推理的效果。
$ cd /home/inano/deepstream/sources/objectDetector_Yolo
$ deepstream-app -c deepstream_app_config_yoloV3.txt
1.3 实践-加速自己的模型完成推理
根据上述配置文件的描述,我们想加速我们自己训练好的yolov3-tiny模型并使用摄像头实时推理,需要进行如下几步骤:
1.3.1 准备必要的文件
需要将我们自己的模型文件xf_yolov3-tiny.weights,xf_yolov3-tiny.cfg,imagelist.txt拷贝到/home/inano/deepstream/sources/objectDetector_Yolo目录中,这些是加速必须的文件。
1.3.2 修改deepstream_app_config_yoloV3_tiny.txt文件
1.3.2.1 修改图像来源
我们图像来源于摄像头,即将deepstream_app_config_yoloV3_tiny.txt文件中,type=3 修改为 type=1;
1.3.2.2 修改图像大小
由于nano算力有限约0.47t,为了想使推理加速后有高的帧率,将图像大小修改,即将即将deepstream_app_config_yoloV3_tiny.txt文件中;width=1280 height=720,修改为width=640 height=360
1.3.3 修改config_infer_primary_yoloV3_tiny.txt文件
1.3.3.1 指定模型配置文件
由于文件就在本文件夹中,所以不需要使用什么绝对路径和相对路径,直接修改,即:
将文件中 custom-network-config=yolov3-tiny.cfg 修改为 custom-network-config=xf_yolov3-tiny.cfg
1.3.3.2 指定模型文件
由于文件就在本文件夹中,所以不需要使用什么绝对路径和相对路径,直接修改,即:
将文件中 model-file=yolov3-tiny.weights 修改为 model-file=xf_yolov3-tiny.weights
1.3.3.3 指定图片集
将labelfile-path=labels.txt 修改为 labelfile-path=imagelist.txt
1.3.3.4 修改类型个数
将num-detected-classes=80 修改为 num-detected-classes=12 (模型对应的imagelist对应12种物品)
2.与ROS进行集成
如果想要使用检测完的结果,如根据识别结果进行其他的操作。就需要我们将里面的检测框位置、类型、置信度等信息提取出来给其他节点使用。我们这里的需求是将其结果传递给机器人动作执行节点,即需要将检测结果发送到ros网络中。在这里我们通过socket通信作为传输方式。编写一个中间节点,一边通过socket接收数据,另一边通过ros的话题和服务发布出去。
而通过分析了解到 后处理功能全部在nvdsinfer_custom_impl_Yolo文件夹中,其中我们需要的数据都在nvdsparsebbox_Yolo.cpp文件中,所以我们修改这个文件就可以了。操作步骤如下:
2.1 修改程序:nvdsinfer_custom_impl_Yolo.cpp
2.1.1 添加头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sstream>
2.1.2 函数decodeYoloV3Tensor()函数
末尾 "return binfo;“上方添加以下代码:
std::cout<<"There are "<<binfo.size()<<" kinds of object" <<endl;
if(binfo.size()>0)
{string send_result = "AA";send_result = send_result + int_to_string(binfo.size());for(int i=0;i<binfo.size();i++){std::cout <<"class::"<<binfo[i].classId <<" location:"<<" x:"<<binfo[i].left<<" y:"<<binfo[i].top<<" w:"<<binfo[i].width<<" h:"<<binfo[i].height <<" confidence:"<<binfo[i].detectionConfidence<<std::endl;send_result = send_result +"BB"+int_to_string(binfo[i].classId)+int_to_string(binfo[i].left)+int_to_string(binfo[i].top)+int_to_string(binfo[i].width)+int_to_string(binfo[i].height)+"CC";}send_result = send_result + "DD";socket_write(send_result);
}
2.1.3 位置3:添加自定义函数int_to_string()
在decodeYoloV3Tensor()函数上方空白处添加自定义函数:int_to_string(int)
string int_to_string(int a)
{stringstream ss;string str;ss << a;ss >> str;if(str.size()<4){string s3(4-str.size(),'0');str = s3+str;}return str;
}
2.1.4 位置4:添加自定义函数socket_write()
在decodeYoloV3Tensor()函数上方空白处添加自定义函数:socket_write(std::string s)
bool socket_write(std::string s)
{int socket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd == -1){cout<<"socket create failed"<<endl;exit(-1);}struct sockaddr_in_addr;addr.sin_family = AF_INET;addr.sin_port = htons(8888);addr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));if (res == -1){cout << "bind 链接失败:" << endl;exit(-1);}const char *p = s.c_str(); write(socket_fd,p, s.size());close(socket_fd);
}
2.2 创建ROS节点:detection_server
本节点作为一个过渡节点,一方面作为上述TCP通信的服务端,实时接收客户端发送过来的流数据,然后读取并解析;另一方面作为话题发布器,将解析到的位置信息和类型信息通过话题的形式发布出去。程序如下:
/*used for deepstream*/
#include <ros/ros.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <waste_clasify/UarmAction.h>
#include <waste_clasify/Uarm.h>
#include <iostream>
/*注意使用eigen的时候路径在/usr/include/eigen3下也可执行如下指令:cp -rf /usr/include/eigen3/Eigen /usr/include/Eigen -R
*/
#include <eigen3/Eigen/Dense>using namespace std;
using namespace Eigen;#define BUF_SIZE 255struct Pose_3D
{float x;float y;float z;
} pose_3d;Pose_3D position_to_3D(int U, int V)
{// //单目相机标定结果,f及光心坐标cMatrix<float, 3, 3> Nei_Can;Nei_Can << 386.157194, 0.000000, 159.067975, 0.000000, 388.063524, 129.470492, 0, 0, 1;// //手眼标定结果,旋转及平移矩阵Matrix<float, 4, 4> Wai_Can;Wai_Can << -47.2253736, 808.943874, -934.623712, 486.125510, 896.251615, 28.4136536, -178.435138, 36.1651555, -3.05148887, 5.81405247, -1133.56463, 279.001094, 0, 0, 0, 1;float Z = 0.1625;//像素坐标系坐标Matrix<float, 3, 1> Pos_XS;Pos_XS << 0,0,0; //初始化为0Pos_XS << Z*U,Z*V,Z*1;//计算相机坐标系的三维坐标:Matrix<float, 3, 1> Pos_TX3D;//在图像坐标系下坐标Pos_TX3D << 0,0,0;//初始化为0Pos_TX3D = Nei_Can.inverse() * Pos_XS;//计算在机械臂基坐标系坐标:Matrix<float, 4, 1> Pos_TX3D_QC;//在图像坐标系下坐标,本行是为了转为齐次形式,便于矩阵相乘Pos_TX3D_QC << 0,0,0,0;//初始化为0Pos_TX3D_QC << Pos_TX3D(0,0),Pos_TX3D(1,0),0.214,1;Matrix<float, 4, 1> Pos_World;Pos_World << 0,0,0,0;//初始化为0Pos_World= Wai_Can*Pos_TX3D_QC;//在机器人基坐标系下坐标Pose_3D pos_world_3d;pos_world_3d.x = Pos_World(0, 0);pos_world_3d.y = Pos_World(1, 0);pos_world_3d.z = 30;return pos_world_3d;
}int string_to_int(string s)
{stringstream ss;ss << s;int i;ss >> i;return i;
}int outof0(string str)
{int i = 0;while (str[i] == '0'){i += 1;}str.erase(0, i);int num = string_to_int(str);return num;
}int main(int argc, char **argv)
{ros::init(argc, argv, "deepstream_server");ros::NodeHandle n;ros::Publisher chatter_pub = n.advertise<waste_clasify::Uarm>("/waste/detection_result", 1000);//1.创建一个socket socket() 函数确定了套接字的各种属性int socket_fd = socket(AF_INET, SOCK_STREAM, 0);if (socket_fd == -1){cout << "socket 创建失败: " << endl;exit(1);}//2.准备通讯地址(必须是服务器的)192.168.1.49是本机的IPstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8888); //将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian)addr.sin_addr.s_addr = inet_addr("192.168.43.78"); //net_addr方法可以转化字符串,主要用来将一个十进制的数转化为二进制的数,用途多于ipv4的IP转化。//3.bind()绑定//参数一:0的返回值(socket_fd)//参数二:(struct sockaddr*)&addr 前面结构体,即地址//参数三: addr结构体的长度//通过 bind() 函数将套接字 serv_sock 与特定的 IP 地址和端口绑定,IP 地址和端口都保存在 sockaddr_in 结构体中int res = bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr));if (res == -1){cout << "bind创建失败: " << endl;exit(-1);}cout << "bind ok 等待客户端的连接" << endl;//4.监听客户端listen()函数,让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”//参数二:进程上限,一般小于30listen(socket_fd, 30);//5.等待客户端的连接accept(),返回用于交互的socket描述符struct sockaddr_in client;socklen_t len = sizeof(client);while (1){//accept() 函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。int fd = accept(socket_fd, (struct sockaddr *)&client, &len);if (fd == -1){cout << "accept错误\n"<< endl;exit(-1);}//6.使用第5步返回socket描述符,进行读写通信。char *ip = inet_ntoa(client.sin_addr);cout << "客户: 【" << ip << "】连接成功" << endl;write(fd, "welcome", 7); //必须给客户端返回一些数据,否则客户端会认为没有完成任务发送,客户端会阻塞。char buffer[BUF_SIZE] = {};int size = read(fd, buffer, sizeof(buffer)); //通过fd与客户端联系在一起,返回接收到的字节数//第一个参数:accept 返回的文件描述符//第二个参数:存放读取的内容//第三个参数:内容的大小cout << "接收到字节数为: " << size << endl;cout << "内容: " << buffer << endl;string result(buffer);waste_clasify::Uarm result_pub;int number_detection = outof0(result.substr(2, 4));vector<int> possible_indexes;vector<float> possible_locations;if (number_detection){result_pub.data = number_detection;string flag = "BB";int position = 0;int i = 1;while ((position = result.find(flag, position)) != string::npos){possible_indexes.push_back(position);position++;}cout << possible_indexes.size() << endl;for (int i = 0; i < possible_indexes.size(); i++){possible_locations.push_back((float)(outof0(result.substr(possible_indexes[i] + 2, 4))));//根据返回的矩形框计算质心位置int X0 = (int)(outof0(result.substr(possible_indexes[i] + 6, 4)) + outof0(result.substr(possible_indexes[i] + 14, 4)) / 2);int Y0 = (int)(outof0(result.substr(possible_indexes[i] + 10, 4)) + outof0(result.substr(possible_indexes[i] + 18, 4)) / 2);//计算三维位置Pose_3D Pos_Grasp = position_to_3D(X0, Y0);possible_locations.push_back(Pos_Grasp.x);possible_locations.push_back(Pos_Grasp.y);possible_locations.push_back(Pos_Grasp.z);}result_pub.pose = possible_locations;chatter_pub.publish(result_pub);}//7.关闭sockfdclose(fd);memset(buffer, 0, BUF_SIZE);}close(socket_fd);return 0;
}
使用deepstream对自己模型进行加速推理以及与ROS通信相关推荐
- 量化感知训练实践:实现精度无损的模型压缩和推理加速
简介:本文以近期流行的YOLOX[8]目标检测模型为例,介绍量化感知训练的原理流程,讨论如何实现精度无损的实践经验,并展示了量化后的模型能够做到精度不低于原始浮点模型,模型压缩4X.推理加速最高2.3 ...
- 如何给深度学习加速——模型压缩、推理加速
深度学习模型往往受到端计算力的限制,无法很好的部署在移动端或无法降低端的计算成本.例如自动驾驶的模型就过于巨大,而且往往是很多模型并行,所以一般会用一些加速的方法来降低推算的计算力要求. 加速方法有多 ...
- Intel N100工控机使用核显加速推理yolov5模型
Intel N100工控机使用核显加速推理yolov5模型 前言 安装openvino环境 核显加速运行yolov5 进一步加速 再进一步量化压榨 前言 今年3月初开始,某平台开始陆续上货基于英特尔A ...
- 1、pth转onnx模型、onnx转tensorrt模型、python中使用tensorrt进行加速推理(全网最全,不信你打我)
本文向所有亲们介绍在python当中配置tensorrt环境.使用tensorrt环境进行推理的教程,主要分为两大部分,第一部分环境配置,第二部分前向推理. 第一部分 环境配置 第一步:检查你的系统类 ...
- float32精度_混合精度对模型训练和推理的影响
单精度/双精度/半精度/混合精度 计算机使用0/1来标识信息,每个0或每个1代表一个bit.信息一般会以下面的三种形式表示: 1 字符串 字符串的最小单元是char,每个char占8个bit,也就是1 ...
- 北京 | 免费高效训练及OpenVINO™加速推理深度学习实战,送Intel神经计算棒二代...
当今人工智能时代,深度学习极大得促进了计算机视觉技术的快速应用和成熟,也是算法工程师们必须掌握的一项技能,然而,不同环境的依赖部署,高算力的需求,海量数据量需求及算法应用高硬件成本也让深度学习陷入了规 ...
- yolov3-tiny原始weights模型转onnx模型并进行推理
时隔不知道多少天,我记起来我还有部分博客没写完(偷懒),所以不能偷懒把它完成!! 这篇博客的主要内容 将yolov3-tiny.weights模型转换到.onnx模型: 使用onnnxruntime- ...
- Java / Tensorflow - API 调用 pb 模型使用 GPU 推理
目录 一.引言 二.Java / Tensorflow 代码配置 1.代码配置 2.Maven 配置 三.环境检测 1.显卡检测 2.显卡监控 四.推理踩坑 1.异常现象 2.异常日志 五.安装 cu ...
- onnx标准 onnxRuntime加速推理引擎
onnx标准 & onnxRuntime加速推理引擎 文章目录 onnx标准 & onnxRuntime加速推理引擎 一.onnx简介 二.pytorch转onnx 三.tf1.0 / ...
最新文章
- 有bug!PyTorch在AMD CPU的计算机上卡死了
- golang []byte和string相互转换
- 用Android自带的signapk.jar + .x509.pem + .pk8签名应用程序
- C语言链表的转置算法,c语言编程集 数据结构 顺序表 点链表 数制转换 矩阵转置.doc...
- firefox2.0的拖放式搜索怎么不行了?是设置问题吗?
- JDK 12:实际中的切换语句/表达式
- stopwatch_在Java中衡量执行时间– Spring StopWatch示例
- pytorch 入门学习 MSE
- OpenCV之响应鼠标(一):利用鼠标获取坐标
- C/C++底层实现指定磁盘只读
- android软件安全权威指南 pdf_目录公众号内的所有资源软件!
- 数据库第四次作业:数据备份与还原
- csv文件行数超过软件上限解决方案
- 【保姆级教程,100%成功】MAC OS打开ntfs格式U盘
- HTML5 CSS3做的一个静态的苹果官网首页
- MySQL:连接错误
- RuiJi Scraper 分页抽取
- 魔兽怀旧服务器位置,《魔兽世界》怀旧服稀有狼位置坐标大全
- 腾讯云CPU处理器Intel Ice Lake主频2.7GHz睿频3.3GHz)
- 谁为企业数字化转型“保驾护航”?