|
| 1 | +# Patchwork++ — Usage Guide |
| 2 | + |
| 3 | +This guide covers three things that are easy to get wrong on first contact: |
| 4 | + |
| 5 | +1. [Choosing a SemanticKITTI evaluation protocol](#1-evaluation-protocols) — picks the right ground-truth definition so numbers match the paper. |
| 6 | +1. [Tuning the algorithm parameters for your sensor](#2-parameter-tuning) — what each knob does and which ones to touch first when results look bad. |
| 7 | +1. [Reproducing the paper's Table I](#3-reproducing-paper-table-i) — a one-command sweep. |
| 8 | + |
| 9 | +For a quick start, jump to [§3](#3-reproducing-paper-table-i). |
| 10 | + |
| 11 | +______________________________________________________________________ |
| 12 | + |
| 13 | +## 1. Evaluation protocols |
| 14 | + |
| 15 | +The Patchwork and Patchwork++ papers use **different** ground-truth definitions on SemanticKITTI. The eval driver `python/examples/evaluate_semantickitti.py` supports both via `--eval_protocol {patchwork, patchworkpp}`. |
| 16 | + |
| 17 | +### A. `--eval_protocol patchwork` (original Patchwork repo protocol) |
| 18 | + |
| 19 | +- **Ground GT** = `{ROAD (40), PARKING (44), SIDEWALK (48), OTHER_GROUND (49), LANE_MARKING (60), VEGETATION (70, only if z < −1.30 m), TERRAIN (72)}` |
| 20 | +- VEGETATION above −1.30 m → counts as **non-ground**. |
| 21 | +- UNLABELED (0) and OUTLIER (1) can be excluded from the precision denominator (`--consider_outliers`, default on). |
| 22 | +- Source: Patchwork paper, *"the points annotated with selected classes, i.e. lane marking, road, parking, sidewalk, other ground, vegetation, and terrain, are considered to be ground-truth ground points... only points whose z values are below −1.3 m with respect to the sensor frame are considered as ground truths"*. |
| 23 | + |
| 24 | +Use this when comparing against numbers from the **original Patchwork paper / `url-kaist/patchwork`**. |
| 25 | + |
| 26 | +### B. `--eval_protocol patchworkpp` (Patchwork++ paper Table I protocol — DEFAULT for reproducing the Patchwork++ paper) |
| 27 | + |
| 28 | +- **Ground GT** = `{ROAD, PARKING, SIDEWALK, OTHER_GROUND, LANE_MARKING, TERRAIN}` — **no VEGETATION**. |
| 29 | +- VEGETATION, UNLABELED, OUTLIER are **fully excluded** from both numerator and denominator. |
| 30 | +- Source: Patchwork++ paper Sec. IV.A, *"unlike our previous work, the points labeled as vegetation are not evaluated as ground nor non-ground points exceptionally because it is impractical to regard the vegetation as a single ground or non-ground class. Note that this implies the points labeled as vegetation are only excluded in the evaluation step; the points are still included in the input point cloud"*. |
| 31 | + |
| 32 | +Use this when comparing against numbers from the **Patchwork++ paper**. |
| 33 | + |
| 34 | +### Why it matters |
| 35 | + |
| 36 | +Same Patchwork++ inference, KITTI 00–10 macro average, two protocols: |
| 37 | + |
| 38 | +| Protocol | Precision | Recall | F1 | |
| 39 | +|---|---|---|---| |
| 40 | +| `--eval_protocol patchwork` | 93.72 | 92.33 | 92.87 | |
| 41 | +| `--eval_protocol patchworkpp` | **95.55** | **97.16** | **96.29** | |
| 42 | +| Patchwork++ paper Table I | 94.92 | 98.18 | 96.51 | |
| 43 | + |
| 44 | +3.4 F1 difference, entirely from the protocol switch. If your reproduction is 3 F1 low, this is almost certainly the cause. |
| 45 | + |
| 46 | +______________________________________________________________________ |
| 47 | + |
| 48 | +## 2. Parameter tuning |
| 49 | + |
| 50 | +If results look wrong on a new sensor (Velodyne 16/32, Ouster 64/128, Livox, etc.), tune in roughly this order. Defaults are in `cpp/patchworkpp/include/patchwork/patchworkpp.h` (Patchwork++) and `cpp/patchwork/include/patchwork/patchwork.h` (classic Patchwork). |
| 51 | + |
| 52 | +### Step 1 — Get `sensor_height` right (the most important parameter) |
| 53 | + |
| 54 | +`sensor_height` is the **height of the LiDAR origin above the ground** when the vehicle is stationary on flat pavement. |
| 55 | + |
| 56 | +- KITTI / HDL-64E on a passenger car: `1.723` m (default). |
| 57 | +- Ouster OS0-128 on a UGV: typically 0.6–1.0 m. |
| 58 | +- Livox Mid-360 on a quadruped: typically 0.3–0.6 m. |
| 59 | + |
| 60 | +**How to tell it is wrong**: precision is fine on far-range patches but ground points near the sensor are split between ground and non-ground in a striped pattern. The elevation threshold and adaptive seed selection both reference `sensor_height` directly. |
| 61 | + |
| 62 | +If you cannot measure it, leave `ATAT_ON = true` and the All-Terrain Automatic heighT estimator will recover it from the first scan. |
| 63 | + |
| 64 | +### Step 2 — Tune `uprightness_thr` for the surface roughness you expect |
| 65 | + |
| 66 | +`uprightness_thr` is the cosine of the maximum tilt angle accepted for a patch's normal vs. world-up. Higher = stricter. |
| 67 | + |
| 68 | +| Setting | Max tilt | When to use | |
| 69 | +|---|---|---| |
| 70 | +| 0.5 | ~60° | very rough terrain, off-road; library default for Patchwork++ | |
| 71 | +| **0.707** | **~45°** | **Patchwork paper / on-road / structured driving — recommended for KITTI** | |
| 72 | +| 0.866 | ~30° | flat indoor floors, parking lots | |
| 73 | + |
| 74 | +If precision is low and you see ramps, low walls, or curbs being labelled as ground: increase to 0.707 or 0.866. |
| 75 | +If recall is low on hills, ramps, or rough pavement: lower to 0.5 or 0.4. |
| 76 | + |
| 77 | +### Step 3 — Set range bounds `min_range` / `max_range` |
| 78 | + |
| 79 | +- `min_range` (default 2.7 m): exclude the cone right under the sensor where points are noisy (vehicle body, multipath). Decrease only if your sensor mount is very low. |
| 80 | +- `max_range` (default 80.0 m): the cap of the concentric zone model. The CZM zone sizes scale with this; resetting it requires rebuilding `min_ranges`. For most rotating LiDARs leave at 80 m. |
| 81 | + |
| 82 | +### Step 4 — Tune the plane-fit thresholds |
| 83 | + |
| 84 | +- `th_seeds` (default 0.5 m): a point is an LPR seed if its `z` is within `th_seeds` of the lowest-z mean. Larger → more seeds → tolerates undulating ground but admits more outliers. Lower for very flat scenes. |
| 85 | +- `th_dist` (default 0.125 m): distance from the fitted plane below which a point counts as ground. **This is the single biggest precision/recall knob.** Increase (0.15–0.2 m) on rough/sloped ground if recall is low. Decrease (0.05–0.1 m) on parking lots if precision is low. |
| 86 | +- `num_iter` (default 3): plane-refit iterations per patch. 2–3 is enough; more is wasted CPU. |
| 87 | + |
| 88 | +### Step 5 — `elevation_thr` and `flatness_thr` (only if you've changed the sensor mount or scene scale) |
| 89 | + |
| 90 | +`elevation_thr = {0.523, 0.746, 0.879, 1.125}` are the **ground-frame** height cutoffs for the four closest CZM rings — patches whose mean is more than this above the ground are rejected unless their planarity (`flatness_thr`) saves them. The library converts these to sensor-frame internally by subtracting `sensor_height`. |
| 91 | + |
| 92 | +Rule of thumb: scale them ∝ `expected_terrain_undulation / 1.723 m` if your sensor sits lower or higher than KITTI. Most users do **not** need to touch these. |
| 93 | + |
| 94 | +### Step 6 — Patchwork++ extras (`pypatchworkpp.patchworkpp` only) |
| 95 | + |
| 96 | +- `enable_RNR` (default true) — Reflected Noise Removal. Turn off only if your sensor has very clean returns near the bottom rings (most rotating LiDARs need it on). |
| 97 | +- `enable_RVPF` (default true) — Region-wise Vertical Plane Fitting. Helps on retaining walls / curbs. Keep on. |
| 98 | +- `enable_TGR` (default true) — Temporal Ground Revert. Reverts FN under-segmentation. Keep on. |
| 99 | +- `RNR_intensity_thr` (default 0.2) — RNR's intensity gate. Calibrate to your sensor's intensity scale: if intensities are 0–255, set to ~50. |
| 100 | + |
| 101 | +______________________________________________________________________ |
| 102 | + |
| 103 | +## 3. Reproducing paper Table I |
| 104 | + |
| 105 | +```bash |
| 106 | +# 1. Install once |
| 107 | +pip install -v ./python/ |
| 108 | + |
| 109 | +# 2. Reproduce Patchwork++ Table I row on KITTI 00–10 |
| 110 | +python python/examples/evaluate_semantickitti.py \ |
| 111 | + --method patchworkpp \ |
| 112 | + --eval_protocol patchworkpp \ |
| 113 | + --dataset_path /path/to/SemanticKITTI/sequences \ |
| 114 | + --output_csv summary_patchworkpp.csv |
| 115 | +``` |
| 116 | + |
| 117 | +Expected output (full sweep, 23,201 frames): |
| 118 | + |
| 119 | +| seq | frames | P | R | F1 | |
| 120 | +|---|---|---|---|---| |
| 121 | +| Avg | 23201 | **95.55** | **97.16** | **96.29** | |
| 122 | + |
| 123 | +Paper Table I: P=94.92, R=98.18, F1=96.51 — match within ±0.22 F1. |
| 124 | + |
| 125 | +### Quick smoke test (3 frames per seq, ~5 s total) |
| 126 | + |
| 127 | +```bash |
| 128 | +python python/examples/evaluate_semantickitti.py \ |
| 129 | + --method patchworkpp \ |
| 130 | + --eval_protocol patchworkpp \ |
| 131 | + --dataset_path /path/to/SemanticKITTI/sequences \ |
| 132 | + --max_frames 3 --verbose |
| 133 | +``` |
| 134 | + |
| 135 | +### Apples-to-apples vs. the original Patchwork repo |
| 136 | + |
| 137 | +```bash |
| 138 | +# Compare the in-repo classic Patchwork against the original ROS 2 patchwork |
| 139 | +python python/examples/evaluate_semantickitti.py \ |
| 140 | + --method patchwork \ |
| 141 | + --eval_protocol patchworkpp \ |
| 142 | + --dataset_path /path/to/SemanticKITTI/sequences \ |
| 143 | + --output_csv summary_patchwork.csv |
| 144 | +``` |
| 145 | + |
| 146 | +`--method patchwork` will be paper-faithful after the fixes on this branch (#89) land — until then it is ~2.3 F1 below the original Patchwork on the same protocol. |
| 147 | + |
| 148 | +______________________________________________________________________ |
| 149 | + |
| 150 | +## See also |
| 151 | + |
| 152 | +- [`python/examples/demo_visualize.py`](python/examples/demo_visualize.py) — single-frame visualisation. |
| 153 | +- [`python/examples/demo_sequential.py`](python/examples/demo_sequential.py) — iterate over a folder of `.bin` files. |
| 154 | +- Issues: [#87](https://github.com/url-kaist/patchwork-plusplus/issues/87) (reproduce paper), [#88](https://github.com/url-kaist/patchwork-plusplus/issues/88) (evaluation protocol), [#89](https://github.com/url-kaist/patchwork-plusplus/issues/89) (performance enhancement). |
0 commit comments