├── .gitignore ├── srv └── FrameGraph.srv ├── msg └── tfMessage.msg ├── doc ├── bifrucation.pdf └── bifrucation.gv ├── test ├── test_message_filter.xml ├── transform_twist_test.launch ├── transform_listener_unittest.launch ├── test_broadcaster.launch ├── method_test.py ├── test_transform_datatypes.cpp ├── testListener.cpp ├── tf_unittest_future.cpp ├── operator_overload.cpp ├── transform_listener_unittest.cpp ├── testBroadcaster.cpp ├── quaternion.cpp ├── python_debug_test.py ├── speed_test.cpp ├── tf_benchmark.cpp ├── test_datatype_conversion.py └── testPython.py ├── index.rst ├── rosdoc.yaml ├── scripts ├── groovy_compatibility │ ├── tf_remap │ └── view_frames ├── tf_remap ├── bullet_migration_sed.py ├── python_benchmark.py └── view_frames ├── remap_tf.launch ├── setup.py ├── README.md ├── include └── tf │ ├── LinearMath │ ├── MinMax.h │ ├── QuadWord.h │ └── Transform.h │ ├── exceptions.h │ ├── transform_broadcaster.h │ ├── time_cache.h │ ├── transform_listener.h │ └── transform_datatypes.h ├── src ├── tf │ ├── __init__.py │ ├── broadcaster.py │ └── tfwtf.py ├── empty_listener.cpp ├── transform_broadcaster.cpp ├── tf_echo.cpp ├── static_transform_publisher.cpp ├── change_notifier.cpp ├── cache.cpp ├── transform_listener.cpp └── tf_monitor.cpp ├── package.xml ├── mainpage.dox ├── CMakeLists.txt └── conf.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | -------------------------------------------------------------------------------- /srv/FrameGraph.srv: -------------------------------------------------------------------------------- 1 | --- 2 | string dot_graph 3 | -------------------------------------------------------------------------------- /msg/tfMessage.msg: -------------------------------------------------------------------------------- 1 | geometry_msgs/TransformStamped[] transforms 2 | -------------------------------------------------------------------------------- /doc/bifrucation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davheld/tf/HEAD/doc/bifrucation.pdf -------------------------------------------------------------------------------- /test/test_message_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/transform_twist_test.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/transform_listener_unittest.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/test_broadcaster.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | tf 2 | == 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | tf_python 8 | 9 | Indices and tables 10 | ================== 11 | 12 | * :ref:`genindex` 13 | * :ref:`modindex` 14 | * :ref:`search` 15 | 16 | -------------------------------------------------------------------------------- /rosdoc.yaml: -------------------------------------------------------------------------------- 1 | - builder: rosmake 2 | - builder: sphinx 3 | name: Python API 4 | output_dir: python 5 | - builder: doxygen 6 | name: C++ API 7 | output_dir: c++ 8 | file_patterns: '*.c *.cpp *.h *.cc *.hh *.dox' 9 | -------------------------------------------------------------------------------- /scripts/groovy_compatibility/tf_remap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Please use the package local script inside tf not the global one, this is deprecated." 4 | echo "Running [rosrun tf tf_remap] for you" 5 | exec rosrun tf tf_remap "$@" -------------------------------------------------------------------------------- /scripts/groovy_compatibility/view_frames: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Please use the package local script inside tf not the global one, this is deprecated." 4 | echo "Running [rosrun tf view_frames] for you" 5 | exec rosrun tf view_frames "$@" -------------------------------------------------------------------------------- /remap_tf.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - {old: "/asdf", 5 | new: "/a"} 6 | - {old: "/fdsa", 7 | new: "/b"} 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | from catkin_pkg.python_setup import generate_distutils_setup 5 | 6 | d = generate_distutils_setup( 7 | packages=['tf'], 8 | package_dir={'': 'src'}, 9 | requires=['genmsg', 'genpy', 'roslib', 'rospkg', 'geometry_msgs', 'sensor_msgs', 'std_msgs'], 10 | scripts=['scripts/groovy_compatibility/tf_remap', 11 | 'scripts/groovy_compatibility/view_frames'] 12 | ) 13 | 14 | setup(**d) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tf 2 | == 3 | 4 | A custom version of the ros/tf package, with small API changes. Used by some the our code, until we migrate to TF2. 5 | 6 | Inserted a class in the class hierarchy between Transformer and 7 | TransformListener. This class serves to decouple the 2 functions 8 | of the original TransformListener class: 9 | - a bunch of helper functions to apply a transform to some data structures 10 | - listen to tf messages and update the buffer 11 | 12 | With the new class hierarchy, TransformerHelper provides the helper functions, 13 | and TransformListener updates the buffer. 14 | 15 | The same decoupling was implemented in TF2, so as soon as we migrate our code 16 | to TF2 we don't need this custom implementation anymore. 17 | 18 | I submitted the changes to the upstream TF package, but Tully Foote does not want 19 | to modify the stable TF API at this point. 20 | -------------------------------------------------------------------------------- /doc/bifrucation.gv: -------------------------------------------------------------------------------- 1 | digraph G{ 2 | label = "Effect of publishing Inverted Parent Child Relationship on TF tree"; 3 | subgraph cluster0{ 4 | a [label="A"]; 5 | aa [label="AA"]; 6 | b [label="B"]; 7 | bb [label="BB"]; 8 | c [ label="C"]; 9 | d [ label="D"]; 10 | a -> b; 11 | aa -> b; 12 | bb -> c; 13 | b -> c; 14 | c -> d; 15 | label = "a) Example Tree"; 16 | color=black; 17 | } 18 | subgraph cluster1{ 19 | a1 [label="A"]; 20 | aa1 [label="AA"]; 21 | b1 [label="B"]; 22 | bb1 [label="BB"]; 23 | c1 [ label="C"]; 24 | d1 [ label="D"]; 25 | a1 -> b1; 26 | aa1 -> b1; 27 | bb1 -> c1; 28 | c1 -> b1; 29 | label = "b) C-B Inverted Case1 (Part of the time)"; 30 | color=black; 31 | } 32 | subgraph cluster2{ 33 | a2 [label="A"]; 34 | aa2 [label="AA"]; 35 | b2 [label="B"]; 36 | bb2 [label="BB"]; 37 | c2 [ label="C"]; 38 | d2 [ label="D"]; 39 | 40 | a2 -> b2; 41 | aa2 -> b2; 42 | bb2 -> c2; 43 | c2 -> d2; 44 | label = "c) C-B Inverted Case2 (part of the time)"; 45 | color=black; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/method_test.py: -------------------------------------------------------------------------------- 1 | import roslib; roslib.load_manifest("tf") 2 | 3 | import rospy 4 | import tf 5 | import time 6 | import bullet 7 | import math 8 | 9 | 10 | try: 11 | transform_stamped = tf.TransformStamped() 12 | print "getting stamp" 13 | print transform_stamped.stamp 14 | # mytime = rospy.Time().now() 15 | mytime = rospy.Time(10,20) 16 | transform_stamped.stamp = mytime 17 | print mytime 18 | print "getting stamp", transform_stamped.stamp 19 | print "transform:", transform_stamped.transform 20 | transform_stamped.transform.setIdentity() 21 | print "after setIdentity()", transform_stamped.transform 22 | # transform_stamped.transform.basis.setEulerZYX(0,0,0) 23 | quat = bullet.Quaternion(math.pi/2,0,0) 24 | print "quaternion ", quat 25 | transform_stamped.transform.setRotation(quat) 26 | print "setting rotation to PI/2\n After setRotation" 27 | print transform_stamped.transform 28 | 29 | other_transform = bullet.Transform() 30 | other_transform.setIdentity() 31 | transform_stamped.transform = other_transform 32 | print "After assignment of Identity" 33 | print transform_stamped.transform 34 | 35 | other_transform = bullet.Transform() 36 | other_transform.setIdentity() 37 | other_transform.setRotation(quat) 38 | transform_stamped.transform = other_transform 39 | print "After assignment of Rotation Transformation" 40 | print transform_stamped.transform 41 | 42 | except ValueError, e: 43 | print "Exception %s Improperly thrown: %s"%(type(e), e) 44 | -------------------------------------------------------------------------------- /include/tf/LinearMath/MinMax.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ 3 | 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it freely, 8 | subject to the following restrictions: 9 | 10 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 11 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 12 | 3. This notice may not be removed or altered from any source distribution. 13 | */ 14 | 15 | 16 | 17 | #ifndef TF_MINMAX_H 18 | #define TF_MINMAX_H 19 | 20 | template 21 | TFSIMD_FORCE_INLINE const T& tfMin(const T& a, const T& b) 22 | { 23 | return a < b ? a : b ; 24 | } 25 | 26 | template 27 | TFSIMD_FORCE_INLINE const T& tfMax(const T& a, const T& b) 28 | { 29 | return a > b ? a : b; 30 | } 31 | 32 | 33 | template 34 | TFSIMD_FORCE_INLINE void tfSetMin(T& a, const T& b) 35 | { 36 | if (b < a) 37 | { 38 | a = b; 39 | } 40 | } 41 | 42 | template 43 | TFSIMD_FORCE_INLINE void tfSetMax(T& a, const T& b) 44 | { 45 | if (a < b) 46 | { 47 | a = b; 48 | } 49 | } 50 | 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/tf/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009, Willow Garage, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of the Willow Garage, Inc. nor the names of its 13 | # contributors may be used to endorse or promote products derived from 14 | # this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | # POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from _tf import * 29 | from listener import TransformListener, TransformerROS 30 | from broadcaster import TransformBroadcaster 31 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | tf 3 | 1.10.8 4 | 5 | 6 | tf is a package that lets the user keep track of multiple coordinate 7 | frames over time. tf maintains the relationship between coordinate 8 | frames in a tree structure buffered in time, and lets the user 9 | transform points, vectors, etc between any two coordinate frames at 10 | any desired point in time. 11 | 12 | 13 | Tully Foote 14 | Eitan Marder-Eppstein 15 | Wim Meeussen 16 | Tully Foote 17 | 18 | BSD 19 | http://www.ros.org/wiki/tf 20 | 21 | catkin 22 | 23 | angles 24 | geometry_msgs 25 | message_filters 26 | message_generation 27 | rosconsole 28 | roscpp 29 | rostest 30 | sensor_msgs 31 | std_msgs 32 | tf2 33 | tf2_ros 34 | 35 | geometry_msgs 36 | graphviz 37 | message_filters 38 | message_runtime 39 | rosconsole 40 | roscpp 41 | roswtf 42 | sensor_msgs 43 | std_msgs 44 | tf2 45 | tf2_ros 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/test_transform_datatypes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "tf/LinearMath/Vector3.h" 35 | 36 | using namespace tf; 37 | 38 | TEST(tf, set) 39 | { 40 | tf::Quaternion q = tf::createQuaternionFromRPY(0, 0, 0); 41 | EXPECT_EQ(q, tf::createIdentityQuaternion()); 42 | } 43 | 44 | 45 | 46 | int main(int argc, char **argv){ 47 | testing::InitGoogleTest(&argc, argv); 48 | return RUN_ALL_TESTS(); 49 | } 50 | -------------------------------------------------------------------------------- /include/tf/exceptions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #ifndef TF_EXCEPTIONS_H 33 | #define TF_EXCEPTIONS_H 34 | 35 | #include 36 | #include 37 | 38 | namespace tf{ 39 | 40 | // Pass through exceptions from tf2 41 | typedef tf2::TransformException TransformException; 42 | typedef tf2::LookupException LookupException; 43 | typedef tf2::ConnectivityException ConnectivityException; 44 | typedef tf2::ExtrapolationException ExtrapolationException; 45 | typedef tf2::InvalidArgumentException InvalidArgument; 46 | 47 | 48 | } 49 | 50 | 51 | #endif //TF_EXCEPTIONS_H 52 | -------------------------------------------------------------------------------- /src/empty_listener.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #include 30 | #include 31 | 32 | 33 | int main(int argc, char** argv) 34 | { 35 | ros::init(argc, argv, "my_tf_listener"); 36 | 37 | ros::NodeHandle node; 38 | 39 | tf::TransformListener listener; 40 | 41 | ros::Rate rate(10.0); 42 | while (node.ok()){ 43 | tf::StampedTransform transform; 44 | try 45 | { 46 | listener.lookupTransform("odom_combined", "base_link", ros::Time(0), transform); 47 | } 48 | catch (tf::TransformException ex) 49 | { 50 | ROS_ERROR("%s",ex.what()); 51 | } 52 | 53 | rate.sleep(); 54 | } 55 | return 0; 56 | }; 57 | -------------------------------------------------------------------------------- /mainpage.dox: -------------------------------------------------------------------------------- 1 | 2 | /** \mainpage 3 | 4 | @htmlinclude manifest.html 5 | 6 | @b tf is a library for keeping track of coordinate frames. There are both C++ and Python bindings. 7 | 8 | @section usage Common Usage 9 | For most ROS use cases, the basic tf::Transformer library is not used directly. 10 | 11 | There are two helper classes to provide sending and recieving of ROS transform 12 | messages. tf::TransformBroadcaster and tf::TransformListener. 13 | 14 | @subsection listener TransformListener 15 | The tf::TransformListener class inherits from tf::Transformer to provide all the functionality of the 16 | basic library. In addition, it provides methods to transform data ROS messages directly and 17 | it automatically listens for transforms published on ROS. 18 | 19 | @subsection message_filter MessageFilter 20 | The tf::MessageFilter is the recommended method for receiving almost any sensor data from ROS. 21 | Data in ROS can be published with respect to any known frame. 22 | The tf::MessageFilter class makes it easy to use this data 23 | by providing callbacks only when it is possible to transform it into your desired 24 | target frame. 25 | 26 | The tf::MessageFilter class can subscribe to any ROS datatype that has a ROS Header. 27 | 28 | @subsection broadcaster TransformBroadcaster 29 | The tf::TransformBroadcaster class is the complement to the tf::TransformListener class. The broadcaster class provides a 30 | simple API for broadcasting coordinate frame transforms to other ROS nodes. 31 | 32 | @subsection send_transform send_transform 33 | The send_transform command is the easiest way to report transforms for fixed offsets. 34 | It is a simple command-line utility that repeatedly publishes the fixed-offset transform to ROS. 35 | 36 | @subsection datatypes Data Types used in tf 37 | - Quaternion typedef of btQuaternion 38 | - Vector3 typedef of btVector3 39 | - Point typedef of btVector3 40 | - Transform typedef of btTransform 41 | - Pose typedef of btTransform 42 | 43 | -Stamped version of all of the above inherits from the data type and also has: 44 | - ros::Time stamp_ 45 | - std::string frame_id_ 46 | - std::string child_frame_id_ (only used for Stamped ) 47 | 48 | - There are analogous ROS messages in std_msgs to the Stamped data types. 49 | 50 | - Time represented by ros::Time and ros::Duration in ros/time.h in roscpp 51 | 52 | 53 | 54 | */ 55 | -------------------------------------------------------------------------------- /test/testListener.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "tf/transform_listener.h" 31 | #include "ros/ros.h" 32 | 33 | #include 34 | 35 | TEST(TransformBroadcaster, single_frame) 36 | { 37 | tf::TransformListener tfl; 38 | EXPECT_TRUE(tfl.waitForTransform("frame1", "frame2", ros::Time(), ros::Duration(2.0))); 39 | 40 | } 41 | 42 | TEST(TransformBroadcaster, multi_frame) 43 | { 44 | tf::TransformListener tfl; 45 | EXPECT_TRUE(tfl.waitForTransform("vframe0", "vframe2", ros::Time(), ros::Duration(2.0))); 46 | 47 | } 48 | 49 | int main(int argc, char ** argv) 50 | { 51 | testing::InitGoogleTest(&argc, argv); 52 | 53 | //Initialize ROS 54 | ros::init(argc, argv, "listener"); 55 | ros::NodeHandle nh; 56 | 57 | int ret = RUN_ALL_TESTS(); 58 | 59 | return ret; 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /test/tf_unittest_future.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tf/LinearMath/Vector3.h" 6 | 7 | using namespace tf; 8 | 9 | void seed_rand() 10 | { 11 | //Seed random number generator with current microseond count 12 | timeval temp_time_struct; 13 | gettimeofday(&temp_time_struct,NULL); 14 | srand(temp_time_struct.tv_usec); 15 | }; 16 | 17 | 18 | 19 | TEST(tf, SignFlipExtrapolate) 20 | { 21 | double epsilon = 1e-3; 22 | 23 | double truex, truey, trueyaw1, trueyaw2; 24 | 25 | truex = 5.220; 26 | truey = 1.193; 27 | trueyaw1 = 2.094; 28 | trueyaw2 = 2.199; 29 | ros::Time ts0; 30 | ts0.fromSec(46.6); 31 | ros::Time ts1; 32 | ts1.fromSec(46.7); 33 | ros::Time ts2; 34 | ts2.fromSec(46.8); 35 | 36 | TransformStorage tout; 37 | double yaw, pitch, roll; 38 | 39 | TransformStorage t0(StampedTransform 40 | (tf::Transform(tf::Quaternion(0.000, 0.000, -0.8386707128751809, 0.5446388118427071), 41 | tf::Vector3(1.0330764266905630, 5.2545257423922198, -0.000)), 42 | ts0, "odom", "other0"), 3); 43 | TransformStorage t1(StampedTransform 44 | (tf::Transform(tf::Quaternion(0.000, 0.000, 0.8660255375641606, -0.4999997682866531), 45 | tf::Vector3(1.5766646418987809, 5.1177550046707436, -0.000)), 46 | ts1, "odom", "other1"), 3); 47 | TransformStorage t2(StampedTransform 48 | (tf::Transform(tf::Quaternion(0.000, 0.000, 0.8910066733792211, -0.4539902069358919), 49 | tf::Vector3(2.1029791754869160, 4.9249128183465967, -0.000)), 50 | ts2, "odom", "other2"), 3); 51 | 52 | tf::TimeCache tc; 53 | tf::Transform res; 54 | 55 | tc.interpolate(t0, t1, ts1, tout); 56 | res = tout.inverse(); 57 | res.getBasis().getEulerZYX(yaw,pitch,roll); 58 | 59 | EXPECT_NEAR(res.getOrigin().x(), truex, epsilon); 60 | EXPECT_NEAR(res.getOrigin().y(), truey, epsilon); 61 | EXPECT_NEAR(yaw, trueyaw1, epsilon); 62 | 63 | tc.interpolate(t0, t1, ts2, tout); 64 | res = tout.inverse(); 65 | res.getBasis().getEulerZYX(yaw,pitch,roll); 66 | 67 | EXPECT_NEAR(res.getOrigin().x(), truex, epsilon); 68 | EXPECT_NEAR(res.getOrigin().y(), truey, epsilon); 69 | EXPECT_NEAR(yaw, trueyaw2, epsilon); 70 | 71 | tc.interpolate(t1, t2, ts2, tout); 72 | res = tout.inverse(); 73 | res.getBasis().getEulerZYX(yaw,pitch,roll); 74 | 75 | EXPECT_NEAR(res.getOrigin().x(), truex, epsilon); 76 | EXPECT_NEAR(res.getOrigin().y(), truey, epsilon); 77 | EXPECT_NEAR(yaw, trueyaw2, epsilon); 78 | } 79 | 80 | 81 | 82 | /** @todo Make this actually Assert something */ 83 | 84 | int main(int argc, char **argv){ 85 | testing::InitGoogleTest(&argc, argv); 86 | return RUN_ALL_TESTS(); 87 | } 88 | 89 | -------------------------------------------------------------------------------- /scripts/tf_remap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Software License Agreement (BSD License) 3 | # 4 | # Copyright (c) 2008, Willow Garage, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of Willow Garage, Inc. nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | 35 | 36 | ## remap a tf topic 37 | 38 | import roslib; roslib.load_manifest('tf') 39 | 40 | import rospy 41 | from tf.msg import tfMessage 42 | 43 | class TfRemapper: 44 | def __init__(self): 45 | self.pub = rospy.Publisher('/tf', tfMessage) 46 | mappings = rospy.get_param('~mappings', []) 47 | self.mappings = {} 48 | 49 | for i in mappings: 50 | if "old" in i and "new" in i: 51 | self.mappings[i["old"]] = i["new"] 52 | 53 | print "Applying the following mappings to incoming tf frame ids", self.mappings 54 | rospy.Subscriber("/tf_old", tfMessage, self.callback) 55 | 56 | def callback(self, tf_msg): 57 | for transform in tf_msg.transforms: 58 | if transform.header.frame_id in self.mappings: 59 | transform.header.frame_id = self.mappings[transform.header.frame_id] 60 | if transform.child_frame_id in self.mappings: 61 | transform.child_frame_id = self.mappings[transform.child_frame_id] 62 | 63 | self.pub.publish(tf_msg) 64 | 65 | def remap_tf(): 66 | 67 | pub.publish(Empty()) 68 | 69 | 70 | if __name__ == '__main__': 71 | rospy.init_node('tf_remapper') 72 | tfr = TfRemapper() 73 | rospy.spin() 74 | -------------------------------------------------------------------------------- /test/operator_overload.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include "tf/LinearMath/Transform.h" 34 | 35 | 36 | void seed_rand() 37 | { 38 | //Seed random number generator with current microseond count 39 | timeval temp_time_struct; 40 | gettimeofday(&temp_time_struct,NULL); 41 | srand(temp_time_struct.tv_usec); 42 | }; 43 | 44 | 45 | int main(int argc, char **argv){ 46 | 47 | unsigned int runs = 400; 48 | seed_rand(); 49 | 50 | std::vector xvalues(runs), yvalues(runs), zvalues(runs); 51 | for ( unsigned int i = 0; i < runs ; i++ ) 52 | { 53 | xvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 54 | yvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 55 | zvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 56 | } 57 | 58 | //Useful Operator Overload 59 | for ( unsigned int i = 0; i < runs ; i++ ) 60 | { 61 | tf::Transform transform(tf::Quaternion(0,0,0), tf::Vector3(xvalues[i],yvalues[i],zvalues[i])); 62 | tf::Quaternion initial(xvalues[i],yvalues[i],zvalues[i]); 63 | tf::Quaternion final(xvalues[i],yvalues[i],zvalues[i]); 64 | final = transform * initial; 65 | std::printf("Useful Operator Overload: %f, angle between quaternions\n", initial.angle(final)); 66 | } 67 | 68 | 69 | return 0; 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/transform_broadcaster.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | /** \author Tully Foote */ 32 | 33 | 34 | #include "ros/ros.h" 35 | #include "tf/transform_broadcaster.h" 36 | #include "tf/transform_listener.h" 37 | 38 | #include 39 | 40 | 41 | 42 | namespace tf { 43 | 44 | TransformBroadcaster::TransformBroadcaster(): 45 | tf2_broadcaster_() 46 | { 47 | }; 48 | 49 | void TransformBroadcaster::sendTransform(const geometry_msgs::TransformStamped & msgtf) 50 | { 51 | tf2_broadcaster_.sendTransform(msgtf); 52 | } 53 | 54 | void TransformBroadcaster::sendTransform(const StampedTransform & transform) 55 | { 56 | geometry_msgs::TransformStamped msgtf; 57 | transformStampedTFToMsg(transform, msgtf); 58 | tf2_broadcaster_.sendTransform(msgtf); 59 | } 60 | 61 | void TransformBroadcaster::sendTransform(const std::vector & msgtf) 62 | { 63 | tf2_broadcaster_.sendTransform(msgtf); 64 | } 65 | 66 | void TransformBroadcaster::sendTransform(const std::vector & transforms) 67 | { 68 | std::vector msgtfs; 69 | for (std::vector::const_iterator it = transforms.begin(); it != transforms.end(); ++it) 70 | { 71 | geometry_msgs::TransformStamped msgtf; 72 | transformStampedTFToMsg(*it, msgtf); 73 | msgtfs.push_back(msgtf); 74 | 75 | } 76 | tf2_broadcaster_.sendTransform(msgtfs); 77 | } 78 | 79 | 80 | 81 | 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /test/transform_listener_unittest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | void seed_rand() 36 | { 37 | //Seed random number generator with current microseond count 38 | timeval temp_time_struct; 39 | gettimeofday(&temp_time_struct,NULL); 40 | srand(temp_time_struct.tv_usec); 41 | }; 42 | 43 | void generate_rand_vectors(double scale, uint64_t runs, std::vector& xvalues, std::vector& yvalues, std::vector&zvalues) 44 | { 45 | seed_rand(); 46 | for ( uint64_t i = 0; i < runs ; i++ ) 47 | { 48 | xvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 49 | yvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 50 | zvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 51 | } 52 | } 53 | 54 | 55 | using namespace tf; 56 | 57 | 58 | TEST(transform_listener, resolve) 59 | { 60 | ros::NodeHandle n("~"); 61 | tf::TransformListener tl; 62 | 63 | //no prefix 64 | EXPECT_STREQ("id", tl.resolve("id").c_str()); 65 | 66 | n.setParam("tf_prefix", "a_tf_prefix"); 67 | tf::TransformListener tp; 68 | 69 | std::string prefix_str = tf::getPrefixParam(n); 70 | 71 | EXPECT_STREQ("a_tf_prefix", prefix_str.c_str()); 72 | 73 | EXPECT_STREQ("a_tf_prefix/id", tf::resolve(prefix_str, "id").c_str()); 74 | 75 | EXPECT_STREQ("a_tf_prefix/id", tp.resolve("id").c_str()); 76 | 77 | 78 | } 79 | 80 | 81 | int main(int argc, char **argv){ 82 | testing::InitGoogleTest(&argc, argv); 83 | ros::init(argc, argv, "transform_listener_unittest"); 84 | return RUN_ALL_TESTS(); 85 | } 86 | -------------------------------------------------------------------------------- /src/tf/broadcaster.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2008, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of the Willow Garage nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import rospy 34 | import tf 35 | import tf.msg 36 | import geometry_msgs.msg 37 | import math 38 | 39 | class TransformBroadcaster: 40 | """ 41 | :class:`TransformBroadcaster` is a convenient way to send transformation updates on the ``"/tf"`` message topic. 42 | """ 43 | 44 | def __init__(self): 45 | self.pub_tf = rospy.Publisher("/tf", tf.msg.tfMessage) 46 | 47 | def sendTransform(self, translation, rotation, time, child, parent): 48 | """ 49 | :param translation: the translation of the transformtion as a tuple (x, y, z) 50 | :param rotation: the rotation of the transformation as a tuple (x, y, z, w) 51 | :param time: the time of the transformation, as a rospy.Time() 52 | :param child: child frame in tf, string 53 | :param parent: parent frame in tf, string 54 | 55 | Broadcast the transformation from tf frame child to parent on ROS topic ``"/tf"``. 56 | """ 57 | 58 | t = geometry_msgs.msg.TransformStamped() 59 | t.header.frame_id = parent 60 | t.header.stamp = time 61 | t.child_frame_id = child 62 | t.transform.translation.x = translation[0] 63 | t.transform.translation.y = translation[1] 64 | t.transform.translation.z = translation[2] 65 | 66 | t.transform.rotation.x = rotation[0] 67 | t.transform.rotation.y = rotation[1] 68 | t.transform.rotation.z = rotation[2] 69 | t.transform.rotation.w = rotation[3] 70 | 71 | tfm = tf.msg.tfMessage([t]) 72 | self.pub_tf.publish(tfm) 73 | 74 | if __name__ == '__main__': 75 | rospy.init_node('tf_turtle') 76 | tfb = TurtleTFBroadcaster(rospy.get_param('~turtle')) 77 | rospy.spin() 78 | -------------------------------------------------------------------------------- /include/tf/transform_broadcaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | /** \author Tully Foote */ 32 | 33 | #ifndef TF_TRANSFORMBROADCASTER_H 34 | #define TF_TRANSFORMBROADCASTER_H 35 | 36 | #include "tf/tf.h" 37 | #include "tf/tfMessage.h" 38 | 39 | #include 40 | 41 | 42 | namespace tf 43 | { 44 | 45 | 46 | /** \brief This class provides an easy way to publish coordinate frame transform information. 47 | * It will handle all the messaging and stuffing of messages. And the function prototypes lay out all the 48 | * necessary data needed for each message. */ 49 | 50 | class TransformBroadcaster{ 51 | public: 52 | /** \brief Constructor (needs a ros::Node reference) */ 53 | TransformBroadcaster(); 54 | 55 | /** \brief Send a StampedTransform 56 | * The stamped data structure includes frame_id, and time, and parent_id already. */ 57 | void sendTransform(const StampedTransform & transform); 58 | 59 | /** \brief Send a vector of StampedTransforms 60 | * The stamped data structure includes frame_id, and time, and parent_id already. */ 61 | void sendTransform(const std::vector & transforms); 62 | 63 | /** \brief Send a TransformStamped message 64 | * The stamped data structure includes frame_id, and time, and parent_id already. */ 65 | void sendTransform(const geometry_msgs::TransformStamped & transform); 66 | 67 | /** \brief Send a vector of TransformStamped messages 68 | * The stamped data structure includes frame_id, and time, and parent_id already. */ 69 | void sendTransform(const std::vector & transforms); 70 | 71 | private: 72 | 73 | tf2_ros::TransformBroadcaster tf2_broadcaster_; 74 | 75 | }; 76 | 77 | } 78 | 79 | #endif //TF_TRANSFORMBROADCASTER_H 80 | -------------------------------------------------------------------------------- /scripts/bullet_migration_sed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Software License Agreement (BSD License) 3 | # 4 | # Copyright (c) 2011, Willow Garage, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of the Willow Garage nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | 34 | 35 | 36 | # Run this script to update bullet datatypes from tf in Electric to 37 | # Fuerte/Unstable or newer 38 | 39 | # by default this assumes your files are not using the tf namespace. 40 | # If they are change the line below with the for loop to use the 41 | # namespaced_rules 42 | 43 | import subprocess 44 | 45 | 46 | 47 | 48 | cmd = "find . -type f ! -name '*.svn-base' -a ! -name '*.hg' -a ! -name '*.git' -a \( -name '*.c*' -o -name '*.h*' \) -exec sed -i '%(rule)s' {} \;" 49 | 50 | rules = ['s|LinearMath/bt|tf/LinearMath/|g', # include path 51 | 's/btTransform\.h/Transform\.h/g', # include files 52 | 's/btMatrix3x3\.h/Matrix3x3\.h/g', 53 | 's/btScalar\.h/Scalar\.h/g', 54 | 's/btQuaternion\.h/Quaternion\.h/g', 55 | 's/btQuadWord\.h/QuadWord\.h/g', 56 | 's/btMinMax\.h/MinMax\.h/g', 57 | 's/btVector3\.h/Vector3\.h/g', 58 | 's/btScalar/tfScalar/g', 59 | ] 60 | 61 | unnamespaced_rules = [ 62 | 's/btTransform/tf::Transform/g', 63 | 's/btQuaternion/tf::Quaternion/g', 64 | 's/btVector3/tf::Vector3/g', 65 | 's/btMatrix3x3/tf::Matrix3x3/g', 66 | 's/btQuadWord/tf::QuadWord/g', 67 | 68 | ] 69 | 70 | namespaced_rules = [ 71 | 's/btTransform/Transform/g', 72 | 's/btQuaternion/Quaternion/g', 73 | 's/btVector3/Vector3/g', 74 | 's/btMatrix3x3/Matrix3x3/g', 75 | 's/btQuadWord/QuadWord/g', 76 | #'s/btScalar/Scalar/g', 77 | ] 78 | 79 | 80 | 81 | for rule in rules + unnamespaced_rules: #change me if using files with namespace tf set 82 | full_cmd = cmd%locals() 83 | print ("Running %s"%full_cmd) 84 | ret_code = subprocess.call(full_cmd, shell=True) 85 | if ret_code == 0: 86 | print ("success") 87 | else: 88 | print ("failure") 89 | -------------------------------------------------------------------------------- /test/testBroadcaster.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "tf/transform_broadcaster.h" 31 | #include "ros/ros.h" 32 | 33 | class testBroadcaster 34 | { 35 | public: 36 | //constructor 37 | testBroadcaster() : count(0), count1(0){}; 38 | //Clean up ros connections 39 | ~testBroadcaster() { } 40 | 41 | //A pointer to the rosTFServer class 42 | tf::TransformBroadcaster broadcaster; 43 | 44 | 45 | // A function to call to send data periodically 46 | void test () { 47 | broadcaster.sendTransform(tf::StampedTransform(tf::Transform(tf::createIdentityQuaternion(), tf::Vector3(1,2,3)), ros::Time().fromSec(1), "frame2", "frame1")); 48 | 49 | if (count > 9000) 50 | { 51 | count = 0; 52 | std::cerr<<"Counter 0 rolledover at 9000"<< std::endl; 53 | } 54 | else 55 | count ++; 56 | //std::cerr< vec; 62 | vec.push_back(tf::StampedTransform(tf::Transform(tf::createIdentityQuaternion(), tf::Vector3(1,2,3)), ros::Time().fromSec(1), "vframe2", "vframe1")); 63 | vec.push_back(tf::StampedTransform(tf::Transform(tf::createIdentityQuaternion(), tf::Vector3(1,2,3)), ros::Time().fromSec(1), "vframe1", "vframe0")); 64 | broadcaster.sendTransform(vec); 65 | 66 | if (count1 > 9000) 67 | { 68 | count1 = 0; 69 | std::cerr<<"Counter 1 rolledover at 9000"<< std::endl; 70 | } 71 | else 72 | count1 ++; 73 | //std::cerr< 31 | #include 32 | #include 33 | #include 34 | 35 | #include "tf/LinearMath/Transform.h" 36 | 37 | double epsilon = 10E-6; 38 | 39 | 40 | void seed_rand() 41 | { 42 | //Seed random number generator with current microseond count 43 | timeval temp_time_struct; 44 | gettimeofday(&temp_time_struct,NULL); 45 | srand(temp_time_struct.tv_usec); 46 | }; 47 | 48 | void testQuatRPY(tf::Quaternion q_baseline) 49 | { 50 | q_baseline.normalize(); 51 | tf::Matrix3x3 m(q_baseline); 52 | double roll, pitch, yaw; 53 | 54 | for (int solution = 1 ; solution < 2 ; ++solution) 55 | { 56 | m.getRPY(roll, pitch, yaw, solution); 57 | tf::Quaternion q_from_rpy; 58 | q_from_rpy.setRPY(roll, pitch, yaw); 59 | // use angleShortestPath() because angle() can return PI for equivalent 60 | // quaternions 61 | double angle1 = q_from_rpy.angleShortestPath(q_baseline); 62 | ASSERT_NEAR(0.0, angle1, epsilon); 63 | //std::printf("%f, angle between quaternions\n", angle1); 64 | 65 | tf::Matrix3x3 m2; 66 | m2.setRPY(roll, pitch, yaw); 67 | tf::Quaternion q_from_m_from_rpy; 68 | m2.getRotation(q_from_m_from_rpy); 69 | // use angleShortestPath() because angle() can return PI for equivalent 70 | // quaternions 71 | double angle2 = q_from_m_from_rpy.angleShortestPath(q_baseline); 72 | ASSERT_NEAR(0.0, angle2, epsilon); 73 | //std::printf("%f, angle between quaternions\n", angle2); 74 | } 75 | } 76 | 77 | TEST(tf, Quaternion) 78 | { 79 | unsigned int runs = 400; 80 | seed_rand(); 81 | 82 | std::vector xvalues(runs), yvalues(runs), zvalues(runs); 83 | for ( unsigned int i = 0; i < runs ; i++ ) 84 | { 85 | xvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 86 | yvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 87 | zvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 88 | } 89 | 90 | 91 | for ( unsigned int i = 0; i < runs ; i++ ) 92 | { 93 | tf::Matrix3x3 mat; 94 | tf::Quaternion q_baseline; 95 | q_baseline.setRPY(zvalues[i],yvalues[i],xvalues[i]); 96 | mat.setRotation(q_baseline); 97 | tf::Quaternion q_from_m; 98 | mat.getRotation(q_from_m); 99 | double angle = q_from_m.angle(q_baseline); 100 | ASSERT_NEAR(0.0, angle, epsilon); 101 | testQuatRPY(q_baseline); 102 | } 103 | 104 | // test some corner cases 105 | testQuatRPY(tf::Quaternion( 0.5, 0.5, 0.5, -0.5)); 106 | testQuatRPY(tf::Quaternion( 0.5, 0.5, 0.5, 0.5)); 107 | testQuatRPY(tf::Quaternion( 0.5, -0.5, 0.5, 0.5)); 108 | testQuatRPY(tf::Quaternion( 0.5, 0.5, -0.5, 0.5)); 109 | testQuatRPY(tf::Quaternion(-0.5, 0.5, 0.5, 0.5)); 110 | } 111 | 112 | 113 | int main(int argc, char **argv){ 114 | testing::InitGoogleTest(&argc, argv); 115 | return RUN_ALL_TESTS(); 116 | } 117 | -------------------------------------------------------------------------------- /src/tf_echo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include "tf/transform_listener.h" 32 | #include "ros/ros.h" 33 | 34 | class echoListener 35 | { 36 | public: 37 | 38 | tf::TransformListener tf; 39 | 40 | //constructor with name 41 | echoListener() 42 | { 43 | 44 | }; 45 | 46 | ~echoListener() 47 | { 48 | 49 | }; 50 | 51 | private: 52 | 53 | }; 54 | 55 | 56 | int main(int argc, char ** argv) 57 | { 58 | //Initialize ROS 59 | ros::init(argc, argv, "tf_echo", ros::init_options::AnonymousName); 60 | 61 | if (argc != 3) 62 | { 63 | printf("Usage: tf_echo source_frame target_frame\n\n"); 64 | printf("This will echo the transform from the coordinate frame of the source_frame\n"); 65 | printf("to the coordinate frame of the target_frame. \n"); 66 | printf("Note: This is the transform to get data from target_frame into the source_frame.\n"); 67 | return -1; 68 | } 69 | 70 | ros::NodeHandle nh; 71 | //Instantiate a local listener 72 | echoListener echoListener; 73 | 74 | 75 | std::string source_frameid = std::string(argv[1]); 76 | std::string target_frameid = std::string(argv[2]); 77 | 78 | // Wait for up to one second for the first transforms to become avaiable. 79 | echoListener.tf.waitForTransform(source_frameid, target_frameid, ros::Time(), ros::Duration(1.0)); 80 | 81 | //Nothing needs to be done except wait for a quit 82 | //The callbacks withing the listener class 83 | //will take care of everything 84 | while(nh.ok()) 85 | { 86 | try 87 | { 88 | tf::StampedTransform echo_transform; 89 | echoListener.tf.lookupTransform(source_frameid, target_frameid, ros::Time(), echo_transform); 90 | std::cout.precision(3); 91 | std::cout.setf(std::ios::fixed,std::ios::floatfield); 92 | std::cout << "At time " << echo_transform.stamp_.toSec() << std::endl; 93 | double yaw, pitch, roll; 94 | echo_transform.getBasis().getRPY(roll, pitch, yaw); 95 | tf::Quaternion q = echo_transform.getRotation(); 96 | tf::Vector3 v = echo_transform.getOrigin(); 97 | std::cout << "- Translation: [" << v.getX() << ", " << v.getY() << ", " << v.getZ() << "]" << std::endl; 98 | std::cout << "- Rotation: in Quaternion [" << q.getX() << ", " << q.getY() << ", " 99 | << q.getZ() << ", " << q.getW() << "]" << std::endl 100 | << " in RPY [" << roll << ", " << pitch << ", " << yaw << "]" << std::endl; 101 | 102 | //print transform 103 | } 104 | catch(tf::TransformException& ex) 105 | { 106 | std::cout << "Failure at "<< ros::Time::now() << std::endl; 107 | std::cout << "Exception thrown:" << ex.what()<< std::endl; 108 | std::cout << "The current list of frames is:" < distutils.version.StrictVersion('2.8'): 107 | subprocess.check_call(["dot", "-Tpdf", "frames.gv", "-o", "frames.pdf"]) 108 | print "frames.pdf generated" 109 | else: 110 | subprocess.check_call(["dot", "-Tps2", "frames.gv", "-o", "frames.ps"]) 111 | print "frames.ps generated" 112 | except subprocess.CalledProcessError: 113 | print >> sys.stderr, "failed to generate frames.pdf" 114 | 115 | 116 | if __name__ == '__main__': 117 | parser = OptionParser(usage="usage: %prog [options]", prog='viewFrames.py') 118 | parser.add_option("--node", metavar="node name", 119 | type="string", help="Node to get frames from", 120 | dest="node") 121 | options, args = parser.parse_args() 122 | 123 | dot_graph = '' 124 | if not options.node: 125 | dot_graph = listen(5.0) 126 | else: 127 | print "Generating graph for node: ", options.node 128 | dot_graph = poll(options.node) 129 | 130 | generate(dot_graph) 131 | -------------------------------------------------------------------------------- /include/tf/time_cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #ifndef TF_TIME_CACHE_H 33 | #define TF_TIME_CACHE_H 34 | 35 | #include 36 | #include 37 | 38 | #include "tf/transform_datatypes.h" 39 | #include "tf/exceptions.h" 40 | 41 | #include "tf/LinearMath/Transform.h" 42 | 43 | #include 44 | 45 | namespace tf 46 | { 47 | enum ExtrapolationMode { ONE_VALUE, INTERPOLATE, EXTRAPOLATE_BACK, EXTRAPOLATE_FORWARD }; 48 | 49 | 50 | typedef uint32_t CompactFrameID; 51 | typedef std::pair P_TimeAndFrameID; 52 | 53 | /** \brief Storage for transforms and their parent */ 54 | class TransformStorage 55 | { 56 | public: 57 | TransformStorage(); 58 | TransformStorage(const StampedTransform& data, CompactFrameID frame_id, CompactFrameID child_frame_id); 59 | 60 | TransformStorage(const TransformStorage& rhs) 61 | { 62 | *this = rhs; 63 | } 64 | 65 | TransformStorage& operator=(const TransformStorage& rhs) 66 | { 67 | #if 01 68 | rotation_ = rhs.rotation_; 69 | translation_ = rhs.translation_; 70 | stamp_ = rhs.stamp_; 71 | frame_id_ = rhs.frame_id_; 72 | child_frame_id_ = rhs.child_frame_id_; 73 | #endif 74 | return *this; 75 | } 76 | 77 | bool operator< (const TransformStorage &b) const 78 | { 79 | return this->stamp_ < b.stamp_; 80 | } 81 | 82 | 83 | tf::Quaternion rotation_; 84 | tf::Vector3 translation_; 85 | ros::Time stamp_; 86 | CompactFrameID frame_id_; 87 | CompactFrameID child_frame_id_; 88 | }; 89 | 90 | 91 | 92 | 93 | /** \brief A class to keep a sorted linked list in time 94 | * This builds and maintains a list of timestamped 95 | * data. And provides lookup functions to get 96 | * data out as a function of time. */ 97 | class TimeCache 98 | { 99 | public: 100 | static const int MIN_INTERPOLATION_DISTANCE = 5; //!< Number of nano-seconds to not interpolate below. 101 | static const unsigned int MAX_LENGTH_LINKED_LIST = 1000000; //!< Maximum length of linked list, to make sure not to be able to use unlimited memory. 102 | static const int64_t DEFAULT_MAX_STORAGE_TIME = 1ULL * 1000000000LL; //!< default value of 10 seconds storage 103 | 104 | TimeCache(ros::Duration max_storage_time = ros::Duration().fromNSec(DEFAULT_MAX_STORAGE_TIME)); 105 | 106 | bool getData(ros::Time time, TransformStorage & data_out, std::string* error_str = 0); 107 | bool insertData(const TransformStorage& new_data); 108 | void clearList(); 109 | CompactFrameID getParent(ros::Time time, std::string* error_str); 110 | P_TimeAndFrameID getLatestTimeAndParent(); 111 | 112 | /// Debugging information methods 113 | unsigned int getListLength(); 114 | ros::Time getLatestTimestamp(); 115 | ros::Time getOldestTimestamp(); 116 | 117 | 118 | private: 119 | typedef std::set L_TransformStorage; 120 | L_TransformStorage storage_; 121 | 122 | ros::Duration max_storage_time_; 123 | 124 | 125 | /// A helper function for getData 126 | //Assumes storage is already locked for it 127 | inline uint8_t findClosest(const TransformStorage*& one, const TransformStorage*& two, ros::Time target_time, std::string* error_str); 128 | 129 | inline void interpolate(const TransformStorage& one, const TransformStorage& two, ros::Time time, TransformStorage& output); 130 | 131 | 132 | void pruneList(); 133 | 134 | 135 | 136 | }; 137 | 138 | 139 | } 140 | #endif // TF_TIME_CACHE_H 141 | -------------------------------------------------------------------------------- /test/python_debug_test.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009, Willow Garage, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of the Willow Garage, Inc. nor the names of its 13 | # contributors may be used to endorse or promote products derived from 14 | # this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | # POSSIBILITY OF SUCH DAMAGE. 27 | 28 | import roslib; roslib.load_manifest("tf") 29 | 30 | import rospy 31 | import tf 32 | import time 33 | import bullet 34 | import math 35 | 36 | try: 37 | 38 | rospy.init_node("test_node") 39 | tfl = tf.TransformListener() 40 | time.sleep(1) 41 | 42 | # View all frames 43 | print "All frames are:\n", tfl.all_frames_as_string() 44 | 45 | # dot based introspection 46 | print "All frame graph is:\n", tfl.all_frames_as_dot() 47 | 48 | 49 | # test transforming pose 50 | po = tf.PoseStamped() 51 | po.frame_id = "base_link" 52 | print "calling transform pose" 53 | po2 = tfl.transform_pose("/map", po) 54 | 55 | print "po2.pose.this", po2.pose.this 56 | print "po.pose.this", po.pose.this 57 | 58 | # test transforming point 59 | po = tf.PointStamped() 60 | po.frame_id = "base_link" 61 | po3 = tfl.transform_point("/map", po) 62 | print "po3" 63 | print po3.this 64 | 65 | # test transforming vector 66 | po = tf.VectorStamped() 67 | po.frame_id = "base_link" 68 | po4 = tfl.transform_vector("/map", po) 69 | print po4.this 70 | 71 | # test transforming quaternion 72 | po = tf.QuaternionStamped() 73 | po.frame_id = "base_link" 74 | po5 = tfl.transform_quaternion("/map", po) 75 | print "po5", po5.this 76 | 77 | tr = tf.TransformStamped() 78 | 79 | lps = tf.PoseStamped() 80 | lps.pose.setIdentity() 81 | print "setting stamp" 82 | mytime = rospy.Time(10,20) 83 | lps.stamp = mytime 84 | print mytime 85 | print "getting stamp" 86 | output = lps.stamp 87 | print output 88 | print lps.pose 89 | print "setting pose.positon to 1,2,3" 90 | lps.pose.setOrigin( bullet.Vector3(1,2,3)) 91 | print lps.pose.getOrigin() 92 | print lps.pose 93 | 94 | transform_stamped = tf.TransformStamped() 95 | print "getting stamp" 96 | print transform_stamped.stamp 97 | # mytime = rospy.Time().now() 98 | mytime = rospy.Time(10,20) 99 | transform_stamped.stamp = mytime 100 | print mytime 101 | print "getting stamp", transform_stamped.stamp 102 | print "transform:", transform_stamped.transform 103 | transform_stamped.transform.setIdentity() 104 | print "after setIdentity()", transform_stamped.transform 105 | # transform_stamped.transform.basis.setEulerZYX(0,0,0) 106 | quat = bullet.Quaternion(math.pi/2,0,0) 107 | print "quaternion ", quat 108 | transform_stamped.transform.setRotation(quat) 109 | print "setting rotation to PI/2",transform_stamped.transform 110 | 111 | 112 | pointstamped = tf.PointStamped() 113 | print "getting stamp" 114 | print pointstamped.stamp 115 | # mytime = rospy.Time().now() 116 | mytime = rospy.Time(10,20) 117 | pointstamped.stamp = mytime 118 | print mytime 119 | print "getting stamp" 120 | output = pointstamped.stamp 121 | print output 122 | print pointstamped.point 123 | print transform_stamped.transform * pointstamped.point 124 | 125 | pose_only = bullet.Transform(transform_stamped.transform) 126 | print "destructing pose_only", pose_only.this 127 | pose_only = [] 128 | 129 | 130 | print "Creating copy" 131 | po2_copy = tf.PoseStamped(po2) 132 | print "po2_copy.pose", po2_copy.pose.this 133 | print "po2.pose", po2.pose.this 134 | 135 | print "Creating copy2" 136 | po2_copy2 = tf.PoseStamped(po2) 137 | print "po2_copy2.pose", po2_copy2.pose.this 138 | 139 | 140 | print "destructing po2 po2.pose is", po2.pose.this 141 | po2 = [] 142 | 143 | 144 | print "destructing po2_copy po2_copy.pose is", po2_copy.pose.this 145 | po2_copy = [] 146 | 147 | 148 | 149 | 150 | 151 | 152 | print "done" 153 | 154 | except ValueError, e: 155 | print "Exception %s Improperly thrown: %s"%(type(e), e) 156 | 157 | 158 | -------------------------------------------------------------------------------- /src/static_transform_publisher.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include "tf/transform_broadcaster.h" 32 | 33 | class TransformSender 34 | { 35 | public: 36 | ros::NodeHandle node_; 37 | //constructor 38 | TransformSender(double x, double y, double z, double yaw, double pitch, double roll, ros::Time time, const std::string& frame_id, const std::string& child_frame_id) 39 | { 40 | tf::Quaternion q; 41 | q.setRPY(roll, pitch,yaw); 42 | transform_ = tf::StampedTransform(tf::Transform(q, tf::Vector3(x,y,z)), time, frame_id, child_frame_id ); 43 | }; 44 | TransformSender(double x, double y, double z, double qx, double qy, double qz, double qw, ros::Time time, const std::string& frame_id, const std::string& child_frame_id) : 45 | transform_(tf::Transform(tf::Quaternion(qx,qy,qz,qw), tf::Vector3(x,y,z)), time, frame_id, child_frame_id){}; 46 | //Clean up ros connections 47 | ~TransformSender() { } 48 | 49 | //A pointer to the rosTFServer class 50 | tf::TransformBroadcaster broadcaster; 51 | 52 | 53 | 54 | // A function to call to send data periodically 55 | void send (ros::Time time) { 56 | transform_.stamp_ = time; 57 | broadcaster.sendTransform(transform_); 58 | }; 59 | 60 | private: 61 | tf::StampedTransform transform_; 62 | 63 | }; 64 | 65 | int main(int argc, char ** argv) 66 | { 67 | //Initialize ROS 68 | ros::init(argc, argv,"static_transform_publisher", ros::init_options::AnonymousName); 69 | 70 | if(argc == 11) 71 | { 72 | ros::Duration sleeper(atof(argv[10])/1000.0); 73 | 74 | if (strcmp(argv[8], argv[9]) == 0) 75 | ROS_FATAL("target_frame and source frame are the same (%s, %s) this cannot work", argv[8], argv[9]); 76 | 77 | TransformSender tf_sender(atof(argv[1]), atof(argv[2]), atof(argv[3]), 78 | atof(argv[4]), atof(argv[5]), atof(argv[6]), atof(argv[7]), 79 | ros::Time() + sleeper, //Future dating to allow slower sending w/o timeout 80 | argv[8], argv[9]); 81 | 82 | 83 | 84 | while(tf_sender.node_.ok()) 85 | { 86 | tf_sender.send(ros::Time::now() + sleeper); 87 | ROS_DEBUG("Sending transform from %s with parent %s\n", argv[8], argv[9]); 88 | sleeper.sleep(); 89 | } 90 | 91 | return 0; 92 | } 93 | else if (argc == 10) 94 | { 95 | ros::Duration sleeper(atof(argv[9])/1000.0); 96 | 97 | if (strcmp(argv[7], argv[8]) == 0) 98 | ROS_FATAL("target_frame and source frame are the same (%s, %s) this cannot work", argv[7], argv[8]); 99 | 100 | TransformSender tf_sender(atof(argv[1]), atof(argv[2]), atof(argv[3]), 101 | atof(argv[4]), atof(argv[5]), atof(argv[6]), 102 | ros::Time() + sleeper, //Future dating to allow slower sending w/o timeout 103 | argv[7], argv[8]); 104 | 105 | 106 | 107 | while(tf_sender.node_.ok()) 108 | { 109 | tf_sender.send(ros::Time::now() + sleeper); 110 | ROS_DEBUG("Sending transform from %s with parent %s\n", argv[7], argv[8]); 111 | sleeper.sleep(); 112 | } 113 | 114 | return 0; 115 | 116 | } 117 | else 118 | { 119 | printf("A command line utility for manually sending a transform.\n"); 120 | printf("It will periodicaly republish the given transform. \n"); 121 | printf("Usage: static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period(milliseconds) \n"); 122 | printf("OR \n"); 123 | printf("Usage: static_transform_publisher x y z qx qy qz qw frame_id child_frame_id period(milliseconds) \n"); 124 | printf("\nThis transform is the transform of the coordinate frame from frame_id into the coordinate frame \n"); 125 | printf("of the child_frame_id. \n"); 126 | ROS_ERROR("static_transform_publisher exited due to not having the right number of arguments"); 127 | return -1; 128 | } 129 | 130 | 131 | }; 132 | 133 | -------------------------------------------------------------------------------- /include/tf/LinearMath/QuadWord.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ 3 | 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it freely, 8 | subject to the following restrictions: 9 | 10 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 11 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 12 | 3. This notice may not be removed or altered from any source distribution. 13 | */ 14 | 15 | 16 | #ifndef TF_QUADWORD_H 17 | #define TF_QUADWORD_H 18 | 19 | #include "Scalar.h" 20 | #include "MinMax.h" 21 | 22 | 23 | #if defined (__CELLOS_LV2) && defined (__SPU__) 24 | #include 25 | #endif 26 | 27 | 28 | namespace tf 29 | { 30 | /**@brief The QuadWord class is base class for Vector3 and Quaternion. 31 | * Some issues under PS3 Linux with IBM 2.1 SDK, gcc compiler prevent from using aligned quadword. 32 | */ 33 | #ifndef USE_LIBSPE2 34 | ATTRIBUTE_ALIGNED16(class) QuadWord 35 | #else 36 | class QuadWord 37 | #endif 38 | { 39 | protected: 40 | 41 | #if defined (__SPU__) && defined (__CELLOS_LV2__) 42 | union { 43 | vec_float4 mVec128; 44 | tfScalar m_floats[4]; 45 | }; 46 | public: 47 | vec_float4 get128() const 48 | { 49 | return mVec128; 50 | } 51 | protected: 52 | #else //__CELLOS_LV2__ __SPU__ 53 | tfScalar m_floats[4]; 54 | #endif //__CELLOS_LV2__ __SPU__ 55 | 56 | public: 57 | 58 | 59 | /**@brief Return the x value */ 60 | TFSIMD_FORCE_INLINE const tfScalar& getX() const { return m_floats[0]; } 61 | /**@brief Return the y value */ 62 | TFSIMD_FORCE_INLINE const tfScalar& getY() const { return m_floats[1]; } 63 | /**@brief Return the z value */ 64 | TFSIMD_FORCE_INLINE const tfScalar& getZ() const { return m_floats[2]; } 65 | /**@brief Set the x value */ 66 | TFSIMD_FORCE_INLINE void setX(tfScalar x) { m_floats[0] = x;}; 67 | /**@brief Set the y value */ 68 | TFSIMD_FORCE_INLINE void setY(tfScalar y) { m_floats[1] = y;}; 69 | /**@brief Set the z value */ 70 | TFSIMD_FORCE_INLINE void setZ(tfScalar z) { m_floats[2] = z;}; 71 | /**@brief Set the w value */ 72 | TFSIMD_FORCE_INLINE void setW(tfScalar w) { m_floats[3] = w;}; 73 | /**@brief Return the x value */ 74 | TFSIMD_FORCE_INLINE const tfScalar& x() const { return m_floats[0]; } 75 | /**@brief Return the y value */ 76 | TFSIMD_FORCE_INLINE const tfScalar& y() const { return m_floats[1]; } 77 | /**@brief Return the z value */ 78 | TFSIMD_FORCE_INLINE const tfScalar& z() const { return m_floats[2]; } 79 | /**@brief Return the w value */ 80 | TFSIMD_FORCE_INLINE const tfScalar& w() const { return m_floats[3]; } 81 | 82 | //TFSIMD_FORCE_INLINE tfScalar& operator[](int i) { return (&m_floats[0])[i]; } 83 | //TFSIMD_FORCE_INLINE const tfScalar& operator[](int i) const { return (&m_floats[0])[i]; } 84 | ///operator tfScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. 85 | TFSIMD_FORCE_INLINE operator tfScalar *() { return &m_floats[0]; } 86 | TFSIMD_FORCE_INLINE operator const tfScalar *() const { return &m_floats[0]; } 87 | 88 | TFSIMD_FORCE_INLINE bool operator==(const QuadWord& other) const 89 | { 90 | return ((m_floats[3]==other.m_floats[3]) && (m_floats[2]==other.m_floats[2]) && (m_floats[1]==other.m_floats[1]) && (m_floats[0]==other.m_floats[0])); 91 | } 92 | 93 | TFSIMD_FORCE_INLINE bool operator!=(const QuadWord& other) const 94 | { 95 | return !(*this == other); 96 | } 97 | 98 | /**@brief Set x,y,z and zero w 99 | * @param x Value of x 100 | * @param y Value of y 101 | * @param z Value of z 102 | */ 103 | TFSIMD_FORCE_INLINE void setValue(const tfScalar& x, const tfScalar& y, const tfScalar& z) 104 | { 105 | m_floats[0]=x; 106 | m_floats[1]=y; 107 | m_floats[2]=z; 108 | m_floats[3] = 0.f; 109 | } 110 | 111 | /* void getValue(tfScalar *m) const 112 | { 113 | m[0] = m_floats[0]; 114 | m[1] = m_floats[1]; 115 | m[2] = m_floats[2]; 116 | } 117 | */ 118 | /**@brief Set the values 119 | * @param x Value of x 120 | * @param y Value of y 121 | * @param z Value of z 122 | * @param w Value of w 123 | */ 124 | TFSIMD_FORCE_INLINE void setValue(const tfScalar& x, const tfScalar& y, const tfScalar& z,const tfScalar& w) 125 | { 126 | m_floats[0]=x; 127 | m_floats[1]=y; 128 | m_floats[2]=z; 129 | m_floats[3]=w; 130 | } 131 | /**@brief No initialization constructor */ 132 | TFSIMD_FORCE_INLINE QuadWord() 133 | // :m_floats[0](tfScalar(0.)),m_floats[1](tfScalar(0.)),m_floats[2](tfScalar(0.)),m_floats[3](tfScalar(0.)) 134 | { 135 | } 136 | 137 | /**@brief Three argument constructor (zeros w) 138 | * @param x Value of x 139 | * @param y Value of y 140 | * @param z Value of z 141 | */ 142 | TFSIMD_FORCE_INLINE QuadWord(const tfScalar& x, const tfScalar& y, const tfScalar& z) 143 | { 144 | m_floats[0] = x, m_floats[1] = y, m_floats[2] = z, m_floats[3] = 0.0f; 145 | } 146 | 147 | /**@brief Initializing constructor 148 | * @param x Value of x 149 | * @param y Value of y 150 | * @param z Value of z 151 | * @param w Value of w 152 | */ 153 | TFSIMD_FORCE_INLINE QuadWord(const tfScalar& x, const tfScalar& y, const tfScalar& z,const tfScalar& w) 154 | { 155 | m_floats[0] = x, m_floats[1] = y, m_floats[2] = z, m_floats[3] = w; 156 | } 157 | 158 | /**@brief Set each element to the max of the current values and the values of another QuadWord 159 | * @param other The other QuadWord to compare with 160 | */ 161 | TFSIMD_FORCE_INLINE void setMax(const QuadWord& other) 162 | { 163 | tfSetMax(m_floats[0], other.m_floats[0]); 164 | tfSetMax(m_floats[1], other.m_floats[1]); 165 | tfSetMax(m_floats[2], other.m_floats[2]); 166 | tfSetMax(m_floats[3], other.m_floats[3]); 167 | } 168 | /**@brief Set each element to the min of the current values and the values of another QuadWord 169 | * @param other The other QuadWord to compare with 170 | */ 171 | TFSIMD_FORCE_INLINE void setMin(const QuadWord& other) 172 | { 173 | tfSetMin(m_floats[0], other.m_floats[0]); 174 | tfSetMin(m_floats[1], other.m_floats[1]); 175 | tfSetMin(m_floats[2], other.m_floats[2]); 176 | tfSetMin(m_floats[3], other.m_floats[3]); 177 | } 178 | 179 | 180 | 181 | }; 182 | 183 | } 184 | 185 | #endif //TFSIMD_QUADWORD_H 186 | -------------------------------------------------------------------------------- /src/tf/tfwtf.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | # 33 | # Revision $Id$ 34 | 35 | import time 36 | 37 | from roswtf.rules import warning_rule, error_rule 38 | import rosgraph 39 | import rospy 40 | 41 | import tf.msg 42 | 43 | 44 | # global list of messages received 45 | _msgs = [] 46 | 47 | ################################################################################ 48 | # RULES 49 | 50 | def rostime_delta(ctx): 51 | deltas = {} 52 | for m, stamp, callerid in _msgs: 53 | for t in m.transforms: 54 | d = t.header.stamp - stamp 55 | secs = d.to_sec() 56 | if abs(secs) > 1.: 57 | if callerid in deltas: 58 | if abs(secs) > abs(deltas[callerid]): 59 | deltas[callerid] = secs 60 | else: 61 | deltas[callerid] = secs 62 | 63 | errors = [] 64 | for k, v in deltas.iteritems(): 65 | errors.append("receiving transform from [%s] that differed from ROS time by %ss"%(k, v)) 66 | return errors 67 | 68 | def reparenting(ctx): 69 | errors = [] 70 | parent_id_map = {} 71 | for m, stamp, callerid in _msgs: 72 | for t in m.transforms: 73 | frame_id = t.child_frame_id 74 | parent_id = t.header.frame_id 75 | if frame_id in parent_id_map and parent_id_map[frame_id] != parent_id: 76 | msg = "reparenting of [%s] to [%s] by [%s]"%(frame_id, parent_id, callerid) 77 | parent_id_map[frame_id] = parent_id 78 | if msg not in errors: 79 | errors.append(msg) 80 | else: 81 | parent_id_map[frame_id] = parent_id 82 | return errors 83 | 84 | def cycle_detection(ctx): 85 | max_depth = 100 86 | errors = [] 87 | parent_id_map = {} 88 | for m, stamp, callerid in _msgs: 89 | for t in m.transforms: 90 | frame_id = t.child_frame_id 91 | parent_id = t.header.frame_id 92 | parent_id_map[frame_id] = parent_id 93 | 94 | for frame in parent_id_map: 95 | frame_list = [] 96 | current_frame = frame 97 | count = 0 98 | while count < max_depth + 2: 99 | count = count + 1 100 | frame_list.append(current_frame) 101 | try: 102 | current_frame = parent_id_map[current_frame] 103 | except KeyError: 104 | break 105 | if current_frame == frame: 106 | errors.append("Frame %s is in a loop. It's loop has elements:\n%s"% (frame, " -> ".join(frame_list))) 107 | break 108 | 109 | 110 | return errors 111 | 112 | def multiple_authority(ctx): 113 | errors = [] 114 | authority_map = {} 115 | for m, stamp, callerid in _msgs: 116 | for t in m.transforms: 117 | frame_id = t.child_frame_id 118 | parent_id = t.header.frame_id 119 | if frame_id in authority_map and authority_map[frame_id] != callerid: 120 | msg = "node [%s] publishing transform [%s] with parent [%s] already published by node [%s]"%(callerid, frame_id, parent_id, authority_map[frame_id]) 121 | authority_map[frame_id] = callerid 122 | if msg not in errors: 123 | errors.append(msg) 124 | else: 125 | authority_map[frame_id] = callerid 126 | return errors 127 | 128 | def no_msgs(ctx): 129 | return not _msgs 130 | 131 | ################################################################################ 132 | # roswtf PLUGIN 133 | 134 | # tf_warnings and tf_errors declare the rules that we actually check 135 | 136 | tf_warnings = [ 137 | (no_msgs, "No tf messages"), 138 | (rostime_delta, "Received out-of-date/future transforms:"), 139 | ] 140 | tf_errors = [ 141 | (reparenting, "TF re-parenting contention:"), 142 | (cycle_detection, "TF cycle detection::"), 143 | (multiple_authority, "TF multiple authority contention:"), 144 | ] 145 | 146 | # rospy subscriber callback for /tf 147 | def _tf_handle(msg): 148 | _msgs.append((msg, rospy.get_rostime(), msg._connection_header['callerid'])) 149 | 150 | # @return bool: True if /tf has a publisher 151 | def is_tf_active(): 152 | master = rosgraph.Master('/tfwtf') 153 | if master is not None: 154 | try: 155 | val = master.getPublishedTopics('/') 156 | if filter(lambda x: x[0] == '/tf', val): 157 | return True 158 | except: 159 | pass 160 | return False 161 | 162 | # roswtf entry point for online checks 163 | def roswtf_plugin_online(ctx): 164 | # don't run plugin if tf isn't active as these checks take awhile 165 | if not is_tf_active(): 166 | return 167 | 168 | print "running tf checks, this will take a second..." 169 | sub1 = rospy.Subscriber('/tf', tf.msg.tfMessage, _tf_handle) 170 | time.sleep(1.0) 171 | sub1.unregister() 172 | print "... tf checks complete" 173 | 174 | for r in tf_warnings: 175 | warning_rule(r, r[0](ctx), ctx) 176 | for r in tf_errors: 177 | error_rule(r, r[0](ctx), ctx) 178 | 179 | 180 | # currently no static checks for tf 181 | #def roswtf_plugin_static(ctx): 182 | # for r in tf_warnings: 183 | # warning_rule(r, r[0](ctx), ctx) 184 | # for r in tf_errors: 185 | # error_rule(r, r[0](ctx), ctx) 186 | 187 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(tf) 3 | 4 | find_package(catkin REQUIRED) 5 | 6 | find_package(Boost REQUIRED thread signals) 7 | find_package(catkin REQUIRED COMPONENTS 8 | angles geometry_msgs message_filters message_generation rosconsole 9 | roscpp rostest rostime sensor_msgs std_msgs tf2_ros 10 | ) 11 | 12 | catkin_python_setup() 13 | 14 | include_directories(SYSTEM ${Boost_INCLUDE_DIR} 15 | ${catkin_INCLUDE_DIRS} 16 | ) 17 | include_directories(include) 18 | link_directories(${catkin_LIBRARY_DIRS}) 19 | 20 | add_message_files(DIRECTORY msg FILES tfMessage.msg) 21 | add_service_files(DIRECTORY srv FILES FrameGraph.srv) 22 | 23 | generate_messages(DEPENDENCIES geometry_msgs sensor_msgs std_msgs) 24 | 25 | catkin_package( 26 | INCLUDE_DIRS include 27 | LIBRARIES ${PROJECT_NAME} 28 | CATKIN_DEPENDS geometry_msgs message_filters message_runtime 29 | roscpp sensor_msgs std_msgs tf2_ros rosconsole 30 | ) 31 | 32 | add_library(${PROJECT_NAME} src/tf.cpp src/transform_listener.cpp src/cache.cpp src/transform_broadcaster.cpp) 33 | target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} ${Boost_LIBRARIES}) 34 | add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_gencpp) 35 | 36 | # Debug 37 | add_executable(tf_empty_listener src/empty_listener.cpp) 38 | target_link_libraries(tf_empty_listener ${PROJECT_NAME}) 39 | 40 | add_executable(tf_echo src/tf_echo.cpp) 41 | target_link_libraries(tf_echo ${PROJECT_NAME}) 42 | 43 | add_executable(tf_change_notifier src/change_notifier.cpp) 44 | target_link_libraries(tf_change_notifier ${PROJECT_NAME}) 45 | 46 | add_executable(tf_monitor src/tf_monitor.cpp) 47 | target_link_libraries(tf_monitor ${PROJECT_NAME}) 48 | 49 | add_executable(static_transform_publisher src/static_transform_publisher.cpp) 50 | target_link_libraries(static_transform_publisher ${PROJECT_NAME}) 51 | 52 | # Tests 53 | if(CATKIN_ENABLE_TESTING) 54 | 55 | catkin_add_gtest(tf_unittest test/tf_unittest.cpp) 56 | target_link_libraries(tf_unittest ${PROJECT_NAME}) 57 | 58 | catkin_add_gtest(tf_quaternion_unittest test/quaternion.cpp) 59 | target_link_libraries(tf_quaternion_unittest ${PROJECT_NAME}) 60 | 61 | catkin_add_gtest(test_transform_datatypes test/test_transform_datatypes.cpp) 62 | target_link_libraries(test_transform_datatypes ${PROJECT_NAME}) 63 | 64 | add_executable(transform_listener_unittest test/transform_listener_unittest.cpp) 65 | target_link_libraries(transform_listener_unittest ${PROJECT_NAME} ${GTEST_LIBRARIES}) 66 | add_rostest(test/transform_listener_unittest.launch) 67 | 68 | # Disabled because of changes in TransformStorage 69 | #catkin_add_gtest_future(tf_unittest_future test/tf_unittest_future.cpp) 70 | #target_link_libraries(tf_unittest_future ${PROJECT_NAME}) 71 | 72 | catkin_add_gtest(test_velocity test/velocity_test.cpp) 73 | target_link_libraries(test_velocity ${PROJECT_NAME}) 74 | 75 | #add_executable(test_transform_twist test/transform_twist_test.cpp) 76 | #target_link_libraries(test_transform_twist ${PROJECT_NAME}) 77 | #catkin_add_gtest_build_flags(test_transform_twist) 78 | #add_rostest(test/transform_twist_test.launch) 79 | 80 | catkin_add_gtest(cache_unittest test/cache_unittest.cpp) 81 | target_link_libraries(cache_unittest ${PROJECT_NAME}) 82 | 83 | 84 | add_executable(test_message_filter EXCLUDE_FROM_ALL test/test_message_filter.cpp) 85 | target_link_libraries(test_message_filter tf ${Boost_LIBRARIES} ${GTEST_LIBRARIES}) 86 | add_rostest(test/test_message_filter.xml) 87 | 88 | 89 | ### Benchmarking 90 | #catkin_add_gtest_future(tf_benchmark test/tf_benchmark.cpp) 91 | #target_link_libraries(tf_benchmark ${PROJECT_NAME}) 92 | 93 | add_executable(testListener test/testListener.cpp) 94 | target_link_libraries(testListener ${PROJECT_NAME} ${GTEST_LIBRARIES}) 95 | add_rostest(test/test_broadcaster.launch) 96 | 97 | add_executable(testBroadcaster test/testBroadcaster.cpp) 98 | target_link_libraries(testBroadcaster ${PROJECT_NAME}) 99 | 100 | endif() 101 | 102 | 103 | #Python setup 104 | set(Python_ADDITIONAL_VERSIONS 2.7) 105 | find_package(PythonLibs REQUIRED) 106 | include_directories(${PYTHON_INCLUDE_PATH}) 107 | 108 | # # If on Darwin, create a symlink _foo.so -> _foo.dylib, because the 109 | # # MacPorts version of Python won't find _foo.dylib for 'import _foo' 110 | # include(CMakeDetermineSystem) 111 | # if(CMAKE_SYSTEM_NAME MATCHES "Darwin") 112 | # add_custom_command(OUTPUT ${LIBRARY_OUTPUT_PATH}/_${PROJECT_NAME}_swig.so 113 | # COMMAND cmake -E create_symlink ${LIBRARY_OUTPUT_PATH}/_${PROJECT_NAME}_swig.dylib ${LIBRARY_OUTPUT_PATH}/_${PROJECT_NAME}_swig.so 114 | # DEPENDS python_${PROJECT_NAME}) 115 | # add_custom_target(symlink_darwin_lib ALL 116 | # DEPENDS ${LIBRARY_OUTPUT_PATH}/_${PROJECT_NAME}_swig.so) 117 | # endif(CMAKE_SYSTEM_NAME MATCHES "Darwin") 118 | 119 | 120 | # Check for SSE 121 | #check_for_sse() 122 | 123 | # Dynamic linking with tf worked OK, except for exception propagation, which failed in the unit test. 124 | # so build with the objects directly instead. 125 | 126 | link_libraries(${PYTHON_LIBRARIES}) 127 | add_library(pytf_py src/pytf.cpp src/tf.cpp src/transform_listener.cpp src/cache.cpp) 128 | add_dependencies(pytf_py ${PROJECT_NAME}_gencpp) 129 | 130 | find_package(PythonLibs REQUIRED) 131 | set_target_properties(pytf_py PROPERTIES OUTPUT_NAME tf PREFIX "_" SUFFIX ".so" 132 | LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${PYTHON_INSTALL_DIR}/tf) 133 | 134 | # target_link_libraries(pytf_py ${PROJECT_NAME}) 135 | # add_compile_flags(pytf_py -g -Wno-missing-field-initializers) 136 | #add_compile_flags(pytf_py ${SSE_FLAGS}) #conditionally adds sse flags if available 137 | # add_pyunit(test/testPython.py) 138 | # DOES PYUNIT WORK IN CATKIN? 139 | target_link_libraries(pytf_py ${Boost_LIBRARIES} ${catkin_LIBRARIES}) 140 | 141 | if(CATKIN_ENABLE_TESTING) 142 | add_executable(tf_speed_test EXCLUDE_FROM_ALL test/speed_test.cpp) 143 | target_link_libraries(tf_speed_test ${PROJECT_NAME}) 144 | endif() 145 | 146 | 147 | install(DIRECTORY include/${PROJECT_NAME}/ 148 | DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}) 149 | 150 | install(TARGETS ${PROJECT_NAME} tf_echo tf_empty_listener tf_change_notifier tf_monitor static_transform_publisher 151 | LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 152 | RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 153 | 154 | install(TARGETS pytf_py 155 | LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_DIR}/tf) 156 | 157 | # Install rosrun-able scripts 158 | install(PROGRAMS 159 | scripts/bullet_migration_sed.py 160 | scripts/tf_remap 161 | scripts/view_frames 162 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) 163 | 164 | # Need to install python msg and srv directories manually because genmsg 165 | # specifically avoids installing python message code if catkin_python_setup() 166 | # has been used. See https://github.com/ros/genmsg/issues/10 167 | install(DIRECTORY ${CATKIN_DEVEL_PREFIX}/${PYTHON_INSTALL_DIR}/tf/msg 168 | DESTINATION ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_DIR}/tf) 169 | install(DIRECTORY ${CATKIN_DEVEL_PREFIX}/${PYTHON_INSTALL_DIR}/tf/srv 170 | DESTINATION ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_DIR}/tf) 171 | 172 | 173 | if(TARGET tests) 174 | add_dependencies(tests testBroadcaster testListener transform_listener_unittest test_message_filter ) 175 | endif() 176 | 177 | 178 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # tf documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Jun 1 14:21:53 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import roslib 15 | roslib.load_manifest('tf') 16 | import sys, os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.append(os.path.abspath('.')) 22 | 23 | # -- General configuration ----------------------------------------------------- 24 | 25 | # Add any Sphinx extension module names here, as strings. They can be extensions 26 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 27 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.pngmath'] 28 | 29 | # Add any paths that contain templates here, relative to this directory. 30 | templates_path = ['_templates'] 31 | 32 | # The suffix of source filenames. 33 | source_suffix = '.rst' 34 | 35 | # The encoding of source files. 36 | #source_encoding = 'utf-8' 37 | 38 | # The master toctree document. 39 | master_doc = 'index' 40 | 41 | # General information about the project. 42 | project = u'tf' 43 | copyright = u'2009, Willow Garage, Inc.' 44 | 45 | # The version info for the project you're documenting, acts as replacement for 46 | # |version| and |release|, also used in various other places throughout the 47 | # built documents. 48 | # 49 | # The short X.Y version. 50 | version = '0.1' 51 | # The full version, including alpha/beta/rc tags. 52 | release = '0.1.0' 53 | 54 | # The language for content autogenerated by Sphinx. Refer to documentation 55 | # for a list of supported languages. 56 | #language = None 57 | 58 | # There are two options for replacing |today|: either, you set today to some 59 | # non-false value, then it is used: 60 | #today = '' 61 | # Else, today_fmt is used as the format for a strftime call. 62 | #today_fmt = '%B %d, %Y' 63 | 64 | # List of documents that shouldn't be included in the build. 65 | #unused_docs = [] 66 | 67 | # List of directories, relative to source directory, that shouldn't be searched 68 | # for source files. 69 | exclude_trees = ['_build'] 70 | 71 | # The reST default role (used for this markup: `text`) to use for all documents. 72 | #default_role = None 73 | 74 | # If true, '()' will be appended to :func: etc. cross-reference text. 75 | #add_function_parentheses = True 76 | 77 | # If true, the current module name will be prepended to all description 78 | # unit titles (such as .. function::). 79 | #add_module_names = True 80 | 81 | # If true, sectionauthor and moduleauthor directives will be shown in the 82 | # output. They are ignored by default. 83 | #show_authors = False 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # A list of ignored prefixes for module index sorting. 89 | #modindex_common_prefix = [] 90 | 91 | 92 | # -- Options for HTML output --------------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. Major themes that come with 95 | # Sphinx are currently 'default' and 'sphinxdoc'. 96 | html_theme = 'default' 97 | 98 | # Theme options are theme-specific and customize the look and feel of a theme 99 | # further. For a list of options available for each theme, see the 100 | # documentation. 101 | #html_theme_options = {} 102 | 103 | # Add any paths that contain custom themes here, relative to this directory. 104 | #html_theme_path = [] 105 | 106 | # The name for this set of Sphinx documents. If None, it defaults to 107 | # " v documentation". 108 | #html_title = None 109 | 110 | # A shorter title for the navigation bar. Default is the same as html_title. 111 | #html_short_title = None 112 | 113 | # The name of an image file (relative to this directory) to place at the top 114 | # of the sidebar. 115 | #html_logo = None 116 | 117 | # The name of an image file (within the static path) to use as favicon of the 118 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 119 | # pixels large. 120 | #html_favicon = None 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named "default.css" will overwrite the builtin "default.css". 125 | html_static_path = ['_static'] 126 | 127 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 128 | # using the given strftime format. 129 | #html_last_updated_fmt = '%b %d, %Y' 130 | 131 | # If true, SmartyPants will be used to convert quotes and dashes to 132 | # typographically correct entities. 133 | #html_use_smartypants = True 134 | 135 | # Custom sidebar templates, maps document names to template names. 136 | #html_sidebars = {} 137 | 138 | # Additional templates that should be rendered to pages, maps page names to 139 | # template names. 140 | #html_additional_pages = {} 141 | 142 | # If false, no module index is generated. 143 | #html_use_modindex = True 144 | 145 | # If false, no index is generated. 146 | #html_use_index = True 147 | 148 | # If true, the index is split into individual pages for each letter. 149 | #html_split_index = False 150 | 151 | # If true, links to the reST sources are added to the pages. 152 | #html_show_sourcelink = True 153 | 154 | # If true, an OpenSearch description file will be output, and all pages will 155 | # contain a tag referring to it. The value of this option must be the 156 | # base URL from which the finished HTML is served. 157 | #html_use_opensearch = '' 158 | 159 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 160 | #html_file_suffix = '' 161 | 162 | # Output file base name for HTML help builder. 163 | htmlhelp_basename = 'tfdoc' 164 | 165 | 166 | # -- Options for LaTeX output -------------------------------------------------- 167 | 168 | # The paper size ('letter' or 'a4'). 169 | #latex_paper_size = 'letter' 170 | 171 | # The font size ('10pt', '11pt' or '12pt'). 172 | #latex_font_size = '10pt' 173 | 174 | # Grouping the document tree into LaTeX files. List of tuples 175 | # (source start file, target name, title, author, documentclass [howto/manual]). 176 | latex_documents = [ 177 | ('index', 'tf.tex', u'stereo\\_utils Documentation', 178 | u'James Bowman', 'manual'), 179 | ] 180 | 181 | # The name of an image file (relative to this directory) to place at the top of 182 | # the title page. 183 | #latex_logo = None 184 | 185 | # For "manual" documents, if this is true, then toplevel headings are parts, 186 | # not chapters. 187 | #latex_use_parts = False 188 | 189 | # Additional stuff for the LaTeX preamble. 190 | #latex_preamble = '' 191 | 192 | # Documents to append as an appendix to all manuals. 193 | #latex_appendices = [] 194 | 195 | # If false, no module index is generated. 196 | #latex_use_modindex = True 197 | 198 | 199 | # Example configuration for intersphinx: refer to the Python standard library. 200 | intersphinx_mapping = { 201 | 'http://docs.python.org/': None, 202 | 'http://opencv.willowgarage.com/documentation/': None, 203 | 'http://opencv.willowgarage.com/documentation/python/': None, 204 | 'http://docs.scipy.org/doc/numpy' : None 205 | } 206 | -------------------------------------------------------------------------------- /test/speed_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | using namespace tf; 38 | 39 | int main(int argc, char** argv) 40 | { 41 | uint32_t num_levels = 10; 42 | if (argc > 1) 43 | { 44 | num_levels = boost::lexical_cast(argv[1]); 45 | } 46 | 47 | tf::Transformer bc; 48 | StampedTransform t; 49 | t.stamp_ = ros::Time(1); 50 | t.frame_id_ = "root"; 51 | t.child_frame_id_ = "0"; 52 | t.setIdentity(); 53 | t.setOrigin(tf::Vector3(1,0,0)); 54 | bc.setTransform(t, "me"); 55 | t.stamp_ = ros::Time(2); 56 | bc.setTransform(t, "me"); 57 | 58 | for (uint32_t i = 1; i < num_levels/2; ++i) 59 | { 60 | for (uint32_t j = 1; j < 3; ++j) 61 | { 62 | std::stringstream parent_ss; 63 | parent_ss << (i - 1); 64 | std::stringstream child_ss; 65 | child_ss << i; 66 | 67 | t.stamp_ = ros::Time(j); 68 | t.frame_id_ = parent_ss.str(); 69 | t.child_frame_id_ = child_ss.str(); 70 | bc.setTransform(t, "me"); 71 | } 72 | } 73 | 74 | t.frame_id_ = "root"; 75 | std::stringstream ss; 76 | ss << num_levels/2; 77 | t.stamp_ = ros::Time(1); 78 | t.child_frame_id_ = ss.str(); 79 | bc.setTransform(t, "me"); 80 | t.stamp_ = ros::Time(2); 81 | bc.setTransform(t, "me"); 82 | 83 | for (uint32_t i = num_levels/2 + 1; i < num_levels; ++i) 84 | { 85 | for (uint32_t j = 1; j < 3; ++j) 86 | { 87 | std::stringstream parent_ss; 88 | parent_ss << (i - 1); 89 | std::stringstream child_ss; 90 | child_ss << i; 91 | 92 | t.stamp_ = ros::Time(j); 93 | t.frame_id_ = parent_ss.str(); 94 | t.child_frame_id_ = child_ss.str(); 95 | bc.setTransform(t, "me"); 96 | } 97 | } 98 | 99 | //ROS_INFO_STREAM(bc.allFramesAsYAML()); 100 | 101 | std::string v_frame0 = boost::lexical_cast(num_levels - 1); 102 | std::string v_frame1 = boost::lexical_cast(num_levels/2 - 1); 103 | ROS_INFO("%s to %s", v_frame0.c_str(), v_frame1.c_str()); 104 | StampedTransform out_t; 105 | 106 | const uint32_t count = 1000000; 107 | ROS_INFO("Doing %d %d-level tests", count, num_levels); 108 | 109 | #if 01 110 | { 111 | ros::WallTime start = ros::WallTime::now(); 112 | for (uint32_t i = 0; i < count; ++i) 113 | { 114 | bc.lookupTransform(v_frame1, v_frame0, ros::Time(0), out_t); 115 | } 116 | ros::WallTime end = ros::WallTime::now(); 117 | ros::WallDuration dur = end - start; 118 | //ROS_INFO_STREAM(out_t); 119 | ROS_INFO("lookupTransform at Time(0) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 120 | } 121 | #endif 122 | 123 | #if 01 124 | { 125 | ros::WallTime start = ros::WallTime::now(); 126 | for (uint32_t i = 0; i < count; ++i) 127 | { 128 | bc.lookupTransform(v_frame1, v_frame0, ros::Time(1), out_t); 129 | } 130 | ros::WallTime end = ros::WallTime::now(); 131 | ros::WallDuration dur = end - start; 132 | //ROS_INFO_STREAM(out_t); 133 | ROS_INFO("lookupTransform at Time(1) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 134 | } 135 | #endif 136 | 137 | #if 01 138 | { 139 | ros::WallTime start = ros::WallTime::now(); 140 | for (uint32_t i = 0; i < count; ++i) 141 | { 142 | bc.lookupTransform(v_frame1, v_frame0, ros::Time(1.5), out_t); 143 | } 144 | ros::WallTime end = ros::WallTime::now(); 145 | ros::WallDuration dur = end - start; 146 | //ROS_INFO_STREAM(out_t); 147 | ROS_INFO("lookupTransform at Time(1.5) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 148 | } 149 | #endif 150 | 151 | #if 01 152 | { 153 | ros::WallTime start = ros::WallTime::now(); 154 | for (uint32_t i = 0; i < count; ++i) 155 | { 156 | bc.lookupTransform(v_frame1, v_frame0, ros::Time(2), out_t); 157 | } 158 | ros::WallTime end = ros::WallTime::now(); 159 | ros::WallDuration dur = end - start; 160 | //ROS_INFO_STREAM(out_t); 161 | ROS_INFO("lookupTransform at Time(2) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 162 | } 163 | #endif 164 | 165 | #if 01 166 | { 167 | ros::WallTime start = ros::WallTime::now(); 168 | for (uint32_t i = 0; i < count; ++i) 169 | { 170 | bc.canTransform(v_frame1, v_frame0, ros::Time(0)); 171 | } 172 | ros::WallTime end = ros::WallTime::now(); 173 | ros::WallDuration dur = end - start; 174 | //ROS_INFO_STREAM(out_t); 175 | ROS_INFO("canTransform at Time(0) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 176 | } 177 | #endif 178 | 179 | #if 01 180 | { 181 | ros::WallTime start = ros::WallTime::now(); 182 | for (uint32_t i = 0; i < count; ++i) 183 | { 184 | bc.canTransform(v_frame1, v_frame0, ros::Time(1)); 185 | } 186 | ros::WallTime end = ros::WallTime::now(); 187 | ros::WallDuration dur = end - start; 188 | //ROS_INFO_STREAM(out_t); 189 | ROS_INFO("canTransform at Time(1) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 190 | } 191 | #endif 192 | 193 | #if 01 194 | { 195 | ros::WallTime start = ros::WallTime::now(); 196 | for (uint32_t i = 0; i < count; ++i) 197 | { 198 | bc.canTransform(v_frame1, v_frame0, ros::Time(1.5)); 199 | } 200 | ros::WallTime end = ros::WallTime::now(); 201 | ros::WallDuration dur = end - start; 202 | //ROS_INFO_STREAM(out_t); 203 | ROS_INFO("canTransform at Time(1.5) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 204 | } 205 | #endif 206 | 207 | #if 01 208 | { 209 | ros::WallTime start = ros::WallTime::now(); 210 | for (uint32_t i = 0; i < count; ++i) 211 | { 212 | bc.canTransform(v_frame1, v_frame0, ros::Time(2)); 213 | } 214 | ros::WallTime end = ros::WallTime::now(); 215 | ros::WallDuration dur = end - start; 216 | //ROS_INFO_STREAM(out_t); 217 | ROS_INFO("canTransform at Time(2) took %.3fs for an average of %.3f us", dur.toSec(), dur.toSec() * 1.0e6 / (double)count); 218 | } 219 | #endif 220 | } 221 | -------------------------------------------------------------------------------- /test/tf_benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "tf/LinearMath/Vector3.h" 35 | 36 | void seed_rand() 37 | { 38 | //Seed random number generator with current microseond count 39 | timeval temp_time_struct; 40 | gettimeofday(&temp_time_struct,NULL); 41 | srand(temp_time_struct.tv_usec); 42 | }; 43 | 44 | void generate_rand_vectors(double scale, uint64_t runs, std::vector& xvalues, std::vector& yvalues, std::vector&zvalues) 45 | { 46 | seed_rand(); 47 | for ( uint64_t i = 0; i < runs ; i++ ) 48 | { 49 | xvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 50 | yvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 51 | zvalues[i] = 1.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 52 | } 53 | } 54 | 55 | 56 | using namespace tf; 57 | 58 | 59 | TEST(tf_benchmark, canTransformCacheLength0) 60 | { 61 | tf::Transformer mTR(true); 62 | 63 | uint64_t runs = 100000; 64 | ros::WallTime start_time = ros::WallTime::now(); 65 | 66 | 67 | start_time = ros::WallTime::now(); 68 | for (uint64_t i = 0 ; i < runs; i++) 69 | { 70 | EXPECT_FALSE(mTR.canTransform("target","source_frame", ros::Time::now())); 71 | } 72 | ros::WallDuration run_duration = ros::WallTime::now() - start_time; 73 | double frequency = (double)runs / run_duration.toSec() ; 74 | printf("can frequency %.2f KHz\n", frequency / 1000.0); 75 | EXPECT_GT( frequency, 10000.0); 76 | } 77 | 78 | 79 | TEST(tf_benchmark, canTransformCacheLength10000) 80 | { 81 | tf::Transformer mTR(true); 82 | 83 | unsigned int cache_length = 10000; 84 | for (unsigned int i = 0; i < cache_length; i++) 85 | { 86 | StampedTransform tranStamped(tf::Transform(tf::Quaternion(0,0,0,1), tf::Vector3(0,0,0)), ros::Time().fromNSec(10.0+i), "my_parent", "child"); 87 | mTR.setTransform(tranStamped); 88 | } 89 | 90 | uint64_t runs = 100000; 91 | ros::WallTime start_time = ros::WallTime::now(); 92 | 93 | 94 | //Worst case 95 | start_time = ros::WallTime::now(); 96 | for (uint64_t i = 0 ; i < runs; i++) 97 | { 98 | EXPECT_TRUE(mTR.canTransform("child","my_parent", ros::Time().fromNSec(10))); 99 | } 100 | ros::WallDuration run_duration = ros::WallTime::now() - start_time; 101 | double frequency = (double)runs / run_duration.toSec() ; 102 | printf("Worst Case Frequency %.2f KHz\n", frequency / 1000.0); 103 | EXPECT_GT( frequency, 10000.0); 104 | 105 | //Worst case 106 | start_time = ros::WallTime::now(); 107 | for (uint64_t i = 0 ; i < runs; i++) 108 | { 109 | EXPECT_TRUE(mTR.canTransform("child","my_parent", ros::Time().fromNSec(10+cache_length/2))); 110 | } 111 | run_duration = ros::WallTime::now() - start_time; 112 | frequency = (double)runs / run_duration.toSec() ; 113 | printf("Intermediate Case Frequency %.2f KHz\n", frequency / 1000.0); 114 | EXPECT_GT( frequency, 10000.0); 115 | 116 | //Best case 117 | start_time = ros::WallTime::now(); 118 | for (uint64_t i = 0 ; i < runs; i++) 119 | { 120 | EXPECT_TRUE(mTR.canTransform("child","my_parent", ros::Time().fromNSec(10+cache_length -1))); 121 | } 122 | run_duration = ros::WallTime::now() - start_time; 123 | frequency = (double)runs / run_duration.toSec() ; 124 | printf("Best Case Frequency %.2f KHz\n", frequency / 1000.0); 125 | EXPECT_GT( frequency, 10000.0); 126 | } 127 | 128 | TEST(tf_benchmark, lookupTransformCacheLength10000) 129 | { 130 | tf::Transformer mTR(true); 131 | 132 | unsigned int cache_length = 10000; 133 | for (unsigned int i = 0; i < cache_length; i++) 134 | { 135 | StampedTransform tranStamped(tf::Transform(tf::Quaternion(0,0,0,1), tf::Vector3(0,0,0)), ros::Time().fromNSec(10.0+i), "my_parent", "child"); 136 | mTR.setTransform(tranStamped); 137 | } 138 | 139 | uint64_t runs = 100000; 140 | ros::WallTime start_time = ros::WallTime::now(); 141 | 142 | StampedTransform rv; 143 | //Worst case 144 | start_time = ros::WallTime::now(); 145 | for (uint64_t i = 0 ; i < runs; i++) 146 | { 147 | mTR.lookupTransform("child","my_parent", ros::Time().fromNSec(10), rv); 148 | } 149 | ros::WallDuration run_duration = ros::WallTime::now() - start_time; 150 | double frequency = (double)runs / run_duration.toSec() ; 151 | printf("Worst Case Lookup Frequency %.2f KHz\n", frequency / 1000.0); 152 | EXPECT_GT( frequency, 10000.0); 153 | 154 | //Worst case 155 | start_time = ros::WallTime::now(); 156 | for (uint64_t i = 0 ; i < runs; i++) 157 | { 158 | mTR.lookupTransform("child","my_parent", ros::Time().fromNSec(10+cache_length/2), rv); 159 | } 160 | run_duration = ros::WallTime::now() - start_time; 161 | frequency = (double)runs / run_duration.toSec() ; 162 | printf("Intermediate Case Lookup Frequency %.2f KHz\n", frequency / 1000.0); 163 | EXPECT_GT( frequency, 10000.0); 164 | 165 | //Best case 166 | start_time = ros::WallTime::now(); 167 | for (uint64_t i = 0 ; i < runs; i++) 168 | { 169 | mTR.lookupTransform("child","my_parent", ros::Time().fromNSec(10+cache_length -1), rv); 170 | } 171 | run_duration = ros::WallTime::now() - start_time; 172 | frequency = (double)runs / run_duration.toSec() ; 173 | printf("Best Case Lookup Frequency %.2f KHz\n", frequency / 1000.0); 174 | EXPECT_GT( frequency, 10000.0); 175 | } 176 | 177 | TEST(tf_benchmark, benchmarkExhaustiveSearch) 178 | { 179 | uint64_t runs = 40000; 180 | double epsilon = 1e-6; 181 | seed_rand(); 182 | 183 | tf::Transformer mTR(true); 184 | std::vector xvalues(runs), yvalues(runs), zvalues(runs); 185 | for ( uint64_t i = 0; i < runs ; i++ ) 186 | { 187 | xvalues[i] = 10.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 188 | yvalues[i] = 10.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 189 | zvalues[i] = 10.0 * ((double) rand() - (double)RAND_MAX /2.0) /(double)RAND_MAX; 190 | 191 | StampedTransform tranStamped(tf::Transform(tf::Quaternion(0,0,0,1), tf::Vector3(xvalues[i],yvalues[i],zvalues[i])), ros::Time().fromNSec(10.0 + i), "my_parent", "child"); 192 | mTR.setTransform(tranStamped); 193 | } 194 | 195 | ros::WallTime start_time = ros::WallTime::now(); 196 | for ( uint64_t i = 0; i < runs ; i++ ) 197 | 198 | { 199 | Stamped inpose (tf::Transform(tf::Quaternion(0,0,0,1), tf::Vector3(0,0,0)), ros::Time().fromNSec(10.0 + i), "child"); 200 | 201 | try{ 202 | Stamped outpose; 203 | outpose.setIdentity(); //to make sure things are getting mutated 204 | mTR.transformPose("my_parent",inpose, outpose); 205 | EXPECT_NEAR(outpose.getOrigin().x(), xvalues[i], epsilon); 206 | EXPECT_NEAR(outpose.getOrigin().y(), yvalues[i], epsilon); 207 | EXPECT_NEAR(outpose.getOrigin().z(), zvalues[i], epsilon); 208 | } 209 | catch (tf::TransformException & ex) 210 | { 211 | std::cout << "TransformExcepion got through!!!!! " << ex.what() << std::endl; 212 | bool exception_improperly_thrown = true; 213 | EXPECT_FALSE(exception_improperly_thrown); 214 | } 215 | } 216 | 217 | Stamped inpose (tf::Transform(tf::Quaternion(0,0,0,1), tf::Vector3(0,0,0)), ros::Time().fromNSec(runs), "child"); 218 | Stamped outpose; 219 | outpose.setIdentity(); //to make sure things are getting mutated 220 | mTR.transformPose("child",inpose, outpose); 221 | EXPECT_NEAR(outpose.getOrigin().x(), 0, epsilon); 222 | EXPECT_NEAR(outpose.getOrigin().y(), 0, epsilon); 223 | EXPECT_NEAR(outpose.getOrigin().z(), 0, epsilon); 224 | 225 | ros::WallDuration run_duration = ros::WallTime::now() - start_time; 226 | double frequency = (double)runs / run_duration.toSec() ; 227 | printf("exhaustive search frequency %.2f KHz\n", frequency / 1000.0); 228 | EXPECT_GT( frequency, 500.0); 229 | 230 | } 231 | 232 | int main(int argc, char **argv){ 233 | ros::Time::init(); 234 | testing::InitGoogleTest(&argc, argv); 235 | return RUN_ALL_TESTS(); 236 | } 237 | -------------------------------------------------------------------------------- /include/tf/transform_listener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #ifndef TF_TRANSFORMLISTENER_H 33 | #define TF_TRANSFORMLISTENER_H 34 | 35 | #include "sensor_msgs/PointCloud.h" 36 | #include "std_msgs/Empty.h" 37 | #include "tf/tfMessage.h" 38 | #include "tf/tf.h" 39 | #include "ros/ros.h" 40 | #include "ros/callback_queue.h" 41 | 42 | #include "tf/FrameGraph.h" //frame graph service 43 | #include "boost/thread.hpp" 44 | 45 | #include 46 | 47 | 48 | namespace tf{ 49 | 50 | /** \brief Get the tf_prefix from the parameter server 51 | * \param nh The node handle to use to lookup the parameter. 52 | * \return The tf_prefix value for this NodeHandle 53 | */ 54 | inline std::string getPrefixParam(ros::NodeHandle & nh) { 55 | std::string param; 56 | if (!nh.searchParam("tf_prefix", param)) 57 | return ""; 58 | 59 | std::string return_val; 60 | nh.getParam(param, return_val); 61 | return return_val; 62 | } 63 | 64 | /** \brief resolve names 65 | * \deprecated Use TransformListener::remap instead */ 66 | std::string remap(const std::string& frame_id) __attribute__((deprecated)); 67 | 68 | 69 | class TransformerHelper: public Transformer { 70 | 71 | public: 72 | TransformerHelper(ros::Duration max_cache_time = ros::Duration(DEFAULT_CACHE_TIME)); 73 | 74 | virtual ~TransformerHelper(); 75 | 76 | /* Methods from transformer unhiding them here */ 77 | using Transformer::transformQuaternion; 78 | using Transformer::transformVector; 79 | using Transformer::transformPoint; 80 | using Transformer::transformPose; 81 | 82 | 83 | /** \brief Transform a Stamped Quaternion Message into the target frame 84 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 85 | void transformQuaternion(const std::string& target_frame, const geometry_msgs::QuaternionStamped& stamped_in, geometry_msgs::QuaternionStamped& stamped_out) const; 86 | /** \brief Transform a Stamped Vector Message into the target frame 87 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 88 | void transformVector(const std::string& target_frame, const geometry_msgs::Vector3Stamped& stamped_in, geometry_msgs::Vector3Stamped& stamped_out) const; 89 | /** \brief Transform a Stamped Point Message into the target frame 90 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 91 | void transformPoint(const std::string& target_frame, const geometry_msgs::PointStamped& stamped_in, geometry_msgs::PointStamped& stamped_out) const; 92 | /** \brief Transform a Stamped Pose Message into the target frame 93 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 94 | void transformPose(const std::string& target_frame, const geometry_msgs::PoseStamped& stamped_in, geometry_msgs::PoseStamped& stamped_out) const; 95 | 96 | /** \brief Transform a Stamped Twist Message into the target frame 97 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 98 | // http://www.ros.org/wiki/tf/Reviews/2010-03-12_API_Review 99 | // void transformTwist(const std::string& target_frame, const geometry_msgs::TwistStamped& stamped_in, geometry_msgs::TwistStamped& stamped_out) const; 100 | 101 | /** \brief Transform a Stamped Quaternion Message into the target frame 102 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 103 | void transformQuaternion(const std::string& target_frame, const ros::Time& target_time, 104 | const geometry_msgs::QuaternionStamped& qin, 105 | const std::string& fixed_frame, geometry_msgs::QuaternionStamped& qout) const; 106 | /** \brief Transform a Stamped Vector Message into the target frame and time 107 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 108 | void transformVector(const std::string& target_frame, const ros::Time& target_time, 109 | const geometry_msgs::Vector3Stamped& vin, 110 | const std::string& fixed_frame, geometry_msgs::Vector3Stamped& vout) const; 111 | /** \brief Transform a Stamped Point Message into the target frame and time 112 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 113 | void transformPoint(const std::string& target_frame, const ros::Time& target_time, 114 | const geometry_msgs::PointStamped& pin, 115 | const std::string& fixed_frame, geometry_msgs::PointStamped& pout) const; 116 | /** \brief Transform a Stamped Pose Message into the target frame and time 117 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 118 | void transformPose(const std::string& target_frame, const ros::Time& target_time, 119 | const geometry_msgs::PoseStamped& pin, 120 | const std::string& fixed_frame, geometry_msgs::PoseStamped& pout) const; 121 | 122 | 123 | /** \brief Transform a sensor_msgs::PointCloud natively 124 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 125 | void transformPointCloud(const std::string& target_frame, const sensor_msgs::PointCloud& pcin, sensor_msgs::PointCloud& pcout) const; 126 | 127 | /** @brief Transform a sensor_msgs::PointCloud in space and time 128 | * This can throw all that lookupTransform can throw as well as tf::InvalidTransform */ 129 | void transformPointCloud(const std::string& target_frame, const ros::Time& target_time, 130 | const sensor_msgs::PointCloud& pcin, 131 | const std::string& fixed_frame, sensor_msgs::PointCloud& pcout) const; 132 | 133 | 134 | 135 | ///\todo move to high precision laser projector class void projectAndTransformLaserScan(const sensor_msgs::LaserScan& scan_in, sensor_msgs::PointCloud& pcout); 136 | 137 | bool getFrames(tf::FrameGraph::Request& req, tf::FrameGraph::Response& res) 138 | { 139 | res.dot_graph = allFramesAsDot(); 140 | return true; 141 | } 142 | 143 | /* \brief Resolve frame_name into a frame_id using tf_prefix parameter */ 144 | std::string resolve(const std::string& frame_name) 145 | { 146 | ros::NodeHandle n("~"); 147 | std::string prefix = tf::getPrefixParam(n); 148 | return tf::resolve(prefix, frame_name); 149 | } 150 | 151 | private: 152 | /** @brief a helper function to be used for both transfrom pointCloud methods */ 153 | void transformPointCloud(const std::string & target_frame, const Transform& transform, const ros::Time& target_time, const sensor_msgs::PointCloud& pcin, sensor_msgs::PointCloud& pcout) const; 154 | }; 155 | 156 | 157 | /** \brief This class inherits from Transformer and automatically subscribes to ROS transform messages */ 158 | class TransformListener : public TransformerHelper { //subscribes to message and automatically stores incoming data 159 | 160 | public: 161 | /**@brief Constructor for transform listener 162 | * \param max_cache_time How long to store transform information */ 163 | TransformListener(ros::Duration max_cache_time = ros::Duration(DEFAULT_CACHE_TIME), bool spin_thread = true); 164 | 165 | /** 166 | * \brief Alternate constructor for transform listener 167 | * \param nh The NodeHandle to use for any ROS interaction 168 | * \param max_cache_time How long to store transform information 169 | */ 170 | TransformListener(const ros::NodeHandle& nh, 171 | ros::Duration max_cache_time = ros::Duration(DEFAULT_CACHE_TIME), bool spin_thread = true); 172 | 173 | ~TransformListener(); 174 | 175 | protected: 176 | bool ok() const; 177 | 178 | private: 179 | 180 | // Must be above the listener 181 | ros::NodeHandle node_; 182 | 183 | /// replacing implementation with tf2_ros' 184 | tf2_ros::TransformListener tf2_listener_; 185 | }; 186 | } 187 | 188 | #endif //TF_TRANSFORMLISTENER_H 189 | -------------------------------------------------------------------------------- /src/change_notifier.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #include "ros/ros.h" 33 | #include "tf/transform_listener.h" 34 | #include "XmlRpcValue.h" 35 | 36 | class FramePair 37 | { 38 | public: 39 | FramePair(const std::string& source_frame, const std::string& target_frame, double translational_update_distance, double angular_update_distance) : 40 | source_frame_(source_frame), 41 | target_frame_(target_frame), 42 | translational_update_distance_(translational_update_distance), 43 | angular_update_distance_(angular_update_distance) 44 | { 45 | pose_in_ = tf::Stamped(tf::Pose(tf::createIdentityQuaternion(), tf::Vector3(0, 0, 0)), ros::Time(), source_frame_); 46 | } 47 | 48 | public: 49 | std::string source_frame_; 50 | std::string target_frame_; 51 | 52 | tf::Stamped pose_in_; 53 | tf::Stamped pose_out_; 54 | tf::Stamped last_sent_pose_; 55 | 56 | double translational_update_distance_; 57 | double angular_update_distance_; 58 | }; 59 | 60 | bool getFramePairs(const ros::NodeHandle& local_node, std::vector& frame_pairs, double default_translational_update_distance, double default_angular_update_distance) 61 | { 62 | XmlRpc::XmlRpcValue frame_pairs_param; 63 | if (!local_node.getParam("frame_pairs", frame_pairs_param)) 64 | { 65 | // No frame_pairs parameter provided. Default to base_link->map. 66 | frame_pairs.push_back(FramePair("base_link", "map", default_translational_update_distance, default_angular_update_distance)); 67 | return true; 68 | } 69 | 70 | if (frame_pairs_param.getType() != XmlRpc::XmlRpcValue::TypeArray) 71 | { 72 | ROS_ERROR("Expecting a list for frame_pairs parameter"); 73 | return false; 74 | } 75 | for (int i = 0; i < frame_pairs_param.size(); i++) 76 | { 77 | XmlRpc::XmlRpcValue frame_pair_param = frame_pairs_param[i]; 78 | if (frame_pair_param.getType() != XmlRpc::XmlRpcValue::TypeStruct) 79 | { 80 | ROS_ERROR("frame_pairs must be specified as maps, but they are XmlRpcType: %d", frame_pair_param.getType()); 81 | return false; 82 | } 83 | 84 | // Get the source_frame 85 | if (!frame_pair_param.hasMember("source_frame")) 86 | { 87 | ROS_ERROR("frame_pair does not specified source_frame"); 88 | return false; 89 | } 90 | XmlRpc::XmlRpcValue source_frame_param = frame_pair_param["source_frame"]; 91 | if (source_frame_param.getType() != XmlRpc::XmlRpcValue::TypeString) 92 | { 93 | ROS_ERROR("source_frame must be a string, but it is XmlRpcType: %d", source_frame_param.getType()); 94 | return false; 95 | } 96 | std::string source_frame = source_frame_param; 97 | 98 | // Get the target_frame 99 | if (!frame_pair_param.hasMember("target_frame")) 100 | { 101 | ROS_ERROR("frame_pair does not specified target_frame"); 102 | return false; 103 | } 104 | XmlRpc::XmlRpcValue target_frame_param = frame_pair_param["target_frame"]; 105 | if (target_frame_param.getType() != XmlRpc::XmlRpcValue::TypeString) 106 | { 107 | ROS_ERROR("target_frame must be a string, but it is XmlRpcType: %d", target_frame_param.getType()); 108 | return false; 109 | } 110 | std::string target_frame = target_frame_param; 111 | 112 | // Get the (optional) translational_update_distance 113 | double translational_update_distance = default_translational_update_distance; 114 | if (frame_pair_param.hasMember("translational_update_distance")) 115 | { 116 | XmlRpc::XmlRpcValue translational_update_distance_param = frame_pair_param["translational_update_distance"]; 117 | if (translational_update_distance_param.getType() != XmlRpc::XmlRpcValue::TypeDouble && 118 | translational_update_distance_param.getType() != XmlRpc::XmlRpcValue::TypeInt) 119 | { 120 | ROS_ERROR("translational_update_distance must be either an integer or a double, but it is XmlRpcType: %d", translational_update_distance_param.getType()); 121 | return false; 122 | } 123 | translational_update_distance = translational_update_distance_param; 124 | } 125 | 126 | // Get the (optional) angular_update_distance 127 | double angular_update_distance = default_angular_update_distance; 128 | if (frame_pair_param.hasMember("angular_update_distance")) 129 | { 130 | XmlRpc::XmlRpcValue angular_update_distance_param = frame_pair_param["angular_update_distance"]; 131 | if (angular_update_distance_param.getType() != XmlRpc::XmlRpcValue::TypeDouble && 132 | angular_update_distance_param.getType() != XmlRpc::XmlRpcValue::TypeInt) 133 | { 134 | ROS_ERROR("angular_update_distance must be either an integer or a double, but it is XmlRpcType: %d", angular_update_distance_param.getType()); 135 | return false; 136 | } 137 | angular_update_distance = angular_update_distance_param; 138 | } 139 | 140 | ROS_INFO("Notifying change on %s -> %s (translational update distance: %.4f, angular update distance: %.4f)", source_frame.c_str(), target_frame.c_str(), translational_update_distance, angular_update_distance); 141 | 142 | frame_pairs.push_back(FramePair(source_frame, target_frame, translational_update_distance, angular_update_distance)); 143 | } 144 | 145 | return true; 146 | } 147 | 148 | /** This is a program to provide notifications of changes of state within tf 149 | * It was written for providing an easy way to on demand update a web graphic of 150 | * where the robot is located. It's not designed or recommended for use in live 151 | * operation for feedback. */ 152 | int main(int argc, char** argv) 153 | { 154 | ros::init(argc, argv, "change_notifier", ros::init_options::AnonymousName); 155 | ros::NodeHandle node; 156 | ros::NodeHandle local_node("~"); 157 | 158 | double polling_frequency, translational_update_distance, angular_update_distance; 159 | local_node.param(std::string("polling_frequency"), polling_frequency, 10.0); 160 | local_node.param(std::string("translational_update_distance"), translational_update_distance, 0.10); 161 | local_node.param(std::string("angular_update_distance"), angular_update_distance, 0.10); 162 | 163 | std::vector frame_pairs; 164 | if (!getFramePairs(local_node, frame_pairs, translational_update_distance, angular_update_distance)) 165 | return 1; 166 | 167 | tf::TransformListener tfl(node); 168 | 169 | // Advertise the service 170 | ros::Publisher pub = node.advertise("tf_changes", 1, true); 171 | 172 | while (node.ok()) 173 | { 174 | try 175 | { 176 | tf::tfMessage msg; 177 | 178 | for (std::vector::iterator i = frame_pairs.begin(); i != frame_pairs.end(); i++) 179 | { 180 | FramePair& fp = *i; 181 | 182 | tfl.transformPose(fp.target_frame_, fp.pose_in_, fp.pose_out_); 183 | 184 | const tf::Vector3& origin = fp.pose_out_.getOrigin(); 185 | const tf::Quaternion& rotation = fp.pose_out_.getRotation(); 186 | 187 | if (origin.distance(fp.last_sent_pose_.getOrigin()) > fp.translational_update_distance_ || 188 | rotation.angle(fp.last_sent_pose_.getRotation()) > fp.angular_update_distance_) 189 | { 190 | fp.last_sent_pose_ = fp.pose_out_; 191 | 192 | tf::StampedTransform stampedTf(tf::Transform(rotation, origin), fp.pose_out_.stamp_, "/" + fp.target_frame_, "/" + fp.source_frame_); 193 | geometry_msgs::TransformStamped msgtf; 194 | transformStampedTFToMsg(stampedTf, msgtf); 195 | msg.transforms.push_back(msgtf); 196 | } 197 | } 198 | 199 | if (msg.transforms.size() > 0) 200 | pub.publish(msg); 201 | } 202 | catch (tf::TransformException& ex) 203 | { 204 | ROS_DEBUG("Exception: %s\n", ex.what()); 205 | } 206 | 207 | // Sleep until next polling 208 | if (polling_frequency > 0) 209 | ros::Duration().fromSec(1.0 / polling_frequency).sleep(); 210 | } 211 | 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /src/cache.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #include "tf/time_cache.h" 33 | #include "tf/exceptions.h" 34 | 35 | #include "tf/LinearMath/Transform.h" 36 | #include 37 | 38 | #include "ros/assert.h" 39 | 40 | using namespace tf; 41 | 42 | TransformStorage::TransformStorage() 43 | { 44 | } 45 | 46 | TransformStorage::TransformStorage(const StampedTransform& data, CompactFrameID frame_id, 47 | CompactFrameID child_frame_id) 48 | : rotation_(data.getRotation()) 49 | , translation_(data.getOrigin()) 50 | , stamp_(data.stamp_) 51 | , frame_id_(frame_id) 52 | , child_frame_id_(child_frame_id) 53 | { } 54 | 55 | TimeCache::TimeCache(ros::Duration max_storage_time) 56 | : max_storage_time_(max_storage_time) 57 | {} 58 | 59 | // hoisting these into separate functions causes an ~8% speedup. Removing calling them altogether adds another ~10% 60 | void createEmptyException(std::string *error_str) 61 | { 62 | if (error_str) 63 | { 64 | *error_str = "Unable to lookup transform, cache is empty"; 65 | } 66 | } 67 | 68 | void createExtrapolationException1(ros::Time t0, ros::Time t1, std::string* error_str) 69 | { 70 | if (error_str) 71 | { 72 | std::stringstream ss; 73 | ss << "Lookup would require extrapolation at time " << t0 << ", but only time " << t1 << " is in the buffer"; 74 | *error_str = ss.str(); 75 | } 76 | } 77 | 78 | void createExtrapolationException2(ros::Time t0, ros::Time t1, std::string* error_str) 79 | { 80 | if (error_str) 81 | { 82 | std::stringstream ss; 83 | ss << "Lookup would require extrapolation into the future. Requested time " << t0 << " but the latest data is at time " << t1; 84 | *error_str = ss.str(); 85 | } 86 | } 87 | 88 | void createExtrapolationException3(ros::Time t0, ros::Time t1, std::string* error_str) 89 | { 90 | if (error_str) 91 | { 92 | std::stringstream ss; 93 | ss << "Lookup would require extrapolation into the past. Requested time " << t0 << " but the earliest data is at time " << t1; 94 | *error_str = ss.str(); 95 | } 96 | } 97 | 98 | uint8_t TimeCache::findClosest(const TransformStorage*& one, const TransformStorage*& two, ros::Time target_time, std::string* error_str) 99 | { 100 | //No values stored 101 | if (storage_.empty()) 102 | { 103 | createEmptyException(error_str); 104 | return 0; 105 | } 106 | 107 | //If time == 0 return the latest 108 | if (target_time.isZero()) 109 | { 110 | one = &(*storage_.rbegin()); 111 | return 1; 112 | } 113 | 114 | // One value stored 115 | if (++storage_.begin() == storage_.end()) 116 | { 117 | const TransformStorage& ts = *storage_.begin(); 118 | if (ts.stamp_ == target_time) 119 | { 120 | one = &ts; 121 | return 1; 122 | } 123 | else 124 | { 125 | createExtrapolationException1(target_time, ts.stamp_, error_str); 126 | return 0; 127 | } 128 | } 129 | 130 | ros::Time latest_time = (*storage_.rbegin()).stamp_; 131 | ros::Time earliest_time = (*(storage_.begin())).stamp_; 132 | 133 | if (target_time == latest_time) 134 | { 135 | one = &(*storage_.rbegin()); 136 | return 1; 137 | } 138 | else if (target_time == earliest_time) 139 | { 140 | one = &(*storage_.begin()); 141 | return 1; 142 | } 143 | // Catch cases that would require extrapolation 144 | else if (target_time > latest_time) 145 | { 146 | createExtrapolationException2(target_time, latest_time, error_str); 147 | return 0; 148 | } 149 | else if (target_time < earliest_time) 150 | { 151 | createExtrapolationException3(target_time, earliest_time, error_str); 152 | return 0; 153 | } 154 | 155 | //Create a temporary object to compare to when searching the lower bound via std::set 156 | TransformStorage tmp; 157 | tmp.stamp_ = target_time; 158 | 159 | //Find the first value equal or higher than the target value 160 | L_TransformStorage::iterator storage_it = storage_.upper_bound(tmp); 161 | 162 | //Finally the case were somewhere in the middle Guarenteed no extrapolation :-) 163 | two = &*(storage_it); //Newer 164 | one = &*(--storage_it); //Older 165 | 166 | return 2; 167 | 168 | } 169 | 170 | void TimeCache::interpolate(const TransformStorage& one, const TransformStorage& two, ros::Time time, TransformStorage& output) 171 | { 172 | // Check for zero distance case 173 | if( two.stamp_ == one.stamp_ ) 174 | { 175 | output = two; 176 | return; 177 | } 178 | //Calculate the ratio 179 | tfScalar ratio = (time.toSec() - one.stamp_.toSec()) / (two.stamp_.toSec() - one.stamp_.toSec()); 180 | 181 | //Interpolate translation 182 | output.translation_.setInterpolate3(one.translation_, two.translation_, ratio); 183 | 184 | //Interpolate rotation 185 | output.rotation_ = slerp( one.rotation_, two.rotation_, ratio); 186 | 187 | output.stamp_ = one.stamp_; 188 | output.frame_id_ = one.frame_id_; 189 | output.child_frame_id_ = one.child_frame_id_; 190 | } 191 | 192 | bool TimeCache::getData(ros::Time time, TransformStorage & data_out, std::string* error_str) //returns false if data not available 193 | { 194 | const TransformStorage* p_temp_1 = NULL; 195 | const TransformStorage* p_temp_2 = NULL; 196 | 197 | int num_nodes = findClosest(p_temp_1, p_temp_2, time, error_str); 198 | if (num_nodes == 0) 199 | { 200 | return false; 201 | } 202 | else if (num_nodes == 1) 203 | { 204 | data_out = *p_temp_1; 205 | } 206 | else if (num_nodes == 2) 207 | { 208 | if( p_temp_1->frame_id_ == p_temp_2->frame_id_) 209 | { 210 | interpolate(*p_temp_1, *p_temp_2, time, data_out); 211 | } 212 | else 213 | { 214 | data_out = *p_temp_1; 215 | } 216 | } 217 | else 218 | { 219 | ROS_BREAK(); 220 | } 221 | 222 | return true; 223 | } 224 | 225 | CompactFrameID TimeCache::getParent(ros::Time time, std::string* error_str) 226 | { 227 | const TransformStorage* p_temp_1 = NULL; 228 | const TransformStorage* p_temp_2 = NULL; 229 | 230 | int num_nodes = findClosest(p_temp_1, p_temp_2, time, error_str); 231 | if (num_nodes == 0) 232 | { 233 | return 0; 234 | } 235 | 236 | return p_temp_1->frame_id_; 237 | } 238 | 239 | bool TimeCache::insertData(const TransformStorage& new_data) 240 | { 241 | 242 | if (storage_.begin() != storage_.end()) 243 | { 244 | // trying to add data that dates back longer than we want to keep history 245 | if (storage_.rbegin()->stamp_ > new_data.stamp_ + max_storage_time_) 246 | return false; 247 | 248 | // if we already have data at that exact time, delete it to ensure the latest data is stored 249 | if (storage_.rbegin()->stamp_ >= new_data.stamp_) 250 | { 251 | L_TransformStorage::iterator storage_it = storage_.find(new_data); 252 | if (storage_it != storage_.end()) 253 | storage_.erase(storage_it); 254 | } 255 | } 256 | 257 | storage_.insert(storage_.end(), new_data); 258 | 259 | pruneList(); 260 | 261 | return true; 262 | } 263 | 264 | void TimeCache::clearList() 265 | { 266 | storage_.clear(); 267 | } 268 | 269 | unsigned int TimeCache::getListLength() 270 | { 271 | return storage_.size(); 272 | } 273 | 274 | P_TimeAndFrameID TimeCache::getLatestTimeAndParent() 275 | { 276 | if (storage_.empty()) 277 | { 278 | return std::make_pair(ros::Time(), 0); 279 | } 280 | 281 | const TransformStorage& ts = *storage_.rbegin(); 282 | return std::make_pair(ts.stamp_, ts.frame_id_); 283 | } 284 | 285 | ros::Time TimeCache::getLatestTimestamp() 286 | { 287 | if (storage_.empty()) return ros::Time(); //empty list case 288 | return storage_.rbegin()->stamp_; 289 | } 290 | 291 | ros::Time TimeCache::getOldestTimestamp() 292 | { 293 | if (storage_.empty()) return ros::Time(); //empty list case 294 | return storage_.begin()->stamp_; 295 | } 296 | 297 | void TimeCache::pruneList() 298 | { 299 | ros::Time latest_time = storage_.rbegin()->stamp_; 300 | 301 | while(!storage_.empty() && storage_.begin()->stamp_ + max_storage_time_ < latest_time) 302 | { 303 | storage_.erase(storage_.begin()); 304 | } 305 | 306 | } 307 | -------------------------------------------------------------------------------- /include/tf/LinearMath/Transform.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ 3 | 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it freely, 8 | subject to the following restrictions: 9 | 10 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 11 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 12 | 3. This notice may not be removed or altered from any source distribution. 13 | */ 14 | 15 | 16 | 17 | #ifndef tfTransform_H 18 | #define tfTransform_H 19 | 20 | 21 | #include "Matrix3x3.h" 22 | 23 | namespace tf 24 | { 25 | 26 | #define TransformData TransformDoubleData 27 | 28 | 29 | /**@brief The Transform class supports rigid transforms with only translation and rotation and no scaling/shear. 30 | *It can be used in combination with Vector3, Quaternion and Matrix3x3 linear algebra classes. */ 31 | class Transform { 32 | 33 | ///Storage for the rotation 34 | Matrix3x3 m_basis; 35 | ///Storage for the translation 36 | Vector3 m_origin; 37 | 38 | public: 39 | 40 | /**@brief No initialization constructor */ 41 | Transform() {} 42 | /**@brief Constructor from Quaternion (optional Vector3 ) 43 | * @param q Rotation from quaternion 44 | * @param c Translation from Vector (default 0,0,0) */ 45 | explicit TFSIMD_FORCE_INLINE Transform(const Quaternion& q, 46 | const Vector3& c = Vector3(tfScalar(0), tfScalar(0), tfScalar(0))) 47 | : m_basis(q), 48 | m_origin(c) 49 | {} 50 | 51 | /**@brief Constructor from Matrix3x3 (optional Vector3) 52 | * @param b Rotation from Matrix 53 | * @param c Translation from Vector default (0,0,0)*/ 54 | explicit TFSIMD_FORCE_INLINE Transform(const Matrix3x3& b, 55 | const Vector3& c = Vector3(tfScalar(0), tfScalar(0), tfScalar(0))) 56 | : m_basis(b), 57 | m_origin(c) 58 | {} 59 | /**@brief Copy constructor */ 60 | TFSIMD_FORCE_INLINE Transform (const Transform& other) 61 | : m_basis(other.m_basis), 62 | m_origin(other.m_origin) 63 | { 64 | } 65 | /**@brief Assignment Operator */ 66 | TFSIMD_FORCE_INLINE Transform& operator=(const Transform& other) 67 | { 68 | m_basis = other.m_basis; 69 | m_origin = other.m_origin; 70 | return *this; 71 | } 72 | 73 | /**@brief Set the current transform as the value of the product of two transforms 74 | * @param t1 Transform 1 75 | * @param t2 Transform 2 76 | * This = Transform1 * Transform2 */ 77 | TFSIMD_FORCE_INLINE void mult(const Transform& t1, const Transform& t2) { 78 | m_basis = t1.m_basis * t2.m_basis; 79 | m_origin = t1(t2.m_origin); 80 | } 81 | 82 | /* void multInverseLeft(const Transform& t1, const Transform& t2) { 83 | Vector3 v = t2.m_origin - t1.m_origin; 84 | m_basis = tfMultTransposeLeft(t1.m_basis, t2.m_basis); 85 | m_origin = v * t1.m_basis; 86 | } 87 | */ 88 | 89 | /**@brief Return the transform of the vector */ 90 | TFSIMD_FORCE_INLINE Vector3 operator()(const Vector3& x) const 91 | { 92 | return Vector3(m_basis[0].dot(x) + m_origin.x(), 93 | m_basis[1].dot(x) + m_origin.y(), 94 | m_basis[2].dot(x) + m_origin.z()); 95 | } 96 | 97 | /**@brief Return the transform of the vector */ 98 | TFSIMD_FORCE_INLINE Vector3 operator*(const Vector3& x) const 99 | { 100 | return (*this)(x); 101 | } 102 | 103 | /**@brief Return the transform of the Quaternion */ 104 | TFSIMD_FORCE_INLINE Quaternion operator*(const Quaternion& q) const 105 | { 106 | return getRotation() * q; 107 | } 108 | 109 | /**@brief Return the basis matrix for the rotation */ 110 | TFSIMD_FORCE_INLINE Matrix3x3& getBasis() { return m_basis; } 111 | /**@brief Return the basis matrix for the rotation */ 112 | TFSIMD_FORCE_INLINE const Matrix3x3& getBasis() const { return m_basis; } 113 | 114 | /**@brief Return the origin vector translation */ 115 | TFSIMD_FORCE_INLINE Vector3& getOrigin() { return m_origin; } 116 | /**@brief Return the origin vector translation */ 117 | TFSIMD_FORCE_INLINE const Vector3& getOrigin() const { return m_origin; } 118 | 119 | /**@brief Return a quaternion representing the rotation */ 120 | Quaternion getRotation() const { 121 | Quaternion q; 122 | m_basis.getRotation(q); 123 | return q; 124 | } 125 | 126 | 127 | /**@brief Set from an array 128 | * @param m A pointer to a 15 element array (12 rotation(row major padded on the right by 1), and 3 translation */ 129 | void setFromOpenGLMatrix(const tfScalar *m) 130 | { 131 | m_basis.setFromOpenGLSubMatrix(m); 132 | m_origin.setValue(m[12],m[13],m[14]); 133 | } 134 | 135 | /**@brief Fill an array representation 136 | * @param m A pointer to a 15 element array (12 rotation(row major padded on the right by 1), and 3 translation */ 137 | void getOpenGLMatrix(tfScalar *m) const 138 | { 139 | m_basis.getOpenGLSubMatrix(m); 140 | m[12] = m_origin.x(); 141 | m[13] = m_origin.y(); 142 | m[14] = m_origin.z(); 143 | m[15] = tfScalar(1.0); 144 | } 145 | 146 | /**@brief Set the translational element 147 | * @param origin The vector to set the translation to */ 148 | TFSIMD_FORCE_INLINE void setOrigin(const Vector3& origin) 149 | { 150 | m_origin = origin; 151 | } 152 | 153 | TFSIMD_FORCE_INLINE Vector3 invXform(const Vector3& inVec) const; 154 | 155 | 156 | /**@brief Set the rotational element by Matrix3x3 */ 157 | TFSIMD_FORCE_INLINE void setBasis(const Matrix3x3& basis) 158 | { 159 | m_basis = basis; 160 | } 161 | 162 | /**@brief Set the rotational element by Quaternion */ 163 | TFSIMD_FORCE_INLINE void setRotation(const Quaternion& q) 164 | { 165 | m_basis.setRotation(q); 166 | } 167 | 168 | 169 | /**@brief Set this transformation to the identity */ 170 | void setIdentity() 171 | { 172 | m_basis.setIdentity(); 173 | m_origin.setValue(tfScalar(0.0), tfScalar(0.0), tfScalar(0.0)); 174 | } 175 | 176 | /**@brief Multiply this Transform by another(this = this * another) 177 | * @param t The other transform */ 178 | Transform& operator*=(const Transform& t) 179 | { 180 | m_origin += m_basis * t.m_origin; 181 | m_basis *= t.m_basis; 182 | return *this; 183 | } 184 | 185 | /**@brief Return the inverse of this transform */ 186 | Transform inverse() const 187 | { 188 | Matrix3x3 inv = m_basis.transpose(); 189 | return Transform(inv, inv * -m_origin); 190 | } 191 | 192 | /**@brief Return the inverse of this transform times the other transform 193 | * @param t The other transform 194 | * return this.inverse() * the other */ 195 | Transform inverseTimes(const Transform& t) const; 196 | 197 | /**@brief Return the product of this transform and the other */ 198 | Transform operator*(const Transform& t) const; 199 | 200 | /**@brief Return an identity transform */ 201 | static const Transform& getIdentity() 202 | { 203 | static const Transform identityTransform(Matrix3x3::getIdentity()); 204 | return identityTransform; 205 | } 206 | 207 | void serialize(struct TransformData& dataOut) const; 208 | 209 | void serializeFloat(struct TransformFloatData& dataOut) const; 210 | 211 | void deSerialize(const struct TransformData& dataIn); 212 | 213 | void deSerializeDouble(const struct TransformDoubleData& dataIn); 214 | 215 | void deSerializeFloat(const struct TransformFloatData& dataIn); 216 | 217 | }; 218 | 219 | 220 | TFSIMD_FORCE_INLINE Vector3 221 | Transform::invXform(const Vector3& inVec) const 222 | { 223 | Vector3 v = inVec - m_origin; 224 | return (m_basis.transpose() * v); 225 | } 226 | 227 | TFSIMD_FORCE_INLINE Transform 228 | Transform::inverseTimes(const Transform& t) const 229 | { 230 | Vector3 v = t.getOrigin() - m_origin; 231 | return Transform(m_basis.transposeTimes(t.m_basis), 232 | v * m_basis); 233 | } 234 | 235 | TFSIMD_FORCE_INLINE Transform 236 | Transform::operator*(const Transform& t) const 237 | { 238 | return Transform(m_basis * t.m_basis, 239 | (*this)(t.m_origin)); 240 | } 241 | 242 | /**@brief Test if two transforms have all elements equal */ 243 | TFSIMD_FORCE_INLINE bool operator==(const Transform& t1, const Transform& t2) 244 | { 245 | return ( t1.getBasis() == t2.getBasis() && 246 | t1.getOrigin() == t2.getOrigin() ); 247 | } 248 | 249 | 250 | ///for serialization 251 | struct TransformFloatData 252 | { 253 | Matrix3x3FloatData m_basis; 254 | Vector3FloatData m_origin; 255 | }; 256 | 257 | struct TransformDoubleData 258 | { 259 | Matrix3x3DoubleData m_basis; 260 | Vector3DoubleData m_origin; 261 | }; 262 | 263 | 264 | 265 | TFSIMD_FORCE_INLINE void Transform::serialize(TransformData& dataOut) const 266 | { 267 | m_basis.serialize(dataOut.m_basis); 268 | m_origin.serialize(dataOut.m_origin); 269 | } 270 | 271 | TFSIMD_FORCE_INLINE void Transform::serializeFloat(TransformFloatData& dataOut) const 272 | { 273 | m_basis.serializeFloat(dataOut.m_basis); 274 | m_origin.serializeFloat(dataOut.m_origin); 275 | } 276 | 277 | 278 | TFSIMD_FORCE_INLINE void Transform::deSerialize(const TransformData& dataIn) 279 | { 280 | m_basis.deSerialize(dataIn.m_basis); 281 | m_origin.deSerialize(dataIn.m_origin); 282 | } 283 | 284 | TFSIMD_FORCE_INLINE void Transform::deSerializeFloat(const TransformFloatData& dataIn) 285 | { 286 | m_basis.deSerializeFloat(dataIn.m_basis); 287 | m_origin.deSerializeFloat(dataIn.m_origin); 288 | } 289 | 290 | TFSIMD_FORCE_INLINE void Transform::deSerializeDouble(const TransformDoubleData& dataIn) 291 | { 292 | m_basis.deSerializeDouble(dataIn.m_basis); 293 | m_origin.deSerializeDouble(dataIn.m_origin); 294 | } 295 | 296 | } 297 | 298 | #endif 299 | 300 | 301 | 302 | 303 | 304 | 305 | -------------------------------------------------------------------------------- /test/test_datatype_conversion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import roslib; roslib.load_manifest('tf') 3 | 4 | import sys 5 | import unittest 6 | import tf 7 | import geometry_msgs.msg 8 | import rospy 9 | 10 | ## A sample python unit test 11 | class PoseConversions(unittest.TestCase): 12 | def setUp(self): 13 | #Setup the pose tests 14 | self.tfpose_stamped = tf.PoseStamped() 15 | self.tfpose_stamped.pose.setIdentity() 16 | self.tfpose_stamped.frame_id = "frame1" 17 | self.tfpose_stamped.stamp = roslib.rostime.Time(10,0) 18 | 19 | self.msgpose_stamped = geometry_msgs.msg.PoseStamped() 20 | self.msgpose_stamped.pose.position.x = 0 21 | self.msgpose_stamped.pose.position.y = 0 22 | self.msgpose_stamped.pose.position.z = 0 23 | self.msgpose_stamped.pose.orientation.x = 0 24 | self.msgpose_stamped.pose.orientation.y = 0 25 | self.msgpose_stamped.pose.orientation.z = 0 26 | self.msgpose_stamped.pose.orientation.w = 1 27 | self.msgpose_stamped.header.frame_id = "frame1" 28 | self.msgpose_stamped.header.stamp = roslib.rostime.Time(10,0) 29 | 30 | ## Setup the point tests 31 | self.tfpoint_stamped = tf.PointStamped() 32 | self.tfpoint_stamped.point.x = 0 33 | self.tfpoint_stamped.point.y = 0 34 | self.tfpoint_stamped.point.z = 0 35 | self.tfpoint_stamped.frame_id = "frame1" 36 | self.tfpoint_stamped.stamp = roslib.rostime.Time(10,0) 37 | 38 | self.msgpoint_stamped = geometry_msgs.msg.PointStamped() 39 | self.msgpoint_stamped.point.x = 0 40 | self.msgpoint_stamped.point.y = 0 41 | self.msgpoint_stamped.point.z = 0 42 | self.msgpoint_stamped.header.frame_id = "frame1" 43 | self.msgpoint_stamped.header.stamp = roslib.rostime.Time(10,0) 44 | 45 | ## Setup the vector tests 46 | self.tfvector_stamped = tf.VectorStamped() 47 | self.tfvector_stamped.vector.x = 0 48 | self.tfvector_stamped.vector.y = 0 49 | self.tfvector_stamped.vector.z = 0 50 | self.tfvector_stamped.frame_id = "frame1" 51 | self.tfvector_stamped.stamp = roslib.rostime.Time(10, 0) 52 | 53 | self.msgvector_stamped = geometry_msgs.msg.Vector3Stamped() 54 | self.msgvector_stamped.vector.x = 0 55 | self.msgvector_stamped.vector.y = 0 56 | self.msgvector_stamped.vector.z = 0 57 | self.msgvector_stamped.header.frame_id = "frame1" 58 | self.msgvector_stamped.header.stamp = roslib.rostime.Time(10,0) 59 | 60 | ## Setup the quaternion tests 61 | self.tfquaternion_stamped = tf.QuaternionStamped() 62 | self.tfquaternion_stamped.quaternion.x = 0 63 | self.tfquaternion_stamped.quaternion.y = 0 64 | self.tfquaternion_stamped.quaternion.z = 0 65 | self.tfquaternion_stamped.quaternion.w = 1 66 | self.tfquaternion_stamped.frame_id = "frame1" 67 | self.tfquaternion_stamped.stamp = roslib.rostime.Time(10, 0) 68 | 69 | self.msgquaternion_stamped = geometry_msgs.msg.QuaternionStamped() 70 | self.msgquaternion_stamped.quaternion.x = 0 71 | self.msgquaternion_stamped.quaternion.y = 0 72 | self.msgquaternion_stamped.quaternion.z = 0 73 | self.msgquaternion_stamped.quaternion.w = 1 74 | self.msgquaternion_stamped.header.frame_id = "frame1" 75 | self.msgquaternion_stamped.header.stamp = roslib.rostime.Time(10,0) 76 | 77 | # Test Pose conversions 78 | def test_msg_operator_equals_pose(self): 79 | self.assertEquals(self.msgpose_stamped, self.msgpose_stamped, "pose msg test correctness") 80 | 81 | def test_bt_operator_equals_pose(self): 82 | self.assertEquals(self.tfpose_stamped, self.tfpose_stamped, "pose bt test correctness") 83 | 84 | def test_msg_operator_on_converted(self): 85 | self.assertEquals(tf.pose_stamped_bt_to_msg(self.tfpose_stamped), tf.pose_stamped_bt_to_msg(self.tfpose_stamped), "pose msg test correctness after conversion") 86 | 87 | def test_bt_operator_on_converted(self): 88 | self.assertEquals(tf.pose_stamped_msg_to_bt(self.msgpose_stamped), tf.pose_stamped_msg_to_bt(self.msgpose_stamped), "pose bt test correctness after conversion") 89 | 90 | def test_to_msg_pose(self): 91 | self.assertEquals(tf.pose_bt_to_msg(self.tfpose_stamped.pose), self.msgpose_stamped.pose, "pose tf to msg incorrect") 92 | def test_to_tf_pose(self): 93 | self.assertEquals(tf.pose_msg_to_bt(self.msgpose_stamped.pose), self.tfpose_stamped.pose, "pose stamped msg to tf incorrect") 94 | 95 | def test_stamped_to_msg_pose(self): 96 | self.assertEquals(tf.pose_stamped_bt_to_msg(self.tfpose_stamped), self.msgpose_stamped, "pose stamped tf to msg incorrect") 97 | def test_stamped_to_tf_pose(self): 98 | self.assertEquals(tf.pose_stamped_msg_to_bt(self.msgpose_stamped), self.tfpose_stamped, "pose stamped msg to tf incorrect") 99 | 100 | 101 | 102 | # Test Point conversions 103 | def test_bt_operator_equal_point(self): 104 | self.assertEquals(self.tfpoint_stamped, self.tfpoint_stamped, "point tf test correctness") 105 | 106 | def test_msg_operator_equal_point(self): 107 | self.assertEquals(self.msgpoint_stamped, self.msgpoint_stamped, "point msg test correctness") 108 | 109 | def test_msg_operator_equal_point_converted(self): 110 | self.assertEquals(tf.point_stamped_bt_to_msg(self.tfpoint_stamped), tf.point_stamped_bt_to_msg(self.tfpoint_stamped), "point msg test correctness after conversion") 111 | 112 | def test_bt_operator_equal_point_converted(self): 113 | self.assertEquals(tf.point_stamped_msg_to_bt(self.msgpoint_stamped), tf.point_stamped_msg_to_bt(self.msgpoint_stamped), "point bt test correctness after conversion") 114 | 115 | def test_to_msg_point(self): 116 | self.assertEquals(tf.point_bt_to_msg(self.tfpoint_stamped.point), self.msgpoint_stamped.point, "point tf to msg incorrect") 117 | def test_to_tf_point(self): 118 | self.assertEquals(tf.point_msg_to_bt(self.msgpoint_stamped.point), self.tfpoint_stamped.point, "point stamped msg to tf incorrect") 119 | 120 | def test_stamped_to_msg_point(self): 121 | self.assertEquals(tf.point_stamped_bt_to_msg(self.tfpoint_stamped), self.msgpoint_stamped, "point stamped tf to msg incorrect") 122 | def test_stamped_to_tf_point(self): 123 | self.assertEquals(tf.point_stamped_msg_to_bt(self.msgpoint_stamped), self.tfpoint_stamped, "point stamped msg to tf incorrect") 124 | 125 | # Test Vector conversions 126 | def test_msg_operator_equal_vector(self): 127 | self.assertEquals(self.tfvector_stamped, self.tfvector_stamped, "vector bt test correctness") 128 | 129 | def test_msg_operator_equal_vector(self): 130 | self.assertEquals(self.msgvector_stamped, self.msgvector_stamped, "vector msg test correctness") 131 | 132 | def test_msg_operator_equal_vector_converted(self): 133 | self.assertEquals(tf.vector_stamped_bt_to_msg(self.tfvector_stamped), tf.vector_stamped_bt_to_msg(self.tfvector_stamped), "vector msg test correctness after conversion") 134 | 135 | def test_bt_operator_equal_vector_converted(self): 136 | self.assertEquals(tf.vector_stamped_msg_to_bt(self.msgvector_stamped), tf.vector_stamped_msg_to_bt(self.msgvector_stamped), "vector bt test correctness after conversion") 137 | 138 | def test_to_msg_vector(self): 139 | self.assertEquals(tf.vector_bt_to_msg(self.tfvector_stamped.vector), self.msgvector_stamped.vector, "vector tf to msg incorrect") 140 | def test_to_tf_vector(self): 141 | self.assertEquals(tf.vector_msg_to_bt(self.msgvector_stamped.vector), self.tfvector_stamped.vector, "vector stamped msg to tf incorrect") 142 | 143 | def test_stamped_to_msg_vector(self): 144 | self.assertEquals(tf.vector_stamped_bt_to_msg(self.tfvector_stamped), self.msgvector_stamped, "vector stamped tf to msg incorrect") 145 | def test_stamped_to_tf_vector(self): 146 | self.assertEquals(tf.vector_stamped_msg_to_bt(self.msgvector_stamped), self.tfvector_stamped, "vector stamped msg to tf incorrect") 147 | 148 | # Test Quaternion conversions 149 | def test_bt_operator_equal_quaternion(self): 150 | self.assertEquals(self.tfquaternion_stamped, self.tfquaternion_stamped, "quaternion bt test correctness") 151 | 152 | def test_msg_operator_equal_quaternion(self): 153 | self.assertEquals(self.msgquaternion_stamped, self.msgquaternion_stamped, "quaternion msg test correctness") 154 | 155 | def test_msg_operator_equal_quaternion_converted(self): 156 | self.assertEquals(tf.quaternion_stamped_bt_to_msg(self.tfquaternion_stamped), tf.quaternion_stamped_bt_to_msg(self.tfquaternion_stamped), "quaternion msg test correctness after conversion") 157 | 158 | def test_bt_operator_equal_quaternion_converted(self): 159 | self.assertEquals(tf.quaternion_stamped_msg_to_bt(self.msgquaternion_stamped), tf.quaternion_stamped_msg_to_bt(self.msgquaternion_stamped), "quaternion bt test correctness after conversion") 160 | 161 | def test_to_msg_quaternion(self): 162 | self.assertEquals(tf.quaternion_bt_to_msg(self.tfquaternion_stamped.quaternion), self.msgquaternion_stamped.quaternion, "quaternion tf to msg incorrect") 163 | def test_to_tf_quaternion(self): 164 | self.assertEquals(tf.quaternion_msg_to_bt(self.msgquaternion_stamped.quaternion), self.tfquaternion_stamped.quaternion, "quaternion stamped msg to tf incorrect") 165 | 166 | def test_stamped_to_msg_quaternion(self): 167 | self.assertEquals(tf.quaternion_stamped_bt_to_msg(self.tfquaternion_stamped), self.msgquaternion_stamped, "quaternion stamped tf to msg incorrect") 168 | def test_stamped_to_tf_quaternion(self): 169 | self.assertEquals(tf.quaternion_stamped_msg_to_bt(self.msgquaternion_stamped), self.tfquaternion_stamped, "quaternion stamped msg to tf incorrect") 170 | if __name__ == '__main__': 171 | import rostest 172 | rostest.unitrun('tf', 'test_tf_data_conversions', PoseConversions) 173 | 174 | -------------------------------------------------------------------------------- /test/testPython.py: -------------------------------------------------------------------------------- 1 | import roslib 2 | roslib.load_manifest('tf') 3 | import rostest 4 | import rospy 5 | import numpy 6 | import unittest 7 | import sys 8 | 9 | import tf.transformations 10 | import geometry_msgs.msg 11 | import sensor_msgs.msg 12 | 13 | import tf 14 | 15 | class Mock: 16 | pass 17 | 18 | def setT(t, parent, frame, ti, x): 19 | m = Mock() 20 | m.parent_id = parent 21 | m.header = Mock() 22 | m.header.stamp = ti 23 | m.header.frame_id = frame 24 | m.transform = Mock() 25 | m.transform.translation = Mock() 26 | m.transform.translation.x = x 27 | m.transform.translation.y = 0 28 | m.transform.translation.z = 0 29 | m.transform.rotation = Mock() 30 | m.transform.rotation.x = 0 31 | m.transform.rotation.y = 0 32 | m.transform.rotation.z = 0 33 | m.transform.rotation.w = 1 34 | t.setTransform(m) 35 | 36 | class TestPython(unittest.TestCase): 37 | 38 | def setUp(self): 39 | pass 40 | 41 | def common(self, t): 42 | m = geometry_msgs.msg.TransformStamped() 43 | m.header.frame_id = "PARENT" 44 | m.child_frame_id = "THISFRAME" 45 | m.transform.translation.y = 5.0 46 | m.transform.rotation = geometry_msgs.msg.Quaternion(*tf.transformations.quaternion_from_euler(0, 0, 0)) 47 | t.setTransform(m) 48 | afs = t.allFramesAsString() 49 | self.assert_(len(afs) != 0) 50 | self.assert_("PARENT" in afs) 51 | self.assert_("THISFRAME" in afs) 52 | self.assert_(t.getLatestCommonTime("THISFRAME", "PARENT").to_sec() == 0) 53 | for ti in [3, 5, 10, 11, 19, 20, 21]: 54 | m.header.stamp.secs = ti 55 | t.setTransform(m) 56 | self.assert_(t.getLatestCommonTime("THISFRAME", "PARENT").to_sec() == ti) 57 | 58 | # Verify that getLatestCommonTime with nonexistent frames raise exception 59 | self.assertRaises(tf.Exception, lambda: t.getLatestCommonTime("MANDALAY", "JUPITER")) 60 | self.assertRaises(tf.LookupException, lambda: t.lookupTransform("MANDALAY", "JUPITER", rospy.Time())) 61 | 62 | # Ask for transform for valid frames, but more than 10 seconds in the past. Should raise ExtrapolationException 63 | self.assertRaises(tf.ExtrapolationException, lambda: t.lookupTransform("THISFRAME", "PARENT", rospy.Time(2))) 64 | 65 | #### print t.lookupVelocity("THISFRAME", "PARENT", rospy.Time(15), rospy.Duration(5)) 66 | 67 | def test_smoke(self): 68 | t = tf.Transformer() 69 | self.common(t) 70 | 71 | def test_cache_time(self): 72 | # Vary cache_time and confirm its effect on ExtrapolationException from lookupTransform(). 73 | 74 | for cache_time in range(2, 98): 75 | t = tf.Transformer(True, rospy.Duration(cache_time)) 76 | m = geometry_msgs.msg.TransformStamped() 77 | m.header.frame_id = "PARENT" 78 | m.child_frame_id = "THISFRAME" 79 | m.transform.translation.y = 5.0 80 | m.transform.rotation = geometry_msgs.msg.Quaternion(*tf.transformations.quaternion_from_euler(0, 0, 0)) 81 | t.setTransform(m) 82 | afs = t.allFramesAsString() 83 | self.assert_(len(afs) != 0) 84 | self.assert_("PARENT" in afs) 85 | self.assert_("THISFRAME" in afs) 86 | self.assert_(t.getLatestCommonTime("THISFRAME", "PARENT").to_sec() == 0) 87 | 88 | # Set transforms for time 0..100 inclusive 89 | for ti in range(101): 90 | m.header.stamp = rospy.Time(ti) 91 | t.setTransform(m) 92 | self.assert_(t.getLatestCommonTime("THISFRAME", "PARENT").to_sec() == ti) 93 | self.assertEqual(t.getLatestCommonTime("THISFRAME", "PARENT").to_sec(), 100) 94 | 95 | # (avoid time of 0 because that means 'latest') 96 | 97 | for ti in range(1, 100 - cache_time): 98 | self.assertRaises(tf.ExtrapolationException, lambda: t.lookupTransform("THISFRAME", "PARENT", rospy.Time(ti))) 99 | for ti in range(100 - cache_time, 100): 100 | t.lookupTransform("THISFRAME", "PARENT", rospy.Time(ti)) 101 | 102 | def test_subclass(self): 103 | class TransformerSubclass(tf.Transformer): 104 | def extra(self): 105 | return 77 106 | t = TransformerSubclass(True, rospy.Duration.from_sec(10.0)) 107 | self.assert_(t.extra() == 77) 108 | self.common(t) 109 | self.assert_(t.extra() == 77) 110 | 111 | def test_twist(self): 112 | t = tf.Transformer() 113 | 114 | vel = 3 115 | for ti in range(5): 116 | m = geometry_msgs.msg.TransformStamped() 117 | m.header.frame_id = "PARENT" 118 | m.header.stamp = rospy.Time(ti) 119 | m.child_frame_id = "THISFRAME" 120 | m.transform.translation.x = ti * vel 121 | m.transform.rotation = geometry_msgs.msg.Quaternion(*tf.transformations.quaternion_from_euler(0, 0, 0)) 122 | t.setTransform(m) 123 | 124 | tw0 = t.lookupTwist("THISFRAME", "PARENT", rospy.Time(0.0), rospy.Duration(4.001)) 125 | self.assertAlmostEqual(tw0[0][0], vel, 2) 126 | tw1 = t.lookupTwistFull("THISFRAME", "PARENT", "PARENT", (0, 0, 0), "THISFRAME", rospy.Time(0.0), rospy.Duration(4.001)) 127 | self.assertEqual(tw0, tw1) 128 | 129 | def test_transformer_ros(self): 130 | tr = tf.TransformerROS() 131 | 132 | m = geometry_msgs.msg.TransformStamped() 133 | m.header.stamp = rospy.Time().from_sec(3.0) 134 | m.header.frame_id = "PARENT" 135 | m.child_frame_id = "THISFRAME" 136 | m.transform.translation.y = 5.0 137 | m.transform.rotation.x = 0.04997917 138 | m.transform.rotation.y = 0 139 | m.transform.rotation.z = 0 140 | m.transform.rotation.w = 0.99875026 141 | tr.setTransform(m) 142 | m.header.stamp = rospy.Time().from_sec(5.0) 143 | tr.setTransform(m) 144 | 145 | # Smoke the various transform* methods 146 | 147 | types = [ "Point", "Pose", "Quaternion", "Vector3" ] 148 | for t in types: 149 | msg = getattr(geometry_msgs.msg, "%sStamped" % t)() 150 | msg.header.frame_id = "THISFRAME" 151 | msg_t = getattr(tr, "transform%s" % t)("PARENT", msg) 152 | self.assertEqual(msg_t.header.frame_id, "PARENT") 153 | 154 | # PointCloud is a bit different, so smoke is different 155 | 156 | msg = sensor_msgs.msg.PointCloud() 157 | msg.header.frame_id = "THISFRAME" 158 | msg.points = [geometry_msgs.msg.Point32(1,2,3)] 159 | xmsg = tr.transformPointCloud("PARENT", msg) 160 | self.assertEqual(xmsg.header.frame_id, "PARENT") 161 | self.assertEqual(len(msg.points), len(xmsg.points)) 162 | self.assertNotEqual(msg.points[0], xmsg.points[0]) 163 | 164 | """ 165 | Two fixed quaternions, a small twist around X concatenated. 166 | 167 | >>> t.quaternion_from_euler(0.1, 0, 0) 168 | array([ 0.04997917, 0. , 0. , 0.99875026]) 169 | >>> t.quaternion_from_euler(0.2, 0, 0) 170 | array([ 0.09983342, 0. , 0. , 0.99500417]) 171 | """ 172 | 173 | # Specific test for quaternion types 174 | 175 | msg = geometry_msgs.msg.QuaternionStamped() 176 | q = [ 0.04997917, 0. , 0. , 0.99875026 ] 177 | msg.quaternion.x = q[0] 178 | msg.quaternion.y = q[1] 179 | msg.quaternion.z = q[2] 180 | msg.quaternion.w = q[3] 181 | msg.header.stamp = rospy.Time().from_sec(3.0) 182 | msg.header.frame_id = "THISFRAME" 183 | msg_t = tr.transformQuaternion("PARENT", msg) 184 | self.assertEqual(msg_t.header.frame_id, "PARENT") 185 | for a,v in zip("xyzw", [ 0.09983342, 0. , 0. , 0.99500417]): 186 | self.assertAlmostEqual(v, 187 | getattr(msg_t.quaternion, a), 188 | 4) 189 | 190 | def test_transformer_wait_for_transform_dedicated_thread(self): 191 | tr = tf.Transformer() 192 | try: 193 | tr.waitForTransform("PARENT", "THISFRAME", rospy.Time().from_sec(4.0), rospy.Duration(3.0)) 194 | self.assertFalse("This should throw") 195 | except tf.Exception, ex: 196 | print "successfully caught" 197 | pass 198 | 199 | 200 | def test_transformer_wait_for_transform(self): 201 | tr = tf.Transformer() 202 | tr.setUsingDedicatedThread(1) 203 | 204 | try: 205 | tr.waitForTransform("PARENT", "THISFRAME", rospy.Time().from_sec(4.0), rospy.Duration(3.0)) 206 | self.assertFalse("This should throw") 207 | except tf.Exception, ex: 208 | pass 209 | 210 | m = geometry_msgs.msg.TransformStamped() 211 | m.header.stamp = rospy.Time().from_sec(3.0) 212 | m.header.frame_id = "PARENT" 213 | m.child_frame_id = "THISFRAME" 214 | m.transform.translation.y = 5.0 215 | m.transform.rotation.x = 0.04997917 216 | m.transform.rotation.y = 0 217 | m.transform.rotation.z = 0 218 | m.transform.rotation.w = 0.99875026 219 | tr.setTransform(m) 220 | m.header.stamp = rospy.Time().from_sec(5.0) 221 | tr.setTransform(m) 222 | 223 | try: 224 | tr.waitForTransform("PARENT", "THISFRAME", rospy.Time().from_sec(4.0), rospy.Duration(3.0)) 225 | except tf.Exception, ex: 226 | self.assertFalse("This should not throw") 227 | 228 | 229 | def test_getTFPrefix(self): 230 | t = tf.Transformer() 231 | self.assertEqual(t.getTFPrefix(), "") 232 | 233 | def no_test_random(self): 234 | import networkx as nx 235 | for (r,h) in [ (2,2), (2,5), (3,5) ]: 236 | G = nx.balanced_tree(r, h) 237 | t = tf.Transformer(True, rospy.Duration(10.0)) 238 | 239 | for n in G.nodes(): 240 | if n != 0: 241 | # n has parent p 242 | p = min(G.neighbors(n)) 243 | setT(t, str(p), str(n), rospy.Time(0), 1) 244 | for n in G.nodes(): 245 | ((x,_,_), _) = t.lookupTransform("0", str(n), rospy.Time(0)) 246 | self.assert_(x == nx.shortest_path_length(G, 0, n)) 247 | for i in G.nodes(): 248 | for j in G.nodes(): 249 | ((x,_,_), _) = t.lookupTransform(str(i), str(j), rospy.Time()) 250 | self.assert_(abs(x) == abs(nx.shortest_path_length(G, 0, i) - nx.shortest_path_length(G, 0, j))) 251 | 252 | if __name__ == '__main__': 253 | rostest.unitrun('tf', 'directed', TestPython) 254 | -------------------------------------------------------------------------------- /src/transform_listener.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #include "tf/transform_listener.h" 33 | 34 | #include 35 | #include 36 | 37 | 38 | using namespace tf; 39 | std::string tf::remap(const std::string& frame_id) 40 | { 41 | ros::NodeHandle n("~"); 42 | return tf::resolve(getPrefixParam(n), frame_id); 43 | } 44 | 45 | 46 | TransformerHelper::TransformerHelper(ros::Duration max_cache_time) 47 | : Transformer(true, max_cache_time) 48 | { 49 | 50 | } 51 | 52 | TransformerHelper::~TransformerHelper() 53 | { 54 | 55 | } 56 | 57 | void TransformerHelper::transformQuaternion(const std::string& target_frame, 58 | const geometry_msgs::QuaternionStamped& msg_in, 59 | geometry_msgs::QuaternionStamped& msg_out) const 60 | { 61 | tf::assertQuaternionValid(msg_in.quaternion); 62 | 63 | Stamped pin, pout; 64 | quaternionStampedMsgToTF(msg_in, pin); 65 | transformQuaternion(target_frame, pin, pout); 66 | quaternionStampedTFToMsg(pout, msg_out); 67 | } 68 | 69 | void TransformerHelper::transformVector(const std::string& target_frame, 70 | const geometry_msgs::Vector3Stamped& msg_in, 71 | geometry_msgs::Vector3Stamped& msg_out) const 72 | { 73 | Stamped pin, pout; 74 | vector3StampedMsgToTF(msg_in, pin); 75 | transformVector(target_frame, pin, pout); 76 | vector3StampedTFToMsg(pout, msg_out); 77 | } 78 | 79 | void TransformerHelper::transformPoint(const std::string& target_frame, 80 | const geometry_msgs::PointStamped& msg_in, 81 | geometry_msgs::PointStamped& msg_out) const 82 | { 83 | Stamped pin, pout; 84 | pointStampedMsgToTF(msg_in, pin); 85 | transformPoint(target_frame, pin, pout); 86 | pointStampedTFToMsg(pout, msg_out); 87 | } 88 | 89 | void TransformerHelper::transformPose(const std::string& target_frame, 90 | const geometry_msgs::PoseStamped& msg_in, 91 | geometry_msgs::PoseStamped& msg_out) const 92 | { 93 | tf::assertQuaternionValid(msg_in.pose.orientation); 94 | 95 | Stamped pin, pout; 96 | poseStampedMsgToTF(msg_in, pin); 97 | transformPose(target_frame, pin, pout); 98 | poseStampedTFToMsg(pout, msg_out); 99 | } 100 | /* http://www.ros.org/wiki/tf/Reviews/2010-03-12_API_Review 101 | void TransformerHelper::transformTwist(const std::string& target_frame, 102 | const geometry_msgs::TwistStamped& msg_in, 103 | geometry_msgs::TwistStamped& msg_out) const 104 | { 105 | tf::Vector3 twist_rot(msg_in.twist.angular.x, 106 | msg_in.twist.angular.y, 107 | msg_in.twist.angular.z); 108 | tf::Vector3 twist_vel(msg_in.twist.linear.x, 109 | msg_in.twist.linear.y, 110 | msg_in.twist.linear.z); 111 | 112 | tf::StampedTransform transform; 113 | lookupTransform(target_frame,msg_in.header.frame_id, msg_in.header.stamp, transform); 114 | 115 | 116 | tf::Vector3 out_rot = transform.getBasis() * twist_rot; 117 | tf::Vector3 out_vel = transform.getBasis()* twist_vel + transform.getOrigin().cross(out_rot); 118 | 119 | geometry_msgs::TwistStamped interframe_twist; 120 | lookupVelocity(target_frame, msg_in.header.frame_id, msg_in.header.stamp, ros::Duration(0.1), interframe_twist); //\todo get rid of hard coded number 121 | 122 | msg_out.header.stamp = msg_in.header.stamp; 123 | msg_out.header.frame_id = target_frame; 124 | msg_out.twist.linear.x = out_vel.x() + interframe_twist.twist.linear.x; 125 | msg_out.twist.linear.y = out_vel.y() + interframe_twist.twist.linear.y; 126 | msg_out.twist.linear.z = out_vel.z() + interframe_twist.twist.linear.z; 127 | msg_out.twist.angular.x = out_rot.x() + interframe_twist.twist.angular.x; 128 | msg_out.twist.angular.y = out_rot.y() + interframe_twist.twist.angular.y; 129 | msg_out.twist.angular.z = out_rot.z() + interframe_twist.twist.angular.z; 130 | 131 | }*/ 132 | 133 | void TransformerHelper::transformQuaternion(const std::string& target_frame, const ros::Time& target_time, 134 | const geometry_msgs::QuaternionStamped& msg_in, 135 | const std::string& fixed_frame, geometry_msgs::QuaternionStamped& msg_out) const 136 | { 137 | tf::assertQuaternionValid(msg_in.quaternion); 138 | Stamped pin, pout; 139 | quaternionStampedMsgToTF(msg_in, pin); 140 | transformQuaternion(target_frame, target_time, pin, fixed_frame, pout); 141 | quaternionStampedTFToMsg(pout, msg_out); 142 | } 143 | 144 | void TransformerHelper::transformVector(const std::string& target_frame, const ros::Time& target_time, 145 | const geometry_msgs::Vector3Stamped& msg_in, 146 | const std::string& fixed_frame, geometry_msgs::Vector3Stamped& msg_out) const 147 | { 148 | Stamped pin, pout; 149 | vector3StampedMsgToTF(msg_in, pin); 150 | transformVector(target_frame, target_time, pin, fixed_frame, pout); 151 | vector3StampedTFToMsg(pout, msg_out); 152 | } 153 | 154 | void TransformerHelper::transformPoint(const std::string& target_frame, const ros::Time& target_time, 155 | const geometry_msgs::PointStamped& msg_in, 156 | const std::string& fixed_frame, geometry_msgs::PointStamped& msg_out) const 157 | { 158 | Stamped pin, pout; 159 | pointStampedMsgToTF(msg_in, pin); 160 | transformPoint(target_frame, target_time, pin, fixed_frame, pout); 161 | pointStampedTFToMsg(pout, msg_out); 162 | } 163 | 164 | void TransformerHelper::transformPose(const std::string& target_frame, const ros::Time& target_time, 165 | const geometry_msgs::PoseStamped& msg_in, 166 | const std::string& fixed_frame, geometry_msgs::PoseStamped& msg_out) const 167 | { 168 | tf::assertQuaternionValid(msg_in.pose.orientation); 169 | 170 | Stamped pin, pout; 171 | poseStampedMsgToTF(msg_in, pin); 172 | transformPose(target_frame, target_time, pin, fixed_frame, pout); 173 | poseStampedTFToMsg(pout, msg_out); 174 | } 175 | 176 | void TransformerHelper::transformPointCloud(const std::string & target_frame, const sensor_msgs::PointCloud & cloudIn, sensor_msgs::PointCloud & cloudOut) const 177 | { 178 | StampedTransform transform; 179 | lookupTransform(target_frame, cloudIn.header.frame_id, cloudIn.header.stamp, transform); 180 | 181 | transformPointCloud(target_frame, transform, cloudIn.header.stamp, cloudIn, cloudOut); 182 | } 183 | void TransformerHelper::transformPointCloud(const std::string& target_frame, const ros::Time& target_time, 184 | const sensor_msgs::PointCloud& cloudIn, 185 | const std::string& fixed_frame, sensor_msgs::PointCloud& cloudOut) const 186 | { 187 | StampedTransform transform; 188 | lookupTransform(target_frame, target_time, 189 | cloudIn.header.frame_id, cloudIn.header.stamp, 190 | fixed_frame, 191 | transform); 192 | 193 | transformPointCloud(target_frame, transform, target_time, cloudIn, cloudOut); 194 | 195 | 196 | } 197 | 198 | inline void transformPointMatVec(const tf::Vector3 &origin, const tf::Matrix3x3 &basis, const geometry_msgs::Point32 &in, geometry_msgs::Point32 &out) 199 | { 200 | // Use temporary variables in case &in == &out 201 | double x = basis[0].x() * in.x + basis[0].y() * in.y + basis[0].z() * in.z + origin.x(); 202 | double y = basis[1].x() * in.x + basis[1].y() * in.y + basis[1].z() * in.z + origin.y(); 203 | double z = basis[2].x() * in.x + basis[2].y() * in.y + basis[2].z() * in.z + origin.z(); 204 | 205 | out.x = x; out.y = y; out.z = z; 206 | } 207 | 208 | 209 | void TransformerHelper::transformPointCloud(const std::string & target_frame, const tf::Transform& net_transform, 210 | const ros::Time& target_time, const sensor_msgs::PointCloud & cloudIn, 211 | sensor_msgs::PointCloud & cloudOut) const 212 | { 213 | tf::Vector3 origin = net_transform.getOrigin(); 214 | tf::Matrix3x3 basis = net_transform.getBasis(); 215 | 216 | unsigned int length = cloudIn.points.size(); 217 | 218 | // Copy relevant data from cloudIn, if needed 219 | if (&cloudIn != &cloudOut) 220 | { 221 | cloudOut.header = cloudIn.header; 222 | cloudOut.points.resize(length); 223 | cloudOut.channels.resize(cloudIn.channels.size()); 224 | for (unsigned int i = 0 ; i < cloudIn.channels.size() ; ++i) 225 | cloudOut.channels[i] = cloudIn.channels[i]; 226 | } 227 | 228 | // Transform points 229 | cloudOut.header.stamp = target_time; 230 | cloudOut.header.frame_id = target_frame; 231 | for (unsigned int i = 0; i < length ; i++) { 232 | transformPointMatVec(origin, basis, cloudIn.points[i], cloudOut.points[i]); 233 | } 234 | } 235 | 236 | 237 | 238 | 239 | TransformListener::TransformListener(ros::Duration max_cache_time, bool spin_thread): 240 | TransformerHelper(max_cache_time), tf2_listener_(Transformer::tf2_buffer_, node_, spin_thread) 241 | { 242 | //Everything is done inside tf2 init 243 | } 244 | 245 | TransformListener::TransformListener(const ros::NodeHandle& nh, ros::Duration max_cache_time, bool spin_thread): 246 | TransformerHelper(max_cache_time), node_(nh), tf2_listener_(Transformer::tf2_buffer_, nh, spin_thread) 247 | { 248 | //Everything is done inside tf2 init 249 | } 250 | 251 | TransformListener::~TransformListener() 252 | { 253 | //Everything is done inside tf2 init 254 | } 255 | 256 | //Override Transformer::ok() for ticket:4882 257 | bool TransformListener::ok() const { return ros::ok(); } 258 | -------------------------------------------------------------------------------- /src/tf_monitor.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | * Copyright (c) 2008, Willow Garage, Inc. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of the Willow Garage, Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** \author Wim Meeussen */ 33 | 34 | 35 | #include "tf/tf.h" 36 | #include "tf/transform_listener.h" 37 | #include 38 | #include 39 | #include 40 | #include "ros/ros.h" 41 | 42 | using namespace tf; 43 | using namespace ros; 44 | using namespace std; 45 | 46 | 47 | class TFMonitor 48 | { 49 | public: 50 | std::string framea_, frameb_; 51 | bool using_specific_chain_; 52 | 53 | ros::NodeHandle node_; 54 | ros::Subscriber subscriber_tf_, subscriber_tf_message_; 55 | std::vector chain_; 56 | std::map frame_authority_map; 57 | std::map > delay_map; 58 | std::map > authority_map; 59 | std::map > authority_frequency_map; 60 | 61 | TransformListener tf_; 62 | 63 | tf::tfMessage message_; 64 | 65 | boost::mutex map_lock_; 66 | void callback(const tf::tfMessageConstPtr& msg_ptr) 67 | { 68 | const tf::tfMessage& message = *msg_ptr; 69 | //Lookup the authority 70 | std::string authority; 71 | std::map* msg_header_map = message.__connection_header.get(); 72 | std::map::iterator it = msg_header_map->find("callerid"); 73 | if (it == msg_header_map->end()) 74 | { 75 | ROS_WARN("Message recieved without callerid"); 76 | authority = "no callerid"; 77 | } 78 | else 79 | { 80 | authority = it->second; 81 | } 82 | 83 | 84 | double average_offset = 0; 85 | boost::mutex::scoped_lock my_lock(map_lock_); 86 | for (unsigned int i = 0; i < message.transforms.size(); i++) 87 | { 88 | frame_authority_map[message.transforms[i].child_frame_id] = authority; 89 | 90 | double offset = (ros::Time::now() - message.transforms[i].header.stamp).toSec(); 91 | average_offset += offset; 92 | 93 | std::map >::iterator it = delay_map.find(message.transforms[i].child_frame_id); 94 | if (it == delay_map.end()) 95 | { 96 | delay_map[message.transforms[i].child_frame_id] = std::vector(1,offset); 97 | } 98 | else 99 | { 100 | it->second.push_back(offset); 101 | if (it->second.size() > 1000) 102 | it->second.erase(it->second.begin()); 103 | } 104 | 105 | } 106 | 107 | average_offset /= max((size_t) 1, message.transforms.size()); 108 | 109 | //create the authority log 110 | std::map >::iterator it2 = authority_map.find(authority); 111 | if (it2 == authority_map.end()) 112 | { 113 | authority_map[authority] = std::vector(1,average_offset); 114 | } 115 | else 116 | { 117 | it2->second.push_back(average_offset); 118 | if (it2->second.size() > 1000) 119 | it2->second.erase(it2->second.begin()); 120 | } 121 | 122 | //create the authority frequency log 123 | std::map >::iterator it3 = authority_frequency_map.find(authority); 124 | if (it3 == authority_frequency_map.end()) 125 | { 126 | authority_frequency_map[authority] = std::vector(1,ros::Time::now().toSec()); 127 | } 128 | else 129 | { 130 | it3->second.push_back(ros::Time::now().toSec()); 131 | if (it3->second.size() > 1000) 132 | it3->second.erase(it3->second.begin()); 133 | } 134 | 135 | }; 136 | 137 | TFMonitor(bool using_specific_chain, std::string framea = "", std::string frameb = ""): 138 | framea_(framea), frameb_(frameb), 139 | using_specific_chain_(using_specific_chain) 140 | { 141 | 142 | if (using_specific_chain_) 143 | { 144 | cout << "Waiting for transform chain to become available between "<< framea_ << " and " << frameb_<< " " << flush; 145 | while (node_.ok() && !tf_.waitForTransform(framea_, frameb_, Time(), Duration(1.0))) 146 | cout << "." << flush; 147 | cout << endl; 148 | 149 | try{ 150 | tf_.chainAsVector(frameb_, ros::Time(), framea_, ros::Time(), frameb_, chain_); 151 | } 152 | catch(tf::TransformException& ex){ 153 | ROS_WARN("Transform Exception %s", ex.what()); 154 | return; 155 | } 156 | 157 | /* cout << "Chain currently is:" <("tf", 100, boost::bind(&TFMonitor::callback, this, _1)); 165 | subscriber_tf_message_ = node_.subscribe("tf_message", 100, boost::bind(&TFMonitor::callback, this, _1)); 166 | 167 | } 168 | 169 | std::string outputFrameInfo(const std::map >::iterator& it, const std::string& frame_authority) 170 | { 171 | std::stringstream ss; 172 | double average_delay = 0; 173 | double max_delay = 0; 174 | for (unsigned int i = 0; i < it->second.size(); i++) 175 | { 176 | average_delay += it->second[i]; 177 | max_delay = std::max(max_delay, it->second[i]); 178 | } 179 | average_delay /= it->second.size(); 180 | ss << "Frame: " << it->first <<" published by "<< frame_authority << " Average Delay: " << average_delay << " Max Delay: " << max_delay << std::endl; 181 | return ss.str(); 182 | } 183 | 184 | void spin() 185 | { 186 | 187 | // create tf listener 188 | double max_diff = 0; 189 | double avg_diff = 0; 190 | double lowpass = 0.01; 191 | unsigned int counter = 0; 192 | 193 | 194 | 195 | while (node_.ok()){ 196 | tf::StampedTransform tmp; 197 | counter++; 198 | // printf("looping %d\n", counter); 199 | if (using_specific_chain_) 200 | { 201 | tf_.lookupTransform(framea_, frameb_, Time(), tmp); 202 | double diff = (Time::now() - tmp.stamp_).toSec(); 203 | avg_diff = lowpass * diff + (1-lowpass)*avg_diff; 204 | if (diff > max_diff) max_diff = diff; 205 | } 206 | Duration(0.01).sleep(); 207 | if (counter > 20){ 208 | counter = 0; 209 | 210 | if (using_specific_chain_) 211 | { 212 | std::cout < "; 219 | } 220 | cout < >::iterator it = delay_map.begin(); 228 | for ( ; it != delay_map.end() ; ++it) 229 | { 230 | if (using_specific_chain_ ) 231 | { 232 | for ( unsigned int i = 0 ; i < chain_.size(); i++) 233 | { 234 | if (it->first != chain_[i]) 235 | continue; 236 | 237 | cout << outputFrameInfo(it, frame_authority_map[it->first]); 238 | } 239 | } 240 | else 241 | cout << outputFrameInfo(it, frame_authority_map[it->first]); 242 | } 243 | std::cerr < >::iterator it1 = authority_map.begin(); 245 | std::map >::iterator it2 = authority_frequency_map.begin(); 246 | for ( ; it1 != authority_map.end() ; ++it1, ++it2) 247 | { 248 | double average_delay = 0; 249 | double max_delay = 0; 250 | for (unsigned int i = 0; i < it1->second.size(); i++) 251 | { 252 | average_delay += it1->second[i]; 253 | max_delay = std::max(max_delay, it1->second[i]); 254 | } 255 | average_delay /= it1->second.size(); 256 | double frequency_out = (double)(it2->second.size())/std::max(0.00000001, (it2->second.back() - it2->second.front())); 257 | //cout << "output" <<&(*it2) <<" " << it2->second.back() <<" " << it2->second.front() <<" " << std::max((size_t)1, it2->second.size()) << " " << frequency_out << endl; 258 | cout << "Node: " <first << " " << frequency_out <<" Hz, Average Delay: " << average_delay << " Max Delay: " << max_delay << std::endl; 259 | } 260 | 261 | } 262 | } 263 | 264 | } 265 | }; 266 | 267 | 268 | int main(int argc, char ** argv) 269 | { 270 | //Initialize ROS 271 | init(argc, argv, "tf_monitor", ros::init_options::AnonymousName); 272 | 273 | 274 | string framea, frameb; 275 | bool using_specific_chain = true; 276 | if (argc == 3){ 277 | framea = argv[1]; 278 | frameb = argv[2]; 279 | } 280 | else if (argc == 1) 281 | using_specific_chain = false; 282 | else{ 283 | ROS_INFO("TF_Monitor: usage: tf_monitor framea frameb"); 284 | return -1; 285 | } 286 | 287 | std::string searched_param; 288 | std::string tf_prefix; 289 | ros::NodeHandle local_nh("~"); 290 | local_nh.searchParam("tf_prefix", searched_param); 291 | local_nh.getParam(searched_param, tf_prefix); 292 | 293 | 294 | //Make sure we don't start before recieving time when in simtime 295 | int iterations = 0; 296 | while (ros::Time::now() == ros::Time()) 297 | { 298 | if (++iterations > 10) 299 | { 300 | ROS_INFO("tf_monitor waiting for time to be published"); 301 | iterations = 0; 302 | } 303 | ros::WallDuration(0.1).sleep(); 304 | } 305 | 306 | ros::NodeHandle nh; 307 | boost::thread spinner( boost::bind( &ros::spin )); 308 | TFMonitor monitor(using_specific_chain, tf::resolve(tf_prefix, framea), tf::resolve(tf_prefix, frameb)); 309 | monitor.spin(); 310 | spinner.join(); 311 | return 0; 312 | 313 | } 314 | -------------------------------------------------------------------------------- /include/tf/transform_datatypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Willow Garage, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the Willow Garage, Inc. nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** \author Tully Foote */ 31 | 32 | #ifndef TF_TRANSFORM_DATATYPES_H 33 | #define TF_TRANSFORM_DATATYPES_H 34 | 35 | #include 36 | #include "geometry_msgs/PointStamped.h" 37 | #include "geometry_msgs/Vector3Stamped.h" 38 | #include "geometry_msgs/QuaternionStamped.h" 39 | #include "geometry_msgs/TransformStamped.h" 40 | #include "geometry_msgs/PoseStamped.h" 41 | #include "tf/LinearMath/Transform.h" 42 | #include "ros/time.h" 43 | 44 | #include "ros/console.h" 45 | 46 | namespace tf 47 | { 48 | 49 | typedef tf::Vector3 Point; 50 | typedef tf::Transform Pose; 51 | 52 | static const double QUATERNION_TOLERANCE = 0.1f; 53 | 54 | /** \brief The data type which will be cross compatable with geometry_msgs 55 | * This is the tf datatype equivilant of a MessageStamped */ 56 | template 57 | class Stamped : public T{ 58 | public: 59 | ros::Time stamp_; ///< The timestamp associated with this data 60 | std::string frame_id_; ///< The frame_id associated this data 61 | 62 | /** Default constructor */ 63 | Stamped() :frame_id_ ("NO_ID_STAMPED_DEFAULT_CONSTRUCTION"){}; //Default constructor used only for preallocation 64 | 65 | /** Full constructor */ 66 | Stamped(const T& input, const ros::Time& timestamp, const std::string & frame_id) : 67 | T (input), stamp_ ( timestamp ), frame_id_ (frame_id){ } ; 68 | 69 | /** Set the data element */ 70 | void setData(const T& input){*static_cast(this) = input;}; 71 | }; 72 | 73 | /** \brief Comparison Operator for Stamped datatypes */ 74 | template 75 | bool operator==(const Stamped &a, const Stamped &b) { 76 | return a.frame_id_ == b.frame_id_ && a.stamp_ == b.stamp_ && static_cast(a) == static_cast(b); 77 | }; 78 | 79 | 80 | /** \brief The Stamped Transform datatype used by tf */ 81 | class StampedTransform : public tf::Transform 82 | { 83 | public: 84 | ros::Time stamp_; ///< The timestamp associated with this transform 85 | std::string frame_id_; ///< The frame_id of the coordinate frame in which this transform is defined 86 | std::string child_frame_id_; ///< The frame_id of the coordinate frame this transform defines 87 | StampedTransform(const tf::Transform& input, const ros::Time& timestamp, const std::string & frame_id, const std::string & child_frame_id): 88 | tf::Transform (input), stamp_ ( timestamp ), frame_id_ (frame_id), child_frame_id_(child_frame_id){ }; 89 | 90 | /** \brief Default constructor only to be used for preallocation */ 91 | StampedTransform() { }; 92 | 93 | /** \brief Set the inherited Traonsform data */ 94 | void setData(const tf::Transform& input){*static_cast(this) = input;}; 95 | 96 | }; 97 | 98 | /** \brief Comparison operator for StampedTransform */ 99 | static inline bool operator==(const StampedTransform &a, const StampedTransform &b) { 100 | return a.frame_id_ == b.frame_id_ && a.child_frame_id_ == b.child_frame_id_ && a.stamp_ == b.stamp_ && static_cast(a) == static_cast(b); 101 | }; 102 | 103 | 104 | /** \brief convert Quaternion msg to Quaternion */ 105 | static inline void quaternionMsgToTF(const geometry_msgs::Quaternion& msg, Quaternion& bt) 106 | { 107 | bt = Quaternion(msg.x, msg.y, msg.z, msg.w); 108 | if (fabs(bt.length2() - 1 ) > QUATERNION_TOLERANCE) 109 | { 110 | ROS_WARN("MSG to TF: Quaternion Not Properly Normalized"); 111 | bt.normalize(); 112 | } 113 | }; 114 | /** \brief convert Quaternion to Quaternion msg*/ 115 | static inline void quaternionTFToMsg(const Quaternion& bt, geometry_msgs::Quaternion& msg) 116 | { 117 | if (fabs(bt.length2() - 1 ) > QUATERNION_TOLERANCE) 118 | { 119 | ROS_WARN("TF to MSG: Quaternion Not Properly Normalized"); 120 | Quaternion bt_temp = bt; 121 | bt_temp.normalize(); 122 | msg.x = bt_temp.x(); msg.y = bt_temp.y(); msg.z = bt_temp.z(); msg.w = bt_temp.w(); 123 | } 124 | else 125 | { 126 | msg.x = bt.x(); msg.y = bt.y(); msg.z = bt.z(); msg.w = bt.w(); 127 | } 128 | }; 129 | 130 | /** \brief Helper function for getting yaw from a Quaternion */ 131 | static inline double getYaw(const Quaternion& bt_q){ 132 | tfScalar useless_pitch, useless_roll, yaw; 133 | tf::Matrix3x3(bt_q).getRPY( useless_roll, useless_pitch,yaw); 134 | return yaw; 135 | } 136 | 137 | /** \brief Helper function for getting yaw from a Quaternion message*/ 138 | static inline double getYaw(const geometry_msgs::Quaternion& msg_q){ 139 | Quaternion bt_q; 140 | quaternionMsgToTF(msg_q, bt_q); 141 | return getYaw(bt_q); 142 | } 143 | 144 | /** \brief construct a Quaternion from Fixed angles 145 | * \param roll The roll about the X axis 146 | * \param pitch The pitch about the Y axis 147 | * \param yaw The yaw about the Z axis 148 | * \return The quaternion constructed 149 | */ 150 | static inline tf::Quaternion createQuaternionFromRPY(double roll,double pitch,double yaw){ 151 | Quaternion q; 152 | q.setRPY(roll, pitch, yaw); 153 | return q; 154 | } 155 | 156 | /** \brief construct a Quaternion from yaw only 157 | * \param yaw The yaw about the Z axis 158 | * \return The quaternion constructed 159 | */ 160 | static inline Quaternion createQuaternionFromYaw(double yaw){ 161 | Quaternion q; 162 | q.setRPY(0.0, 0.0, yaw); 163 | return q; 164 | } 165 | 166 | /** \brief construct a Quaternion Message from yaw only 167 | * \param yaw The yaw about the Z axis 168 | * \return The quaternion constructed 169 | */ 170 | static inline geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw){ 171 | Quaternion q; 172 | q.setRPY(0.0, 0.0, yaw); 173 | geometry_msgs::Quaternion q_msg; 174 | quaternionTFToMsg(q, q_msg); 175 | return q_msg; 176 | } 177 | 178 | /** \brief construct a Quaternion Message from Fixed angles 179 | * \param roll The roll about the X axis 180 | * \param pitch The pitch about the Y axis 181 | * \param yaw The yaw about the Z axis 182 | * \return The quaternion constructed 183 | */ 184 | static inline geometry_msgs::Quaternion createQuaternionMsgFromRollPitchYaw(double roll,double pitch,double yaw){ 185 | geometry_msgs::Quaternion q_msg; 186 | quaternionTFToMsg(createQuaternionFromRPY(roll, pitch, yaw), q_msg); 187 | return q_msg; 188 | } 189 | 190 | /** \brief construct an Identity Quaternion 191 | * \return The quaternion constructed 192 | */ 193 | static inline tf::Quaternion createIdentityQuaternion() 194 | { 195 | Quaternion q; 196 | q.setRPY(0,0,0); 197 | return q; 198 | }; 199 | 200 | /** \brief convert QuaternionStamped msg to Stamped */ 201 | static inline void quaternionStampedMsgToTF(const geometry_msgs::QuaternionStamped & msg, Stamped& bt) 202 | {quaternionMsgToTF(msg.quaternion, bt); bt.stamp_ = msg.header.stamp; bt.frame_id_ = msg.header.frame_id;}; 203 | /** \brief convert Stamped to QuaternionStamped msg*/ 204 | static inline void quaternionStampedTFToMsg(const Stamped& bt, geometry_msgs::QuaternionStamped & msg) 205 | {quaternionTFToMsg(bt, msg.quaternion); msg.header.stamp = bt.stamp_; msg.header.frame_id = bt.frame_id_;}; 206 | 207 | /** \brief convert Vector3 msg to Vector3 */ 208 | static inline void vector3MsgToTF(const geometry_msgs::Vector3& msg_v, Vector3& bt_v) {bt_v = Vector3(msg_v.x, msg_v.y, msg_v.z);}; 209 | /** \brief convert Vector3 to Vector3 msg*/ 210 | static inline void vector3TFToMsg(const Vector3& bt_v, geometry_msgs::Vector3& msg_v) {msg_v.x = bt_v.x(); msg_v.y = bt_v.y(); msg_v.z = bt_v.z();}; 211 | 212 | /** \brief convert Vector3Stamped msg to Stamped */ 213 | static inline void vector3StampedMsgToTF(const geometry_msgs::Vector3Stamped & msg, Stamped& bt) 214 | {vector3MsgToTF(msg.vector, bt); bt.stamp_ = msg.header.stamp; bt.frame_id_ = msg.header.frame_id;}; 215 | /** \brief convert Stamped to Vector3Stamped msg*/ 216 | static inline void vector3StampedTFToMsg(const Stamped& bt, geometry_msgs::Vector3Stamped & msg) 217 | {vector3TFToMsg(bt, msg.vector); msg.header.stamp = bt.stamp_; msg.header.frame_id = bt.frame_id_;}; 218 | 219 | 220 | /** \brief convert Point msg to Point */ 221 | static inline void pointMsgToTF(const geometry_msgs::Point& msg_v, Point& bt_v) {bt_v = Vector3(msg_v.x, msg_v.y, msg_v.z);}; 222 | /** \brief convert Point to Point msg*/ 223 | static inline void pointTFToMsg(const Point& bt_v, geometry_msgs::Point& msg_v) {msg_v.x = bt_v.x(); msg_v.y = bt_v.y(); msg_v.z = bt_v.z();}; 224 | 225 | /** \brief convert PointStamped msg to Stamped */ 226 | static inline void pointStampedMsgToTF(const geometry_msgs::PointStamped & msg, Stamped& bt) 227 | {pointMsgToTF(msg.point, bt); bt.stamp_ = msg.header.stamp; bt.frame_id_ = msg.header.frame_id;}; 228 | /** \brief convert Stamped to PointStamped msg*/ 229 | static inline void pointStampedTFToMsg(const Stamped& bt, geometry_msgs::PointStamped & msg) 230 | {pointTFToMsg(bt, msg.point); msg.header.stamp = bt.stamp_; msg.header.frame_id = bt.frame_id_;}; 231 | 232 | 233 | /** \brief convert Transform msg to Transform */ 234 | static inline void transformMsgToTF(const geometry_msgs::Transform& msg, Transform& bt) 235 | {bt = Transform(Quaternion(msg.rotation.x, msg.rotation.y, msg.rotation.z, msg.rotation.w), Vector3(msg.translation.x, msg.translation.y, msg.translation.z));}; 236 | /** \brief convert Transform to Transform msg*/ 237 | static inline void transformTFToMsg(const Transform& bt, geometry_msgs::Transform& msg) 238 | {vector3TFToMsg(bt.getOrigin(), msg.translation); quaternionTFToMsg(bt.getRotation(), msg.rotation);}; 239 | 240 | /** \brief convert TransformStamped msg to tf::StampedTransform */ 241 | static inline void transformStampedMsgToTF(const geometry_msgs::TransformStamped & msg, StampedTransform& bt) 242 | {transformMsgToTF(msg.transform, bt); bt.stamp_ = msg.header.stamp; bt.frame_id_ = msg.header.frame_id; bt.child_frame_id_ = msg.child_frame_id;}; 243 | /** \brief convert tf::StampedTransform to TransformStamped msg*/ 244 | static inline void transformStampedTFToMsg(const StampedTransform& bt, geometry_msgs::TransformStamped & msg) 245 | {transformTFToMsg(bt, msg.transform); msg.header.stamp = bt.stamp_; msg.header.frame_id = bt.frame_id_; msg.child_frame_id = bt.child_frame_id_;}; 246 | 247 | /** \brief convert Pose msg to Pose */ 248 | static inline void poseMsgToTF(const geometry_msgs::Pose& msg, Pose& bt) 249 | {bt = Transform(Quaternion(msg.orientation.x, msg.orientation.y, msg.orientation.z, msg.orientation.w), Vector3(msg.position.x, msg.position.y, msg.position.z));}; 250 | /** \brief convert Pose to Pose msg*/ 251 | static inline void poseTFToMsg(const Pose& bt, geometry_msgs::Pose& msg) 252 | {pointTFToMsg(bt.getOrigin(), msg.position); quaternionTFToMsg(bt.getRotation(), msg.orientation);}; 253 | 254 | /** \brief convert PoseStamped msg to Stamped */ 255 | static inline void poseStampedMsgToTF(const geometry_msgs::PoseStamped & msg, Stamped& bt) 256 | {poseMsgToTF(msg.pose, bt); bt.stamp_ = msg.header.stamp; bt.frame_id_ = msg.header.frame_id;}; 257 | /** \brief convert Stamped to PoseStamped msg*/ 258 | static inline void poseStampedTFToMsg(const Stamped& bt, geometry_msgs::PoseStamped & msg) 259 | {poseTFToMsg(bt, msg.pose); msg.header.stamp = bt.stamp_; msg.header.frame_id = bt.frame_id_;}; 260 | 261 | 262 | 263 | 264 | 265 | 266 | } 267 | #endif //TF_TRANSFORM_DATATYPES_H 268 | --------------------------------------------------------------------------------