ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

tf_static frames not available in lookup_transform

asked 2021-09-10 21:16:41 -0600 gravatar image

updated 2021-09-10 22:08:02 -0600

Running ROS2 Foxy + Ubuntu 20.04

I am using TF2 with Python, rclpy

I created a few /tf_static transforms in my launch file, and I have dynamic frames in /tf. The corresponding kinemaitc chain looks correct when calling

$ ros2 run tf2_tools

$ evince frames.pdf

However, when I call the transform_lookup method (Python) and I pass in the frame names, I get a frame not found exception when I pass the name of a static frame as listed in /tf_static

Are tf_static frames available for access and correct transforming when calling transform_lookup?


This is working intermittantlly. I am running the same code / launch file, and sometimes I get the expected result, where all frames are available, and other times they are not available...

Any thoughts??

edit retag flag offensive close merge delete

3 Answers

Sort by ยป oldest newest most voted

answered 2023-02-16 17:22:22 -0600

AndyZe gravatar image

updated 2023-02-17 11:44:07 -0600

I found a workaround after about 8 hours of tears. The commented code is the first attempt that did not work.

              import asyncio
                tf_future = self.tf_buffer.wait_for_transform_async(

                rclpy.spin_until_future_complete(self, tf_future)
                print("Got it!")

#                world_to_reference_transform = self.tf_buffer.lookup_transform(
#                    pose_stamped.header.frame_id,
#                    reference_frame,
#                    rclpy.time.Time(),
#                    timeout=rclpy.duration.Duration(seconds=5.0))

                world_to_reference_transform =
edit flag offensive delete link more


+1 for wait_for_transform_async and spin_until_future_complete combo, as I think those helped my tf-buffer when it was struggling to lookup (I didn't have to use asyncio). I don't think these are needed particularly for static-stansform as I do see static tf is looked up without (albeit I'm still new to ros2, so mine is just another anecdote).

130s gravatar image 130s  ( 2023-05-05 15:11:05 -0600 )edit

answered 2023-05-04 16:35:10 -0600

130s gravatar image

updated 2023-05-04 16:36:18 -0600

My answer may be broader than what OP is asking, but because the initial motivation was the same for me to start tackling (static_transform not looked up via Python api in ROS2 for me), I share here what I found. It's embarrassing to admit that simple mistake.

Simply, I just had to run lookup AFTER spin at least once.

The following code is verified to lookup with turtle_tf2_py tutorial along with online tutorial ( Worked with static_transform too.

Btw, I still haven't had "try2" approach working (the one @AndyZe suggested). I doubt there _may_ be some issues as I see rclpy#989? No idea.


import asyncio
import time
import yaml

import rclpy
import tf2_ros
from tf2_ros import TransformException

class TfFramesFinder(rclpy.node.Node):
    def __init__(self):
        self._tf_buffer = tf2_ros.buffer.Buffer()
        self._tf_listener = tf2_ros.transform_listener.TransformListener(self._tf_buffer, self, spin_thread = False)

    def try3(self, target_f="kts2_table_frame", source_f="world"):
        self.get_logger().info("Getting it3!")
        _MAX_ATTEMPTS = 15
        _attempts = 0
        _transform_res = None
        while _attempts < _MAX_ATTEMPTS:
        _time_snapshot = rclpy.time.Time()
                self.get_logger().info("Looking up {}th time".format(_attempts))

                _transform_res = self._tf_buffer.lookup_transform(
            except TransformException as ex:
                if _MAX_ATTEMPTS < _attempts:
                    raise RuntimeError(
                        f"Could not transform '{target_f}' to '{source_f}' at the time '{_time_snapshot}'.: {ex}")
                    self.get_logger().warn("Failed {} times".format(_attempts))
            _attempts += 1
        self.get_logger().info("x: {}, y: {}".format(
            _transform_res.transform.translation.x, _transform_res.transform.translation.y))

    def try2(self, target_f="kts2_table_frame", source_f="world"):
        self.get_logger().info("Getting it!")
        tf_future = self._tf_buffer.wait_for_transform_async(
        self.get_logger().info("wait_for_transform_async returned. tf_future: {}".format(tf_future))
        rclpy.spin_until_future_complete(self, tf_future)
        self.get_logger().info("spin_until_future_complete returned.")
        world_to_reference_transform =
            target_f, source_f, rclpy.time.Time()))

node = TfFramesFinder()
#frame_parent = "turtle2"
frame_parent = "world"
#frame_child = "turtle2"
frame_child = "static_3"
except KeyboardInterrupt as e:
    print("k/b interrupted")

edit flag offensive delete link more


My takeaway is to avoid Python in ROS2 like the plague :/

AndyZe gravatar image AndyZe  ( 2023-05-05 15:58:41 -0600 )edit

answered 2021-09-12 02:12:52 -0600

tfoote gravatar image

The lookup shouldn't care about whether the frame is static or not for detecting it. The best way to think of "frame not found" is that the buffer hasn't heard about that frame yet.

Without seeing anything more specific my first guess is that you are not giving your buffer enough time to initialize and connect on all the topics and for the static transform publisher to publish the static frame information. This will cause a race condition which will give you intermittent behavior.

The most common fix for this is to catch the exception and try again on the next cycle, or explicitly give time to initialize the transform buffer. But making your algorithm robust to the occasional transform data failure is strongly recommended if possible. This is a common race condition on startup of a distributed system.

edit flag offensive delete link more

Question Tools

1 follower


Asked: 2021-09-10 21:16:41 -0600

Seen: 665 times

Last updated: May 04