Statistics
| Branch: | Tag: | Revision:

hlrc / client / python / hlrc_client / MiddlewareROS.py @ c5a47e56

History | View | Annotate | Download (10.639 KB)

1
"""
2
This file is part of hlrc
3

4
Copyright(c) sschulz <AT> techfak.uni-bielefeld.de
5
http://opensource.cit-ec.de/projects/hlrc
6

7
This file may be licensed under the terms of the
8
GNU General Public License Version 3 (the ``GPL''),
9
or (at your option) any later version.
10

11
Software distributed under the License is distributed
12
on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
13
express or implied. See the GPL for the specific language
14
governing rights and limitations.
15

16
You should have received a copy of the GPL along with this
17
program. If not, go to http://www.gnu.org/licenses/gpl.html
18
or write to the Free Software Foundation, Inc.,
19
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20

21
The development of this software was supported by the
22
Excellence Cluster EXC 277 Cognitive Interaction Technology.
23
The Excellence Cluster EXC 277 is a grant of the Deutsche
24
Forschungsgemeinschaft (DFG) in the context of the German
25
Excellence Initiative.
26
"""
27

    
28
from Middleware import *
29
import errno
30

    
31
try:
32
        import roslib; 
33
        
34
        import rospy 
35
        import actionlib
36
        from hlrc_server.msg import *
37
    
38
except ImportError as exception:
39
        sys.stderr.write("ImportError: {}\n> HINT: try to export PYTHONPATH=$PYTHONPATH:$YOUR_PREFIX/lib/python2.7/site-packages/\n\n".format(exception))
40
        sys.exit(errno.ENOPKG)
41

    
42
class MiddlewareROS(Middleware):
43
        #default timeout to wait in case of blocking calls
44
        ROS_ACTION_CALL_TIMEOUT = 30.0
45
        
46
        #######################################################################
47
        def __init__(self, scope, loglevel=logging.WARNING):
48
                """initialise
49
                :param scope: base scope we want to listen on 
50
                """
51
                #init base settings
52
                Middleware.__init__(self,scope,loglevel)
53
                #call mw init
54
                self.init_middleware()
55
                        
56
        def __del__(self):
57
                """destructor
58
                """
59
                self.logger.debug("destructor of MiddlewareROS called")
60
                
61
        #######################################################################
62
        def init_middleware(self):
63
                """initialise middleware
64
                """
65
                self.logger.info("initialising ROS middleware connection on scope %s" % (self.base_scope))
66
                
67
                rospy.init_node('hlrc_client', anonymous=True)
68
                
69
                self.logger.info("creating action clients")
70
                
71
                self.logger.debug("creating speech action client")
72
                self.speech_client = actionlib.SimpleActionClient(self.base_scope + "/set/speech", speechAction)
73
                self.speech_client.wait_for_server()
74
                
75
                self.logger.debug("creating default emotion action client")
76
                self.default_emotion_client = actionlib.SimpleActionClient(self.base_scope + "/set/defaultEmotion", emotionstateAction)
77
                self.default_emotion_client.wait_for_server()
78
                
79
                self.logger.debug("creating current emotion action client")
80
                self.current_emotion_client = actionlib.SimpleActionClient(self.base_scope + "/set/currentEmotion", emotionstateAction)
81
                self.current_emotion_client.wait_for_server()
82
                
83
                self.logger.debug("creating animation action client")
84
                self.animation_client = actionlib.SimpleActionClient(self.base_scope + "/set/animation", animationAction)
85
                self.animation_client.wait_for_server()
86
                
87
                self.logger.debug("creating gazetarget action client")
88
                self.gazetarget_client = actionlib.SimpleActionClient(self.base_scope + "/set/gaze", gazetargetAction)
89
                self.gazetarget_client.wait_for_server()
90
                
91
                self.logger.debug("creating mouthtarget action client")
92
                self.mouthtarget_client = actionlib.SimpleActionClient(self.base_scope + "/set/mouth", mouthtargetAction)
93
                self.mouthtarget_client.wait_for_server()
94
                
95
                self.logger.info("MiddlewareROS initialised")
96
                
97

    
98
        #######################################################################
99
        def publish_emotion(self, em_type, emotion, blocking):
100
                """publish an emotion via mw
101
                :param em_type: type of emotion (RobotEmotion::TYPE_DEFAULT or RobotEmotion::TYPE_CURRENT)
102
                :param emotion: emotion to set
103
                :param blocking: True if this call should block until execution finished on robot
104
                """
