Skip to content

Latest commit

 

History

History
393 lines (309 loc) · 13.9 KB

File metadata and controls

393 lines (309 loc) · 13.9 KB

ERASOR2 — Usage

This is the operational reference for ERASOR2: installation, running the pipeline on SemanticKITTI / HeLiPR / HeLiMOS, visualization, YAML schema, and the practical notes collected during the ROS+catkin to plain-CMake refactor.

The headline numbers and the short reproduction path live in the top-level README.md. This page keeps the deeper reference for users who want to inspect or modify each step.


📦 Installation

ERASOR2 builds with a standard CMake flow on Linux or macOS when PCL, Eigen, OpenCV, OpenMP, Boost, and yaml-cpp are available. No catkin workspace or ROS environment is required.

System packages

# Ubuntu 20.04 / 22.04
sudo apt-get install -y \
    build-essential cmake \
    libpcl-dev libeigen3-dev libopencv-dev libomp-dev \
    libboost-system-dev libboost-filesystem-dev \
    libyaml-cpp-dev

The rerun_sdk 0.32 dependency is fetched by CMake at configure time; no system install is needed.

C++

git clone https://github.com/LimHyungTae/ERASOR2.git
cd ERASOR2
cmake -B build -S .
cmake --build build -j

The same source tree has been verified on Ubuntu 20.04 (GCC 9.4 + PCL 1.10) and Ubuntu 22.04 (GCC 11.4 + PCL 1.12). The build produces these binaries under build/:

Binary Purpose
mapgen Accumulate raw scans into a labelled ground-truth map
run_erasor2 Remove dynamic objects → static map + per-frame MOS labels
compare_map Compare multiple estimates against GT (TP/FP/FN/TN)
accum_4dmos Accumulate 4D-MOS predictions into a static map
fill_removert_labels Fill REMOVERT label files (two positional PCD paths)
helipr_to_kitti, merge_heliclouds HeLiPR / HeLiMOS preprocessing
Q. I don't want to install the system layer myself.

A pre-built image is published at shapelim/opengl-ubuntu20.04-erasor2:latest. Mount the repository and build inside the container:

docker run --rm -v "$PWD":/work -w /work \
    shapelim/opengl-ubuntu20.04-erasor2:latest \
    bash -c "cmake -B build -S . && cmake --build build -j"
Q. Build fails on Ubuntu 24.04 with target "VTK::mpi" was not found.

Ubuntu 24.04 can expose a stale VTK::mpi import in the PCL config. Add find_package(MPI QUIET) before find_package(PCL) in CMakeLists.txt, or build inside the 20.04 docker.

Q. Where are the ROS launch files / RViz configs?

roslaunch erasor2 run_erasor2.launch target_seq:=seq_05 was removed in v1.0. Use the CMake-built binary directly:

./build/run_erasor2 ./config/erasor2/seq_05.yaml

The launch/ and rviz/ directories remain for historical reference, but they are no longer wired into the build. The 2D grid abstraction that previously came from ros-noetic-grid-map-* now lives in include/erasor2/grid_map.hpp: a small subset of the upstream grid_map_core API, matched against the seq-05 parity check. Visualization now uses rerun.io instead of RViz / tf2.

Python (preprocessing + evaluation)

The C++ binaries consume per-frame instance labels, ground labels, and PCD outputs generated by the Python helpers (open3d, pypatchworkpp, and HDBSCAN). The conda recipe is shipped in the repository:

conda env create -f scripts/environment.yml   # creates env "erasor2"
conda activate erasor2

Once the environment is active, scripts/run_pipeline.py can chain preprocessing → mapgen → run_erasor2 → evaluate in one command (see How to run).

Q. open3d import crashes with CXXABI_1.3.15 not found.

Ubuntu 20.04's system libstdc++ only goes up to CXXABI_1.3.14; open3d ≥ 0.18 needs 1.3.15, which lives in the conda env's own libstdc++. Preload it:

export LD_PRELOAD="$CONDA_PREFIX/lib/libstdc++.so.6"

scripts/run_pipeline.py --conda-env <path> sets this automatically.

Q. Why not pure pip?

pypatchworkpp and open3d ship native wheels that need a recent glibc / libstdc++. Mixing them with a system Python on Ubuntu 20.04 often causes ABI mismatches. Conda keeps the Python toolchain isolated, which is what run_pipeline.py expects.


🚀 How to run

ERASOR2 needs three inputs per sequence: raw scans, per-frame instance labels from HDBSCAN, and per-frame ground labels from Patchwork. The Python helper generates these labels, and the Python driver chains the C++ binaries end-to-end.

# 1. Per-frame instance + ground labels (conda env with open3d + pypatchworkpp).
#    --kitti_dir is the directory ABOVE 'dataset/', so that
#    <kitti_dir>/dataset/sequences/05/velodyne/ exists.
python scripts/kitti_clustering.py \
    --kitti_dir /home/url/datasets/kitti \
    --seq 05 --init_stamp 2350 --end_stamp 2670 \
    --save-instance-labels --save-ground-labels

# 2. Full pipeline: mapgen → run_erasor2 → evaluate.
#    Before running, edit `config/erasor2/seq_05.yaml` and set `abs_data_dir`
#    (= <kitti_dir>/dataset/sequences) and `abs_save_dir` (where mapgen
#    and run_erasor2 write the GT/estimated PCDs) for your machine.
python scripts/run_pipeline.py \
    --config config/erasor2/seq_05.yaml \
    --conda-env ~/.miniconda3/envs/erasor2-3.10

Important

Each config/*.yaml ships with example paths from the maintainer's machine. Before running anything that takes a --config, open the file and update dataloader.abs_data_dir + dataloader.abs_save_dir (and, for HeLiPR, the per-sensor paths) to point at your own dataset and output locations. run_pipeline.py derives --kitti_dir for the preprocessing step from abs_data_dir automatically (two parents up).

SemanticKITTI evaluation uses SuMa poses. Place poses_suma_optim.txt inside each sequence directory, e.g. <kitti_dir>/dataset/sequences/05/poses_suma_optim.txt.

Warning

Always inspect the HDBSCAN instance segmentation before running the full pipeline. The clustering quality varies sequence-to-sequence and frame-to-frame; bad clusters (over-segmented dynamic objects, merged car-plus-ground blobs, missed pedestrians) will silently degrade ERASOR2's PR/RR/F1. Use the visualizer below to scrub the labels frame-by-frame and confirm distinct objects get distinct colors before trusting the downstream numbers.

Visualizing the clustering output

scripts/visualize_clustering.py loads the per-frame Velodyne scan, Patchwork ground labels, and HDBSCAN instance labels, then streams them into a rerun viewer on a frame timeline:

  • world/ground — Patchwork++ ground points (brown)
  • world/instances — non-ground points colored by HDBSCAN cluster ID
# Spawns the rerun viewer. Use the same args you passed to kitti_clustering.py.
python scripts/visualize_clustering.py \
    --kitti_dir /home/url/datasets/kitti \
    --seq 05 --init_stamp 2350 --end_stamp 2670

# Or save a .rrd you can open later with `rerun /path/to/file.rrd`:
python scripts/visualize_clustering.py \
    --kitti_dir /home/url/datasets/kitti \
    --seq 05 --init_stamp 2350 --end_stamp 2670 \
    --save cluster_viz.rrd

Drag the timeline slider at the bottom of the viewer to step through frames. --show-raw adds the uncolored raw scan as a third overlay.

The wrapper orchestrates the C++ binaries via subprocess. You can also invoke each binary directly:

./build/mapgen      config/erasor2/seq_05.yaml
./build/run_erasor2 config/erasor2/seq_05.yaml
python scripts/evaluate.py \
    --gt  <abs_save_dir>/<gt>.pcd \
    --est <abs_save_dir>/<est>.pcd
YAML schema (highlights)

Each binary takes one positional argument: the path to a YAML. Any key omitted falls back to the default in include/erasor2/Config.hpp.

start_frame: 2350
end_frame:   2670
is_large_scale: true

dataloader:
  dataset_name: "SemanticKITTI"          # or "HeLiPR"
  abs_data_dir: "/path/to/sequences"
  sequence:    "05"
  abs_save_dir: "/path/to/output"
  instance_seg_method: "hdbscan"         # or "cais"
  accum_interval: 2
  voxel_size:     0.2
  map_voxel_size: 0.2

erasor2:
  grid_resolution:     2.0
  range_of_interest:   60.0
  min_z_voi:          -3.0
  max_z_voi:           1.5
  scan_ratio_threshold: 0.2
  log_odds:            { increment_gain: 2.0, increment: 0.15 }
  region_proposal_thr: 0.8
  moving_object_detection:
    obj_score_soft_thr: 0.8
    obj_score_hard_thr: 14.0
    hard_thr_radius:    10.0
  save_map: true

extrinsic:
  robot_body_size: 2.7
  sensor_height:   1.73
  rotation:    [1, 0, 0,  0, 1, 0,  0, 0, 1]
  translation: [0, 0, 0]

rerun:
  enabled:   true   # false ⇒ every log call is a no-op
  spawn:     true   # launch the rerun viewer subprocess
  save_path: ""     # if non-empty, dump a .rrd instead of spawning

Per-sequence configs live under config/ (seq_05.yaml, seq_07.yaml, ...).

Headless / batch operation

For CI, batch evaluation, or paper-figure generation, point rerun.save_path at a .rrd file and set spawn: false:

rerun:
  enabled:   true
  spawn:     false
  save_path: /tmp/erasor2_run.rrd

Inspect later with rerun /tmp/erasor2_run.rrd. Setting rerun.enabled: false makes every visualization call a no-op (slightly faster end-to-end).


🚚 HeLiPR / HeLiMOS

Warning

This path is experimental and has not been fully validated yet.

The same binaries handle HeLiPR-style data: set dataloader.dataset_name to "HeLiPR" and point abs_data_dir at the directory that contains the per-sensor trees:

<abs_data_dir>/
└── <sensor>/                  # Avia, Aeva, VLP16, or Merged
    ├── velodyne/              # .bin scans
    ├── poses.txt              # KITTI-style 3x4 row-major
    ├── patchwork/             # ground labels (from pypatchworkpp)
    └── hdbscan/               # instance labels (from clusters_hdbscan)

Avia, Aeva, and VLP16 poses are corrected by the per-sensor extrinsic baked into src/dataloader/dataloader.cpp (T_OS2_*). Merged is assumed to be in the Ouster frame and uses identity.

# Per-frame labels (same script as KITTI; just change --seq).
# For HeLiMOS, --kitti_dir is the directory directly containing the
# sequence folders (e.g. .../HeLiMOS/KAIST05/deskewed_LiDAR/).
python scripts/kitti_clustering.py \
    --kitti_dir /home/url/datasets/HeLiMOS/KAIST05/deskewed_LiDAR \
    --seq Merged --init_stamp 8600 --end_stamp 8649 \
    --save-instance-labels --save-ground-labels

# mapgen + run_erasor2 with the HeLiPR config.
./build/mapgen      config/erasor2/helipr_mapgen.yaml
./build/run_erasor2 config/erasor2/helipr_mapgen.yaml

Warning

Same as the KITTI flow: visually inspect a sample of the HDBSCAN instance labels before trusting the downstream metrics. HeLiPR sensors have very different point densities (Avia/Aeva are far sparser than Ouster/Velodyne), so clustering hyperparameters that look fine on one sensor may over- or under-segment on another.

The shipped HeLiPR configs (config/erasor2/HeLiPR.yaml, config/erasor2/HeLiPR_kitti.yaml, and config/erasor2/helipr_mapgen.yaml) contain example paths from the original development machines under /media/...; update abs_data_dir and abs_save_dir before running. The HeLiPR-specific preprocessing binaries helipr_to_kitti and merge_heliclouds (per-sensor extraction → time-aligned merge into Merged) use their own YAMLs; see the dataprocessor: section in config/erasor2/HeLiPR_kitti.yaml.


⚠️ Common issues

  • dataloader.expansion_range defaults to 20. run_erasor2 looks 20 frames before each cluster's start_frame to expand the trajectory submap. For partial copies of a dataset (CI / smoke testing), either copy 20 frames of padding before start_frame, or set expansion_range: 0 in the YAML.
  • The accumulation loop iterates [start_frame, end_frame + accum_interval). With end_frame: 2670 and accum_interval: 2, the loop reads frame 2671. Copy at least one extra frame past end_frame when using a trimmed dataset.
  • mapgen prints [pcl::VoxelGrid] Leaf size too small … on HeLiMOS-sized maps. This is cosmetic: actual voxelization is handled by voxelize_preserving_labels_by_nanoflann, which avoids the integer-index overflow that triggers the PCL warning.

📁 Repository layout

config/             per-sequence YAML configs
include/            public headers (Config, GridMap, rerun logger, utils)
src/                C++ sources for the seven binaries + shared utils
scripts/            Python helpers (preprocessing, evaluation, pipeline driver)
tests/              parity-check CI (workflow disabled until self-hosted runner is registered)
launch/             [legacy] ROS1 launch files, no longer wired into the build
rviz/               [legacy] RViz configs, replaced by the rerun entity tree

🤝 Acknowledgements

ERASOR2 builds on open-source LiDAR tooling, especially PCL, Eigen, rerun.io, pypatchworkpp, and nanoflann. The v1.0/v1.1 transition removed ROS and catkin from the runtime, which was possible because these dependencies are clean to consume from a plain CMake project.