赞
踩
官网建议新工作直接使用tf2,因为它有一个更清洁的界面,和更好的使用体验。(自ROS Hydro以来,tf第一代已被“弃用”,转而支持tf2)
TF(TransForm),就是坐标转换,包括了位置和姿态两个方面的变换。注意区分坐标转换和坐标系转换。
坐标转换是一个坐标在不同坐标系下的表示,而坐标系转换不同坐标系的相对位姿关系。
ROS中机器人模型包含大量的部件,每一个部件统称之为link(比如手部、头部、某个关节、某个连杆),每一个link上面对应着一个frame(坐标系), 用frame表示该部件的坐标系,frame和link是绑定在一起的。
TF是一个通俗的名称,实际上它有很多含义:
/tf
,话题中的消息保存的就是tf tree的数据结构格式。维护了整个机器人的甚至是地图的坐标转换关系。维持并更新机器人整个坐标系的话题是/tf
,/tf
话题表示的内容是整个机器人的tf树,而非仅仅是某两个坐标系的转换关系,这样的话,/tf
话题是需要很多的节点来维护的,每一个节点维护两个frame之间的关系。tf是一个树状结构,维护坐标系之间的关系,靠话题通信机制来持续地发布不同link之间的坐标关系。作为树状结构,要保证父子坐标系都有某个节点在持续地发布他们之间的位姿关系,才能使树状结构保持完整。只有父子坐标系的位姿关系能被正确的发布,才能保证任意两个frame之间的连通。
如果出现某一环节的断裂,就会引发error系统报错.所以完整的tf tree不能有任何断层的地方,这样我们才能查清楚任意两个frame之间的关系。
每两个相邻frame之间靠节点发布它们之间的位姿关系,这种节点称为broadcaster。broadcaster就是一个发布器publisher,如果两个frame之间发生了相对运动,broadcaster就会发布相关消息。
TF库的目的是实现系统中任一个点在所有坐标系之间的坐标变换。也就是说,只要给定一个坐标系下的一个点的坐标,就能获得这个点在其他任意坐标系的坐标。
为了达到上述目的,就需要提供当前ROS系统的中任意两个坐标系的位姿变换关系。
那么TF是用什么方式来描述与记录任意两个坐标系的位姿变换关系的呢?
这里存在一个问题。假设有n个坐标系,那么他们之间的组合关系有c(n,2)个。如果这样穷举个数会非常多,所以不会采用这个方法。
为了更合理、更高效地表示任意坐标系的变换关系。TF使用多层多叉树的形式来描述这个这个ROS系统的坐标系,树中的每一个节点都是一个坐标系。TF树的特点是每个节点只要一个父节点,即采用每个坐标系都有一个父坐标系,可以有多个子坐标系的原则。
每个坐标系都有一个父坐标系,可以有多个子坐标系。TF树就是以父子坐标系的形式来组织的,最上面是父坐标系,往下是子坐标系。
在TF树中具有父子关系的坐标系是相邻的,用带箭头的线连接起来。在TF树中用箭头表示这种父子关系。
上图所表示的TF树中base_link
坐标系是base_footprint
的子坐标系,base_cover_link
坐标系也是base_footprint
的子坐标系。
描述规范:
TF树的建立和维护是基于Topic通信机制的。
根据TF树的原理,它是靠建立与维护每个父子坐标系的变换关系来维护整个系统的所有坐标系的变换关系的。每个parent 坐标系到child坐标系变换关系是靠被称为broadcastor的发布器节点来持续发布的。
虽然是靠Topic通信机制发布的parent 坐标系到child坐标系的变换,但并不是让每一对父子坐标系都发布一个话题,实际上发布的唯一个话题是/topic
,该话题集合了所有发布的父子坐标系的变换关系。
也就是说TF机制并不是让每一对父子坐标系都发布一个话题,而是将所有的父子坐标系都集合到到一个话题上,该话题的消息中传递的数据是所有父子坐标系的变换关系,是父子坐标系变换关系的一个大数组。
使用tf的tflisener就可以监听从任意两个坐标系的变换。前提是TF的树上能把这两个联通。
在开始建立TF树的时候需要指定第一个父坐标系(parent frame)作为最初的坐标系。比如机器人系统中的map坐标系。
在第一次发布一个从已有的parent frame到新的child frame的坐标系变换时,这棵树就会添加一个树枝,之后就是维护。
TF树的建立和维护靠的是tf提供的tfbroadcastor类的sendtransform接口。
transformBroadcaster()
类就是一个publisher,而sendTransform的作用是来封装publish的函数。
在运行过程中要不断更新已有的parent frame到已有的child frame的坐标系变换,从而保证最新的位姿转换关系。
作为树状结构,要保证父子frame都有某个节点在持续地发布这两个frame之间的位姿关系,才能使树状结构保持完整。只有每一个父子的frame的位姿关系能被正确的发布,才能保证任意两个frame之间的连通。
一旦正常的建立一个TF树,保证每个父子坐标系都能得到正常的维护,那么就可以利用TF提供的订阅器,订阅任意两个坐标系的转换关系。
如何根据TF树得到任意坐标系的转换关系?
如果想要获得任意两个坐标系的转换关系,其实订阅器是收取的/tf
话题上的消息,该消息集合了所有发布的父子坐标系的变换关系。订阅器接收的其实是当前时刻的整个TF树,然后搜索这棵树,根据不同的父子坐标系关系找到一条变换的路径。这条变换路径就能通过父子关系通路连接起所求的这两个坐标系,从而通过不断将该通路上的变换矩阵相乘得到最终的所求的这两个坐标系的变换关系。
TF对发布器与订阅器进行了封装,使开发者通过TF的接口更加简单地建立对TF树中某些坐标系转换关系的维护与订阅。用tf的tflisener监听某一个指定的从一个a frame到b frame的变换即可。
基本原理是,tfbroadcastor的类里有个publisher,tflisener的类里有个subscriber,一个发布叫/tf
的topic,一个订阅这个topic,传送的消息message里包含了每一对parent frameid和child frameid的信息。这个机制意味着,所有的tb会发布某一特定的parent到child的变换,而所有tl会收到所有的这些变换,然后tl利用一个tfbuffercore的数据结构维护一个完整的树结构及其状态。基于此,tl在使用这棵树时,会用lookuptransform或waitfortransform来获得任意坐标系之间的变换。
这样即使只要是一个tflisener(即只监听两个坐标系的变换关系),就要跟所有tfbroadcastor建立连接,就要收取/tf
上的整个TF树,还要负责搜索这棵树,找到一条变换的路径,然后通过变换矩阵相乘得到两个坐标系最终的变换关系。
优点:
/
缺点:
每个父子坐标系之间都会有broadcaster节点来发布消息维系坐标之间的转换关系。TransformStampde.msg
就是/tf
话题上消息。该消息格式用来表示两个frame之间一小段tf变换的相对坐标关系的。
说明:
ROS中实际上是靠TF tree来表示整个系统的坐标系关系的,而非简单地靠多个两两父子坐标系的转换关系来描述的。这里的TransformStampde.msg
消息的TF tree消息类型的片段即其中的一对父子坐标系位姿的描述方式,TF tree消息类型基于TransformStampde.msg
消息,因此先介绍TransformStampde.msg
。TransformStampde.msg
本质上描述的是TF tree中一小段tf变换。
具体消息类型如下:
geometry_msgs/TransformStamped
(可见该消息类型是属于geometry_msgs
程序包的,而非tf包)
- std_mags/Header header
- uint32 seq
- time stamp
- string frame_id
- string child_frame_id
- geometry_msgs/Transform transform
- geometry_msgs/Vector3 translation
- float64 x
- float64 y
- float64 z
- geometry_msgs/Quaternion rotation
- float64 x
- float64 y
- flaot64 z
- float64 w
该消息表示的的是当前坐标系frame_id和它的子坐标系child_frame_id之间的转换关系。具体的转换位姿是由geometry_msgs/Transform
消息类型来定义的,该消息类型用三维向量表示平移,用四元组表示旋转。
/tf
话题表示的内容是整个机器人的tf树,而非仅仅是某两个坐标系的转换关系,这样的话,/tf
话题是需要很多的节点来维护的,每一个节点维护两个父子frame之间的关系。即一个/tf
话题上面,可能会有很多个node向上面发送消息。
这样就相当于TF tree是由很多的frame之间TF拼接而成。刚才说的TransformStampde.msg
消息类型表示的是两个frame之间TF关系,接下来要介绍真正在/tf
话题上进行传输的TF tree的消息类型。
在tf2中的TF树对应的消息类型是tf2_msgs/TFMessage.msg
。可见该消息位于tf2_msgs
程序包内。
tf2_msgs/TFMessage消息的具体格式:
- geometry_msgs/TransformStamped[] transforms
- std_msgs/Header header
- uint32 seq
- time stamp
- string frame_id
- string child_frame_id
- geometry_msgs/Transform transform
- geometry_msgs/Vector3 translation
- float64 x
- float64 y
- float64 z
- geometry_msgs/Quaternion rotation
- float64 x
- float64 y
- flaot64 z
- float64 w
可以看出TF树的消息类型实际上就是一个TransformStamped
类型定义的可变长度数组。也就是说本质就是由很多个两个frame之间的TF消息TransformStamped
形成描述整个机器人的TF树的消息类型tf2_msgs/TFMessage.msg
。
无论是roscpp中还是rospy中都有TF库,TF提供了很多有用的接口。这里只大体描述,具体使用的时候再参考具体的资料。
提供的结构种类:
/tf
话题上的一段transform上;也可以订阅/tf
话题,并且得到到从源坐标系到目标坐标系这两个坐标系之间的转换关系。transformBroadcaster()
类就是一个publisher,而sendTransform的作用是来封装publish的函数。在实际的使用中,我们需要在某个Node中构建tf::TransformBroadcaster类,然后调用sendTransform(),将transform发布到/tf
的一段transform上。
TransformListener
类就是从/tf
上接收的类。
用命令行显示当前所有frame的方法:
rosrun tf tf_monitor #显示当前坐标变换树的信息,主要是名称和实时的时间延时
rostopic echo /tf #以TransformStamped消息类型的数组显示所有父子frame的位姿转换关系
以上主要是数据显示
根据当前的tf树创建一个pdf图:
$ rosrun tf view_frames
这个工具首先订阅/tf
,订阅5秒钟,根据这段时间接受到的tf信息,绘制成一张tf tree,然后创建成一个pdf图。
将会以图形的形式显示出TF树中所有的frame和两个frame 的父子关系及其Broadcaster、Average rate等
查看当前的tf树:
$ rosrun rqt_tf_tree rqt_tf_tree
该命令同样是查询tf tree的,但是与第一个命令的区别是该命令是动态的查询当前的tf tree,当前的任何变化都能当即看到,例如何时断开何时连接,捕捉到这些然后通过rqt插件显示出来。
查看两个frame之间的变换关系:
$ rosrun tf tf_echo[source_frame][target_frame]
将会持续的显示源坐标系和目标坐标系的位姿变换关系。
该指令可以查询任意两个frame的转换关系。
https://sychaichangkun.gitbooks.io/ros-tutorial-icourse163/content/chapter8/8.3.html(基础讲义)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。