ROS 网站发布后控件消失的map 为什么会慢慢消失?

& & 相信大家在用《ROS by example》学习ROS的过程中,基本上都是使用书本中的例程,在终端输入几行别人已经写好的代码,看看仿真效果。可是这样一来,笔者在跟着书本初略过了一遍后,却还是不知道如何通过ROS去具体操作实实在在的机器人,譬如想做导航和定位,自己的机器人该如何跟ROS这个框架结合起来,网上这方面中文教程很少,同时,英文教程里,可不足的是很多细节没有涉及到,对于初学者还是有些难度。因此,我想结合自己的开发经验来写作此教程,希望能节省大家的开发时间。
& & 笔者的机器人为两轮驱动移动机器人,前端有一个万向的支撑轮。电机驱动模块为DSP,通过串口和电脑相连。在继续下面的教程前,读者最好已经入门了ROS:看完了,按着教程建立了你自己catkin_ws工作空间,以及建立了第一个package
beginner_tutorials。并且,最好已经看完《ROS by Example 1》中的7-8章。
& & 本教程将具体涉及如下问题:
& & 系列(1):move_base发出的控制指令是什么?该如何转化为移动机器人左右轮的速度。
& & 系列(2):移动机器人的左右轮的编码器信息如何转化为ROS的/odom;
& & 系列(3):navigation的几个坐标系(/map frame , /odom frame , /base_link frame)是什么,他们之间该如何建立tf转换;如何使用move_base在一个空白的地图(blank map)上完成对实际机器人的控制,对应《ROS by Example 1》的8.3节。
控制系统的架构:
& & 关于机器人导航与定位的系统架构,在《ros by example》 chapter 7一章中介绍了控制机器人的5个层次。这里笔者按照自己的理解以及开发经验给出三个层次。
& & 最底层:机器人本身的电机驱动部分(我用的是DSP,其实最简单的51单片机都可以满足要求),该部分通过串口接收电脑端输出的左右轮期望速度,对左右轮分别进行PID控速。同时,定时采样电机码盘值,并转化为左右轮速度值通过串口上传给电脑。当然PID控速这一部分也可以放到电脑ROS端,这样的话,电脑串口输出的是直接的PWM值而不是之前的期望速度了。
& &中间通信层:电脑端和底层电机的控制通信,以及将传感器信息发布给ROS的通信。这一层主要通过串口(ROS已经集成了pyserial 用python操作这个模块进行串口控制)收集左右轮速度值,用航迹推演法将左右轮速度转化为机器人的x轴方向速度和机器人的旋转速度,然后发布/odom主题,好让ROS的相应package收到这个消息,进行机器人位置的估计。同时,这一部分还要关注ROS相应部分发出的机器人控制指令,转化为左右轮的期望速度,再通过串口传给DSP。这一层是自己写程序完成。
& &决策层:就是与导航有关的了,建立地图和定位,然后用move_base根据你发布的传感器信息做出路径规划以及机器人的速度和转向控制。这一部分为ROS相应的package已经完成,我们只需要调用即可。
& &在这个系列里,我们只关注如何用 move_base package 做出的控制对机器人进行实际控制。文章接下来的部分将按照从上到下的顺序,一个问题接一个问题的来介绍如何使用move_base控制实际机器人。
1. move_base package的系统介绍:
& & &ROS提供的move_base 包让我们能够在已建立好的地图中指定目标位置和方向后,move_base根据机器人的传感器信息控制机器人到达我们想要的目标位置。它主要功能包括:结合机器人码盘推算出的odometry信息,作出路径规划,输出前进速度和转向速度。这两个速度是根据你在配置文件里设定的最大速度和最小速度而自动作出的加减速决策。下面的白色底色方框内就是move_base的内容:
图中我们可以看到move_base package 的输入和输出。要使得它能运行起来,我们就得构建好这些输入和输出。
必要的输入:
& & & goal : 期望机器人在地图中的目标位置。
& & & tf : 各个坐标系之间的转换关系。(具体/map frame --& /odom frame ,/odom frame --& /base_link frame) & & &
& & &odom:根据机器人左右轮速度推算出的航向信息(即/odom 坐标系中机器人x,y坐标以及航向角yaw,下面会具体介绍)
& & &LaserScan:激光传感器的信息,用于定位。(在这个系列教程中,我们没有用到这个激光信息,而是在一个假的空白地图上对机器人进行控制,并假定/map坐标系和/odom坐标系完全重合,在后面会有关于这两个坐标系的介绍)
& & &cmd_vel:在cmd_vel这个主题上发布Twist消息,这个消息包含的就是机器人的期望前进速度和转向速度。
& & &&再整理下思路:move_base收到goal以后,将目标goal通过基于actionlib的client(客户端)向服务器发送,服务器根据你的tf关系以及发布的odom消息不断反馈机器人的状态(feedbackcall)到客户端,
让move_base做路径规划和控制twist。
& & &知道了move_base的这些外围消息接口以后,move_base的运行还需要一些内部的配置参数,如机器人的最大最小速度,已经路径规划时的最大转弯半径等等,这些参数配置在《Ros by Example 1》的8.1.2节有详细介绍。。
& & &至此,我们已经熟悉了move_base的各种接口,它订阅了什么消息,会发布什么消息都已经清楚了。因此让move_base控制实际的机器人最主要的就是要解决实际机器人如何发布这些消息给move_base,以及如何接受move_base发出的消息。
2. Twist 消息转化为机器人左右轮期望速度。
& & 首先,假设move_base能够正常工作了,它将把控制命令Twist发布到cmd_vel这个主题上。我们现在来解决如何利用这个Twist消息来对机器人进行控制。
& & 在ROS by example 一书中的第七章开头就规定了机器人自身的坐标系系统,如下图。注意两个坐标系的建立都是右手坐标系,左图中的右手就是机器人本身,x轴就是前进的方向,垂直于两轮之间的轴连线,Y轴就是两个轮之间的轴连线。右图表示机器人的旋转坐标系,大拇指指向Z轴,逆时针方向为正值。
& & & & & &
清楚了坐标系以后,再来看看Twist这个消息里包含的是什么东西。
&&&&&& 使用ctrl + alt + t 打开一个新的终端以后,输入如下命令,就可以查看Twist的消息类型了。
rosmsg show geometry_msgs/Twist
其中linear 的x就是代表前进方向的速度,单位为m/s。angular 的z就代表机器人的绕中心旋转的角速度,单位为 弧度/s (rad/s)。
& & &因此,我们只要在自己写的中间通信层程序中订阅cmd_twist这个主题(topic),就可以收到move_base发出的命令了。 下面给出一个如何订阅cmd_twist主题的demo。
&&&& 首先在你之前建立的package的scripts文件夹下,笔者的是 beginner_tutorials/scripts文件夹,将下列代码复制进去,保存为your_filename.py。保存以后记得要chmod一下,让这个文件成为可执行的节点,具体操作如下。
roscd beginner_tutorials
cd scripts
chmod +x lis_cmdvel.py
程序代码:
#!/usr/bin/env python
#refernence: http://answers.ros.org/question/29706/twist-message-example-and-cmd_vel/
roslib.load_manifest('beginner_tutorials')
import rospy
import tf.transformations
from geometry_msgs.msg import Twist
def callback(msg):
rospy.loginfo(&Received a /cmd_vel message!&)
rospy.loginfo(&Linear Components: [%f, %f, %f]&%(msg.linear.x, msg.linear.y, msg.linear.z))
rospy.loginfo(&Angular Components: [%f, %f, %f]&%(msg.angular.x, msg.angular.y, msg.angular.z))
# Do velocity processing here:
# Use the kinematics of your robot to map linear and angular velocities into motor commands
# Then set your wheel speeds (using wheel_left and wheel_right as examples)
wheel_left.set_speed(v_l)
wheel_right.set_speed(v_r)
def listener():
rospy.init_node('cmd_vel_listener')
rospy.Subscriber(&/cmd_vel&, Twist, callback)#/cmd_vel
rospy.spin()
if __name__ == '__main__':
listener()
执行这些操作以后,这个文件就可以用rosrun指令执行了。
rosrun beginner_tutorials lis_cmdvel.py
&&&&&& 注意这个demo里的每当有Twist消息时,就会调用callback这个函数,callback这个函数里就是我们要处理的内容。这里只是简单的打印收到的消息,还没有对消息进行处理。
& & & 机器人期望的前进速度linear.x和转弯速度angular.z都由move_base输出了,那么如何将他们转化成机器人左右轮的期望速度呢?关于如何转化为左右轮的期望速度,我先贴出自己程序中的源代码部分,下面这三个函数是一个属于同一个类:
def callback(self,msg ):
cmd_twist_rotation =
msg.angular.z #
cmd_twist_x
= msg.linear.x
cmd_twist_y =
msg.linear.y #这个一般为0
#将twist消息转化为左右轮各自的期望速度
wheelspeed = self.odom_to_speed(cmd_twist_x, cmd_twist_y,cmd_twist_rotation)
print 'msg:', msg
#打印得到的twist消息
print wheelspeed
#打印转化后的速度
#蓝牙串口发送到DSP
wheelspeed[0]左轮速度, wheelspeed[1]右轮速度
self.blue_tooth_send([wheelspeed[0], self.speed_kp, self.speed_ki,
wheelspeed[1]])
def odom_to_speed(self, cmd_twist_x =0, cmd_twist_y=0,cmd_twist_rotation=0):
'一般情况下,linear_y = 0 所以只需关注twist.linear.x 和 twist.angle.z的转换'
#这部分本来还有一段,关于twist.linear.y不为0时,如何转化的程序,Lz自己写的,实际可运行,但是不知道是否正确,所以这里删掉了。
cent_speed = cmd_twist_x
#前进的速度,即两轮的中心速度
#将 指定的转速twist.angular.z 转化为左右轮的差速
yawrate2 = self.yawrate_to_speed(cmd_twist_rotation)
Lwheelspeed = cent_speed - yawrate2/2
Rwheelspeed = cent_speed + yawrate2/2
return Lwheelspeed, Rwheelspeed
def yawrate_to_speed(self, yawrate):
if yawrate & 0:
theta_to_speed = 0.0077 #右转系数
theta_to_speed = 0.0076
#yawrate :rad/s *0.02表示 20ms内应该转多少弧度,/0.0076是把 要转的弧度转化为左右轮速度差
x = (yawrate * 0.02) / theta_to_speed
& & & 这一段程序里最主要的是如何将指定的转速转化为两轮的差速。主程序中订阅了cmd_vel主题,一旦收到move_base发出的twist消息,就调用callback函数进行转化。如果linear.y 不为0,说明小车要沿着y轴运动,这会导致两轮的差速,但是对于两轮控制的移动机器人,twist.linear.y = 0 是在move_base的配置文件base_local_planner_params.yaml中有明确指定,所以不需关注linear.y的转换:
max_vel_y = 0.0 #zero for a differential drive robot
min_vel_y = 0.0
也就是只要关注前进速度linear.x和旋转速度angular.z的转换。
& & & &在直线行驶时,前进速度linear.x就是左右轮的期望速度。最主要的是将转速转化为左右轮的差速,一个轮子转的快,一个轮子转的慢,就有了转速。
& & & &下面介绍如何将指定的转速转化为两轮差速:
& & & &1.dsp采样的是单位时间内的码盘值,将码盘值转化为左右轮速度值后(Lwheelspeed,Rwheelspeed)通过串口发送给电脑端。
& & & &2.关于航迹推演(Odometry) 的公式中有一个关于如何有左右轮差速转化为旋转速度的计算公式。即yaw_rate = (Rwheelspeed - Lwheelspeed) / d .其中d为两轮间的间距,得到的转速单位rad/s。或者,。直接用这个公式可以计算,但是在测量这个公式中的d的时候有测量误差。因此,楼主这里采用的拟合的方法得到这个差速到转速之间的转换系数。具体操作如下:
& & & &先从0度开始逆时钟旋转小车(角速度为正),分别记下转到pi/2,pi,pi*3/2,2pi时两轮差速的累计值,(即右轮速度减去左轮速度的累计值)。思路是:两轮差速乘以系数为转速,两轮差速累计值乘以系数就是旋转的角度。因此多次记下这些数据后,拟合就能得到得到特定的差速值到对应角度之间的转换关系。
& & & &如楼主的数据如下:
& & & & & 角度 & & & & & & & & pi/2 & & & & &pi & & & & & & 3/2*pi & & & 2*pi
& & & &差速累计和 & & &&209.21& & &&415 & & & & &620.54 & &&825.6
& & & & & & & & & & & & & & & &&208.8 & & &&414.1 & & & 611.49 & &&812.39
由于是线性关系,我们进行拟合以后得到这个转换系数为0.0077,拟合曲线如图,在实际操作中我记录了5组数据:
& & & &顺时针也可以采用这个系数,但是笔者,了防止左右轮机械上的差异导致这个系数不同,对顺时针也单独拟合了一次,得到0.0076。
右轮减去左轮的差速转化为角速度或者角度的系数有了,反过来,就可以将指定的旋转速度得到左右轮差速。
& & & 将指定的转速twist.angular.z * 0.02得到一个速度控制周期内(DSP底层设定的速度采样时间为20ms)应该旋转的角度量,这个角度量除以前面的系数就得到了单位控制周期内两轮之间速度的差异值。这就是上面程序中yawrate_to_speed()函数的计算思路。最后将这个速度的差异值/2,分别添加在中心速度上就分别得到了左右轮的期望速度。
& & & &现在完成了从cmd_vel twist 发送到电机这一部分的程序,在下一篇博客中,将介绍如何将介绍如何将电机左右轮的速度发布出来,让move_base接收到。
(转载请注明作者和出处:&未经允许请勿用于商业用途)
本文已收录于以下专栏:
相关文章推荐
在前一节中,采用gmapping包实现了机器人工作环境的地图构建,这一节将利用amcl包和move_base包实现移动机器人的自主导航。至此已完成整个移动机器人导航仿真的工作,完整代码见:http:/...
本文分析move base 的配置文件,从配置文件设置的角度,可以更清晰的把握move base对于costmap2D,global planner,local planner的调用关系。
这里采用...
初学ROS的,接触到的一部分就是tf坐标变换,下面我介绍下我的理解。
最常用的就是map,odom,base_link,base_laser坐标系,这也是开始接触gmapping的一些坐标系。
在上一篇的博客中,我们一起学习了ROS定位于导航的总体框架,这一篇我们主要研究其中最重要的move_base包。
在理解了move_base的基础上,我们开始机器人的定位与导航。gmaping包是用来生成地图的,需要使用实际的机器人获取激光或者深度数据,所以我们先在已有的地图上进行导航与定位的仿真。
     ...
