ROS小车研究笔记3/11/2023:多点导航及其源码实现

news2024/12/24 3:02:57

多点导航操作

打开导航launch文件

roslaunch turn_on_wheeltec_robot navigation.launch
rviz

在rviz里,选择publish point在地图上点击标记目标点。在标记多个目标点后小车会按标记顺序依次在各个目标点中往返

多点导航对于话题MarkerArray。需要在rviz中使markerArray订阅小车发布的path_point话题
在这里插入图片描述

多点导航源码分析

在导航节点 turn_on_wheeltec_robot navigation.launch文件中,启动了如下节点用于多点导航

 <!-- MarkerArray功能节点> -->
 <node name='send_mark' pkg="turn_on_wheeltec_robot" type="send_mark.py">
 </node>

找到turn_on_wheeltec_robot下的send_mark.py。该节点即为多点导航控制节点

1 主方法

def send_mark():
    global markerArray, markerArray_number, count, index, try_again, mark_pub, goal_pub
    markerArray = MarkerArray() #目标点标记数组
    markerArray_number = MarkerArray() #目标点标记数组
    count = 0
    index = 0
    try_again = 1
    sendflagPublisher = rospy.Publisher('/send_flag', Int8, queue_size =1)
    rospy.init_node('path_point_demo') #初始化节点
    mark_pub    = rospy.Publisher('/path_point', MarkerArray, queue_size = 100) #用于发布目标点标记
    navGoal_sub = rospy.Subscriber('/send_mark_goal', PoseStamped, navGoal_callback) #订阅rviz内标记按下的位置
    click_sub   = rospy.Subscriber('/clicked_point', PointStamped, click_callback) #订阅rviz内标记按下的位置
    goal_pub    = rospy.Publisher('/move_base_simple/goal', PoseStamped, queue_size = 1) #用于发布目标点
    send_flag=Int8()
    send_flag.data=1
    sendflagPublisher.publish(send_flag)
    rospy.sleep(1.)
    sendflagPublisher.publish(send_flag)
    rospy.loginfo('a=%d',send_flag.data)
    print("11111111111111111111111111")
    goal_status_sub = rospy.Subscriber('/move_base/result', MoveBaseActionResult, pose_callback) #用于订阅是否到达目标点状态


    while not rospy.is_shutdown():
        key = getKey() #获取键值
        if(key=='c'): #键值为c是清空目标点
            count = 0
            index = 0
            try_again = 1

            markerArray = MarkerArray() 
            marker = Marker()
            marker.header.frame_id = 'map' 
            marker.type = marker.TEXT_VIEW_FACING 
            marker.action = marker.DELETEALL 
            marker.text = '' 
            markerArray.markers.append(marker) 

            for m in markerArray_number.markers:    
                m.action = marker.DELETEALL

            mark_pub.publish(markerArray) 
            mark_pub.publish(markerArray_number) 
            markerArray = MarkerArray() 
            markerArray_number = MarkerArray() 

        elif (key == '\x03'): #ctrl+c退出
            break
def breakkey():
    fd = sys.stdin.fileno()
    new_settings = termios.tcgetattr(fd)
    new_settings[3]=new_settings[3] | termios.ECHO
    termios.tcsetattr(fd, termios.TCSADRAIN, new_settings)

if __name__ == '__main__':
    settings = termios.tcgetattr(sys.stdin) #获取键值初始化
    rospy.on_shutdown(breakkey)#退出前执行键值初始化
    send_mark()

对于创建的变量,markerArray是一个数组用于保存所有目标的。count为目标点总数,index为下一个要前往的目标点,try_again为在前往目标点失败后,在重新选择前往下一个目标点前尝试前往该目标点的次数。

后面定义了几个发布者和订阅者

