Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove many extra conversions from Matrix3x3 to Quaternion #741

Open
wants to merge 46 commits into
base: rolling
Choose a base branch
from

Conversation

kyle-basis
Copy link

When calling setTransform(), rotation data would flow as ros_msgs::Quaternion -> tf2::Quaternion -> tf2::Matrix3x3, then reconvert to tf2::Quaternion 12 times to check validity of the rotation, then do one final conversion to actually store it inside the buffer.

When calling lookupTransform(), we'd go from tf2::Quaternion->tf2::Matrix3x3->tf2::Quaternion (x4)->ros_msgs::Quaternion.

I've fixed this in two parts -

  1. Change setTransformImpl and lookupTransformImpl to take a vector+quat, eliminating both the roundtrip through tf2::Transfrom and the possible footgun of getRotation
  2. In places that still need a tf2::Transform, only call getRotation() once, and reusing the result afterwards
  3. I did not change the reference frame version of lookupTransform nor lookupVelocity, those are far less mechanical changes - would appreciate if someone fixed these up in a followup PR

Some notes:

  1. getRotation() is poorly named - would recommend deprecating and renaming to calculateRotation or similar. I shot myself in the foot implementing tf2_basis and not realizing that origin is by reference and rotation is by value.
  2. Part of the reason this happened is Draft: Break geometry_msgs dependency in tf2 #732 - there are conversion functions that actually do the conversion correctly (not converting once per component) but they are unable to be used due to circular reference
  3. It feels like tf2::Transform should really be a vector+quat, but it would be an API change

@@ -382,12 +382,15 @@ class BufferCore : public BufferCoreInterface
std::string allFramesAsStringNoLock() const;