导航与定位是机器人研究中的重要部分。
        一般机器人在陌生的环境下需要使用激光传感器(或者深度传感器转换成激光数据),先进行地图建模,然后在根据建立的地图进行导航、定位。在ROS中也有很多...
ROS 学习move_base包
ROS下使用Hokuyo和AMCL进行P3dx平台导航的实现
       最近一段时间使用AMCL package进行了小车导航和避障,主要参考ROS官网上的相关教程。这里主要讲述如何使用navi...
关于amclamcl的英文全称是adaptive Monte Carlo localization,其实就是蒙特卡洛定位方法的一种升级版,使用自适应的KLD方法来更新粒子,这里不再多说(主要我也不熟)...
他的最新文章
讲师:王哲涵
讲师:王渊命
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)以下大部分内容参考自
ros_by_example_hydro_volume_1.pdf
local costmap 是怎么生成的?跟三维点云有什么关系?
global costmap在没有全局地图下怎么办?
要实现标题所述功能:
需要配置local costmap和global costmap
在move_base里默认用到的costmap(这一点在论文:ROS Navigation: Concepts and Tutorial 3.6最后一段节有说到,而且说明了怎么配置layered costmap)
是monolithic costmap这个在论文:
Layered Costmaps for Context-Sensitive Navigation中有详细描述
layered costmap就是一层层不同地图重叠在一起
在论文:Layered Costmaps for Context-Sensitive Navigation里描述的很详细,主要是讲了layered costmap这么进行更新和每一层都是什么
其中master layer是aggregate layer where path planning occurs
对于local costmap, 如voxel layer就是其中一层,他可以判断比如一个桌子,激光会扫到桌子腿,认为可以过去,但是机器人还是会碰到桌子,因为激光扫不到桌面,
这个例子是在讲voxel grid论文里看到的:
The Office Marathon:Robust Navigation in an Indoor Office Environment
然后在layered costmap update时就会在master上三维向二维投影出桌面,让机器人不走这条路径。
local costmap和global costmap采用地图方式不一样
global costmap采用静态地图(通过slam产生的),这里只是涉及避障所以可以将静态地图设置为空白地图,将地图世界坐标/map和定位采用的坐标系tf通过static_transform固定在一起这个可以看
/hong2016/p/6831484.html
如果之后要采用静态地图再修改
/hong2016/p/6831484.html
&中讲到的llaunch file就好了
&&&node name="map_server"&pkg="map_server"&type="map_server"&args="$(find pioneer_zed)/maps/blank_map.yaml"&/&
下面这段话摘自ros bt example volume1&8.2 Testing move_base in the ArbotiX Simulator 第二段
Finally, since we are using a blank map and our simulated robot has no sensors, the&robot cannot use scan data for localization. Instead, we simply set a static identity
transform to tie the robot's odometry frame to the map frame which essentially assumesthat the odometry is perfect.
local costmap不采用静态地图而是采用rolling window
you're right - I figured that out, this thread is a bit outdated. As for your question - the normal trend is to use rolling_window: true for local costmap since by definition that map is used for collision checking with the immediate robot.
For the tutorial, only the global costmap uses an a priori map, so the static_map parameter is set to true. The local costmap, however, only uses local sensor information to build an obstacle map, so static_map is set to false.
rolling window是
"Rolling window" means that you do not use the costmap to represent your complete environment, but only to represent your local surroundings (e.g. 5m x 5m around your robot). The costmap will then move along with your robot and will be updated by incoming sensor data.
You have to make sure that you also set the size of the rolling window appropriately:
rolling_window: true
width: 15.0
height: 15.0
resolution: 0.05
origin_x: 0.0
origin_y: 0.0
When using a static map, the width and height are determined using the loaded image. In a rolling window, you must manually specify these parameters for your robot. Just know that it places your robot in the center of the rolling window, so the robot can only see things that are&height/2&meters away.
阅读(...) 评论()深入探究ros::init() - 为程序员服务
深入探究ros::init()
ROS是Willow Garage公司与Stanford University合作推出的开源机器人操作系统(Robot Operation System), Willow Garage也在此系统之上推出了Personal Robot 2(PR2), PR2由于出色的抓取在家庭服务机器人里可谓相当吊! 重要的是ROS已经被大多数科研机构接受并在其上进行研究和开发. 感觉Willow Garage在下一盘很大的棋, 有点安卓系统在手机上的赶脚...
用ROS也有大半年了, 但有个问题一直困扰我, 就是ROS每个Node在建立时第一步要做的就是
ros::init()
一个node, 但是在C++的API里边, ros一定要加入argc和argv两个参数. 但是这两个参数又从来没用过,而且在运行的时候ROS一般又不用
XX [Options]
这种方式, 那参数到底是谁传进去的, 传进去到底做了什么呢?
今天仔细翻了一下API, 算是把这个问题大概理解清楚了.
void ros::init(int& argc, char** argv, const std::string& name, unit32_t options=0);
void ros::init(const M_string& remappings, const std::string& name, unit32_t options=0);
void ros::init(const VP_string& remappings_args, const std::string& name, unit32_t options=0);
谁传入参数? 传入参数做什么?
先说第一个, 官方说argc, argv是用来解析来自命令行remapping arguments, 而所谓remapping arguments就是在调用
rosrun package_name node_name param_name:=param_value
param:=param_value
部分. OK, 解决了第一个问题: 这些参数是从命令行的remapping arguments中传入的.
那么传入参数是有一定格式的, ROS wiki在
对Remappig Arguments做了详细解释. 在看这个之前, 我们先来回顾一个Resources Names这个概念.
Resource Names
Graph Resource Names
图资源命名提供了ROS计算图中资源的分层命名结构, 这些计算图包括 Nodes/Parameters/Topics/Services等.
ROS中图资源命名包括四种格式: base/relative/global/private, 大概是这个样子:
relative/name
/global/name
~private/name
也就是说直接名字开头的不以/开头的表示base names, 这种方式是最常用的, 因为可以方便的remapping到其他namespace.
而以/开头的表示global names, 这种方式不推荐用, 因为这样写出来的代码不太方便移植.
以~开头的则是private names, 这个目前没用到过, 不太理解.
Package Resource Names
Package命名就是比如
std_msgs/String
就是一个package name. 这里就不详细说了.
传入参数做什么?
回顾完资源命名方法, 就可以来说传入参数到底是干什么的了.
param_name:=param_value
传入的参数, 就是为了remap resource name, 其格式如下:
Node Namespace
Remapping Argument
Matching Names
Final Resolved Name
foo, /baz/foo
/foo:=/a/b/c/bar
/a/b/c/bar
用两句话概括就是: 不具名的命名空间中, 匹配的是param_name和/param_name, Resolve是直接加/替换; 具名命名空间中, 匹配的是/namespace/param_name, Resolve分两种: 1) 替换内容中无命名空间, 则加Node namespace替换, 2)替换内容中有命名空间, 则直接替换.
最后回过头看下函数原型: 一三函数分别将argv 和 VP_String类型的参数转化为M_String, 从而调用第二个函数, 最终用这些参数初始化了master和network.
其中M_String是typedef的
map&string, string&
, 而VP_String则是
vector&pair&string, string&&
Make A Difference
原文地址:, 感谢原作者分享。
您可能感兴趣的代码}

我要回帖

更多关于 形容慢慢消失的成语 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信