ROS探索总结(十)——语音控制

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

一、语音识别包

1、安装

        安装很简单,直接使用ubuntu命令即可,首先安装依赖库:

[plain] view plaincopy
  1. $ sudo apt-get install gstreamer0.10-pocketsphinx
  2. $ sudo apt-get install ros-fuerte-audio-common
  3. $ sudo apt-get install libasound2
        然后来安装ROS包:

[plain] view plaincopy
  1. $ svn checkout http://albany-ros-pkg.googlecode.com/svn/trunk/rharmony
  2. $ rosmake pocketsphinx
        其中的核心文件就是nodes文件夹下的recognizer.py文件了。这个文件通过麦克风收集语音信息,然后调用语音识别库进行识别生成文本信息,通过/recognizer/output消息发布,其他节点就可以订阅该消息然后进行相应的处理了。

2、测试

        安装完成后我们就可以运行测试了。
        首先,插入你的麦克风设备,然后在系统设置里测试麦克风是否有语音输入。
        然后,运行包中的测试程序:

[plain] view plaincopy
  1. $ roslaunch pocketsphinx robocup.launch
        此时,在终端中会看到一大段的信息。尝试说一些简单的语句,当然,必须是英语,例如:bring me the glass,come with me,看看能不能识别出来。
       《ros by example》这本书中写得测试还是很准确的,但是我在测试中感觉识别相当不准确,可能是我的英语太差了吧。
        
        我们也可以直接看ROS最后发布的结果消息:

[plain] view plaincopy
  1. $ rostopic echo /recognizer/output
        

二、语音库

1、查看语音库

        这个语音识别时一种离线识别的方法,将一些常用的词汇放到一个文件中,作为识别的文本库,然后分段识别语音信号,最后在库中搜索对应的文本信息。如果想看语音识别库中有哪些文本信息,可以通过下面的指令进行查询:

[plain] view plaincopy
  1. $ roscd pocketsphinx/demo
  2. $ more robocup.corpus

2、添加语音库

        我们可以自己向语音库中添加其他的文本识别信息,《ros by example》自带的例程中是带有语音识别的例程的,而且有添加语音库的例子。
        首先看看例子中要添加的文本信息:

[plain] view plaincopy
  1. $ roscd rbx1_speech/config
  2. $ more nav_commands.txt

        
        这就是需要添加的文本,我们也可以修改其中的某些文本,改成自己需要的。
        然后我们要把这个文件在线生成语音信息和库文件,这一步需要登陆网站http://www.speech.cs.cmu.edu/tools/lmtool-new.html,根据网站的提示上传文件,然后在线编译生成库文件。
        把下载的文件都解压放在rbx1_speech包的config文件夹下。我们可以给这些文件改个名字:

[plain] view plaincopy
  1. $ roscd rbx1_speech/config
  2. $ rename -f 's/3026/nav_commands/' *

        在rbx1_speech/launch文件夹下看看voice_nav_commands.launch这个文件:

[plain] view plaincopy
  1. <launch>
  2. <node name="recognizer" pkg="pocketsphinx" type="recognizer.py"
  3. output="screen">
  4. <param name="lm" value="$(find rbx1_speech)/config/nav_commands.lm"/>
  5. <param name="dict" value="$(find rbx1_speech)/config/nav_commands.dic"/>
  6. </node>
  7. </launch>

        可以看到,这个launch文件在运行recognizer.py节点的时候使用了我们生成的语音识别库和文件参数,这样就可以实用我们自己的语音库来进行语音识别了。
        通过之前的命令来测试一下效果如何吧:

[plain] view plaincopy
  1. $ roslaunch rbx1_speech voice_nav_commands.launch
  2. $ rostopic echo /recognizer/output

三、语音控制

        有了语音识别,我们就可以来做很多犀利的应用了,首先先来尝试一下用语音来控制机器人动作。

1、机器人控制节点

        前面说到的recognizer.py会将最后识别的文本信息通过消息发布,那么我们来编写一个机器人控制节点接收这个消息,进行相应的控制即可。
        在pocketsphinx包中本身有一个语音控制发布Twist消息的例程voice_cmd_vel.py,rbx1_speech包对其进行了一些简化修改,在nodes文件夹里可以查看voice_nav.py文件:

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

        可以看到,代码中定义了接收到各种命令时的控制策略。

2、仿真测试

        和前面一样,我们在rviz中进行仿真测试。
        首先是运行一个机器人模型:

[plain] view plaincopy
  1. $ roslaunch rbx1_bringup fake_turtlebot.launch

        然后打开rviz:

[plain] view plaincopy
  1. $ rosrun rviz rviz -d `rospack find rbx1_nav`/sim_fuerte.vcg

        如果不喜欢在终端里看输出,可以打开gui界面:

[plain] view plaincopy
  1. $ rxconsole

        再打开语音识别的节点:

[plain] view plaincopy
  1. $ roslaunch rbx1_speech voice_nav_commands.launch

        最后就是机器人的控制节点了:

[plain] view plaincopy
  1. $ roslaunch rbx1_speech turtlebot_voice_nav.launch

       OK,打开上面这一堆的节点之后,就可以开始了。可以使用的命令如下:
        下图是我的测试结果,不过感觉准确度还是欠佳:

四、播放语音

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

[plain] view plaincopy
  1. $ rosrun sound_play soundplay_node.py
  2. $ rosrun sound_play say.py "Greetings Humans. Take me to your leader."

        有没有听见声音!ROS通过识别我们输入的文本,让机器人读了出来。发出这个声音的人叫做kal_diphone,如果不喜欢,我们也可以换一个人来读:

[plain] view plaincopy
  1. $ sudo apt-get install festvox-don
  2. $ rosrun sound_play say.py "Welcome to the future" voice_don_diphone

       哈哈,这回换了一个人吧,好吧,这不也是我们的重点。
       在rbx1_speech/nodes文件夹中有一个让机器人说话的节点talkback.py:

[python] view plaincopy
  1. #!/usr/bin/env python
  2. """
  3. talkback.py - Version 0.1 2012-01-10
  4. Use the sound_play client to say back what is heard by the pocketsphinx recognizer.
  5. Created for the Pi Robot Project: http://www.pirobot.org
  6. Copyright (c) 2012 Patrick Goebel.  All rights reserved.
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.5
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details at:
  15. http://www.gnu.org/licenses/gpl.htmlPoint
  16. """
  17. import roslib; roslib.load_manifest('rbx1_speech')
  18. import rospy
  19. from std_msgs.msg import String
  20. from sound_play.libsoundplay import SoundClient
  21. import sys
  22. class TalkBack:
  23. def __init__(self, script_path):
  24. rospy.init_node('talkback')
  25. rospy.on_shutdown(self.cleanup)
  26. # Set the default TTS voice to use
  27. self.voice = rospy.get_param("~voice", "voice_don_diphone")
  28. # Set the wave file path if used
  29. self.wavepath = rospy.get_param("~wavepath", script_path + "/../sounds")
  30. # Create the sound client object
  31. self.soundhandle = SoundClient()
  32. # Wait a moment to let the client connect to the
  33. # sound_play server
  34. rospy.sleep(1)
  35. # Make sure any lingering sound_play processes are stopped.
  36. self.soundhandle.stopAll()
  37. # Announce that we are ready for input
  38. self.soundhandle.playWave(self.wavepath + "/R2D2a.wav")
  39. rospy.sleep(1)
  40. self.soundhandle.say("Ready", self.voice)
  41. rospy.loginfo("Say one of the navigation commands...")
  42. # Subscribe to the recognizer output and set the callback function
  43. rospy.Subscriber('/recognizer/output', String, self.talkback)
  44. def talkback(self, msg):
  45. # Print the recognized words on the screen
  46. rospy.loginfo(msg.data)
  47. # Speak the recognized words in the selected voice
  48. self.soundhandle.say(msg.data, self.voice)
  49. # Uncomment to play one of the built-in sounds
  50. #rospy.sleep(2)
  51. #self.soundhandle.play(5)
  52. # Uncomment to play a wave file
  53. #rospy.sleep(2)
  54. #self.soundhandle.playWave(self.wavepath + "/R2D2a.wav")
  55. def cleanup(self):
  56. self.soundhandle.stopAll()
  57. rospy.loginfo("Shutting down talkback node...")
  58. if __name__=="__main__":
  59. try:
  60. TalkBack(sys.path[0])
  61. rospy.spin()
  62. except rospy.ROSInterruptException:
  63. rospy.loginfo("Talkback node terminated.")

         我们来运行看一下效果:

[plain] view plaincopy
  1. $ roslaunch rbx1_speech talkback.launch

         然后再说话,ROS不仅将文本信息识别出来了,而且还读了出来,厉害吧。当然了,现在还没有加入什么人工智能的算法,不能让机器人和我们聪明的说话,不过这算是基础了,以后有时间再研究一下人工智能就更犀利了。

----------------------------------------------------------------

ROS探索总结(十一)——机器视觉

机器视觉在计算机时代已经越来越流行,摄像头价格越来越低廉,部分集成深度传感器的混合型传感器也逐渐在研究领域普及,例如微软推出的Kinect,而且与之配套的软件功能十分强大,为开发带来了极大的便利。ROS集成了Kinect的的驱动包OpenNI,而且使用OpenCV库可以进行多种多样的图像处理。

注:本章内容及代码均参考《ROS by Example》书中的第十章。

一、图像显示

        我们从最基础的开始,想办法显示Kinect的图像数据。

1、安装驱动包

       安装步骤很简单:

[plain] view plaincopy
  1. $sudo apt-get install ros-fuerte-openni-kinect

2、测试

        首先运行kinect节点:

[plain] view plaincopy
  1. $roslaunch rbx1_vision openni_node_fuerte.launch

然后我们调用ROS的image_view包来直接显示摄像头的数据库。image_view包的介绍可以参考:

        http://www.ros.org/wiki/image_view。

[plain] view plaincopy
  1. $rosrun image_view image_view image:=/camera/rgb/image_color

我们可以看到弹出了一个独立的图像显示框:

3、分析数据

        下图是我们上面测试中的节点图。
        我们可以使用如下的命令来查看节点之间发送的图像消息是什么样的:

[plain] view plaincopy
  1. rostopic echo /camera/rgb/image_color

然后就会看到数据刷刷的在显示,有没有感觉看不清楚,我们使用终端的重定向功能将数据全部存储到文件中:

[plain] view plaincopy
  1. rostopic echo /camera/rgb/image_color > test

好了,现在看看文件中是不是已经有数据了,我们来看一下其中的一帧数据:

[plain] view plaincopy
  1. header:
  2. seq: 19285
  3. stamp:
  4. secs: 1370867560
  5. nsecs: 538447820
  6. frame_id: camera_rgb_optical_frame
  7. height: 240
  8. width: 320
  9. encoding: rgb8
  10. is_bigendian: 0
  11. step: 960
  12. data: [223, 225, 225, 220, 225, 225……………..
        从数据中我们可以的出来几个很重要的参数,首先是图像的分辨率:240*320,编码的格式是rgb8,也就是说图像应该有240*320=76800个像素,而每个像素由八位的R、G、B三个数据组成,因此我们可以预计下面的data中应该有76800*3=230400个八位的数据了。
        我们可以验证一下data中到底有多少个数据,这个很简单了,数一下就行了,不过好像有点多,我使用的是linux的“wc”命令。首先我一帧数据复制到单独的文件中,每个数据都是用“,”号隔开的,只要计算“,”的数量就知道数据的数量了。
        结果和我们预想的是一样的。知道这个数据格式以后,我们以后就可以直接把其他摄像头的数据装换成这种格式的数据就可以直接在ROS中使用了。

4、rviz显示图像

        我们再来点高级的。rviz是我们经常使用的工具,把图像显示在rviz中才更有应用价值。rviz已经为我们提供好了显示图像的接口,使用非常简单。
        首先按照上一节的方法运行kinect节点,然后打开rviz:

[plain] view plaincopy
  1. rosrun rviz rviz

然后修改“Fixed Frame”为/camera_rgb_color,修改“Target Frame”为<Fixed Frame>,接着点击add,选择camera类型。添加成功后选择camera菜单下的Iamge Topic选项,选择/camera/rgb/image_color,确定后下面的显示框内就显示图像了。

二、深度显示

        使用kinect的一大特色就是可以获得传感器的深度数据,也就是物体距离传感器的距离,传说kinect的可识别范围在60cm到10m之间。  

1、显示深度图像

        首先也需要运行Kinect的节点:

[plain] view plaincopy
  1. roslaunch openni_launch openni.launch

这一步我使用的是ROS安装openni包中的节点,使用与前面相同的命令运行的节点在后面无法产生深度数据。
然后同样适用iamge_view包就可以简单的显示深度图像了:

[plain] view plaincopy
  1. rosrun image_view disparity_view image:=/camera/depth/disparity
        

2、rviz中显示深度

        首先运行rviz:
[plain] view plaincopy
  1. rosrun rviz rviz

然后修改“Fixed Frame”和“Target Frame”,在add中添加PointCloud2类型,修改“topic”,具体参数如下图所示

----------------------------------------------------------------

ROS探索总结(十二)——坐标系统

在机器人的控制中,坐标系统是非常重要的,在ROS使用tf软件库进行坐标转换。

相关链接:http://www.ros.org/wiki/tf/Tutorials#Learning_tf

一、tf简介

        我们通过一个小小的实例来介绍tf的作用。

1、安装turtle包

[plain] view plaincopy
  1. $ rosdep install turtle_tf rviz
  2. $ rosmake turtle_tf rviz

2、运行demo

运行简单的demo:

[plain] view plaincopy
  1. $ roslaunch turtle_tf turtle_tf_demo.launch

然后就会看到两只小乌龟了。

        该例程中带有turtlesim仿真,可以在终端激活的情况下进行键盘控制。

可以发现,第二只乌龟会跟随你移动的乌龟进行移动。

3、demo分析

        接下来我们就来看一看到底ROS做了什么事情。
        这个例程使用tf建立了三个参考系:a world frame, a turtle1 frame, and a turtle2 frame。然后使用tf broadcaster发布乌龟的参考系,并且使用tf listener计算乌龟参考系之间的差异,使得第二只乌龟跟随第一只乌龟。
        我们可以使用tf工具来具体研究。

[plain] view plaincopy
  1. $ rosrun tf view_frames

然后会看到一些提示,并且生成了一个frames.pdf文件。

        该文件描述了参考系之间的联系。三个节点分别是三个参考系,而/world是其他两个乌龟参考系的父参考系。还包含一些调试需要的发送频率、最近时间等信息。
        tf还提供了一个tf_echo工具来查看两个广播参考系之间的关系。我们可以看一下第二只得乌龟坐标是怎么根据第一只乌龟得出来的。

[plain] view plaincopy
  1. $ rosrun tf tf_echo turtle1 turtle2
        控制一只乌龟,在终端中会看到第二只乌龟的坐标转换关系。
        我们也可以通过rviz的图形界面更加形象的看到这三者之间的关系。

[plain] view plaincopy
  1. $ rosrun rviz rviz -d `rospack find turtle_tf`/rviz/turtle_rviz.vcg
        移动乌龟,可以看到在rviz中的坐标会跟随变化。其中左下角的是/world,其他两个是乌龟的参考系。
       下面我们就来详细分析这个实例。

二、Writing a tf broadcaster

1、创建包

[plain] view plaincopy
  1. $ roscd tutorials
  2. $ roscreate-pkg learning_tf tf roscpp rospy turtlesim
  3. $ rosmake learning_tf

2、broadcast transforms

我们首先看一下如何把参考系发布到tf。
        代码文件:/nodes/turtle_tf_broadcaster.py

[python] view plaincopy
  1. #!/usr/bin/env python
  2. import roslib
  3. roslib.load_manifest('learning_tf')
  4. import rospy
  5. import tf
  6. import turtlesim.msg
  7. def handle_turtle_pose(msg, turtlename):
  8. br = tf.TransformBroadcaster()
  9. br.sendTransform((msg.x, msg.y, 0),
  10. tf.transformations.quaternion_from_euler(0, 0, msg.theta),
  11. rospy.Time.now(),
  12. turtlename,
  13. "world")  #发布乌龟的平移和翻转
  14. if __name__ == '__main__':
  15. rospy.init_node('turtle_tf_broadcaster')
  16. turtlename = rospy.get_param('~turtle')   #获取海龟的名字(turtle1,turtle2)
  17. rospy.Subscriber('/%s/pose' % turtlename,
  18. turtlesim.msg.Pose,
  19. handle_turtle_pose,
  20. turtlename)   #订阅 topic "turtleX/pose"
  21. rospy.spin()

        创建launch文件start_demo.launch:

[plain] view plaincopy
  1. <launch>
  2. <!-- Turtlesim Node-->
  3. <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
  4. <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
  5. <node name="turtle1_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
  6. <param name="turtle" type="string" value="turtle1" />
  7. </node>
  8. <node name="turtle2_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
  9. <param name="turtle" type="string" value="turtle2" />
  10. </node>
  11. </launch>

运行:

[plain] view plaincopy
  1. $ roslaunch learning_tf start_demo.launch

可以看到界面中只有移植乌龟了,打开tf_echo的信息窗口:

[plain] view plaincopy
  1. $ rosrun tf tf_echo /world /turtle1
        world参考系的原点在最下角,对于turtle1的转换关系,其实就是turtle1在world参考系中所在的坐标位置以及旋转角度。

三、Writing a tf listener

这一步,我们将看到如何使用tf进行参考系转换。首先写一个tf listener(nodes/turtle_tf_listener.py):

[python] view plaincopy
  1. #!/usr/bin/env python
  2. import roslib
  3. roslib.load_manifest('learning_tf')
  4. import rospy
  5. import math
  6. import tf
  7. import turtlesim.msg
  8. import turtlesim.srv
  9. if __name__ == '__main__':
  10. rospy.init_node('tf_turtle')
  11. listener = tf.TransformListener() #TransformListener创建后就开始接受tf广播信息,最多可以缓存10s
  12. rospy.wait_for_service('spawn')
  13. spawner = rospy.ServiceProxy('spawn', turtlesim.srv.Spawn)
  14. spawner(4, 2, 0, 'turtle2')
  15. turtle_vel = rospy.Publisher('turtle2/command_velocity', turtlesim.msg.Velocity)
  16. rate = rospy.Rate(10.0)
  17. while not rospy.is_shutdown():
  18. try:
  19. (trans,rot) = listener.lookupTransform('/turtle2', '/turtle1', rospy.Time(0))
  20. except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
  21. continue
  22. angular = 4 * math.atan2(trans[1], trans[0])
  23. linear = 0.5 * math.sqrt(trans[0] ** 2 + trans[1] ** 2)
  24. turtle_vel.publish(turtlesim.msg.Velocity(linear, angular))
  25. rate.sleep()
        在launch文件中添加下面的节点:

[plain] view plaincopy
  1. <launch>
  2. ...
  3. <node pkg="learning_tf" type="turtle_tf_listener.py"
  4. name="listener" />
  5. </launch>
        然后在运行,就可以看到两只turtle了,也就是我们在最开始见到的那种跟随效果。

四、Adding a frame

        在很多应用中,添加一个参考系是很有必要的,比如在一个world参考系下,有很一个激光扫描节点,tf可以帮助我们将激光扫描的信息坐标装换成全局坐标。

1、tf消息结构

        tf中的信息是一个树状的结构,world参考系是最顶端的父参考系,其他的参考系都需要向下延伸。如果我们在上文的基础上添加一个参考系,就需要让这个新的参考系成为已有三个参考系中的一个的子参考系。

2、建立固定参考系(fixed frame)

        我们以turtle1作为父参考系,建立一个新的参考系“carrot1”。代码如下(nodes/fixed_tf_broadcaster.py):

[plain] view plaincopy
  1. #!/usr/bin/env python
  2. import roslib
  3. roslib.load_manifest('learning_tf')
  4. import rospy
  5. import tf
  6. if __name__ == '__main__':
  7. rospy.init_node('my_tf_broadcaster')
  8. br = tf.TransformBroadcaster()
  9. rate = rospy.Rate(10.0)
  10. while not rospy.is_shutdown():
  11. br.sendTransform((0.0, 2.0, 0.0),
  12. (0.0, 0.0, 0.0, 1.0),
  13. rospy.Time.now(),
  14. "carrot1",
  15. "turtle1") #建立一个新的参考系,父参考系为turtle1,并且距离父参考系2米
  16. rate.sleep()
       在launch文件中添加节点:

[plain] view plaincopy
  1. <launch>
  2. ...
  3. <node pkg="learning_tf" type="fixed_tf_broadcaster.py"
  4. name="broadcaster_fixed" />
  5. </launch>
        运行,还是看到两只乌龟和之前的效果一样。新添加的参考系并没有对其他参考系产生什么影响。打开nodes/turtle_tf_listener.py文件,将turtle1改成carrot1:

[python] view plaincopy
  1. (trans,rot) = self.tf.lookupTransform("/turtle2", "/carrot1", rospy.Time(0))
        重新运行,现在乌龟之间的跟随关系就改变了:

3、建立移动参考系(moving frame)

        我们建立的新参考系是一个固定的参考系,在仿真过程中不会改变,如果我们要把carrot1参考系和turtle1参考系之间的关系设置可变的,可以修改代码如下:

[python] view plaincopy
  1. #!/usr/bin/env python
  2. import roslib
  3. roslib.load_manifest('learning_tf')
  4. import rospy
  5. import tf
  6. import math
  7. if __name__ == '__main__':
  8. rospy.init_node('my_tf_broadcaster')
  9. br = tf.TransformBroadcaster()
  10. rate = rospy.Rate(10.0)
  11. while not rospy.is_shutdown():
  12. t = rospy.Time.now().to_sec() * math.pi
  13. br.sendTransform((2.0 * math.sin(t), 2.0 * math.cos(t), 0.0),
  14. (0.0, 0.0, 0.0, 1.0),
  15. rospy.Time.now(),
  16. "carrot1",
  17. "turtle1")
  18. rate.sleep()
        这次carrot1的位置现对于turtle1来说是一个三角函数关系了。
代码下载:http://download.csdn.net/detail/hcx25909/5708199

----------------------------------------------------------------

欢迎大家转载我的文章。

转载请注明:转自古-月

http://blog.csdn.net/hcx25909

欢迎继续关注我的博客

ROS探索总结(十)(十一)(十二)——语音控制 机器视觉 坐标系统相关推荐

  1. ROS探索总结(十二)——坐标系统

    ROS探索总结(十二)--坐标系统 ubuntu 14.04  indigo版本 转摘自:http://www.guyuehome.com/265 一.tf简介 1.安装turtle包 1 rosde ...

  2. ROS探索总结(十六)(十七)(十八)(十九)——HRMRP机器人的设计 构建完整的机器人应用系统 重读tf 如何配置机器人的导航功能

    ROS探索总结(十六)--HRMRP机器人的设计 1. HRMRP简介         HRMRP(Hybrid Real-time Mobile Robot Platform,混合实时移动机器人平台 ...

  3. ROS探索总结(十三)(十四)(十五)——导航与定位框架 move_base(路径规划) amcl(导航与定位)

    ROS探索总结(十三)--导航与定位框架 导航与定位是机器人研究中的重要部分.         一般机器人在陌生的环境下需要使用激光传感器(或者深度传感器转换成激光数据),先进行地图建模,然后在根据建 ...

  4. ROS探索总结(十八)——重读tf

    在之前的博客中,有讲解tf的相关内容,本篇博客重新整理了tf的介绍和学习内容,对tf的认识会更加系统. 1 tf简介 1.1 什么是tf tf是一个让用户随时间跟踪多个参考系的功能包,它使用一种树型数 ...

  5. 古月居 ROS入门21讲 第十二讲 话题消息的定义与使用

    古月居 ROS入门21讲 第十二讲 话题消息的定义与使用 Person.msg string name uint8 sex uint8 ageuint8 unknown=0 uint8 male=1 ...

  6. 即时通讯音视频开发(十):实时语音通讯的回音消除技术详解

    前言 即时通讯应用中的实时音视频技术,几乎是IM开发中的最后一道高墙.原因在于:实时音视频技术 = 音视频处理技术 + 网络传输技术 的横向技术应用集合体,而公共互联网不是为了实时通信设计的.有关实时 ...

  7. ROS中阶笔记(十):ROS机器人综合应用

    ROS中阶笔记(十):ROS机器人综合应用 文章目录 1 ROS机器人综合应用 1.1 PR2 1.2 PR2实践 1.3 TurtleBot 1.3.1 TurtleBot2实践 1.3.2 Tur ...

  8. 【ROS学习笔记】(十)ROS中的坐标系管理系统

    一.机器人中的坐标变换 TF功能包用来管理所有的坐标系.它可以记录十秒钟之内所有坐标系之间的关系,可以展示夹取的物体相对于机器人中心坐标系的位置在哪里. 二.举例:小海龟跟随实验 1. 小海龟跟随 两 ...

  9. Lyra——开启下一个十亿用户的语音通话

    Lyra--开启下一个十亿用户的语音通话 前言 过去的一年已经表明,在线交流对我们的生活至关重要.无论你身在何处.可用的网络条件如何,清楚地了解彼此之间的联系变得前所未有地重要.因此,我们在2月推出了 ...

最新文章

  1. python爬虫教程 百度云-如何使用python编程【python爬虫教程 百度云】
  2. thinkphp5 消息队列thinkphp-queue扩展
  3. 来潮汕,这些食物不吃后悔一辈子...
  4. 使用线程,防止当前程序被阻塞
  5. 从系统集成到虚拟化,IT之路艰难前行...
  6. arcmap提取dem高程_ArcGIS提取高程点
  7. 汇编语言在线视频教程
  8. Lu窗口库LuWin
  9. Cocos2d-x3.2刀塔创奇三消游戏源码,跑酷游戏源码,塔防游戏源码
  10. 怎么设置织梦栏目html结尾,dedecms网站栏目地址url优化成.html结尾的而不是文件夹形式结尾的。请大家来帮忙。...
  11. 【华人学者风采】黄霞 清华大学
  12. @Column中的length属性和@Size和@length区别
  13. 1134: 字符串转换 C语言
  14. Linux浏览器无法访问网络解决方案
  15. 数码相框(三、LCD显示文字)
  16. win11 安装Subversion与配置 svn服务端
  17. [转载] 服务器基础知识
  18. 零基础小白学Python之多继承习题演练
  19. 【论文阅读】基于区块链的无人集群作战信息共享架构_臧义华
  20. TNF抑制剂相关的肿瘤风险:阿达木单抗、依那西普和英夫利昔单抗随机对照试验的荟萃分析...

热门文章

  1. Vue 里的$如何理解
  2. 图文详解】Chrome中安装JsonView插件
  3. 从重采样到数据合成:如何处理机器学习中的不平衡分类问题? 转载 2017年08月01日 17:09:03 标签: 机器学习 / 数据 719 转自:http://www.sohu.com/a/12
  4. 算法与数据结构(归并排序)
  5. 全局事件-广播(Broadcast)
  6. 加入依赖后刷新_和平精英海岛图神秘野区加入!地图没显示 刷新点在这儿
  7. 学习笔记Flink(八)—— 基于Flink 在线交易反欺诈检测
  8. Java实现反向输出链表
  9. ROS知识 【6】Ubuntu20.04下安装ROS-noetic
  10. matlab plot title 包含变量的图片标题