RAPP Platform  v0.6.0
RAPP Platform is a collection of ROS nodes and back-end processes that aim to deliver ready-to-use generic services to robots
 All Classes Namespaces Files Functions Variables Macros
speech_recognition_sphinx4_handler_node.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- encode: utf-8 -*-
3 
4 #Copyright 2015 RAPP
5 
6 #Licensed under the Apache License, Version 2.0 (the "License");
7 #you may not use this file except in compliance with the License.
8 #You may obtain a copy of the License at
9 
10  #http://www.apache.org/licenses/LICENSE-2.0
11 
12 #Unless required by applicable law or agreed to in writing, software
13 #distributed under the License is distributed on an "AS IS" BASIS,
14 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 #See the License for the specific language governing permissions and
16 #limitations under the License.
17 
18 # Authors: Aris Thallas
19 # contact: aris.thallas@{iti.gr, gmail.com}
20 
21 import rospy
22 import sys
23 import time
24 import hashlib
25 import threading
26 import os.path
27 import rospkg
28 
29 from speech_recognition_sphinx4 import *
30 
31 from rapp_utilities import RappUtilities
32 
33 
34 from rapp_platform_ros_communications.srv import (
35  SpeechRecognitionSphinx4TotalSrv,
36  SpeechRecognitionSphinx4TotalSrvResponse
37  )
38 
39 ## @class SpeechRecognitionSphinx4HandlerNode
40 # @brief Maintains Sphinx instances to perform speech recognition
41 #
42 # Maintains a number of child processes to perform speech recognition utilizing
43 # Sphinx4
44 # (rapp_speech_detection_sphinx4.speech_recognition_sphinx4.SpeechRecognitionSphinx4).
45 # Provides ros services and handles the requests according to the child
46 # processes' status.
48 
49  ## @brief Initializes the subprocesses and the services (constructor)
50  def __init__(self):
51 
52  ## The number of child subprocesses.
53  self._threads = \
54  rospy.get_param("rapp_speech_detection_sphinx4_threads")
55 
56  if rospy.get_param("rapp_speech_detection_sphinx4_preconfigure"):
57  configurations = self._getPreconfigurationNames()
58  ## The subprocesses structure that contains information used for the
59  # subprocess handling
61  'sphinx': SpeechRecognitionSphinx4(configurations[i]), \
62  'running': False, \
63  'configuration_hash': 0\
64  } for i in range(self._threads)]
65  for proc in self._availableProcesses:
66  proc['configuration_hash'] = proc['sphinx'].getConfigurationHash()
67  else:
68  self._availableProcesses = [{
69  'sphinx': SpeechRecognitionSphinx4(), \
70  'running': False, \
71  'configuration_hash': 0\
72  } for i in range(self._threads)]
73 
74  ## Thread conditional variable used for the subprocess scheduling
75  self._lock = threading.Condition()
76  ## Total service callback threads waiting to execute
77  self._threadCounter = 0
78 
79  serv_batch_topic = \
80  rospy.get_param("rapp_speech_detection_sphinx4_total_topic")
81  if(not serv_batch_topic):
82  rospy.logerror("Sphinx4 Speech detection batch topic param not found")
83 
84  ## Ros service server for sphinx speech recognition
85  self._speech_recognition_batch_service = rospy.Service( \
86  serv_batch_topic, SpeechRecognitionSphinx4TotalSrv, \
88 
89 
90  ## @brief Specifies the requested preconfiguration names
91  #
92  # Reads and creates a matrix with the configuration name requested from
93  # rapp_speech_detection_sphinx4::cfg::sphinx4_wrapper_params.yaml
94  #
95  # @return preconf [ list<string> ] The preconfiguration names for all subprocesses
97  preconf = []
98 
99  RappUtilities.rapp_print( 'Fetcing preconfiguration names' )
100  # Get number of requested preconfigurations
101  confNumber = \
102  rospy.get_param("rapp_speech_detection_sphinx4_preconfigure_number")
103 
104  # Too many configurations
105  if confNumber > self._threads:
106  RappUtilities.rapp_print("Sphinx preconfigurations requested exceed " + \
107  "Sphinx processes. Truncating", 'WARN')
108  confNumber = self._threads
109 
110  # Check actual unique configurations provided
111  if rospy.has_param("rapp_speech_detection_sphinx4_preconfiguration"):
112  confDict = \
113  rospy.get_param("rapp_speech_detection_sphinx4_preconfiguration")
114  uniqueConfigurations = len( confDict )
115  if uniqueConfigurations > confNumber:
116  uniqueConfigurations = confNumber
117  else:
118  RappUtilities.rapp_print("Preconfigurations requested, but none was " + \
119  "provided", 'ERROR')
120  for it in range(self._threads):
121  preconf.append(None)
122  return preconf
123 
124  for confIter in range(confNumber):
125  preconf.append(confDict[ str(confIter % uniqueConfigurations) ])
126 
127  for it in range(self._threads - confNumber):
128  preconf.append(None)
129 
130  RappUtilities.rapp_print(str(preconf), 'DEBUG')
131  return preconf
132 
133 
134  ## @brief The callback to perform speech recognition
135  #
136  # @param req [rapp_platform_ros_communications::SpeechDetectionSphinx4Wrapper::SpeechRecognitionSphinx4TotalSrvRequest] The service request
137  # @return res [rapp_platform_ros_communications::SpeechDetectionSphinx4Wrapper::SpeechRecognitionSphinx4TotalSrvResponse] The service response
139 
140  RappUtilities.rapp_print("Received service request", 'DEBUG')
141  res = SpeechRecognitionSphinx4TotalSrvResponse()
142 
143  request_hash = self._calculateRequestHash( req )
144 
145  self._lock.acquire()
146  self._threadCounter += 1
147  if self._threadCounter > self._threads:
148  self._lock.wait()
149 
150  # Search for available Sphinx with similar configuration
151  for proc in self._availableProcesses:
152  if proc['running'] == False and \
153  proc['configuration_hash'] == request_hash:
154 
155  RappUtilities.rapp_print("Found Sphinx process with same configuration",\
156  'DEBUG')
157  proc['running'] = True
158  self._lock.release()
159  res = proc['sphinx'].speechRecognitionBatch( req )
160 
161  self._lock.acquire()
162  proc['running'] = False
163  self._threadCounter -= 1
164  if self._threadCounter >= self._threads:
165  self._lock.notify()
166  self._lock.release()
167 
168  return res
169 
170  # Search for available Sphinx
171  for proc in self._availableProcesses:
172  if proc['running'] == False:
173 
174  proc['configuration_hash'] = request_hash
175  proc['running'] = True
176 
177  RappUtilities.rapp_print("Found Sphinx process", 'DEBUG')
178  self._lock.release()
179  res = proc['sphinx'].speechRecognitionBatch( req )
180 
181  self._lock.acquire()
182  proc['running'] = False
183  self._threadCounter -= 1
184  if self._threadCounter >= self._threads:
185  self._lock.notify()
186  self._lock.release()
187 
188  return res
189 
190  ## @brief Calculates the service request sha1 hash for process handling purposes
191  #
192  # Hash is used to identify common request configurations for proper subprocess selection.
193  # (Requests with common requests do not require reconfiguration reducing computation time)
194  #
195  # @param req [rapp_platform_ros_communications::SpeechDetectionSphinx4Wrapper::SpeechRecognitionSphinx4TotalSrvRequest] The service request
196  #
197  # @return hexdigest [string] The hash digest containing only hexadecimal digits
198  def _calculateRequestHash(self, req):
199  hash_object = hashlib.sha1()
200  hash_object.update( req.language )
201  for word in req.words:
202  hash_object.update( word )
203  for gram in req.grammar:
204  hash_object.update( gram )
205  for sent in req.sentences:
206  hash_object.update( sent )
207  return hash_object.hexdigest()
208 
209 
210 if __name__ == "__main__":
211  rospy.init_node('SpeechRecognitionSphinx4')
212 
213  rospack = rospkg.RosPack()
214  sphinx_class = rospack.get_path('rapp_speech_detection_sphinx4') + \
215  "/src/Sphinx4.class"
216 
217  if not os.path.isfile(sphinx_class):
218  rospy.logerr("speech_recognition_sphinx4_handler_node: Sphinx.class file is missing. You can execute 'buildJava.sh'")
219  else:
220  SpeechRecognitionSphinx4HandlerNode = SpeechRecognitionSphinx4HandlerNode()
221  RappUtilities.rapp_print("Sphinx4 Handler node initialized", 'DEBUG')
222  rospy.spin()
223 
_availableProcesses
The subprocesses structure that contains information used for the subprocess handling.