导航与定位是机器人研究中的重要部分。
一般机器人在陌生的环境下需要使用激光传感器(或者深度传感器转换成激光数据),先进行地图建模,然后在根据建立的地图进行导航、定位。在ROS中也有很多完善的包可以直接使用。
在ROS中,进行导航需要使用到的三个包是:
(1) move_base:根据参照的消息进行路径规划,使移动机器人到达指定的位置;
(2) gmapping:根据激光数据(或者深度数据模拟的激光数据)建立地图;
(3) amcl:根据已经有的地图进行定位。
参考链接:http://www.ros.org/wiki/navigation/Tutorials/RobotSetup
http://www.ros.org/wiki/navigation/Tutorials
整体导航包的格局如下图所示:
其中白色框内的是ROS已经为我们准备好的必须使用的组件,灰色框内的是ROS中可选的组件,蓝色的是用户需要提供的机器人平台上的组件。
1、sensor transforms
其中涉及到使用tf进行传感器坐标的转换,因为我们使用的机器人的控制中心不一定是在传感器上,所以要把传感器的数据转换成在控制中心上的坐标信息。如下图所示,传感器获取的数据是在base_laser的坐标系统中的,但是我们控制的时候是以base_link为中心,所以要根据两者的位置关心进行坐标的变换。
变换的过程不需要我们自己处理,只需要将base_laser和base_link两者之间的位置关系告诉tf,就可以自动转换了。具体的实现可以参见:http://blog.csdn.net/hcx25909/article/details/9255001。
2、sensor sources
这里是机器人导航传感器数据输入,一般只有两种:
(1) 激光传感器数据
(2) 点云数据
具体实现见:http://www.ros.org/wiki/navigation/Tutorials/RobotSetup/Sensors
3、odometry source
机器人的导航需要输入里程计的数据(tf,nav_msgs/Odometry_message),具体实现见:
http://www.ros.org/wiki/navigation/Tutorials/RobotSetup/Odom
4、base controller
在导航过程中,该部分负责将之前得出来的数据转封装成具体的线速度和转向角度信息(Twist),并且发布给硬件平台。
5、map_server
在导航过程中,地图并不是必须的,此时相当于是在一个无限大的空地上进行导航,并没有任何障碍物。但是考虑到实际情况,在我们使用导航的过程中还是要一个地图的。
具体实现见:http://www.ros.org/wiki/slam_gmapping/Tutorials/MappingFromLoggedData
在ROS的导航中,costmap_2d这个包主要负责根据传感器的信息建立和更新二维或三维的地图。ROS的地图(costmap)采用网格(grid)的形式,每个网格的值从0~255,分为三种状态:占用(有障碍物)、无用(空闲的)、未知。具体状态和值的对应关系如下:
上图共分为五个部分:(下面的红色框图是机器人的轮廓,旁边的黑框是上图的映射位置)
(1) Lethal(致命的):机器人的中心与该网格的中心重合,此时机器人必然与障碍物冲突。
(2) Inscribed(内切):网格的外切圆与机器人的轮廓内切,此时机器人也必然与障碍物冲突。
(3) Possibly circumscribed(外切):网格的外切圆与机器人的轮廓外切,此时机器人相当于靠在障碍物附近,所以不一定冲突。
(4) Freespace(自由空间):没有障碍物的空间。
(5) Unknown(未知):未知的空间。
具体可见:http://www.ros.org/wiki/costmap_2d
评论列表(48条)
您好,请问导航不需要地图是指在launch文件里不加载地图还是要加载空白地图
可以不加载地图
谢谢,那请问不加载的话需要修改别的么,我遇到这样的警告且无法导航。Timed out waiting for transform from base_footprint to map to become available before running costmap, tf error: canTransform: target_frame map does not exist.. canTransform returned after 0.1 timeout was 0.1.
map坐标系还是得有的,可以建立一个odom到map的静态变换
老师您好,我想问一下如何在线实时地得到gmapping建立的地图的各个栅格信息,并实时地处理地图呢?
可以订阅map数据的话题拿到实时数据
请教一下,我在保存地图后,使用已有地图进行导航和定位。然后再次启动机器人(turtlebot3),发现机器人在世界坐标系map中的坐标是0,0,怎么设置和编程才能正确反映机器人在map中的坐标,而不是每次启动,机器人的坐标都是0,0?
和map匹配的一个yaml文件里可以设置机器人的初始位姿
古月老师,我在ros机器人开发实践这边书中尝试,看到roslaunch mbot_gazebo mbot_laser_nav_gazebo.launch 启动gazebo,但是我的虚拟机在启动后出现:
[gazebo-2] process has died [pid 10971, exit code 139, cmd /opt/ros/kinetic/lib/gazebo_ros/gzserver -e ode /home/riki/Desktop/slam_ws/src/mbot_gazebo/worlds/cloister.world __name:=gazebo __log:=/home/riki/.ros/log/fa10831c-c4a0-11e9-bf24-000c299a7790/gazebo-2.log].
log file: /home/riki/.ros/log/fa10831c-c4a0-11e9-bf24-000c299a7790/gazebo-2*.log这样的错误,我尝试从百度寻找答案,但是没有一个合适的说法,请问这种问题如何解决。
古月老师,您好。我使用rplidar a2 + rb1_base做2D Nav Goal可以正常运行,但是我做机器人的自主探索SLAM时,在mrobot_navigation/scripts/exploring_slam.py文件中修改了一些点的坐标。rosrun exploring.py文件过程中,不论目标点是几号,机器人都会往同一个方向上移动,同时避障功能也失效。请问这个时什么引起的?我应该如何去改正?
调试下程序,确定发送的目标点是不一样的么
古月老师你好,我想问一下/move_base_node/NavfnROS/plan的作用是什么,他是属于global_planner吗?我程序代码中没有找到move_base中这个话题发布的位置,我应该如何使用它呢?
move_base是action通信接口,参考《ROS机器人开发实践》中有调用这个action的例子
古月大大你好:
我在用hector与move_base搭配使用,但是之后move_base的导航功能无法正常实现,请问是什么原因呢?与gmapping一起就完全没问题。
具体原因不太清楚,看下有没有正常发布里程计odom消息吧
古月大大你好!
我有个问题,gmapping的scan不是laserscan吗。 kinect也可以使用这个包,照理说使用kinect不是要把深度信息转换成laser吗,为什么我在gmapping的包里没有看到有关转换的呢?
是的,kinect需要将点云转换成scan数据,有个depthimage_to_laserscan包可以实现这个功能
大神你好,我在实际使用rplidar+gmapping建图的时候地图更新速度非常慢,大概得有十来秒才更新一次,而我在配置gmapping的配置文件时设置了map_update_interval=1,请问这是什么原因呢?
有可能其他参数有影响吧
古月大大:
我在用gmapping做gazebo仿真,打开rviz时候,Fixed Frame :map. 但是Map的反馈是No map received,rviz中是黑色一片和小车的一个白点,请问这个您有什么建议吗?因为我用的是您之前的教程代码,不知道问题处在哪儿。
可能是没启动gmapping节点,用tf工具看下中间是不是有哪个tf断掉了,或者终端中有没有错误提示
我也遇到了同样的问题,请问你解决了嘛?
我也遇到这个问题了 不是代码的问题 是gmapping的问题 但我不知道怎么重装gmapping,就重新装了系统(虚拟机里),就运行正常了
胡大神您好,有个问题请教下。map_server里的分辨率设置为0.05,利用导航包可以快速准确的到达目标位置;但是我将分辨率改为更大后,如0.25,问题出现了,更改后再调用导航包老是出现到达不了目标位置,在目标位置不停地打转。您能帮忙分析下这是什么造成的么?万分感谢!
应该是允许误差小于地图分辨率了,修改一下配置文件中的允许误差
试过了,改大了允许误差还是有问题,老是在目标位置附近打转,到达不了目标位置
看下还有没有其他关联参数,都尝试修改一下。move_base能够实现基础功能,但是效果没办法尽善尽美,只能通过调试参数来优化。要进一步完善,需要自己做代码级的优化和修改。
古月大大您好!
昨天在您這看見可以透過訂閱/move_base/NavFnROS/plan 取得
我擷取的資料型態是data.poses 但是他的資料是一整個矩陣資料
可不可以只取得 其中的xy
像是data.poses.pose.position.x 這樣子的感覺
修正 是取得 透過導航包 規劃出來的 路徑資料
可以的,通过data.poses[0].pose.position.x这样的形式是可以读取其中的x、y的
原來是這樣啊! 感謝古月大大 真是博學多聞呢,已經順利取出!
古大,我在跟着slam_gmapping里生成地图的教程走的时候,遇到了两个问题。
1:如果我将use_sim_time设为true,用rosbag记录的时候会提示我“/use_sim_time set to true and no clock published. Still waiting for valid time…”,然后就无法记录。你知道是什么原因吗?谷歌之后有人说将use_sim_time 设为false,我试过之后又出现了第2个问题。
2:将use_sim_time设为false之后,bag文件可以生成也可以回放,但是在回放数据的时候slam_gmapping节点一直提示“TF_OLD_DATA ignoring data from past for frame laser at time 1.52415e+09 according to authority unknown_publisher”,最后也无法生成地图,我在tf中用的时间戳都是ros::Time::now(),你知道这是什么问题吗?
这两个问题我都谷歌过了,但还是没发现问题在哪里,求古大指教
好吧,问题解决了,还是自己的问题。首先在用rosbag记录的时候需要将use_sim_time设置为false才能记录,记录完成之后需要再将use_sim_time设置为true然后再运行slam_gmapping才能完成mapper的初始化,然后按照步骤就能生成地图了。而且我在记录bag的时候还犯了一个严重的错误,就是把雷达发布的话题写错了(把scan给记成了laser_scan,==|||),还是查看了一下bag文件才发现的。
不过我还是有个问题想请教一下古大,这个use_sim_time到底是什么意思?网上解释说是设置为true是仿真时间,false是wall time,但我还是不太能理解两者的区别,也不清楚到底什么时候该设为true,什么时候该设为false,古大能帮忙解答我的这些疑惑吗?先行谢过了
use_sim_time是配置是否使用仿真时间,后一个错误是说TF数据的时间戳相差太大
古老师,您好!我也遇到了上面类似的问题,按照他的解决办法还是解决不了,下面时候我的问题提示,麻烦问一下,这个怎么解决?
Warning: TF_OLD_DATA ignoring data from the past for frame base_footprint at time 1.5473e+09 according to authority unknown_publisher
Possible reasons are listed at http://wiki.ros.org/tf/Errors%20explained
at line 277 in /tmp/binarydeb/ros-kinetic-tf2-0.5.20/src/buffer_core.cpp
不确定原因,参考下后边的链接:http://wiki.ros.org/tf/Errors%20explained
at line 277 in /tmp/binarydeb/ros-kinetic-tf2-0.5.20/src/buffer_core.cpp
古老师,麻烦问一下,我创建urdf模型的时候,base_footprint有必要吗?上面那个错误,我把odom发布部分child_frame_id =base_footprint换成child_frame_id =base_link之后就不报这个错误了。还有就是我做好urdf模型就不用写tf相关程序了吧?
base_footprint不是必须的,urdf中的link会自动放到tf中,不用单独写
你好我也碰到这个问题了,请问你解决了吗
你好,我也遇到这个问题了,然后按照上面的操作了一通,还是没有解决问题。
古老师,您好,我将 ROS BY EXAMPLES书上的nav_square.py机器人走正方形路线的程序修改了。具体的修改思路是:机器人每行走1m停下来,逆时针旋转90度,采集待检测的点信息,然后再逆时针旋转360度至前进方向。一共执行上面的过程6次,最后将上面的整个步骤循环四次,这样走出的轨迹应该是一个边长为6m的正方形轨迹,但是实际上机器人运行到正方行第二条边上时,旋转方向已经不对了,请问这个是怎么回事?是由于只用里程计的效果吗?
下面是我的程序
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
from geometry_msgs.msg import Twist, Point, Quaternion
import tf
from rbx1_nav.transform_utils import quat_to_angle, normalize_angle
from math import radians, copysign, sqrt, pow, pi
class RunningCar():
def __init__(self):
# Give the node a name
rospy.init_node(‘nav_square’, anonymous=False)
# Set rospy to execute a shutdown function when terminating the script
rospy.on_shutdown(self.shutdown)
# How fast will we check the odometry values?
rate = 20
# Set the equivalent ROS rate variable
r = rospy.Rate(rate)
# Set the parameters for the target square
#goal_distance = rospy.get_param(“~goal_distance”, 2.0) # meters
goal_distance1 = rospy.get_param(“~goal_distance1”, 1.0)
goal_angle1 = rospy.get_param(“~goal_angle1”, radians(0))
goal_angle2 = rospy.get_param(“~goal_angle2”, radians(90))
goal_angle3 = rospy.get_param(“~goal_angle3”, radians(360))
goal_angle = rospy.get_param(“~goal_angle”, radians(270))
goal_angle4 = rospy.get_param(“~goal_angle4”, radians(0))
goal_angle5 = rospy.get_param(“~goal_angle5”, radians(270))
goal_angle6 = rospy.get_param(“~goal_angle6”, radians(180))
linear_speed = rospy.get_param(“~linear_speed”, 0.2) # meters per second
angular_speed = rospy.get_param(“~angular_speed”, 0.7) # radians per second
angular_tolerance = rospy.get_param(“~angular_tolerance”, radians(2)) # degrees to radians
# Publisher to control the robot’s speed
self.cmd_vel = rospy.Publisher(‘/cmd_vel’, Twist)
# The base frame is base_footprint for the TurtleBot but base_link for Pi Robot
self.base_frame = rospy.get_param(‘~base_frame’, ‘/base_link’)
# The odom frame is usually just /odom
self.odom_frame = rospy.get_param(‘~odom_frame’, ‘/odom’)
# Initialize the tf listener
self.tf_listener = tf.TransformListener()
# Give tf some time to fill its buffer
rospy.sleep(2)
# Set the odom frame
self.odom_frame = ‘/odom’
# Find out if the robot uses /base_link or /base_footprint
try:
self.tf_listener.waitForTransform(self.odom_frame, ‘/base_footprint’, rospy.Time(), rospy.Duration(1.0))
self.base_frame = ‘/base_footprint’
except (tf.Exception, tf.ConnectivityException, tf.LookupException):
try:
self.tf_listener.waitForTransform(self.odom_frame, ‘/base_link’, rospy.Time(), rospy.Duration(1.0))
self.base_frame = ‘/base_link’
except (tf.Exception, tf.ConnectivityException, tf.LookupException):
rospy.loginfo(“Cannot find transform between /odom and /base_link or /base_footprint”)
rospy.signal_shutdown(“tf Exception”)
# Initialize the position variable as a Point type
position = Point()
# Set the movement command to a rotation
move_cmd = Twist()
move_cmd.angular.z = angular_speed
# Track the last angle measured
last_angle = rotation = 0
# Track how far we have turned
turn_angle = 0
# Begin the rotation
while abs(turn_angle + angular_tolerance) < abs(goal_angle1) and not rospy.is_shutdown():
# Publish the Twist message and sleep 1 cycle
self.cmd_vel.publish(move_cmd)
r.sleep()
# Get the current rotation
(position, rotation) = self.get_odom()
# Compute the amount of rotation since the last lopp
delta_angle = normalize_angle(rotation – last_angle)
turn_angle += delta_angle
last_angle = rotation
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(1.0)
for n in range(4):
# 第一段走2m的程序
i = 1
while i < 3:
move_cmd = Twist()
move_cmd.linear.x = linear_speed
(position, rotation) = self.get_odom()
x_start = position.x
y_start = position.y
distance1 = 0
while distance1 < goal_distance1 and not rospy.is_shutdown():
self.cmd_vel.publish(move_cmd)
r.sleep()
(position, rotation) = self.get_odom()
distance1 = sqrt(pow((position.x – x_start), 2) +
pow((position.y – y_start), 2))
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(2.0)
# 检测数据程序时间设为2s
move_cmd.angular.z = angular_speed
last_angle = rotation = 0
turn_angle = 0
while abs(turn_angle + angular_tolerance) < abs(goal_angle2) and not rospy.is_shutdown():
self.cmd_vel.publish(move_cmd)
r.sleep()
(position, rotation) = self.get_odom()
delta_angle = normalize_angle(rotation – last_angle)
turn_angle += delta_angle
last_angle = rotation
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(2.0)
# 方向复位时间
move_cmd.angular.z = angular_speed
last_angle = rotation = 0
turn_angle = 0
while abs(turn_angle + angular_tolerance) < abs(goal_angle3) and not rospy.is_shutdown():
self.cmd_vel.publish(move_cmd)
r.sleep()
(position, rotation) = self.get_odom()
delta_angle = normalize_angle(rotation – last_angle)
turn_angle += delta_angle
last_angle = rotation
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(1.0)
i += 1
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(1.0)
# 到第一个角点处转向的程序
move_cmd.angular.z = angular_speed
last_angle = rotation = 0
turn_angle = 0
while abs(turn_angle + angular_tolerance) < abs(goal_angle) and not rospy.is_shutdown():
self.cmd_vel.publish(move_cmd)
r.sleep()
(position, rotation) = self.get_odom()
delta_angle = normalize_angle(rotation – last_angle)
turn_angle += delta_angle
last_angle = rotation
move_cmd = Twist()
self.cmd_vel.publish(move_cmd)
rospy.sleep(1.0)
# Stop the robot when we are done
self.cmd_vel.publish(Twist())
def get_odom(self):
# Get the current transform between the odom and base frames
try:
(trans, rot) = self.tf_listener.lookupTransform(self.odom_frame, self.base_frame, rospy.Time(0))
except (tf.Exception, tf.ConnectivityException, tf.LookupException):
rospy.loginfo("TF Exception")
return
return (Point(*trans), quat_to_angle(Quaternion(*rot)))
def shutdown(self):
# Always stop the robot when shutting down the node
rospy.loginfo("Stopping the robot…")
self.cmd_vel.publish(Twist())
rospy.sleep(1)
if __name__ == '__main__':
try:
RunningCar()
except rospy.ROSInterruptException:
rospy.loginfo("Navigation terminated.")
里程计会产生累计误差,尤其是旋转,误差很大
之前我以为是我的程序逻辑问题,现在确定是rviz中只利用self.get_odom()来获得位置和方向不准确引起的。那请问胡老师我可以在rviz中添加IMU传感器,来获得机器人比较准确的位置和方向吗?我看了相关论文,一般利用里程计+IMU来获得机器人的位置和方向。谢谢老师!
这是一种提高精度的方法,rviz主要是做显示,如果要添加IMU+odom,需要在gazebo仿真或使用真实机器人,只用rviz不行。
我上午在gazebo中的model没有找到IMU传感器,只是找到了laser和立体摄像头的模型,请问gazebo中能添加IMU传感器吗?
可以的,参考这里:http://gazebosim.org/tutorials?tut=ros_gzplugins
月哥, 我想修改cost map为一个长方形,机器人位于某”宽“(对应的是长)边中间,机器人的前面是一大片costmap面积,当机器人旋转时costmap随之旋转。 请问实现这样的功能有什么问题吗?
我的目的是想减少costmap的面积,以提高性能。
这个我不太清楚