cover

问题背景

这篇博客用来记录参加海赛C2过程中,如何实现在Jetson Nano上部署目标检测算法以及使用串口通信的方法。

数据标注与模型训练

YoloV5的使用与简单讲解

Yolov5模型目前在网络上已经经过了许多考验,且其Github官方也仍在不断更新,其相关平台的部署在官网也有很多教程,所以如果想在嵌入式平台部署目标检测以及分类网络,选择Yolov5可以带给我们极大的便利,这也是我选择使用Yolov5的原因。以下内容都是基于Yolov5目标检测模型的部署。

数据集的标注(使用Labelimg进行数据标注)

  1. 安装labelimg
    这里是在Anaconda环境下的安装

    conda create -n label python==3.9 # 注意这里必须是python3.9版本,否则安装会导致无法正常使用
    conda activate label # 激活环境
    pip install labelimg # 使用pip安装labelimg
    labelimg # 这是打开labelimg界面的指令
  2. 打开labelimg并配置信息
    输入命令:

    labelimg

    之后会进入如下界面

    • 这里主要需要配置上图中三个红圈选项
      先简单介绍一下:
      • Open Dir:这里是打开你存放图片的文件夹
      • Change Save Dir:这里是你图片标注的标签数据存放位置
      • Yolo:这个是选择标注数据的格式,其中有很多个选项,根据自己模型训练所需的标签类型进行选择,这里选择yolo因为我训练的数据标签要求是Yolo类型。
  3. 进行数据标注
    当把上述配置准备工作完成后,就可以开始数据标注了啦,如下视频所示:

    • W键:显示十字光标,出现十字光标后即可配合鼠标框住目标
    • D键:下一张图片
    • A键:上一张图片
    • Ctrl+S键:对当前操作进行保存
    • Ctrl+鼠标滚轮:对图片上鼠标所指区域进行放大和缩小操作
      一般来说,知道上述四种快捷键即可帮助你进行快捷标注数据啦

进行训练

在标注完数据后,剩下的就是核心环节:训练模型

  1. 下载官方源码
    首先从Yolov5官方Github上clone其代码:https://github.com/ultralytics/yolov5
    可以使用两种方式下载其代码:

    • 方法一:使用git clone命令下载:

      如上图所示,copy当前github下载链接,然后在你的虚拟环境下输入命令:

      git clone git@github.com:ultralytics/yolov5.git  # 这条指令会在你的当前路径下载Yolov5文件

      如果失败了,显示没有找到git命令,那么就是当前你的虚拟环境没有安装git,使用conda install git 或者 pip install git命令进行安装
      如果失败了,显示是网络问题,那么可能你需要挂梯子才可以。如果仍然无法解决,那么就尝试开关重启。

    • 方法二:直接从官网下载zip文件:

      如上图所示,点击红圈选项,下载zip文件,随后将其解压即可得到Yolov5官方文件

  2. 配置模型训练相关信息
    然后进入文件目录,在data文件夹下创建一个C2.yaml文件,其内容如下:

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    # COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017) by Ultralytics
    # Example usage: python train.py --data coco128.yaml
    # parent
    # ├── yolov5
    # └── datasets
    #     └── coco128  ← downloads here (7 MB)
    
    
    # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
    path: ../datasets/coco128  # dataset root dir
    train: images/train2017  # train images (relative to 'path') 128 images
    val: images/train2017  # val images (relative to 'path') 128 images
    test:  # test images (optional)
    
    # Classes
    names:
      0: hole

    文件路径以及内容详细参考以下图片:

    其中:

    • path:是指根目录的路径
    • train:是指要训练的图片存放位置
    • val:是指要验证的图片存放位置(这里和train保持一致即可)
    • name:类别存放位置,如图中所示:0表示目标类别索引(从0开始),hole表示其代表的类别名称

    这里再附上一张训练数据存放的文件夹路径及内容:

    这里只需要关注白色圈中的两个文件夹dataset与yolov5,其中路径相对位置如下:

    # parent
    # ├── yolov5
    # └── datasets
    #     └── coco128
    #         └── images
    #             └── train2017 # 文件夹下是训练图片
    #         └── labels
    #             └── train2017 # 文件夹下是训练图片对应的标注文件

之后按照官方文档进行配置你的虚拟环境,配置完成后即可使用终端在当前路径下输入以下命令进行训练:

python train.py --data C2.yaml --weights yolov5n.pt --cfg yolov5n.yaml --img 640  --batch-size 4

指令解释:

  • –data:C2.yaml为前面所创建的文件,里面存放的是配置训练数据的相关内容
  • –weights: yolov5n.pt是指使用官方的yolov5n预训练模型来进行迁移学习训练
  • –cfg:yolov5n.yaml文件时模型的结构文件
  • –img:640是指送进模型进行训练的图片尺寸,640表示640x640。注意此数字应为32的整数倍,不然训练会出错
  • –batch-size:4是指一次送入模型训练的图片个数为4,这一项和你的GPU性能相联系,batch-size过大会导致你的GPU因为显存不够而无法训练。

如果上述命令输入后显示错误,一般来说是你的batch-size设置过大了,调小即可,如2或1。
当然还有一个前提是,你按照官方文档成功配置好了你的虚拟环境,如pytorch、cuda等。

训练完成后,你的模型存放在如下路径:

# parent
# ├── yolov5
#     └── runs
#         └── train
#             └── exp # 不一定是exp,也有可能是exp+一个数字
#                 └── weights
#                     └── best.pt # 这是训练得到的最优的权重

安装ros,以进行串口通信

这里先附上jetson nano的引脚图

首先进行换源

  1. 备份源,防止操作错误以复用
    cp /etc/apt/sources.list /etc/apt/sources.list.bak
  2. 编辑源文件,添加镜像
    sudo gedit /etc/apt/sources.list
  3. 在打开的源文件中,将原内容删除,然后添加以下内容:
  • 清华源:
    # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
    deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
    # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
    deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
    # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
    deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse
    # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse
    deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse
    # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse
    deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-proposed main restricted universe multiverse
    deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-proposed main restricted universe multiverse
  • 中科大源:
    # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
    deb http://mirrors.ustc.edu.cn/ubuntu-ports bionic main restricted universe multiverse
    # deb-src http://mirrors.ustc.edu.cn/ubuntu-ports bionic main restricted universe multiverse
    deb http://mirrors.ustc.edu.cn/ubuntu-ports bionic-updates main restricted universe multiverse
    # deb-src http://mirrors.ustc.edu.cn/ubuntu-ports bionic-updates main restricted universe multiverse
    deb http://mirrors.ustc.edu.cn/ubuntu-ports bionic-backports main restricted universe multiverse
    # deb-src http://mirrors.ustc.edu.cn/ubuntu-ports bionic-backports main restricted universe multiverse
    deb http://mirrors.ustc.edu.cn/ubuntu-ports bionic-security main restricted universe multiverse
    # deb-src http://mirrors.ustc.edu.cn/ubuntu-ports bionic-security main restricted universe multiverse
    deb http://mirrors.ustc.edu.cn/ubuntu-ports bionic-proposed main restricted universe multiverse
    deb-src http://mirrors.ustc.edu.cn/ubuntu-ports bionic-proposed main restricted universe multiverse
  • 阿里源:
    # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
    deb https://mirrors.aliyun.com/ubuntu-ports/ bionic main restricted universe multiverse
    # deb-src https://mirrors.aliyun.com/ubuntu-ports/ bionic main restricted universe multiverse
    deb https://mirrors.aliyun.com/ubuntu-ports/ bionic-updates main restricted universe multiverse
    # deb-src https://mirrors.aliyun.com/ubuntu-ports/ bionic-updates main restricted universe multiverse
    deb https://mirrors.aliyun.com/ubuntu-ports/ bionic-backports main restricted universe multiverse
    # deb-src https://mirrors.aliyun.com/ubuntu-ports/s bionic-backports main restricted universe multiverse
    deb https://mirrors.aliyun.com/ubuntu-ports/ bionic-security main restricted universe multiverse
    # deb-src https://mirrors.aliyun.com/ubuntu-ports/ bionic-security main restricted universe multiverse
    deb https://mirrors.aliyun.com/ubuntu-ports/ bionic-proposed main restricted universe multiverse
    deb-src https://mirrors.aliyun.com/ubuntu-ports/ bionic-proposed main restricted universe multiverse
  1. 添加完成后,保存并退出。使用下述两条命令更新:
    sudo apt update
    sudo apt upgrade -y
  2. 更换ROS的国内源(两条指令差不多,第一个是国外,第二个是中科大的源,选择一个即可)
    sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
    sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.ustc.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'
  3. 然后再使用如下命令更新,完成后即配置成功
    sudo apt update

