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

[BE] - fix inconsistent base_rot property #2085

Merged
merged 4 commits into from
Oct 10, 2024
Merged

Conversation

aclegg3
Copy link
Contributor

@aclegg3 aclegg3 commented Sep 23, 2024

Motivation and Context

This PR addresses a long-standing bug in the articulated_agent.base_rot property API.

The bug: 🐛
Rotations are stored internally as Magnum.Quaternion objects. The base_rot property operates in scalar space, namely the angle of rotation about the Y axis. This means that every call to the setter creates a Quaternion from a rotation angle and Y axis, while the getter deconstructs a Quaternion into an angle using 2arccos.
While the result is correct in some sense, it does not account for:

  1. Inversion of the axis
  2. redundancies in SO3
    This results in the potential for several nasty bugs. The most obvious being that getting an angle after settings does not return the same scalar value (it could be a correct, but different angle) and could return an erroneous value (e.g. if the quaternion axis is inverted during normalization).

The solution:
This PR modifies the getter to:

  1. check for inverted Y axis and negate the angle if detected. This prevents cases where an erroneous angle would be returned due to unknown inversion of the axis.
  2. clamping the output range to [-2pi, 2pi] by adding or subtracting 2pi overflows.

Together, these changes result in a few nice guarantees from the function:

  1. Testable consistency between the getter and setter within the range (-pi, pi). Note the end points are excluded because -pi ==pi. This means, e.g. base_rot = pi/2 guarantees base_rot == pi/2.
  2. The getter can be used in the setter to increment the rotation like: base_rot = base_rot + 0.1 with the expected outcome of continual rotation.
  3. The getter will always return a value in the range [-2pi, 2pi] and it will always be an equivalent rotation to that which was set (though the scalar value may be different outside the range (-pi,pi))

How Has This Been Tested

Added unit tests for robot wrapper and humanoid wrapper.
This includes changes from #2084. Note that CI was failing there and is now passing.

test_base_rot.mp4

Types of changes

  • [Refactoring] Large changes to the code that improve its functionality or performance
  • [Bug Fix] (non-breaking change which fixes an issue)

Checklist

  • My code follows the code style of this project.
  • I have updated the documentation if required.
  • I have read the CONTRIBUTING document.
  • I have completed my CLA (see CONTRIBUTING)
  • I have added tests to cover my changes if required.

@facebook-github-bot facebook-github-bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Sep 23, 2024
Copy link
Contributor

@0mdc 0mdc left a comment

Choose a reason for hiding this comment

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

LGTM! Tested on the HITL stack. Left some general comments.

assert kin_humanoid.get_robot_sim_id() == 1 # 0 is the ground plane
assert (
kin_humanoid.get_robot_sim_id() == 2
) # 0 is the stage and 1 is the ground plane
Copy link
Contributor

Choose a reason for hiding this comment

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

We should use constants here for clarity (enum?).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This isn't a general thing. The stage_id is a constant from sim, but the others are just situational here.

produce_debug_video = False
observations = []
cfg_settings = default_sim_settings.copy()
cfg_settings["scene"] = "NONE"
Copy link
Contributor

Choose a reason for hiding this comment

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

Good to know :)

"color",
"test_base_rot",
open_vid=True,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Great idea!

angle += mn.math.pi * 2
# NOTE: This final fmod ensures that large angles are mapped back into the -2pi, 2pi range.
angle = mn.math.fmod(angle, 2 * mn.math.pi)
return angle
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, it would be more robust to force usage of forward vectors or rotation quaternions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that we are baking in lots of assumptions using a scalar angle about Y axis with implicitly defined origin.
Happy to re-design this part of the system, but pretty deeply embedded, so just trying to get it to behave consistently for now.

@aclegg3 aclegg3 merged commit c9e5366 into main Oct 10, 2024
4 checks passed
@aclegg3 aclegg3 deleted the alex-09_23-fix_base_rot branch October 10, 2024 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Do not delete this pull request or issue due to inactivity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants