1.导航框架
在总体框架图中可以看到,move_base提供了ROS导航的配置、运行、交互接口,它主要包括两个部分:
(1) 全局路径规划(global planner):根据给定的目标位置进行总体路径的规划;
(2) 本地实时规划(local planner):根据附近的障碍物进行躲避路线规划。
- 全局路径规划(global planner)
在ROS的导航中,首先会通过全局路径规划,计算出机器人到目标位置的全局路线。这一功能是navfn这个包实现的。
navfn通过Dijkstra最优路径的算法,计算costmap上的最小花费路径,作为机器人的全局路线。将来在算法上应该还会加入A*算法。
- 本地实时规划(local planner)
本地的实时规划是利用base_local_planner包实现的。该包使用Trajectory Rollout 和Dynamic Window approaches算法计算机器人每个周期内应该行驶的速度和角度(dx,dy,dtheta velocities)。
base_local_planner这个包通过地图数据,通过算法搜索到达目标的多条路经,利用一些评价标准(是否会撞击障碍物,所需要的时间等等)选取最优的路径,并且计算所需要的实时速度和角度。
其中,Trajectory Rollout 和Dynamic Window approaches算法的主要思路如下:
(1) 采样机器人当前的状态(dx,dy,dtheta);
(2) 针对每个采样的速度,计算机器人以该速度行驶一段时间后的状态,得出一条行驶的路线。
(3) 利用一些评价标准为多条路线打分。
(4) 根据打分,选择最优路径。
(5) 重复上面过程。
2.安装rbx1 package
- 安装gmapping: 由激光雷达数据生成地图(或者深度相机数据)
git clone https://github.com/ros-perception/slam_gmapping.git
- 安装amcl:在已有的地图内定位机器人
git clone https://github.com/ros-planning/navigation.git
- 其他可能用到的包:提示没有哪个就装哪个
sudo apt-get install ros-indigo-turtlebot-bringup \ ros-indigo-turtlebot-create-desktop ros-indigo-openni-* \ ros-indigo-openni2-* ros-indigo-freenect-* ros-indigo-usb-cam \ ros-indigo-laser-* ros-indigo-hokuyo-node \
ros-indigo-audio-common gstreamer0.10-pocketsphinx \ ros-indigo-pocketsphinx ros-indigo-slam-gmapping \ ros-indigo-joystick-drivers python-rosinstall \ ros-indigo-orocos-kdl ros-indigo-python-orocos-kdl \
python-setuptools ros-indigo-dynamixel-motor-* \
libopencv-dev python-opencv ros-indigo-vision-opencv \ ros-indigo-depthimage-to-laserscan ros-indigo-arbotix-* \(我安装的过程中提示没有这个: ERROR:cannot launch node of type [arbotix_python/arbotix_driver]:arbotix_python) ros-indigo-turtlebot-teleop ros-indigo-move-base \ ros-indigo-map-server ros-indigo-fake-localization \ ros-indigo-amcl git subversion mercurial
- 安装rbx1 package
cd ~/catkin_ws/src git clone https://github.com/pirobot/rbx1.git cd rbx1 git checkout indigo-devel cd ~/catkin_ws catkin_make source ~/catkin_ws/devel/setup.bash rospack profile
- 测试
roslaunch rbx1_bringup fake_turtlebot.launch
rosrun rviz rviz -d `rospack find rbx1_nav`/sim.rviz
提示错误:Error: package 'rbx1_nav' not found。vmw_ioctl_command error Invalid argument.
解决:退出后,在终端中运行:
$ export SVGA_VGPU10=0
3.运行正方形
- 运行机器人
1.打开一个终端 cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src roslaunch rbx1_bringup fake_turtlebot.launch
- 运行rviz仿真环境
//新打开一个终端
cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src rosrun rviz rviz -d `rospack find rbx1_nav`/nav_fuerte.rviz
- 运行空地图
//新打开一个终端 cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src roslaunch rbx1_nav fake_move_base_blank_map.launch
- 运行正方形路径
//打开一个新终端, cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src rosrun rbx1_nav move_base_square.py
代码解释 move_base_square.py
4.避障行走
- 找到空白地图的终端,ctrl+c退出这个地图,运行障碍地图
roslaunch rbx1_nav fake_move_base_map_with_obstacles.launch
- 运行正方形路径
rosrun rbx1_nav move_base_square.py
运行效果:
若没有显示障碍物,在rviz里加载了这几个display,其中global plan下的costmap,RobotModel,global plan下的Path,Axes是必须的。
5.amcl定位
- 先运行仿真机器人
//打开一个新终端
cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src roslaunch rbx1_bringup fake_turtlebot.launch
- 运行amcl节点
amcl是二维环境下的概率定位系统,之所以说是概率定位系统,是因为它用的是自适应的蒙特卡洛的定位方法,就是之前的粒子滤波,用这个粒子滤波去跟踪机器人当前的状态/
//打开一个新终端
cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src roslaunch rbx1_nav fake_amcl.launch map:=test_map.yaml
- 然后运行rviz
cd ~/catkin_ws/ catkin_make source ./devel/setup.bash cd src rosrun rviz rviz -d `rospack find rbx1_nav`/nav_fuerte.rviz
6.关键文件解析
fake_nav_test.launch:
"use_sim_time" value="false" /> "$(find rbx1_bringup)/launch/fake_turtlebot.launch" />//加载机器人驱动 "map_server" pkg="map_server" type="map_server" args="$(find rbx1_nav)/maps/test_map.yaml"/>//这里的地图就是map_server,有时候可不需要 "move_base" type="move_base" respawn="false" name="move_base" clear_params="true" output="screen">//加载了它的几个配置文件,分别是: "$(find rbx1_nav)/config/fake/costmap_common_params.yaml" command="load" ns="global_costmap" /> "$(find rbx1_nav)/config/fake/costmap_common_params.yaml" command="load" ns="local_costmap" /> "$(find rbx1_nav)/config/fake/local_costmap_params.yaml" command="load" /> "$(find rbx1_nav)/config/fake/global_costmap_params.yaml" command="load" /> "$(find rbx1_nav)/config/fake/base_local_planner_params.yaml" command="load" /> "$(find rbx1_nav)/config/nav_test_params.yaml" command="load" /> "fake_localization" type="fake_localization" name="fake_localization" clear_params="true" output="screen">//调用之前的amcl节点 from="base_pose_ground_truth" to="odom" /> "global_frame_id" value="map" /> "base_frame_id" value="base_footprint" /> "rbx1_nav" type="nav_test.py" name="nav_test" output="screen">//加载nav_test.py程序,进行随机导航(这里的随机是指目标点位置随机) "rest_time" value="1" /> "fake_test" value="true" />
nav_test.py
#!/usr/bin/env python
""" nav_test.py - Version 1.1 2013-12-20
Command a robot to move autonomously among a number of goal locations defined in the map frame.
On each round, select a new random sequence of locations, then attempt to move to each location
in succession. Keep track of success rate, time elapsed, and total distance traveled.
Created for the Pi Robot Project: http://www.pirobot.org
Copyright (c) 2012 Patrick Goebel. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.5
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details at:
http://www.gnu.org/licenses/gpl.html
"""
import rospy
import actionlib
from actionlib_msgs.msg import *
from geometry_msgs.msg import Pose, PoseWithCovarianceStamped, Point, Quaternion, Twist
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
from random import sample
from math import pow, sqrt
class NavTest():
def __init__(self):
rospy.init_node('nav_test', anonymous=True)
rospy.on_shutdown(self.shutdown)
# How long in seconds should the robot pause at each location?
self.rest_time = rospy.get_param("~rest_time", 10)
# Are we running in the fake simulator?
self.fake_test = rospy.get_param("~fake_test", False)
# Goal state return values
goal_states = ['PENDING', 'ACTIVE', 'PREEMPTED',
'SUCCEEDED', 'ABORTED', 'REJECTED',
'PREEMPTING', 'RECALLING', 'RECALLED',
'LOST']
# Set up the goal locations. Poses are defined in the map frame.
# An easy way to find the pose coordinates is to point-and-click
# Nav Goals in RViz when running in the simulator.
# Pose coordinates are then displayed in the terminal
# that was used to launch RViz.
locations = dict()
locations['hall_foyer'] = Pose(Point(0.643, 4.720, 0.000), Quaternion(0.000, 0.000, 0.223, 0.975))
locations['hall_kitchen'] = Pose(Point(-1.994, 4.382, 0.000), Quaternion(0.000, 0.000, -0.670, 0.743))
locations['hall_bedroom'] = Pose(Point(-3.719, 4.401, 0.000), Quaternion(0.000, 0.000, 0.733, 0.680))
locations['living_room_1'] = Pose(Point(0.720, 2.229, 0.000), Quaternion(0.000, 0.000, 0.786, 0.618))
locations['living_room_2'] = Pose(Point(1.471, 1.007, 0.000), Quaternion(0.000, 0.000, 0.480, 0.877))
locations['dining_room_1'] = Pose(Point(-0.861, -0.019, 0.000), Quaternion(0.000, 0.000, 0.892, -0.451))
# Publisher to manually control the robot (e.g. to stop it, queue_size=5)
self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=5)
# Subscribe to the move_base action server
self.move_base = actionlib.SimpleActionClient("move_base", MoveBaseAction)
rospy.loginfo("Waiting for move_base action server...")
# Wait 60 seconds for the action server to become available
self.move_base.wait_for_server(rospy.Duration(60))
rospy.loginfo("Connected to move base server")
# A variable to hold the initial pose of the robot to be set by
# the user in RViz
initial_pose = PoseWithCovarianceStamped()
# Variables to keep track of success rate, running time,
# and distance traveled
n_locations = len(locations)
n_goals = 0
n_successes = 0
i = n_locations
distance_traveled = 0
start_time = rospy.Time.now()
running_time = 0
location = ""
last_location = ""
# Get the initial pose from the user
rospy.loginfo("*** Click the 2D Pose Estimate button in RViz to set the robot's initial pose...")
rospy.wait_for_message('initialpose', PoseWithCovarianceStamped)
self.last_location = Pose()
rospy.Subscriber('initialpose', PoseWithCovarianceStamped, self.update_initial_pose)
# Make sure we have the initial pose
while initial_pose.header.stamp == "":
rospy.sleep(1)
rospy.loginfo("Starting navigation test")
# Begin the main loop and run through a sequence of locations
while not rospy.is_shutdown():
# If we've gone through the current sequence,
# start with a new random sequence
if i == n_locations:
i = 0
sequence = sample(locations, n_locations)
# Skip over first location if it is the same as
# the last location
if sequence[0] == last_location:
i = 1
# Get the next location in the current sequence
location = sequence[i]
# Keep track of the distance traveled.
# Use updated initial pose if available.
if initial_pose.header.stamp == "":
distance = sqrt(pow(locations[location].position.x -
locations[last_location].position.x, 2) +
pow(locations[location].position.y -
locations[last_location].position.y, 2))
else:
rospy.loginfo("Updating current pose.")
distance = sqrt(pow(locations[location].position.x -
initial_pose.pose.pose.position.x, 2) +
pow(locations[location].position.y -
initial_pose.pose.pose.position.y, 2))
initial_pose.header.stamp = ""
# Store the last location for distance calculations
last_location = location
# Increment the counters
i += 1
n_goals += 1
# Set up the next goal location
self.goal = MoveBaseGoal()
self.goal.target_pose.pose = locations[location]
self.goal.target_pose.header.frame_id = 'map'
self.goal.target_pose.header.stamp = rospy.Time.now()
# Let the user know where the robot is going next
rospy.loginfo("Going to: " + str(location))
# Start the robot toward the next location
self.move_base.send_goal(self.goal)
# Allow 5 minutes to get there
finished_within_time = self.move_base.wait_for_result(rospy.Duration(300))
# Check for success or failure
if not finished_within_time:
self.move_base.cancel_goal()
rospy.loginfo("Timed out achieving goal")
else:
state = self.move_base.get_state()
if state == GoalStatus.SUCCEEDED:
rospy.loginfo("Goal succeeded!")
n_successes += 1
distance_traveled += distance
rospy.loginfo("State:" + str(state))
else:
rospy.loginfo("Goal failed with error code: " + str(goal_states[state]))
# How long have we been running?
running_time = rospy.Time.now() - start_time
running_time = running_time.secs / 60.0
# Print a summary success/failure, distance traveled and time elapsed
rospy.loginfo("Success so far: " + str(n_successes) + "/" +
str(n_goals) + " = " +
str(100 * n_successes/n_goals) + "%")
rospy.loginfo("Running time: " + str(trunc(running_time, 1)) +
" min Distance: " + str(trunc(distance_traveled, 1)) + " m")
rospy.sleep(self.rest_time)
def update_initial_pose(self, initial_pose):
self.initial_pose = initial_pose
def shutdown(self):
rospy.loginfo("Stopping the robot...")
self.move_base.cancel_goal()
rospy.sleep(2)
self.cmd_vel_pub.publish(Twist())
rospy.sleep(1)
def trunc(f, n):
# Truncates/pads a float f to n decimal places without rounding
slen = len('%.*f' % (n, f))
return float(str(f)[:slen])
if __name__ == '__main__':
try:
NavTest()
rospy.spin()
except rospy.ROSInterruptException:
rospy.loginfo("AMCL navigation test finished.")
7.执行步骤解析
- 执行fake_nav_test.launch
1.加载机器人驱动fake_turtlebot.launch
1.1 Load the URDF/Xacro model
1.2 加载arbotix节点(加载配置文件:fake_turtlebot_arbotix.yaml)
1.3 加载robot_state_publisher节点(设置频率publish_frequency:20)
2. 加载地图节点
3. 加载move_base节点(包含costmap_common_params.yaml;local_costmap_params.yaml;global_costmap_params.yaml;base_local_planner_params.yaml;nav_test_params.yaml四个配置文件)
4. 加载fake_localization节点(AMCL)
5. 加载rbx1_nav节点,调用nav_test.py程序进行导航
- 执行nav_test_fuerte.rviz
参考:
https://www.cnblogs.com/talugirl/p/5962806.html
https://blog.csdn.net/hcx25909/article/details/9470297