Return identity directly when resolving a frame against itself (backport #1693) - #1698
Merged
Conversation
resolvePose computes the pose of a frame relative to a resolve-to frame as poseR.Inverse() * _pose. When the two frames are the same vertex this composes an edge chain with its own inverse, which is the identity in real arithmetic but not necessarily in floating point: the quaternion round trip can leave a residual on the order of 2^-56. Return the identity directly for that case instead of deriving it. The first resolvePoseRelativeToRoot call is kept so the existing validation and error diagnostics for the frame vertex are unchanged. Adds a test pinning the contract, both at the resolvePose level and through JointAxis::ResolveXyz for an axis declared without xyz_expressed_in. Part of #1692 Assisted-by: Claude Opus 5 (Anthropic Claude Code) Signed-off-by: samirbhattarai135 <147074565+samirbhattarai135@users.noreply.github.com> Co-authored-by: Steve Peters <scpeters@intrinsic.ai> (cherry picked from commit d08e88d)
8 tasks
scpeters
approved these changes
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎉 New feature
Part of #1692
Summary
resolvePosecomputes the pose of a frame relative to a resolve-to frame asposeR.Inverse() * _pose. When both are the same vertex, that composes an edge chain with its own inverse — the identity in real arithmetic, but not guaranteed in floating point. This returns the identity directly for that case instead of deriving it.The first
resolvePoseRelativeToRootcall is kept, so validation and error diagnostics for the frame vertex are unchanged.What this changes
resolvePoseleaves a sub-epsilon residual when a frame is resolved against itself, and this removes it. Confirmed by @scpeters on macOS arm64 by reverting theFrameSemantics.ccchange and running the new test:That
xyz.Y()value is exactly what gazebosim/gz-sim#3602 prints for the joint axis.Correction to my earlier claim in this PR and in #1692. I originally wrote that the test passed with and without the change. That was wrong, and the reason is worth recording: my CI runs used GitHub's
macos-latest, which now resolves to themacos-26-arm64image. So the architecture was right, but the toolchain was not the one that exhibits the residual — the newer Apple clang on macOS 26 apparently contracts the quaternion multiply such that the round trip cancels exactly. Same source, same architecture, different codegen, opposite result.Worth knowing for anyone testing this area: reproducing it needs the right compiler, not just an arm64 machine, and CI on
macos-latestwill not show it.Test
FrameSemantics.resolveAgainstOwnFrameIsExactchecks the identity case at theresolvePoselevel and throughJointAxis::ResolveXyzfor an axis declared withoutxyz_expressed_in.EXPECT_EQcannot be used here:Pose3dandVector3dcompare through tolerance-basedoperator==, so a residual near1e-17compares equal. Components are checked directly.The fixture uses
-1.5707, mirroring gz-sim'sstatic_diff_drive_vehicle.sdf. Note thatmodel_frame_relative_to_joint.sdfuses only0and exactpi/2rotations and round trips exactly on some toolchains, so a test written against that fixture can pass whether or not the behaviour is present.Checklist
codecheckpassed (See contributing)I have not run the full suite locally — I do not have a working sdformat build on this machine, so I have been relying on the fork's CI. Flagging that rather than ticking the box.
Note: Assisted by Claude (Anthropic), per the GenAI disclosure policy; commits carry an
Assisted-by:trailer. All analysis and measurements above were verified against real CI runs rather than taken on trust.This is an automatic backport of pull request #1693 done by Mergify.