servoing.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
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         #rospy.Subscriber('/base_scan', LaserScan, self.base_laser_cb)
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         #Initialize variables, so as not to spew errors before seeing a goal
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             #print "Sending vel_command: \r\n %s" %self.command
00078             self.vel_out.publish(command)
00079             rospy.sleep(0.01) #Min sleep
00080             update_rate.sleep() #keep pace
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         #(current angle in odom, plus the robot-relative change to face goal)
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         # Normalized via unwrap relative to 0; (keeps between -pi/pi)
00114         self.ang_diff = np.unwrap([0, self.ang_goal - self.curr_ang[2]])[1]
00115         #print "Ang Diff: %s" %self.ang_diff
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         #Filter out noise(<0.003), points >1m, leaves obstacles
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         #print "bad side: %s" %bad_side # (1 (pos) = left, -1 = right)
00144         #print "Min in Ranges: %s" %min(ranges)
00145        
00146         #if len(near_ranges) > 0:
00147         xs = near_ranges * np.cos(near_angles)
00148         ys = near_ranges * np.sin(near_angles)
00149         #print "xs: %s" %xs
00150         self.points = np.vstack((xs,ys))
00151         #print "Points: %s" %points
00152         self.bfp_points = np.vstack((np.add(0.275, xs),ys))
00153         #print "bfp Points: %s" %bfp_points
00154         self.bfp_dists = np.sqrt(np.add(np.square(self.bfp_points[0][:]),
00155                                         np.square(self.bfp_points[1][:])))
00156         #print min(self.bfp_dists)
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         ##Testing and Visualization:###
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        # Determine left/right movement speed for strafing obstacle avoidance 
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         #print "Slide speed (m/s): %s" %slide
00247         return  np.sign(slide)*np.clip(abs(slide), 0.04, 0.07)
00248 
00249     def avoid_obstacles(self):
00250         ##Determine rotation to avoid obstacles in front of robot#
00251         x = y = None
00252         z = 0.
00253         if len(self.front[0][:]) > 0:
00254             if min(self.front[0][:]) < self.retreat_thresh: 
00255                 #(round-up on corner-to-corner radius of robot) -
00256                 # 0.275 (x diff from base laser link to base footprint)
00257                 #print "front[0][:] %s" %self.front[0][:]
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                 #print "min x/y: %s,%s" %(min_x, min_y)
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                 # This should probably be avoided...
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): # Base Laser cannot see obstacles beside the back edge of the robot, which could cause problems, especially just after passing through doorways...
00295         if len(self.left[0][:])>0:
00296             #Find points directly to the right or left of the robot (not in front or behind)
00297             # x<0.1 (not in front), x>-0.8 (not behind)
00298             left_obs = self.left[:, self.left[1,:]<0.4] #points too close.
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             #Find points directly to the right or left of the robot (not in front or behind)
00312             # x<0.1 (not in front), x>-0.8 (not behind)
00313            right_obs = self.right[:, self.right[1,:]>-0.4] #points too close.
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()


assistive_teleop
Author(s): Phillip M. Grice, Advisor: Prof. Charlie Kemp, Lab: The Healthcare Robotoics Lab at Georgia Tech.
autogenerated on Wed Nov 27 2013 11:35:34