一、解读launch文件:fake_mrobot_with_laser.launch
<launch>
<param name="/use_sim_time" value="false" />
<!-- 加载机器人URDF/Xacro模型 -->
<!-- 关于这个而言,我们不需要太多的深究,因为不是了解框架的重点 -->
<arg name="urdf_file" default="$(find xacro)/xacro --inorder '$(find mrobot_description)/urdf/mrobot_with_rplidar.urdf.xacro'" />
<param name="robot_description" command="$(arg urdf_file)" />
<node name="arbotix" pkg="arbotix_python" type="arbotix_driver" output="screen" clear_params="true">
<rosparam file="$(find mrobot_bringup)/config/fake_mrobot_arbotix.yaml" command="load" />
<param name="sim" value="true"/>
</node>
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher">
<param name="publish_frequency" type="double" value="20.0" />
</node>
</launch>
二、解读launch文件:fake_nav_demo.launch
<launch>
<!-- 告诉ROS节点要求获取ROS话题/clock发布的时间信息(默认为true)-->
<param name="use_sim_time" value="false" />
<!-- 这个参数当回放bag数据集是设置为true,此时说明系统使用的是仿真时间,如果设置为false,则系统使用walltime -->
<!-- 设置地图的配置文件,当然我把这个地图换掉了,yaml文件里面包含了地图的各种信息 -->
<arg name="map" default="map.yaml" />
<!-- 运行地图服务器,并且加载设置的地图-->
<node name="map_server" pkg="map_server" type="map_server" args="$(find mrobot_navigation)/maps/$(arg map)"/>
<!-- 运行move_base节点 -->
<include file="$(find mrobot_navigation)/launch/fake_move_base.launch" />
<!-- 运行虚拟定位,兼容AMCL输出 -->
<node pkg="fake_localization" type="fake_localization" name="fake_localization" output="screen" />
<!-- 对于虚拟定位,需要设置一个/odom与/map之间的静态坐标变换 -->
<node pkg="tf" type="static_transform_publisher" name="map_odom_broadcaster" args="0 0 0 0 0 0 /map /odom 100" />
<!-- 运行rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find mrobot_navigation)/rviz/nav.rviz"/>
</launch>
fake_localization包提供了一个单一ROS node,fake_localization, 用来替代定位系统,它也提供了amcl定位算法ROS API的子集。该node在仿真中被频繁使用,是一种不需要大量计算资源就能进行定位的方式。具体来说,fake_localization是将odometry数据转换成位姿,粒子云以及amcl用的那种tf格式的tf数据。
三、解读launch文件:fake_move_base.launch
<launch>
<!-- 启动move_base节点,加载配置所有的配置文件,下面的这几个配置文件,都是我们在导航功能包里面讲解过了 -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true">
<rosparam file="$(find mrobot_navigation)/config/fake/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find mrobot_navigation)/config/fake/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find mrobot_navigation)/config/fake/local_costmap_params.yaml" command="load" />
<rosparam file="$(find mrobot_navigation)/config/fake/global_costmap_params.yaml" command="load" />
<rosparam file="$(find mrobot_navigation)/config/fake/base_local_planner_params.yaml" command="load" />
</node>
</launch>
四、解读python文件:random_navigation.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import roslib;
#这个库和rospy差不多,都是提供API接口的
import rospy
import actionlib
#actionlib是ROS中一个很重要的功能包集合
#尽管在ROS中已经提供了srevice机制来满足请求—响应式的使用场景
#但是假如某个请求执行时间很长,在此期间用户想查看执行的进度或者取消这个请求的话,service机制就不能满足,但是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('random_navigation', anonymous=True)
rospy.on_shutdown(self.shutdown)
# 在每个目标位置暂停的时间
self.rest_time = rospy.get_param("~rest_time", 2)
#这个函数的作用就是得到设置后面的参数的值
# 到达目标的状态
goal_states = ['PENDING', 'ACTIVE', 'PREEMPTED',
'SUCCEEDED', 'ABORTED', 'REJECTED',
'PREEMPTING', 'RECALLING', 'RECALLED',
'LOST']
# 设置目标点的位置
# 如果想要获得某一点的坐标,在rviz中点击 2D Nav Goal 按键,然后单机地图中一点(或者可以直接在rviz中用publish)
# 在终端中就会看到坐标信息
locations = dict()
#后面的四元数应该表示的到点之后的位姿
locations['p1'] = Pose(Point(5.61, -4.4, -0.00143), Quaternion(0.000, 0.000, -0.013, 1.000))
locations['p2'] = Pose(Point(-4.53, 5.04, -0.00143), Quaternion(0.000, 0.000, 0.063, 0.998))
locations['p3'] = Pose(Point(2.98, 0.975, -0.00143), Quaternion(0.000, 0.000, 0.946, -0.324))
locations['p4'] = Pose(Point(0.708, -5.75, -0.00143), Quaternion(0.000, 0.000, 0.946, -0.324))
locations['p5'] = Pose(Point(6.142, -3.75, -0.00143), Quaternion(0.000, 0.000, 0.946, -0.324))
# 发布控制机器人的消息
self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=5)
# 订阅move_base服务器的消息,其实和subscriber差不多
self.move_base = actionlib.SimpleActionClient("move_base", MoveBaseAction)
rospy.loginfo("Waiting for move_base action server...")
# 60s等待时间限制
self.move_base.wait_for_server(rospy.Duration(60))
rospy.loginfo("Connected to move base server")
# 保存机器人的在rviz中的初始位置
initial_pose = PoseWithCovarianceStamped()
# 保存成功率、运行时间、和距离的变量
n_locations = len(locations) #在这个点的条件下,为5,表示一共有5个位置
n_goals = 0
n_successes = 0 #成功导航的次数,后面成功一次就加1
i = n_locations #表示第i个位置
distance_traveled = 0 #设置穿过的路程,计算所有导航次数以来的总路程
start_time = rospy.Time.now() #设置开始运动的初试时间
running_time = 0 #设置每次开始到结束的时间差
location = "" #设置当前位置
last_location = "" #设置最后一个位置
# 确保有初始位置
while initial_pose.header.stamp == "":
rospy.sleep(1)
rospy.loginfo("Starting navigation test")
# 开始主循环,随机导航
while not rospy.is_shutdown():
# 如果已经走完了所有点,再重新开始排序,就是说如果现在是的位置i就是5
if i == n_locations:
i = 0 #这个时候就把i再变成0,重新开始
sequence = sample(locations, n_locations) #这个是将五个位置随机排成好一个次序
# 如果最后一个点(上一个点)和第一个点相同,则跳过,从后一个点开始
if sequence[0] == last_location:
i = 1
# 在当前的排序中获取下一个目标点
location = sequence[i]
# 跟踪行驶距离
# 使用更新的初始位置
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 = ""
# 存储上一次的位置,计算距离
last_location = location
# 计数器加1
i += 1
n_goals += 1
# 设定下一个目标点
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()
# 让用户知道下一个位置
rospy.loginfo("Going to: " + str(location))
# 向下一个位置进发
self.move_base.send_goal(self.goal)
# 五分钟时间限制 (就是说这个导航的过程有没有在5分钟内完成)
finished_within_time = self.move_base.wait_for_result(rospy.Duration(300)) #300秒
# 查看是否成功到达
if not finished_within_time: #如果5分钟内没有完成完成
self.move_base.cancel_goal() #那么就取消这个目标点
rospy.loginfo("Timed out achieving goal") #发布超时的指令
else:
state = self.move_base.get_state() #如果已经成功到达,状态就得到get_state(),这个是上面的状态的队列
if state == GoalStatus.SUCCEEDED: #如果状态为“SUCCCEEDED”,就执行下面一系列操作
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])) #否则输出error
# 运行所用时间
running_time = rospy.Time.now() - start_time
running_time = running_time.secs / 60.0
# 输出本次导航的所有信息
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):
slen = len('%.*f' % (n, f))
return float(str(f)[:slen])
if __name__ == '__main__':
try:
NavTest()
rospy.spin()
except rospy.ROSInterruptException:
rospy.loginfo("Random navigation finished.")
action API
Move_base节点提供SimpleActionServer(见action documentation)的一个实现,需要含有geometry_msgs/PoseStamped消息的目标。可以通过ROS直接与move_base节点通信,但如果考虑使用SimpleActionClient跟踪他们的状态,推荐发送目标到move_base的方法。详见actionlib documentation来获取更多信息。
Action Subscribe Topics action订阅话题
move_base/goal (move_base_msgs/MoveBaseActionGoal)
move_base在世界中的目标
move_base/cancel (actionlib_msgs/GoalID)撤销指定目标的请求
Action Published Topics action发布话题
move_base/feedback (move_base_msgs/MoveBaseActionFeedback)包含基座在世界中当前位置的反馈
move_base/status (actionlib_msgs/GoalStatusArray)提供有关(被发送到move_base action)目标的状态信息
move_base/result (move_base_msgs/MoveBaseActionResult)对move_base action 结果是空
评论(0)
您还未登录,请登录后发表或查看评论