36 KiB
Full-Scene ICP Pipeline Upgrade
TL;DR
Quick Summary: Upgrade the current floor-band-only ICP registration to support hybrid (floor + vertical structure) and full-scene modes, with optional FPFH+RANSAC global pre-alignment, fixed success gates, robust kernels, and per-pair diagnostic logging.
Deliverables:
--icp-region floor|hybrid|fullCLI flag (default: hybrid)--icp-global-initoptional FPFH+RANSAC pre-alignment- Fixed success gate (
>0instead of>1)- Per-pair diagnostic logging (all pairs, not just converged)
- Statistical outlier removal preprocessing
- TukeyLoss robust kernel support
- 3D AABB overlap check for hybrid/full modes
- Exposed CLI flags:
--icp-min-overlap,--icp-band-height- Relaxed defaults: gravity penalty, correspondence distance factor, min_overlap_area
- Comprehensive tests covering all new modes + edge cases
Estimated Effort: Medium (3–5 days) Parallel Execution: YES - 3 waves Critical Path: Task 1 → Task 2 → Task 3 → Task 5 → Task 7 → Task 9
Context
Original Request
User observed that camera SN44289123 appears floor-disconnected by ~3–5 cm after ground-plane refinement. Diagnostic sweeps showed ICP consistently failing: only 1 of 6 pairs converges, graph stays disconnected, and the success gate (>1) rejects valid single-camera corrections. Oracle analysis identified floor-band degeneracy (planar sliding), tight gating, and over-aggressive gravity penalty as root causes. User requested upgrading from floor-only to full-scene/hybrid ICP.
Interview Summary
Key Discussions:
- User chose hybrid as default ICP region mode (floor + vertical structure)
- User chose FPFH+RANSAC as optional global pre-alignment (
--icp-global-init) - User chose to include success gate fix in this plan (not a separate patch)
- Oracle recommended: lower gravity penalty (10→2), widen correspondence factor (1.4→2.5), lower min_overlap_area (1.0→0.5), add per-pair logging, add robust kernels
Research Findings:
- Open3D multiway registration: pairwise ICP → pose graph → global optimization (LM)
- FPFH features + RANSAC for global pre-alignment when init is poor
- Statistical outlier removal (nb_neighbors=20, std_ratio=2.0) standard for ZED
- TukeyLoss(k=0.05–0.1) effective for depth sensor noise at 3–5 m range
- Hybrid floor+structure recommended over pure floor (adds XZ constraints)
- Voxel size: 0.05 m for global/coarse, 0.02 m for fine refinement
Metis Review
Identified Gaps (addressed):
- Backward compatibility:
--icp-region floorpreserves legacy behavior exactly - Hybrid degeneracy: if no vertical structure found, gracefully falls back to floor-only with warning
- FPFH failure fallback: when RANSAC fails, continues with extrinsic-based init (no crash)
- Determinism: RANSAC seeds controlled for reproducibility; tests use synthetic geometry
- Ceiling/overhang dominance in full mode: gravity constraint prevents upside-down alignment
- Symmetric environments: transform sanity bounds catch FPFH mis-registration
Work Objectives
Core Objective
Replace the floor-band-only ICP pipeline with a configurable region selection system (floor/hybrid/full) that provides stronger geometric constraints, better pair convergence, and more reliable multi-camera correction.
Concrete Deliverables
- Modified
aruco/icp_registration.py: new extraction functions, 3D overlap, robust kernel, relaxed defaults, diagnostic logging - Modified
refine_ground_plane.py: new CLI flags for region, global-init, overlap, band-height - New/extended
tests/test_icp_registration.py: ~15 new test cases - Updated
README.md: new flags documented
Definition of Done
uv run refine_ground_plane.py --helpshows--icp-region,--icp-global-init,--icp-min-overlap,--icp-band-heightuv run pytest -x -vv→ all tests pass (existing + new)uv run basedpyright aruco/icp_registration.py refine_ground_plane.py→ 0 errors--icp-region floorproduces identical output to current behavior (regression)--icp-region hybridproduces ≥ as many converged pairs as floor on test data
Must Have
- Region selection:
floor,hybrid,fullmodes - Backward compatibility:
floormode matches current behavior - Success gate:
num_cameras_optimized > 0(not> 1) - Per-pair diagnostic logging at INFO level
- Robust kernel (TukeyLoss) support
- Statistical outlier removal preprocessing
- 3D AABB overlap check for hybrid/full
- Optional FPFH+RANSAC global init with clean fallback
Must NOT Have (Guardrails)
- No changes to HDF5 schema or
aruco/depth_save.py - No additional global registration methods beyond FPFH+RANSAC
- No auto-parameter search or adaptive voxel size loops
- No multiple outlier strategies (SOR only, no radius outlier removal)
- No multiple robust kernels (TukeyLoss only, no Huber/Cauchy menus)
- No refactoring of pose graph optimization or information matrix computation
- No new persistent output files beyond normal console logging
- No changes to
aruco/ground_plane.pycore functions (only consume them)
Verification Strategy
UNIVERSAL RULE: ZERO HUMAN INTERVENTION
ALL tasks in this plan MUST be verifiable WITHOUT any human action.
Test Decision
- Infrastructure exists: YES
- Automated tests: YES (Tests-after for new functionality)
- Framework: pytest
Agent-Executed QA Scenarios (MANDATORY — ALL tasks)
Verification Tool by Deliverable Type:
| Type | Tool | How Agent Verifies |
|---|---|---|
| CLI flags | Bash | uv run refine_ground_plane.py --help and grep for flags |
| Library/Module | Bash (pytest) | uv run pytest tests/test_icp_registration.py -v |
| Type safety | Bash (basedpyright) | uv run basedpyright aruco/icp_registration.py |
| Regression | Bash (pipeline run) | Compare outputs with floor-only baseline |
Execution Strategy
Parallel Execution Waves
Wave 1 (Start Immediately):
├── Task 1: Fix success gate + per-pair diagnostic logging
├── Task 2: Add point extraction functions (hybrid/full/SOR)
└── Task 3: Add 3D AABB overlap check
Wave 2 (After Wave 1):
├── Task 4: Add TukeyLoss robust kernel support
├── Task 5: Integrate region selection into refine_with_icp
└── Task 6: Add FPFH+RANSAC global pre-alignment
Wave 3 (After Wave 2):
├── Task 7: Wire CLI flags in refine_ground_plane.py
├── Task 8: Relax ICPConfig defaults
└── Task 9: Tests + README + regression verification
Critical Path: Task 1 → Task 2 → Task 5 → Task 7 → Task 9
Parallel Speedup: ~40% faster than sequential
Dependency Matrix
| Task | Depends On | Blocks | Can Parallelize With |
|---|---|---|---|
| 1 | None | 5, 9 | 2, 3 |
| 2 | None | 5 | 1, 3 |
| 3 | None | 5 | 1, 2 |
| 4 | None | 5 | 1, 2, 3 |
| 5 | 1, 2, 3, 4 | 7 | 6 |
| 6 | None | 7 | 4, 5 |
| 7 | 5, 6 | 9 | 8 |
| 8 | None | 9 | 7 |
| 9 | 7, 8 | None | None (final) |
Agent Dispatch Summary
| Wave | Tasks | Recommended Agents |
|---|---|---|
| 1 | 1, 2, 3 | task(category="quick") for Task 1; task(category="unspecified-high") for Tasks 2, 3 |
| 2 | 4, 5, 6 | task(category="unspecified-high") for all three |
| 3 | 7, 8, 9 | task(category="unspecified-high") for Task 7, task(category="quick") for Task 8, task(category="unspecified-high") for Task 9 |
TODOs
-
1. Fix success gate + add per-pair diagnostic logging
What to do:
- Change
metrics.success = metrics.num_cameras_optimized > 1tometrics.success = metrics.num_cameras_optimized > 0(line 475) - Log ALL pair outcomes (not just converged) at INFO level: fitness, RMSE, converged status
- Always record to
metrics.per_pair_results(currently only converged pairs are stored, line 417) - Add
logger.info(f"Pair ({s1},{s2}): fitness={result.fitness:.3f}, rmse={result.inlier_rmse:.4f}, converged={result.converged}")after each pairwise ICP - Add
logger.debug(f"Pair ({s1},{s2}) overlap {area:.2f} m² < {config.min_overlap_area}. Skipping.")before overlap skip
Must NOT do:
- Do not change pairwise_icp algorithm or pose graph logic
- Do not modify ICPConfig defaults (separate task)
Recommended Agent Profile:
- Category:
quick- Reason: Small, focused change — two lines of logic + logging additions
- Skills: []
- Skills Evaluated but Omitted:
git-master: No git operations needed during implementation
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 2, 3)
- Blocks: Tasks 5, 9
- Blocked By: None
References:
Pattern References:
aruco/icp_registration.py:414-421— Current convergence check and pair_results storage (change to always store)aruco/icp_registration.py:475— Success gate line to modifyaruco/icp_registration.py:382-386— Overlap skip without logging (add debug log)
API/Type References:
aruco/icp_registration.py:48-59— ICPMetrics dataclass (per_pair_results field)aruco/icp_registration.py:37-45— ICPResult dataclass (fitness, inlier_rmse, converged fields)
WHY Each Reference Matters:
- Line 475: the exact success gate that Oracle identified as too strict
- Lines 414-421: where pair results are conditionally stored; need to always store for diagnostics
- Lines 382-386: overlap rejection currently silent; need logging for operator debugging
Acceptance Criteria:
metrics.successisTruewhennum_cameras_optimized == 1- All pair results appear in
metrics.per_pair_resultsregardless of convergence - Log output at INFO shows fitness/RMSE for every attempted pair
uv run pytest tests/test_icp_registration.py -v→ all existing tests pass
Agent-Executed QA Scenarios:
Scenario: Success gate accepts single-camera optimization Tool: Bash (pytest) Preconditions: test_icp_registration.py has test_success_gate_single_camera Steps: 1. uv run pytest tests/test_icp_registration.py -k "success_gate" -v 2. Assert: exit code 0 3. Assert: output contains "PASSED" Expected Result: Test passes confirming success=True when 1 camera optimized Evidence: pytest output captured Scenario: Per-pair logging visible in debug output Tool: Bash (pipeline run) Preconditions: output/e2e_refine_depth.json and .h5 exist Steps: 1. uv run refine_ground_plane.py --input-extrinsics output/e2e_refine_depth.json --input-depth output/e2e_refine_depth.h5 --output-extrinsics /tmp/test_logging.json --icp --icp-method gicp --icp-voxel-size 0.04 --seed 42 --debug 2. Assert: stdout contains "fitness=" for multiple pairs 3. Assert: stdout contains "overlap" for skipped pairs Expected Result: All 6 pairs produce diagnostic output Evidence: Terminal output capturedCommit: YES
- Message:
fix(icp): relax success gate to >0 and add per-pair diagnostic logging - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Change
-
2. Add point extraction functions (hybrid/full/SOR)
What to do:
- Add
extract_scene_points(points_world, floor_y, floor_normal, mode, band_height)function toaruco/icp_registration.pymode="floor": delegate to existingextract_near_floor_band(backward compat)mode="hybrid": floor band UNION with points whose surface normals are near-horizontal (walls/vertical structure). Use Open3D normal estimation on the full cloud, then filter byabs(normal · floor_normal) < 0.3for "vertical" points. Combine with floor band.mode="full": return all points after SOR filtering
- Add
preprocess_point_cloud(pcd, voxel_size)function:- Statistical outlier removal:
pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) - Return cleaned point cloud
- Statistical outlier removal:
- Add
region: str = "floor"field toICPConfigdataclass (values: "floor", "hybrid", "full") - If hybrid mode finds no vertical structure points, log warning and fall back to floor-only points
Must NOT do:
- Do not modify
extract_near_floor_band(keep it as-is for floor mode) - Do not add multiple outlier strategies
- Do not change ground_plane.py
Recommended Agent Profile:
- Category:
unspecified-high- Reason: New extraction logic with mode dispatch, normal-based filtering, and SOR integration
- Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 3)
- Blocks: Task 5
- Blocked By: None
References:
Pattern References:
aruco/icp_registration.py:62-82—extract_near_floor_band(floor mode delegate, keep unchanged)aruco/icp_registration.py:20-34—ICPConfigdataclass (addregionfield here)
API/Type References:
aruco/ground_plane.py:114-145—detect_floor_planereturnsFloorPlane(normal, d)— used for floor_y and floor_normal inputs- Open3D
remove_statistical_outlier(nb_neighbors, std_ratio)— returns (cleaned_pcd, indices) - Open3D
estimate_normals(KDTreeSearchParamHybrid(radius, max_nn))— for hybrid vertical detection
External References:
- Open3D SOR: https://www.open3d.org/docs/release/tutorial/geometry/pointcloud.html#Statistical-outlier-removal
WHY Each Reference Matters:
- Lines 62-82: the existing extraction function that floor mode must delegate to unchanged
- ICPConfig: where
regionfield lives sorefine_with_icpcan dispatch - Open3D SOR API: exact function signature for outlier removal
Acceptance Criteria:
extract_scene_pointsexists and dispatches correctly for all 3 modespreprocess_point_cloudapplies SOR and returns cleaned cloudICPConfig.regionfield exists with default"floor"- Floor mode produces identical output to
extract_near_floor_band - Hybrid mode includes vertical-normal points when present
- Hybrid mode falls back to floor-only with warning when no vertical points
uv run basedpyright aruco/icp_registration.py→ 0 errors
Agent-Executed QA Scenarios:
Scenario: Floor mode identical to legacy Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "floor_mode_legacy" -v 2. Assert: exit code 0 Expected Result: Floor extraction unchanged Evidence: pytest output Scenario: Hybrid includes vertical structure Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "hybrid_vertical" -v 2. Assert: exit code 0 Expected Result: Hybrid returns more points than floor-only when walls present Evidence: pytest output Scenario: Hybrid fallback when no vertical structure Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "hybrid_fallback" -v 2. Assert: exit code 0 Expected Result: Falls back to floor-only points, logs warning Evidence: pytest outputCommit: YES
- Message:
feat(icp): add hybrid/full point extraction with SOR preprocessing - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Add
-
3. Add 3D AABB overlap check
What to do:
- Add
compute_overlap_3d(points_a, points_b, margin)function toaruco/icp_registration.py- Compute 3D axis-aligned bounding box intersection volume
- Return volume in m³
- Add
overlap_mode: str = "xz"field toICPConfig(values: "xz", "3d")"xz": use existingcompute_overlap_xz(backward compat for floor mode)"3d": use newcompute_overlap_3d(for hybrid/full modes)
- Keep
compute_overlap_xzunchanged
Must NOT do:
- Do not remove or modify
compute_overlap_xz - Do not add OBB or complex overlap metrics
Recommended Agent Profile:
- Category:
quick- Reason: Simple geometric function + config field addition
- Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 2)
- Blocks: Task 5
- Blocked By: None
References:
Pattern References:
aruco/icp_registration.py:85-105—compute_overlap_xz(follow same pattern for 3D version)
WHY Each Reference Matters:
- Lines 85-105: exact pattern to follow (min/max bounding box, intersection, area/volume)
Acceptance Criteria:
compute_overlap_3dexists and returns volume in m³ICPConfig.overlap_modefield exists with default"xz"- Disjoint clouds return 0.0
- Fully overlapping clouds return correct volume
uv run basedpyright aruco/icp_registration.py→ 0 errors
Agent-Executed QA Scenarios:
Scenario: 3D overlap correctness Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "overlap_3d" -v 2. Assert: exit code 0 Expected Result: Correct volume for known geometries Evidence: pytest outputCommit: YES
- Message:
feat(icp): add 3D AABB overlap check for hybrid/full modes - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Add
-
4. Add TukeyLoss robust kernel support
What to do:
- Add
robust_kernel: str = "none"field toICPConfig(values: "none", "tukey") - Add
robust_kernel_k: float = 0.1field toICPConfig - In
pairwise_icp, whenconfig.robust_kernel == "tukey":- Create
loss = o3d.pipelines.registration.TukeyLoss(k=config.robust_kernel_k) - Pass loss to
TransformationEstimationPointToPlane(loss)orTransformationEstimationForGeneralizedICP(loss)
- Create
- When
config.robust_kernel == "none": use current behavior (no loss function)
Must NOT do:
- Do not add Huber, Cauchy, or other kernels
- Do not change default behavior (default is "none")
Recommended Agent Profile:
- Category:
quick- Reason: Config field + conditional in pairwise_icp
- Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 5, 6)
- Blocks: Task 5
- Blocked By: None
References:
Pattern References:
aruco/icp_registration.py:177-213—pairwise_icpestimation method dispatch (add kernel here)
External References:
- Open3D robust kernels: https://www.open3d.org/docs/release/tutorial/t_pipelines/t_robust_kernel.html
TukeyLoss(k)where k ≈ expected noise std dev in meters
WHY Each Reference Matters:
- Lines 177-213: exact location where estimation objects are created; kernel wraps them
Acceptance Criteria:
ICPConfig.robust_kernelandrobust_kernel_kfields existpairwise_icpuses TukeyLoss when configured- Default behavior unchanged (no kernel applied)
uv run basedpyright aruco/icp_registration.py→ 0 errors
Agent-Executed QA Scenarios:
Scenario: Tukey kernel applied correctly Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "robust_kernel" -v 2. Assert: exit code 0 Expected Result: ICP runs with Tukey kernel without errors Evidence: pytest outputCommit: YES
- Message:
feat(icp): add TukeyLoss robust kernel support - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Add
-
5. Integrate region selection into refine_with_icp
What to do:
- Modify
refine_with_icp()to useextract_scene_pointsinstead ofextract_near_floor_band - Dispatch overlap check based on
config.overlap_mode:"xz"→compute_overlap_xz(floor mode)"3d"→compute_overlap_3d(hybrid/full)
- Auto-set
overlap_modebased onregion:floor→overlap_mode="xz"hybridorfull→overlap_mode="3d"
- Apply
preprocess_point_cloud(SOR) to all point clouds before ICP - When
config.robust_kernel != "none", pass kernel config through topairwise_icp - Use world-frame points for ICP when in hybrid/full mode (current floor mode transforms back to camera frame — keep that for floor; for hybrid/full, ICP in world frame is more stable with mixed geometry)
Must NOT do:
- Do not change pose graph construction or optimization logic
- Do not change the validation/safety bounds logic
Recommended Agent Profile:
- Category:
unspecified-high- Reason: Core integration task connecting all new components
- Skills: []
Parallelization:
- Can Run In Parallel: YES (with Task 6)
- Parallel Group: Wave 2
- Blocks: Task 7
- Blocked By: Tasks 1, 2, 3, 4
References:
Pattern References:
aruco/icp_registration.py:327-478—refine_with_icpfull function (the integration target)aruco/icp_registration.py:346-372— Current point extraction loop (replace with extract_scene_points)aruco/icp_registration.py:378-386— Current overlap check (dispatch based on mode)
WHY Each Reference Matters:
- Lines 346-372: the exact loop to modify for region-aware extraction
- Lines 378-386: where overlap gating happens, needs mode dispatch
Acceptance Criteria:
refine_with_icpusesextract_scene_pointsfor point extraction- Overlap check dispatches based on region mode
- SOR preprocessing applied to all point clouds
- Floor mode produces identical behavior to current implementation
- Hybrid/full modes extract more points and use 3D overlap
uv run pytest tests/test_icp_registration.py -v→ all pass
Agent-Executed QA Scenarios:
Scenario: Floor mode regression Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "floor_mode" -v 2. Assert: exit code 0 Expected Result: Floor mode unchanged Evidence: pytest output Scenario: Hybrid mode integration Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "hybrid_integration" -v 2. Assert: exit code 0 Expected Result: Hybrid uses mixed-geometry extraction + 3D overlap Evidence: pytest outputCommit: YES
- Message:
feat(icp): integrate region selection, SOR, and robust kernel into refine_with_icp - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Modify
-
6. Add FPFH+RANSAC global pre-alignment
What to do:
- Add
global_init: bool = Falsefield toICPConfig - Add
compute_fpfh_features(pcd_down, voxel_size)function:- Compute FPFH features using
o3d.pipelines.registration.compute_fpfh_feature - Use
KDTreeSearchParamHybrid(radius=voxel_size*5, max_nn=100)
- Compute FPFH features using
- Add
global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size)function:- Use
registration_ransac_based_on_feature_matching - Correspondence checkers: EdgeLength(0.9), Distance(voxel_size*1.5)
- Convergence: 4M iterations, 500 validations
- Return transformation matrix
- Use
- In
refine_with_icppairwise loop: whenconfig.global_initis True:- Attempt global registration first
- Validate result (fitness > 0.1, transform magnitude within bounds)
- If valid: use as init_T for pairwise_icp instead of extrinsic-derived init
- If invalid: log warning, fall back to extrinsic-derived init
- When
config.global_initis False: use current extrinsic-based init (unchanged)
Must NOT do:
- Do not add other global registration methods
- Do not make global_init the default
Recommended Agent Profile:
- Category:
unspecified-high- Reason: New FPFH pipeline with validation and fallback logic
- Skills: []
Parallelization:
- Can Run In Parallel: YES (with Tasks 4, 5)
- Parallel Group: Wave 2
- Blocks: Task 7
- Blocked By: None (uses only Open3D APIs + ICPConfig)
References:
Pattern References:
aruco/icp_registration.py:388-407— Current pairwise ICP loop where init_T is computed and used
External References:
- Open3D FPFH: https://www.open3d.org/docs/release/tutorial/pipelines/global_registration.html
- Open3D
registration_ransac_based_on_feature_matchingAPI
WHY Each Reference Matters:
- Lines 388-407: where init_T is computed; global_init replaces this when enabled
Acceptance Criteria:
ICPConfig.global_initfield exists (default False)compute_fpfh_featuresandglobal_registrationfunctions exist- When enabled: uses FPFH transform as init if valid, falls back otherwise
- When disabled: behavior unchanged
- Fallback logged at WARNING level
uv run basedpyright aruco/icp_registration.py→ 0 errors
Agent-Executed QA Scenarios:
Scenario: Global init fallback on bad data Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "global_init_fallback" -v 2. Assert: exit code 0 Expected Result: Falls back to extrinsic init when FPFH fails Evidence: pytest output Scenario: Global init disabled by default Tool: Bash (pytest) Steps: 1. uv run pytest tests/test_icp_registration.py -k "global_init_disabled" -v 2. Assert: exit code 0 Expected Result: No FPFH computation when global_init=False Evidence: pytest outputCommit: YES
- Message:
feat(icp): add optional FPFH+RANSAC global pre-alignment - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Add
-
7. Wire CLI flags in refine_ground_plane.py
What to do:
- Add CLI options to
refine_ground_plane.py:--icp-region: type=click.Choice(["floor", "hybrid", "full"]), default="hybrid"--icp-global-init / --no-icp-global-init: default False--icp-min-overlap: type=float, default=0.5--icp-band-height: type=float, default=0.3--icp-robust-kernel: type=click.Choice(["none", "tukey"]), default="none"--icp-robust-k: type=float, default=0.1
- Pass these through to
ICPConfigconstruction - Include new config values in
_meta.icp_refined.configoutput JSON
Must NOT do:
- Do not change existing CLI flag names or defaults for non-ICP flags
- Do not change ground-plane refinement logic
Recommended Agent Profile:
- Category:
unspecified-high- Reason: Multiple CLI flags with proper Click integration and JSON metadata
- Skills: []
Parallelization:
- Can Run In Parallel: YES (with Task 8)
- Parallel Group: Wave 3
- Blocks: Task 9
- Blocked By: Tasks 5, 6
References:
Pattern References:
refine_ground_plane.py— Existing--icp,--icp-method,--icp-voxel-sizeflags (follow same pattern)refine_ground_plane.py—_meta.icp_refined.configsection in output JSON
WHY Each Reference Matters:
- Existing ICP flags: exact pattern for Click option declaration + ICPConfig construction
Acceptance Criteria:
uv run refine_ground_plane.py --helpshows all new flags--icp-regionaccepts floor, hybrid, full--icp-global-initis a boolean flag- Output JSON
_meta.icp_refined.configincludes region, global_init, min_overlap, band_height uv run basedpyright refine_ground_plane.py→ 0 errors
Agent-Executed QA Scenarios:
Scenario: New CLI flags appear in help Tool: Bash Steps: 1. uv run refine_ground_plane.py --help 2. Assert: output contains "--icp-region" 3. Assert: output contains "floor|hybrid|full" 4. Assert: output contains "--icp-global-init" 5. Assert: output contains "--icp-min-overlap" 6. Assert: output contains "--icp-band-height" Expected Result: All flags documented Evidence: Help output captured Scenario: Flags pass through to ICPConfig Tool: Bash (pipeline run) Steps: 1. uv run refine_ground_plane.py --input-extrinsics output/e2e_refine_depth.json --input-depth output/e2e_refine_depth.h5 --output-extrinsics /tmp/test_cli_flags.json --icp --icp-region hybrid --icp-voxel-size 0.04 --seed 42 2. Parse /tmp/test_cli_flags.json 3. Assert: _meta.icp_refined.config.region == "hybrid" Expected Result: Config values in output JSON Evidence: JSON file contentsCommit: YES
- Message:
feat(cli): wire ICP region, global-init, overlap, kernel CLI flags - Files:
refine_ground_plane.py - Pre-commit:
uv run pytest -q
- Add CLI options to
-
8. Relax ICPConfig defaults
What to do:
- Change
ICPConfigdefaults:min_fitness: float = 0.3→0.15(more permissive pair acceptance)min_overlap_area: float = 1.0→0.5(allow sparser overlap)gravity_penalty_weight: float = 10.0→2.0(allow useful tilt corrections)max_correspondence_distance_factor: float = 1.4→2.5(wider capture at coarse scale)max_translation_m: float = 0.1→0.3(allow reasonable corrections)max_rotation_deg: float = 5.0→10.0(allow moderate rotation corrections)
- Keep
region: str = "floor"as default in ICPConfig (CLI default is "hybrid", but library default stays conservative)
Must NOT do:
- Do not change method, voxel_size, or band_height defaults
- Do not change any algorithmic logic
Recommended Agent Profile:
- Category:
quick- Reason: Pure config value changes, no logic
- Skills: []
Parallelization:
- Can Run In Parallel: YES (with Task 7)
- Parallel Group: Wave 3
- Blocks: Task 9
- Blocked By: None
References:
Pattern References:
aruco/icp_registration.py:20-34— ICPConfig dataclass (all defaults here)
WHY Each Reference Matters:
- Lines 20-34: the exact defaults to modify, with Oracle's recommendations as rationale
Acceptance Criteria:
- All default values updated as specified
- Existing tests still pass with new defaults
uv run pytest tests/test_icp_registration.py -v→ all pass
Agent-Executed QA Scenarios:
Scenario: Defaults changed correctly Tool: Bash (python) Steps: 1. uv run python -c "from aruco.icp_registration import ICPConfig; c=ICPConfig(); print(c.min_fitness, c.min_overlap_area, c.gravity_penalty_weight, c.max_correspondence_distance_factor, c.max_translation_m, c.max_rotation_deg)" 2. Assert: output is "0.15 0.5 2.0 2.5 0.3 10.0" Expected Result: All defaults match spec Evidence: Terminal outputCommit: YES
- Message:
chore(icp): relax ICPConfig defaults per Oracle recommendations - Files:
aruco/icp_registration.py - Pre-commit:
uv run pytest tests/test_icp_registration.py -q
- Change
-
9. Tests + README + regression verification
What to do:
- Add new tests to
tests/test_icp_registration.py:test_success_gate_single_camera: verify success=True when 1 camera optimizedtest_floor_mode_legacy_equivalence: floor extraction matches extract_near_floor_bandtest_hybrid_includes_vertical_structure: hybrid returns more points with wallstest_hybrid_fallback_no_vertical: falls back to floor-only with warningtest_full_mode_all_points: full mode returns all valid points after SORtest_overlap_3d_disjoint: 3D overlap returns 0 for disjoint cloudstest_overlap_3d_partial: 3D overlap returns correct volumetest_robust_kernel_tukey: ICP runs with TukeyLoss without errorstest_global_init_fallback: falls back when FPFH failstest_global_init_disabled_default: no FPFH when global_init=Falsetest_per_pair_logging_all_pairs: all pairs stored in metrics regardless of convergencetest_preprocess_sor: SOR removes outliers correctly
- Update
README.md:- Document
--icp-region,--icp-global-init,--icp-min-overlap,--icp-band-height,--icp-robust-kernel,--icp-robust-kflags - Add example command with hybrid mode
- Document
- Run full regression:
uv run pytest -x -vv→ all passuv run basedpyright aruco/icp_registration.py refine_ground_plane.py→ 0 errors
Must NOT do:
- Do not add tests for features not implemented in Tasks 1-8
- Do not modify code logic (test-only + docs-only task)
Recommended Agent Profile:
- Category:
unspecified-high- Reason: Large test suite + documentation + regression verification
- Skills: []
Parallelization:
- Can Run In Parallel: NO (final task)
- Parallel Group: Wave 3 (after Tasks 7, 8)
- Blocks: None (final)
- Blocked By: Tasks 7, 8
References:
Pattern References:
tests/test_icp_registration.py— Existing test patterns (synthetic point clouds, monkeypatching, numpy assertions)
Test References:
tests/test_icp_registration.py:test_pairwise_icp_known_transform— Pattern for synthetic ICP teststests/test_icp_registration.py:test_refine_with_icp_synthetic_offset— Pattern for integration tests
WHY Each Reference Matters:
- Existing tests: follow same synthetic data patterns, assertion styles, and monkeypatching approaches
Acceptance Criteria:
- ≥12 new test cases added
uv run pytest tests/test_icp_registration.py -v→ all pass (existing + new)uv run pytest -x -vv→ all pass (full suite)uv run basedpyright aruco/icp_registration.py refine_ground_plane.py→ 0 errors- README.md documents all new CLI flags with examples
Agent-Executed QA Scenarios:
Scenario: Full test suite passes Tool: Bash (pytest) Steps: 1. uv run pytest -x -vv 2. Assert: exit code 0 3. Assert: output contains "passed" and no "FAILED" Expected Result: All tests pass Evidence: pytest output captured Scenario: Type check clean Tool: Bash (basedpyright) Steps: 1. uv run basedpyright aruco/icp_registration.py refine_ground_plane.py 2. Assert: output contains "0 errors" Expected Result: No type errors Evidence: basedpyright output Scenario: README documents new flags Tool: Bash (grep) Steps: 1. grep -c "icp-region" README.md 2. Assert: count > 0 3. grep -c "icp-global-init" README.md 4. Assert: count > 0 Expected Result: Flags documented Evidence: grep outputCommit: YES
- Message:
test(icp): add comprehensive tests for full-scene ICP pipeline + update docs - Files:
tests/test_icp_registration.py,README.md - Pre-commit:
uv run pytest -x -vv
- Add new tests to
Commit Strategy
| After Task | Message | Files | Verification |
|---|---|---|---|
| 1 | fix(icp): relax success gate to >0 and add per-pair diagnostic logging |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 2 | feat(icp): add hybrid/full point extraction with SOR preprocessing |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 3 | feat(icp): add 3D AABB overlap check for hybrid/full modes |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 4 | feat(icp): add TukeyLoss robust kernel support |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 5 | feat(icp): integrate region selection, SOR, and robust kernel into refine_with_icp |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 6 | feat(icp): add optional FPFH+RANSAC global pre-alignment |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 7 | feat(cli): wire ICP region, global-init, overlap, kernel CLI flags |
refine_ground_plane.py |
uv run pytest -q |
| 8 | chore(icp): relax ICPConfig defaults per Oracle recommendations |
aruco/icp_registration.py |
uv run pytest tests/test_icp_registration.py -q |
| 9 | test(icp): add comprehensive tests for full-scene ICP pipeline + update docs |
tests/test_icp_registration.py, README.md |
uv run pytest -x -vv |
Success Criteria
Verification Commands
uv run refine_ground_plane.py --help # Expected: shows --icp-region, --icp-global-init, etc.
uv run pytest -x -vv # Expected: all tests pass
uv run basedpyright aruco/icp_registration.py refine_ground_plane.py # Expected: 0 errors
Final Checklist
- All "Must Have" present
- All "Must NOT Have" absent
- All tests pass
--icp-region floorbackward compatible--icp-region hybriddefault in CLI- README documents all new flags