mark_pub = rospy.Publisher(‘/path_point’, MarkerArray, queue_size = 100) #用于发布目标点标记
navGoal_sub = rospy.Subscriber(‘/send_mark_goal’, PoseStamped, navGoal_callback) #订阅rviz内标记按下的位置
click_sub = rospy.Subscriber(‘/clicked_point’, PointStamped, click_callback) #订阅rviz内标记按下的位置
goal_pub = rospy.Publisher(‘/move_base_simple/goal’, PoseStamped, queue_size = 1) #用于发布目标点
send_flag=Int8()

这里我们得到rviz里发布的clicked_point和send_mark_goal代表得到的目标点。并发布小车的目标点

click_sub的回调函数click_callback

#rviz内标记按下的回调函数,输入参数:按下的位置[x, y, z=0]
def click_callback(msg):           
    global index, count

    print('Add a new target point '+str(count)+':')
    print('x:'+str(msg.point.x)+
        ', y:'+str(msg.point.y)+
        ', z:0'+', w:1') 

    marker = Marker()      #创建marker对象
    marker.header.frame_id = 'map' #以哪一个TF坐标为原点
    marker.type = marker.ARROW #一直面向屏幕的字符格式
    marker.action = marker.ADD #添加marker
    marker.scale.x = 0.2 #marker大小
    marker.scale.y = 0.05 #marker大小
    marker.scale.z = 0.05 #marker大小,对于字符只有z起作用
    marker.color.a = 1 #字符透明度
    marker.color.r = 1 #字符颜色R(红色)通道
    marker.color.g = 0 #字符颜色G(绿色)通道
    marker.color.b = 0 #字符颜色B(蓝色)通道
    marker.pose.position.x = msg.point.x #字符位置
    marker.pose.position.y = msg.point.y #字符位置
    marker.pose.orientation.z = 0 #字符位置
    marker.pose.orientation.w = 1 #字符位置
    markerArray.markers.append(marker) #添加元素进数组

    marker_number = Marker()      #创建marker对象
    marker_number.header.frame_id = 'map' #以哪一个TF坐标为原点
    marker_number.type = marker_number.TEXT_VIEW_FACING #一直面向屏幕的字符格式
    marker_number.action = marker_number.ADD #添加marker
    marker_number.scale.x = 0.5 #marker大小
    marker_number.scale.y = 0.5 #marker大小
    marker_number.scale.z = 0.5 #marker大小,对于字符只有z起作用
    marker_number.color.a = 1 #字符透明度
    marker_number.color.r = 1 #字符颜色R(红色)通道
    marker_number.color.g = 0 #字符颜色G(绿色)通道
    marker_number.color.b = 0 #字符颜色B(蓝色)通道
    marker_number.pose.position.x = msg.point.x #字符位置
    marker_number.pose.position.y = msg.point.y #字符位置
    marker_number.pose.position.z = 0.1 #字符位置
    marker_number.pose.orientation.z = 0 #字符位置
    marker_number.pose.orientation.w = 1 #字符位置
    marker_number.text = str(count) #字符内容
    markerArray_number.markers.append(marker_number) #添加元素进数组

    #markers的id不能一样,否则rviz只会识别最后一个元素
    id = 0
    for m in markerArray.markers:    #遍历marker分别给id赋值
        m.id = id
        id += 1

    for m in markerArray_number.markers:    #遍历marker分别给id赋值
        m.id = id
        id += 1
    
    mark_pub.publish(markerArray) #发布markerArray,rviz订阅并进行可视化
    mark_pub.publish(markerArray_number) #发布markerArray,rviz订阅并进行可视化

    #第一次添加marker时直接发布目标点
    if count == 0:
        pose = PoseStamped() #创建目标点对象
        pose.header.frame_id = 'map' #以哪一个TF坐标为原点
        pose.header.stamp = rospy.Time.now()
        pose.pose.position.x = msg.point.x #目标点位置
        pose.pose.position.y = msg.point.y #目标点位置
        pose.pose.orientation.z = 0 #四元数,到达目标点后小车的方向,z=sin(angle/2)
        pose.pose.orientation.w = 1 #四元数,到达目标点后小车的方向,w=cos(angle/2)
        goal_pub.publish(pose)
        index += 1 #下一次要发布的目标点序号

    count += 1 #有几个目标点

click_callback实现了如下功能
1 在终端打出得到的目标点位置和编号

2 创建Marker对象(rviz上的标记点),并设置标记面向屏幕,大小,透明度,颜色,位置,方向

3 设置marker的id为标记的顺序值。默认情况下marker id都为0,这会导致当有多个id相同的marker时rviz只显示最后一个

4 发布markerArray,由rviz订阅以进行可视化

5 在第一次添加marker时会发布目标点消息,类型为PoseStamped。在之后会让count加一

疑点:这里我们创建了marker和marker_number,并对这两个对象进行了完全一样的操作,设置这一个完全相同的marker_number的目的是什么

navGoal_sub的回调函数pose_callback

#到达目标点成功或失败的回调函数,输入参数:[3:成功, 其它:失败](4:ABORTED)
def pose_callback(msg):
    global try_again, index, try_again, index
    if msg.status.status == 3 and count>0 :  #成功到达任意目标点,前往下一目标点
        try_again = 1 #允许再次尝试前往尚未抵达的该目标点
       
        #count表示当前目标点计数,index表示已完成的目标点计数
        if index == count:                   #当index等于count时,表示所有目标点完成,重新开始巡航
            print ('Reach the target point '+str(index-1)+':')
            print('x:'+str(markerArray.markers[index-1].pose.position.x)+
                ', y:'+str(markerArray.markers[index-1].pose.position.y)+
                ', z:'+str(markerArray.markers[index-1].pose.orientation.z)+
                ', w:'+str(markerArray.markers[index-1].pose.orientation.w))   

            if count > 1:
                print('Complete instructions!') #只有一个目标点不算巡航
                index = 0
            pose = PoseStamped()
            pose.header.frame_id = 'map'
            pose.header.stamp = rospy.Time.now()
            pose.pose.position.x = markerArray.markers[index].pose.position.x
            pose.pose.position.y = markerArray.markers[index].pose.position.y
            pose.pose.orientation.z = markerArray.markers[index].pose.orientation.z
            pose.pose.orientation.w = markerArray.markers[index].pose.orientation.w
            goal_pub.publish(pose)
            index += 1 #下一次要发布的目标点序号

        elif index < count:                   #当index小于count时,表示还未完成所有目标点,目标巡航未完成
            print ('Reach the target point '+str(index-1)+':')    
            print('x:'+str(markerArray.markers[index-1].pose.position.x)+
                ', y:'+str(markerArray.markers[index-1].pose.position.y)+
                ', z:'+str(markerArray.markers[index-1].pose.orientation.z)+
                ', w:'+str(markerArray.markers[index-1].pose.orientation.w)) 

            pose = PoseStamped()
            pose.header.frame_id = 'map'
            pose.header.stamp = rospy.Time.now()
            pose.pose.position.x = markerArray.markers[index].pose.position.x
            pose.pose.position.y = markerArray.markers[index].pose.position.y
            pose.pose.orientation.z = markerArray.markers[index].pose.orientation.z
            pose.pose.orientation.w = markerArray.markers[index].pose.orientation.w
            goal_pub.publish(pose)
            index += 1 #下一次要发布的目标点序号
        
    elif count > 0: #未抵达设定的目标点    
        rospy.logwarn('Can not reach the target point '+str(index-1)+':'+'\r\n'+
                      'x:'+str(markerArray.markers[index-1].pose.position.x)+
                    ', y:'+str(markerArray.markers[index-1].pose.position.y)+
                    ', z:'+str(markerArray.markers[index-1].pose.orientation.z)+
                    ', w:'+str(markerArray.markers[index-1].pose.orientation.w)) 

        #如果未尝试过前往尚未抵达的目标点,则尝试前往尚未抵达的目标点
        if try_again == 1:
            rospy.logwarn('trying reach the target point '+str(index-1)+' again!'+'\r\n'+
                          'x:'+str(markerArray.markers[index-1].pose.position.x)+
                        ', y:'+str(markerArray.markers[index-1].pose.position.y)+
                        ', z:'+str(markerArray.markers[index-1].pose.orientation.z)+
                        ', w:'+str(markerArray.markers[index-1].pose.orientation.w)) 

            pose = PoseStamped()
            pose.header.frame_id = 'map'
            pose.header.stamp = rospy.Time.now()
            pose.pose.position.x = markerArray.markers[index - 1].pose.position.x           
            pose.pose.position.y = markerArray.markers[index - 1].pose.position.y
            pose.pose.orientation.z = markerArray.markers[index-1].pose.orientation.z
            pose.pose.orientation.w = markerArray.markers[index-1].pose.orientation.w
            goal_pub.publish(pose)
            try_again = 0 #不允许再次尝试前往尚未抵达的该目标点

        #如果已经尝试过前往尚未抵达的目标点,则前往下一个目标点
        elif index < len(markerArray.markers):      #若还未完成目标点
            rospy.logwarn('try reach the target point '+str(index-1)+' failed! reach next point:'+'\r\n'+
                          'x:'+str(markerArray.markers[index-1].pose.position.x)+
                        ', y:'+str(markerArray.markers[index-1].pose.position.y)+
                        ', z:'+str(markerArray.markers[index-1].pose.orientation.z)+
                        ', w:'+str(markerArray.markers[index-1].pose.orientation.w)) 

            if index==count: index=0 #如果下一个目标点序号为count,说明当前目标点为最后一个目标点,下一个目标点序号应该设置为0
            pose = PoseStamped()
            pose.header.frame_id = 'map'
            pose.header.stamp = rospy.Time.now()
            pose.pose.position.x = markerArray.markers[index].pose.position.x      
            pose.pose.position.y = markerArray.markers[index].pose.position.y
            pose.pose.orientation.z = markerArray.markers[index].pose.orientation.z
            pose.pose.orientation.w = markerArray.markers[index].pose.orientation.w
            goal_pub.publish(pose)
            index += 1 #下一次要发布的目标点序号
            try_again = 1 #允许再次尝试前往尚未抵达的该目标点

函数内部执行的操作均为封装PoseStamped话题并通过goal_pub发布给小车运动控制节点进行执行。这里我们主要看该函数的导航逻辑

1

if msg.status.status == 3 and count>0 : #成功到达任意目标点,前往下一目标点

在我们受到的参数msg中,status为3代表成功到达目标点,4(或者任何其他值)代表失败。判断count > 0是为了确定小车处于多点导航模式。如果count为0代表小车只是执行了一次自主导航,而没有开启多点导航,故不执行后面程序

2

if index == count: #当index等于count时,表示所有目标点完成,重新开始巡航
print (‘Reach the target point ‘+str(index-1)+’:’)

index代表下一个目标点,count代表目标点总数。因此当两者相等时代表本轮巡航彻底完成,开始新一轮巡航。

这时我们将index重设为0,并发布话题让小车回到0点

3

elif index < count: #当index小于count时,表示还未完成所有目标点,目标巡航未完成

index小于count时代表还没有完成所有目标点,此时发布话题指挥小车前往下一个目标点并使index + 1。这里注意区分index - 1代表了当前小车所在目标点,而index 代表小车要去的下一个目标点

4

elif count > 0: #未抵达设定的目标点

此时代表小车处于多点导航状态,但是没有收到成功信息3,代表小车没有成功到达目标点。这时如果try_again为1,代表小车有一次再次尝试机会,控制小车再次前往目标点(前往当前目标点index - 1)。如果尝试后依然失败,则让小车前往下一个目标点(index)

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

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

相关文章

网页基本标签、图像标签、链接标签、块内元素和块元素、列表标签、表格标签

