🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 8.4 tf in python ### 8.4.1 简介 我们知道tf中不仅有C++的接口,也有Python的接口。相比C++,tf在Python中的具体实现相对简单好用。 ### 8.4.2 数据类型 TF的相关数据类型,向量、点、四元数、矩阵都可以表示成类似数组形式,就是它们都可以用Tuple,List,Numpy Array来表示。 例如: ``` t = (1.0,1.5,0) #平移 q = [1,0,0,0] #四元数 m = numpy.identity(3) #旋转矩阵 ``` 第一个平移数据使用Tuple表示的,同时也可以用List表示成t=\[1.0,1.5,0\],也能用numpy.array\(1.0,1.5,0\)来表示都是可以的。这些数据类型没有特殊对应,全部是通用的,所以这里也就没有了各种数据类型的转换的麻烦。 ### 8.4.3 TF库 ##### tf.transformations 基本数学运算函数 | 函数 | 注释 | | :---: | :---: | | euler\_matrix\(ai,aj,ak,axes='sxyz'\) | 欧拉角到矩阵 | | eulaer\_form\_matrix\(matrix,axes='sxyz'\) | 矩阵到欧拉角 | | eular\_from\_quaternion\(quaternion,axes='sxyz'\) | 四元数到欧拉角 | | quaternion\_form\_euler\(ai,aj,ak,axes='sxyz'\) | 欧拉角到四元数 | | quaternion\_matrix\(quaternion\) | 四元数到矩阵 | | quaternion\_form\_matrix\(matrix\) | 矩阵到四元数 | | ...... | ...... | 使用该函数库时候,首先`import tf`,tf.transformations给我们提供了一些基本的数学运算函数如上,使用起来非常方便。在tf\_demo中教学包当中,我们列举了一些tf.transformations常见的API和示例代码,具详见下表。 ###### 第1部分,定义空间点和空间向量 | 编号 | 函数名称 | 函数功能 | | :---: | :---: | :---: | | 1.1 | tf.transformations.random\_quaternion\(rand=None\) | 返回均匀随机单位四元数 | | 1.2 | tf.transformations.random\_rotation\_matrix\(rand=None\) | 返回均匀随机单位旋转矩阵 | | 1.3 | tf.transformations.random\_vector\(size\) | 返回均匀随机单位向量 | | 1.4 | tf.transformations.translation\_matrix\(v\) | 通过向量来求旋转矩阵 | | 1.5 | tf.transformations.translation\_from\_matrix\(m\) | 通过旋转矩阵来求向量 | ###### 第2部分,定义四元数 | 编号 | 函数名称 | 函数功能 | | :---: | :---: | :---: | | 2.1 | tf.transformations.quaternion\_about\_axis\(angle axis\) | 通过旋转轴和旋转角返回四元数 | | 2.2 | tf.transformations.quaternion\_conjugate\(quaternion\) | 返回四元数的共轭 | | 2.3 | tf.transformations.quaternion\_from\_euler\(ai,aj,ak, axes'ryxz'\) | 从欧拉角和旋转轴,求四元数 | | 2.4 | tf.transformations.quaternion\_from\_matrix\(matrix\) | 从旋转矩阵中,返回四元数 | | 2.5 | tf.transformations.quaternion\_multiply\(quaternion1,quaternion2\) | 两个四元数相乘 | ###### 第3部分,定义四元数 | 编号 | 函数名称 | 函数功能 | | :---: | :---: | :---: | | 3.1 | tf.transformations.euler\_matrix\(ai,aj,ak,axes='xyz'\) | 由欧拉角和旋转轴返回旋转矩阵 | | 3.2 | tf.transformations.euler\_from\_matrix\(matrix\) | 由旋转矩阵和特定的旋转轴返回欧拉角 | | 3.3 | tf.transformations.euler\_from\_quaternion\(quaternion\) | 由四元数和特定的轴得到欧拉角 | 示例代码: #!/usr/bin/env python # -*- coding:utf-8 -*- import rospy import math import tf if __name__ == '__main__': rospy.init_node('py_coordinate_transformation') #第1部分,定义空间点和空间向量 print '第1部分,定义空间点和空间向量' #1.1 返回均匀随机单位四元数 q=tf.transformations.random_quaternion(rand=None) print '定义均匀随机四元数:' print q #1.2 返回均匀随机单位旋转矩阵 m=tf.transformations.random_rotation_matrix(rand=None) print '定义均匀随机单位旋转矩阵:' print m #1.3 返回均匀随机单位向量 v=tf.transformations.random_vector(3) print '定义均匀随机单位向量:' print v #1.4 通过向量来求旋转矩阵 v_m=tf.transformations.translation_matrix(v) print '通过向量来求旋转矩阵:' print v_m #1.5 通过旋转矩阵来求向量 m_v=tf.transformations.translation_from_matrix(m) print '通过旋转矩阵来求向量:' print m_v #第2部分,定义四元数 print '第2部分,定义四元数' #2.1 通过旋转轴和旋转角返回四元数 axis_q=tf.transformations.quaternion_about_axis(0.123, (1, 0, 0)) print '通过旋转轴和旋转角返回四元数:' print axis_q #2.2 返回四元数的共轭 n_q=tf.transformations.quaternion_conjugate(q) print '返回四元数q的共轭:' print n_q #2.3 从欧拉角和旋转轴,求四元数 o_q=tf.transformations.quaternion_from_euler(1, 2, 3, 'ryxz') print '从欧拉角和旋转轴,求四元数:' print o_q #2.4 从旋转矩阵中,返回四元数 m_q=tf.transformations.quaternion_from_matrix(m) print '从旋转矩阵中,返回四元数:' print m_q #2.5 两个四元数相乘 qxq=tf.transformations.quaternion_multiply(q,n_q) print '两个四元数相乘' print qxq ` ### 8.4.4 TF类 ##### tf.TransformListener类 | 方法 | 作用 | | :---: | :---: | | canTransform\(self,target\_frame,source\_frame,time\) | frame是否相通 | | waitForTransform\(self,target\_frame,source\_frame,time,timeout\) | 阻塞直到frame相通 | | lookup Transform\(self,target\_frame,source\_frame,time\) | 查看相对的tf,返回(trans,quat) | tf.TransformListener类中主要包含以上三种方法,它的构造函数不需要填值。注意这里的time参数,依然是使用`rospy.Time(0)`而不是`rospy.Time.now()`.具体原因上节已经介绍,这里不再赘述。除了上述三种重要的方法,这个类中还有一些辅助用的方法如下: | 方法 | 作用 | | :---: | :---: | | chain\(target\_frame,target\_time,source\_frame,source\_time,fixed\_frame\) | frame的连接关系 | | frameExists\(self,frame\_id\) | frame是否存在 | | getFrameStrings\(self\) | 返回所有tf的名称 | | fromTranslationRotation\(translation,rotation\) | 根据平移和旋转返回4X4矩阵 | | transformPoint\(target\_frame,point\_msg\) | 将PointStamped消息转换到新frame下 | | transformPose\(target\_frame,pose\_msg\) | 将PoseStamped消息转换到新frame下 | | transformQuaternion\(target\_frame,quat\_msg\) | 将QuaternionStamped...返回相同类型 | | ... | ... | 在`tf_demo`教学包当中的`scripts/py_tf_listerner.py`给出了示例程序,详见如下。 py\_tf\_listerner.py ``` #!/usr/bin/env python # -*- coding:utf-8 -*- import rospy import math import tf if __name__ == '__main__': rospy.init_node('py_tf_turtle') listener = tf.TransformListener() #TransformListener创建后就开始接受tf广播信息,最多可以缓存10s 目前存在的问题,是四个数值的顺序我还有点问题 rate = rospy.Rate(1.0) #1. 阻塞直到frame相通 print '1. 阻塞直到frame相通' listener.waitForTransform("/base_link", "/link1", rospy.Time(), rospy.Duration(4.0)) while not rospy.is_shutdown(): try: #2. 监听对应的tf,返回平移和旋转 print '2. 监听对应的tf,返回平移和旋转' (trans,rot) = listener.lookupTransform('/base_link', '/link1', rospy.Time(0)) #rospy.Time(0)不表示0时刻的tf,而是指最近一帧tf except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): continue rospy.loginfo('距离原点的位置: x=%f ,y= %f,z=%f \n 旋转四元数: w=%f ,x= %f,y=%f z=%f ',trans[0],trans[1],trans[2],rot[0],rot[1],rot[2],rot[3]) #3. 判断两个frame是否相通 print '3. 判断两个frame是否相通' if listener.canTransform('/link1','/base_link',rospy.Time(0)) : print 'true' else : print 'false' rate.sleep() ``` ##### ##### tf.TransformBroadcaster类 类似的,我们介绍的是发布方,tf.TransformBroadcaster类。该类的构造函数也是不需要填值,成员函数有两个如下: * sendTransform\(translation,rotation,time,child,parent\)\#向/tf发布消息 * sendTransformMessage\(transform\)\#向/tf发布消息 第一个sendTransform\(\)把transform的平移和旋转填好,打上时间戳,然后表示出从父到子的frame流,然后发向`/tf`的topic。第二种是发送transform已经封装好的Message给`/tf`,这两种不同的发送方式,功能是一致的。在`tf_demo`教学包当中的`scripts/py_tf_broadcaster.py`和`scripts/py_tf_broadcaster02.py`给出了示例程序,详见如下。 py\_tf\_broadcaster.py ``` #!/usr/bin/env python # -*- coding:utf-8 -*- import rospy import math import tf if __name__ == '__main__': rospy.init_node('py_tf_broadcaster') print '讲解tf.transformBroadcaster类' print '第1种发布方式:sendTransform(translation,rotation,time,child,parent)' #第一部分,发布sendTransform(translation,rotation,time,child,parent) br = tf.TransformBroadcaster() #输入相对原点的值和欧拉角 x=1.0 y=2.0 z=3.0 roll=0 pitch=0 yaw=1.57 rate = rospy.Rate(1) while not rospy.is_shutdown(): yaw=yaw+0.1 br.sendTransform((x,y,z), tf.transformations.quaternion_from_euler(roll,pitch,yaw), rospy.Time.now(), "base_link", "link1") #发布base_link到link1的平移和翻转 rate.sleep() ``` py\_tf\_broadcaster02.py ``` #!/usr/bin/env python # -*- coding:utf-8 -*- import rospy import geometry_msgs.msg import tf2_ros.transform_broadcaster import math import tf if __name__ == '__main__': rospy.init_node('py_tf_broadcaster') print '讲解tf.transformBroadcaster类' print '第2种发布方式:sendTransformMessage(transform)' #第二部分,发布sendTransformMessage(transform) m=tf.TransformBroadcaster() t = geometry_msgs.msg.TransformStamped() t.header.frame_id = 'base_link' t.header.stamp = rospy.Time(0) t.child_frame_id = 'link1' t.transform.translation.x = 1 t.transform.translation.y = 2 t.transform.translation.z = 3 t.transform.rotation.w=1 t.transform.rotation.x=0 t.transform.rotation.y=0 t.transform.rotation.z=0 #输入相对原点的值和欧拉角 rate = rospy.Rate(1) while not rospy.is_shutdown(): m.sendTransformMessage(t) rate.sleep() ``` ### 8.4.5 TF相关工具命令 1. 根据当前的tf树创建一个pdf图: ``` $ rosrun tf view_frames ``` 这个工具首先订阅`/tf`,订阅5秒钟,根据这段时间接受到的tf信息,绘制成一张tf tree,然后创建成一个pdf图。 2. 查看当前的tf树: ``` $ rosrun rqt_tf_tree rqt_tf_tree ``` 该命令同样是查询tf tree的,但是与第一个命令的区别是该命令是动态的查询当前的tf tree,当前的任何变化都能当即看到,例如何时断开何时连接,捕捉到这些然后通过rqt插件显示出来。 3. 查看两个frame之间的变换关系: ``` $ rosrun tf tf_echo[reference_frame][target_frame] ```