Jetson Nano之ROS入门 -- YOLO目标检测与定位
文章目录
- 前言
- 一、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.weightswget http://pjreddie.com/media/files/yolov2-tiny.weightsVOC data set (Yolo v2):wget http://pjreddie.com/media/files/yolov2-voc.weightswget http://pjreddie.com/media/files/yolov2-tiny-voc.weightsYolo v3:wget http://pjreddie.com/media/files/yolov3.weightswget 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_boxesType: darknet_ros_msgs/BoundingBoxesPublishers: * /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 mathclass ObjectLocation:def __init__(self):self.bridge = CvBridge()self.depth_image = Noneself.bounding_boxes = Noneself.probability = 0self.Class = Noneself.distance = 0self.centerx = 0self.centery = 0self.x_h = 0self.y_h = 0self.w_h = 0cv2.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 heightxmin = 0 if bounding_box.xmin < 0 else bounding_box.xminxmax = 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.yminymax = bounding_box.ymax if bounding_box.ymax < len(depth_array) else len(depth_array)dep_aver = 0filter_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.probabilityself.Class = bounding_box.Classself.distance = dep_averself.centerx = (xmin + xmax) / 2self.centery = (ymin + ymax) / 2rospy.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_boxesif __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: 3cols: 3data: [ 581.88585, 0. , 314.2472 ,0. , 592.27138, 210.27091,0. , 0. , 1. ]
camera_model: plumb_bob
distortion_model: plumb_bob
distortion_coefficients:rows: 1cols: 5data: [0.221666, -0.575455, -0.014215, 0.003895, 0.000000]
rectification_matrix:rows: 3cols: 3data: [ 1., 0., 0.,0., 1., 0.,0., 0., 1.]
projection_matrix:rows: 3cols: 4data: [ 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 mathclass ObjectLocation:def __init__(self):self.bridge = CvBridge()self.depth_image = Noneself.bounding_boxes = Noneself.probability = 0self.Class = Noneself.distance = 0self.centerx = 0self.centery = 0self.x_h = 0self.y_h = 0self.w_h = 0cv2.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 heightxmin = 0 if bounding_box.xmin < 0 else bounding_box.xminxmax = 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.yminymax = bounding_box.ymax if bounding_box.ymax < len(depth_array) else len(depth_array)dep_aver = 0filter_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.probabilityself.Class = bounding_box.Classself.distance = dep_averself.centerx = (xmin + xmax) / 2self.centery = (ymin + ymax) / 2rospy.loginfo("Class=%s, Probability=%f, Distance=%f", self.Class, self.probability, self.distance)if self.Class == "pottedplant":fx = 581.88585fy = 592.27138cx = 314.2472cy = 210.27091K = 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.distanceself.y_h = - point_camera[0] - 460self.x_h = point_camera[2] + 110orientation = 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 / 5000goal.target_pose.pose.position.y = self.y_h * 4 / 5000goal.target_pose.pose.orientation.w = self.w_hclient.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_boxesif __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很好的融合在一起,为机器人视觉任务开发提供了更多可能性。通过实时目标检测和识别,机器人能够感知和理解环境,实现人机交互,提高安全性,并在各种应用中发挥更智能和自主的作用。通过实时目标检测和识别,可以帮助机器人识别不同类型的物体,并根据目标物体的位置和特征来理解当前场景。这对于机器人在导航、路径规划和任务执行中具有重要意义。
相关文章:

Jetson Nano之ROS入门 -- YOLO目标检测与定位
文章目录 前言一、yolo板端部署推理二、目标深度测距三、目标方位解算与导航点设定1、相机成像原理2、Python实现目标定位 总结 前言 Darknet_ros是一个基于ROS(机器人操作系统)的开源深度学习框架,它使用YOLO算法进行目标检测和识别。YOLO算…...

【移动机器人运动规划】01 —— 常见地图基础 |图搜索基础
文章目录 前言相关代码整理:相关文章: 可视化网址:常用地图基础Occupancy grid mapOcto-mapVoxel hashingPoint cloud mapTSDF mapESDF mapFree-space RoadmapVoronoi Diagram Map 图搜索基础配置空间图搜索基本概念DijkstraAStarAstar的一些变种&#x…...

mongotop跟踪Mongodb集合读取和写入数据
版本控制 从 MongoDB 4.4 开始,MongoDB 数据库工具现在与 MongoDB 服务器分开发布,并使用自己的版本控制,初始版本为100.0.0. 此前,这些工具与 MongoDB 服务器一起发布,并使用匹配的版本控制。 兼容性 mongotop 版本…...

Linux中使用du命令来查看目录的大小
在Linux中,你可以使用du命令来查看目录的大小。下面是一些常用的du命令选项: -h:以人类可读的格式显示文件大小。-s:仅显示总大小,而不显示每个子目录的大小。-c:显示总大小,并在最后一行显示总…...

【Linux】进程篇Ⅰ:进程信息、进程状态、环境变量、进程地址空间
文章目录 一、概述二、查看进程信息1. 系统文件夹 /proc2. 用户级工具 ps3. getpid() 函数:查看进程 PID4. 用 kill 杀进程5. 进程优先级 二、进程状态分析0. 1. R (running) 运行状态2. S (sleeping) 休眠状态3. D (disk sleep) 不可中断的休眠状态4. T (stopped) …...

保护 TDengine 查询性能——3.0 如何大幅降低乱序数据干扰?
在时序数据库(Time Series Database)场景下,乱序数据的定义为:“时间戳(timestamp)不按照递增顺序到达数据库的数据。”虽然它的定义很简单,但时序数据库需要有相应的处理逻辑来保证数据存储时的…...

状态机实现N位按键消抖
状态机实现N位按键消抖 1、原理 利用状态机实现按键的消抖,具体的原理可参考 (50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客 状态机简介: 状态机分类可以主要分为两类:moore和mealy 根据三段式状态机最后…...

uniapp自定义消息语音
需求是后端推送的消息APP要响自定义语音,利用官方插件,总结下整体流程 uniapp后台配置 因为2.0只支持uniapp自己的后台发送消息,所以要自己的后台发送消息只能用1.0 插件地址和代码 插件地址: link let isIos (plus.os.name "iOS&qu…...

k8s安装Jenkins
目录 编辑 一、环境准备 1.1 环境说明 二、安装nfs 2.1 安装NFS 2.2 创建NFS共享文件夹 2.3 配置共享文件夹 2.4 使配置生效 2.5 查看所有共享目录 2.6 启动nfs 2.7 其他节点安装nfs-utils 三、创建PVC卷 3.1 创建namespace 3.2 创建nfs 客户端sa授权 3.3 创建…...

共筑开源新长城 龙蜥社区走进开放原子校源行-清华大学站
6 月 28 日,以“聚缘于校,开源共行”为主题的 2023 年开放原子校源行活动在清华大学成功举行。本次活动由开放原子开源基金会和清华大学共同主办,来自各行业的 22 位大咖共聚校园共话开源。龙蜥社区技术专家边子政受邀进行技术分享࿰…...

Jgit 工具类 (代码检出、删除分支(本地、远程)、新建分支、切换分支、代码提交)
https://blog.csdn.net/qq_37203082/article/details/120327084 Jgit 工具类 (代码检出、删除分支(本地、远程)、新建分支、切换分支、代码提交)_jgit删除远程分支_CJ点的博客-CSDN博客 <!--JAVA操作GIT--><dependency><groupId>org.…...

什么是redux?如何在react 项目中使用redux?
redux 概念 redux是一种用于管理JavaScript应用程序的状态管理库。它可以与React、Augular、Vue等前端框架结合使用,但也可以纯在JavaScript应用程序中独立使用。redux遵循单项数据流的原则,通过一个全局的状态树来管理应用程序的状态,从而使…...

mysql的json处理
写在前面 需要注意,5.7以上版本才支持,但如果是生产环境需要使用的话,尽量使用8.0版本,因为8.0版本对json处理做了比较大的性能优化。你你可以使用select version();来查看版本信息。 本文看下MySQL的json处理。在正式开始让我们先…...

前端学习——Vue (Day8)
Vue3 create-vue搭建Vue3项目 注意要使用nodejs16.0版本以上,windows升级node可以西安使用where node查看本地node位置,然后到官网下载msi文件,在本地路径下安装即可 安装完可以使用node -v检查版本信息 项目目录和关键文件 组合式API - s…...

Windows环境下安装及部署Nginx
一、安装Nginx教程 1、官网下载地址:https://nginx.org/en/download.html 2、下载教程:选择Stable version版本下载到本地 3、下载完成后,解压放入本地非中文的文件夹中: 4、启动nginx:双击nginx.exe,若双击…...

使用AOP切面对返回的数据进行脱敏的问题
1.注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** Author: xiaoxin* Date: 2023/7/21 17:15*/ Retention(RetentionPolicy.RUNTIME) Targe…...

TDengine时区设置
一般来说,时序数据就是带有时间序列属性的数据。在处理时序数据时,TDengine有着自己独特的方式。但是如果没有正确理解TDengine在写入和查询上的行为,极可能会因为配置了错误的时区(timezone),而导致写入和…...

站外引流效果差?一文带你搞懂解海外主流社交媒体算法!
在流量成本越来越高的当下,无论是平台卖家还是独立站卖家都在努力拓展流量渠道。站外引流是推动业务增长的关键策略,很多卖家会把重点放在内容营销上,但其实除了做好内容之前,了解社交媒体的算法才能让营销效果最大化。 01.Faceb…...

css 动画之旋转视差
序:网上看到的一个例子,做一下 效果图: 代码: <style>.content{width: 300px;height: 300px;margin: 139px auto;display: grid;grid-template-columns: repeat(3,1fr);grid-template-rows: repeat(3,1fr);grid-template:…...

maven项目、springboot项目复制文件进来后没反应、不编译解决方法
问题如下 把文件复制进springboot项目后,没反应,不编译。 解决 在maven工具框中选择compile工具,运行即可。...

android jetpack App Startup 应用启动时初始化组件(java)
有什么用? 应用启动时初始化组件。 怎么用 添加依赖 dependencies {implementation "androidx.startup:startup-runtime:1.1.1" }创建类,继承Initializer。 public class AppInit implements Initializer<String> {NonNullOverride…...

【设计模式|行为型】命令模式(Command Pattern)
说明 命令模式(Command Pattern)是一种行为设计模式,它将请求封装为一个对象,以便在不同的请求者和接收者之间进行解耦、参数化和操作的队列化。命令模式允许你将具体的请求封装为对象,这些对象之间彼此独立ÿ…...

SqlServer 批量删除表
SqlServer 批量删除表 直接上SQL脚本吧 SELECT row_number()over(order by Name) as FID,Name into #temp FROM SysObjects Where XTypeU --类型,U为实体表 and name like TMP% --表名过滤(自定义就好) ORDER BY Namedeclare count int 0…...

[Linux]线程基本知识
概念 进程 一个正在执行的程序,它是资源分配的最小单位 进程中的事情需要按照一定的顺序逐个进行 进程出现了很多弊端: 一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程; 二是由于对称多…...

STM32 串口基础知识学习
串行/并行通信 串行通信:数据逐位按顺序依次传输。 并行通信:数据各位通过多条线同时传输。 对比 传输速率:串行通信较低,并行通信较高。抗干扰能力:串行通信较强,并行通信较弱。通信距离:串…...

页面滚动时隐藏element-ui下拉框/时间弹框
场景 在系统中,当(有垂直滚动时)点击下拉框后滚动页面,会发现下拉项会遮盖住页面中的元素,不会隐藏 解决:(以vue为例) 在页面滚动或者缩放时隐藏下拉项即可(借助点击目标元素,下…...

C#中i++和++i的底层原理
一:前言 我们都知道,i是先取值,后计算。i是先计算,后取值。下面说下它的底层原理 二:原理 int i 0; i; Console.WriteLine(i); 结果是1 执行步骤是: 1.将常量0压入栈中 2.从栈中取出元素0,局…...

在win10下安装verilator
主要参考文章 Verilator简介及其下载安装卸载_徐晓康的博客的博客-CSDN博客https://blog.csdn.net/weixin_42837669/article/details/114505364上面的文章可以解决大部分问题,但是可能是方案有些老了,已经安装最新的版本,下面对最新的版本安装提供解决方案 一 预备工作 安…...

java设计模式-建造者(Builder)设计模式
介绍 Java的建造者(Builder)设计模式可以将产品的内部表现和产品的构建过程分离开来,这样使用同一个构建过程来构建不同内部表现的产品。 建造者设计模式涉及如下角色: 产品(Product)角色:被…...

iOS开发-实现获取下载主题配置动态切换主题
iOS开发-实现获取下载主题配置动态切换主题 iOS开发-实现获取下载主题配置更切换主题,主要是通过请求服务端配置的主题配置、下载主题、解压保存到本地。通知界面获取对应的图片及颜色等。 比如新年主题风格,常见的背景显示红色氛围图片、tabbar显示新…...