social_gaze_server.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 '''Handles where to point robot's head'''
00004 
00005 # ######################################################################
00006 # Imports
00007 # ######################################################################
00008 
00009 # Core ROS imports come first.
00010 import rospy
00011 
00012 # System builtins
00013 import time
00014 from numpy import array, linalg
00015 
00016 # ROS builtins
00017 # from scipy.ndimage.filters import *
00018 from actionlib_msgs.msg import GoalStatus
00019 from actionlib import SimpleActionClient, SimpleActionServer
00020 from control_msgs.msg import PointHeadAction, PointHeadGoal
00021 from geometry_msgs.msg import Point
00022 from tf import TransformListener
00023 
00024 # Local
00025 from fetch_pbd_interaction.srv import GetGazeGoal, GetGazeGoalResponse
00026 from fetch_social_gaze.msg import GazeGoal, GazeAction
00027 
00028 # ######################################################################
00029 # Classes
00030 # ######################################################################
00031 
00032 class SocialGaze:
00033     '''Handles where to point robot's head'''
00034 
00035     def __init__(self):
00036         self._default_focus_point = Point(1, 0, 1.05)
00037         self._down_focus_point = Point(0.5, 0, 0.5)
00038         self._target_focus_point = Point(1, 0, 1.05)
00039         self._current_focus_point = Point(1, 0, 1.05)
00040 
00041         self._current_gaze_action = GazeGoal.LOOK_FORWARD
00042         self._prev_gaze_action = self._current_gaze_action
00043         self._prev_target_focus_point = array(self._default_focus_point)
00044 
00045         # Some constants
00046         self._no_interrupt = [GazeGoal.NOD,
00047                                GazeGoal.SHAKE, GazeGoal.LOOK_DOWN]
00048         self._nod_positions = [Point(1, 0, 0.70), Point(1, 0, 1.20)]
00049         self._shake_positions = [Point(1, 0.2, 1.05), Point(1, -0.2, 1.05)]
00050         self._n_nods = 5
00051         self._n_shakes = 5
00052 
00053         self._nod_counter = 5
00054         self._shake_counter = 5
00055         self._face_pos = None
00056         self._glance_counter = 0
00057 
00058         self.gaze_goal_strs = {
00059             GazeGoal.LOOK_FORWARD: 'LOOK_FORWARD',
00060             GazeGoal.FOLLOW_EE: 'FOLLOW_EE',
00061             GazeGoal.GLANCE_EE: 'GLANCE_EE',
00062             GazeGoal.NOD: 'NOD',
00063             GazeGoal.SHAKE: 'SHAKE',
00064             GazeGoal.FOLLOW_FACE: 'FOLLOW_FACE',
00065             GazeGoal.LOOK_AT_POINT: 'LOOK_AT_POINT',
00066             GazeGoal.LOOK_DOWN: 'LOOK_DOWN',
00067             GazeGoal.NONE: 'NONE',
00068         }
00069 
00070         ## Action client for sending commands to the head.
00071         self._head_action_client = SimpleActionClient(
00072             '/head_controller/point_head', PointHeadAction)
00073         self._head_action_client.wait_for_server()
00074         rospy.loginfo('Head action client has responded.')
00075 
00076         self._head_goal = PointHeadGoal()
00077         self._head_goal.target.header.frame_id = 'base_link'
00078         self._head_goal.min_duration = rospy.Duration(1.0)
00079         self._head_goal.target.point = Point(1, 0, 1)
00080 
00081         ## Service client for arm states
00082         self._tf_listener = TransformListener()
00083 
00084         ## Server for gaze requested gaze actions
00085         self._gaze_action_server = SimpleActionServer(
00086             '/fetch_pbd/gaze_action', GazeAction, self._execute_gaze_action, False)
00087         self._gaze_action_server.start()
00088 
00089         self._is_action_complete = True
00090 
00091         rospy.Service('/fetch_pbd/get_current_gaze_goal', GetGazeGoal,
00092                       self._get_gaze_goal)
00093 
00094     # ##################################################################
00095     # Instance methods: Public (API)
00096     # ##################################################################
00097 
00098     def update(self):
00099         '''Update goal for head movement'''
00100         is_action_possibly_complete = True
00101 
00102         if self._current_gaze_action == GazeGoal.FOLLOW_EE:
00103             self._target_focus_point = self._get_ee_pos()
00104 
00105         elif self._current_gaze_action == GazeGoal.NOD:
00106             self._target_focus_point = self._get_next_nod_point(
00107                 self._current_focus_point, self._target_focus_point)
00108             self._head_goal.min_duration = rospy.Duration(0.5)
00109             is_action_possibly_complete = False
00110 
00111         elif self._current_gaze_action == GazeGoal.SHAKE:
00112             self._target_focus_point = self._get_next_shake_point(
00113                 self._current_focus_point, self._target_focus_point)
00114             self._head_goal.min_duration = rospy.Duration(0.5)
00115             is_action_possibly_complete = False
00116 
00117         elif self._current_gaze_action == GazeGoal.GLANCE_EE:
00118             self._target_focus_point = self._get_next_glance_point(
00119                 self._current_focus_point, self._target_focus_point)
00120             is_action_possibly_complete = False
00121 
00122         self._current_focus_point = SocialGaze._filter_look_at_position(
00123             self._current_focus_point, self._target_focus_point)
00124         if self._current_gaze_action is GazeGoal.NONE:
00125             pass
00126         elif (SocialGaze._is_the_same(
00127                         SocialGaze._point2array(self._head_goal.target.point),
00128                         SocialGaze._point2array(self._target_focus_point))):
00129 
00130             if is_action_possibly_complete:
00131                 head_state = self._head_action_client.get_state()
00132                 if head_state != GoalStatus.ACTIVE:
00133                     self._is_action_complete = True
00134         else:
00135             self._head_goal.target.point.x = self._current_focus_point.x
00136             self._head_goal.target.point.y = self._current_focus_point.y
00137             self._head_goal.target.point.z = self._current_focus_point.z
00138             self._head_action_client.send_goal(self._head_goal)
00139 
00140         time.sleep(0.02)
00141 
00142     # ##################################################################
00143     # Static methods: Internal ("private")
00144     # ##################################################################
00145 
00146     @staticmethod
00147     def _is_the_same(current, target):
00148         '''Get 3DOF pose of end effector
00149 
00150         Args:
00151             current (array): (x, y, z) of point
00152             target (array): (x, y, z) of point
00153 
00154         Returns:
00155             bool
00156         '''
00157         diff = target - current
00158         dist = linalg.norm(diff)
00159         return dist < 0.003
00160 
00161     @staticmethod
00162     def _point2array(p):
00163         '''Make Point msg into array
00164 
00165         Args:
00166             p (Point)
00167 
00168         Returns:
00169             array
00170         '''
00171         return array((p.x, p.y, p.z))
00172 
00173     @staticmethod
00174     def _array2point(a):
00175         '''Make array into Point msg
00176 
00177         Args:
00178             a (array)
00179 
00180         Returns:
00181             Point
00182         '''
00183         return Point(a[0], a[1], a[2])
00184 
00185     @staticmethod
00186     def _filter_look_at_position(current, target):
00187         '''If head goal is too far away, returns an intermediate position
00188            to limit speed
00189 
00190         Args:
00191             current (Point): current head goal
00192             target (Point): new head goal
00193 
00194         Returns:
00195             Point
00196         '''
00197         speed = 0.02
00198         diff = (SocialGaze._point2array(target) -
00199                     SocialGaze._point2array(current))
00200         dist = linalg.norm(diff)
00201         if dist > speed:
00202             step = dist / speed
00203             return SocialGaze._array2point(SocialGaze._point2array(current) +
00204                                            diff / step)
00205         else:
00206             return target
00207 
00208     # ##################################################################
00209     # Instance methods: Internal ("private")
00210     # ##################################################################
00211 
00212     def _get_gaze_goal(self, req):
00213         '''Return current gaze goal
00214 
00215         Args:
00216             req (GetGazeGoalRequest) : Unused
00217         Returns:
00218             GetGazeGoalResponse
00219         '''
00220         goal = self._current_gaze_action
00221         # rospy.loginfo("Gaze Goal: {}".format(goal))
00222         return GetGazeGoalResponse(int(goal))
00223 
00224 
00225     def _get_ee_pos(self):
00226         '''Get 3DOF pose of end effector
00227 
00228         Returns:
00229             Point: location of wrist of end effector
00230                     (could change to be finger tips)
00231         '''
00232 
00233         from_frame = '/base_link'
00234         to_frame = '/wrist_roll_link'
00235 
00236         try:
00237             t = self._tf_listener.getLatestCommonTime(from_frame, to_frame)
00238             (position, rotation) = self._tf_listener.lookupTransform(
00239                                                                    from_frame,
00240                                                                    to_frame, t)
00241         except:
00242             rospy.logerr('Could not get the end-effector pose.')
00243 
00244         return Point(position[0], position[1], position[2])
00245 
00246     ## Callback function for receiving gaze commands
00247     def _execute_gaze_action(self, goal):
00248         '''Get 3DOF pose of end effector
00249 
00250         Args:
00251             goal (GazeGoal): what type of action to perform next
00252         '''
00253         # rospy.loginfo("Got a head goal: {}".format(goal.action))
00254         command = goal.action
00255         if self._no_interrupt.count(self._current_gaze_action) == 0:
00256             if (self._current_gaze_action != command or
00257                 command == GazeGoal.LOOK_AT_POINT):
00258                 self._is_action_complete = False
00259                 if command == GazeGoal.LOOK_FORWARD:
00260                     self._target_focus_point = self._default_focus_point
00261                 elif command == GazeGoal.LOOK_DOWN:
00262                     self._target_focus_point = self._down_focus_point
00263                 elif command == GazeGoal.NOD:
00264                     self._n_nods = goal.repeat
00265                     self._start_nod()
00266                 elif command == GazeGoal.SHAKE:
00267                     self._n_shakes = goal.repeat
00268                     self._start_shake()
00269                 elif command == GazeGoal.GLANCE_EE:
00270                     self._start_glance()
00271                 elif command == GazeGoal.LOOK_AT_POINT:
00272                     self._target_focus_point = goal.point
00273                 # rospy.loginfo('\tSetting gaze action to: ' +
00274                 #               self.gaze_goal_strs[command])
00275                 self._current_gaze_action = command
00276 
00277                 while not self._is_action_complete:
00278                     time.sleep(0.1)
00279                 self._current_gaze_action = GazeGoal.NONE
00280                 # Perturb the head goal so it gets updated in the update loop.
00281                 self._head_goal.target.point.x += 1
00282                 self._gaze_action_server.set_succeeded()
00283             else:
00284                 self._gaze_action_server.set_aborted()
00285 
00286         else:
00287             self._gaze_action_server.set_aborted()
00288 
00289     def _start_nod(self):
00290         '''Start nod action'''
00291         self._prev_target_focus_point = self._target_focus_point
00292         self._prev_gaze_action = str(self._current_gaze_action)
00293         self._nod_counter = 0
00294         self._target_focus_point = self._nod_positions[0]
00295 
00296     def _start_glance(self):
00297         '''Start glance action'''
00298         self._prev_target_focus_point = self._target_focus_point
00299         self._prev_gaze_action = str(self._current_gaze_action)
00300         self._glance_counter = 0
00301         self._target_focus_point = self._get_ee_pos()
00302 
00303     def _start_shake(self):
00304         '''Start shake action'''
00305         self._prev_target_focus_point = self._target_focus_point
00306         self._prev_gaze_action = str(self._current_gaze_action)
00307         self._shake_counter = 0
00308         self._target_focus_point = self._shake_positions[0]
00309 
00310     def _get_next_nod_point(self, current, target):
00311         '''Get next point to look at while nodding
00312 
00313         Args:
00314             current (Point): current head goal
00315             target (Point): new head goal
00316 
00317         Returns:
00318             Point
00319         '''
00320         if (SocialGaze._is_the_same(SocialGaze._point2array(current),
00321                            SocialGaze._point2array(target))):
00322             self._nod_counter += 1
00323             if self._nod_counter == self._n_nods:
00324                 self._current_gaze_action = self._prev_gaze_action
00325                 return self._prev_target_focus_point
00326             else:
00327                 return self._nod_positions[self._nod_counter % 2]
00328         else:
00329             return target
00330 
00331     def _get_next_glance_point(self, current, target):
00332         '''Get next point to look at while glancing
00333 
00334         Args:
00335             current (Point): current head goal
00336             target (Point): new head goal
00337 
00338         Returns:
00339             Point
00340         '''
00341         if (SocialGaze._is_the_same(SocialGaze._point2array(current),
00342                            SocialGaze._point2array(target))):
00343             self._glance_counter = 1
00344             self._current_gaze_action = self._prev_gaze_action
00345             return self._prev_target_focus_point
00346         else:
00347             return target
00348 
00349     def _get_next_shake_point(self, current, target):
00350         '''Get next point to look at while shaking
00351 
00352         Args:
00353             current (Point): current head goal
00354             target (Point): new head goal
00355 
00356         Returns:
00357             Point
00358         '''
00359         if (SocialGaze._is_the_same(SocialGaze._point2array(current),
00360                            SocialGaze._point2array(target))):
00361             self._shake_counter += 1
00362             if self._shake_counter == self._n_shakes:
00363                 self._current_gaze_action = self._prev_gaze_action
00364                 return self._prev_target_focus_point
00365             else:
00366                 return self._shake_positions[self._shake_counter % 2]
00367         else:
00368             return target
00369 
00370 if __name__ == '__main__':
00371     rospy.init_node('social_gaze')
00372     gaze = SocialGaze()
00373     while not rospy.is_shutdown():
00374         gaze.update()


fetch_social_gaze
Author(s): Sarah Elliott
autogenerated on Thu Jun 6 2019 18:27:24