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工具,运行即可。...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
