refactor: remove --metrics-json from refine_ground_plane.py

This commit is contained in:
2026-02-09 09:59:43 +00:00
parent 3b471c2cae
commit 915c7973d1
4 changed files with 78 additions and 42 deletions
@@ -150,8 +150,9 @@ uv run calibrate_extrinsics.py ... --save-depth output/calibration_depth.h5
- `meta/`: Global metadata (schema version, units=meters).
- `cameras/{serial}/`:
- `intrinsics`: Camera matrix (3x3).
- `pooled_depth`: The aggregated depth map used for verification (gzip compressed).
- `resolution`: [width, height].
- `pooled_depth`: The aggregated depth map used for verification (gzip compressed).
- `raw_frames/`: (Optional) Individual frames if pooling wasn't used.
This allows `refine_ground_plane.py` to run repeatedly with different parameters without re-processing the raw SVO files.
@@ -227,3 +228,65 @@ The system now explicitly sets `InitParameters.coordinate_units = sl.UNIT.METER`
If `refine_depth` shows `success: false` but `nfev` (evaluations) is high, the optimizer may have hit a flat region or local minimum.
- **Check**: Look at `termination_message` in the JSON output.
- **Fix**: Try enabling `--use-confidence-weights` or checking if the initial ArUco pose is too far off (reprojection error > 2.0).
## Implementation Details
### 1. Depth Data Structure (`--save-depth`)
The system uses HDF5 for efficient, compressed storage of depth data required for decoupled refinement.
**File Structure:**
- **`meta/`**: Global metadata.
- `schema_version`: Integer version (currently 1).
- `units`: Explicitly "meters".
- `coordinate_frame`: "world_from_cam".
- **`cameras/{serial}/`**: Per-camera data.
- `intrinsics`: 3x3 camera matrix.
- `resolution`: [width, height].
- `pooled_depth`: Aggregated depth map (gzip compressed, level 4).
- `raw_frames/`: (Optional) Individual frames if pooling wasn't used.
This structure allows `refine_ground_plane.py` to load pre-processed depth maps without needing the original SVO files or re-running the ArUco detection pipeline.
### 2. Ground Plane Refinement Pipeline
The `refine_ground_plane.py` tool implements a robust multi-camera consensus algorithm to align the floor plane.
**Core Algorithm (`aruco/ground_plane.py`):**
1. **Per-Camera Plane Detection**:
- **Unprojection**: Converts the depth map to a point cloud in the *world frame* using the initial ArUco extrinsics.
- **RANSAC**: Uses Open3D's `segment_plane` to find the dominant plane.
- **Quality Gates**:
- `min_inliers`: Requires at least 500 points.
- `normal_vertical_thresh`: Normal must be roughly vertical (>0.9 dot product with Y-axis).
2. **Robust Consensus**:
- Computes the **geometric median** of all valid plane normals and distances to reject outliers.
- **Outlier Rejection**: Discards planes deviating >15° in angle or >0.5m in distance from the median.
- **Weighted Average**: Computes the final consensus plane from the remaining inliers.
3. **Correction Calculation**:
- Computes a rigid transform $T_{corr}$ for each camera to align its detected floor to the consensus plane (or absolute Y=0).
- **Constraints**:
- **Rotation**: Corrects only pitch and roll to align the normal. Yaw is preserved.
- **Translation**: Corrects only vertical height. X/Z positions are preserved.
- **Consensus-Relative Correction**: By default, aligns cameras to the *consensus plane* to ensure relative consistency.
- **Safety Bounds**: The correction is **rejected** if it exceeds safety limits (default: 5° rotation, 0.1m translation).
### 3. Observed Behavior & Tuning
**Real-World Performance:**
- **Legacy/Unstable Behavior**: In early versions (before unit standardization), the system often reported 0 corrections or attempted extreme translations (>1m) due to mm/m confusion or depth noise.
- **Hardened Behavior**: In validated runs, the system now applies small, precise corrections (e.g., max ~0.078m translation, < 1° rotation), effectively "snapping" the floor to a consistent level without disrupting the lateral calibration.
**Why No ICP?**
Iterative Closest Point (ICP) is **not enabled** by default for ground plane alignment.
- **Reason**: ICP on featureless planar surfaces is ill-constrained; it can "slide" along the floor, introducing drift in X/Z.
- **Approach**: Plane-to-plane alignment is analytically exact for the vertical dimension and rotation, which are the only degrees of freedom we want to correct.
**When to Escalate:**
If the ground plane refinement fails or produces large corrections (>5°), it usually indicates:
1. **Poor Initial Calibration**: The ArUco markers were moved or poorly detected. Re-run `calibrate_extrinsics.py`.
2. **Non-Planar Floor**: The floor has significant slopes or steps.
3. **Obstacles**: Large objects are occluding the floor in the depth map.