获取密钥

有两种方法,推荐方法一

  • 方法一:官网操作
    可以获取最新的密钥,不用担心密钥是否过期,但可能受网络影响
    sudo apt install curl        # 如果你没有安装curl的话,就使用这条命令
    curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
  • 方法二:网络教程
    密钥可能过期,可以到网上搜索其他人发的密钥,试一试
    sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 8D5A09DC9B929006

然后再使用如下命令对源软件列表进行更新(原先可能没密钥导致更新失败)

sudo apt update

安装ROS

  1. 安装全功能的ROS,这个安装包大概超过200M,执行以下命令:
    sudo apt install ros-melodic-desktop-full
  2. 设置环境变量
    echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
    source ~/.bashrc
  3. 安装相关的依赖
    sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential  vim openconnect openssh-server
  4. ROS初始化
    使用如下命令:
    sudo rosdep
    rosdep update
  5. ROS运行测试
    在终端分别执行以下语句,测试小乌龟(具体作用我也不懂),只要能成功运行,那么表明你的ROS安装成功。
    roscore
    rosrun turtlesim turtlesim_node
    rosrun turtlesim turtle_teleop_key

如果出现 Command rosrun not found 即 rosrun 命令找不到的情况,输入以下命令即可:

sudo apt-get install rosbash

删除卸载ROS

当发现自己安装失败无法解决时,可以尝试重新开始,那么就需要把ROS重新装一遍了。

  • 输入以下命令进行ROS的删除卸载:
    sudo apt-get purge ros-*
    sudo rm -rf /etc/ros
    gedit ~/.bashrc   # 打开该文件,删除带有melodic那一行即可
    source ~/.bashrc  # 刷新环境

可能遇到的问题报错

  1. Could not find a package configuration file provided by “serial“ with any serialConfig.cmake
    • 输入以下命令
      sudo apt-get install ros-melodic-serial
  2. 无法定位功能包 E: Unable to locate package ros-melodic-***
    • 方案一:首先使用以下命令更新一下,然后再次安装
      sudo apt update
    • 方案二:如果仍然无法下载,尝试输入以下命令,然后再次安装
      sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
      sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
      sudo apt update
    • 方案三:进行换源
      • 再次打开sources.list文件,使用以下命令:
        sudo gedit /etc/apt/sources.list
      • 在文件末尾加入以下内容:
        deb http://packages.ros.org/ros-shadow-fixed/ubuntu bionic main  
      • 然后再次使用以下命令更新完后,再次安装:
        sudo apt-get update
  3. sudo rosdep init 找不到命令
    • 首先查看rosdep是否安装,使用以下命令:
      whereis rosdep
    • 若没有显示任何信息,则表示没有安装,然后输入以下命令进行安装rosdep:
      sudo apt install python-rosdep2 -y

将模型部署在Jetson nano上

