Skip to content

Commit 948e2d6

Browse files
committed
initial commit of a tool for publishing a tf frame from a twist message
0 parents  commit 948e2d6

File tree

6 files changed

+220
-0
lines changed

6 files changed

+220
-0
lines changed

CMakeLists.txt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cmake_minimum_required(VERSION 2.4.6)
2+
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
3+
4+
# Set the build type. Options are:
5+
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
6+
# Debug : w/ debug symbols, w/o optimization
7+
# Release : w/o debug symbols, w/ optimization
8+
# RelWithDebInfo : w/ debug symbols, w/ optimization
9+
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
10+
#set(ROS_BUILD_TYPE RelWithDebInfo)
11+
12+
rosbuild_init()
13+
14+
#set the default path for built executables to the "bin" directory
15+
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
16+
#set the default path for built libraries to the "lib" directory
17+
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
18+
19+
#uncomment if you have defined messages
20+
#rosbuild_genmsg()
21+
#uncomment if you have defined services
22+
#rosbuild_gensrv()
23+
24+
#common commands for building c++ executables and libraries
25+
#rosbuild_add_library(${PROJECT_NAME} src/example.cpp)
26+
#target_link_libraries(${PROJECT_NAME} another_library)
27+
#rosbuild_add_boost_directories()
28+
#rosbuild_link_boost(${PROJECT_NAME} thread)
29+
#rosbuild_add_executable(example examples/example.cpp)
30+
#target_link_libraries(example ${PROJECT_NAME})

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include $(shell rospack find mk)/cmake.mk

launch/twist_frame_integrator.launch

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<launch>
2+
<node pkg="lcsr_tf_tools" type="twist_frame_integrator.py" name="twist_frame_integrator" output="screen">
3+
<param name="linear_multiplier" value="0.0001"/>
4+
<param name="angular_multiplier" value="0.0001"/>
5+
<param name="broadcast_rate" value="50"/>
6+
7+
<param name="child_frame_id" value="integrated_twist_frame"/>
8+
<param name="frame_id" value="/world"/>
9+
10+
<param name="body_fixed" value="true"/>
11+
12+
<remap from="/twist" to="/spacenav/twist"/>
13+
</node>
14+
</launch>

mainpage.dox

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
\mainpage
3+
\htmlinclude manifest.html
4+
5+
\b lcsr_tf_tools
6+
7+
<!--
8+
Provide an overview of your package.
9+
-->
10+
11+
-->
12+
13+
14+
*/

manifest.xml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<package>
2+
<description brief="lcsr_tf_tools">
3+
4+
lcsr_tf_tools
5+
6+
</description>
7+
<author>Jonathan Bohren</author>
8+
<license>BSD</license>
9+
<review status="unreviewed" notes=""/>
10+
<url>http://ros.org/wiki/lcsr_tf_tools</url>
11+
12+
<depend package="rospy"/>
13+
<depend package="tf"/>
14+
<depend package="geometry_msgs"/>
15+
<depend package="python_orocos_kdl"/>
16+
</package>
17+
18+

scripts/twist_frame_integrator.py

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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

Comments
 (0)