ros tf2使用示例

关于ROS中使用tf/tf2获取到transformation之后的怎样处理,写一个例子作为参考:

已知两组固定坐标系,B->A,D->C,其关系定义如代码 static_publisher.py所示

#!/usr/bin/env python
# static_publisher.py
import rospy import sys import tf2_ros as tf2 import tf_conversions as tfc import geometry_msgs.msg from tf2_msgs.msg import TFMessage import math import numpy def __genTf(frameT, frameS, posTs, q): ts = geometry_msgs.msg.TransformStamped() ts.header.stamp = rospy.Time.now() ts.header.frame_id = frameT ts.child_frame_id = frameS ts.transform.translation.x = posTs[0] ts.transform.translation.y = posTs[1] ts.transform.translation.z = posTs[2] ts.transform.rotation.x = q[0] ts.transform.rotation.y = q[1] ts.transform.rotation.z = q[2] ts.transform.rotation.w = q[3] return ts def __main(): rospy.init_node("AB&CD") rate = rospy.Rate(100) tbf = tf2.Buffer() tl = tf2.TransformListener(tbf) t = TFMessage() sbc = tf2.StaticTransformBroadcaster() qA2B = tfc.transformations.quaternion_from_euler(math.pi, 0, math.pi / 2) ts1 = __genTf("A", "B", (-1, 0, 0.2), qA2B) t.transforms.append(ts1) qC2D = tfc.transformations.quaternion_from_euler(math.pi, 0, 0) ts2 = __genTf("C", "D", (0, 0, 1), qC2D) t.transforms.append(ts2) sbc.pub_tf.publish(t) rospy.spin() if __name__ == "__main__": __main()

先假定D传感器的观测数据会发布在B坐标系下,而A是实际地图坐标系,求C物体坐标系(source frame, child frame)到A坐标系(destination frame, parent frame)的转换,代码如下:

#!/usr/bin/env python
# dynamic_publisher.py

import rospy
import sys
import tf2_ros as tf2
import tf_conversions as tfc
import geometry_msgs.msg
from tf2_msgs.msg import TFMessage
import math
import numpy

def qRot(p, q):
    qInvert = tfc.transformations.quaternion_inverse
    qMultiply = tfc.transformations.quaternion_multiply
    qInv = qInvert(q)
    pNew = qMultiply(qMultiply(q, p), qInv)
    return pNew

def qRotInv(p, q):
    qInvert = tfc.transformations.quaternion_inverse
    qMultiply = tfc.transformations.quaternion_multiply
    qInv = qInvert(q)
    pNew = qMultiply(qMultiply(qInv, p), q)
    return pNew


def __genTf(frameT, frameS, posTs, q):
    ts = geometry_msgs.msg.TransformStamped()
    ts.header.stamp = rospy.Time.now()
    ts.header.frame_id = frameT
    ts.child_frame_id = frameS
    
    ts.transform.translation.x = posTs[0]
    ts.transform.translation.y = posTs[1]
    ts.transform.translation.z = posTs[2]

    ts.transform.rotation.x = q[0]
    ts.transform.rotation.y = q[1]
    ts.transform.rotation.z = q[2]
    ts.transform.rotation.w = q[3]
    return ts

def __main():
    rospy.init_node("A2C")
    rate = rospy.Rate(1)
    tbf = tf2.Buffer()
    tl = tf2.TransformListener(tbf)
    
    a = 0
    pp = 0
    while not tbf.can_transform("C", "D", rospy.Time()) and not rospy.is_shutdown():
        print "waiting for static tf ready.{C-D}"
        rate.sleep()
        
    while not tbf.can_transform("D", "C", rospy.Time()) and not rospy.is_shutdown():
        print "waiting for static tf ready.{D-C}"
        rate.sleep()
        
    while not tbf.can_transform("A", "B", rospy.Time()) and not rospy.is_shutdown():
        print "waiting for static tf ready.{A-B}"
        rate.sleep()
        
    while not tbf.can_transform("B", "A", rospy.Time()) and not rospy.is_shutdown():
        print "waiting for static tf ready.{B-A}"
        rate.sleep()
        
    print ("all static tf ready")
            
    roundCount = 0
    rateFast = rospy.Rate(10)
    
    qInvert = tfc.transformations.quaternion_inverse
    qMultiply = tfc.transformations.quaternion_multiply
    while not rospy.is_shutdown():
        print("begin: " + str(roundCount))
        roundCount += 1
        # lookup_transform(desFrame, srcFrame, time)
        tfD2C = tbf.lookup_transform("C", "D", rospy.Time())
        tfC2D = tbf.lookup_transform("D", "C", rospy.Time())
        tfB2A = tbf.lookup_transform("A", "B", rospy.Time())
        tfA2B = tbf.lookup_transform("B", "A", rospy.Time())
        
        bc = tf2.TransformBroadcaster()

        tD2B = (2 * math.cos(a), 1 + 2 * math.sin(a), -1, 0)
        qD2B = tfc.transformations.quaternion_from_euler(0, 0, math.pi / 2 + a)
        
        ts = __genTf("B", "DB", tD2B, qD2B)
        bc.sendTransform(ts)

        # db -> da
        tfm = tfB2A
        r = tfm.transform.rotation
        ts = tfm.transform.translation
        qB2A = numpy.array([r.x, r.y, r.z, r.w])
        tB2A = numpy.array([ts.x, ts.y, ts.z, 0])
        qD2A = qMultiply(qB2A, qD2B)
        tD2A = qRot(tD2B, qB2A)  + tB2A

        ts = __genTf("A", "DA", tD2A, qD2A)
        bc.sendTransform(ts)
        
        # da -> ca
        tfm = tfC2D
        r = tfm.transform.rotation
        ts = tfm.transform.translation
        qC2D = numpy.array([r.x, r.y, r.z, r.w])
        tC2D = numpy.array([ts.x, ts.y, ts.z, 0])
        qC2A = qMultiply(qD2A, qC2D)
        tC2A = qRot(tC2D, qD2A) + tD2A
        ts = __genTf("A", "C", tC2A, qC2A)
        bc.sendTransform(ts)

        if a < math.pi * 2:
            a += 0.01
        else:
            a = 0

        rateFast.sleep()


if __name__ == "__main__":
    __main() 

上述代码首先模拟了传感器D发布自身在B上的绝对位置

tD2B = (2 * math.cos(a), 1 + 2 * math.sin(a), -1, 0)
qD2B = tfc.transformations.quaternion_from_euler(0, 0, math.pi / 2 + a)

之后通过D->B,B->A得到D->A的坐标系转换,再通过C->D得到C->A的坐标系转换

这里需要注意的是四元数乘法不遵守交换率,因此不要将转换次序弄错

运行roscore, rviz,再运行python static_publisher.py, python dynamic_publisher.py, 在rviz选择frame A为主坐标系,之后可以看到D,DA,DB重合在一起围绕A做圆周运动

原文地址:https://www.cnblogs.com/astreye/p/7085375.html