iterative closest points
CEV ICP algorithm library
Loading...
Searching...
No Matches
feature_aware.cpp
Go to the documentation of this file.
1#include <Eigen/Core>
2#include <Eigen/SVD>
3#include <Eigen/Dense>
4
5/* #name Feature-Aware */
6/* #register feature_aware */
7
8/* #desc Builds on top of \ref trimmed_icp. In addition to matching points based on a point-to-point
9distance criteria, matches them based on a local "feature vector."
10*/
11
13
14namespace icp {
15 FeatureAware::FeatureAware(double overlap_rate, double feature_weight, int symmetric_neighbors)
16 : ICP(),
17 overlap_rate(overlap_rate),
18 symmetric_neighbors(symmetric_neighbors),
19 feature_weight(feature_weight),
20 neighbor_weight(1 - feature_weight) {}
21
22 /* #conf "overlap_rate" A `double` between `0.0` and `1.0` for the overlap rate. The default is
23 * `1.0`. */
24 /* #conf "feature_weight" A `double` between `0.0` and `1.0` with default value `0.7`. Decides
25 * how much to weight feature cost versus point-to-point cost. */
26 /* #conf "symmetric_neighbors" An `int` with default value `10`. Decides how many neighbors to
27 * use on each side of a point when constructing the feature vector. */
29 : FeatureAware(config.get<double>("overlap_rate", 0.9),
30 config.get<double>("feature_weight", 0.7),
31 config.get<int>("symmetric_neighbors", 10)) {}
32
34
36 a_current.resize(a.size());
37 a_features.resize(a.size());
38 b_features.resize(b.size());
39
40 b_cm = get_centroid(b);
41
42 compute_features(a, get_centroid(a), a_features);
43 compute_features(b, b_cm, b_features);
44
45 norm_feature_dists = compute_norm_dists(a_features, b_features);
46
47 compute_matches();
48 }
49
51 const size_t n = a.size();
52
53 for (size_t i = 0; i < n; i++) {
54 a_current[i] = transform.apply_to(a[i]);
55 }
56
57 /*
58 #step
59 Matching Step:
60
61 Matches are computed based on a weighted average of the point-to-point cost metric and
62 the feature cost metric (with weight `feature_weight` given to the feature vector
63 cost metric).
64
65 The feature vector for each point is computed as follows. Let \f$ p_i \f$ be the i-th
66 point in the scan, ordered by angle from the scan origin (LiDAR center), and let \f$ c
67 \f$ be the centroid of the scan. Then, we can define \f$ q_i = p_i - c \f$. Also, let \f$
68 n \f$ be the number of `symmetric_neighbors`. The feature vector for \f$ p_k \f$ is then
69 \f$ f_k = \begin{bmatrix} |q_{k - n}| - |q_k| & \ldots & |q_{k - 1}| - |q_k| & |q_{k+1}|
70 - |q_k| & \ldots & |q_{k + n}| - |q_k| \end{bmatrix} \f$. The feature cost metric between
71 two feature vectors \f$ f_a \f$ and \f$ f_b \f$ is simply \f$ |f_a - f_b| \f$.
72 */
73 compute_matches();
74
75 /*
76 #step
77 Trimming Step: see \ref trimmed_icp for details.
78 */
79 std::sort(matches.begin(), matches.end(),
80 [](const auto& a, const auto& b) { return a.cost < b.cost; });
81 size_t new_n = static_cast<size_t>(overlap_rate * n);
82 new_n = std::max<size_t>(new_n, 1); // TODO: bad for scans with 0 points
83
84 // yeah, i know this is inefficient. we'll get back to it later.
85 std::vector<icp::Vector> trimmed_current(new_n);
86 std::vector<icp::Vector> trimmed_b(new_n);
87 for (size_t i = 0; i < new_n; i++) {
88 trimmed_current[i] = a_current[matches[i].point];
89 trimmed_b[i] = b[matches[i].pair];
90 }
91
92 icp::Vector trimmed_cm = get_centroid(trimmed_current);
93 icp::Vector trimmed_b_cm = get_centroid(trimmed_b);
94
95 /* #step SVD: see \ref vanilla_icp for details. */
96 Matrix N = Matrix::Zero();
97 for (size_t i = 0; i < new_n; i++) {
98 N += (trimmed_current[i] - trimmed_cm) * (trimmed_b[i] - trimmed_b_cm).transpose();
99 }
100
101 auto svd = N.jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV);
102 Matrix U = svd.matrixU();
103 Matrix V = svd.matrixV();
104 Matrix R = V * U.transpose();
105
106 /* #step Reflection Handling: see \ref vanilla_icp for details. */
107 if (R.determinant() < 0) {
108 V = V * Eigen::DiagonalMatrix<double, 2>(1, -1);
109 R = V * U.transpose();
110 }
111
113
114 /* #step Transformation Step: see \ref vanilla_icp for details. */
115 transform.translation += trimmed_b_cm - R * trimmed_cm;
116 }
117
118 void FeatureAware::compute_matches() {
119 const size_t n = a.size();
120 const size_t m = b.size();
121
122 Eigen::MatrixXd norm_dists = compute_norm_dists(a_current, b);
123
124 for (size_t i = 0; i < n; i++) {
125 matches[i].point = i;
126 matches[i].cost = std::numeric_limits<double>::infinity();
127 for (size_t j = 0; j < m; j++) {
128 double dist = norm_dists(i, j);
129 double feature_dist = norm_feature_dists(i, j);
130 double cost = neighbor_weight * dist + feature_weight * feature_dist;
131
132 if (cost < matches[i].cost) {
133 matches[i].cost = cost;
134 matches[i].pair = j;
135 }
136 }
137 }
138 }
139
140 void FeatureAware::compute_features(const std::vector<Vector>& points, Vector cm,
141 std::vector<FeatureVector>& features) {
142 for (size_t i = 0; i < points.size(); i++) {
143 Vector p = points[i];
144 double cm_dist_p = (p - cm).norm();
145
146 FeatureVector feature(2 * symmetric_neighbors);
147 feature.setZero();
148
149 size_t lower = std::max((size_t)0, i - symmetric_neighbors);
150 for (size_t j = lower; j < i; j++) {
151 Vector q = points[j];
152 double cm_dist_q = (q - cm).norm();
153 feature[j - lower] = cm_dist_q - cm_dist_p;
154 }
155
156 size_t upper = std::min(points.size() - 1, i + symmetric_neighbors);
157 for (size_t j = i + 1; j <= upper; j++) {
158 Vector q = points[j];
159 double cm_dist_q = (q - cm).norm();
160 feature[j - i - 1 + symmetric_neighbors] = cm_dist_q - cm_dist_p;
161 }
162
163 features[i] = feature;
164 }
165 }
166}
FeatureAware(double overlap_rate, double feature_weight, int symmetric_neighbors)
void setup() override
Per-method setup code.
void iterate() override
Perform one iteration of ICP for the point clouds a and b provided with ICP::begin.
Configuration for ICP instances.
Definition icp.h:80
Interface for iterative closest points.
Definition icp.h:48
RBTransform transform
The current point cloud transformation that is being optimized.
Definition icp.h:58
std::vector< Vector > a
The source point cloud.
Definition icp.h:61
std::vector< Vector > b
The destination point cloud.
Definition icp.h:64
std::vector< Match > matches
The pairing of each point in a to its closest in b.
Definition icp.h:67
Definition driver.h:7
Eigen::Vector2d Vector
Definition geo.h:15
Eigen::Matrix2d Matrix
Definition geo.h:16
Vector get_centroid(const std::vector< Vector > &points)
Definition geo.cpp:10
Matrix rotation
Definition geo.h:21
Vector apply_to(Vector v) const
Definition geo.h:31
Vector translation
Definition geo.h:20