\(\def\dt{\Delta t}\)
\(\newcommand{\transpose}[0]{^\mathrm{T}}\)
\(\newcommand{\half}[0]{\tfrac{1}{2}}\)
\(\newcommand{\Half}[0]{\frac{1}{2}}\)
\(\newcommand{\norm}[1]{\left\lVert#1\right\rVert}\)
\(\newcommand\given[1][]{\:#1\vert\:}\)
UP | HOME

TF in ROS

Table of Contents

1 What is tf?

  • tf is the ROS way of handling geometry between coordinate systems
  • It has tools for tracking how transformations between frames change over time
  • The API's allow you to ask for raw transform data between arbitrary frames, and they can transform many basic ROS messages for you
    • All of these transformations are time-specific i.e. you must ask for the transform at a particular time!
    • An example would be I want this geometry_msgs/Point transformed from its original frame to a new frame using the transform at a particular time

2 Overall concepts

  • tf is a totally distributed system where all of the tf data is available to any node that wants it, and any node can add information to the /tf topic
  • Every coordinate system in the ROS world has a frame_id
    • A unique name that identifies each frame
    • All of the API's use the frame_id to identify frames of interest
    • The frame_id follows a similar convention to general ROS names
  • Nodes asking to receive the data from the tf tree (via a listener) automatically buffer 10 seconds of tree history
    • Allows looking up transforms in the past

2.1 tf Tree

  • All frames in the ROS world must fit together as a single tree
  • Each node in the tree is a frame, and each edge of the tree is a transformation
  • Each frame can have only one parent
  • Each frame can have unlimited children
  • Nodes that are producing tree information should not be sending out conflicting transforms i.e. each edge of the tree should only have one source!

2.2 Listeners

  • listeners are created if a node is going to need information from the tf tree
    • Is this node going to transform data?
    • Does this node need to lookup how coordinates have changed over time?
  • A listener automatically subscribes and buffers all information coming in over the /tf topic
  • The methods for querying transforms and for transforming standard datatypes are usually accessed via a listener object

2.3 Broadcasters

  • If a node has additional information to add to the tf tree, then the node uses a broadcaster
  • ROS systems can have many broadcasters distributed across many nodes each providing different parts of the tf tree

3 How does tf represent transformations

  • A rigid-body transformation generally is composed of two parts
    • Translation
    • Rotation
  • ROS uses special datatypes for each of these parts
    • This matters more in C++!
    • In Python, everything is either a numpy array or built-in tuples/lists!
  • Translations are just vectors with 3 elements
  • Rotations have many different representations, and ROS can work with several different conventions

3.1 Conventions

  • All distances should be in meters
  • All angles should be in radians
  • All coordinate systems are right-handed
  • There are standards for how frames should be attached to various components on robots
    • On mobile robots, X is forward, Y is left, Z is up
    • For cameras, Z is forward, X is right, Y is down, and frame_id typically has _optical as a suffix
  • For rotation, ROS supports four descriptions of orientation. In their preferred order, they are
    1. Quaternions
    2. Rotation matrices (elements of \(SO(3)\))
    3. Fixed axis rotations using standard roll-pitch-yaw i.e. fixed axis XYZ Euler angles.
    4. Standard, relative-axis ZYX Euler angles
  • Note that quaternions, RPY, and ZYX Euler angles are all parameterizations of \(SO(3)\)
    • Not all tools in ROS will support all four representations
  • A transform is specified in the direction that will transform frame_id into child_frame_id; this transform will transform data from the child_frame_id into the frame_id

4 tf vs. tf2

  • tf2 is a new API that is meant to address some of the shortcomings of the original tf
  • The migration page has many of the changes between the two
    • One of the most relevant changes was the inclusion of the /tf_static topic. We don't need to constantly re-publish static transforms anymore.
  • As of Hydro, all of tf is handled under-the-hood by tf2
  • Unfortunately, the production of the documentation for tf2 took quite a long time.
    • As a result many people have generally just used the tf API
    • The documentation is now there, and I suspect we will now start to see people transitioning to using tf2 more and more, but it will take years for the transition to really take hold.
  • This used to be a major point of confusion and issues, but it seems to be mostly cleaned up at this point.
  • I'd recommend using tf2 APIs whenever possible in any newly developed applications.
    • That said, nearly all concepts from tf still exist and much of the tf documentation is still relevant – feel free to study tf documentation, examples, and Q&As
  • It is well worth reading the tf2 Migration guide that discusses why the re-design took place and what the major changes were, as well as the tf2 Design guide that includes details on how the re-design was actually implemented.
  • Another big change worth mentioning is that now all of the math required for tracking a tf tree and querying that tree has been broken out from ROS, and custom tf-specific datatypes are used everywhere.
    • One could, in principle, use tf2 without using ROS at all
    • There are specific APIs for using tf2 with some common other geometry libraries (e.g. bullet, Eigen, and KDL) and for geometry-related ROS messages (e.g. sensor_msgs and geometry_msgs)
    • A ROS package is provided that wraps the core tf2 functionality with ROS-specific behavior (e.g. automatically subscribing to the /tf topic and maintaining updates to the /tf tree). This package is called tf2_ros. It has full documentation for both its Python and C++ APIs.

5 tf Command line tools

  • tf_monitor Print information about the transform tree in general or about a specific transform
  • tf_echo print transformation values between two frames
  • static_transform_publisher send a constant transform between two frames at a specified frequency (or sends the transform on the /tf_static topic if using tf2
  • view_frames create a PDF of all or part of your tf tree
  • roswtf the standard ROS command line debugging tool can report information about missing transforms, disconnected trees, etc.

6 tf nodes

  • tf_remap for renaming frames
  • change_notifier listens to the /tf topic and republishes transforms that have changed by a given amount on the /tf_changes topic

7 tf API notes and tips

  • Likely, the most common issue with tf is related to time
    • You will often get "lookup exceptions"… these are caused by transforms being slightly out of sync. You try to look up a transform between two frames at a particular time, and the timing of when the necessary transforms were published is such that /tf refuses to interpolate/extrapolate that big of a time window.
      • Always use try-except blocks around transform calls
    • Be sure your listener is only being created once and that you aren't immediately trying to lookup a transform. The listener needs time to establish a connection and start filling its buffer.
    • In both C++ and Python, you can ask for transforms at the most recent possible time. Generally, this is a good idea!
  • Three rules to always follow:
    1. Always avoid disconnected /tf trees
    2. Never have frames with more than one parent
    3. Only provide a particular /tf transform in a single place
  • There is no concept of a world frame. The only frames that exist are frames that are explicitly put there. If you'd like to have a global, inertial frame that all other frames are relative to, then you need to create one.
  • rviz needs you to choose a single frame to consider as the Fixed frame. All data will be displayed relative to this frame. Not properly choosing a Fixed frame in rviz is likely the most common source of "I can't see my data in rviz" errors.
  • C++ API and documentations has many mentions of transforming datatypes between ROS geometry_msgs, tf, Eigen, and Bullet. This is much less of a problem in Python as everything is just Numpy or built-ins.
  • tf_prefix is being deprecated. It was originally designed for multi-robot scenarios, and it was meant to mimic the ROS namespace concepts. I would not recommend using this in future designs.

8 Useful webpages

8.1 tf specific

8.3 General help

Creative Commons License
ME 495: Embedded Systems in Robotics by Jarvis Schultz is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.