小猫

版主
  • 主题:39
  • 回复:39
  • 金钱:132
  • 积分:181
本帖最后由 蒜泥小猫 于 2014-8-13 11:33 编辑

      如今语音识别在PC机和智能手机上炒的火热,ROS走在技术的最前沿当然也不会错过这么帅的技术。ROS中使用了CMU SphinxFestival开源项目中的代码,发布了独立的语音识别包,而且可以将识别出来的语音转换成文字,然后让机器人智能处理后说话。


一、语音识别包
1、安装
        安装很简单,直接使用ubuntu命令即可,首先安装依赖库:


  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_598        ]nbsp;sudo apt-get install gstreamer0.10-pocketsphinx  
  3. [        DISCUZ_CODE_598        ]nbsp;sudo apt-get install ros-fuerte-audio-common  
  4. [        DISCUZ_CODE_598        ]nbsp;sudo apt-get install libasound2  
复制代码
然后来安装ROS包:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_599        ]nbsp;svn checkout http://albany-ros-pkg.googlecode.com/svn/trunk/rharmony  
  3. [        DISCUZ_CODE_599        ]nbsp;rosmake pocketsphinx  
复制代码
     其中的核心文件就是nodes文件夹下的recognizer.py文件了。这个文件通过麦克风收集语音信息,然后调用语音识别库进行识别生成文本信息,通过/recognizer/output消息发布,其他节点就可以订阅该消息然后进行相应的处理了。

2、测试
        安装完成后我们就可以运行测试了。
        首先,插入你的麦克风设备,然后在系统设置里测试麦克风是否有语音输入。
        然后,运行包中的测试程序:       
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_600        ]nbsp;roslaunch pocketsphinx robocup.launch  
复制代码
       此时,在终端中会看到一大段的信息。尝试说一些简单的语句,当然,必须是英语,例如:bring me the glasscome with me,看看能不能识别出来。

    我们也可以直接看ROS最后发布的结果消息:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_601        ]nbsp;rostopic echo /recognizer/output  
复制代码


二、语音库
1、查看语音库
        这个语音识别时一种离线识别的方法,将一些常用的词汇放到一个文件中,作为识别的文本库,然后分段识别语音信号,最后在库中搜索对应的文本信息。如果想看语音识别库中有哪些文本信息,可以通过下面的指令进行查询:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_602        ]nbsp;roscd pocketsphinx/demo  
  3. [        DISCUZ_CODE_602        ]nbsp;more robocup.corpus  
复制代码
2、添加语音库
        我们可以自己向语音库中添加其他的文本识别信息,《ros by example》自带的例程中是带有语音识别的例程的,而且有添加语音库的例子。
        首先看看例子中要添加的文本信息:
[plain] view plaincopy
$ roscd rbx1_speech/config  
$ more nav_commands.txt  

    这就是需要添加的文本,我们也可以修改其中的某些文本,改成自己需要的。
        然后我们要把这个文件在线生成语音信息和库文件,这一步需要登陆网站http://www.speech.cs.cmu.edu/tools/lmtool-new.html,根据网站的提示上传文件,然后在线编译生成库文件。
     把下载的文件都解压放在rbx1_speech包的config文件夹下。我们可以给这些文件改个名字:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_603        ]nbsp;roscd rbx1_speech/config  
  3. [        DISCUZ_CODE_603        ]nbsp;rename -f 's/3026/nav_commands/' *  
复制代码
    在rbx1_speech/launch文件夹下看看voice_nav_commands.launch这个文件:


  1. [plain] view plaincopy
  2. <launch>  
  3. <node name="recognizer" pkg="pocketsphinx" type="recognizer.py"  
  4. output="screen">  
  5. <param name="lm" value="$(find rbx1_speech)/config/nav_commands.lm"/>  
  6. <param name="dict" value="$(find rbx1_speech)/config/nav_commands.dic"/>  
  7. </node>  
  8. </launch>  
