Add full submap scan matching in 3D. (#145)
parent
88e4ea2866
commit
318607ccd1
|
@ -95,10 +95,8 @@ FastCorrelativeScanMatcher::FastCorrelativeScanMatcher(
|
||||||
const proto::FastCorrelativeScanMatcherOptions& options)
|
const proto::FastCorrelativeScanMatcherOptions& options)
|
||||||
: options_(options),
|
: options_(options),
|
||||||
resolution_(hybrid_grid.resolution()),
|
resolution_(hybrid_grid.resolution()),
|
||||||
linear_xy_window_size_(
|
origin_(hybrid_grid.origin()),
|
||||||
common::RoundToInt(options_.linear_xy_search_window() / resolution_)),
|
width_in_voxels_(hybrid_grid.grid_size()),
|
||||||
linear_z_window_size_(
|
|
||||||
common::RoundToInt(options_.linear_z_search_window() / resolution_)),
|
|
||||||
precomputation_grid_stack_(
|
precomputation_grid_stack_(
|
||||||
common::make_unique<PrecomputationGridStack>(hybrid_grid, options)),
|
common::make_unique<PrecomputationGridStack>(hybrid_grid, options)),
|
||||||
rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {}
|
rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {}
|
||||||
|
@ -109,19 +107,54 @@ bool FastCorrelativeScanMatcher::Match(
|
||||||
const transform::Rigid3d& initial_pose_estimate,
|
const transform::Rigid3d& initial_pose_estimate,
|
||||||
const sensor::PointCloud& coarse_point_cloud,
|
const sensor::PointCloud& coarse_point_cloud,
|
||||||
const sensor::PointCloud& fine_point_cloud, const float min_score,
|
const sensor::PointCloud& fine_point_cloud, const float min_score,
|
||||||
float* score, transform::Rigid3d* pose_estimate) const {
|
float* const score, transform::Rigid3d* const pose_estimate) const {
|
||||||
|
const SearchParameters search_parameters{
|
||||||
|
common::RoundToInt(options_.linear_xy_search_window() / resolution_),
|
||||||
|
common::RoundToInt(options_.linear_z_search_window() / resolution_),
|
||||||
|
options_.angular_search_window()};
|
||||||
|
return MatchWithSearchParameters(search_parameters, initial_pose_estimate,
|
||||||
|
coarse_point_cloud, fine_point_cloud,
|
||||||
|
min_score, score, pose_estimate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FastCorrelativeScanMatcher::MatchFullSubmap(
|
||||||
|
const Eigen::Quaterniond& gravity_alignment,
|
||||||
|
const sensor::PointCloud& coarse_point_cloud,
|
||||||
|
const sensor::PointCloud& fine_point_cloud, const float min_score,
|
||||||
|
float* const score, transform::Rigid3d* const pose_estimate) const {
|
||||||
|
const transform::Rigid3d initial_pose_estimate(origin_.cast<double>(),
|
||||||
|
gravity_alignment);
|
||||||
|
float max_point_distance = 0.f;
|
||||||
|
for (const Eigen::Vector3f& point : coarse_point_cloud) {
|
||||||
|
max_point_distance = std::max(max_point_distance, point.norm());
|
||||||
|
}
|
||||||
|
const int linear_window_size =
|
||||||
|
(width_in_voxels_ + 1) / 2 + std::ceil(max_point_distance);
|
||||||
|
const SearchParameters search_parameters{linear_window_size,
|
||||||
|
linear_window_size, M_PI};
|
||||||
|
return MatchWithSearchParameters(search_parameters, initial_pose_estimate,
|
||||||
|
coarse_point_cloud, fine_point_cloud,
|
||||||
|
min_score, score, pose_estimate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FastCorrelativeScanMatcher::MatchWithSearchParameters(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
|
const transform::Rigid3d& initial_pose_estimate,
|
||||||
|
const sensor::PointCloud& coarse_point_cloud,
|
||||||
|
const sensor::PointCloud& fine_point_cloud, const float min_score,
|
||||||
|
float* const score, transform::Rigid3d* const pose_estimate) const {
|
||||||
CHECK_NOTNULL(score);
|
CHECK_NOTNULL(score);
|
||||||
CHECK_NOTNULL(pose_estimate);
|
CHECK_NOTNULL(pose_estimate);
|
||||||
|
|
||||||
const std::vector<DiscreteScan> discrete_scans =
|
const std::vector<DiscreteScan> discrete_scans = GenerateDiscreteScans(
|
||||||
GenerateDiscreteScans(coarse_point_cloud, fine_point_cloud,
|
search_parameters, coarse_point_cloud, fine_point_cloud,
|
||||||
initial_pose_estimate.cast<float>());
|
initial_pose_estimate.cast<float>());
|
||||||
|
|
||||||
const std::vector<Candidate> lowest_resolution_candidates =
|
const std::vector<Candidate> lowest_resolution_candidates =
|
||||||
ComputeLowestResolutionCandidates(discrete_scans);
|
ComputeLowestResolutionCandidates(search_parameters, discrete_scans);
|
||||||
|
|
||||||
const Candidate best_candidate =
|
const Candidate best_candidate = BranchAndBound(
|
||||||
BranchAndBound(discrete_scans, lowest_resolution_candidates,
|
search_parameters, discrete_scans, lowest_resolution_candidates,
|
||||||
precomputation_grid_stack_->max_depth(), min_score);
|
precomputation_grid_stack_->max_depth(), min_score);
|
||||||
if (best_candidate.score > min_score) {
|
if (best_candidate.score > min_score) {
|
||||||
*score = best_candidate.score;
|
*score = best_candidate.score;
|
||||||
|
@ -137,6 +170,7 @@ bool FastCorrelativeScanMatcher::Match(
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscreteScan FastCorrelativeScanMatcher::DiscretizeScan(
|
DiscreteScan FastCorrelativeScanMatcher::DiscretizeScan(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
const sensor::PointCloud& point_cloud,
|
const sensor::PointCloud& point_cloud,
|
||||||
const transform::Rigid3f& pose) const {
|
const transform::Rigid3f& pose) const {
|
||||||
std::vector<std::vector<Eigen::Array3i>> cell_indices_per_depth;
|
std::vector<std::vector<Eigen::Array3i>> cell_indices_per_depth;
|
||||||
|
@ -156,7 +190,9 @@ DiscreteScan FastCorrelativeScanMatcher::DiscretizeScan(
|
||||||
options_.branch_and_bound_depth() - full_resolution_depth;
|
options_.branch_and_bound_depth() - full_resolution_depth;
|
||||||
CHECK_GE(low_resolution_depth, 0);
|
CHECK_GE(low_resolution_depth, 0);
|
||||||
const Eigen::Array3i search_window_start(
|
const Eigen::Array3i search_window_start(
|
||||||
-linear_xy_window_size_, -linear_xy_window_size_, -linear_z_window_size_);
|
-search_parameters.linear_xy_window_size,
|
||||||
|
-search_parameters.linear_xy_window_size,
|
||||||
|
-search_parameters.linear_z_window_size);
|
||||||
for (int i = 0; i != low_resolution_depth; ++i) {
|
for (int i = 0; i != low_resolution_depth; ++i) {
|
||||||
const int reduction_exponent = i + 1;
|
const int reduction_exponent = i + 1;
|
||||||
const Eigen::Array3i low_resolution_search_window_start(
|
const Eigen::Array3i low_resolution_search_window_start(
|
||||||
|
@ -178,6 +214,7 @@ DiscreteScan FastCorrelativeScanMatcher::DiscretizeScan(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(
|
std::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
const sensor::PointCloud& coarse_point_cloud,
|
const sensor::PointCloud& coarse_point_cloud,
|
||||||
const sensor::PointCloud& fine_point_cloud,
|
const sensor::PointCloud& fine_point_cloud,
|
||||||
const transform::Rigid3f& initial_pose) const {
|
const transform::Rigid3f& initial_pose) const {
|
||||||
|
@ -194,8 +231,8 @@ std::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(
|
||||||
kSafetyMargin * std::acos(1.f -
|
kSafetyMargin * std::acos(1.f -
|
||||||
common::Pow2(resolution_) /
|
common::Pow2(resolution_) /
|
||||||
(2.f * common::Pow2(max_scan_range)));
|
(2.f * common::Pow2(max_scan_range)));
|
||||||
const int angular_window_size =
|
const int angular_window_size = common::RoundToInt(
|
||||||
common::RoundToInt(options_.angular_search_window() / angular_step_size);
|
search_parameters.angular_search_window / angular_step_size);
|
||||||
// TODO(whess): Should there be a small search window for rotations around
|
// TODO(whess): Should there be a small search window for rotations around
|
||||||
// x and y?
|
// x and y?
|
||||||
std::vector<float> angles;
|
std::vector<float> angles;
|
||||||
|
@ -216,19 +253,23 @@ std::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(
|
||||||
Eigen::Translation3f(initial_pose.translation()) *
|
Eigen::Translation3f(initial_pose.translation()) *
|
||||||
transform::AngleAxisVectorToRotationQuaternion(angle_axis) *
|
transform::AngleAxisVectorToRotationQuaternion(angle_axis) *
|
||||||
Eigen::Quaternionf(initial_pose.rotation()));
|
Eigen::Quaternionf(initial_pose.rotation()));
|
||||||
result.push_back(DiscretizeScan(coarse_point_cloud, pose));
|
result.push_back(
|
||||||
|
DiscretizeScan(search_parameters, coarse_point_cloud, pose));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Candidate>
|
std::vector<Candidate>
|
||||||
FastCorrelativeScanMatcher::GenerateLowestResolutionCandidates(
|
FastCorrelativeScanMatcher::GenerateLowestResolutionCandidates(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
const int num_discrete_scans) const {
|
const int num_discrete_scans) const {
|
||||||
const int linear_step_size = 1 << precomputation_grid_stack_->max_depth();
|
const int linear_step_size = 1 << precomputation_grid_stack_->max_depth();
|
||||||
const int num_lowest_resolution_linear_xy_candidates =
|
const int num_lowest_resolution_linear_xy_candidates =
|
||||||
(2 * linear_xy_window_size_ + linear_step_size) / linear_step_size;
|
(2 * search_parameters.linear_xy_window_size + linear_step_size) /
|
||||||
|
linear_step_size;
|
||||||
const int num_lowest_resolution_linear_z_candidates =
|
const int num_lowest_resolution_linear_z_candidates =
|
||||||
(2 * linear_z_window_size_ + linear_step_size) / linear_step_size;
|
(2 * search_parameters.linear_z_window_size + linear_step_size) /
|
||||||
|
linear_step_size;
|
||||||
const int num_candidates =
|
const int num_candidates =
|
||||||
num_discrete_scans *
|
num_discrete_scans *
|
||||||
common::Power(num_lowest_resolution_linear_xy_candidates, 2) *
|
common::Power(num_lowest_resolution_linear_xy_candidates, 2) *
|
||||||
|
@ -236,11 +277,13 @@ FastCorrelativeScanMatcher::GenerateLowestResolutionCandidates(
|
||||||
std::vector<Candidate> candidates;
|
std::vector<Candidate> candidates;
|
||||||
candidates.reserve(num_candidates);
|
candidates.reserve(num_candidates);
|
||||||
for (int scan_index = 0; scan_index != num_discrete_scans; ++scan_index) {
|
for (int scan_index = 0; scan_index != num_discrete_scans; ++scan_index) {
|
||||||
for (int z = -linear_z_window_size_; z <= linear_z_window_size_;
|
for (int z = -search_parameters.linear_z_window_size;
|
||||||
z += linear_step_size) {
|
z <= search_parameters.linear_z_window_size; z += linear_step_size) {
|
||||||
for (int y = -linear_xy_window_size_; y <= linear_xy_window_size_;
|
for (int y = -search_parameters.linear_xy_window_size;
|
||||||
|
y <= search_parameters.linear_xy_window_size;
|
||||||
y += linear_step_size) {
|
y += linear_step_size) {
|
||||||
for (int x = -linear_xy_window_size_; x <= linear_xy_window_size_;
|
for (int x = -search_parameters.linear_xy_window_size;
|
||||||
|
x <= search_parameters.linear_xy_window_size;
|
||||||
x += linear_step_size) {
|
x += linear_step_size) {
|
||||||
candidates.emplace_back(scan_index, Eigen::Array3i(x, y, z));
|
candidates.emplace_back(scan_index, Eigen::Array3i(x, y, z));
|
||||||
}
|
}
|
||||||
|
@ -277,15 +320,18 @@ void FastCorrelativeScanMatcher::ScoreCandidates(
|
||||||
|
|
||||||
std::vector<Candidate>
|
std::vector<Candidate>
|
||||||
FastCorrelativeScanMatcher::ComputeLowestResolutionCandidates(
|
FastCorrelativeScanMatcher::ComputeLowestResolutionCandidates(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
const std::vector<DiscreteScan>& discrete_scans) const {
|
const std::vector<DiscreteScan>& discrete_scans) const {
|
||||||
std::vector<Candidate> lowest_resolution_candidates =
|
std::vector<Candidate> lowest_resolution_candidates =
|
||||||
GenerateLowestResolutionCandidates(discrete_scans.size());
|
GenerateLowestResolutionCandidates(search_parameters,
|
||||||
|
discrete_scans.size());
|
||||||
ScoreCandidates(precomputation_grid_stack_->max_depth(), discrete_scans,
|
ScoreCandidates(precomputation_grid_stack_->max_depth(), discrete_scans,
|
||||||
&lowest_resolution_candidates);
|
&lowest_resolution_candidates);
|
||||||
return lowest_resolution_candidates;
|
return lowest_resolution_candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
Candidate FastCorrelativeScanMatcher::BranchAndBound(
|
Candidate FastCorrelativeScanMatcher::BranchAndBound(
|
||||||
|
const FastCorrelativeScanMatcher::SearchParameters& search_parameters,
|
||||||
const std::vector<DiscreteScan>& discrete_scans,
|
const std::vector<DiscreteScan>& discrete_scans,
|
||||||
const std::vector<Candidate>& candidates, const int candidate_depth,
|
const std::vector<Candidate>& candidates, const int candidate_depth,
|
||||||
float min_score) const {
|
float min_score) const {
|
||||||
|
@ -303,15 +349,17 @@ Candidate FastCorrelativeScanMatcher::BranchAndBound(
|
||||||
std::vector<Candidate> higher_resolution_candidates;
|
std::vector<Candidate> higher_resolution_candidates;
|
||||||
const int half_width = 1 << (candidate_depth - 1);
|
const int half_width = 1 << (candidate_depth - 1);
|
||||||
for (int z : {0, half_width}) {
|
for (int z : {0, half_width}) {
|
||||||
if (candidate.offset.z() + z > linear_z_window_size_) {
|
if (candidate.offset.z() + z > search_parameters.linear_z_window_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (int y : {0, half_width}) {
|
for (int y : {0, half_width}) {
|
||||||
if (candidate.offset.y() + y > linear_xy_window_size_) {
|
if (candidate.offset.y() + y >
|
||||||
|
search_parameters.linear_xy_window_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (int x : {0, half_width}) {
|
for (int x : {0, half_width}) {
|
||||||
if (candidate.offset.x() + x > linear_xy_window_size_) {
|
if (candidate.offset.x() + x >
|
||||||
|
search_parameters.linear_xy_window_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
higher_resolution_candidates.emplace_back(
|
higher_resolution_candidates.emplace_back(
|
||||||
|
@ -321,10 +369,10 @@ Candidate FastCorrelativeScanMatcher::BranchAndBound(
|
||||||
}
|
}
|
||||||
ScoreCandidates(candidate_depth - 1, discrete_scans,
|
ScoreCandidates(candidate_depth - 1, discrete_scans,
|
||||||
&higher_resolution_candidates);
|
&higher_resolution_candidates);
|
||||||
best_high_resolution_candidate =
|
best_high_resolution_candidate = std::max(
|
||||||
std::max(best_high_resolution_candidate,
|
best_high_resolution_candidate,
|
||||||
BranchAndBound(discrete_scans, higher_resolution_candidates,
|
BranchAndBound(search_parameters, discrete_scans,
|
||||||
candidate_depth - 1,
|
higher_resolution_candidates, candidate_depth - 1,
|
||||||
best_high_resolution_candidate.score));
|
best_high_resolution_candidate.score));
|
||||||
}
|
}
|
||||||
return best_high_resolution_candidate;
|
return best_high_resolution_candidate;
|
||||||
|
|
|
@ -89,28 +89,55 @@ class FastCorrelativeScanMatcher {
|
||||||
const sensor::PointCloud& fine_point_cloud, float min_score,
|
const sensor::PointCloud& fine_point_cloud, float min_score,
|
||||||
float* score, transform::Rigid3d* pose_estimate) const;
|
float* score, transform::Rigid3d* pose_estimate) const;
|
||||||
|
|
||||||
|
// Aligns 'coarse_point_cloud' within the 'hybrid_grid' given a rotation which
|
||||||
|
// is expected to be approximately gravity aligned. If a score above
|
||||||
|
// 'min_score' (excluding equality) is possible, true is returned, and 'score'
|
||||||
|
// and 'pose_estimate' are updated with the result. 'fine_point_cloud' is used
|
||||||
|
// to compute the rotational scan matcher score.
|
||||||
|
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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DiscreteScan DiscretizeScan(const sensor::PointCloud& point_cloud,
|
struct SearchParameters {
|
||||||
|
const int linear_xy_window_size; // voxels
|
||||||
|
const int linear_z_window_size; // voxels
|
||||||
|
const double angular_search_window; // radians
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
DiscreteScan DiscretizeScan(const SearchParameters& search_parameters,
|
||||||
|
const sensor::PointCloud& point_cloud,
|
||||||
const transform::Rigid3f& pose) const;
|
const transform::Rigid3f& pose) const;
|
||||||
std::vector<DiscreteScan> GenerateDiscreteScans(
|
std::vector<DiscreteScan> GenerateDiscreteScans(
|
||||||
|
const SearchParameters& search_parameters,
|
||||||
const sensor::PointCloud& coarse_point_cloud,
|
const sensor::PointCloud& coarse_point_cloud,
|
||||||
const sensor::PointCloud& fine_point_cloud,
|
const sensor::PointCloud& fine_point_cloud,
|
||||||
const transform::Rigid3f& initial_pose) const;
|
const transform::Rigid3f& initial_pose) const;
|
||||||
std::vector<Candidate> GenerateLowestResolutionCandidates(
|
std::vector<Candidate> GenerateLowestResolutionCandidates(
|
||||||
int num_discrete_scans) const;
|
const SearchParameters& search_parameters, int num_discrete_scans) const;
|
||||||
void ScoreCandidates(int depth,
|
void ScoreCandidates(int depth,
|
||||||
const std::vector<DiscreteScan>& discrete_scans,
|
const std::vector<DiscreteScan>& discrete_scans,
|
||||||
std::vector<Candidate>* const candidates) const;
|
std::vector<Candidate>* const candidates) const;
|
||||||
std::vector<Candidate> ComputeLowestResolutionCandidates(
|
std::vector<Candidate> ComputeLowestResolutionCandidates(
|
||||||
|
const SearchParameters& search_parameters,
|
||||||
const std::vector<DiscreteScan>& discrete_scans) const;
|
const std::vector<DiscreteScan>& discrete_scans) const;
|
||||||
Candidate BranchAndBound(const std::vector<DiscreteScan>& discrete_scans,
|
Candidate BranchAndBound(const SearchParameters& search_parameters,
|
||||||
|
const std::vector<DiscreteScan>& discrete_scans,
|
||||||
const std::vector<Candidate>& candidates,
|
const std::vector<Candidate>& candidates,
|
||||||
int candidate_depth, float min_score) const;
|
int candidate_depth, float min_score) const;
|
||||||
|
|
||||||
const proto::FastCorrelativeScanMatcherOptions options_;
|
const proto::FastCorrelativeScanMatcherOptions options_;
|
||||||
const float resolution_;
|
const float resolution_;
|
||||||
const int linear_xy_window_size_;
|
const Eigen::Vector3f origin_;
|
||||||
const int linear_z_window_size_;
|
const int width_in_voxels_;
|
||||||
std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_;
|
std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_;
|
||||||
RotationalScanMatcher rotational_scan_matcher_;
|
RotationalScanMatcher rotational_scan_matcher_;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue