工控机 ubuntu16.04 yolov5 cpu版本安装
- 背景
-
- 环境安装
- 结果
- 安装过程中yolov5报错:
- 配合pyrealsense2 做检测
- 通过二维点获取三维坐标
- rospy发送msg
背景
项目有需要用到目标检测,所以想到yolo,之前跑过yolov3,现想yolov5搭配intel realsense D435i来做开发。选cpu版本是因为工控机没有独显。
环境安装
ubuntu16.04
默认是python2.7和python3.5,但是在安装yolov5时候需要matplotlib>=3.2.2,而这是需要python3.8才能安装的。好久没碰过这些老问题啦,不知道前我还傻傻的以为matplotlib安装了python3.6就行,但是python3.6更新不到3.2.2以上,然后还卸载原来的python3.5,差点导致系统崩溃。所以我还是下载anaconda3,在虚拟环境里面使用python3.8,安装yolov5所需要的库。
-
anaconda3的安装和yolov5安装教程看这位博主
-
anaconda默认环境python3.7里安装lableImg:安装教程
-
将xml格式的标签文件转换为txt文件
-
训练自己的模型
结果
安装过程中yolov5报错:
- Can‘t get attribute ‘SPPF‘ on <module ‘models.common‘ from ‘–yolov5-5.0\models\common.py
解决方法:解决方案:
去V6版本里面的model/common.py里面去找到这个SPPF的类,把它拷过来到你的这个V5的model/common.py里面,这样你的代码就也有这个类了,之后在common.py中引入warnings包就可以了。
2. python切换默认版本看这篇文章
配合pyrealsense2 做检测
安装pyrealsense2
pip install --user pyrealsense2
新建realsense_detect.py,对识别框的中心点附近判断该点深度,代码如下
import argparse
import os
import shutil
import time
from pathlib import Pathimport cv2
import torch
import torch.backends.cudnn as cudnn
from numpy import random
import numpy as np
import pyrealsense2 as rsfrom models.experimental import attempt_load
from utils.general import (check_img_size, non_max_suppression, apply_classifier, scale_coords,xyxy2xywh, strip_optimizer, set_logging)
from utils.torch_utils import select_device, load_classifier, time_synchronized
from utils.plots import plot_one_box
from utils.datasets import letterboxdef detect(save_img=False):out, source, weights, view_img, save_txt, imgsz = \opt.save_dir, opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_sizewebcam = source == '0' or source.startswith(('rtsp://', 'rtmp://', 'http://')) or source.endswith('.txt')# Initializeset_logging()device = select_device(opt.device)if os.path.exists(out): # output dirshutil.rmtree(out) # delete diros.makedirs(out) # make new dirhalf = device.type != 'cpu' # half precision only supported on CUDA# Load modelmodel = attempt_load(weights, map_location=device) # load FP32 modelimgsz = check_img_size(imgsz, s=model.stride.max()) # check img_sizeif half:model.half() # to FP16# Set Dataloadervid_path, vid_writer = None, Noneview_img = Truecudnn.benchmark = True # set True to speed up constant image size inference#dataset = LoadStreams(source, img_size=imgsz)# Get names and colorsnames = model.module.names if hasattr(model, 'module') else model.namescolors = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(names))]# Run inferencet0 = time.time()img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img_ = model(img.half() if half else img) if device.type != 'cpu' else None # run oncepipeline = rs.pipeline()# 创建 config 对象:config = rs.config()# config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 60)config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 60)# Start streamingpipeline.start(config)align_to_color = rs.align(rs.stream.color)while True:start = time.time()# Wait for a coherent pair of frames(一对连贯的帧): depth and colorframes = pipeline.wait_for_frames()frames = align_to_color.process(frames)# depth_frame = frames.get_depth_frame()depth_frame = frames.get_depth_frame()color_frame = frames.get_color_frame()color_image = np.asanyarray(color_frame.get_data())depth_image = np.asanyarray(depth_frame.get_data())mask = np.zeros([color_image.shape[0], color_image.shape[1]], dtype=np.uint8)mask[0:480, 320:640] = 255sources = [source]imgs = [None]path = sourcesimgs[0] = color_imageim0s = imgs.copy()img = [letterbox(x, new_shape=imgsz)[0] for x in im0s]img = np.stack(img, 0)img = img[:, :, :, ::-1].transpose(0, 3, 1, 2) # BGR to RGB, to 3x416x416, uint8 to float32img = np.ascontiguousarray(img, dtype=np.float16 if half else np.float32)img /= 255.0 # 0 - 255 to 0.0 - 1.0# Get detectionsimg = torch.from_numpy(img).to(device)if img.ndimension() == 3:img = img.unsqueeze(0)t1 = time_synchronized()pred = model(img, augment=opt.augment)[0]# Apply NMSpred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms)t2 = time_synchronized()for i, det in enumerate(pred): # detections per imagep, s, im0 = path[i], '%g: ' % i, im0s[i].copy()s += '%gx%g ' % img.shape[2:] # print stringgn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwhif det is not None and len(det):# Rescale boxes from img_size to im0 sizedet[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()# Print resultsfor c in det[:, -1].unique():n = (det[:, -1] == c).sum() # detections per classs += '%g %ss, ' % (n, names[int(c)]) # add to string# Write resultsfor *xyxy, conf, cls in reversed(det):xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywhline = (cls, conf, *xywh) if opt.save_conf else (cls, *xywh) # label formatdistance_list = []mid_pos = [int((int(xyxy[0]) + int(xyxy[2])) / 2), int((int(xyxy[1]) + int(xyxy[3])) / 2)] # 确定索引深度的中心像素位置左上角和右下角相加在/2min_val = min(abs(int(xyxy[2]) - int(xyxy[0])), abs(int(xyxy[3]) - int(xyxy[1]))) # 确定深度搜索范围# print(box,)randnum = 40for i in range(randnum):bias = random.randint(-min_val // 4, min_val // 4)dist = depth_frame.get_distance(int(mid_pos[0] + bias), int(mid_pos[1] + bias))# print(int(mid_pos[1] + bias), int(mid_pos[0] + bias))if dist:distance_list.append(dist)distance_list = np.array(distance_list)distance_list = np.sort(distance_list)[randnum // 2 - randnum // 4:randnum // 2 + randnum // 4] # 冒泡排序+中值滤波label = '%s %.2f%s' % (names[int(cls)], np.mean(distance_list), 'm')plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)# Print time (inference + NMS)print('%sDone. (%.3fs)' % (s, t2 - t1))# Stream resultsif view_img:cv2.imshow(p, im0)if cv2.waitKey(1) == ord('q'): # q to quitraise StopIterationprint('Done. (%.3fs)' % (time.time() - t0))if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--weights', nargs='+', type=str, default='runs/train/exp8/weights/best.pt', help='model.pt path(s)')parser.add_argument('--source', type=str, default='inference/images', help='source') # file/folder, 0 for webcamparser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--view-img', action='store_true', help='display results')parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')parser.add_argument('--save-dir', type=str, default='inference/output', help='directory to save results')parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')parser.add_argument('--augment', action='store_true', help='augmented inference')parser.add_argument('--update', action='store_true', help='update all models')opt = parser.parse_args()print(opt)with torch.no_grad(): # 一个上下文管理器,被该语句wrap起来的部分将不会track梯度detect()
通过二维点获取三维坐标
参考这位博主的几篇D435i的文章
然后把yolov5原来的detect.py简单修改一下,在对目标框中心点附近40个点求均值深度大于某个值的基础上,再来获取物体框中心点的深度值,话题publish出去并且给目标框的中心点上色。
if dis_mean > 1.0e-8:im0, xyz = get_depth_xyz(int(mid_pos[0]), int(mid_pos[1]))TargetPointData_msg.class_name = names[int(cls)]TargetPointData_msg.precision = float(conf)TargetPointData_msg.x = xyz[0]TargetPointData_msg.y = xyz[1]TargetPointData_msg.z = xyz[2]# result = str(xyz[0]) + "," + str(xyz[1]) + "," + str(xyz[2])pub.publish(TargetPointData_msg)
rospy发送msg
之前迷惑的是,roscore是python2.7启动的,但是yolov5是conda里面的python3.8运行的,怎么办,如何让conda的python3.8支持roscore?而且rospy的msg怎么写?网上查找资料后我简单讲解一下如何解决,不知道思路是否正确。
以下引用摘抄自:https://blog.csdn.net/xiaoxiaopo55/article/details/109694241
Ubuntu16.04 安装了ROS Kinetic之后Python3不能import cv2
python中是通过cv2.so调用opencv的库的:
Python调用opencv的原理是:opencv编译出共享库文件,python把这个共享库文件作为一个模块加载并使用。通俗点就是,编译opencv的时候开启python接口选项,编译好了会产生cv2.so(linux下)或者cv2.pyd(windows下)这个共享库文件,python代码中import这个cv2就可以用了。为了能正确import它,往往需要把cv2.so放在python找包能找到的路径下,或者修改PYTHONPATH环境变量让它包含cv2.so所在路径。
可以看出这个问题是由ROS添加/opt/ros/kinetic/lib/python2.7/dist-packages到python路径引起的。”
所以我们需要在import cv2 前移除‘/opt/ros/kinetic/lib/python2.7/dist-packages’,让python3.8找到cv2,找完后因为我们还要import rospy,所以我们还要把‘/opt/ros/kinetic/lib/python2.7/dist-packages’路径添加回来
分两步:第一步,头指定python
#! /usr/bin/env python3
第二步:暂时移除路径和添加路径
import sys
sys.path.remove('/opt/ros/kinetic/lib/python2.7/dist-packages') # ROS安装之后的~/.bashrc里有setup.bash,导致ROS添加/opt/ros/kinetic/lib/python2.7/dist-packages到python路径。导致无法找到cv2.so
import cv2
sys.path.append('/opt/ros/kinetic/lib/python2.7/dist-packages')
详情看如下代码
#! /usr/bin/env python3
import argparse
import os
import shutil
import time
from pathlib import Pathimport sys
sys.path.remove('/opt/ros/kinetic/lib/python2.7/dist-packages') # ROS安装之后的~/.bashrc里有setup.bash,导致ROS添加了/opt/ros/kinetic/lib/python2.7/dist-packages到python路径。导致无法找到cv2.so
import cv2
sys.path.append('/opt/ros/kinetic/lib/python2.7/dist-packages')
import torch
import torch.backends.cudnn as cudnn
from numpy import random
import numpy as np
import pyrealsense2 as rsfrom models.experimental import attempt_load
from utils.general import (check_img_size, non_max_suppression, apply_classifier, scale_coords,xyxy2xywh, strip_optimizer, set_logging)
from utils.torch_utils import select_device, load_classifier, time_synchronized
from utils.plots import plot_one_box
from utils.datasets import letterboximport rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
from std_msgs.msg import String
from truck_ros.msg import TargetPointData
pub = rospy.Publisher('targePoint_xyz', TargetPointData, queue_size=10)TargetPointData_msg = TargetPointData()
那python如何调用msg?因为之前没用过rospy,所以有点蒙,因为我不想创建rospy功能包,所以还是老样子,只是camkelist.txt添加msg,修改下package.xml,通过catkin_make去编译,把msg这个包加到ros包里面了,然后python直接import这个包就能找到msg了。(至于rospy创建功能包,可以看这位博主,Python实现ROS节点可以看这位博主)
cmakelist.txt
cmake_minimum_required(VERSION 3.0.2)
project(truck_ros)find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)## Generate messages in the 'msg' folder
add_message_files(FILESTargetPointData.msg
)## Generate added messages and services with any dependencies listed here
generate_messages(DEPENDENCIESstd_msgs
)catkin_package(
# INCLUDE_DIRS include
# LIBRARIES truck_rosCATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)include_directories(
# include${
catkin_INCLUDE_DIRS}
)
package.xml
<?xml version="1.0"?>
<package format="2"><name>truck_ros</name><version>0.0.0</version><description>The truck_ros package</description><!-- One maintainer tag required, multiple allowed, one person per tag --><!-- Example: --><!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> --><maintainer email="icr@todo.todo">icr</maintainer><!-- One license tag required, multiple allowed, one license per tag --><!-- Commonly used license strings: --><!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 --><license>TODO</license><!-- Url tags are optional, but multiple are allowed, one per tag --><!-- Optional attribute type can be: website, bugtracker, or repository --><!-- Example: --><!-- <url type="website">http://wiki.ros.org/truck_ros</url> --><!-- Author tags are optional, multiple are allowed, one per tag --><!-- Authors do not have to be maintainers, but could be --><!-- Example: --><!-- <author email="jane.doe@example.com">Jane Doe</author> --><!-- The *depend tags are used to specify dependencies --><!-- Dependencies can be catkin packages or system dependencies --><!-- Examples: --><!-- Use depend as a shortcut for packages that are both build and exec dependencies --><!-- <depend>roscpp</depend> --><!-- Note that this is equivalent to the following: --><!-- <build_depend>roscpp</build_depend> --><!-- <exec_depend>roscpp</exec_depend> --><!-- Use build_depend for packages you need at compile time: --><!-- <build_depend>message_generation</build_depend> --><!-- Use build_export_depend for packages you need in order to build against this package: --><!-- <build_export_depend>message_generation</build_export_depend> --><!-- Use buildtool_depend for build tool packages: --><!-- <buildtool_depend>catkin</buildtool_depend> --><!-- Use exec_depend for packages you need at runtime: --><!-- <exec_depend>message_runtime</exec_depend> --><!-- Use test_depend for packages you need only for testing: --><!-- <test_depend>gtest</test_depend> --><!-- Use doc_depend for packages you need only for building documentation: --><!-- <doc_depend>doxygen</doc_depend> --><buildtool_depend>catkin</buildtool_depend><build_depend>roscpp</build_depend><build_depend>rospy</build_depend><build_depend>std_msgs</build_depend><build_export_depend>roscpp</build_export_depend><build_export_depend>rospy</build_export_depend><build_export_depend>std_msgs</build_export_depend><exec_depend>roscpp</exec_depend><exec_depend>rospy</exec_depend><exec_depend>std_msgs</exec_depend><build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend><!-- The export tag contains other, unspecified, tags --><export><!-- Other tools can request additional information be placed here --></export>
</package>