赞
踩
项目链接:https://github.com/cabinx/cabin_auv_ws
在目标跟踪时,摄像头提供实时的图片信息,我们需要识别出图片目标,且输出目标在图片中的位置,为后续的控制提供条件。在demo中,我是借助darknet_ros实现这一目标。当然这一模块可以替换成性能更优秀的识别算法。
darknet_ros为yolov3在ros下的一个工具包(https://github.com/leggedrobotics/darknet_ros)。需要对yolov3的使用有所了解(YOLO: Real-Time Object Detection)。例程我就不介绍了,可以在网上搜索。在此主要基于demo测试介绍我个人的使用情况,主要包括摄像头驱动、数据集制作、模型训练、模型部署。
本文介绍使用darknet_ros进行目标识别模型的训练及部署。
一、训练模型
这里我主要参考了博客:darknet-yolov3训练自己的数据集(超详细) - AnswerThe - 博客园,同时结合yolo官网教程。在此默认已配置好GPU及CUDA等环境。训练数据集亦已准备好。
1、编译darknet(启用GPU)
在/darknet_ros/darknet路径下找到Makefile文件并根据需求进行修改:
- GPU=1 #如果使用GPU设置为1,CPU设置为0
- CUDNN=0 #如果使用CUDNN设置为1,否则为0
- OPENCV=1 #如果调用摄像头,还需要设置OPENCV为1,否则为0
- OPENMP=0 #如果使用OPENMP设置为1,否则为0
- DEBUG=0 #如果使用DEBUG设置为1,否则为0
关于ARCH值的设置,可以到NVIDIA官网查询自身显卡的算力(CUDA GPUs | NVIDIA Developer):
如2080TI,则为:
ARCH = -gencode arch=compute_75, code=[sm_75,compute_75]
编译:
make
编译完成后可以选择进行验证,下载训练好的模型:
wget https://pjreddie.com/media/files/yolov3.weights
运行detector:
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
若能识别图片中的物体,则证明darknet安装配置成功。
顺便下载预训练模型权值后边备用。
wget https://pjreddie.com/media/files/darknet53.conv.74
2、配置文件
进入路径darknet/cfg,复制yolov3-voc.cfg、voc.data,并更名为cabin_yolov3.cfg,cabin_data.data。
①新建cabin_name.names文件,每一行填入一类目标。如我的demo只有一类目标,所以cabin_name.names文件为:
sea_cucumber
②再来看cabin_data.data文件:
- classes= 1
- train = /home/pcl-02/darknet_ws/src/darknet_ros/darknet/cabin_data/data/ImageSets/Main/train.txt
- valid = /home/pcl-02/darknet_ws/src/darknet_ros/darknet/cabin_data/data/ImageSets/Main/val.txt
- names = cfg/cabin_name.names
- backup = /home/pcl-02/darknet_ws/src/darknet_ros/darknet/cabin_data/data/weights
train和valid分别为训练集和验证集路径,与上一篇数据集制作的博客相对应;
names为新建的类别名称文件,上文已提及;
buckup为训练出来的模型的存储路径。
③ 最后看cabin_yolov3.cfg文件,我们首先将训练模式打开:
- [net]
- # Testing ### 测试模式
- # batch=1
- # subdivisions=1
- # Training ### 训练模式,每次前向的图片数目 = batch/subdivisions
- batch=64
- subdivisions=16
- width=416 ### 网络的输入宽、高、通道数
- height=416
- channels=3
- momentum=0.9 ### 动量
- decay=0.0005 ### 权重衰减
- angle=0
- saturation = 1.5 ### 饱和度
- exposure = 1.5 ### 曝光度
- hue=.1 ### 色调
- learning_rate=0.001 ### 学习率
- burn_in=1000 ### 学习率控制的参数
- max_batches = 10000 ### 迭代次数
- policy=steps ### 学习率策略
- steps=40000,45000 ### 学习率变动步长
- scales=.1,.1
其它诸如学习率learning_rate,迭代次数max_batches等等网上介绍很多,我就不重复了。
然后搜索关键词yolo,一共有3处含yolo,每处需要修改2个地方。
filters:3*(5+len(classes));
其中:classes: 目标种类,这里以我的工程为例
filters = 18
classes = 1
可修改:random = 1:原来是1,显存小改为0。(是否要多尺度输出。)
如下:
- [convolutional]
- size=1
- stride=1
- pad=1
- filters=18 #############
- activation=linear
-
- [yolo]
- mask = 6,7,8
- anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
- classes=1 ###############
- num=9
- jitter=.3
- ignore_thresh = .5
- truth_thresh = 1
- random=1 ###############
3、开始训练
./darknet detector train cfg/cabin_data.data cfg/cabin_yolov3.cfg darknet53.conv.74 -gpus 0,1
在此,利用到了上文提及的下载好的预训练模型权值darknet53.conv.74。由于我训练时使用了两张显卡,故-gpus 0,1 。
训练时得到的模型将位于cabin_name.names中backup一项设置的路径中。按上文中的配置,模型名为cabin_yolov3.backup。训练过程中实时的权值存储于该模型文件中,当判断已经收敛时,可以终止训练,下一步使用该模型时需要将其拷贝出来并修改其文件名为cabin_yolov3.weights。
若模型因为训练不充分导致训练的效果不佳,可以在不修改参数的前提下基于该模型文件继续训练:
./darknet detector train cfg/cabin_data.data cfg/cabin_yolov3.cfg /home/pcl-02/darknet_ws/src/darknet_ros/darknet/cabin_data/data/weights/cabin_yolov3.backup -gpus 0,1
关于如何判断训练已经收敛了,这里推荐一篇关于yolov3训练时输出log的文章解析:理解 YOLOv3 的训练输出日志信息_Right here, Code is Magic-CSDN博客_yolov3的输出是什么。
如下图所示:
在此关注如下几项:
256: 指示当前训练的迭代次数;
3.784128: 是总体的 Loss(损失);
4.452100 avg: 是平均 Loss, 这个数值应该越低越好, 一般来说, 一旦这个数值趋于稳定不下降时,就可以终止训练了;
0.000009 rate: 代表当前的学习率, 是在.cfg文件中定义的;
4.415291 seconds: 表示当前批次训练花费的总时间;
32768 images:表示到目前为止, 参与训练的图片的总量。
4、测试模型
在调试过程中,我直接用的录制的视频包进行验证,我会在下一部分的部署模型里介绍。但比较简单直接的方法可以用上文编译介绍部分中图片验证的方法。
需要将cabin_yolov3.cfg文件中的测试模式打开。
- [net]
- # Testing
- batch=1
- subdivisions=1
- # Training
- #batch=64
- #subdivisions=16
二、模型部署
基于darknet_ros模型部署主要参照了文章:ROS下实现darknet_ros(YOLO V3)检测_pd很不专业的博客-CSDN博客_ros yolov3。
输入输出如下:
监听topic:/usb_cam/image_raw,格式sensor_msgs::Image,摄像头图片数据流;
发布topic:/darknet_ros/bounding_boxes,格式darknet_ros_msgs::BoundingBoxes,目标在图片中的位置框图坐标。
1、离线调试
完成训练后获得模型cabin_yolov3.weights。上一篇博客里介绍的数据包6.bag未参与数据集制作,即未参与模型训练,可用于离线测试。
①darknet_ros/config/ros.yaml
此处配置监听及发布的topic的名称,需要注意监听的摄像头的数据流的topic。根据之前关于ros下USB摄像头使用的那篇文章,有:
- camera_reading:
- topic: /usb_cam/image_raw
- queue_size: 1
②darknet_ros/config/cabin_data.yaml
此处载入训练好的模型及yolov3的配置文件。此处我将训练介绍部分的darknet下的cabin_yolov3.weights拷贝至/darknet_ros/yolo_network_config/weights;将cabin_yolov3.cfg拷贝至/darknet_ros/yolo_network_config/cfg。然后在darknet_ros/config下新建cabin_data.yaml,如下:
- yolo_model:
-
- config_file:
- name: cabin_yolov3.cfg
- weight_file:
- name: cabin_yolov3.weights
- threshold:
- value: 0.3
- detection_classes:
- names:
- - sea_cucumeber
其中threshold为置信度,大于0.3时识别为目标物。
③darknet_ros/launch/cabin_darknet_ros.launch
拷贝darknet_ros.launch并重命名为cabin_darknet_ros.launch。修改摄像头数据的topic名称/usb_cam/image_raw以及上文提及的配置文件cabin_data.yaml路径。如下:
- <?xml version="1.0" encoding="utf-8"?>
-
- <launch>
- <!-- Console launch prefix -->
- <arg name="launch_prefix" default=""/>
- <arg name="image" default="/usb_cam/image_raw" />
-
- <!-- Config and weights folder. -->
- <arg name="yolo_weights_path" default="$(find darknet_ros)/yolo_network_config/weights"/>
- <arg name="yolo_config_path" default="$(find darknet_ros)/yolo_network_config/cfg"/>
-
- <!-- ROS and network parameter files -->
- <arg name="ros_param_file" default="$(find darknet_ros)/config/ros.yaml"/>
- <arg name="network_param_file" default="$(find darknet_ros)/config/cabin_data.yaml"/>
-
- <!-- Load parameters -->
- <rosparam command="load" ns="darknet_ros" file="$(arg ros_param_file)"/>
- <rosparam command="load" ns="darknet_ros" file="$(arg network_param_file)"/>
-
- <!-- Start darknet and ros wrapper -->
- <node pkg="darknet_ros" type="darknet_ros" name="darknet_ros" output="screen" launch-prefix="$(arg launch_prefix)">
- <param name="weights_path" value="$(arg yolo_weights_path)" />
- <param name="config_path" value="$(arg yolo_config_path)" />
- <remap from="camera/rgb/image_raw" to="$(arg image)" />
- </node>
-
- <!--<node name="republish" type="republish" pkg="image_transport" output="screen" args="compressed in:=/front_camera/image_raw raw out:=/camera/image_raw" /> -->
- </launch>
④运行
分别在终端中启动darknet_ros及播放数据包:
roslaunch darknet_ros cabin_darknet_ros.launch
rosbag play 6.bag
效果如下:
从效果上看,帧率很低;但从输出的yolo的log上看,帧率有60帧(使用的是双2080TI)。
这就关系到前文提及的视频传输的时延。录制数据包时视频由水下的树莓派经电力载波线上传至岸上PC录制,不做处理时时延及丢帧非常明显,显然会对后面的跟踪控制部分造成影响。
2、在线部署
离线调试成功后,这一步就非常简单了,水下摄像头接在树莓派上,岸上pc通过ssh登录到树莓派启动摄像头,此时网络中图片数据发布于/usb_cam/image_raw。
此时就与离线调试一样了,在岸上PC启动darknet_ros即可。
=========================================================================
在这里我还是补充一下darknet_ros的配置文件吧,虽然有点绕,但我先前认为各文件的条理还是很清晰的,文件内容也很简单,稍微注意点就好,所以就不细说。配置文件没做好会出现各种各样的错误。
回到cabin_darknet_ros.launch这个文件。
显然:
- <!-- ROS and network parameter files -->
- <arg name="ros_param_file" default="$(find darknet_ros)/config/ros.yaml"/>
- <arg name="network_param_file" default="$(find darknet_ros)/config/cabin_data.yaml"/>
ros.yaml和cabin_data.yaml就是两个需要先配置好的文件。
我们打开ros.yaml:
subscribers: camera_reading: topic: /usb_cam/image_raw queue_size: 1 actions: camera_reading: name: /darknet_ros/check_for_objects publishers: object_detector: topic: /darknet_ros/found_object queue_size: 1 latch: false bounding_boxes: topic: /darknet_ros/bounding_boxes queue_size: 1 latch: false detection_image: topic: /darknet_ros/detection_image queue_size: 1 latch: true image_view: enable_opencv: true wait_key_delay: 1 enable_console_output: true
很简单,相机的topic,这个需要和系统里的topic相匹配,以及为输出的数据自定义topic名。
我们再回到第二个文件cabin_data.yaml:
- config_file:
- name: cabin_yolov3.cfg
这个是之前我们训练自己网络时的yolo的配置文件名,那这个文件路径在哪呢?在cabin_darknet_ros.launch里。也就是说该路径下要有cabin_yolov3.cfg的配置文件,当然这个路径是可以自由配置的。使用时别忘了将模式改成test。
<arg name="yolo_config_path" default="$(find darknet_ros)/yolo_network_config/cfg"/>
同理,cabin_data.yaml中:
- weight_file:
- name: cabin_yolov3.weights
这个是我们训练好的网络的权重文件名,注意,权重文件必须配合训练该文件的yolo的配置文件使用,对于cabin_yolov3.weights就是cabin_yolov3.cfg。
那么,权重文件的存放路径同理在cabin_darknet_ros.launch中:
<arg name="yolo_weights_path" default="$(find darknet_ros)/yolo_network_config/weights"/>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。