复制代码
       可以看到,这个launch文件在运行recognizer.py节点的时候使用了我们生成的语音识别库和文件参数,这样就可以实用我们自己的语音库来进行语音识别了。
       通过之前的命令来测试一下效果如何吧:


  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_605        ]nbsp;roslaunch rbx1_speech voice_nav_commands.launch  
  3. [        DISCUZ_CODE_605        ]nbsp;rostopic echo /recognizer/output  
复制代码
三、语音控制
        有了语音识别,我们就可以来做很多犀利的应用了,首先先来尝试一下用语音来控制机器人动作。

1、机器人控制节点
        前面说到的recognizer.py会将最后识别的文本信息通过消息发布,那么我们来编写一个机器人控制节点接收这个消息,进行相应的控制即可。
        pocketsphinx包中本身有一个语音控制发布Twist消息的例程voice_cmd_vel.pyrbx1_speech包对其进行了一些简化修改,在nodes文件夹里可以查看voice_nav.py文件:
  1. [python] view plaincopy
  2. #!/usr/bin/env python  
  3.   
  4. """
  5.   voice_nav.py
  6.    
  7.   Allows controlling a mobile base using simple speech commands.
  8.    
  9.   Based on the voice_cmd_vel.py script by Michael Ferguson in
  10.   the pocketsphinx ROS package.
  11.    
  12.   See http://www.ros.org/wiki/pocketsphinx
  13. """  
  14.   
  15. import roslib; roslib.load_manifest('rbx1_speech')  
  16. import rospy  
  17. from geometry_msgs.msg import Twist  
  18. from std_msgs.msg import String  
  19. from math import copysign  
  20.   
  21. class VoiceNav:  
  22.     def __init__(self):  
  23.         rospy.init_node('voice_nav')  
  24.          
  25.         rospy.on_shutdown(self.cleanup)  
  26.          
  27.         # Set a number of parameters affecting the robot's speed  
  28.         self.max_speed = rospy.get_param("~max_speed", 0.4)  
  29.         self.max_angular_speed = rospy.get_param("~max_angular_speed", 1.5)  
  30.         self.speed = rospy.get_param("~start_speed", 0.1)  
  31.         self.angular_speed = rospy.get_param("~start_angular_speed", 0.5)  
  32.         self.linear_increment = rospy.get_param("~linear_increment", 0.05)  
  33.         self.angular_increment = rospy.get_param("~angular_increment", 0.4)  
  34.          
  35.         # We don't have to run the script very fast  
  36.         self.rate = rospy.get_param("~rate", 5)  
  37.         r = rospy.Rate(self.rate)  
  38.          
  39.         # A flag to determine whether or not voice control is paused  
  40.         self.paused = False  
  41.          
  42.         # Initialize the Twist message we will publish.  
  43.         self.cmd_vel = Twist()  
  44.   
  45.         # Publish the Twist message to the cmd_vel topic  
  46.         self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist)  
  47.          
  48.         # Subscribe to the /recognizer/output topic to receive voice commands.  
  49.         rospy.Subscriber('/recognizer/output', String, self.speech_callback)  
  50.          
  51.         # A mapping from keywords or phrases to commands  
  52.         self.keywords_to_command = {'stop': ['stop', 'halt', 'abort', 'kill', 'panic', 'off', 'freeze', 'shut down', 'turn off', 'help', 'help me'],  
  53.                                     'slower': ['slow down', 'slower'],  
  54.                                     'faster': ['speed up', 'faster'],  
  55.                                     'forward': ['forward', 'ahead', 'straight'],  
  56.                                     'backward': ['back', 'backward', 'back up'],  
  57.                                     'rotate left': ['rotate left'],  
  58.                                     'rotate right': ['rotate right'],  
  59.                                     'turn left': ['turn left'],  
  60.                                     'turn right': ['turn right'],  
  61.                                     'quarter': ['quarter speed'],  
  62.                                     'half': ['half speed'],  
  63.                                     'full': ['full speed'],  
  64.                                     'pause': ['pause speech'],  
  65.                                     'continue': ['continue speech']}  
  66.          
  67.         rospy.loginfo("Ready to receive voice commands")  
  68.          
  69.         # We have to keep publishing the cmd_vel message if we want the robot to keep moving.  
  70.         while not rospy.is_shutdown():  
  71.             self.cmd_vel_pub.publish(self.cmd_vel)  
  72.             r.sleep()                        
  73.               
  74.     def get_command(self, data):  
  75.         # Attempt to match the recognized word or phrase to the   
  76.         # keywords_to_command dictionary and return the appropriate  
  77.         # command  
  78.         for (command, keywords) in self.keywords_to_command.iteritems():  
  79.             for word in keywords:  
  80.                 if data.find(word) > -1:  
  81.                     return command  
  82.          
  83.     def speech_callback(self, msg):  
  84.         # Get the motion command from the recognized phrase  
  85.         command = self.get_command(msg.data)  
  86.          
  87.         # Log the command to the screen  
  88.         rospy.loginfo("Command: " + str(command))  
  89.          
  90.         # If the user has asked to pause/continue voice control,  
  91.         # set the flag accordingly   
  92.         if command == 'pause':  
  93.             self.paused = True  
  94.         elif command == 'continue':  
  95.             self.paused = False  
  96.          
  97.         # If voice control is paused, simply return without  
  98.         # performing any action  
  99.         if self.paused:  
  100.             return         
  101.          
  102.         # The list of if-then statements should be fairly  
  103.         # self-explanatory  
  104.         if command == 'forward':      
  105.             self.cmd_vel.linear.x = self.speed  
  106.             self.cmd_vel.angular.z = 0  
  107.               
  108.         elif command == 'rotate left':  
  109.             self.cmd_vel.linear.x = 0  
  110.             self.cmd_vel.angular.z = self.angular_speed  
  111.                   
  112.         elif command == 'rotate right':   
  113.             self.cmd_vel.linear.x = 0        
  114.             self.cmd_vel.angular.z = -self.angular_speed  
  115.               
  116.         elif command == 'turn left':  
  117.             if self.cmd_vel.linear.x != 0:  
  118.                 self.cmd_vel.angular.z += self.angular_increment  
  119.             else:         
  120.                 self.cmd_vel.angular.z = self.angular_speed  
  121.                   
  122.         elif command == 'turn right':      
  123.             if self.cmd_vel.linear.x != 0:  
  124.                 self.cmd_vel.angular.z -= self.angular_increment  
  125.             else:         
  126.                 self.cmd_vel.angular.z = -self.angular_speed  
  127.                   
  128.         elif command == 'backward':  
  129.             self.cmd_vel.linear.x = -self.speed  
  130.             self.cmd_vel.angular.z = 0  
  131.               
  132.         elif command == 'stop':   
  133.             # Stop the robot!  Publish a Twist message consisting of all zeros.           
  134.             self.cmd_vel = Twist()  
  135.          
  136.         elif command == 'faster':  
  137.             self.speed += self.linear_increment  
  138.             self.angular_speed += self.angular_increment  
  139.             if self.cmd_vel.linear.x != 0:  
  140.                 self.cmd_vel.linear.x += copysign(self.linear_increment, self.cmd_vel.linear.x)  
  141.             if self.cmd_vel.angular.z != 0:  
  142.                 self.cmd_vel.angular.z += copysign(self.angular_increment, self.cmd_vel.angular.z)  
  143.               
  144.         elif command == 'slower':  
  145.             self.speed -= self.linear_increment  
  146.             self.angular_speed -= self.angular_increment  
  147.             if self.cmd_vel.linear.x != 0:  
  148.                 self.cmd_vel.linear.x -= copysign(self.linear_increment, self.cmd_vel.linear.x)  
  149.             if self.cmd_vel.angular.z != 0:  
  150.                 self.cmd_vel.angular.z -= copysign(self.angular_increment, self.cmd_vel.angular.z)  
  151.                   
  152.         elif command in ['quarter', 'half', 'full']:  
  153.             if command == 'quarter':  
  154.                 self.speed = copysign(self.max_speed / 4, self.speed)  
  155.          
  156.             elif command == 'half':  
  157.                 self.speed = copysign(self.max_speed / 2, self.speed)  
  158.               
  159.             elif command == 'full':  
  160.                 self.speed = copysign(self.max_speed, self.speed)  
  161.               
  162.             if self.cmd_vel.linear.x != 0:  
  163.                 self.cmd_vel.linear.x = copysign(self.speed, self.cmd_vel.linear.x)  
  164.   
  165.             if self.cmd_vel.angular.z != 0:  
  166.                 self.cmd_vel.angular.z = copysign(self.angular_speed, self.cmd_vel.angular.z)  
  167.                   
  168.         else:  
  169.             return  
  170.   
  171.         self.cmd_vel.linear.x = min(self.max_speed, max(-self.max_speed, self.cmd_vel.linear.x))  
  172.         self.cmd_vel.angular.z = min(self.max_angular_speed, max(-self.max_angular_speed, self.cmd_vel.angular.z))  
  173.   
  174.     def cleanup(self):  
  175.         # When shutting down be sure to stop the robot!  
  176.         twist = Twist()  
  177.         self.cmd_vel_pub.publish(twist)  
  178.         rospy.sleep(1)  
  179.   
  180. if __name__=="__main__":  
  181.     try:  
  182.         VoiceNav()  
  183.         rospy.spin()  
  184.     except rospy.ROSInterruptException:  
  185.         rospy.loginfo("Voice navigation terminated.")  
复制代码
可以看到,代码中定义了接收到各种命令时的控制策略。

2、仿真测试
        和前面一样,我们在rviz中进行仿真测试。
        首先是运行一个机器人模型:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_607        ]nbsp;roslaunch rbx1_bringup fake_turtlebot.launch  
复制代码
然后打开rviz
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_608        ]nbsp;rosrun rviz rviz -d `rospack find rbx1_nav`/sim_fuerte.vcg  
复制代码
   如果不喜欢在终端里看输出,可以打开gui界面:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_609        ]nbsp;rxconsole  
复制代码
再打开语音识别的节点:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_610        ]nbsp;roslaunch rbx1_speech voice_nav_commands.launch  
复制代码
最后就是机器人的控制节点了:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_611        ]nbsp;roslaunch rbx1_speech turtlebot_voice_nav.launch  
复制代码
OK,打开上面这一堆的节点之后,就可以开始了。可以使用的命令如下:
下图是测试结果,准确度还是欠佳的:


四、播放语音
        现在机器人已经可以按照我们说的话行动了,要是机器人可以和我们对话就更好了。ROS中已经集成了这样的包,下面就来尝试一下。
        运行下面的命令:

  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_612        ]nbsp;rosrun sound_play soundplay_node.py  
  3. [        DISCUZ_CODE_612        ]nbsp;rosrun sound_play say.py "Greetings Humans. Take me to your leader."  
