|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +import roslib; roslib.load_manifest('lcsr_tf_tools') |
| 4 | +import rospy |
| 5 | +import tf |
| 6 | +import geometry_msgs.msg as geometry_msgs |
| 7 | +import threading |
| 8 | +import math |
| 9 | +import PyKDL |
| 10 | +import copy |
| 11 | + |
| 12 | +def multiply_quat(q0,q1): |
| 13 | + [x0,y0,z0,w0] = q0 |
| 14 | + [x1,y1,z1,w1] = q1 |
| 15 | + return ( (w0*x1 + x0*w1 + y0*z1 - z0*y1), |
| 16 | + (w0*y1 - x0*z1 + y0*w1 + z0*x1), |
| 17 | + (w0*z1 + x0*y1 - y0*x1 + z0*w1), |
| 18 | + (w0*w1 - x0*x1 - y0*y1 - z0*z1) ) |
| 19 | + |
| 20 | +class TwistFrameIntegrator(object): |
| 21 | + def __init__(self): |
| 22 | + # Get the node params |
| 23 | + self.linear_multiplier = rospy.get_param('~linear_multiplier') |
| 24 | + self.angular_multiplier = rospy.get_param('~angular_multiplier') |
| 25 | + self.broadcast_rate = rospy.get_param('~broadcast_rate') |
| 26 | + self.body_fixed = rospy.get_param('~body_fixed') |
| 27 | + |
| 28 | + # Initialize the frame we're going to publish |
| 29 | + self.transform = PyKDL.Frame() |
| 30 | + self.transform_out = PyKDL.Frame() |
| 31 | + self.translation = (0,0,0) |
| 32 | + self.rotation = (0,0,0,1) |
| 33 | + self.time = rospy.Time.now() |
| 34 | + self.frame_id = rospy.get_param('~frame_id') |
| 35 | + self.child_frame_id = rospy.get_param('~child_frame_id') |
| 36 | + |
| 37 | + # Synchronization |
| 38 | + self.broadcast_lock = threading.Lock() |
| 39 | + |
| 40 | + # Initialze TF structures |
| 41 | + self.broadcaster = tf.TransformBroadcaster() |
| 42 | + |
| 43 | + # Subscribe to twist inputs |
| 44 | + self.twist_sub = rospy.Subscriber( |
| 45 | + "twist", geometry_msgs.Twist, |
| 46 | + self.twist_cb) |
| 47 | + self.twiststamped_sub = rospy.Subscriber( |
| 48 | + "twist_stamped", geometry_msgs.TwistStamped, |
| 49 | + self.twiststamped_cb) |
| 50 | + |
| 51 | + # Broadcast the frame at a given rate |
| 52 | + self.broadcast_thread = threading.Thread(target=self.broadcast_loop) |
| 53 | + self.broadcast_thread.start() |
| 54 | + |
| 55 | + def broadcast_loop(self): |
| 56 | + """ |
| 57 | + Broadcast the integrated TF frame at a fixed rate. |
| 58 | + """ |
| 59 | + rate = rospy.Rate(self.broadcast_rate) |
| 60 | + while not rospy.is_shutdown(): |
| 61 | + # Acquire broadcast lock |
| 62 | + self.broadcast_lock.acquire() |
| 63 | + # Broadcast the frame |
| 64 | + try: |
| 65 | + self.broadcaster.sendTransform( |
| 66 | + (self.transform_out.p.x(), self.transform_out.p.y(), self.transform_out.p.z()), |
| 67 | + self.transform_out.M.GetQuaternion(), |
| 68 | + rospy.Time.now(), |
| 69 | + self.child_frame_id, |
| 70 | + self.frame_id) |
| 71 | + except Exception as ex: |
| 72 | + rospy.logerr("Failed to broadcast transform: "+str(ex)) |
| 73 | + # Release broadcast lock |
| 74 | + self.broadcast_lock.release() |
| 75 | + # Don't spam TF |
| 76 | + rate.sleep() |
| 77 | + |
| 78 | + def twiststamped_cb(self,msg): |
| 79 | + """ |
| 80 | + This callback pulls the twist out of a TwistStamped message. For now, |
| 81 | + it disregards the origin frame. |
| 82 | + """ |
| 83 | + twist_cb(msg.twist) |
| 84 | + |
| 85 | + def twist_cb(self,msg): |
| 86 | + """ |
| 87 | + This callback integrates the linear and angular velocities into a TF |
| 88 | + frame, and then broadcasts the frame. |
| 89 | + """ |
| 90 | + try: |
| 91 | + # Acquire broadcast lock |
| 92 | + self.broadcast_lock.acquire() |
| 93 | + |
| 94 | + # Convert angular rotation vector to quaternion |
| 95 | + q_velocity = ( |
| 96 | + msg.angular.x * math.sin(self.angular_multiplier/2), |
| 97 | + msg.angular.y * math.sin(self.angular_multiplier/2), |
| 98 | + msg.angular.z * math.sin(self.angular_multiplier/2), |
| 99 | + math.cos(self.angular_multiplier/2)) |
| 100 | + |
| 101 | + q_velocity_norm = math.sqrt(math.pow(q_velocity[0],2) + math.pow(q_velocity[1],2) + math.pow(q_velocity[2],2) + math.pow(q_velocity[3],2)) |
| 102 | + q_velocity = [i/q_velocity_norm for i in q_velocity] |
| 103 | + |
| 104 | + if self.body_fixed: |
| 105 | + # Integrate linear velocity in local frame |
| 106 | + self.transform.p += self.transform.M*(self.linear_multiplier*PyKDL.Vector(msg.linear.x,msg.linear.y,msg.linear.z)) |
| 107 | + # Integrate angular velocity in local frame |
| 108 | + self.rotation = multiply_quat(self.rotation,q_velocity) |
| 109 | + self.transform.M = PyKDL.Rotation.Quaternion(*self.rotation) |
| 110 | + |
| 111 | + # Copy this transform |
| 112 | + self.transform_out = PyKDL.Frame(self.transform) |
| 113 | + else: |
| 114 | + # Integrate linear velocity |
| 115 | + self.transform.p += self.linear_multiplier*PyKDL.Vector(msg.linear.x,msg.linear.y,msg.linear.z) |
| 116 | + # Integrate angular velocity |
| 117 | + self.rotation = multiply_quat(q_velocity,self.rotation) |
| 118 | + self.transform.M = PyKDL.Rotation.Quaternion(*self.rotation) |
| 119 | + |
| 120 | + # Invert the transform to get parent-frame relative transform |
| 121 | + self.transform_out = self.transform.Inverse() |
| 122 | + |
| 123 | + self.transform_out.M.DoRotZ(-math.pi/2) |
| 124 | + self.transform_out.M.DoRotX(-math.pi/2) |
| 125 | + |
| 126 | + |
| 127 | + |
| 128 | + finally: |
| 129 | + # Release broadcast lock |
| 130 | + self.broadcast_lock.release() |
| 131 | + |
| 132 | +def main(): |
| 133 | + rospy.init_node('twist_frame_integrator') |
| 134 | + |
| 135 | + twist_frame_integrator = TwistFrameIntegrator() |
| 136 | + |
| 137 | + rospy.loginfo("Spinning...") |
| 138 | + rospy.spin() |
| 139 | + rospy.loginfo("Done.") |
| 140 | + |
| 141 | + |
| 142 | +if __name__ == '__main__': |
| 143 | + main() |
0 commit comments