WUT Velma robot API
velma_look_at_action_server.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 
6 
7 # Copyright (c) 2017, Robot Control and Pattern Recognition Group,
8 # Institute of Control and Computation Engineering
9 # Warsaw University of Technology
10 #
11 # All rights reserved.
12 #
13 # Redistribution and use in source and binary forms, with or without
14 # modification, are permitted provided that the following conditions are met:
15 # * Redistributions of source code must retain the above copyright
16 # notice, this list of conditions and the following disclaimer.
17 # * Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution.
20 # * Neither the name of the Warsaw University of Technology nor the
21 # names of its contributors may be used to endorse or promote products
22 # derived from this software without specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 # DISCLAIMED. IN NO EVENT SHALL <COPYright HOLDER> BE LIABLE FOR ANY
28 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #
35 # Author: Dawid Seredynski
36 #
37 
38 import roslib; roslib.load_manifest('velma_task_cs_ros_interface')
39 
40 import math
41 
42 import rospy
43 import actionlib
44 import geometry_msgs.msg
45 
46 from velma_look_at_action_msgs.msg import LookAtAction, LookAtFeedback, LookAtResult
47 from velma_common.velma_interface import VelmaInterface
48 
50  def __init__(self, points=None):
51  self.__points__ = []
52 
53  if not points is None:
54  for pt in points:
55  self.addPoint( pt[0], pt[1] )
56 
57  def addPoint(self, x, val):
58  if len(self.__points__) == 0:
59  self.__points__.append( (x, val) )
60  return
61 
62  for idx, pt in enumerate(self.__points__):
63  if x < pt[0]:
64  self.__points__.insert( idx, (x, val) )
65  break
66  elif idx == len(self.__points__)-1:
67  self.__points__.append( (x, val) )
68  break
69  elif x > pt[0] and x < self.__points__[idx+1][0]:
70  self.__points__.insert( idx+1, (x, val) )
71  break;
72  elif x == pt[0] or x == self.__points__[idx+1][0]:
73  raise Exception('Added point with the same x twice')
74 
75  def interpolate(self, x):
76  x = float(x)
77  if len(self.__points__) < 2:
78  raise Exception('Could not interpolate the function with only one point')
79 
80  if x < self.__points__[0][0]:
81  raise Exception('x is below the domain range')
82 
83  for idx, pt in enumerate(self.__points__):
84  if idx == len(self.__points__)-1:
85  break
86  elif x >= pt[0] and x <= self.__points__[idx+1][0]:
87  f = (x - pt[0]) / (self.__points__[idx+1][0] - pt[0])
88  return (1.0-f) * pt[1] + f * self.__points__[idx+1][1]
89  raise Exception('x is above the domain range: ' + str(x) + ', points: ' + str(self.__points__))
90 
91 def simplifiedHeadIK(torso_angle, pt):
92  # yaw is a sum of torso angle and head pan angle
93  yaw = math.atan2( pt.y, pt.x ) - torso_angle
94 
95  # pitch is head tilt angle
96  xy_dist = math.sqrt( pt.x**2 + pt.y**2 )
97  head_z = 1.75
98  pitch = -math.atan2( pt.z - head_z, xy_dist )
99 
100  return yaw, pitch
101 
103  horizontal_fov = 1.047
104  horizontal_fov_margin = math.radians(10.0)
105  head_pan_limits = [-1.56, 1.56]
106  func = LinearIntervalFunction( [
107  (-10000, 0),
108  (head_pan_limits[0] - horizontal_fov + horizontal_fov_margin, 0),
109  (head_pan_limits[0], 1),
110  (head_pan_limits[1], 1),
111  (head_pan_limits[1] + horizontal_fov - horizontal_fov_margin, 0),
112  (10000, 0)] )
113  return func.interpolate( head_q[0] )
114 
115 class VelmaLookAtAction(object):
116  # create messages that are used to publish feedback/result
117  __feedback = LookAtFeedback()
118  __result = LookAtResult()
119 
120  def __init__(self, name):
121  self.__action_name = name
122  self.__head_pan_limits = [-1.56, 1.56]
123 
124  self.__velma = VelmaInterface()
125  print('VelmaLookAtAction: Waiting for VelmaInterface initialization...')
126  if not self.__velma.waitForInit(timeout_s=10.0):
127  self.__velma = None
128  print('VelmaLookAtAction: Could not initialize VelmaInterface')
129  return
130  #raise Exception('Could not initialize VelmaInterface')
131  print('VelmaLookAtAction: Initialization of VelmaInterface ok!')
132 
133  self.__as = actionlib.SimpleActionServer(self.__action_name, LookAtAction,
134  execute_cb=self.execute_cb, auto_start=False)
135 
136  #self.__as = actionlib.SimpleActionServer(self.__action_name, LookAtAction,
137  # goal_cb=self.goal_cb, cancel_cb=self.cancel_cb, auto_start=False)
138 
139  self.__as.start()
140  print('VelmaLookAtAction: Initialization of action server ok!')
141  print('VelmaLookAtAction: Action name: {}'.format(self.__action_name))
142 
143  def isOk(self):
144  return not self.__velma is None
145 
146  #def __del__(self):
147  # with self.terminate_mutex:
148  # self.need_to_terminate = True
149  # assert(self.execute_thread)
150  # self.execute_thread.join()
151 
152  #def cancel_cb(self):
153  # pass
154 
155  def execute_cb(self, goal):
156  print('VelmaLookAtAction: Received a new goal: {} ({}, {}, {})'.format(goal.frame_id,
157  goal.target.x, goal.target.y, goal.target.z))
158  # TODO: Transform target point to the torso_base frame
159  assert goal.frame_id == 'torso_base'
160 
161  js = self.__velma.getLastJointState()
162 
163  # Simple version of IK for head
164  head_q = list( simplifiedHeadIK( js[1]['torso_0_joint'] , goal.target ) )
165 
166  quality = calculateViewQuality( head_q )
167  if head_q[0] < self.__head_pan_limits[0]:
168  head_q[0] = self.__head_pan_limits[0]
169  elif head_q[0] > self.__head_pan_limits[1]:
170  head_q[0] = self.__head_pan_limits[1]
171 
172  if quality < 0.001:
173  self.__result.error_code = LookAtResult.OUT_OF_RANGE
174  print('VelmaLookAtAction: rejected goal: OUT_OF_RANGE')
175  self.__as.set_preempted(self.__result)
176  return
177 
178  diag = self.__velma.getCoreCsDiag()
179  if (not diag.inStateCartImp()) and (not diag.inStateJntImp()):
180  self.__result.error_code = LookAtResult.WRONG_BEHAVIOUR
181  print('VelmaLookAtAction: rejected goal: WRONG_BEHAVIOUR')
182  self.__as.set_preempted(self.__result)
183  return
184 
185  self.__feedback.quality = quality
186  # TODO:
187  #self.__feedback.view_x_factor
188  #self.__feedback.view_y_factor
189 
190  dist = max( abs( js[1]['head_pan_joint']-head_q[0] ), abs(js[1]['head_tilt_joint']-head_q[1]) )
191  max_vel = 0.5 # radians per second
192  time = dist / max_vel
193  self.__velma.moveHead(head_q, time, start_time=0.5)
194  while not rospy.is_shutdown():
195  result = self.__velma.waitForHead(timeout_s=0.1)
196  if not result is None:
197  if result == 0:
198  break
199  else:
200  self.__result.error_code = LookAtResult.MOTION_FAILED
201  print('VelmaLookAtAction: aborted: MOTION_FAILED')
202  self.__as.set_preempted(self.__result)
203  return
204  # check that preempt has not been requested by the client
205  if self.__as.is_preempt_requested():
206  # set the action state to preempted
207  js = self.__velma.getLastJointState()
208  print "js: ", js[1]['head_pan_joint'],js[1]['head_tilt_joint']
209  max_vel = 0.5 # radians per second
210  time = 0.1
211  self.__velma.moveHead((js[1]['head_pan_joint'],js[1]['head_tilt_joint']), time, start_time=0.01)
212  while not rospy.is_shutdown():
213  result = self.__velma.waitForHead(timeout_s=0.1)
214  if not result is None:
215  if result == 0:
216  break
217  else:
218  self.__result.error_code = LookAtResult.MOTION_FAILED
219  print('VelmaLookAtAction: aborted: MOTION_FAILED')
220  self.__as.set_preempted(self.__result)
221  return
222  self.__result.error_code = LookAtResult.CANCELLED
223  self.__as.set_preempted(self.__result)
224  return
225 
226  self.__as.publish_feedback(self.__feedback)
227 
228  self.__result.error_code = LookAtResult.SUCCESSFUL
229  print('VelmaLookAtAction: reached the goal: SUCCESSFUL')
230  self.__as.set_succeeded(self.__result)
231 
232 if __name__ == '__main__':
233 
234 
240 
241  rospy.init_node('velma_look_at_action')
242  server = VelmaLookAtAction('velma_look_at_action')
243  if server.isOk():
244  rospy.spin()
245  exit(0)
246  else:
247  exit(1)
ROS-based, Python interface class for WUT Velma Robot.