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

tf2 lookup_transform target_frame does not exist

asked 2022-03-23 18:49:56 -0500

mauro gravatar image

updated 2022-03-23 18:52:30 -0500

I'm having troubles getting the static transform between two links which is broadcasted by the robot_state_publisher node.

The simple example below shows my problem. I expected tf_buffer.lookup_transform() to return the static transform between the base link and the gps antenna but I always get an exception "gps_antenna" passed to lookupTransform argument target_frame does not exist.

I read somewhere that this might be because the tf_buffer needs time to "fill up", but I haven't found a way to give it that time. The only way I could get things to work is by creating a timer and setting the get_dist() method as a callback. However, since I'm just looking up a static transform once, that approach doesn't seem right.

Can anybody help me out? Thanks a lot!

class Example(Node):

    def __init__(self):
        super().__init__("example_node")
        self.tf_buffer = Buffer()
        self.tf_listener = TransformListener(self.tf_buffer, self)
        self.dist_gps = self.get_dist()

    def get_dist(self):
        source_frame = "base_link"
        target_frame = "gps_antenna"
        dist = 0

        try:
            now = rclpy.time.Time()
            trans = self.tf_buffer.lookup_transform(
                target_frame,
                source_frame,
                now)
        except TransformException as ex:
             self.get_logger().error(ex)
        else:
            dist =  trans.transform.translation.x

        return dist

def main(args=None):
    rclpy.init(args=args)

    example = Example()
    rclpy.spin(example)

    example.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Note: I'm running ROS2 Galactic in a docker container (osrf/ros:galactic-desktop)

edit retag flag offensive close merge delete

Comments

can you confirm that the transform does exist as you would expect through other means? what does your tf tree look like (ros2 run tf2_tools view_frames)? Can you query tf directly and get a response (ros2 run tf2_ros tf2_echo base_link gps_antenna)?

shonigmann gravatar image shonigmann  ( 2022-03-25 14:33:07 -0500 )edit

Yes, the transform exists. The tf tree looks correct and I get a response with ros2 run tf2_ros tf2_echo base_link gps_antenna. I also tried the async_wait_for_transform.pyexample from the geometry2 examples, which works fine as well (https://github.com/ros2/geometry2/blo...). The only issue is that I don't need this to run regularly but only once.

mauro gravatar image mauro  ( 2022-03-25 17:13:05 -0500 )edit

Hi, have you solved this issue?

songanz gravatar image songanz  ( 2022-12-13 00:24:31 -0500 )edit

Unfortunately not, but I also haven't tried much since I posted the issue.

mauro gravatar image mauro  ( 2022-12-13 01:44:58 -0500 )edit
1

I have the same issue in ROS2 Humble. I suspect this is a threading issue.

AndyZe gravatar image AndyZe  ( 2023-02-14 12:25:06 -0500 )edit

2 Answers

Sort by ยป oldest newest most voted
0

answered 2023-02-17 08:39:54 -0500

AndyZe gravatar image

updated 2023-02-17 11:43:27 -0500

Try this. The commented code is the first attempt that did not work.

              import asyncio
...
                tf_future = self.tf_buffer.wait_for_transform_async(
                    target_frame=pose_stamped.header.frame_id,
                    source_frame=reference_frame,
                    time=rclpy.time.Time()
                )

                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 = asyncio.run(self.tf_buffer.lookup_transform_async(
                    pose_stamped.header.frame_id,
                    reference_frame,
                    rclpy.time.Time()
                ))
edit flag offensive delete link more
0

answered 2023-02-14 16:26:51 -0500

tfoote gravatar image

I read somewhere that this might be because the tf_buffer needs time to "fill up", but I haven't found a way to give it that time. The only way I could get things to work is by creating a timer and setting the get_dist() method as a callback. However, since I'm just looking up a static transform once, that approach doesn't seem right.

That's correct.

You create the TransformListener and then immediately query it with zero delay.

    self.tf_listener = TransformListener(self.tf_buffer, self)
    self.dist_gps = self.get_dist()

As such it will likely have received zero messages from any broadcaster not to mention a specific one that you're looking for.

A standard approach for this sort of thing would be to try to look up the trasnform and if it fails, wait a little bit and try again. Once you have confirmed receipt your startup process has completed.

However at the higher level I would question if this is the right approach overall. You are explicitly baking in your assumption about the source of the specific coordinate frames to your algorithm. A more robust way to approach this is to simply query the TransformListener for the transform when you need it, and don't make an assumption about the source or structure of the tree nor the frames that you're querying about. You can then make the frames parameters too. I bet that you won't see a significant performance improvement from precaching this one transform.

Of course when you do this make sure to use a try block and catch an error at or near startup before the TransformListener has received enough messages.

edit flag offensive delete link more

Question Tools

4 followers

Stats

Asked: 2022-03-23 18:49:56 -0500

Seen: 594 times

Last updated: Feb 17 '23