一、网页基本标签 标题标签 段落标签 未写段落标签前&#xff0c;文本没有按照想要的格式排列显示 写段落标签后&#xff1a; 每句都是一段&#xff0c;所以句与句距离比较宽 换行标签 同一段&#xff0c;只是把文字换行&#xff0c;所以比较紧凑 水平线标签 字体样式标签 …

【深度学习】神经网络和深度学习--卷积和池化的作用

深度学习通常指训练大型深度的神经网络的过程。 与传统的神经网络模型相比&#xff0c;深度学习模型在结构上与之非常相似&#xff1b;不同的是&#xff0c;深度学习模型的“深度”更大&#xff0c;“深度”的体现就是神经网络层数多&#xff0c;神经网络每一层的结点数多。 本…

# 关于Docker容器中时间时区问题的测试

关于Docker容器中时间时区问题的测试 文章目录关于Docker容器中时间时区问题的测试1 基础知识1.1 /etc/localtime1.2 /etc/timezone2 我在gitlab中遇到的问题与解决方法3 ubuntu官方镜像时区相关问题的测试3.0 官方原版测试3.1 不设置任何参数测试3.2 只设置TZ"Asia/Shang…

Spring Cloud Gateway 监控、多网关实例路由共享 | Spring Cloud 18

一、监控 Actuator是Spring Boot提供的用来对应用系统进行监控的功能模块&#xff0c;借助于Actuator开发者可以很方便地对应用系统某些监控指标进行查看、统计等。 Actuator的核心是端点Endpoint。 Endpoint可以让我们监视应用程序并与其交互。Spring Boot包含许多内置端点…

rapidcsv 写csv文件实例

csv实质是一个文本文件&#xff0c;可以使用rapidcsv写文件操作&#xff0c;如下实例&#xff1a; 第一行实质是从-1行开始&#xff0c;列是从0开始 #include "rapidcsv.h" #include <string> using namespace std; void CMFCApplication1Dlg::OnBnClickedBu…

【flask】URL和视图映射

目录 首页 传参 URL数据类型 get传参 首页 url与视图函数的映射是通过app.route()装饰器实现的。 只有一个斜杠代表的是根目录——首页。 传参 URL传参是通过<参数名称>的形式进行传递。URL中有几个参数&#xff0c;在视图函数中也要指定几个参数 from flask im…

WattOS:一个稳又快的轻量级 Linux 发行版

导读Linux 领域里的每个人不是听说过就是使用过某个轻量级的 Linux 发行版。大家都知道我们不断追求的是&#xff1a;占用内存少&#xff0c;配置资源要求低&#xff0c;包含一个轻量级的桌面环境&#xff08;或者窗口管理器&#xff09;&#xff0c;并且提供和其他发行版相似的…

从官网下载/处理 MNIST 数据集,并构造CNN网络训练

这里写自定义目录标题MNIST 网络 测试用1. 导入所需要的模块2. 下载 MNIST 数据集3. 读取 MNIST 数据集MNIST 网络 测试用 1. 导入所需要的模块 import sys sys.path.append(../../) from zfdplearn import fdutils, fdtorch_net, fddata import os import os.path as path i…

# 数据完整性算法在shell及python中的实践

数据完整性算法在shell及python中的实践 文章目录数据完整性算法在shell及python中的实践1 预备知识1.1 摘要算法1.2 报文&#xff08;数据&#xff09;完整性校验1.3 python byte类型字符串与普通字符串区别2 传统方法&#xff08;散列函数&#xff09;2.1 在shell中实践2.2 在…

python调试模块ipdb

1. 调试python ipdb是用来python中用以交互式debug的模块&#xff0c;可以直接利用pip安装; 其功能类似于pycharm中 python控制台&#xff0c; 而使用ipdb 的优点&#xff0c;便是直接在代码中调试&#xff0c; 避免了在python控制台&#xff0c;或者重新设置一些简单变量。…

