Files
2026-05-14 12:16:26 +02:00

70 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ICP LiDAR ↔ SMPL-X Dense Fusion
Refines Multi-HMR SMPL-X meshes using live iPhone LiDAR via point-to-plane ICP.
## Env vars
| Var | Default | Effect |
|-----|---------|--------|
| `ICP_FUSION` | `0` | `1` enables LiDAR receiver + FusionWorker |
| `ICP_LIDAR_HOST` | _(required when on)_ | iPhone ARBodyTracker IP on the LAN |
| `ICP_LIDAR_PORT` | `5500` | TCP port the iOS app publishes ARMesh on |
| `ICP_LIDAR_EXTRINSIC` | `~/.config/av-live/lidar_extrinsic.json` | Path to persisted extrinsic JSON |
## Relation to ARKit joint fusion
ICP LiDAR fusion is **mesh-level** and complementary to the existing **joint-level** ARKit fusion (`iphone_osc_listener.py` + `pose_filter.py::ArkitFuse` + `multi_hmr_worker.arkit_pelvis_z_override`). The two run independently:
- **ARKit joints** (OSC :57128) — sparse (14 mapped joints), 60 Hz, fast, used to override MediaPipe pose joint slots and lock Multi-HMR pelvis Z.
- **ICP LiDAR mesh** (TCP :5500) — dense (~thousand points), 510 Hz, used to register Multi-HMR SMPL-X vertices onto the real-world geometry captured by the iPhone LiDAR.
They can be enabled together or separately. ICP runs only when `ICP_FUSION=1`.
## Calibration
1. Launch the iPhone ARBodyTracker app and note its LAN IP.
2. From `data_only_viz/`:
```bash
uv run --extra lidar python -m data_only_viz.scripts.calibrate_lidar \
--lidar-host <iPhone IP> --lidar-port 5500 --webcam-index 0
```
3. The script asks for 4 stances (front / left / right / back). Hold still each time and press ENTER.
4. The estimated extrinsic is written to `ICP_LIDAR_EXTRINSIC` (or the default path). Re-run any time the camera or iPhone moves.
> NOTE — as of the initial ICP MVP (Task 9), `multi_hmr_worker.predict_once` is a stub raising `NotImplementedError`. The calibration CLI runs the LiDAR reader and 4-stance loop scaffold but cannot capture the webcam pelvis side until a follow-up wires `predict_once` to the existing inference path. Track this in the next planning round.
## Runtime
```bash
ICP_FUSION=1 ICP_LIDAR_HOST=192.168.0.42 \
uv run --extra lidar python -m data_only_viz.main
```
## Architecture (summary)
```
iPhone ARBodyTracker app
├── OSC :57128 /body3d/kp → IphoneOSCListener (ARKit joint fusion)
└── TCP :5500 ARMeshAnchors → LidarTCPReader (ICP mesh fusion)
FusionWorker.run_once(state)
state.persons_smplx[*].vertices_3d
(replaced in place when ICP accepts)
```
ICP fusion runs in its own daemon thread (`IcpFusionThread`, target 8 Hz). It is opt-in (off by default) and a no-op if the LiDAR stream is absent.
## Troubleshooting
- **`open3d` missing** → `cd data_only_viz && uv sync --extra lidar`
- **No LiDAR frames** → check that the iPhone app is publishing on the expected port and that nothing else is bound to it. `nc -l 5500` from the Mac should not succeed while the app runs.
- **ICP always rejected (`fitness < 0.30`)** → the extrinsic is likely stale; re-run calibration. Verify the iPhone is facing the same scene as the webcam.
- **Mesh appears scaled wrong** → SMPL-X is in metres; the iPhone publishes metres. If you see a factor-1000 mismatch the iOS encoder is sending millimetres — patch the iOS app, not this code.
- **Bench shows `latency_ms_p95 > 100`** → reduce `IcpConfig.voxel_size_m` (e.g. 0.03 m) or `max_iterations` (e.g. 20).
- **Python `cp314` wheel failure on `uv sync --extra lidar`** → open3d does not ship cp313+ wheels yet. Use Python 3.12 (`uv venv --python 3.12`).
## Implementation note (Task 6 deviation)
`register_mesh_to_lidar` uses a two-stage coarse-to-fine ICP internally: a warm-start pass at `max(0.25 m, 5× threshold)` correspondence, then a strict pass at `IcpConfig.max_correspondence_m` (default 0.05 m). The accept/reject gate (`fitness ≥ 0.30`, `rmse ≤ 0.05 m`) is evaluated **only on the strict pass** so the contract is preserved. The warm-start makes ICP converge reliably when Multi-HMR's initial mesh sits more than 5 cm off the LiDAR surface (typical with a fresh calibration or pose change).