105
                self.logger.debug("calling the emotion rpc (%s)..." % ("BLOCKING" if blocking else "NON-BLOCKING"))
106
                
107
                #create a goal to send to the action server.
108
                goal = emotionstateGoal()
109
                goal.value    = self.convert_emotiontype_to_rosval(emotion.value)
110
                goal.duration =  int(emotion.time_ms)
111
                
112
                #which client do we use?
113
                if (em_type == RobotEmotion.TYPE_DEFAULT):
114
                        client = self.default_emotion_client
115
                else:
116
                        client = self.current_emotion_client
117
                
118
                #send the goal to the server
119
                client.send_goal(goal)
120
                
121
                #wait for the server to finish 
122
                if (blocking):
123
                        timed_out = client.wait_for_result(rospy.Duration.from_sec(MiddlewareROS.ROS_ACTION_CALL_TIMEOUT))
124
                        if (timed_out):
125
                                self.logger.error("%s timed out waiting for result" % (sys._getframe().f_code.co_name))
126
                
127
        def publish_default_emotion(self, emotion, blocking):
128
                self.publish_emotion(RobotEmotion.TYPE_DEFAULT, emotion, blocking)
129
        
130
        def publish_current_emotion(self, emotion, blocking):
131
                self.publish_emotion(RobotEmotion.TYPE_CURRENT, emotion, blocking)
132
        
133
        def publish_head_animation(self, animation, blocking):
134
                """publish an head animation via mw
135
                :param animation: animation to set
136
                :param blocking: True if this call should block until execution finished on robot
137
                """
138
                self.logger.debug("calling the animation rpc (%s)..." % ("BLOCKING" if blocking else "NON-BLOCKING"))
139
                
140
                #create a goal to send to the action server.
141
                goal = animationGoal()
142
                goal.target = self.convert_animationtype_to_rosval(animation.value)
143
                goal.repetitions = animation.repetitions
144
                goal.duration_each =  int(animation.time_ms)
145
                goal.scale = animation.scale
146
                
147
                #send the goal to the server
148
                self.animation_client.send_goal(goal)
149
                
150
                #wait for the server to finish 
151
                if (blocking):
152
                        timed_out = self.animation_client.wait_for_result(rospy.Duration.from_sec(MiddlewareROS.ROS_ACTION_CALL_TIMEOUT))
153
                        if (timed_out):
154
                                self.logger.error("%s timed out waiting for result" % (sys._getframe().f_code.co_name))
155
                
156
                self.logger.debug("animation rpc done")
157
        
158
        def publish_gaze_target(self, gaze, blocking):
159
                """publish a gaze target via mw
160
                :param gaze: gaze to set
161
                :param blocking: True if this call should block until execution finished on robot
162
                """
163
                self.logger.debug("calling the gaze rpc (%s)..." % ("BLOCKING" if blocking else "NON-BLOCKING"))
164
                
165
                #create a goal to send to the action server.
166
                goal = gazetargetGoal()
167
                goal.pan  = gaze.pan
168
                goal.tilt = gaze.tilt
169
                goal.roll = gaze.roll
170
                goal.vergence = gaze.vergence
171
                goal.pan_offset  = gaze.pan_offset
172
                goal.tilt_offset = gaze.tilt_offset
173

    
174
                #type
175
                if (gaze.gaze_type == RobotGaze.ABSOLUTE):
176
                        goal.gaze_type = gazetargetGoal.ABSOLUTE
177
                else:
178
                        goal.gaze_type = gazetargetGoal.RELATIVE
179

    
180
                goal.timestamp = rospy.Time.from_sec(timestamp)
181
        
182
        
183
                #send the goal to the server
184
                self.gazetarget_client.send_goal(goal)
185
                
186
                #wait for the server to finish 
187
                if (blocking):
188
                        timed_out = self.gazetarget_client.wait_for_result(rospy.Duration.from_sec(MiddlewareROS.ROS_ACTION_CALL_TIMEOUT))
189
                        if (timed_out):
190
                                self.logger.error("%s timed out waiting for result" % (sys._getframe().f_code.co_name))
191
                
192
                
193
                self.logger.debug("gaze rpc done")
194
        
195
        def publish_mouth_target(self, mouth, blocking):
196
                """publish a mouth target via mw
197
                :param mouth: mouth value to set
198
                :param blocking: True if this call should block until execution finished on robot
199
                """
200
                self.logger.debug("calling the mouth rpc (%s)..." % ("BLOCKING" if blocking else "NON-BLOCKING"))
