Skip to content

Ground height taken from a table plane, so every height is about 3 ft too high #62

Open

Description

@AidenJohnston

In run 1 the app took an ARKit plane classified table (the top of a folding table) as the ground instead of the detected floor plane, so every height in the scan came out about 3 ft 2 in too high.

Seen in the build 4.1 field test on 2026-09-26: TestFlight 4.1, beta at 45017ba, iPhone 14 (no LiDAR), iOS 26.6.2. Times are in the screen recording.

  • Run 1 (20:00, indoor mock wall):
    • 0:52–0:58: the ground task at the meter is met, then asked again 3 s later;
    • 3:13: the tester says "I think the ground is a little too high";
    • 3:44–3:46: an up arrow while the card says "Tilt down";
    • 4:29–5:39: the phone's own gap request sits at 0% for about 55 s, and 8% at most;
    • 4:44–4:47: the gap patch is drawn exactly on the table top;
    • 5:58: the patch at the meter is drawn at the bottom of the conduit, not on the turf.
  • Runs 2 and 3 used the floor plane correctly. So whether this happens depends on which planes ARKit finds first, and without LiDAR they come late.

Report: A41-1-01, A41-1-04, A41-1-07.

What happens.

  • session.meter_anchor.ground_y_m is −0.515 m. The packet has a floor plane at −1.474 m (11.2 m × 8.0 m) and a table plane at −0.516 m (1.0 m × 5.9 m, 1.4 m out from the wall). The ground matches the table to the millimetre.
  • With the ground 0.96 m (3 ft 2 in) too high:
    • the meter is exported 1 ft 8 in above the ground; it is really about 4 ft 10 in up;
    • the window is exported at 0 ft 2 in to 4 ft 1 in up; it is really about 3 ft 4 in to 7 ft 2 in;
    • the ground sample rows sit in mid-air, and the foot-of-wall row is a point on the wall below the meter. The target ring sat on the wall, and the arrow pointed up during "Tilt down";
    • the strip's ground row never turned green, and the tilt-down task at the meter could never finish;
    • gap patches were drawn at the table's height;
    • every height sent to the server is shifted: the 6.5 ft bands, the window's height for the opening check, and the overhead tilt target.
  • At 0:52.5–0:54.5 the ground cells by the meter turned green and the task was logged met. At 0:57 they were amber again and the same ±0.3 m task came back. The likely cause is that the ground switched to the higher plane and coverage was rebuilt at the new row heights. That is inferred: planes carry no timestamps in the packet.

Cause.

  • groundBelow returns the highest horizontal plane at least 0.3 m below the meter whose extent comes within 2 m of it, with no check on its classification (ScanEngine+Actions.swift#L57-L66).
  • LiveCapture passes every horizontal ARPlaneAnchor as a centre and half-diagonal, and drops the classification (LiveCapture.swift#L407-L413). A 5.9 m plane has a "radius" of about 3 m, so the 2 m test barely constrains it.
  • refineGround re-runs this whenever the planes change and lets a higher plane replace a lower one. It then rebuilds coverage, which is how a met task un-covers itself (ScanEngine.swift#L364-L379).

Fix direction.

  • Pass the classification through. Accept .floor, or .none where classification isn't supported; drop .table, .seat, .ceiling and the rest.
  • Prefer the lowest plausible plane, or the plane under the phone.
  • Once a floor has been measured, don't let a later plane raise the ground by more than about 10 cm.
  • Add a sanity band for the meter's height, e.g. 3 to 6.5 ft above the ground. Outside it, keep the guess and leave groundMeasured false.
  • Log each ground change in the packet, with its time, plane id and classification. Don't silently un-cover cells the user was just told were done.

Related.

Screen recordings are in the comment below.

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    size:M30-99 effective changed lines.
    on Sep 27, 2026
  2. AidenJohnston commented on Sep 27, 2026

    @AidenJohnston
    CollaboratorAuthor

    Screen recordings from the build 4.1 field test (TestFlight 4.1, iPhone 14 without LiDAR). Click a link to play.

    Times are in each run's recording. Full analyses: PR #60.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.size:M30-99 effective changed lines.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions