Skip to content

Fix mirrored REP-103 positions in the Three.js frame conversion - #5

Merged
JaidenAGrimminck merged 1 commit into
mainfrom
fix-rep103-handedness
Oct 2, 2026
Merged

JaidenAGrimminck merged 1 commit into
mainfrom
fix-rep103-handedness

Conversation

@clmazzac

@clmazzac clmazzac commented Oct 1, 2026

Copy link
Copy Markdown
Member

Problem

Published positions are mirrored left to right. Yaw and yaw rate are not. In a turn, the published position curves one way and the heading turns the other way.

CoordinateFrames.js assumes the Three.js scene is +X forward, +Y up, +Z left. Three.js is right-handed, so with +X forward and +Y up, +Z is the vehicle's right. The conversion maps Three.js +Z to REP-103 +Y (left). That is a reflection, not a rotation.

Three checks, none of which depend on this change:

  • threeCameraLookAlongMountForwardRotation() puts image-right on Three.js +Z.
  • The GeoFrame test asserts "east-up-south": north is -Z. The GNSS sensor reported north as +Z.
  • In a recorded run, travel direction equals minus the published yaw.

Fix

Map REP-103 +Y to Three.js -Z:

  • CoordinateFrames.js: the two vector helpers and the two Euler helpers. The pitch component changes sign with the axis.
  • The places that converted by hand, with the same assumption:
    • lidar point packing (SensorMessages.js) now uses the existing lidarShaderDirectionToRep103
    • oracle bounds (PerceptionTruthIndex.js); min and max swap on the negated axis
    • the analytic GPU sensor mount (AnalyticGpuScene.js)
    • the localization overlay and AutonomyVisualizationModel.js

Each negation is written 0 - v. A plain -v gives -0 for zero, and that broke strict equality and the run bundle digest in two existing tests.

Measurements

One scenario, run before and after: about 90 m with two turns, route follower at 3 m/s. Sensor messages were recorded from the orchestrator. The internal pose came from the run's SFLog.

Check Before After
GNSS north vs internal z (least-squares slope) +1.002 -0.998
Published truth odometry: travel direction vs yaw 1.905 rad apart (mean) 0.0006 rad apart (mean), 0.016 max
GNSS east vs internal x +1.000 +1.000
GNSS longitude, per fix same to 5e-9 deg
IMU, 3,859 samples bit-for-bit the same

Behavior changes

Every published lateral coordinate changes sign: GNSS north, odometry and TF y, lidar points, oracle boxes. A sensor mounted with a non-zero y offset, or with pitch, moves to the side its manifest states. Code that compensated for the mirror must remove that compensation.

Tests

  • npm test: 1,893 of 1,900 pass. The one failure, Python decodes retained RGB, depth, and CameraInfo SFLog messages, also fails on main on my machine.
  • Three tests asserted the mirrored mapping. They now assert the corrected one.
  • Four new tests in coordinate-frames.test.js. They fail on main:
    • the basis conversion is a rotation, and zero stays +0
    • REP-103 yaw matches the direction of travel
    • a lidar ray toward the vehicle's right has negative y
    • the vector helpers are inverses

Not in this change

  • ControlsPathArc.js, spatial/planview/transform.js and docs/vehicle-manifests.md also say "+Z left". Their code works in Three.js space only, so I left them.
  • I did not view lidar clouds, oracle boxes or off-centre sensor mounts in a rendered scene. The unit tests cover them.
  • The IMU reports no lateral acceleration in turns. That is a separate issue with its own PR.

🤖 Generated with Claude Code

Three.js is right-handed. With +X forward and +Y up, +Z is the vehicle's
right. The conversion mapped Three.js +Z to REP-103 +Y, which is left.
That is a reflection. It mirrored positions but not yaw or yaw rate, so a
vehicle moved toward +Y while its yaw pointed toward -Y.

Map REP-103 +Y to Three.js -Z in the vector and Euler basis helpers. Use
the same mapping in the places that converted by hand: lidar point
packing, oracle bounds, the analytic GPU sensor mount, and the
localization overlay.

Write each negation as `0 - v`. Negative zero breaks strict equality and
the run bundle digest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JaidenAGrimminck
JaidenAGrimminck merged commit 2051f8d into main Oct 2, 2026
7 of 8 checks passed
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.

2 participants