00001
00002
00003 '''
00004 Created on 01.08.2013
00005
00006 @author: Thorsten Gedicke
00007 '''
00008
00009 import roslib; roslib.load_manifest('move_base_straight')
00010 import rospy
00011 import numpy as np
00012 import tf
00013 import actionlib
00014 from geometry_msgs.msg import PoseStamped, Twist
00015 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal, MoveBaseResult
00016 from sensor_msgs.msg import LaserScan
00017
00018 class MoveBaseStraightAction(object):
00019
00020 FREQ = 10
00021
00022 def __init__(self, name):
00023
00024
00025 self.MAX_SPEED = rospy.get_param('~max_speed', 0.2)
00026 self.MIN_SPEED = rospy.get_param('~min_speed', 0.05)
00027
00028
00029 self.GOAL_THRESHOLD = rospy.get_param('~goal_threshold', 0.02)
00030
00031
00032
00033 self.GOAL_THRESHOLD_ACCEPTABLE = rospy.get_param('~goal_threshold_acceptable', 0.2)
00034
00035
00036 self.RANGE_MINIMUM = rospy.get_param('~range_minimum', 0.5)
00037
00038
00039
00040 self.SLOWDOWN_RANGE = rospy.get_param('~slowdown_range', 0.5)
00041
00042
00043
00044
00045 self.REQUIRED_APERTURE = rospy.get_param('~required_aperture', np.pi * 0.75)
00046
00047
00048 self.GOAL_TOPIC_NAME = rospy.get_param('~goal_topic_name', None)
00049
00050
00051 self.action_name = name
00052 self.tf_listener = tf.TransformListener()
00053 self.cmd_vel_pub = rospy.Publisher('base_controller/command', Twist)
00054
00055
00056 self.scan = None
00057 rospy.Subscriber("/base_scan", LaserScan, self.laser_cb)
00058
00059 footprint_frame = rospy.get_param('~footprint_frame', '/base_footprint')
00060 laser_frame = rospy.get_param('~laser_frame', '/base_laser_link')
00061
00062
00063 while not rospy.is_shutdown():
00064 try:
00065 self.tf_listener.waitForTransform(footprint_frame, laser_frame, rospy.Time(), rospy.Duration(1.0))
00066 self.LASER_BASE_OFFSET = self.tf_listener.lookupTransform(footprint_frame, laser_frame, rospy.Time())[0][0]
00067 break
00068 except (tf.LookupException, tf.ConnectivityException) as e:
00069 rospy.logwarn("MoveBaseBlind tf exception! Message: %s" % e.message)
00070 rospy.sleep(0.1)
00071 continue
00072
00073
00074 self.action_server = actionlib.SimpleActionServer(self.action_name, MoveBaseAction, execute_cb=self.execute_cb, auto_start=False)
00075 self.action_server.start()
00076 rospy.loginfo('%s: Action server up and running...' % (self.action_name))
00077
00078 if self.GOAL_TOPIC_NAME:
00079 self.client = actionlib.SimpleActionClient('move_base_straight', MoveBaseAction)
00080 self.client.wait_for_server()
00081 rospy.Subscriber(self.GOAL_TOPIC_NAME, PoseStamped, self.manual_cb)
00082 rospy.loginfo('%s: Manual control topic is: %s' % (self.action_name, self.GOAL_TOPIC_NAME))
00083
00084 def laser_to_base(self, distance_laser, angle_laser):
00085 """
00086 Uses the laws of sines and cosines to transform range/angle pairs in the
00087 base laser frame to range/angle pairs relative to the base footprint frame.
00088 """
00089 gamma = np.pi - angle_laser
00090 distance_base = np.sqrt(self.LASER_BASE_OFFSET**2 + distance_laser**2 - 2 * self.LASER_BASE_OFFSET * distance_laser * np.cos(gamma))
00091 angle_base = np.arcsin(distance_laser * np.sin(gamma) / distance_base)
00092 return (distance_base, angle_base)
00093
00094 def laser_cb(self, scan):
00095 self.scan = scan
00096
00097 def manual_cb(self, target_pose):
00098 goal = MoveBaseGoal(target_pose=target_pose)
00099 self.client.send_goal(goal)
00100
00101 def execute_cb(self, goal):
00102 if not self.scan:
00103 rospy.logwarn('No messages received yet on topic %s, aborting goal!' % rospy.resolve_name('base_scan'))
00104 self.action_server.set_aborted()
00105 return
00106
00107
00108 success = False
00109 target_pose = goal.target_pose
00110
00111
00112 rospy.loginfo('%s: Executing, moving to position: (%f, %f)' % (self.action_name, target_pose.pose.position.x, target_pose.pose.position.y))
00113
00114 rate = rospy.Rate(hz = MoveBaseStraightAction.FREQ)
00115 while not rospy.is_shutdown():
00116 rate.sleep()
00117
00118 while not rospy.is_shutdown() and not self.action_server.is_preempt_requested():
00119 try:
00120
00121
00122 target_pose.header.stamp = rospy.Time.now()
00123
00124 self.tf_listener.waitForTransform('/base_footprint',
00125 target_pose.header.frame_id,
00126 target_pose.header.stamp, rospy.Duration(1.0))
00127 target_pose_transformed = self.tf_listener.transformPose('/base_footprint', target_pose)
00128 break
00129 except (tf.LookupException, tf.ConnectivityException) as e:
00130 rospy.logwarn("MoveBaseBlind tf exception! Message: %s" % e.message)
00131 rospy.sleep(0.1)
00132 continue
00133
00134
00135 if self.action_server.is_preempt_requested():
00136 rospy.loginfo('%s: Preempted' % self.action_name)
00137 self.action_server.set_preempted()
00138 break
00139
00140 x_diff = target_pose_transformed.pose.position.x
00141 y_diff = target_pose_transformed.pose.position.y
00142 dist = np.sqrt(x_diff ** 2 + y_diff ** 2)
00143
00144
00145 if (dist < self.GOAL_THRESHOLD):
00146
00147 self.cmd_vel_pub.publish(Twist())
00148 rospy.loginfo('%s: Succeeded' % self.action_name)
00149 self.action_server.set_succeeded()
00150 break
00151
00152
00153 target_angle = np.arctan2(target_pose_transformed.pose.position.y,
00154 target_pose_transformed.pose.position.x)
00155
00156 blocked = False
00157 block_reason = None
00158 speed_multiplier = min(1.0, (dist - self.GOAL_THRESHOLD) / self.SLOWDOWN_RANGE)
00159 laser_angle = self.scan.angle_min
00160 for laser_distance in self.scan.ranges:
00161 (base_distance, base_angle) = self.laser_to_base(laser_distance, laser_angle)
00162 angle_diff = abs(base_angle - target_angle)
00163 will_get_closer = angle_diff < (np.pi / 2.0)
00164 is_too_close = (base_distance < self.RANGE_MINIMUM) and (laser_distance > self.scan.range_min)
00165 if will_get_closer:
00166 speed_multiplier = min(speed_multiplier, (base_distance - self.RANGE_MINIMUM) / self.SLOWDOWN_RANGE)
00167 if is_too_close and will_get_closer:
00168 blocked = True
00169 block_reason = (base_distance, base_angle, angle_diff)
00170 break;
00171 laser_angle += self.scan.angle_increment
00172 if blocked:
00173
00174 self.cmd_vel_pub.publish(Twist())
00175 rospy.logwarn('%s: Blocked! Distance to goal: %s m. Critical object distance of %s m at angle %s (%s angle difference to goal direction)' % (self.action_name, dist, block_reason[0], block_reason[1], block_reason[2]))
00176 if(dist <= self.GOAL_THRESHOLD_ACCEPTABLE):
00177 rospy.logwarn('%s: Succeeded' % self.action_name)
00178 self.action_server.set_succeeded()
00179 else:
00180 rospy.logwarn('%s: Aborted' % self.action_name)
00181 self.action_server.set_aborted()
00182 break
00183
00184
00185 if ((target_angle - self.REQUIRED_APERTURE/2) < self.scan.angle_min or
00186 (target_angle + self.REQUIRED_APERTURE/2) > self.scan.angle_max):
00187
00188 pass
00189
00190
00191 drive_speed = max(self.MAX_SPEED * speed_multiplier, self.MIN_SPEED)
00192 cmd = Twist()
00193 cmd.linear.x = (x_diff / dist) * drive_speed
00194 cmd.linear.y = (y_diff / dist) * drive_speed
00195 self.cmd_vel_pub.publish(cmd)
00196
00197
00198 if __name__ == '__main__':
00199 rospy.init_node('move_base_straight')
00200 MoveBaseStraightAction(rospy.get_name())
00201 rospy.spin()