ruvnet/RuView · error · RuntimeError

solvePnP failed for all corner-ordering combinations

Error message

solvePnP failed for all corner-ordering combinations

What it means

solve_two_board_extrinsics jointly solves the camera→room transform over the wall and floor checkerboards, enumerating corner-ordering (flip) combinations to break the single-board centrosymmetric ambiguity. If cv2.solvePnP fails for every combination, `best` stays None and RuntimeError is raised — indicating the inputs themselves are bad, not mere ordering ambiguity.

Source

Thrown at scripts/calibration_lib.py:268

            room = np.concatenate([wall_room, floor_room], axis=0)
            img = np.concatenate([wi, fi], axis=0)
            ext = _solve_pnp(room, img, camera_matrix, dist_coeffs)
            if ext is None:
                continue
            if best is None or ext["rmse_px"] < best[0]["rmse_px"]:
                ext["wall_flipped"] = wall_flipped
                ext["floor_flipped"] = floor_flipped
                rvec, _ = cv2.Rodrigues(np.asarray(ext["rotation"]).T)
                tvec = -np.asarray(ext["rotation"]).T @ np.asarray(ext["translation_m"])
                ext["per_board"] = {
                    "wall": {"rmse_px": reprojection_rmse(
                        wall_room, wi, rvec, tvec, camera_matrix, dist_coeffs)},
                    "floor": {"rmse_px": reprojection_rmse(
                        floor_room, fi, rvec, tvec, camera_matrix, dist_coeffs)},
                }
                best = (ext,)
    if best is None:
        raise RuntimeError("solvePnP failed for all corner-ordering combinations")
    return best[0]


def extrinsics_consistency(ext_a: dict, ext_b: dict) -> dict:
    """Angular + translational disagreement between two extrinsic solutions
    (the two single-board solves). Large values mean a mis-entered board
    placement or a bad corner detection.
    """
    ra = np.asarray(ext_a["rotation"])
    rb = np.asarray(ext_b["rotation"])
    r_delta = ra.T @ rb
    angle = float(np.degrees(np.arccos(np.clip((np.trace(r_delta) - 1.0) / 2.0, -1.0, 1.0))))
    t_delta = float(
        np.linalg.norm(np.asarray(ext_a["translation_m"]) - np.asarray(ext_b["translation_m"]))
    )
    return {"rotation_deg": angle, "translation_m": t_delta}

View on GitHub (pinned to 4685618388)

Solutions

  1. Recapture with the camera NOT moved between the wall and floor shots (ADR-152 requirement)
  2. Re-check each board's cols, rows, square_size and the entered wall/floor placement values
  3. Improve corner detection (focus, lighting, flat boards) and confirm findChessboardCorners succeeds on both images
  4. Run the two single-board solves and compare via extrinsics_consistency — large angle/translation flags a mis-entered placement
Defensive patterns

Strategy: validation

Validate before calling

ok_wall = len(wall_room) == len(wall_image) and len(wall_image) > 0
ok_floor = len(floor_room) == len(floor_image) and len(floor_image) > 0
if not (ok_wall and ok_floor):
    raise SystemExit("corner detection failed on a board; recapture/re-detect first")

Try / catch

try:
    ext = solve_two_board_extrinsics(wall_room, wall_image, floor_room, floor_image, K, D)
except RuntimeError as e:
    log.error("two-board solve failed: %s", e)
    log.info("consistency: %r", extrinsics_consistency(wall_ext, floor_ext))
    raise SystemExit(
        "recapture wall/floor with a fixed camera and verify board placements"
    ) from e

Prevention

When it happens

Trigger: The camera moved between the wall and floor captures; wrong wall/floor placement entries; per-board cols/rows/square_size mismatched; corner detection failing on one board; intrinsics from a different camera or resolution.

Common situations: Handheld capture breaking the fixed-camera requirement; wall and floor image arguments swapped; stale intrinsics after a lens or resolution change.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/54031829f5a7408a. Report an issue: GitHub.