A production-oriented, hardware-independent 3D LiDAR obstacle pipeline for
Ubuntu 24.04 and ROS 2 Jazzy. It consumes sensor_msgs/PointCloud2, produces
ground and nonground point clouds, clustered 3D boxes and box centers, optional
temporally consistent track IDs and estimated velocities, RViz markers,
standard diagnostics, and typed metrics.
The hot path is C++17/PCL. A deterministic synthetic scene and dataset player make the complete graph usable without LiDAR hardware.
The v1.1.0 release extends the pipeline from deterministic synthetic validation to reproducible real-data engineering evaluation using KITTI raw LiDAR frames.
It adds KITTI playback, production-code-based offline benchmarking, per-stage performance profiling, reproducible parameter sweeps, machine-readable experimental evidence, and documented failure analysis.
Across the documented KITTI frames 000000–000002, with 10 timed repetitions
per configuration:
| Metric | Result |
|---|---|
| Mean input points | 120,847.7 |
| Mean filtered points | 25,814.0 |
| Mean ground points | 11,113.0 |
| Mean nonground points | 14,701.0 |
| Mean clusters | 50.0 |
| Mean processing time | 62.102 ms |
| Mean clustering time | 45.268 ms |
Euclidean clustering accounted for approximately 72.9% of baseline processing time and was the dominant stage.
- Voxel leaf size
0.10 → 0.40 m: processing time decreased from147.859 → 10.208 ms. - Cluster tolerance
0.30 → 1.00 m: mean cluster count decreased from98.0 → 24.3, demonstrating fragmentation-to-merging pressure. - Minimum cluster size
3 → 60: mean cluster count decreased from105.3 → 16.3. - Ground distance threshold
0.10 → 0.35 m: mean ground-classified points increased from7,956.7 → 13,415.0.
These results are controlled engineering measurements on a small three-frame subset and are not claims of dataset-wide KITTI detection accuracy.
Detailed reports:
REAL_DATA_VALIDATION_V1_1.mdPARAMETER_SENSITIVITY_V1_1.mdENGINEERING_NOTES.mdRELEASE_READINESS_V1_1.md
- finite-point cleanup, voxel downsampling, and configurable XYZ ROI
- optional statistical or radius outlier filtering
- Z-constrained RANSAC ground plane and nonground extraction
- k-d-tree Euclidean clustering with size gates
- axis-aligned or yaw-oriented PCA bounding boxes
- box center, dimensions, point count, and
vision_msgs/Detection3DArray - optional constant-velocity Kalman tracking with gated data association
- debug clouds, RViz markers, standard
/diagnostics, and frame metrics - deterministic synthetic publisher and ordered PCD/KITTI playback
- GTest coverage, lint integration, GitHub Actions, and JSON benchmark tooling
Install ROS 2 Jazzy Desktop (or ROS Base plus RViz), PCL, and the package dependencies. From the repository root:
source /opt/ros/jazzy/setup.bash
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo
source install/setup.bashThe deterministic synthetic scene exercises ground segmentation, Euclidean clustering, 3D bounding-box estimation, and optional multi-object tracking without requiring physical LiDAR hardware.
ros2 launch lidar_perception synthetic.launch.py rviz:=trueThe fixed frame is lidar. The synthetic scene contains a noisy planar road,
three cuboids, sparse outliers, and one moving cuboid. Generation is seeded, so
regression runs are repeatable while each scan still has different samples.
For a real sensor or rosbag that publishes /points_raw:
ros2 launch lidar_perception pipeline.launch.pyOverride or remap the point-cloud topic as required.
For PCD or KITTI binary point clouds:
ros2 launch lidar_perception dataset.launch.py \
path:=/data/pcd_sequence \
format:=auto \
rate:=10The dataset player accepts one .pcd or KITTI .bin file, or a directory, and
processes files in lexical order. format may be auto, pcd, or kitti;
auto infers each frame from its extension. KITTI records must contain four
32-bit floats per point (x, y, z, and reflectance).
All algorithm settings are in
perception.yaml. Parameters can
be changed at runtime except for the subscription topic and queue depth, which
are startup-only. Invalid updates are rejected as a set, and accepted updates
reset tracker state. Important choices are:
outlier.method:none,statistical, orradiusbbox.oriented: PCA yaw box when true, AABB when falsetracking.enabled: bypass or enable confirmed-track outputpublish_debug_clouds: disable three large cloud publications in deployment
Namespaced output topics resolve beneath /lidar_perception. See
ARCHITECTURE.md for the exact graph and contracts and
ALGORITHMS.md for assumptions and tuning guidance.
colcon test --event-handlers console_direct+
colcon test-result --verbose
ros2 run lidar_perception run_benchmark.py \
--warmup 3 --duration 30 --output results/benchmark.jsonThe benchmark prints and writes only values observed from PipelineMetrics in
that run. No performance claim is hard-coded. Interpretation and limitations
are documented in BENCHMARKING.md.
The checked-in synthetic benchmark was recorded using ROS 2 Jazzy, Fast DDS,
a RelWithDebInfo build, and the documented seeded synthetic workload.
| Metric | Result |
|---|---|
| Samples | 102 |
| Mean callback latency | 13.178 ms |
| p95 callback latency | 14.107 ms |
| Observed workload rate | 10.003 Hz |
| Clusters per sampled frame | 3.0 |
| Confirmed tracks per sampled frame | 3.0 |
| Adjacent-frame ID retention | 1.0 |
| Test-result entries (current v1.1 suite) | 99 |
| Errors / failures | 0 / 0 |
| Skipped | 14 (cppcheck safeguard in ROS 2 Jazzy) |
| CTest | 13/13 passed |
| GitHub Actions | Passed |
The 10.003 Hz value reflects the configured synthetic input rate and is not a maximum-throughput measurement. Results apply only to the documented host, middleware, parameters, build, and synthetic workload.
See docs/evidence/README.md and
docs/evidence/synthetic_benchmark.json
for the full evidence and provenance.
The v1.1 offline runner, measured KITTI subset results, and one-factor sweeps
are documented in
REAL_DATA_VALIDATION_V1_1.md and
PARAMETER_SENSITIVITY_V1_1.md.
The checked-in validation evidence includes an actual RViz capture and the raw JSON from a reproducible synthetic benchmark run.
src/
├── lidar_perception_msgs/ ROS 2 message contracts
└── lidar_perception/ C++ perception library, nodes, launch/config/RViz, tests
docs/ Architecture, algorithms, benchmarking, evidence
.github/workflows/ ROS 2 Jazzy CI
- The incoming frame is preserved; no implicit TF transform is performed.
- The baseline uses one dominant ground plane and fixed-radius clustering. Sloped/undulating roads and strongly range-dependent density need more advanced segmentation/tolerance models.
- Synthetic geometry validates behavior, integration, and repeatability. It is not a substitute for labeled-dataset precision/recall evaluation.
- Track IDs are local to a process lifetime and parameter configuration.
Added KITTI LiDAR playback, reproducible offline benchmarking, per-stage profiling, parameter-sensitivity experiments, failure analysis, and machine-readable real-data evidence.
Established the ROS 2 Jazzy C++17/PCL perception pipeline with deterministic synthetic validation, clustering, bounding boxes, optional tracking, diagnostics, and reproducible benchmarking.
Licensed under the MIT License.
