mapping_3d/scan_matching/fast_correlative_scan_matcher.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2016 The Cartographer Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <algorithm>
20 #include <cmath>
21 #include <functional>
22 #include <limits>
23 
24 #include "Eigen/Geometry"
28 #include "cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h"
30 #include "glog/logging.h"
31 
32 namespace cartographer {
33 namespace mapping_3d {
34 namespace scan_matching {
35 
36 proto::FastCorrelativeScanMatcherOptions
38  common::LuaParameterDictionary* const parameter_dictionary) {
39  proto::FastCorrelativeScanMatcherOptions options;
40  options.set_branch_and_bound_depth(
41  parameter_dictionary->GetInt("branch_and_bound_depth"));
42  options.set_full_resolution_depth(
43  parameter_dictionary->GetInt("full_resolution_depth"));
44  options.set_rotational_histogram_size(
45  parameter_dictionary->GetInt("rotational_histogram_size"));
46  options.set_min_rotational_score(
47  parameter_dictionary->GetDouble("min_rotational_score"));
48  options.set_linear_xy_search_window(
49  parameter_dictionary->GetDouble("linear_xy_search_window"));
50  options.set_linear_z_search_window(
51  parameter_dictionary->GetDouble("linear_z_search_window"));
52  options.set_angular_search_window(
53  parameter_dictionary->GetDouble("angular_search_window"));
54  return options;
55 }
56 
58  public:
60  const HybridGrid& hybrid_grid,
61  const proto::FastCorrelativeScanMatcherOptions& options) {
62  CHECK_GE(options.branch_and_bound_depth(), 1);
63  CHECK_GE(options.full_resolution_depth(), 1);
64  precomputation_grids_.reserve(options.branch_and_bound_depth());
65  precomputation_grids_.push_back(ConvertToPrecomputationGrid(hybrid_grid));
66  Eigen::Array3i last_width = Eigen::Array3i::Ones();
67  for (int depth = 1; depth != options.branch_and_bound_depth(); ++depth) {
68  const bool half_resolution = depth >= options.full_resolution_depth();
69  const Eigen::Array3i next_width = ((1 << depth) * Eigen::Array3i::Ones());
70  const int full_voxels_per_high_resolution_voxel =
71  1 << std::max(0, depth - options.full_resolution_depth());
72  const Eigen::Array3i shift =
73  (next_width - last_width +
74  (full_voxels_per_high_resolution_voxel - 1)) /
75  full_voxels_per_high_resolution_voxel;
76  precomputation_grids_.push_back(
77  PrecomputeGrid(precomputation_grids_.back(), half_resolution, shift));
78  last_width = next_width;
79  }
80  }
81 
82  const PrecomputationGrid& Get(int depth) const {
83  return precomputation_grids_.at(depth);
84  }
85 
86  int max_depth() const { return precomputation_grids_.size() - 1; }
87 
88  private:
89  std::vector<PrecomputationGrid> precomputation_grids_;
90 };
91 
93  const HybridGrid& hybrid_grid,
94  const std::vector<mapping::TrajectoryNode>& nodes,
95  const proto::FastCorrelativeScanMatcherOptions& options)
96  : options_(options),
97  resolution_(hybrid_grid.resolution()),
98  width_in_voxels_(hybrid_grid.grid_size()),
99  precomputation_grid_stack_(
100  common::make_unique<PrecomputationGridStack>(hybrid_grid, options)),
101  rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {}
102 
104 
106  const transform::Rigid3d& initial_pose_estimate,
107  const sensor::PointCloud& coarse_point_cloud,
108  const sensor::PointCloud& fine_point_cloud, const float min_score,
109  float* const score, transform::Rigid3d* const pose_estimate) const {
110  const SearchParameters search_parameters{
111  common::RoundToInt(options_.linear_xy_search_window() / resolution_),
112  common::RoundToInt(options_.linear_z_search_window() / resolution_),
113  options_.angular_search_window()};
114  return MatchWithSearchParameters(search_parameters, initial_pose_estimate,
115  coarse_point_cloud, fine_point_cloud,
116  min_score, score, pose_estimate);
117 }
118 
120  const Eigen::Quaterniond& gravity_alignment,
121  const sensor::PointCloud& coarse_point_cloud,
122  const sensor::PointCloud& fine_point_cloud, const float min_score,
123  float* const score, transform::Rigid3d* const pose_estimate) const {
124  const transform::Rigid3d initial_pose_estimate(Eigen::Vector3d::Zero(),
125  gravity_alignment);
126  float max_point_distance = 0.f;
127  for (const Eigen::Vector3f& point : coarse_point_cloud) {
128  max_point_distance = std::max(max_point_distance, point.norm());
129  }
130  const int linear_window_size =
131  (width_in_voxels_ + 1) / 2 +
132  common::RoundToInt(max_point_distance / resolution_ + 0.5f);
133  const SearchParameters search_parameters{linear_window_size,
134  linear_window_size, M_PI};
135  return MatchWithSearchParameters(search_parameters, initial_pose_estimate,
136  coarse_point_cloud, fine_point_cloud,
137  min_score, score, pose_estimate);
138 }
139 
141  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
142  const transform::Rigid3d& initial_pose_estimate,
143  const sensor::PointCloud& coarse_point_cloud,
144  const sensor::PointCloud& fine_point_cloud, const float min_score,
145  float* const score, transform::Rigid3d* const pose_estimate) const {
146  CHECK_NOTNULL(score);
147  CHECK_NOTNULL(pose_estimate);
148 
149  const std::vector<DiscreteScan> discrete_scans = GenerateDiscreteScans(
150  search_parameters, coarse_point_cloud, fine_point_cloud,
151  initial_pose_estimate.cast<float>());
152 
153  const std::vector<Candidate> lowest_resolution_candidates =
154  ComputeLowestResolutionCandidates(search_parameters, discrete_scans);
155 
156  const Candidate best_candidate = BranchAndBound(
157  search_parameters, discrete_scans, lowest_resolution_candidates,
158  precomputation_grid_stack_->max_depth(), min_score);
159  if (best_candidate.score > min_score) {
160  *score = best_candidate.score;
161  *pose_estimate =
163  resolution_ * best_candidate.offset.matrix().cast<float>(),
164  Eigen::Quaternionf::Identity()) *
165  discrete_scans[best_candidate.scan_index].pose)
166  .cast<double>();
167  return true;
168  }
169  return false;
170 }
171 
173  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
174  const sensor::PointCloud& point_cloud,
175  const transform::Rigid3f& pose) const {
176  std::vector<std::vector<Eigen::Array3i>> cell_indices_per_depth;
177  const PrecomputationGrid& original_grid = precomputation_grid_stack_->Get(0);
178  std::vector<Eigen::Array3i> full_resolution_cell_indices;
179  for (const Eigen::Vector3f& point :
180  sensor::TransformPointCloud(point_cloud, pose)) {
181  full_resolution_cell_indices.push_back(original_grid.GetCellIndex(point));
182  }
183  const int full_resolution_depth = std::min(options_.full_resolution_depth(),
184  options_.branch_and_bound_depth());
185  CHECK_GE(full_resolution_depth, 1);
186  for (int i = 0; i != full_resolution_depth; ++i) {
187  cell_indices_per_depth.push_back(full_resolution_cell_indices);
188  }
189  const int low_resolution_depth =
190  options_.branch_and_bound_depth() - full_resolution_depth;
191  CHECK_GE(low_resolution_depth, 0);
192  const Eigen::Array3i search_window_start(
193  -search_parameters.linear_xy_window_size,
194  -search_parameters.linear_xy_window_size,
195  -search_parameters.linear_z_window_size);
196  for (int i = 0; i != low_resolution_depth; ++i) {
197  const int reduction_exponent = i + 1;
198  const Eigen::Array3i low_resolution_search_window_start(
199  search_window_start[0] >> reduction_exponent,
200  search_window_start[1] >> reduction_exponent,
201  search_window_start[2] >> reduction_exponent);
202  cell_indices_per_depth.emplace_back();
203  for (const Eigen::Array3i& cell_index : full_resolution_cell_indices) {
204  const Eigen::Array3i cell_at_start = cell_index + search_window_start;
205  const Eigen::Array3i low_resolution_cell_at_start(
206  cell_at_start[0] >> reduction_exponent,
207  cell_at_start[1] >> reduction_exponent,
208  cell_at_start[2] >> reduction_exponent);
209  cell_indices_per_depth.back().push_back(
210  low_resolution_cell_at_start - low_resolution_search_window_start);
211  }
212  }
213  return DiscreteScan{pose, cell_indices_per_depth};
214 }
215 
217  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
218  const sensor::PointCloud& coarse_point_cloud,
219  const sensor::PointCloud& fine_point_cloud,
220  const transform::Rigid3f& initial_pose) const {
221  std::vector<DiscreteScan> result;
222  // We set this value to something on the order of resolution to make sure that
223  // the std::acos() below is defined.
224  float max_scan_range = 3.f * resolution_;
225  for (const Eigen::Vector3f& point : coarse_point_cloud) {
226  const float range = point.norm();
227  max_scan_range = std::max(range, max_scan_range);
228  }
229  const float kSafetyMargin = 1.f - 1e-2f;
230  const float angular_step_size =
231  kSafetyMargin * std::acos(1.f - common::Pow2(resolution_) /
232  (2.f * common::Pow2(max_scan_range)));
233  const int angular_window_size = common::RoundToInt(
234  search_parameters.angular_search_window / angular_step_size);
235  // TODO(whess): Should there be a small search window for rotations around
236  // x and y?
237  std::vector<float> angles;
238  for (int rz = -angular_window_size; rz <= angular_window_size; ++rz) {
239  angles.push_back(rz * angular_step_size);
240  }
241  const std::vector<float> scores = rotational_scan_matcher_.Match(
242  sensor::TransformPointCloud(fine_point_cloud, initial_pose), angles);
243  for (size_t i = 0; i != angles.size(); ++i) {
244  if (scores[i] < options_.min_rotational_score()) {
245  continue;
246  }
247  const Eigen::Vector3f angle_axis(0.f, 0.f, angles[i]);
248  // It's important to apply the 'angle_axis' rotation between the translation
249  // and rotation of the 'initial_pose', so that the rotation is around the
250  // origin of the range data, and yaw is in map frame.
251  const transform::Rigid3f pose(
252  initial_pose.translation(),
254  initial_pose.rotation());
255  result.push_back(
256  DiscretizeScan(search_parameters, coarse_point_cloud, pose));
257  }
258  return result;
259 }
260 
261 std::vector<Candidate>
263  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
264  const int num_discrete_scans) const {
265  const int linear_step_size = 1 << precomputation_grid_stack_->max_depth();
266  const int num_lowest_resolution_linear_xy_candidates =
267  (2 * search_parameters.linear_xy_window_size + linear_step_size) /
268  linear_step_size;
269  const int num_lowest_resolution_linear_z_candidates =
270  (2 * search_parameters.linear_z_window_size + linear_step_size) /
271  linear_step_size;
272  const int num_candidates =
273  num_discrete_scans *
274  common::Power(num_lowest_resolution_linear_xy_candidates, 2) *
275  num_lowest_resolution_linear_z_candidates;
276  std::vector<Candidate> candidates;
277  candidates.reserve(num_candidates);
278  for (int scan_index = 0; scan_index != num_discrete_scans; ++scan_index) {
279  for (int z = -search_parameters.linear_z_window_size;
280  z <= search_parameters.linear_z_window_size; z += linear_step_size) {
281  for (int y = -search_parameters.linear_xy_window_size;
282  y <= search_parameters.linear_xy_window_size;
283  y += linear_step_size) {
284  for (int x = -search_parameters.linear_xy_window_size;
285  x <= search_parameters.linear_xy_window_size;
286  x += linear_step_size) {
287  candidates.emplace_back(scan_index, Eigen::Array3i(x, y, z));
288  }
289  }
290  }
291  }
292  CHECK_EQ(candidates.size(), num_candidates);
293  return candidates;
294 }
295 
297  const int depth, const std::vector<DiscreteScan>& discrete_scans,
298  std::vector<Candidate>* const candidates) const {
299  const int reduction_exponent =
300  std::max(0, depth - options_.full_resolution_depth() + 1);
301  for (Candidate& candidate : *candidates) {
302  int sum = 0;
303  const DiscreteScan& discrete_scan = discrete_scans[candidate.scan_index];
304  const Eigen::Array3i offset(candidate.offset[0] >> reduction_exponent,
305  candidate.offset[1] >> reduction_exponent,
306  candidate.offset[2] >> reduction_exponent);
307  CHECK_LT(depth, discrete_scan.cell_indices_per_depth.size());
308  for (const Eigen::Array3i& cell_index :
309  discrete_scan.cell_indices_per_depth[depth]) {
310  const Eigen::Array3i proposed_cell_index = cell_index + offset;
311  sum += precomputation_grid_stack_->Get(depth).value(proposed_cell_index);
312  }
313  candidate.score = PrecomputationGrid::ToProbability(
314  sum /
315  static_cast<float>(discrete_scan.cell_indices_per_depth[depth].size()));
316  }
317  std::sort(candidates->begin(), candidates->end(), std::greater<Candidate>());
318 }
319 
320 std::vector<Candidate>
322  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
323  const std::vector<DiscreteScan>& discrete_scans) const {
324  std::vector<Candidate> lowest_resolution_candidates =
325  GenerateLowestResolutionCandidates(search_parameters,
326  discrete_scans.size());
327  ScoreCandidates(precomputation_grid_stack_->max_depth(), discrete_scans,
328  &lowest_resolution_candidates);
329  return lowest_resolution_candidates;
330 }
331 
333  const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
334  const std::vector<DiscreteScan>& discrete_scans,
335  const std::vector<Candidate>& candidates, const int candidate_depth,
336  float min_score) const {
337  if (candidate_depth == 0) {
338  // Return the best candidate.
339  return *candidates.begin();
340  }
341 
342  Candidate best_high_resolution_candidate(0, Eigen::Array3i::Zero());
343  best_high_resolution_candidate.score = min_score;
344  for (const Candidate& candidate : candidates) {
345  if (candidate.score <= min_score) {
346  break;
347  }
348  std::vector<Candidate> higher_resolution_candidates;
349  const int half_width = 1 << (candidate_depth - 1);
350  for (int z : {0, half_width}) {
351  if (candidate.offset.z() + z > search_parameters.linear_z_window_size) {
352  break;
353  }
354  for (int y : {0, half_width}) {
355  if (candidate.offset.y() + y >
356  search_parameters.linear_xy_window_size) {
357  break;
358  }
359  for (int x : {0, half_width}) {
360  if (candidate.offset.x() + x >
361  search_parameters.linear_xy_window_size) {
362  break;
363  }
364  higher_resolution_candidates.emplace_back(
365  candidate.scan_index, candidate.offset + Eigen::Array3i(x, y, z));
366  }
367  }
368  }
369  ScoreCandidates(candidate_depth - 1, discrete_scans,
370  &higher_resolution_candidates);
371  best_high_resolution_candidate = std::max(
372  best_high_resolution_candidate,
373  BranchAndBound(search_parameters, discrete_scans,
374  higher_resolution_candidates, candidate_depth - 1,
375  best_high_resolution_candidate.score));
376  }
377  return best_high_resolution_candidate;
378 }
379 
380 } // namespace scan_matching
381 } // namespace mapping_3d
382 } // namespace cartographer
proto::FastCorrelativeScanMatcherOptions CreateFastCorrelativeScanMatcherOptions(common::LuaParameterDictionary *const parameter_dictionary)
proto::RangeDataInserterOptions options_
PointCloud TransformPointCloud(const PointCloud &point_cloud, const transform::Rigid3f &transform)
Definition: point_cloud.cc:25
const Quaternion & rotation() const
Eigen::Quaternion< T > AngleAxisVectorToRotationQuaternion(const Eigen::Matrix< T, 3, 1 > &angle_axis)
Definition: transform.h:86
std::vector< Candidate > ComputeLowestResolutionCandidates(const SearchParameters &search_parameters, const std::vector< DiscreteScan > &discrete_scans) const
Candidate BranchAndBound(const SearchParameters &search_parameters, const std::vector< DiscreteScan > &discrete_scans, const std::vector< Candidate > &candidates, int candidate_depth, float min_score) const
Eigen::Array3i GetCellIndex(const Eigen::Vector3f &point) const
Definition: hybrid_grid.h:428
constexpr T Pow2(T a)
Definition: math.h:50
int RoundToInt(const float x)
Definition: port.h:42
std::vector< Candidate > GenerateLowestResolutionCandidates(const SearchParameters &search_parameters, int num_discrete_scans) const
Rigid3< OtherType > cast() const
constexpr T Power(T base, int exponent)
Definition: math.h:44
transform::Rigid3d pose
FastCorrelativeScanMatcher(const HybridGrid &hybrid_grid, const std::vector< mapping::TrajectoryNode > &nodes, const proto::FastCorrelativeScanMatcherOptions &options)
std::vector< DiscreteScan > GenerateDiscreteScans(const SearchParameters &search_parameters, const sensor::PointCloud &coarse_point_cloud, const sensor::PointCloud &fine_point_cloud, const transform::Rigid3f &initial_pose) const
const Vector & translation() const
std::vector< Eigen::Vector3f > PointCloud
Definition: point_cloud.h:30
bool MatchWithSearchParameters(const SearchParameters &search_parameters, const transform::Rigid3d &initial_pose_estimate, const sensor::PointCloud &coarse_point_cloud, const sensor::PointCloud &fine_point_cloud, float min_score, float *score, transform::Rigid3d *pose_estimate) const
PrecomputationGrid PrecomputeGrid(const PrecomputationGrid &grid, const bool half_resolution, const Eigen::Array3i &shift)
bool MatchFullSubmap(const Eigen::Quaterniond &gravity_alignment, const sensor::PointCloud &coarse_point_cloud, const sensor::PointCloud &fine_point_cloud, float min_score, float *score, transform::Rigid3d *pose_estimate) const
PrecomputationGridStack(const HybridGrid &hybrid_grid, const proto::FastCorrelativeScanMatcherOptions &options)
PrecomputationGrid ConvertToPrecomputationGrid(const HybridGrid &hybrid_grid)
DiscreteScan DiscretizeScan(const SearchParameters &search_parameters, const sensor::PointCloud &point_cloud, const transform::Rigid3f &pose) const
std::vector< float > Match(const sensor::PointCloud &point_cloud, const std::vector< float > &angles) const
_Unique_if< T >::_Single_object make_unique(Args &&...args)
Definition: make_unique.h:46
bool Match(const transform::Rigid3d &initial_pose_estimate, const sensor::PointCloud &coarse_point_cloud, const sensor::PointCloud &fine_point_cloud, float min_score, float *score, transform::Rigid3d *pose_estimate) const
void ScoreCandidates(int depth, const std::vector< DiscreteScan > &discrete_scans, std::vector< Candidate > *const candidates) const


cartographer
Author(s):
autogenerated on Wed Jun 5 2019 21:57:58