Jetson Nano之ROS入门 -- YOLO目标检测与定位

news2024/9/19 10:35:15

文章目录

  • 前言
  • 一、yolo板端部署推理
  • 二、目标深度测距
  • 三、目标方位解算与导航点设定
    • 1、相机成像原理
    • 2、Python实现目标定位
  • 总结


前言

在这里插入图片描述

Darknet_ros是一个基于ROS(机器人操作系统)的开源深度学习框架,它使用YOLO算法进行目标检测和识别。YOLO算法是一种实时物体检测算法,它能够在单个前向传递中同时检测图像中的多个目标。

Darknet_ros将YOLO算法集成到ROS中,使得机器人可以实时地检测和识别周围环境中的物体。它提供了一些ROS节点和服务,可以在ROS系统中轻松使用YOLO算法进行目标检测和识别。同时,它还提供了一些示例程序,帮助用户快速了解如何在ROS中使用深度学习算法进行机器人视觉任务。

Darknet_ros的优点是速度快、准确度高,可以在实时应用中使用。它还可以在不同的机器人项目中使用,例如无人机、机器人车辆和机器人臂等。


一、yolo板端部署推理

首先确认CUDA,OpenCV,cuDNN已安装,可以用命令查看CUDA是否安装

ls -l /usr/local | grep cuda

查看opencv版本

opencv_version

接着从gitee上克隆darknet_ros源码下来

git clone https://gitee.com/bluesky_ryan/darknet_ros.git

按照如下指引下载权重文件,将下载的weights权重文件放在
darknet_ros/darknet_ros/yolo_network_config/weights

cd catkin_workspace/src/darknet_ros/darknet_ros/yolo_network_config/weights/

COCO data set (Yolo v2):
  wget http://pjreddie.com/media/files/yolov2.weights
  wget http://pjreddie.com/media/files/yolov2-tiny.weights

VOC data set (Yolo v2):
  wget http://pjreddie.com/media/files/yolov2-voc.weights
  wget http://pjreddie.com/media/files/yolov2-tiny-voc.weights

Yolo v3:
  wget http://pjreddie.com/media/files/yolov3.weights
  wget http://pjreddie.com/media/files/yolov3-voc.weights

使用jetson nano自带的opencv4.x编译会报错,需要安装opencv3.4.2版本,配置opencv cmake桥接目录

sudo vim /opt/ros/melodic/share/cv_bridge/cmake/cv_bridgeConfig.cmake 

在这里插入图片描述

接着cd到工作空间下,编译darknet_ros功能包

catkin_make -DCATKIN_WHITELIST_PACKAGES="darknet_ros"

配置launch文件图像输入的topic,可以打开摄像头节点后用rostopic命令查看

rostopic list

我的RGB图像发布的话题是/camera/rgb/image_raw,接着配置yolo网络参数,我用的是yolov2-tiny权重文件,检查一下ros.yaml文件也要配置成/camera/rgb/image_raw
在这里插入图片描述
打开终端,source一下工作空间下ros的环境变量

source /catkin_ws/devel/setup.bash

首先启动相机节点,需要有/camera/rgb/image_raw的话题输出,再运行darknet_ros 节点

roslaunch darknet_ros darknet_ros.launch

接着会弹出一个窗口,展示识别到的物体
在这里插入图片描述

二、目标深度测距

目标的深度测距的实现我们可以结合darknet_ros功能包,我这里用的是RGBD相机,型号是Astra_Pro,深度相机节点在启动后会输出深度图的话题

rostopic list

/camera/depth/image_rect_raw

查看一下darknet_ros功能包的msg消息文件,下面是BoundingBoxes.msg消息文件

Header header
Header image_header
BoundingBox[] bounding_boxes

下面是BoundingBox.msg消息文件

float64 probability
int64 xmin
int64 ymin
int64 xmax
int64 ymax
int16 id
string Class

用rostopic info命令可以看到/darknet_ros/bounding_boxes话题的具体内容

rostopic info /darknet_ros/bounding_boxes

Type: darknet_ros_msgs/BoundingBoxes

