0
$\begingroup$

Rosanswers logo

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 view_frames.py

$ 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?

UPDATE:

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??


Originally posted by [email protected] on ROS Answers with karma: 71 on 2021-09-10

Post score: 1

$\endgroup$

2 Answers 2

1
$\begingroup$

Rosanswers logo

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 (docs.ros.org/en/galactic/Tutorials/Intermediate/Tf2). 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.

#!/usr/bin/python3

import asyncio
import time
import yaml

import rclpy
import tf2_ros
from tf2_ros import TransformException

class TfFramesFinder(rclpy.node.Node):
    def __init__(self):
        super().__init__("tf2_frames_finder")
        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()
            try:
                self.get_logger().info("Looking up {}th time".format(_attempts))

                _transform_res = self._tf_buffer.lookup_transform(
                    target_f,
                    source_f,
                    _time_snapshot)
                break
            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}")
                else:
                    self.get_logger().warn("Failed {} times".format(_attempts))
                    time.sleep(1)
            _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(
            target_frame=target_f,
            source_frame=source_f,
            time=rclpy.time.Time())
        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 = asyncio.run(self._tf_buffer.lookup_transform_async(
            target_f, source_f, rclpy.time.Time()))

rclpy.init()
node = TfFramesFinder()
#frame_parent = "turtle2"
frame_parent = "world"
#frame_child = "turtle2"
frame_child = "static_3"
try:
    rclpy.spin_once(node)
    node.try3(
        target_f=frame_parent,
        source_f=frame_child)
except KeyboardInterrupt as e:
    print("k/b interrupted")

node.destroy_node()
rclpy.shutdown()

Originally posted by 130s with karma: 10937 on 2023-05-04

This answer was NOT ACCEPTED on the original site

Post score: 1


Original comments

Comment by AndyZe on 2023-05-05:
My takeaway is to avoid Python in ROS2 like the plague :/

$\endgroup$
0
$\begingroup$

Rosanswers logo

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(
                    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()
                ))

Originally posted by AndyZe with karma: 2331 on 2023-02-16

This answer was NOT ACCEPTED on the original site

Post score: 1


Original comments

Comment by 130s on 2023-05-05:
+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).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.