Web前端开发--自用

第一章 1.1 时间&#xff1a;1980 人物&#xff1a;Tim Berners-Lee 地点&#xff1a;欧洲核子研究组织中最大的欧洲核子物理实验室 事件&#xff1a;与Robert Cailliau建立ENQUIRE系统 1984年&#xff0c;世界上第一个客户端浏览器&#xff08;World Wide Web&#xff09;和第…

软考高项——配置管理

配置管理配置管理配置管理6个主要活动配置项配置基线配置项的状态配置库配置库权限管理配置审计配置管理 配置管理的总线索包括&#xff1a; 1&#xff09;配置管理6个主要活动 2&#xff09;配置项 3&#xff09;配置基线 4&#xff09;配置项的状态 5&#xff09;配置库 6&a…

SAP SQVI快速报表的使用

SQVI快速报表 一、说明 对数据表进行查询通常使用SE16&#xff0c;但只限于单张表&#xff0c;对于多表联动的查询&#xff0c;则需要通过创建Query的方式&#xff0c;方法有多种&#xff0c;而SQVI是一种简洁快速的工具。SQVI全称是Quick Viewer&#xff0c;可以快速生成多表…

动态规划回文子串

647. 回文子串方法&#xff1a;双指针回文子串有长度为奇数和偶数两种&#xff0c;extend(s, i, i, n); extend(s, i, i 1, n);就分别对应长度为奇数和偶数的情况class Solution { private:int extend(const string& s, int i, int j, int n) {int res 0;while (i > 0…

前端——8.超链接标签

这篇文章&#xff0c;我们来讲一下超链接标签 目录 1.超链接标签介绍 1.1链接的分类 2.具体案例讲解 2.1外部链接 2.2 内部标签 2.3 空链接 2.4下载连接 2.5网页元素链接 2.6锚点标签 3.小结 1.超链接标签介绍 超链接标签是HTML中一个十分重要的标签&#xff0c;下…

案例18-面向对象之开门小例子

目录 一&#xff1a;背景介绍 二&#xff1a;思路&方案 1.面向过程 2.面向对象 3.面向对象(反射) 三&#xff1a;过程 1.面向过程&#xff1a;原本何老师的作用交给我了米老师来完成。 2.面向对象&#xff1a;把开门的方法完全交个何老师&#xff0c;米老师不需要有…

k8s 部署 skywalking 并持久化到es

1、k8s中安装部署 skywalking skywalking集群情况下需要保证用同一数据源&#xff0c;这里我们存储方式改为es 1.1 部署elasticsearch docker run -it -d -p 9200:9200 -p 9300:9300 -e ES_JAVA_OPTS"-Xms256m -Xmx256m" -e "discovery.typesingle-node"…

DSRC技术

DSRC(Dedicated Short Range Communication)专用短程通信 定位 是V2X领域存在的两大通信技术之一&#xff08;另一个为LTE-V2X&#xff09;。 所属技术路线 与这两大技术相对应&#xff0c;是V2X无线通信技术的两大技术路线&#xff1a; IEEE 802.11p 本是04年指定的一个通…

一文入门HTML+CSS+JS(样例后续更新)

一文入门HTMLCSSJS&#xff08;样例后续更新&#xff09;前言HTML&#xff0c;CSS和JS的关系HTMLhead元素titlelinkmetabody元素设置网页正文颜色与背景颜色添加网页背景图片设置网页链接文字颜色设置网页边框文字与段落标记普通文字的输入对文字字体的设置 font使用文字的修饰…

代码随想录刷题-数组总结篇

文章目录数组二分查找原理习题题目1思路和代码题目-2移除元素习题我的想法暴力解法双指针有序数组的平方习题暴力排序双指针长度最小的子数组习题暴力解法滑动窗口螺旋矩阵 II习题我的解法别人的解法总结数组 二分查找 本节对应代码随想录中&#xff1a;代码随想录-二分查找 …