\(\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\:}\)
TF in ROS
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
- A rigid-body transformation generally is composed of two parts
- 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
- Quaternions
- Rotation matrices (elements of \(SO(3)\))
- Fixed axis rotations using standard roll-pitch-yaw i.e. fixed axis XYZ
Euler angles.
- 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.
- 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.
- 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:
- Always avoid disconnected
/tf
trees
- Never have frames with more than one parent
- 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.