voice_nav.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env python
  2. """
  3. voice_nav.py - Version 1.1 2013-12-20
  4. Allows controlling a mobile base using simple speech commands.
  5. Based on the voice_cmd_vel.py script by Michael Ferguson in
  6. the pocketsphinx ROS package.
  7. See http://www.ros.org/wiki/pocketsphinx
  8. """
  9. import rospy
  10. from geometry_msgs.msg import Twist
  11. from std_msgs.msg import String
  12. from math import copysign
  13. class VoiceNav:
  14. def __init__(self):
  15. rospy.init_node('voice_nav')
  16. rospy.on_shutdown(self.cleanup)
  17. # Set a number of parameters affecting the robot's speed
  18. self.max_speed = rospy.get_param("~max_speed", 0.4)
  19. self.max_angular_speed = rospy.get_param("~max_angular_speed", 1.5)
  20. self.speed = rospy.get_param("~start_speed", 0.1)
  21. self.angular_speed = rospy.get_param("~start_angular_speed", 0.5)
  22. self.linear_increment = rospy.get_param("~linear_increment", 0.05)
  23. self.angular_increment = rospy.get_param("~angular_increment", 0.4)
  24. # We don't have to run the script very fast
  25. self.rate = rospy.get_param("~rate", 5)
  26. r = rospy.Rate(self.rate)
  27. # A flag to determine whether or not voice control is paused
  28. self.paused = False
  29. # Initialize the Twist message we will publish.
  30. self.cmd_vel = Twist()
  31. # Publish the Twist message to the cmd_vel topic
  32. self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=5)
  33. # Subscribe to the /recognizer/output topic to receive voice commands.
  34. rospy.Subscriber('/recognizer/output', String, self.speech_callback)
  35. # A mapping from keywords or phrases to commands
  36. self.keywords_to_command = {'stop': ['stop', 'halt', 'abort', 'kill', 'panic', 'off', 'freeze', 'shut down', 'turn off', 'help', 'help me'],
  37. 'slower': ['slow down', 'slower'],
  38. 'faster': ['speed up', 'faster'],
  39. 'forward': ['forward', 'ahead', 'straight'],
  40. 'backward': ['back', 'backward', 'back up'],
  41. 'rotate left': ['rotate left'],
  42. 'rotate right': ['rotate right'],
  43. 'turn left': ['turn left'],
  44. 'turn right': ['turn right'],
  45. 'quarter': ['quarter speed'],
  46. 'half': ['half speed'],
  47. 'full': ['full speed'],
  48. 'pause': ['pause speech'],
  49. 'continue': ['continue speech']}
  50. rospy.loginfo("Ready to receive voice commands")
  51. # We have to keep publishing the cmd_vel message if we want the robot to keep moving.
  52. while not rospy.is_shutdown():
  53. self.cmd_vel_pub.publish(self.cmd_vel)
  54. r.sleep()
  55. def get_command(self, data):
  56. # Attempt to match the recognized word or phrase to the
  57. # keywords_to_command dictionary and return the appropriate
  58. # command
  59. for (command, keywords) in self.keywords_to_command.iteritems():
  60. for word in keywords:
  61. if data.find(word) > -1:
  62. return command
  63. def speech_callback(self, msg):
  64. # Get the motion command from the recognized phrase
  65. command = self.get_command(msg.data)
  66. # Log the command to the screen
  67. rospy.loginfo("Command: " + str(command))
  68. # If the user has asked to pause/continue voice control,
  69. # set the flag accordingly
  70. if command == 'pause':
  71. self.paused = True
  72. elif command == 'continue':
  73. self.paused = False
  74. # If voice control is paused, simply return without
  75. # performing any action
  76. if self.paused:
  77. return
  78. # The list of if-then statements should be fairly
  79. # self-explanatory
  80. if command == 'forward':
  81. self.cmd_vel.linear.x = self.speed
  82. self.cmd_vel.angular.z = 0
  83. elif command == 'rotate left':
  84. self.cmd_vel.linear.x = 0
  85. self.cmd_vel.angular.z = self.angular_speed
  86. elif command == 'rotate right':
  87. self.cmd_vel.linear.x = 0
  88. self.cmd_vel.angular.z = -self.angular_speed
  89. elif command == 'turn left':
  90. if self.cmd_vel.linear.x != 0:
  91. self.cmd_vel.angular.z += self.angular_increment
  92. else:
  93. self.cmd_vel.angular.z = self.angular_speed
  94. elif command == 'turn right':
  95. if self.cmd_vel.linear.x != 0:
  96. self.cmd_vel.angular.z -= self.angular_increment
  97. else:
  98. self.cmd_vel.angular.z = -self.angular_speed
  99. elif command == 'backward':
  100. self.cmd_vel.linear.x = -self.speed
  101. self.cmd_vel.angular.z = 0
  102. elif command == 'stop':
  103. # Stop the robot! Publish a Twist message consisting of all zeros.
  104. self.cmd_vel = Twist()
  105. elif command == 'faster':
  106. self.speed += self.linear_increment
  107. self.angular_speed += self.angular_increment
  108. if self.cmd_vel.linear.x != 0:
  109. self.cmd_vel.linear.x += copysign(self.linear_increment, self.cmd_vel.linear.x)
  110. if self.cmd_vel.angular.z != 0:
  111. self.cmd_vel.angular.z += copysign(self.angular_increment, self.cmd_vel.angular.z)
  112. elif command == 'slower':
  113. self.speed -= self.linear_increment
  114. self.angular_speed -= self.angular_increment
  115. if self.cmd_vel.linear.x != 0:
  116. self.cmd_vel.linear.x -= copysign(self.linear_increment, self.cmd_vel.linear.x)
  117. if self.cmd_vel.angular.z != 0:
  118. self.cmd_vel.angular.z -= copysign(self.angular_increment, self.cmd_vel.angular.z)
  119. elif command in ['quarter', 'half', 'full']:
  120. if command == 'quarter':
  121. self.speed = copysign(self.max_speed / 4, self.speed)
  122. elif command == 'half':
  123. self.speed = copysign(self.max_speed / 2, self.speed)
  124. elif command == 'full':
  125. self.speed = copysign(self.max_speed, self.speed)
  126. if self.cmd_vel.linear.x != 0:
  127. self.cmd_vel.linear.x = copysign(self.speed, self.cmd_vel.linear.x)
  128. if self.cmd_vel.angular.z != 0:
  129. self.cmd_vel.angular.z = copysign(self.angular_speed, self.cmd_vel.angular.z)
  130. else:
  131. return
  132. self.cmd_vel.linear.x = min(self.max_speed, max(-self.max_speed, self.cmd_vel.linear.x))
  133. self.cmd_vel.angular.z = min(self.max_angular_speed, max(-self.max_angular_speed, self.cmd_vel.angular.z))
  134. def cleanup(self):
  135. # When shutting down be sure to stop the robot!
  136. twist = Twist()
  137. self.cmd_vel_pub.publish(twist)
  138. rospy.sleep(1)
  139. if __name__=="__main__":
  140. try:
  141. VoiceNav()
  142. rospy.spin()
  143. except rospy.ROSInterruptException:
  144. rospy.loginfo("Voice navigation terminated.")