00001
00002
00003 import math
00004 import decimal
00005 import numpy as np
00006
00007 import roslib; roslib.load_manifest('assistive_teleop')
00008 import rospy
00009 import actionlib
00010 from std_msgs.msg import String, Bool
00011 from geometry_msgs.msg import (PoseWithCovarianceStamped, PointStamped,
00012 Twist, Point32, PoseStamped)
00013 from tf import TransformListener, transformations as tft
00014 from sensor_msgs.msg import LaserScan, PointCloud
00015
00016 from assistive_teleop.msg import ServoAction, ServoResult, ServoFeedback
00017
00018 class ServoingServer(object):
00019 def __init__(self):
00020 rospy.init_node('relative_servoing')
00021 rospy.Subscriber('robot_pose_ekf/odom_combined',
00022 PoseWithCovarianceStamped,
00023 self.update_position)
00024
00025 self.servoing_as = actionlib.SimpleActionServer('servoing_action',
00026 ServoAction,
00027 self.goal_cb, False)
00028 self.vel_out = rospy.Publisher('base_controller/command', Twist)
00029 self.tfl = TransformListener()
00030 self.goal_out = rospy.Publisher('/servo_dest', PoseStamped, latch=True)
00031 self.left_out = rospy.Publisher('/left_points', PointCloud)
00032 self.right_out = rospy.Publisher('/right_points', PointCloud)
00033 self.front_out = rospy.Publisher('/front_points', PointCloud)
00034
00035 self.at_goal = False
00036 self.rot_safe = True
00037 self.bfp_goal = PoseStamped()
00038 self.odom_goal = PoseStamped()
00039 self.x_max = 0.5
00040 self.x_min = 0.05
00041 self.y_man = 0.3
00042 self.y_min = 0.05
00043 self.z_max = 0.5
00044 self.z_min = 0.05
00045 self.ang_goal = 0.0
00046 self.ang_thresh_small = 0.01
00047 self.ang_thresh_big = 0.04
00048 self.ang_thresh = self.ang_thresh_big
00049 self.retreat_thresh = 0.3
00050 self.curr_pos = PoseWithCovarianceStamped()
00051 self.dist_thresh = 0.15
00052 self.left = [[],[]]
00053 self.right = [[],[]]
00054 self.front = [[],[]]
00055 self.servoing_as.start()
00056
00057 def goal_cb(self, goal):
00058 self.update_goal(goal.goal)
00059 update_rate = rospy.Rate(40)
00060 command = Twist()
00061 while not (rospy.is_shutdown() or self.at_goal):
00062 command.linear.x = self.get_trans_x()
00063 command.linear.y = self.get_trans_y()
00064 command.angular.z = self.get_rot()
00065 (x,y,z) = self.avoid_obstacles()
00066 if x is not None:
00067 command.linear.x = x
00068 if y is not None:
00069 command.linear.y = y
00070 command.angular.z += z
00071 if command.linear.y > 0:
00072 if not self.left_clear():
00073 command.linear.y = 0.0
00074 elif command.linear.y < 0:
00075 if not self.right_clear():
00076 command.linear.y = 0.0
00077
00078 self.vel_out.publish(command)
00079 rospy.sleep(0.01)
00080 update_rate.sleep()
00081 if self.at_goal:
00082 print "Arrived at goal"
00083 result = ServoResult()
00084 result.arrived = Bool(True)
00085 self.servoing_as.set_succeeded(result)
00086
00087 def update_goal(self, msg):
00088 msg.header.stamp = rospy.Time.now()
00089 if not self.tfl.waitForTransform(msg.header.frame_id, '/base_footprint',
00090 msg.header.stamp, rospy.Duration(30)):
00091 rospy.logwarn('Cannot find /base_footprint transform')
00092 self.bfp_goal = self.tfl.transformPose('/base_footprint', msg)
00093 if not self.tfl.waitForTransform(msg.header.frame_id, 'odom_combined',
00094 msg.header.stamp, rospy.Duration(30)):
00095 rospy.logwarn('Cannot find /odom_combined transform')
00096 self.odom_goal = self.tfl.transformPose('/odom_combined', msg)
00097 self.goal_out.publish(self.odom_goal)
00098 ang_to_goal = math.atan2(self.bfp_goal.pose.position.y,
00099 self.bfp_goal.pose.position.x)
00100
00101 self.ang_goal = self.curr_ang[2] + ang_to_goal
00102 rospy.logwarn(self.odom_goal)
00103 rospy.logwarn(self.ang_goal)
00104 print "New Goal: \r\n %s" %self.bfp_goal
00105
00106 def update_position(self, msg):
00107 if not self.servoing_as.is_active():
00108 return
00109 self.curr_ang=tft.euler_from_quaternion([msg.pose.pose.orientation.x,
00110 msg.pose.pose.orientation.y,
00111 msg.pose.pose.orientation.z,
00112 msg.pose.pose.orientation.w])
00113
00114 self.ang_diff = np.unwrap([0, self.ang_goal - self.curr_ang[2]])[1]
00115
00116
00117 self.dist_to_goal = ((self.odom_goal.pose.position.x-
00118 msg.pose.pose.position.x)**2 +
00119 (self.odom_goal.pose.position.y-
00120 msg.pose.pose.position.y)**2)**(1./2)
00121
00122 rospy.logwarn('Distance to goal (msg): ')
00123 rospy.logwarn(msg)
00124 rospy.logwarn('Distance to goal (odom_goal): ')
00125 rospy.logwarn(self.odom_goal)
00126
00127 if ((self.dist_to_goal < self.dist_thresh) and
00128 (abs(self.ang_diff) < self.ang_thresh)):
00129 self.at_goal = True
00130 else:
00131 self.at_goal = False
00132
00133 def base_laser_cb(self, msg):
00134 max_angle = msg.angle_max
00135 ranges = np.array(msg.ranges)
00136 angles = np.arange(msg.angle_min, msg.angle_max, msg.angle_increment)
00137
00138 near_angles = np.extract(np.logical_and(ranges<1, ranges>0.003),
00139 angles)
00140 near_ranges = np.extract(np.logical_and(ranges<1, ranges>0.003),
00141 ranges)
00142 self.bad_side = np.sign(near_angles[np.argmax(abs(near_angles))])
00143
00144
00145
00146
00147 xs = near_ranges * np.cos(near_angles)
00148 ys = near_ranges * np.sin(near_angles)
00149
00150 self.points = np.vstack((xs,ys))
00151
00152 self.bfp_points = np.vstack((np.add(0.275, xs),ys))
00153
00154 self.bfp_dists = np.sqrt(np.add(np.square(self.bfp_points[0][:]),
00155 np.square(self.bfp_points[1][:])))
00156
00157 if len(self.bfp_dists) >0:
00158 if min(self.bfp_dists) > 0.5:
00159 self.rot_safe = True
00160 else:
00161 self.rot_safe = False
00162 else:
00163 self.rot_safe = True
00164
00165 self.left = np.vstack((xs[np.nonzero(ys>0.35)[0]],
00166 ys[np.nonzero(ys>0.35)[0]]))
00167 self.right= np.vstack((xs[np.nonzero(ys<-0.35)[0]],
00168 ys[np.nonzero(ys<-0.35)[0]]))
00169 self.front = np.vstack(
00170 (np.extract(np.logical_and(ys<0.35,ys>-0.35),xs),
00171 np.extract(np.logical_and(ys<0.35, ys>-0.35),ys)))
00172
00173 front_dist = (self.front[:][0]**2+self.front[:][1]**2)**(1/2)
00174
00175
00176 if len(self.left[:][0]) > 0:
00177 leftScan = PointCloud()
00178 leftScan.header.frame_id = '/base_laser_link'
00179 leftScan.header.stamp = rospy.Time.now()
00180 for i in range(len(self.left[0][:])):
00181 pt = Point32()
00182 pt.x = self.left[0][i]
00183 pt.y = self.left[1][i]
00184 pt.z = 0
00185 leftScan.points.append(pt)
00186 self.left_out.publish(leftScan)
00187
00188 if len(self.right[:][0]) > 0:
00189 rightScan = PointCloud()
00190 rightScan.header.frame_id = '/base_laser_link'
00191 rightScan.header.stamp = rospy.Time.now()
00192 for i in range(len(self.right[:][0])):
00193 pt = Point32()
00194 pt.x = self.right[0][i]
00195 pt.y = self.right[1][i]
00196 pt.z = 0
00197 rightScan.points.append(pt)
00198 self.right_out.publish(rightScan)
00199
00200 if len(self.front[:][0]) > 0:
00201 frontScan = PointCloud()
00202 frontScan.header.frame_id = 'base_laser_link'
00203 frontScan.header.stamp = rospy.Time.now()
00204 for i in range(len(self.front[:][0])):
00205 pt = Point32()
00206 pt.x = self.front[0][i]
00207 pt.y = self.front[1][i]
00208 pt.z = 0
00209 frontScan.points.append(pt)
00210 self.front_out.publish(frontScan)
00211
00212 def get_rot(self):
00213 if abs(self.ang_diff) < self.ang_thresh:
00214 self.ang_thresh = self.ang_thresh_big
00215 return 0.0
00216 else:
00217 self.ang_thresh = self.ang_thresh_small
00218 if self.rot_safe:
00219 return np.sign(self.ang_diff)*np.clip(abs(0.35*self.ang_diff),
00220 0.1, 0.5)
00221 else:
00222 fdbk = ServoFeedback()
00223 fdbk.current_action = String("Cannot Rotate, obstacles nearby")
00224 self.servoing_as.publish_feedback(fdbk)
00225 return 0.0
00226
00227 def get_trans_x(self):
00228 if (abs(self.ang_diff) < math.pi/6 and
00229 self.dist_to_goal > self.dist_thresh):
00230 return np.clip(self.dist_to_goal*0.125, 0.05, 0.3)
00231 else:
00232 return 0.0
00233
00234 def get_trans_y(self):
00235
00236 push_from_left = push_from_right = 0.0
00237 if len(self.left[:][0]) > 0:
00238 lefts = np.extract(self.left[:][1]<0.45, self.left[:][1])
00239 if len(lefts) > 0:
00240 push_from_left = -0.45 + min(lefts)
00241 if len(self.right[:][0]) > 0:
00242 rights = np.extract(self.right[:][1]>-0.45, self.right[:][1])
00243 if len(rights) > 0:
00244 push_from_right = 0.45 + max(rights)
00245 slide = push_from_right + push_from_left
00246
00247 return np.sign(slide)*np.clip(abs(slide), 0.04, 0.07)
00248
00249 def avoid_obstacles(self):
00250
00251 x = y = None
00252 z = 0.
00253 if len(self.front[0][:]) > 0:
00254 if min(self.front[0][:]) < self.retreat_thresh:
00255
00256
00257
00258 front_dists = np.sqrt(np.add(np.square(self.front[0][:]),
00259 np.square(self.front[1][:])))
00260 min_x = self.front[0][np.argmin(front_dists)]
00261 min_y = self.front[1][np.argmin(front_dists)]
00262
00263 x = -np.sign(min_x)*np.clip(abs(min_x),0.05,0.1)
00264 y = -np.sign(min_y)*np.clip(abs(min_y),0.05,0.1)
00265 z = 0.
00266
00267 fdbk = ServoFeedback()
00268 fdbk.current_action = String("TOO CLOSE: Back up slowly...")
00269 self.servoing_as.publish_feedback(fdbk)
00270 self.retreat_thresh = 0.4
00271 elif min(self.front[0][:]) < 0.45:
00272 self.retreat_thresh = 0.3
00273 fdbk = ServoFeedback()
00274 fdbk.current_action=String("Turning Away from obstacles in front")
00275 self.servoing_as.publish_feedback(fdbk)
00276 lfobs = self.front[0][np.logical_and(self.front[1]>0,
00277 self.front[0]<0.45)]
00278 rfobs = self.front[0][np.logical_and(self.front[1]<0,
00279 self.front[0]<0.45)]
00280 if len(lfobs) == 0:
00281 y = 0.07
00282 elif len(rfobs) == 0:
00283 y = -0.07
00284 weight = np.reciprocal(np.sum(np.reciprocal(rfobs)) -
00285 np.sum(np.reciprocal(lfobs)))
00286 if weight > 0:
00287 z = 0.05
00288 else:
00289 z = -0.05
00290 else:
00291 self.retreat_thresh = 0.3
00292 return (x,y,z)
00293
00294 def left_clear(self):
00295 if len(self.left[0][:])>0:
00296
00297
00298 left_obs = self.left[:, self.left[1,:]<0.4]
00299 if len(left_obs[0][:])>0:
00300 left_obs = left_obs[:, np.logical_and(left_obs[0,:]<0.1,
00301 left_obs[0,:]>-0.8)]
00302 if len(left_obs[:][0])>0:
00303 fdbk = ServoFeedback()
00304 fdbk.current_action = String("Obstacle to the left, cannot move.")
00305 self.servoing_as.publish_feedback(fdbk)
00306 return False
00307 return True
00308
00309 def right_clear(self):
00310 if len(self.right[0][:])>0:
00311
00312
00313 right_obs = self.right[:, self.right[1,:]>-0.4]
00314 if len(right_obs[0][:])>0:
00315 right_obs = right_obs[:, np.logical_and(right_obs[0,:]<0.1,
00316 right_obs[0,:]>-0.8)]
00317 if len(right_obs[:][0])>0:
00318 fdbk = ServoFeedback()
00319 fdbk.current_action = String("Obstacle immediately to the right, cannot move.")
00320 self.servoing_as.publish_feedback(fdbk)
00321 return False
00322 return True
00323
00324 if __name__ == '__main__':
00325 servoer = ServoingServer()
00326 while not rospy.is_shutdown():
00327 rospy.spin()
00328 servoer.servoing_as.set_aborted()