Publishers: 
 * /darknet_ros (http://localhost:43545/)

这个话题就是我们需要的目标检测框的信息,将目标检测框输出到深度图我们就可以读取目标的深度

首先创建一个scripts文件夹,创建ObjectLocation.py文件

touch ObjectLocation.py

目标深度测距的代码思路首先导入rospy、cv2、numpy、darknet_ros_msgs等包,创建一个目标定位的类,实例化类的一些参数,订阅深度图话题并转化为深度矩阵,订阅目标检测框将坐标信息对齐到深度图中,按照3x3滑动窗口遍历检测框进行中值滤波,最后取中心深度作为目标的估计深度,并发布距离话题

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image
from darknet_ros_msgs.msg import BoundingBoxes 
import numpy as np
from scipy.ndimage import median_filter
import math



class ObjectLocation:
    
    def __init__(self):
        self.bridge = CvBridge()
        self.depth_image = None
        self.bounding_boxes = None
        self.probability = 0
        self.Class = None
        self.distance = 0
        self.centerx = 0
        self.centery = 0
        self.x_h = 0
        self.y_h = 0
        self.w_h = 0
        cv2.destroyAllWindows()
        
    '''
    get depth distance from found object rect
    '''
    def depthDistanceFromCoordinate(self, data):
        try:
            self.depth_image = self.bridge.imgmsg_to_cv2(data, "passthrough")
            depth_array = np.array(self.depth_image, dtype=np.float32)
            filter_depth_array = np.zeros_like(depth_array)
            if self.bounding_boxes != None:
                for bounding_box in self.bounding_boxes:
                    # bounding box coordinates 
                    # less than 0 and larger than width or height
                    xmin = 0 if bounding_box.xmin < 0 else bounding_box.xmin
                    xmax = bounding_box.xmax if bounding_box.xmax < len(depth_array[0]) else len(depth_array[0])
                    ymin = 0 if bounding_box.ymin < 0 else bounding_box.ymin
                    ymax = bounding_box.ymax if bounding_box.ymax < len(depth_array) else len(depth_array)
                    dep_aver = 0
                    filter_depth_array = median_filter(depth_array, size=(3, 3))
                    dep_aver = filter_depth_array[int((ymin + ymax) / 2), int((xmin + xmax) / 2)]
                    self.probability = bounding_box.probability
                    self.Class = bounding_box.Class
                    self.distance = dep_aver
                    self.centerx =  (xmin + xmax) / 2
                    self.centery =  (ymin + ymax) / 2
                    rospy.loginfo("Class=%s, Probability=%f, Distance=%f", self.Class, self.probability, self.distance)
                    pub = rospy.Publisher('/darknet_ros/distance', self.distance, queue_size = 100)
                    pub.publish(self.distance)
        except Exception as e:
                print(e)

            
    def bbox_callback(self, msg):
        self.bounding_boxes = msg.bounding_boxes

        
if __name__ == '__main__':
    od = ObjectLocation()
    rospy.init_node('ObjectLocation', anonymous=True)
    rospy.Subscriber('/darknet_ros/bounding_boxes', BoundingBoxes, od.bbox_callback)
    rospy.Subscriber('/camera/depth/image_rect_raw', Image, od.depthDistanceFromCoordinate)
    rate = rospy.Rate(10)
    rate.sleep()
    rospy.spin()

三、目标方位解算与导航点设定

1、相机成像原理

我们需要根据相机内外参去解算目标的方位,相机内参外参原理参照下面链接,依据成像原理我们可以将在像素坐标系下的目标映射到相机坐标系
https://zhuanlan.zhihu.com/p/389653208

2、Python实现目标定位

运行标定相机的demo就可以得到相机的内参外参矩阵,我的是出厂就标定好的附带内参矩阵,放在
home/wheeltec/.ros/camera_info/camera.yaml 文件里,内参矩阵是相机出厂时就决定了的

image_width: 640
image_height: 480
camera_name: camera
camera_matrix:
  rows: 3
  cols: 3
  data: [ 581.88585,    0.     ,  314.2472 ,
            0.     ,  592.27138,  210.27091,
            0.     ,    0.     ,    1.     ]
camera_model: plumb_bob
distortion_model: plumb_bob
distortion_coefficients:
  rows: 1
  cols: 5
  data: [0.221666, -0.575455, -0.014215, 0.003895, 0.000000]
rectification_matrix:
  rows: 3
  cols: 3
  data: [ 1.,  0.,  0.,
          0.,  1.,  0.,
          0.,  0.,  1.]
projection_matrix:
  rows: 3
  cols: 4
  data: [ 591.88599,    0.     ,  315.96148,    0.     ,
            0.     ,  603.39893,  205.72873,    0.     ,
            0.     ,    0.     ,    1.     ,    0.     ]

相机的外参矩阵则要确定机器人坐标系下的相机位姿,才能将目标方位解算到机器人bse_link坐标系下。用Python程序设定导航目标点的主要通过ROS中名为"move_base"的动作服务器(Action Server),负责接收和处理导航目标,我们将导航目标点位姿信息解算好,发送到move_base节点即可。

在原有的ObjectLocation.py上导入actionlib、tf、move_base_msgs等包,使用矩阵求逆加内积来将像素坐标系下的目标位置映射到相机坐标系,再通过tf坐标变换映射到机器人坐标系,实例化导航目标点,将目标点位姿发送到move_base节点,等待动作服务器响应即可

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image
from darknet_ros_msgs.msg import  BoundingBoxes 
import numpy as np
from scipy.ndimage import median_filter
import actionlib
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal 
import tf.transformations as tr
import math



class ObjectLocation:
    
    def __init__(self):
        self.bridge = CvBridge()
        self.depth_image = None
        self.bounding_boxes = None
        self.probability = 0
        self.Class = None
        self.distance = 0
        self.centerx = 0
        self.centery = 0
        self.x_h = 0
        self.y_h = 0
        self.w_h = 0
        cv2.destroyAllWindows()
        
    '''
    get depth distance from found object rect
    '''
    def depthDistanceFromCoordinate(self, data):
        try:
            self.depth_image = self.bridge.imgmsg_to_cv2(data, "passthrough")
            depth_array = np.array(self.depth_image, dtype=np.float32)
            filter_depth_array = np.zeros_like(depth_array)
            if self.bounding_boxes != None:
                for bounding_box in self.bounding_boxes:
                    # bounding box coordinates 
                    # less than 0 and larger than width or height
                    xmin = 0 if bounding_box.xmin < 0 else bounding_box.xmin
                    xmax = bounding_box.xmax if bounding_box.xmax < len(depth_array[0]) else len(depth_array[0])
                    ymin = 0 if bounding_box.ymin < 0 else bounding_box.ymin
                    ymax = bounding_box.ymax if bounding_box.ymax < len(depth_array) else len(depth_array)
                    dep_aver = 0
                    filter_depth_array = median_filter(depth_array, size=(3, 3))
                    dep_aver = filter_depth_array[int((ymin + ymax) / 2), int((xmin + xmax) / 2)]
                    self.probability = bounding_box.probability
                    self.Class = bounding_box.Class
                    self.distance = dep_aver
                    self.centerx =  (xmin + xmax) / 2
                    self.centery =  (ymin + ymax) / 2
                    rospy.loginfo("Class=%s, Probability=%f, Distance=%f", self.Class, self.probability, self.distance)
                    if self.Class == "pottedplant":
                        fx = 581.88585
                        fy = 592.27138
                        cx = 314.2472
                        cy = 210.27091
                        K = np.array([[fx, 0., cx], [0., fy, cy], [0., 0., 1.]])
                        point_pixel = np.array([self.centerx, self.centery, 1])
                        point_camera = np.dot(np.linalg.inv(K), point_pixel) * self.distance
                        self.y_h = - point_camera[0] - 460
                        self.x_h = point_camera[2] + 110
                        orientation = tr.quaternion_from_euler(0, 0, math.atan2(self.y_h, self.x_h))
                        self.w_h = orientation[3]
                        rospy.loginfo("%s Goal location is x_h = %f, y_h = %f, w_h=%f", "pottedplant", self.x_h, self.y_h, self.w_h)
                        self.movebase_callback()
                    pub = rospy.Publisher('/darknet_ros/distance', self.distance, queue_size = 100)
                    pub.publish(self.distance)
        except Exception as e:
                print(e)



    def movebase_callback(self):
        try:
            client = actionlib.SimpleActionClient('move_base', MoveBaseAction)
            client.wait_for_server()
            goal = MoveBaseGoal()
            goal.target_pose.header.frame_id = "base_link"
            goal.target_pose.pose.position.x = self.x_h * 4 / 5000
            goal.target_pose.pose.position.y = self.y_h * 4 / 5000
            goal.target_pose.pose.orientation.w = self.w_h
            client.send_goal(goal)
            rospy.loginfo("move_base set goal success!")
            client.wait_for_result()
        except Exception as e:
            print("movebase_callback service failed")
            
    def bbox_callback(self, msg):
        self.bounding_boxes = msg.bounding_boxes

        
if __name__ == '__main__':
    od = ObjectLocation()
    rospy.init_node('ObjectLocation', anonymous=True)
    rospy.Subscriber('/darknet_ros/bounding_boxes', BoundingBoxes, od.bbox_callback)
    rospy.Subscriber('/camera/depth/image_rect_raw', Image, od.depthDistanceFromCoordinate)
    rate = rospy.Rate(10)
    rate.sleep()
    rospy.spin()

下面是演示结果,其实还可以用其他滤波方法让深度估计更加精确,用重采样方法让方位估计更加精准
在这里插入图片描述


总结

darknet_ros软件包将ROS和YOLO很好的融合在一起,为机器人视觉任务开发提供了更多可能性。通过实时目标检测和识别,机器人能够感知和理解环境,实现人机交互,提高安全性,并在各种应用中发挥更智能和自主的作用。通过实时目标检测和识别,可以帮助机器人识别不同类型的物体,并根据目标物体的位置和特征来理解当前场景。这对于机器人在导航、路径规划和任务执行中具有重要意义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/816176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apache Storm入门介绍之三分钟看懂Apache Storm

文章目录 0.前言1. 什么是 Apache Storm&#xff1f;1.1. Nimbus1.2. Zookeeper1.3. Supervisor1.4. Worker1.5 集群模式下各组件职责 2. 核心概念2.1基本架构和任务模型2.2 工作流程 3. 源码地址3.1. 代码结构3.1. 核心模块介绍 4. Storm入门实例0.创建java工程并引入依赖1. 创…

印度转向第一人口大国的背后,是红利还是负担?

KlipC报道&#xff1a;印度正在成为全球第一人口大国&#xff0c;人们对于该事件的关注也持续不断。 KlipC的合伙人Andi Duan表示&#xff1a;“自1881年以来&#xff0c;印度人口就一直增长&#xff0c;据联合国人口统计的数据显示”&#xff0c;今年4月印度人口已经成为全球最…

ElasticSearch_学习笔记

一、初始elasticsearch 什么是elasticsearch&#xff1f; 一个开源的分布式搜索引擎&#xff0c;可以用来时限搜素、日志统计、分析、系统监控等功能。什么是elasitc stack&#xff08;ELK&#xff09;&#xff1f; 是以elasticsearch为核心的技术栈&#xff0c;包括 beats、L…

C#——多线程之Thread

C#——多线程之Thread 前言一、Thread是什么&#xff1f;二、各应用场景以及实例分析1.前台线程和后台线程&#xff1a;2.异步处理3.线程状态及手动销毁线程4.线程同步/等待线程完成 总结 前言 上次简单讲述了关于多线程中Task的相关应用以及场景。今天我们来看一下多线程中Th…

这就是ChatGPT,走进我们的生活!

这就是ChatGPT&#xff0c;走进我们的生活&#xff01; 早在年初&#xff0c;合作导师将我叫过去&#xff0c;让我了解学习一下ChatGPT&#xff0c;看能不能对我们的生活有所帮助。一直使用着国内镜像&#xff0c;五月份我才注册了OpenAI的账户。如今&#xff0c;打开购物商城购…

安全测试国家标准解读——并发程序安全

本系列文章主要围绕《GB/T 38674—2020 信息安全技术 应用软件安全编程指南》进行讲解&#xff0c;该标准是2020年4月28日&#xff0c;由国家市场监督管理总局、国家标准化管理委员会发布&#xff0c;2020年11月01日开始实施。我们对该标准中一些常见的漏洞进行了梳理&#xff…

leetcode 134. 加油站

2023.7.31 这题直观思路是暴力法&#xff0c;用一个for循环遍历所有起点&#xff0c;判断哪个起点能走回来。 不过最后有几个很阴间的示例通过不了&#xff1a; class Solution { public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {f…

SpringBoot使用MyBatis Plus + 自动更新数据表

1、Mybatis Plus介绍 Mybatis&#xff0c;用过的都知道&#xff0c;这里不介绍&#xff0c;mybatis plus只是在mybatis原来的基础上做了些改进&#xff0c;增强了些功能&#xff0c;增强的功能主要为增加更多常用接口方法调用&#xff0c;减少xml内sql语句编写&#xff0c;也可…

编写Java代码制造一个内存溢出的情况

编写Java代码制造一个内存溢出的情况 这将会是一篇比较邪恶的文章&#xff0c;当你想在某个人的生活中制造悲剧时你可能会去google搜索它。在java的世界里&#xff0c;内存溢出仅仅只是你在这种情况下可能会引入的一种bug。你的受害者会在办公室里度过几天甚至是几周的不眠之夜…

IO流(3)—转换流与打印流

目录 1. 为什么要用到转换流&#xff1f; 2. 字符输入转换流&#xff08;重点掌握&#xff09; 3. 字符转换输出流&#xff08;理解即可&#xff09; 4. 认识打印流 5. 打印流的作用 6. PrintStream如何使用&#xff1f; 7. PrintStream 内部没有缓冲区 8. PrintWriter…

四、Spring源码-DI的过程

Spring源码-DI的过程 接下来我们分析下Spring源码中Bean初始化过程中的DI过程。也就是属性的依赖注入。 一、构造参数依赖 1. 如何确定构造方法 在Spring中生成Bean实例的时候默认是调用对应的无参构造方法来处理。 Component public class BeanK {private BeanE beanE;priv…

LUN映射出错导致写操作不互斥的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 某公司的光纤SAN存储系统&#xff0c;6块硬盘组建一组RAID6&#xff0c;划分若干LUN&#xff0c;MAP到不同的SOLARIS操作系统服务器上。 服务器故障&分析&#xff1a; 由于业务增长需要新增应用&#xff0c;工作人员增加了一台IBM服务器&am…

共享麻将室开启无人值守新潮流

共享麻将室是指一种基于共享经济模式&#xff0c;将麻将室资源进行共享的服务&#xff0c;为用户提供舒适、方便的娱乐场所。通过共享麻将室&#xff0c;用户可以按需预约和使用麻将室&#xff0c;享受社交娱乐的同时&#xff0c;减少了个人投资和管理麻将室的成本。 相比传统麻…

kotlin 编写一个简单的天气预报app(二)增加搜索城市功能

增加界面显示openweathermap返回的信息。 在activity_main.xml里增加输入框来输入城市&#xff0c;在输入款旁边增加搜索按钮来进行查询。 然后原来显示helloworld的TextView用来显示结果。 1. 增加输入城市名字的EditText <EditTextandroid:id"id/editTextCity"…

puppeteer代理的搭建和配置

puppeteer代理的搭建和配置 本文深入探讨了Puppeteer在网络爬虫和自动化测试中的重要角色&#xff0c;着重介绍了如何搭建和配置代理服务器&#xff0c;以优化Puppeteer的功能和性能。文章首先介绍了Puppeteer作为一个强大的Headless浏览器自动化工具的优势和应用场景&#xf…

【嵌入式学习笔记】嵌入式入门1——GPIO

1.什么是GPIO General Purpose Input Output&#xff0c;即通用输入输出端口&#xff0c;简称GPIO&#xff0c;作用是负责采集外部器件的信息或者控制外部器件工作&#xff0c;即输入输出。 2.STM32 GPIO简介 2.1.GPIO特点 不同型号&#xff0c;IO口数量可能不一样&#x…

企业如何在线编写一份优秀的产品说明文档?

编写一份优秀的产品说明文档对于企业来说非常重要&#xff0c;它可以帮助用户理解产品的功能、使用方法和优势&#xff0c;提高用户体验和满意度。下面是一些关键的步骤和建议&#xff0c;帮助企业在线编写一份优秀的产品说明文档。 一、明确目标受众 在编写产品说明文档之前…

搞活系列-Java NIO之偏偏不用buffer.flip()会出现什么问题?

最近看博客又看到了Java NIO相关的博客&#xff0c;其中有讲解NIO和传统IO关于文件复制的文章&#xff0c;看到了如下的代码&#xff1a; /**** channel用例* 基于channel的文件复制*/Testpublic void fileCopyByChannel(){try {FileInputStream fileInputStream new FileInpu…

MyBatis小记_one

目录 什么是框架 1.框架的概述 2.框架要解决的问题 3. 软件开发的分层重要性 4.分层开发的常见框架 MyBatis 框架概述 JDBC 编程的回顾 JDBC 问题分析 MyBatis 框架快速入门 1.官网下载MyBatis框架jar包 2.搭建MyBatis 开发环境 3. 编写持久层接口的映射文件 IUserD…

Stable Diffusion 使用教程

环境说明&#xff1a; stable diffusion version: v1.5.1python: 3.10.6torch: 2.0.1cu118xformers: N/Agradio: 3.32.0 1. 下载 webui 下载地址&#xff1a; GitHub stable-diffusion-webui 下载 根据自己的情况去下载&#xff1a; 最好是 N 卡&#xff1a;&#xff08;我的…