如何在无人机上部署YOLOv4物体检测器?

如何在无人机上部署YOLOv4物体检测器

准备工作

Linux上编译

下载YOLOv4源码,推荐使用Ubuntu 18.04

sudo apt-get install -y git
git clone https://github.com/AlexeyAB/darknet.git

配置Makefile文件中的参数,然后运行make -j8进行编译,具体参数解释如下:

  • GPU=1 使用CUDA和GPU(CUDA默认路径为/usr/local/cuda

  • CUDNN=1使用cuDNN v5-v7加速网络(cuDNN默认路径/usr/local/cudnn

  • CUDNN_HALF=1 使用Tensor Cores(可用GPU为Titan V / Tesla V100 / DGX-2或者更新的)检测速度3x,训练速度2x

  • OPENCV=1 使用OpenCV 4.x/3.x/2.4.x,运行检测视频和摄像机

  • DEBUG=1 编译调试版本

  • OPENMP=1 使用OpenMP利用多CPU加速

  • LIBSO=1 编译darknet.so

  • ZED_CAMERA=1 增加ZED-3D相机的支持(需要先安装好ZED SDK)

    • 运行LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH ./uselib data/coco.names cfg/yolov4.cfg yolov4.weights zed_camera

常见编译问题

/bin/sh: 1: nvcc: not found

首先确保CUDA正确安装,并且在路径/usr/local/cuda下,然后输入如下指令:

echo "PATH=/usr/local/cuda/bin:$PATH" >> ~/.bashrc
source ~/.bashrc

include/darknet.h:46:10: fatal error: cudnn.h: No such file or directory

首先下载cuDNNhttps://developer.nvidia.com/rdp/cudnn-archive,需要根据自己的CUDA版本选择,然后解压,输入指令:

sudo cp -r cudnn-10.1-linux-x64-v7.6.5.32/cuda /usr/local/cudnn

预训练模型

所有模型都是在MS-COCO数据集上训练,模型包括两个文件(cfgweights

R表示在RTX 2070设备上的FPS,V表示在Tesla V100设备上的FPS

百度网盘打包下载,链接:https://pan.baidu.com/s/1QQPB27n18XeRDnhHA2Gxuw,提取码:uill

可以在如下路径找到所有的cfg文件: darknet/cfg/

运行指令介绍

需要将训练好的weights文件放到darknet根目录下,运行如下指令:

  • 检测单张图像
./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights -thresh 0.25
  • 检测给定路径的单张图像(参数最后的路径需要写待检测图像的路径)
./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights -ext_output /home/jario/Pictures/h1.jpg
  • 检测给定路径的单个视频
./darknet detector demo cfg/coco.data cfg/yolov4.cfg yolov4.weights -ext_output test.mp4
  • 检测给定路径的单个视频,并将检测结果保存为视频
./darknet detector demo cfg/coco.data cfg/yolov4.cfg yolov4.weights test.mp4 -out_filename res.avi
  • 利用摄像机实时检测(YOLOv4)
./darknet detector demo cfg/coco.data cfg/yolov4.cfg yolov4.weights -c 0
  • 利用摄像机实时检测(YOLOv3-Tiny)
./darknet detector demo cfg/coco.data cfg/yolov3-tiny.cfg yolov3-tiny.weights -c 0
  • 在GPU1上检测给定路径的单个视频
./darknet detector demo cfg/coco.data cfg/yolov3-tiny.cfg yolov3-tiny.weights -i 1 test.mp4
  • 检测列表data/train.txt中图像,并将结果保存在result.json
./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights -ext_output -dont_show -out result.json < data/train.txt
  • 检测列表data/train.txt中图像,并将结果保存在result.txt
./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights -dont_show -ext_output < data/train.txt > result.txt

如何构建自己的训练数据

下载数据集标注工具,下载地址:(https://pan.baidu.com/s/1EE52cDStjIxsRgM_a9pWQQ) (password: 4b2q) 或者 Spire Web.

数据集管理软件github地址:https://github.com/jario-jin/spire-image-manager

打开标注软件 SpireImageTools_x.x.x.exe

首先点击Tools->Setting...,填写一个 save path (所有的标注文件都会存储在这个文件夹中)

image

如果采集的数据集是视频 (如果采集的是图像,则调过这一步骤),点击 Input->Video, 选择要标注的视频。

image

然后,点击Tools->Video to Image

image

点击OK 后,等待完成,结果会存储在

image

打开需要标注的图像

Input->Image Dir, 找到需要标注的图像所在文件夹 Ctrl+A,全选,打开

image

点击,Tools->Annotate Image->Instance Label,开始标注图像

image

在 label 中填写待标注目标名称,然后将对话框拖到一边
在主窗口中开始标注,鼠标滚轮放大缩小图像,按住左键移动可视图像区域不断点击左键将目标框包围,
使用 Yolo 训练时,点击 2 个点即可

image

标注时,如果点错,按鼠标右键可以取消
标注完成后,如果不满意,可以点击绿色边框(边框会变红,如下图所示),按Delete 删除

image

将标注输出为 Yolo 格式,准备训练

在标注完成之后,按下 Ctrl+O

image

点击确定后

image

然后将下面 4 个文件取出用于 Yolo 训练

image

开始训练YOLO

使用YOLOv4和YOLOv3:

  1. 针对选择的模型,下载预训练权重:

  1. cfg/yolov4-custom.cfg拷贝一份,重命名为yolov4-obj.cfgobj可以是自定义名称)

  2. darknet/data路径下创建obj.names,其中每一行是一个目标类别名称

    • 将数据集标注得到的文件Yolo_categories.names重命名为obj.names,并放到darknet/data
  3. darknet/data路径下创建obj.data

  • 教程 darknet 路径为 /home/user/darknet,本文以此为例,请根据自己的路径进行修改。在 /home/user/darknet/cfg/ 文件夹下新建一个文件,名字叫 obj.data 在里面写入:

    classes = 1
    train = /home/user/darknet/data/coco/Yolo_20180908_234114.txt
    valid = /home/user/darknet/data/coco/Yolo_20180908_234114.txt
    names = data/obj.names
    backup = backup
    eval = coco
    
  • 注意:classes 为类别数量,对于单类检测问题,写 1

  1. 将图像文件(.jpg)与标注文件放入到如下路径darknet\data\coco\路径下

    • scaled_images里的图像拷贝到 /home/user/darknet/data/coco/images/train
    • Yolo_labels里的标注文件拷贝到/home/user/darknet/data/coco/images/train
    • Yolo_20180908_234114.txt拷贝到/home/user/darknet/data/coco
  2. 开始训练

    • 训练指令:./darknet detector train data/obj.data cfg/yolo-obj.cfg yolov4.conv.137
      • (对于最新100次迭代的最新权重yolo-obj_last.weights会保存在darknet\backup\
      • (对于每1000次迭代的权重yolo-obj_xxxx.weights会保存在darknet\backup\
      • (关闭Loss的显示窗口./darknet detector train data/obj.data cfg/yolo-obj.cfg yolov4.conv.137 -dont_show
      • (通过浏览器查看训练过程./darknet detector train data/obj.data yolo-obj.cfg yolov4.conv.137 -dont_show -mjpeg_port 8090 -map,然后打开Chrome浏览器,输入http://ip-address:8090
      • (如果需要在训练中计算mAP,每4期计算一次,需要在obj.data文件中设置valid=valid.txt,运行:./darknet detector train data/obj.data yolo-obj.cfg yolov4.conv.137 -map
  3. 训练结束,结果保存在darknet\backup\yolo-obj_final.weights

    • 如果训练中断,可以选择一个保存的权重继续训练,使用./darknet detector train data/obj.data yolo-obj.cfg backup\yolo-obj_2000.weights

注意:在训练中,如果avg(loss)出现nan,则训练出了问题,如果是其他字段出现nan,这种情况是正常的。
注意:如果需要改变cfg文件中的width=height=,新的数字需要被32整除。
注意:训练完成后,检测指令为:./darknet detector test data/obj.data yolo-obj.cfg yolo-obj_8000.weights
注意:如果出现Out of memory,需要修改cfg文件中的subdivisions=163264

训练YOLOv3-Tiny

训练YOLOv3-Tiny与选了YOLOv4、YOLOv3基本相同,主要有以下小区别:

  1. 下载yolov3-tiny预训练权重,运行命令./darknet partial cfg/yolov3-tiny.cfg yolov3-tiny.weights yolov3-tiny.conv.15 15
  2. 新建自定义cfg文件yolov3-tiny-obj.cfg(可以复制cfg/yolov3-tiny.cfgyolov3-tiny-obj.cfg
  3. 运行训练命令:./darknet detector train data/obj.data yolov3-tiny-obj.cfg yolov3-tiny.conv.15

多GPU训练

  1. 首先在1块GPU上训练1000次./darknet detector train cfg/coco.data cfg/yolov4.cfg yolov4.conv.137
  2. 停止训练,使用权重darknet/backup/yolov4_1000.weights,在多块GPU上训练,运行./darknet detector train cfg/coco.data cfg/yolov4.cfg /backup/yolov4_1000.weights -gpus 0,1,2,3

注意:如果出现nan,应该降低学习率,如4块GPUlearning_rate=0.00065(learning_rate=0.00261/GPUs),还应该增加cfg文件中的burn_in=为原先的4x,如burn_in=4000

训练常见程序问题

注意:如果出现如下错误

image

需要修改源码/home/user/darknet/src/data.c
将如下代码

list *get_paths(char *filename)
{
  char *path;
  FILE *file = fopen(filename, "r");
  if(!file)
    file_error(filename);
  list *lines = make_list();
  while((path=fgetl(file))) {
    list_insert(lines, path);
  }
  fclose(file);
  return lines;
}

修改为:

void ltrim(char *s)
{
  char *p; p = s;
  while (*p == ' ' || *p == '\t' || *p == '\r') { p++; } strcpy(s,p);
}

void rtrim(char *s)
{
  int i;
  i = strlen(s) - 1;
  while ((s[i] == ' ' || s[i] == '\t' || s[i] == '\r') && i >= 0 ) { i--; } s[i+1] = '\0';
}

void _trim(char *s)
{
  ltrim(s);
  rtrim(s);
}

list *get_paths(char *filename)
{
  char *path;
  FILE *file = fopen(filename, "r"); if(!file) file_error(filename); list *lines = make_list(); while((path=fgetl(file))) {
    _trim(path); list_insert(lines, path);
  }
  fclose(file); return lines;
}

保存,make -j8重新编译
下面为正常训练时画面

image

何时应该停止训练

通常情况下,为每个类别迭代2000次是足够的,且总的迭代次数不能低于4000次。但是如果想要更加精确的停止时间,可以参考以下说明:

  1. 在训练过程中,你会看到一系列训练误差,当0.XXXXXXX avg这个参数不再下降时,就该停止训练了

Region Avg IOU: 0.798363, Class: 0.893232, Obj: 0.700808, No Obj: 0.004567, Avg Recall: 1.000000, count: 8 Region Avg IOU: 0.800677, Class: 0.892181, Obj: 0.701590, No Obj: 0.004574, Avg Recall: 1.000000, count: 8
</br>
9002: 0.211667, 0.60730 avg, 0.001000 rate, 3.868000 seconds, 576128 images Loaded: 0.000000 seconds

  • 9002 - 迭代数量(batch数量)
  • 0.60730 avg - 平均损失(误差),越低越好

如果发现0.XXXXXXX avg在很多次迭代后都不再降低,则是时候该停止训练了。最终的平均损失从0.05(对于小模型和简单训练数据)到3.0(对于大模型和复杂训练数据)不等。

  1. 当训练停止之后,可以从darknet\backup中取出最新保存的训练权重.weights,并选择它们中检测效果最好的

例如,当训练9000次停止后,效果最好的模型可能是之前保存权重中的一个(7000,8000,9000),这是因为过拟合(Overfiting)现象。过拟合的表现可以解释为,在训练图像上检测效果很好,但是在其他图像上效果不佳,这时候就该尽早停止训练(早停点)。

image

2.1. 首先,你需要在obj.data中指定验证数据集valid=valid.txt,如果你没有准备验证数据集,可以简单的复制data\train.txtdata\valid.txt

2.2. 如果你在迭代9000次之后停止训练,验证之前的模型权重可以使用如下命令:

* ./darknet detector map data/obj.data cfg/yolo-obj.cfg backup\yolo-obj_7000.weights
* ./darknet detector map data/obj.data cfg/yolo-obj.cfg backup\yolo-obj_8000.weights
* ./darknet detector map data/obj.data cfg/yolo-obj.cfg backup\yolo-obj_9000.weights

然后对比每个权重(7000,8000,9000)最后一行输出,选择mAP(mean average precision)最高权重,或者对比IoU(intersect over union)进行选择。例如,yolo-obj_8000.weights的mAP最高,则使用这个权重。

也可以在训练时加上-map参数:

./darknet detector train data/obj.data cfg/yolo-obj.cfg yolov4.conv.137 -map

结果如下图所示,mAP每4期(Epoch)通过obj.data中设置的验证集valid=valid.txt上计算一次(1期=train_txt中图像数量 / batch 次迭代)。

image

运行训练好的模型,进行目标检测,执行:

./darknet detector test data/obj.data cfg/yolo-obj.cfg yolo-obj_8000.weights

如何提升检测效果

训练之前提升检测效果的技巧

  • 设置.cfg文件中random=1,可以使用多分辨率输入增加检测效果:link

  • .cfg文件中增加网络的输入分辨率(设置任意可以被32整除的数字,如,height=608width=608),可以增加精度

  • 检查图像每个目标是否都被标记,图像中的所有目标都必须被正确标记,推荐使用数据管理工具检查:spire-image-manager

  • Loss很大,mAP很低,是不是训练错了?在训练中使用-show_imgs参数,能够可视化目标框真值,检查数据集是否出了问题。

  • 对于每一个你要检测的物体,在训练数据集中至少需要有一个实例与之相似,包括:形状、物体侧面、相对大小、旋转角度、倾斜方位角、光照等。因此,你的训练数据集需要包含具有不同对象属性的图像:比例、旋转、光照、不同侧面、不同背景等。建议对每一类物体收集2000张不同图像,并迭代训练2000类别数量*次。

  • 推荐在训练数据集中包含带有不希望检测的非标记目标的图像。负样本图像不需要方框标记(空.txt文件),越多越好。

  • 标注目标的最佳方式是:仅标注物体的可见部分,或标注物体的可见和重叠部分,或标注比整个物体稍多一点的部分(有一点间隙),标注你想让检测器检测的部分。

  • 如果单幅图像中的物体很多,需要在[yolo]层或[region]层中修改参数max=200或者更高(全局最大目标检测数量为0,0615234375*(width*height))。

  • 如果想要检测小目标(图像被缩放到416\times 416后,小于16\times 16的目标)

  • 如果想要同时检测大目标与小目标,可以使用修改模型:

  • 如果你训练的数据类别需要区分左右目标(如检测左右手,交通信号中的左右方向),则不能使用左右翻转图像增强,在cfg文件中设置flip=0: https://github.com/AlexeyAB/darknet/blob/3d2d0a7c98dbc8923d9ff705b81ff4f7940ea6ff/cfg/yolov3.cfg#L17

  • 一般规则 - 您的训练数据集应包含待检测目标的相对大小的集合:

    • train_network_width * train_obj_width / train_image_width ~= detection_network_width * detection_obj_width / detection_image_width
    • train_network_height * train_obj_height / train_image_height ~= detection_network_height * detection_obj_height / detection_image_height

    也就是,对于测试数据集中的每个物体,训练数据集中必须至少有一个具有相同类与大约相同相对大小的物体。如果训练数据中仅有占图像面积80-90%的物体,则训练后的网络不能够检测占图像面积1-10%的物体。

  • 如果想加速训练(损失检测精度),可以在cfg文件layer-136中设置参数stopbackward=1

  • 注意物体的模型、侧面、光照、尺度、方位角等属性,从神经网络的内部角度来看,这些是不同的物体。因此,你想检测的物体越多,就应该使用越复杂的网络模型。

  • 如果想要外包矩形框更加精确,可以在[yolo]层中增加3个参数:ignore_thresh=.9 iou_normalizer=0.5 iou_loss=giou,这会增加mAP@0.9,同时降低mAP@0.5。

  • 如果你比较熟悉检测网络了,可以重新计算自定义数据集的锚框(Anchor):./darknet detector calc_anchors data/obj.data -num_of_clusters 9 -width 416 -height 416,然后设置cfg文件中3个[yolo]层9个锚框。同时需要改变每个[yolo]层中的锚框索引mask=,第一层有大于60\times 60的锚框,第二层有大于30\times 30的锚框,第三层相同。也需要改变每个[yolo]层之前的filters=(classes + 5)*<number of mask>。如果许多计算出的锚框不适合在适当的层下 - 那么就尝试使用默认锚框。

训练之后提升检测效果的技巧

  • 增加cfg文件中网络输入的分辨率,如,height=608width=608,或height=832width=832,这样可以检测更小的目标。

TX2上的准备工作

使用JetPack为TX2安装CUDA与cuDNN

image
  • 进入 sdkmanager-[version].[build].deb 所在的路径,其中version和build代表相应各自的编号,安装Debian包:
sudo apt install ./sdkmanager-[version].[build].deb
  • 安装好之后,在Terminal中输入
sdkmanager
  • 使用NVIDIA账号登录

  • 选择开发环境

    • 在 Product Category 中选择 Jetson.
    • 在 Hardware Configuration 中选择 target hardware(Jetson TX2),勾掉 host machine
    • 在 Target Operating System 中选择 JetPack 的版本.
    • 点击CONTINUE进入下一步
image
  • 检查下载组件(如果仅安装CUDA和cuDNN,则只勾选红圈内的选项)、选择存储路径以及接收条款
image
  • 保证Host计算机与TX2在同一局域网内,输入TX2的IP地址就可以安装

部署Darknet-ROS

  • 下载darknet_ros源码
cd ~
cd catkin_ws/src
git clone --recursive https://github.com/leggedrobotics/darknet_ros.git
cd ../
  • 编译
catkin_make -DCMAKE_BUILD_TYPE=Release
  • 将训练好的cfg和weights加载到darknet_ros中

/home/user/darknet/cfg/yolov3-tiny.cfg/home/user/darknet/backup中刚刚训练好的参数
分别拷贝到/home/user/catkin_ws/src/darknet_ros/darknet_ros/yolo_network_config中的cfgweights两个文件夹中
/home/user/catkin_ws/src/darknet_ros/darknet_ros/config文件夹中新建yolov3-tiny-obj.yaml

里面写入

yolo_model:
  config_file:
    name: yolov3-tiny-obj.cfg
  weight_file:
    name: yolov3-tiny-obj.weights
  threshold:
    value: 0.3
  detection_classes:
    names:
      - drone

注意,在yolov3-tiny-obj.yaml文件中,需要指定刚才拷贝的cfgweights文件以及names为自己训练的类别

/home/user/catkin_ws/src/darknet_ros/darknet_ros/launch文件夹中,复制一份darknet_ros.launch,重命名为obj_det.launch
修改里面的

<rosparam command="load" ns="darknet_ros" file="$(find darknet_ros)/config/yolov2-tiny.yaml"/>

<rosparam command="load" ns="darknet_ros" file="$(find darknet_ros)/config/yolov3-tiny-obj.yaml"/>

注意:这正式刚才编写的yaml文件

运行启动检测节点

roslaunch darknet_ros obj_det.launch

注意:进行检测,需要先打开一个ros_web_cam节点,以提供摄像头数据

最后,给一张YOLOv4检测结果的样张吧

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335