201
                
202
                #create a goal to send to the action server.
203
                goal = mouthtargetGoal()
204
                goal.opening_left  = mouth.opening_left
205
                goal.opening_center = mouth.opening_center
206
                goal.opening_right = mouth.opening_right
207
                goal.position_left = mouth.position_left
208
                goal.position_center  = mouth.position_center
209
                goal.position_right = mouth.position_right
210
                
211
                #send the goal to the server
212
                self.mouthtarget_client.send_goal(goal)
213
                
214
                #wait for the server to finish 
215
                if (blocking):
216
                        timed_out = self.mouthtarget_client.wait_for_result(rospy.Duration.from_sec(MiddlewareROS.ROS_ACTION_CALL_TIMEOUT))
217
                        if (timed_out):
218
                                self.logger.error("%s timed out waiting for result" % (sys._getframe().f_code.co_name))
219
                
220
                self.logger.debug("mouth rpc done")
221
        
222
        def publish_speech(self, text_, blocking):
223
                """publish a tts request via mw
224
                :param text_: text to synthesize and speak
225
                :param blocking: True if this call should block until execution finished on robot
226
                """
227
                self.logger.debug("calling the speech rpc (%s)..." % ("BLOCKING" if blocking else "NON-BLOCKING"))
228
                
229
                #create a goal to send to the action server.
230
                goal = speechGoal(text=text_)
231
                
232
                #send the goal to the server
233
                self.speech_client.send_goal(goal)
234
                
235
                #wait for the server to finish 
236
                if (blocking):
237
                        timed_out = self.speech_client.wait_for_result(rospy.Duration.from_sec(MiddlewareROS.ROS_ACTION_CALL_TIMEOUT))
238
                        if (timed_out):
239
                                self.logger.error("%s timed out waiting for result" % (sys._getframe().f_code.co_name))
240
                                
241
                self.logger.debug("speech rpc done")
242
        
243
        #######################################################################
244
        # some helpers
245
        def convert_animationtype_to_rosval(self, value):
246
                """convert RobotAnimation.value to ROS animation value
247
                :param value: RobotAnimation.* id to convert to rsb id
248
                """
249
                #NOTE: this convertion is important as the actual integer values of
250
                #      thy python api and the protobuf might be different
251
                if (value == RobotAnimation.IDLE):
252
                        return animationGoal.IDLE
253
                elif (value == RobotAnimation.HEAD_NOD):
254
                        return animationGoal.HEAD_NOD
255
                elif (value == RobotAnimation.HEAD_SHAKE):
256
                        return animationGoal.HEAD_SHAKE
257
                elif (value == RobotAnimation.EYEBLINK_L):
258
                        return animationGoal.EYEBLINK_L
259
                elif (value == RobotAnimation.EYEBLINK_R):
260
                        return  animationGoal.EYEBLINK_R
261
                elif (value == RobotAnimation.EYEBLINK_BOTH):
262
                        return  animationGoal.EYEBLINK_BOTH
263
                elif (value == RobotAnimation.EYEBROWS_RAISE):
264
                        return  animationGoal.EYEBROWS_RAISE
265
                elif (value == RobotAnimation.EYEBROWS_LOWER):
266
                        return  animationGoal.EYEBROWS_LOWER
267
                else:
268
                        self.logger.error("invalid animation type %d\n" % (value))
269
                        return  animationGoal.NEUTRAL
270
                
271
        def convert_emotiontype_to_rosval(self, value):
272
                """convert RobotEmotion.value to ROS animation value
273
                :param value: RobotEmotion.* id to convert to ros id
274
                """
275
                #NOTE: this convertion is important as the actual integer values of
276
                #      thy python api and the protobuf might be different
277

    
278
                if (value == RobotEmotion.NEUTRAL):
279
                        return emotionstateGoal.NEUTRAL
280
                elif (value == RobotEmotion.HAPPY):
281
                        return emotionstateGoal.HAPPY
282
                elif (value == RobotEmotion.SAD):
283
                        return emotionstateGoal.SAD
284
                elif (value == RobotEmotion.ANGRY):
285
                        return emotionstateGoal.ANGRY
286
                elif (value == RobotEmotion.SURPRISED):
287
                        return  emotionstateGoal.SURPRISED
288
                elif (value == RobotEmotion.FEAR):
289
                        return emotionstateGoal.FEAR
290
                else:
291
                        self.logger.error("invalid emotion type %d\n" % (value))
292
                        return  emotionstateGoal.NEUTRAL