复制代码
       有没有听见声音!ROS通过识别我们输入的文本,让机器人读了出来。发出这个声音的人叫做kal_diphone,如果不喜欢,我们也可以换一个人来读:


  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_613        ]nbsp;sudo apt-get install festvox-don  
  3. [        DISCUZ_CODE_613        ]nbsp;rosrun sound_play say.py "Welcome to the future" voice_don_diphone
复制代码
  哈哈,这回换了一个人吧,好吧,这不也是我们的重点。
       rbx1_speech/nodes文件夹中有一个让机器人说话的节点talkback.py:

  1. [python] view plaincopy
  2. #!/usr/bin/env python  
  3.   
  4. """
  5.     talkback.py - Version 0.1 2012-01-10
  6.      
  7.     Use the sound_play client to say back what is heard by the pocketsphinx recognizer.
  8.      
  9.     Created for the Pi Robot Project: http://www.pirobot.org
  10.     Copyright (c) 2012 Patrick Goebel.  All rights reserved.

  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License as published by
  13.     the Free Software Foundation; either version 2 of the License, or
  14.     (at your option) any later version.5
  15.      
  16.     This program is distributed in the hope that it will be useful,
  17.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.     GNU General Public License for more details at:
  20.      
  21.     http://www.gnu.org/licenses/gpl.htmlPoint
  22. """  
  23.   
  24. import roslib; roslib.load_manifest('rbx1_speech')  
  25. import rospy  
  26. from std_msgs.msg import String  
  27. from sound_play.libsoundplay import SoundClient  
  28. import sys  
  29.   
  30. class TalkBack:  
  31.     def __init__(self, script_path):  
  32.         rospy.init_node('talkback')  
  33.   
  34.         rospy.on_shutdown(self.cleanup)  
  35.          
  36.         # Set the default TTS voice to use  
  37.         self.voice = rospy.get_param("~voice", "voice_don_diphone")  
  38.          
  39.         # Set the wave file path if used  
  40.         self.wavepath = rospy.get_param("~wavepath", script_path + "/../sounds")  
  41.          
  42.         # Create the sound client object  
  43.         self.soundhandle = SoundClient()  
  44.          
  45.         # Wait a moment to let the client connect to the  
  46.         # sound_play server  
  47.         rospy.sleep(1)  
  48.          
  49.         # Make sure any lingering sound_play processes are stopped.  
  50.         self.soundhandle.stopAll()  
  51.          
  52.         # Announce that we are ready for input  
  53.         self.soundhandle.playWave(self.wavepath + "/R2D2a.wav")  
  54.         rospy.sleep(1)  
  55.         self.soundhandle.say("Ready", self.voice)  
  56.          
  57.         rospy.loginfo("Say one of the navigation commands...")  
  58.   
  59.         # Subscribe to the recognizer output and set the callback function  
  60.         rospy.Subscriber('/recognizer/output', String, self.talkback)  
  61.          
  62.     def talkback(self, msg):  
  63.         # Print the recognized words on the screen  
  64.         rospy.loginfo(msg.data)  
  65.          
  66.         # Speak the recognized words in the selected voice  
  67.         self.soundhandle.say(msg.data, self.voice)  
  68.          
  69.         # Uncomment to play one of the built-in sounds  
  70.         #rospy.sleep(2)  
  71.         #self.soundhandle.play(5)  
  72.          
  73.         # Uncomment to play a wave file  
  74.         #rospy.sleep(2)  
  75.         #self.soundhandle.playWave(self.wavepath + "/R2D2a.wav")  
  76.   
  77.     def cleanup(self):  
  78.         self.soundhandle.stopAll()  
  79.         rospy.loginfo("Shutting down talkback node...")  
  80.   
  81. if __name__=="__main__":  
  82.     try:  
  83.         TalkBack(sys.path[0])  
  84.         rospy.spin()  
  85.     except rospy.ROSInterruptException:  
  86.         rospy.loginfo("Talkback node terminated.")  
复制代码
我们来运行看一下效果:
  1. [plain] view plaincopy
  2. [        DISCUZ_CODE_615        ]nbsp;roslaunch rbx1_speech talkback.launch
复制代码
       然后再说话,ROS不仅将文本信息识别出来了,而且还读了出来,厉害吧。当然了,现在还没有加入什么人工智能的算法,不能让机器人和我们聪明的说话,不过这算是基础了,以后有时间再研究一下人工智能就更犀利了。