|
| 1 | +#ifndef PATCHWORK_CLASSIC_H |
| 2 | +#define PATCHWORK_CLASSIC_H |
| 3 | + |
| 4 | +#include <vector> |
| 5 | + |
| 6 | +#include <Eigen/Dense> |
| 7 | + |
| 8 | +#include "patchwork/patchworkpp.h" // for patchwork::PointXYZ |
| 9 | + |
| 10 | +namespace patchwork { |
| 11 | + |
| 12 | +struct PCAFeature { |
| 13 | + Eigen::Vector3f normal_; |
| 14 | + Eigen::Vector3f mean_; |
| 15 | + Eigen::Vector3f singular_values_; |
| 16 | + float d_; |
| 17 | + float th_dist_d_; |
| 18 | + float linearity_; |
| 19 | + float planarity_; |
| 20 | +}; |
| 21 | + |
| 22 | +enum class PatchStatus { |
| 23 | + NotAssigned = -2, |
| 24 | + FewPoints = -1, |
| 25 | + UprightEnough = 0, |
| 26 | + FlatEnough = 1, |
| 27 | + TooHighElevation = 2, |
| 28 | + TooTilted = 3, |
| 29 | + GloballyTooHighElevation = 4, |
| 30 | +}; |
| 31 | + |
| 32 | +struct PatchworkParams { |
| 33 | + // Sensor / range |
| 34 | + double sensor_height = 1.723; |
| 35 | + double max_range = 80.0; |
| 36 | + double min_range = 2.7; |
| 37 | + |
| 38 | + // Concentric Zone Model (parametric) |
| 39 | + int num_zones = 4; |
| 40 | + std::vector<int> num_sectors_each_zone = {16, 32, 54, 32}; |
| 41 | + std::vector<int> num_rings_each_zone = {2, 4, 4, 4}; |
| 42 | + std::vector<double> min_ranges = {2.7, 12.3625, 22.025, 41.35}; |
| 43 | + |
| 44 | + // Plane fit |
| 45 | + int num_iter = 3; |
| 46 | + int num_lpr = 20; |
| 47 | + int num_min_pts = 10; |
| 48 | + double th_seeds = 0.5; |
| 49 | + double th_dist = 0.125; |
| 50 | + |
| 51 | + // Ground likelihood thresholds (fixed, the Patchwork classic style) |
| 52 | + double uprightness_thr = 0.5; |
| 53 | + std::vector<double> elevation_thr = {0.523, 0.746, 0.879, 1.125}; |
| 54 | + std::vector<double> flatness_thr = {0.0005, 0.000725, 0.001, 0.001}; |
| 55 | + |
| 56 | + // Adaptive seed selection margin for highly tilted ground |
| 57 | + double adaptive_seed_selection_margin = -1.1; |
| 58 | + |
| 59 | + // Global elevation guard |
| 60 | + bool using_global_thr = true; |
| 61 | + double global_elevation_thr = 0.0; |
| 62 | + |
| 63 | + // ATAT (default ON) |
| 64 | + bool ATAT_ON = true; |
| 65 | + double max_h_for_ATAT = 0.3; |
| 66 | + int num_sectors_for_ATAT = 20; |
| 67 | + double noise_bound = 0.2; |
| 68 | + |
| 69 | + bool verbose = false; |
| 70 | +}; |
| 71 | + |
| 72 | +class PatchWork { |
| 73 | + public: |
| 74 | + PatchWork() = default; |
| 75 | + explicit PatchWork(const PatchworkParams& params); |
| 76 | + |
| 77 | + void estimateGround(const Eigen::MatrixXf& cloud); |
| 78 | + |
| 79 | + Eigen::MatrixX3f getGround() const; |
| 80 | + Eigen::MatrixX3f getNonground() const; |
| 81 | + std::vector<int> getGroundIndices() const; |
| 82 | + std::vector<int> getNongroundIndices() const; |
| 83 | + double getTimeTaken() const; |
| 84 | + double getHeight() const; |
| 85 | + |
| 86 | + private: |
| 87 | + // Helper functions (defined in patchwork.cpp in later tasks) |
| 88 | + void initialize(); |
| 89 | + void flush(); |
| 90 | + double xy2theta(double x, double y) const; |
| 91 | + double xy2radius(double x, double y) const; |
| 92 | + void pc2regionwise_patches(const std::vector<PointXYZ>& src); |
| 93 | + void estimate_plane(const std::vector<PointXYZ>& seeds, PCAFeature& out); |
| 94 | + void extract_initial_seeds(int zone_idx, |
| 95 | + const std::vector<PointXYZ>& sorted, |
| 96 | + std::vector<PointXYZ>& seeds); |
| 97 | + PatchStatus determine_gle_status(int zone_idx, int ring_idx, const PCAFeature& feature) const; |
| 98 | + void perform_regionwise_segmentation(int zone_idx, |
| 99 | + int ring_idx, |
| 100 | + const std::vector<PointXYZ>& patch, |
| 101 | + std::vector<PointXYZ>& patch_ground, |
| 102 | + std::vector<PointXYZ>& patch_nonground, |
| 103 | + PatchStatus& status_out); |
| 104 | + void estimate_sensor_height(std::vector<PointXYZ>& cloud); |
| 105 | + double consensus_set_based_height_estimation(const std::vector<double>& candidate_heights); |
| 106 | + void materialize() const; |
| 107 | + |
| 108 | + PatchworkParams params_; |
| 109 | + |
| 110 | + // Per-iteration scratch (cleared each estimateGround call) |
| 111 | + using Sector = std::vector<PointXYZ>; |
| 112 | + using Ring = std::vector<Sector>; // sectors within one ring |
| 113 | + using Zone = std::vector<Ring>; // rings within one zone |
| 114 | + using RegionwisePatches = std::vector<Zone>; |
| 115 | + RegionwisePatches regionwise_patches_; |
| 116 | + |
| 117 | + // Materialized output points (per-call) |
| 118 | + std::vector<PointXYZ> ground_pts_; |
| 119 | + std::vector<PointXYZ> nonground_pts_; |
| 120 | + |
| 121 | + // Cached output matrices/indices (lazy via materialize()) |
| 122 | + mutable Eigen::MatrixX3f ground_mat_; |
| 123 | + mutable Eigen::MatrixX3f nonground_mat_; |
| 124 | + mutable std::vector<int> ground_idx_; |
| 125 | + mutable std::vector<int> nonground_idx_; |
| 126 | + mutable bool outputs_dirty_ = true; |
| 127 | + |
| 128 | + double time_taken_ = 0.0; |
| 129 | + double sensor_height_ = 1.723; // updated by ATAT if enabled |
| 130 | +}; |
| 131 | + |
| 132 | +} // namespace patchwork |
| 133 | + |
| 134 | +#endif |
0 commit comments