Fix 3D rotation composition with quaternions - #1247
Conversation
|
@fly1d nice fix - this is the right way to do it. A heads-up on how it interacts with #1248, which I just merged. This makes The small slowdown is |
Store a normalized quaternion behind the existing Euler accessors so ordered rotation composition no longer adds Euler components. Apply Resource.rotate increments in the parent coordinate frame while preserving the serialized x/y/z shape. Add regressions for non-commuting rotations, hierarchy composition, normalization, gimbal-lock representations, and serialization. Assisted-by: OpenAI Codex
Use the stored identity quaternion in the absolute-location hot path and document why the matrix helper remains as an independent test reference. Assisted-by: OpenAI Codex
1d8bd1c to
6330eb5
Compare
|
Thanks. I rebased onto current |
PyLabRobot#1247 replaced `rotate`'s three Euler additions with `rotation._prepend`, which composes by quaternion product rather than by adding components. This branch wraps those same three lines with the pivot, so the two met head on. Resolved by keeping the pivot and taking upstream's composition: the reference point's travel is measured from the rotation matrices before and after, so it does not care how the orientation was arrived at - and under a real composition the "after" matrix is now correct where Euler addition would have been wrong for a turn about anything but one axis. `rotate_to` also composes rather than assigns, so it was worth checking it still lands where it is told. Swept 108 starting orientations against four targets: every one arrives exactly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`rotate_to` computed a per-axis Euler delta and handed it to `rotate`, which composes by quaternion since PyLabRobot#1247. Those are not the same operation. Only Z survived it, and by accident of the convention: Euler is Rz*Ry*Rx and `_prepend` pre-multiplies, so a pure-Z delta adds cleanly where X and Y have rotations applied after them. 73 of 108 cases landed off target on X, 42 on Y. `_turn` takes the orientation a caller wants and leaves `reference` where it was, so `rotate` composes and `rotate_to` assigns, and neither carries a copy of the pivot arithmetic. It writes the angles in place rather than binding a new `Rotation`, keeping the instance and the normalisation `_prepend` established. Tests: `rotate_to` lands on every axis from every starting orientation, angles stay in [0, 360) on all three axes, and a pivot inside a turned parent holds - the last of those covering the frame correction, which nothing reached before. Checked by mutation: five mutations of these lines, five caught, where two survived beforehand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Takes PyLabRobot#1247 and resolves `rotate` the way the primitives branch does: the pivot stays and the composition is upstream's. `resource.py` and `resource_tests.py` are that branch's, so the two do not drift while it is in review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes #1165.
Summary
Rotationwhile keeping the publicx,y, andzaccessors and serialized shape.Rotationvalues with the Hamilton product instead of adding Euler components.Resource.rotate()increments in the parent-coordinate frame while preserving the existingRotationinstance and[0, 360)component normalization.Why
Adding Euler components does not compose general 3D rotations. For example, applying an x-axis increment after a 90-degree z rotation produces a different matrix when the angles are added. Quaternion multiplication preserves the requested order without introducing a matrix-to-Euler conversion into each composition.
Euler angles remain part of the public and serialized API. Equivalent Euler representations are therefore selected close to the previous component-wise values, with an explicit singular case at
pitch = +/-90degrees.Verification
.venv/bin/python -m pytest -q: 2983 passed, 36 skipped, 759 subtests passedBIN=.venv/bin/ make lintBIN=.venv/bin/ make format-checkBIN=.venv/bin/ make typecheck: 808 source files checked1.110e-15and2.753e-12No hardware was connected or driven.
AI assistance: OpenAI Codex assisted with implementation and test validation.