模型权重的文件类型转换

  1. 将模型权重由pt格式转换为wts格式
  • 在yolov5文件夹中创建一个gen_wts.py文件,内容如下:
    import sys
    import argparse
    import os
    import struct
    import torch
    from utils.torch_utils import select_device
    
    
    def parse_args():
        parser = argparse.ArgumentParser(description='Convert .pt file to .wts')
        parser.add_argument('-w', '--weights', required=True,
                            help='Input weights (.pt) file path (required)')
        parser.add_argument(
            '-o', '--output', help='Output (.wts) file path (optional)')
        parser.add_argument(
            '-t', '--type', type=str, default='detect', choices=['detect', 'cls', 'seg'],
            help='determines the model is detection/classification')
        args = parser.parse_args()
        if not os.path.isfile(args.weights):
            raise SystemExit('Invalid input file')
        if not args.output:
            args.output = os.path.splitext(args.weights)[0] + '.wts'
        elif os.path.isdir(args.output):
            args.output = os.path.join(
                args.output,
                os.path.splitext(os.path.basename(args.weights))[0] + '.wts')
        return args.weights, args.output, args.type
    
    
    pt_file, wts_file, m_type = parse_args()
    print(f'Generating .wts for {m_type} model')
    
    # Load model
    print(f'Loading {pt_file}')
    device = select_device('cpu')
    model = torch.load(pt_file, map_location=device)  # Load FP32 weights
    model = model['ema' if model.get('ema') else 'model'].float()
    
    if m_type in ['detect', 'seg']:
        # update anchor_grid info
        anchor_grid = model.model[-1].anchors * model.model[-1].stride[..., None, None]
        # model.model[-1].anchor_grid = anchor_grid
        delattr(model.model[-1], 'anchor_grid')  # model.model[-1] is detect layer
        # The parameters are saved in the OrderDict through the "register_buffer" method, and then saved to the weight.
        model.model[-1].register_buffer("anchor_grid", anchor_grid)
        model.model[-1].register_buffer("strides", model.model[-1].stride)
    
    model.to(device).eval()
    
    print(f'Writing into {wts_file}')
    with open(wts_file, 'w') as f:
        f.write('{}\n'.format(len(model.state_dict().keys())))
        for k, v in model.state_dict().items():
            vr = v.reshape(-1).cpu().numpy()
            f.write('{} {} '.format(k, len(vr)))
            for vv in vr:
                f.write(' ')
                f.write(struct.pack('>f', float(vv)).hex())
            f.write('\n')
  • 然后在你的虚拟环境下运行如下命令:
    python gen_wts.py -w ./runs/train/exp/weights/best.pt -o ./best.wts -t detect
    命令解释:
    • -w:是权重权重存放位置
    • -o:是生成的wts类型权重的名称以及存放路径
    • -t:是指定你的模型类型,detect表示目标检测网络,cls表示分类网络,seg表示分割网络
      运行完毕后即可发现在当前路径下以及生成了一个best.wts文件
  1. 将wts格式权重转换为tensorrt的engine格式
    这里需要使用Github上开源的相关工程文件,使用git clone将工程克隆到任意路径下,需要注意的是接下来的操作都是在Jetson系统上进行的。
    • 下载相关文件,使用命令(方法同上):
      git clone https://github.com/wang-xinyu/tensorrtx
    • 该工程目录如下:
      # ├── tensorrtx
      #     └── yolov5
      #         └── images
      #         └── best.wts # 将上述生成得到的best.wts文件放入此路径
      #         └── plugin # 里面存放的适合yolov5模型结构相关的文件
      #         └── src # 里面是我们需要修改到的模型运行配置文件
      #             └── config.h
      #                 └── weights
      #                     └── best.pt
    • 修改细节如下:
      • config.h:将其中的kNumClass = 80修改为我们模型训练使用的类别数,这里我修改的kNumClass = 2;
    • 然后在yolov5路径下使用如下命令将wts格式权重文件转换为engine格式权重文件
      mkdir build
      cd build
      cmake ..
      make
      sudo ./yolov5_det -s ../best.wts ../best.engine n
      命令解释:
      • 前四行命令:是使用cmake相关指令对相关文件进行编译,以生成可执行文件yolov5_det
      • 最后一行命令:
        • yolov5_det:目标检测的可执行文件,yolov5_cls与yolov5_seg同理;
        • -s:应该是一种模型选择(即选择进行模型转换模型),…/best.wts表示我们前面操作生成的wts格式权重文件,即源文件;…/best.engine是目标文件,即生成转换得到的文件
        • n:是我们yolov5模型的类型,这里由于我选择使用的是yolov5n模型,故为n。这里拓展一下若是yolov5m模型,则是m;yolov5l模型,则是l;其他类型同理即可。
          注意:如果运行失败,请严格对照是否按照上述操作对相关文件进行修改。
    • 之后使用如下命令检验生成的engine文件是否有问题:
      sudo ./yolov5_det -d ../best.engine ../samples
      命令解释:
      • -d:解释同上,是一种模型选择,即进行测试
      • …/best.engine:我们生成的engine格式文件路径
      • …/samples:我们要检测的图片存放路径,这里可自行创建该文件夹,并将自己的图片放入此文件夹
    • 运行完后,会在当前路径下得到检测后的图片,可以自行观察检测模型是否有问题。
    • 相关路径:
      # ├── tensorrtx
      #     └── yolov5
      #         └── images
      #         └── best.wts # 将上述生成得到的best.wts文件放入此路径
      #         └── best.engine # 生成的engine文件
      #         └── samples
      #             └── test1.jpg
      #             └── test2.jpg
      #             └── ...
      #         └── plugin # 里面存放的适合yolov5模型结构相关的文件
      #         └── src # 里面是我们需要修改到的模型运行配置文件
      #             └── config.h
      #                 └── weights
      #                     └── best.pt
      #         └── build # cmake相关语句后得到的文件
      #             └── yolov5_det
      #             └── libmyplugins.so

选择框架——DeepStream

  1. 安装Deepstream5.x
  2. 下载相关源文件
    从Github上克隆他人开源的工程文件,使用命令(方法同上):
    git clone git@github.com:DanaHan/Yolov5-in-Deepstream-5.0.git
    该工程目录如下(这里只给出要修改的文件):
    # ├── Yolov5-in-Deepstream-5.0
    #     └── Deepstream5.0
    #         └── includes
    #         └── nvdsinfer_custom_impl_Yolo
    #             └── nvdsparsebbox_Yolo.cpp
    #             └── Makefile
    #         └── deepstream_app_config_yoloV5.txt
  3. 修改细节:
    • 进入nvdsparsebbox_Yolo.cpp文件,内容如下,细节见注释:
      ...
      #include <cstdio>
      #include "serial/serial.h"
      #include <vector>
      ...
      
      static bool NvDsInferParseYoloV5(
          std::vector<NvDsInferLayerInfo> const& outputLayersInfo,
          NvDsInferNetworkInfo const& networkInfo,
          NvDsInferParseDetectionParams const& detectionParams,
          std::vector<NvDsInferParseObjectInfo>& objectList){
          
          serial::Serial my_serial("/dev/ttyTHS1", 9600, serial::Timeout::simpleTimeout(50)); // 设置串口
          /*if(my_serial.isOpen()){                                                           // 测试串口是否成功打开
              std::cout << "the location:    X:" << std::endl;
          }*/
          std::vector<Detection> res;
      
          nms(res, (float*)(outputLayersInfo[0].buffer), CONF_THRESH, NMS_THRESH);
          //std::cout<<"Nms done sucessfully----"<<std::endl;
          NvDsInferParseObjectInfo globalbbox;                                                // 设置目标检测区域
          globalbbox.classId = 0;
          globalbbox.left    = static_cast<unsigned int>(160);
          globalbbox.top     = static_cast<unsigned int>(120);
          globalbbox.width   = static_cast<unsigned int>(320);
          globalbbox.height  = static_cast<unsigned int>(240);
          globalbbox.detectionConfidence = 0.9;
          objectList.push_back(globalbbox);                                                   // 将目标检测区域当作目标,以在视频中得到显示
          std::string output = "A";                                                           // 设置标志位
          unsigned int x = 100;                                                               // 设置初始目标的中心
          unsigned int y = 100;
          unsigned int x_temp = 0;                                                            // 设置暂存变量
          unsigned int y_temp = 0;
          double confidence_max = 0;                                                          // 按照置信度进行优先级选择
          for(auto& r : res) {
            NvDsInferParseObjectInfo oinfo;        
            oinfo.classId = r.class_id;
                  if(oinfo.classId != 0) continue;
            oinfo.left    = static_cast<unsigned int>(r.bbox[0]-r.bbox[2]*0.5f);
            oinfo.top     = static_cast<unsigned int>(r.bbox[1]-r.bbox[3]*0.5f);
            oinfo.width   = static_cast<unsigned int>(r.bbox[2]);
            oinfo.height  = static_cast<unsigned int>(r.bbox[3]);
            oinfo.detectionConfidence = r.conf;
            x_temp = static_cast<unsigned int>(oinfo.left + oinfo.width*0.5f);                // 计算目标的中心x,y
            y_temp = static_cast<unsigned int>(oinfo.top + oinfo.height*0.5f);
            if(x_temp>160 && x_temp<480 && y_temp>120 && y_temp< 360 && r.conf > confidence_max){  // 判断目标中心是否落入检测区域
                x = x_temp;y = y_temp; confidence_max = r.conf;
            }
            objectList.push_back(oinfo);        
          }
          output += std::to_string(x);                                                        // 将串口要发送的位置信息转换为合适格式
          output += std::to_string(y);
          output += 'B';                                                                      // 设置停止位
          std::vector<uint8_t> data(output.begin(), output.end());                            // 对数据进行编码,以配合串口发送信息
          // std::cout << data.size() << std::endl;
          my_serial.write(data.data(), data.size());                                          // 串口发送坐标信息
          std::cout << "the location:    X:" << x <<",   Y:" << y  << ",   conf:" << confidence_max << ",   link:" << data.data() << std::endl;  // d打印当前串口发送的坐标信息,实际中可以将此行注释掉,因为print操作会影响程序运行速度
          return true;
      }
    • 将上述模型格式转换过程中生成的best.engine与libmyplugins.so文件复制到Deepstream5.0路径下
    • 修改deepstream_app_config_yoloV5.txt文件,修改细节以及具体注释如下:
      ...
      
      [source0]
      enable=1
      #Type - 1=CameraV4L2 2=URI 3=MultiURI
      type=1   # camera
      camera-width=640 # 相机获取视频的宽
      camera-height=480# 相机获取视频的高
      camera-fps-n=30  # 相机获取视频的帧率分子为30
      camera-fps-d=1   # 相机获取视频的帧率分母为1,n与d配合,即30帧
      #camera-v412-dev-node=0
      #uri=file:/opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_1080p_h264.mp4
      #uri=file:/home/nvidia/Documents/5-Materials/Videos/0825.avi
      num-sources=1    # 设置输入源数量,这里是1个
      gpu-id=0         # 设置使用的GPU ID,这里是0号GPU
      # (0): memtype_device   - Memory type Device
      # (1): memtype_pinned   - Memory type Host Pinned
      # (2): memtype_unified  - Memory type Unified
      cudadec-memtype=0  # 设置CUDA解码器使用的内存类型,这里是Device内存
      
      ...
      
      # 这里是配置生成视频流
      [streammux] 
      ...
      width=640
      height=480
      ...
      
      ...
      
      [primary-gie]
      enable=1
      gpu-id=0
      model-engine-file=best.engine # 物体检测模型引擎文件
      labelfile-path=labels.txt     # 标签文件路径,包含类名,这里需要自己创建一个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
      interval=0  # 推理间隔,单位毫秒
      gie-unique-id=1
      nvbuf-memory-type=0
      config-file=config_infer_primary_yoloV5.txt  # 包含更多设置的配置文件
      
      [tracker]
      enable=0
      tracker-width=512
      tracker-height=320
      ll-lib-file=/opt/nvidia/deepstream/deepstream-5.0/lib/libnvds_mot_klt.so  # 根据自己的libnvds_mot_klt.so存放路径进行更改,一般而言不需要修改此项。如果是Deepstream5.1,可能需要修改为/opt/nvidia/deepstream/deepstream-5.1/lib/libnvds_mot_klt.so
      ...
    • 在Deepstream5.0路径下创建一个labels.txt文件,内容如下:
      hole
      解释:一个目标一行,即表示索引和内容
    • 修改Makefile文件内容,细节以及注释如下:
      CUDA_VER?=10.2
      ifeq ($(CUDA_VER),)
        $(error "CUDA_VER is not set")
      endif
      CC:= g++
      NVCC:=/usr/local/cuda-$(CUDA_VER)/bin/nvcc
      
      CFLAGS:= -Wall -std=c++11 -shared -fPIC -Wno-error=deprecated-declarations
      CFLAGS+= -I../includes -I/usr/local/cuda-$(CUDA_VER)/include -I/opt/ros/melodic/include # 新增的,以成功在待编译的cpp文件中引入<serial/serial.h>头文件
      
      LIBS:= -lnvinfer_plugin -lnvinfer -lnvparsers -L/usr/local/cuda-$(CUDA_VER)/lib64 -lcudart -lcublas -lstdc++fs -L/opt/ros/melodic/lib -lserial  # 新增的,链接库文件/opt/ros/melodic/lib/libserial.so,以实现串口通信。
      LFLAGS:= -shared -Wl,--start-group $(LIBS) -Wl,--end-group
      
      INCS:= $(wildcard *.h) 
      SRCFILES:= nvdsinfer_yolo_engine.cpp \
                nvdsparsebbox_Yolo.cpp   \
                trt_utils.cpp              \
                #  yolo.cpp              \  # 注释掉这两行,因为使用不到,并且使用的话会导致无法顺利通过编译。
                #  yoloPlugins.cpp          # 注释掉这两行
      TARGET_LIB:= libnvdsinfer_custom_impl_Yolo.so
      #TARGET_LIB:= libnvdsinfer_custom_impl_Yolo.so
      TARGET_OBJS:= $(SRCFILES:.cpp=.o)
      TARGET_OBJS:= $(TARGET_OBJS:.cu=.o)
      
      all: $(TARGET_LIB)
      
      %.o: %.cpp $(INCS) Makefile
        $(CC) -c -o $@ $(CFLAGS) $<
      
      %.o: %.cu $(INCS) Makefile
        $(NVCC) -c -o $@ --compiler-options '-fPIC' $<
      
      $(TARGET_LIB) : $(TARGET_OBJS)
        $(CC) -o $@  $(TARGET_OBJS) $(LFLAGS)
      
      clean:
        rm -rf $(TARGET_LIB)
      上述Makefile文件的修改具体解释可以参考我的另一篇博客,里面详细记录了相关内容:https://sprli.github.io/2023/05/26/Cmake_Study/

运行及使用

运行使用命令:

sudo LD_PRELOAD=/home/nano/Desktop/Yolov5-in-Deepstream-5.0/Deepstream5.0/libmyplugins.so:/opt/ros/melodic/lib/libserial.so deepstream-app -c /home/nano/Desktop/Yolov5-in-Deepstream-5.0/Deepstream5.0/deepstream_app_config_yoloV5.txt

如何实现程序自启动