Skip to content

Commit 451dbea

Browse files
committed
Document all methods / classes
1 parent f3bba86 commit 451dbea

File tree

18 files changed

+686
-118
lines changed

18 files changed

+686
-118
lines changed

config/25.10.08_short_verify.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pipelines:
22
- pipeline: form
3-
name: keypoint_class_final
3+
name: 2_docs_fixed
44

55
datasets:
66
# ncd20

form/feature/extraction.hpp

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
// MIT License
2+
3+
// Copyright (c) 2025 Easton Potokar, Taylor Pool, and Michael Kaess
4+
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
//
23+
/// This extraction code is largely based on Dan McGann's LOAM implementation
24+
/// (https://github.com/DanMcGann/loam/), with some modifications to remove edge
25+
/// features extraction and add in normal estimation.
126
#pragma once
227

328
#include "form/feature/features.hpp"
@@ -28,78 +53,105 @@ template <typename T> struct Curvature {
2853
}
2954
};
3055

56+
/// @brief Class for extracting planar and point features from a LiDAR scan
3157
class FeatureExtractor {
3258
public:
3359
struct Params {
34-
// Parameters for keypoint extraction
60+
// KEYPOINT EXTRACTION PARAMETERS
61+
/// @brief Number of neighboring points to use for curvature computation and for
62+
/// normal estimation
3563
size_t neighbor_points = 5;
64+
/// @brief Number of sectors to divide the scan into for feature extraction
3665
size_t num_sectors = 6;
66+
/// @brief Threshold for planar feature extraction
3767
double planar_threshold = 1.0;
68+
/// @brief Number of planar features to extract per sector
3869
size_t planar_feats_per_sector = 50;
70+
/// @brief Number of point features to extract per sector
3971
size_t point_feats_per_sector = 3;
4072

41-
// Parameters for normal estimation
73+
// NORMAL ESTIMATION PARAMETERS
74+
/// @brief Radius for neighborhood search
4275
double radius = 1.0;
76+
/// @brief Minimum number of points required for normal estimation
4377
size_t min_points = 5;
4478

45-
// Based on LiDAR info
79+
// MISC LiDAR PARAMETERS
80+
/// @brief Minimum range for a point to be considered valid
4681
double min_norm_squared = 1.0;
82+
/// @brief Maximum range for a point to be considered valid
4783
double max_norm_squared = 100.0 * 100.0;
84+
/// @brief Number of columns in the LiDAR scan
4885
int num_columns = 1024;
86+
/// @brief Number of rows aka scanlines in the LiDAR scan
4987
int num_rows = 64;
5088
};
5189

5290
Params params;
5391

54-
FeatureExtractor(const Params &params, size_t num_threads) : params(params) {
92+
/// @brief Explicit parameterized constructor
93+
FeatureExtractor(const Params &params, size_t num_threads = 0) : params(params) {
5594
static const auto tbb_control_settings = set_num_threads(num_threads);
5695
}
5796

5897
// ------------------------- Entrypoint ------------------------- //
98+
/// @brief Extract planar and point features from a LiDAR scan
5999
template <typename Point>
60100
[[nodiscard]] std::tuple<std::vector<PlanarFeat>, std::vector<PointFeat>>
61101
extract(const std::vector<Point> &scan, size_t scan_idx) const;
62102

63103
private:
64104
// ------------------------- Validators ------------------------- //
105+
/// @brief Compute a mask of valid points based on range and NaN checks, with
106+
/// neighbors also marked invalid for planar feature extraction
65107
template <typename Point>
66108
std::vector<bool> compute_valid_points(const std::vector<Point> &scan) const;
67109

110+
/// @brief Compute a mask of valid points based on range and NaN checks, without
111+
/// neighbors marked invalid for point feature extraction
68112
template <typename Point>
69113
std::vector<bool> compute_point_valid_points(const std::vector<Point> &scan) const;
70114

71115
// ------------------------- Computers ------------------------- //
116+
/// @brief Compute the curvature for each point in the scan
72117
template <typename Point>
73118
std::vector<Curvature<typename Point::Scalar>>
74119
compute_curvature(const std::vector<Point> &scan, const std::vector<bool> &mask,
75120
size_t scan_idx) const noexcept;
76121

122+
/// @brief Compute the normal for a point given its index, the scan, and a validity
123+
/// mask
77124
template <typename Point>
78125
std::optional<Eigen::Matrix<typename Point::Scalar, 3, 1>>
79126
compute_normal(const size_t &idx, const std::vector<Point> &scan,
80127
const std::vector<bool> &valid_mask) const noexcept;
81128

82129
// ------------------------- Extractors ------------------------- //
83-
130+
/// @brief Extract planar features from a sector of the scan based on curvature
84131
template <typename T>
85132
void extract_planar(const size_t &sector_start_point,
86133
const size_t &sector_end_point,
87134
const std::vector<Curvature<T>> &curvature,
88135
std::vector<size_t> &out_features,
89136
std::vector<bool> &valid_mask) const noexcept;
90137

138+
/// @brief Extract point features from a sector of the scan
91139
inline void extract_point(const size_t &sector_start_point,
92140
const size_t &sector_end_point,
93141
std::vector<size_t> &out_features,
94142
std::vector<bool> &valid_mask) const noexcept;
95143

96144
// ------------------------- Helpers ------------------------- //
145+
/// @brief Find the index of the closest valid point to a given point within a
146+
/// range (range is usually a scanline)
97147
template <typename Point>
98148
std::optional<size_t>
99149
find_closest(const Point &point, const size_t &start, const size_t &end,
100150
const std::vector<Point> &scan,
101151
const std::vector<bool> &valid_mask) const noexcept;
102152

153+
/// @brief Find the neighboring points of a given point index in the scan, only
154+
/// including points within neighbor_points and radius
103155
template <typename Point>
104156
void find_neighbors(const size_t &idx, const std::vector<Point> &scan,
105157
std::vector<Point> &out) const noexcept;

form/feature/extraction.tpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
// MIT License
2+
3+
// Copyright (c) 2025 Easton Potokar, Taylor Pool, and Michael Kaess
4+
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
122
#pragma once
223

324
#include "form/feature/extraction.hpp"

form/feature/factor.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
// MIT License
2+
3+
// Copyright (c) 2025 Easton Potokar, Taylor Pool, and Michael Kaess
4+
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
122
#include "form/feature/factor.hpp"
223
#include "form/optimization/gtsam.hpp"
324
#include <Eigen/src/Core/Matrix.h>
@@ -119,14 +140,14 @@ FeatureFactor::FeatureFactor(
119140
FastIsotropic::Sigma(sigma, std::get<0>(constraints)->num_residuals() +
120141
std::get<1>(constraints)->num_residuals()),
121142
i, j),
122-
plane_plane(std::get<0>(constraints)), point_point(std::get<1>(constraints)) {}
143+
plane_point(std::get<0>(constraints)), point_point(std::get<1>(constraints)) {}
123144

124145
[[nodiscard]] gtsam::Vector
125146
FeatureFactor::evaluateError(const gtsam::Pose3 &Ti, const gtsam::Pose3 &Tj,
126147
boost::optional<gtsam::Matrix &> H1,
127148
boost::optional<gtsam::Matrix &> H2) const noexcept {
128149

129-
size_t size = plane_plane->num_residuals() + point_point->num_residuals();
150+
size_t size = plane_point->num_residuals() + point_point->num_residuals();
130151
gtsam::Vector residual(size);
131152

132153
if (H1) {
@@ -137,11 +158,11 @@ FeatureFactor::evaluateError(const gtsam::Pose3 &Ti, const gtsam::Pose3 &Tj,
137158
}
138159

139160
// Plane-Plane constraints
140-
size_t start = 0, end = plane_plane->num_residuals();
141-
if (plane_plane->num_residuals() > 0) {
161+
size_t start = 0, end = plane_point->num_residuals();
162+
if (plane_point->num_residuals() > 0) {
142163
Eigen::MatrixXd H1_temp, H2_temp;
143164
residual.segment(start, end - start) =
144-
plane_plane->evaluateError(Ti, Tj, H1 ? &H1_temp : 0, H2 ? &H2_temp : 0);
165+
plane_point->evaluateError(Ti, Tj, H1 ? &H1_temp : 0, H2 ? &H2_temp : 0);
145166
if (H1) {
146167
H1->block(start, 0, end - start, 6) = H1_temp;
147168
}

form/feature/factor.hpp

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
// MIT License
2+
3+
// Copyright (c) 2025 Easton Potokar, Taylor Pool, and Michael Kaess
4+
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
122
#pragma once
223

324
#include <gtsam/geometry/Point3.h>
@@ -17,82 +38,131 @@ static void check(const gtsam::SharedNoiseModel &noiseModel, size_t m) {
1738

1839
using OptionalJacobian = gtsam::OptionalJacobian<Eigen::Dynamic, Eigen::Dynamic>;
1940

41+
/// @brief Structure that holds plane-point correspondences and computes their error
42+
///
43+
/// Points and normals are stored in a std::vector that is then mapped to Eigen
44+
/// matrices for efficient computation.
2045
struct PlanePoint {
46+
/// @brief Shared pointer type
2147
typedef std::shared_ptr<PlanePoint> Ptr;
2248

49+
/// @brief Vectors to hold point and normal data
2350
std::vector<double> p_i;
2451
std::vector<double> n_i;
2552
std::vector<double> p_j;
2653

2754
PlanePoint() = default;
2855

56+
/// @brief Add a new plane-point correspondence
2957
void push_back(const PlanarFeat &p_i_, const PlanarFeat &p_j_) {
3058
p_i.insert(p_i.end(), {p_i_.x, p_i_.y, p_i_.z});
3159
n_i.insert(n_i.end(), {p_i_.nx, p_i_.ny, p_i_.nz});
3260
p_j.insert(p_j.end(), {p_j_.x, p_j_.y, p_j_.z});
3361
}
3462

63+
/// @brief Clear all stored correspondences
3564
void clear() noexcept {
3665
p_i.clear();
3766
n_i.clear();
3867
p_j.clear();
3968
}
4069

70+
/// @brief Get the number of residuals (one per correspondence)
4171
size_t num_residuals() const noexcept { return p_i.size() / 3; }
72+
73+
/// @brief Get the number of constraints / correspondences
4274
size_t num_constraints() const noexcept { return p_i.size() / 3; }
4375

76+
/// @brief Evaluate the residual given two poses
77+
///
78+
/// @param Ti The pose of the first frame
79+
/// @param Tj The pose of the second frame
80+
/// @param residual_D_Ti Optional Jacobian of the residual w.r.t. Ti
81+
/// @param residual_D_Tj Optional Jacobian of the residual w.r.t. Tj
82+
/// @return The residual vector
4483
[[nodiscard]] gtsam::Vector
4584
evaluateError(const gtsam::Pose3 &Ti, const gtsam::Pose3 &Tj,
4685
OptionalJacobian residual_D_Ti = boost::none,
4786
OptionalJacobian residual_D_Tj = boost::none) const noexcept;
4887
};
4988

89+
/// @brief Structure that holds point-point correspondences and computes their error
90+
///
91+
/// Points are stored in a std::vector that is then mapped to Eigen matrices for
92+
/// efficient computation.
5093
struct PointPoint {
94+
/// @brief Shared pointer type
5195
typedef std::shared_ptr<PointPoint> Ptr;
5296

97+
/// @brief Vectors to hold point data
5398
std::vector<double> p_i;
5499
std::vector<double> p_j;
55100

56101
PointPoint() = default;
57102

58-
size_t num_residuals() const noexcept { return p_i.size(); }
59-
size_t num_constraints() const noexcept { return p_i.size() / 3; }
60-
103+
/// @brief Add a new point-point correspondence
61104
void push_back(const PointFeat &p_i_, const PointFeat &p_j_) {
62105
p_i.insert(p_i.end(), {p_i_.x, p_i_.y, p_i_.z});
63106
p_j.insert(p_j.end(), {p_j_.x, p_j_.y, p_j_.z});
64107
}
65108

109+
/// @brief Clear all stored correspondences
66110
void clear() noexcept {
67111
p_i.clear();
68112
p_j.clear();
69113
}
70114

115+
/// @brief Get the number of residuals (three per correspondence)
116+
size_t num_residuals() const noexcept { return p_i.size(); }
117+
118+
/// @brief Get the number of constraints / correspondences
119+
size_t num_constraints() const noexcept { return p_i.size() / 3; }
120+
121+
/// @brief Evaluate the residual given two poses
122+
///
123+
/// @param Ti The pose of the first frame
124+
/// @param Tj The pose of the second frame
125+
/// @param residual_D_Ti Optional Jacobian of the residual w.r.t. Ti
126+
/// @param residual_D_Tj Optional Jacobian of the residual w.r.t. Tj
127+
/// @return The residual vector
71128
[[nodiscard]] gtsam::Vector
72129
evaluateError(const gtsam::Pose3 &Ti, const gtsam::Pose3 &Tj,
73130
OptionalJacobian residual_D_Ti = boost::none,
74131
OptionalJacobian residual_D_Tj = boost::none) const noexcept;
75132
};
76133

134+
/// @brief A wrapper factor that holds both plane-point and point-point constraints
77135
class FeatureFactor : public DenseFactor {
78136
public:
79-
PlanePoint::Ptr plane_plane;
137+
/// @brief Plane-point constraints
138+
PlanePoint::Ptr plane_point;
139+
140+
/// @brief Point-point constraints
80141
PointPoint::Ptr point_point;
81142

82143
public:
144+
/// @brief Constructor
145+
///
146+
/// @param i Key for the first pose
147+
/// @param j Key for the second pose
148+
/// @param constraint A tuple containing plane-point and point-point constraints
149+
/// @param sigma The isotropic noise standard deviation
83150
FeatureFactor(const gtsam::Key i, const gtsam::Key j,
84151
const std::tuple<PlanePoint::Ptr, PointPoint::Ptr> &constraint,
85152
double sigma) noexcept;
86153

154+
/// @brief Evaluate the residual given two poses
155+
///
156+
/// @param Ti The pose of the first frame
157+
/// @param Tj The pose of the second frame
158+
/// @param residual_D_Ti Optional Jacobian of the residual w.r.t. Ti
159+
/// @param residual_D_Tj Optional Jacobian of the residual w.r.t. Tj
160+
/// @return The residual vector
87161
[[nodiscard]] gtsam::Vector
88162
evaluateError(const gtsam::Pose3 &Ti, const gtsam::Pose3 &Tj,
89163
boost::optional<gtsam::Matrix &> residual_D_Ti = boost::none,
90164
boost::optional<gtsam::Matrix &> residual_D_Tj =
91165
boost::none) const noexcept override;
92-
93-
[[nodiscard]] gtsam::Key getKey_i() const noexcept { return keys_[0]; }
94-
95-
[[nodiscard]] gtsam::Key getKey_j() const noexcept { return keys_[1]; }
96166
};
97167

98168
} // namespace form

0 commit comments

Comments
 (0)