bool setTransformImpl(
const tf2::Transform & transform_in, const std::string frame_id,
const std::string child_frame_id, const TimePoint stamp,
const tf2::Vector3 & origin_in, const tf2::Quaternion & rotation_in, const std::string & frame_id,
Copy link
Author

@kyle-basis kyle-basis Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I changed the string values into references - this really should be a std::string_view - it shouldn't break any code, and ROS is c++17 now - but it's somewhat a moot point as SSO will kick in for most char* conversions.

@kyle-basis
Copy link
Author

@kscottz - tagging as requested

@kyle-basis
Copy link
Author

Note: I've broken all tests, I'll fix it - the base idea is sound, however

@kyle-basis
Copy link
Author

Never mind - appears to just be tests using hardcoded quaternion values, rather than actually checking equality - I'm happy to fix this either by manually fixing the test quaternions, or by doing some nicer equality check. This does mean that downstream users will sometimes get out different (but equiv) rotations from the library than they got before.

@kyle-basis
Copy link
Author

Pushed what should hopefully fix the failing tests. q == -q

test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
test_tf2/test/buffer_core_test.cpp Outdated Show resolved Hide resolved
tf2/src/buffer_core.cpp Outdated Show resolved Hide resolved
tf2/src/buffer_core.cpp Show resolved Hide resolved
@kyle-basis
Copy link
Author

Hit a failed test in CI, I don't think this is my fault - ci flake?

02:29:00 5:  - /usr/bin/python3 -m launch_testing.launch_test /tmp/ws/src/geometry2/test_tf2/test/buffer_client_tester.launch.py --junit-xml=/tmp/ws/test_results/test_tf2/test_buffer_client_tester.launch.py.xunit.xml --package-name=test_tf2
02:29:00 5: [INFO] [launch]: All log files can be found below /home/buildfarm/.ros/log/2024-12-04-02-29-00-675074-4d63d25031d7-4049
02:29:00 5: [INFO] [launch]: Default logging verbosity is set to INFO
02:29:00 5: test_termination (test_tf2.TestBufferClient.test_termination) ... [INFO] [static_transform_publisher-1]: process started with pid [4053]
02:29:00 5: [INFO] [test_buffer_server-2]: process started with pid [4054]
02:29:00 5: [INFO] [test_buffer_client-3]: process started with pid [4055]
02:29:00 5: [INFO] [python3-4]: process started with pid [4056]
02:29:00 5: [static_transform_publisher-1] [INFO] [1733308140.776567271] [static_transform_publisher_VNHQnJSRyXBSDNpK]: Spinning until stopped - publishing transform
02:29:00 5: [static_transform_publisher-1] translation: ('5.000000', '6.000000', '7.000000')
02:29:00 5: [static_transform_publisher-1] rotation: ('0.000000', '0.000000', '0.000000', '1.000000')
02:29:00 5: [static_transform_publisher-1] from 'a' to 'b'
02:29:00 5: [test_buffer_client-3] [==========] Running 2 tests from 1 test suite.
02:29:01 5: [test_buffer_client-3] [----------] Global test environment set-up.
02:29:01 5: [test_buffer_client-3] [----------] 2 tests from tf2_ros
02:29:01 5: [test_buffer_client-3] [ RUN      ] tf2_ros.buffer_client
02:29:01 5: [test_buffer_client-3] FOOO
02:29:01 5: [test_buffer_client-3] [INFO] [1733308142.718698560] [tf_action_node]: p1: (0.00, 0.00, 0.00), p2: (-5.00, -6.00, -7.00)
02:29:02 5: [test_buffer_client-3] [       OK ] tf2_ros.buffer_client (1014 ms)
02:29:02 5: [test_buffer_client-3] [ RUN      ] tf2_ros.buffer_client_different_types
02:29:02 5: [test_buffer_client-3] [ERROR] [1733308142.795791742] [tf_action_node]: Bullet: (-1.0000, 0.0000, 0.0000)
02:29:02 5: [test_buffer_client-3] [ERROR] [1733308142.795866165] [tf_action_node]: KDL: (0.0000, 0.0000, 0.0000)
02:29:02 5: [test_buffer_client-3] /tmp/ws/src/geometry2/test_tf2/test/test_buffer_client.cpp:127: Failure
02:29:02 5: [test_buffer_client-3] The difference between b1[0] and -5.0 is 4, which exceeds EPS, where
02:29:02 5: [test_buffer_client-3] b1[0] evaluates to -1,
02:29:02 5: [test_buffer_client-3] -5.0 evaluates to -5, and
02:29:02 5: [test_buffer_client-3] EPS evaluates to 0.001.
02:29:02 5: [test_buffer_client-3] 
02:29:02 5: [test_buffer_client-3] /tmp/ws/src/geometry2/test_tf2/test/test_buffer_client.cpp:128: Failure
02:29:02 5: [test_buffer_client-3] The difference between b1[1] and -6.0 is 6, which exceeds EPS, where
02:29:02 5: [test_buffer_client-3] b1[1] evaluates to 0,
02:29:02 5: [test_buffer_client-3] -6.0 evaluates to -6, and
02:29:02 5: [test_buffer_client-3] EPS evaluates to 0.001.
02:29:02 5: [test_buffer_client-3] 
02:29:02 5: [test_buffer_client-3] /tmp/ws/src/geometry2/test_tf2/test/test_buffer_client.cpp:129: Failure
02:29:02 5: [test_buffer_client-3] The difference between b1[2] and -7.0 is 7, which exceeds EPS, where
02:29:02 5: [test_buffer_client-3] b1[2] evaluates to 0,
02:29:02 5: [test_buffer_client-3] -7.0 evaluates to -7, and
02:29:02 5: [test_buffer_client-3] EPS evaluates to 0.001.
02:29:02 5: [test_buffer_client-3] 
02:29:02 5: [test_buffer_client-3] [  FAILED  ] tf2_ros.buffer_client_different_types (74 ms)
02:29:02 5: [test_buffer_client-3] [----------] 2 tests from tf2_ros (1089 ms total)
02:29:02 5: [test_buffer_client-3] 
02:29:02 5: [test_buffer_client-3] [----------] Global test environment tear-down
02:29:02 5: [test_buffer_client-3] [==========] 2 tests from 1 test suite ran. (1089 ms total)
02:29:02 5: [test_buffer_client-3] [  PASSED  ] 1 test.
02:29:02 5: [test_buffer_client-3] [  FAILED  ] 1 test, listed below:
02:29:02 5: [test_buffer_client-3] [  FAILED  ] tf2_ros.buffer_client_different_types
02:29:02 5: [test_buffer_client-3] 
02:29:02 5: [test_buffer_client-3]  1 FAILED TEST

Locally:

[test_buffer_client-3] [==========] Running 2 tests from 1 test suite.
[test_buffer_client-3] [----------] Global test environment set-up.
[test_buffer_client-3] [----------] 2 tests from tf2_ros
[test_buffer_client-3] [ RUN      ] tf2_ros.buffer_client
[test_buffer_client-3] FOOO
[test_buffer_client-3] [INFO] [1733348400.995224948] [tf_action_node]: p1: (0.00, 0.00, 0.00), p2: (-5.00, -6.00, -7.00)
[test_buffer_client-3] [       OK ] tf2_ros.buffer_client (403 ms)
[test_buffer_client-3] [ RUN      ] tf2_ros.buffer_client_different_types
[test_buffer_client-3] [ERROR] [1733348401.032240829] [tf_action_node]: Bullet: (-5.0000, -6.0000, -7.0000)
[test_buffer_client-3] [ERROR] [1733348401.032402204] [tf_action_node]: KDL: (0.0000, 0.0000, 0.0000)
[test_buffer_client-3] [       OK ] tf2_ros.buffer_client_different_types (30 ms)
[test_buffer_client-3] [----------] 2 tests from tf2_ros (434 ms total)
[test_buffer_client-3] 
[test_buffer_client-3] [----------] Global test environment tear-down
[test_buffer_client-3] [==========] 2 tests from 1 test suite ran. (434 ms total)
[test_buffer_client-3] [  PASSED  ] 2 tests.
[INFO] [test_buffer_client-3]: process has finished cleanly [pid 254386]
ok

@kscottz
Copy link

kscottz commented Dec 4, 2024

Sorry about the delay, had a power outage this morning. I'll take a look but I am not super familiar with the internals of TF / Geometry. Thanks for sticking with this and trying to get it over the line. We really appreciate the help and could always use a few more contributors. ❤️

Note: I've broken all tests, I'll fix it - the base idea is sound, however

I figured you might hit a few flakey tests as anything that relies on float / double ends up being a bit brittle. I can't speak to any of these tests being historically flakey. I think setting the epsilons to five or six sig figs should be sufficient.

This could probably use some documentation

Absolutely. We're starting to have conversations about bringing in someone to address API doc deficiencies but that's still far off. I would need more familiarity to do it myself.

Never mind - appears to just be tests using hardcoded quaternion values, rather than actually checking equality - I'm happy to fix this either by manually fixing the test quaternions, or by doing some nicer equality check. This does mean that downstream users will sometimes get out different (but equiv) rotations from the library than they got before.

This one's on you and what you're up for. I would have to look at the individual tests to make a call. Please do keep in mind that more you change on the lower level code the more likely it is that it may break something up stream. Smaller deltas are always better and get finished faster. You can always circle back

isNan() or isValid() on Vector/Quaternion would be really nice for this, along with a normalization check on quaternion - this code doesn't belong here.

I'll need to look at these a bit more but probably warrants an issue with a good first issue tag.

@clalancette
Copy link
Contributor

I figured you might hit a few flakey tests as anything that relies on float / double ends up being a bit brittle. I can't speak to any of these tests being historically flakey. I think setting the epsilons to five or six sig figs should be sufficient.

For what it is worth, we have no reports of flakey tests from our nightly CI in this package that I can remember, and nothing is reported in https://github.com/ros2/geometry2/issues . So while it is always possible that there is a flakey test here, we do not currently know about any.

EXPECT_NEAR(q_simple.quaternion.z, -1 * M_SQRT1_2, EPS);
EXPECT_NEAR(q_simple.quaternion.w, 0, EPS);
{
const geometry_msgs::msg::QuaternionStamped q_simple = tf_buffer->transform(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might help the reviewers, and readability, if you articulate what's going on in this function call. I've been looking at this for ten minutes and it still isn't quite clear what's going on.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an intermediate variable - I think the diff moved your comment though, which line specifically is confusing?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, that doesn't really tell me what the test does, it just tells me what the result should be. What would really help readers is something like, // Frame a and frame b differ by [x,y,z] and [p,q,r] at time t, check the resulting quat is <foo>

That's why I dislike this big ball of wax test pattern, when you write one function per test you can name the tests something sane like, "check_basic_frame_transform" and it all makes sense. The original author really should have at least left us a few bread crumb comments. 😢

Anyway, not your fault, thanks for putting up with this stuff.

Copy link

@kscottz kscottz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good to go once the uncrustify suggestions are merged.

@kscottz
Copy link

kscottz commented Dec 16, 2024

@kyle-basis I was busy last week but I wanted to check in on your progress here.

I added a couple of quick suggests to deal with uncrustify.

@kyle-basis I want to give you a shout out on the ROS channels. What would be really awesome to include is an estimate of much performance has improved with these changes. Would it be possible to time the test performance before and after these changes to see how things have improved?

kyle-basis and others added 3 commits December 16, 2024 15:20
Co-authored-by: Katherine Scott <[email protected]>
Signed-off-by: kyle-basis <[email protected]>
Co-authored-by: Katherine Scott <[email protected]>
Signed-off-by: kyle-basis <[email protected]>
Co-authored-by: jmachowinski <[email protected]>
Signed-off-by: kyle-basis <[email protected]>
@kyle-basis
Copy link
Author

Sorry, I've been busy with work on Basis and holiday stuff! Merged the suggestions. I can try and get performance numbers for before and after. It's tricky as I don't actually have a full ROS environment to test some of this stuff in.

}
}

namespace tf2 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
namespace tf2 {
namespace tf2
{

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦🏻

@jmachowinski
Copy link
Contributor

@kyle-basis
ament_uncrustify --reformat
is your friend here ;-)

@tfoote
Copy link
Contributor

tfoote commented Dec 18, 2024

getRotation() is poorly named - would recommend deprecating and renaming to calculateRotation or similar. I shot myself in the foot implementing tf2_basis and not realizing that origin is by reference and rotation is by value.

It feels like tf2::Transform should really be a vector+quat, but it would be an API change

Both of these are legacy from the LInearMath library that we have forked from Bullet to use internally: https://github.com/bulletphysics/bullet3/tree/master/src/LinearMath

We chose to internalize this library with renaming process to break the dependency and it's main goal is to only be internally used. But we didn't restrict access and so people have used it despite our preference for it not to be used directly. We've tried to keep this to be a direct fork instead of evolving it and improving it. We don't want to get into the business of maintaining yet another Linear Math library. The recommendation is to use Eigen or any other one of your own choice instead, leveraging the templated interface to tf2.

@kyle-basis
Copy link
Author

That's reasonable - still, the hard dependency on geometry_msgs is painful and unnecessary.

@kyle-basis
Copy link
Author

@kyle-basis ament_uncrustify --reformat is your friend here ;-)

I've used this before, it ends up reformatting a lot of files in tf2/, but will try and run it more precisely.

@ahcorde
Copy link
Contributor

ahcorde commented Dec 18, 2024

Pulls: #741
Gist: https://gist.githubusercontent.com/ahcorde/f91deb8bb6b91dd854769ff2ccc660c1/raw/a373fa10f8335099af08f044cb888a9555482834/ros2.repos
BUILD args: --packages-above-and-dependencies test_tf2 tf2 tf2_geometry_msgs --packages-above-and-dependencies test_tf2 tf2 tf2_geometry_msgs
TEST args: --packages-above test_tf2 tf2 tf2_geometry_msgs --packages-above test_tf2 tf2 tf2_geometry_msgs
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/14977

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

Copy link
Contributor

@ahcorde ahcorde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged the another PR and now we have conflicts. Sorry about that, do you mind to fix it ?

@kyle-basis
Copy link
Author

Let me see what I can do.

@kyle-basis
Copy link
Author

Manually moving buffer_core.h before the merge appears to have cleaned up the conflict, we'll see if CI is happy.

Copy link
Contributor

@ahcorde ahcorde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mind to restore tf2/include/tf2/buffer_core.h with https://github.com/ros2/geometry2/blob/rolling/tf2/include/tf2/buffer_core.h ?

@kyle-basis
Copy link
Author

Sorry about that - done

Copy link
Contributor

@ahcorde ahcorde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kyle-basis
Copy link
Author

No problems found??

@kyle-basis
Copy link
Author

Fixed. Will work on perf testing today or tomorrow.

@kyle-basis
Copy link
Author

kyle-basis commented Dec 19, 2024

Will fix the BUFFER_CORE_INTERFACE_HEADER_DEPERCATION warnings, too.

...should I make a PR to fix the spelling of that warning?

Edit: these warnings don't appear to be my fault.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants