Use TrajectoryNode::Data in FastCorrelativeMatcher. (#493)

* Use TrajectoryNode::Data in FastCorrelativeMatcher.
master
Christoph Schütte 2017-08-31 15:10:48 +02:00 committed by GitHub
parent e2c67a7bde
commit 982f2bd2e0
4 changed files with 79 additions and 67 deletions

View File

@ -24,6 +24,7 @@
#include "Eigen/Geometry" #include "Eigen/Geometry"
#include "cartographer/common/make_unique.h" #include "cartographer/common/make_unique.h"
#include "cartographer/common/math.h" #include "cartographer/common/math.h"
#include "cartographer/mapping_3d/scan_matching/low_resolution_matcher.h"
#include "cartographer/mapping_3d/scan_matching/precomputation_grid.h" #include "cartographer/mapping_3d/scan_matching/precomputation_grid.h"
#include "cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h" #include "cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h"
#include "cartographer/transform/transform.h" #include "cartographer/transform/transform.h"
@ -126,6 +127,7 @@ struct Candidate {
FastCorrelativeScanMatcher::FastCorrelativeScanMatcher( FastCorrelativeScanMatcher::FastCorrelativeScanMatcher(
const HybridGrid& hybrid_grid, const HybridGrid& hybrid_grid,
const HybridGrid* const low_resolution_hybrid_grid,
const std::vector<mapping::TrajectoryNode>& nodes, const std::vector<mapping::TrajectoryNode>& nodes,
const proto::FastCorrelativeScanMatcherOptions& options) const proto::FastCorrelativeScanMatcherOptions& options)
: options_(options), : options_(options),
@ -133,49 +135,53 @@ FastCorrelativeScanMatcher::FastCorrelativeScanMatcher(
width_in_voxels_(hybrid_grid.grid_size()), width_in_voxels_(hybrid_grid.grid_size()),
precomputation_grid_stack_( precomputation_grid_stack_(
common::make_unique<PrecomputationGridStack>(hybrid_grid, options)), common::make_unique<PrecomputationGridStack>(hybrid_grid, options)),
low_resolution_hybrid_grid_(low_resolution_hybrid_grid),
rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {} rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {}
FastCorrelativeScanMatcher::~FastCorrelativeScanMatcher() {} FastCorrelativeScanMatcher::~FastCorrelativeScanMatcher() {}
bool FastCorrelativeScanMatcher::Match( bool FastCorrelativeScanMatcher::Match(
const transform::Rigid3d& initial_pose_estimate, const transform::Rigid3d& initial_pose_estimate,
const sensor::PointCloud& coarse_point_cloud, const mapping::TrajectoryNode::Data& constant_data, const float min_score,
const sensor::PointCloud& fine_point_cloud, const float min_score, float* const score, transform::Rigid3d* const pose_estimate,
const MatchingFunction& low_resolution_matcher, float* const score, float* const rotational_score, float* const low_resolution_score) const {
transform::Rigid3d* const pose_estimate, float* const rotational_score, const auto low_resolution_matcher = scan_matching::CreateLowResolutionMatcher(
float* const low_resolution_score) const { low_resolution_hybrid_grid_, &constant_data.low_resolution_point_cloud);
const SearchParameters search_parameters{ const SearchParameters search_parameters{
common::RoundToInt(options_.linear_xy_search_window() / resolution_), common::RoundToInt(options_.linear_xy_search_window() / resolution_),
common::RoundToInt(options_.linear_z_search_window() / resolution_), common::RoundToInt(options_.linear_z_search_window() / resolution_),
options_.angular_search_window(), &low_resolution_matcher}; options_.angular_search_window(), &low_resolution_matcher};
return MatchWithSearchParameters(search_parameters, initial_pose_estimate, return MatchWithSearchParameters(
coarse_point_cloud, fine_point_cloud, search_parameters, initial_pose_estimate,
min_score, score, pose_estimate, constant_data.high_resolution_point_cloud,
rotational_score, low_resolution_score); constant_data.range_data.returns.Decompress(), min_score, score,
pose_estimate, rotational_score, low_resolution_score);
} }
bool FastCorrelativeScanMatcher::MatchFullSubmap( bool FastCorrelativeScanMatcher::MatchFullSubmap(
const Eigen::Quaterniond& gravity_alignment, const Eigen::Quaterniond& gravity_alignment,
const sensor::PointCloud& coarse_point_cloud, const mapping::TrajectoryNode::Data& constant_data, const float min_score,
const sensor::PointCloud& fine_point_cloud, const float min_score, float* const score, transform::Rigid3d* const pose_estimate,
const MatchingFunction& low_resolution_matcher, float* const score, float* const rotational_score, float* const low_resolution_score) const {
transform::Rigid3d* const pose_estimate, float* const rotational_score,
float* const low_resolution_score) const {
const transform::Rigid3d initial_pose_estimate(Eigen::Vector3d::Zero(), const transform::Rigid3d initial_pose_estimate(Eigen::Vector3d::Zero(),
gravity_alignment); gravity_alignment);
float max_point_distance = 0.f; float max_point_distance = 0.f;
for (const Eigen::Vector3f& point : coarse_point_cloud) { for (const Eigen::Vector3f& point :
constant_data.high_resolution_point_cloud) {
max_point_distance = std::max(max_point_distance, point.norm()); max_point_distance = std::max(max_point_distance, point.norm());
} }
const int linear_window_size = const int linear_window_size =
(width_in_voxels_ + 1) / 2 + (width_in_voxels_ + 1) / 2 +
common::RoundToInt(max_point_distance / resolution_ + 0.5f); common::RoundToInt(max_point_distance / resolution_ + 0.5f);
const auto low_resolution_matcher = scan_matching::CreateLowResolutionMatcher(
low_resolution_hybrid_grid_, &constant_data.low_resolution_point_cloud);
const SearchParameters search_parameters{ const SearchParameters search_parameters{
linear_window_size, linear_window_size, M_PI, &low_resolution_matcher}; linear_window_size, linear_window_size, M_PI, &low_resolution_matcher};
return MatchWithSearchParameters(search_parameters, initial_pose_estimate, return MatchWithSearchParameters(
coarse_point_cloud, fine_point_cloud, search_parameters, initial_pose_estimate,
min_score, score, pose_estimate, constant_data.high_resolution_point_cloud,
rotational_score, low_resolution_score); constant_data.range_data.returns.Decompress(), min_score, score,
pose_estimate, rotational_score, low_resolution_score);
} }
bool FastCorrelativeScanMatcher::MatchWithSearchParameters( bool FastCorrelativeScanMatcher::MatchWithSearchParameters(
@ -269,8 +275,9 @@ std::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(
} }
const float kSafetyMargin = 1.f - 1e-2f; const float kSafetyMargin = 1.f - 1e-2f;
const float angular_step_size = const float angular_step_size =
kSafetyMargin * std::acos(1.f - common::Pow2(resolution_) / kSafetyMargin * std::acos(1.f -
(2.f * common::Pow2(max_scan_range))); common::Pow2(resolution_) /
(2.f * common::Pow2(max_scan_range)));
const int angular_window_size = common::RoundToInt( const int angular_window_size = common::RoundToInt(
search_parameters.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

View File

@ -51,6 +51,7 @@ class FastCorrelativeScanMatcher {
public: public:
FastCorrelativeScanMatcher( FastCorrelativeScanMatcher(
const HybridGrid& hybrid_grid, const HybridGrid& hybrid_grid,
const HybridGrid* low_resolution_hybrid_grid,
const std::vector<mapping::TrajectoryNode>& nodes, const std::vector<mapping::TrajectoryNode>& nodes,
const proto::FastCorrelativeScanMatcherOptions& options); const proto::FastCorrelativeScanMatcherOptions& options);
~FastCorrelativeScanMatcher(); ~FastCorrelativeScanMatcher();
@ -65,11 +66,9 @@ class FastCorrelativeScanMatcher {
// 'rotational_score', and 'low_resolution_score' are updated with the result. // 'rotational_score', and 'low_resolution_score' are updated with the result.
// 'fine_point_cloud' is used to compute the rotational scan matcher score. // 'fine_point_cloud' is used to compute the rotational scan matcher score.
bool Match(const transform::Rigid3d& initial_pose_estimate, bool Match(const transform::Rigid3d& initial_pose_estimate,
const sensor::PointCloud& coarse_point_cloud, const mapping::TrajectoryNode::Data& constant_data,
const sensor::PointCloud& fine_point_cloud, float min_score, float min_score, float* score, transform::Rigid3d* pose_estimate,
const MatchingFunction& low_resolution_matcher, float* score, float* rotational_score, float* low_resolution_score) const;
transform::Rigid3d* pose_estimate, float* rotational_score,
float* low_resolution_score) const;
// Aligns 'coarse_point_cloud' within the 'hybrid_grid' given a rotation which // Aligns 'coarse_point_cloud' within the 'hybrid_grid' given a rotation which
// is expected to be approximately gravity aligned. If a score above // is expected to be approximately gravity aligned. If a score above
@ -78,11 +77,9 @@ class FastCorrelativeScanMatcher {
// are updated with the result. 'fine_point_cloud' is used to compute the // are updated with the result. 'fine_point_cloud' is used to compute the
// rotational scan matcher score. // rotational scan matcher score.
bool MatchFullSubmap(const Eigen::Quaterniond& gravity_alignment, bool MatchFullSubmap(const Eigen::Quaterniond& gravity_alignment,
const sensor::PointCloud& coarse_point_cloud, const mapping::TrajectoryNode::Data& constant_data,
const sensor::PointCloud& fine_point_cloud, float min_score, float* score,
float min_score, transform::Rigid3d* pose_estimate,
const MatchingFunction& low_resolution_matcher,
float* score, transform::Rigid3d* pose_estimate,
float* rotational_score, float* rotational_score,
float* low_resolution_score) const; float* low_resolution_score) const;
@ -130,6 +127,7 @@ class FastCorrelativeScanMatcher {
const float resolution_; const float resolution_;
const int width_in_voxels_; const int width_in_voxels_;
std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_; std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_;
const HybridGrid* const low_resolution_hybrid_grid_;
RotationalScanMatcher rotational_scan_matcher_; RotationalScanMatcher rotational_scan_matcher_;
}; };

View File

@ -72,7 +72,9 @@ class FastCorrelativeScanMatcherTest : public ::testing::Test {
", " ", "
"rotational_histogram_size = 30, " "rotational_histogram_size = 30, "
"min_rotational_score = 0.1, " "min_rotational_score = 0.1, "
"min_low_resolution_score = 0.5, " // Unknown space has kMinProbability = 0.1, so we need to make sure here
// to pick a larger number otherwise we always find matches.
"min_low_resolution_score = 0.15, "
"linear_xy_search_window = 0.8, " "linear_xy_search_window = 0.8, "
"linear_z_search_window = 0.8, " "linear_z_search_window = 0.8, "
"angular_search_window = 0.3, " "angular_search_window = 0.3, "
@ -94,16 +96,28 @@ class FastCorrelativeScanMatcherTest : public ::testing::Test {
std::unique_ptr<FastCorrelativeScanMatcher> GetFastCorrelativeScanMatcher( std::unique_ptr<FastCorrelativeScanMatcher> GetFastCorrelativeScanMatcher(
const proto::FastCorrelativeScanMatcherOptions& options, const proto::FastCorrelativeScanMatcherOptions& options,
const transform::Rigid3f& pose) { const transform::Rigid3f& pose) {
HybridGrid hybrid_grid(0.05f); hybrid_grid_ = common::make_unique<HybridGrid>(0.05f);
range_data_inserter_.Insert( range_data_inserter_.Insert(
sensor::RangeData{pose.translation(), sensor::RangeData{pose.translation(),
sensor::TransformPointCloud(point_cloud_, pose), sensor::TransformPointCloud(point_cloud_, pose),
{}}, {}},
&hybrid_grid); hybrid_grid_.get());
hybrid_grid.FinishUpdate(); hybrid_grid_->FinishUpdate();
return common::make_unique<FastCorrelativeScanMatcher>( return common::make_unique<FastCorrelativeScanMatcher>(
hybrid_grid, std::vector<mapping::TrajectoryNode>(), options); *hybrid_grid_, hybrid_grid_.get(),
std::vector<mapping::TrajectoryNode>(), options);
}
mapping::TrajectoryNode::Data CreateConstantData(
const sensor::PointCloud& low_resolution_point_cloud) {
return mapping::TrajectoryNode::Data{
common::FromUniversal(0),
Compress(sensor::RangeData{Eigen::Vector3f::Zero(), point_cloud_, {}}),
{},
point_cloud_,
low_resolution_point_cloud,
transform::Rigid3d::Identity()};
} }
std::mt19937 prng_ = std::mt19937(42); std::mt19937 prng_ = std::mt19937(42);
@ -112,11 +126,10 @@ class FastCorrelativeScanMatcherTest : public ::testing::Test {
RangeDataInserter range_data_inserter_; RangeDataInserter range_data_inserter_;
const proto::FastCorrelativeScanMatcherOptions options_; const proto::FastCorrelativeScanMatcherOptions options_;
sensor::PointCloud point_cloud_; sensor::PointCloud point_cloud_;
std::unique_ptr<HybridGrid> hybrid_grid_;
}; };
constexpr float kMinScore = 0.1f; constexpr float kMinScore = 0.1f;
constexpr float kPassingLowResolutionScore = 1.f;
constexpr float kFailingLowResolutionScore = 0.f;
TEST_F(FastCorrelativeScanMatcherTest, CorrectPoseForMatch) { TEST_F(FastCorrelativeScanMatcherTest, CorrectPoseForMatch) {
for (int i = 0; i != 20; ++i) { for (int i = 0; i != 20; ++i) {
@ -130,20 +143,21 @@ TEST_F(FastCorrelativeScanMatcherTest, CorrectPoseForMatch) {
float rotational_score = 0.f; float rotational_score = 0.f;
float low_resolution_score = 0.f; float low_resolution_score = 0.f;
EXPECT_TRUE(fast_correlative_scan_matcher->Match( EXPECT_TRUE(fast_correlative_scan_matcher->Match(
transform::Rigid3d::Identity(), point_cloud_, point_cloud_, kMinScore, transform::Rigid3d::Identity(), CreateConstantData(point_cloud_),
[](const transform::Rigid3f&) { return kPassingLowResolutionScore; }, kMinScore, &score, &pose_estimate, &rotational_score,
&score, &pose_estimate, &rotational_score, &low_resolution_score)); &low_resolution_score));
EXPECT_LT(kMinScore, score); EXPECT_LT(kMinScore, score);
EXPECT_LT(0.09f, rotational_score); EXPECT_LT(0.09f, rotational_score);
EXPECT_LT(0.99f, low_resolution_score); EXPECT_LT(0.14f, low_resolution_score);
EXPECT_THAT(expected_pose, EXPECT_THAT(expected_pose,
transform::IsNearly(pose_estimate.cast<float>(), 0.05f)) transform::IsNearly(pose_estimate.cast<float>(), 0.05f))
<< "Actual: " << transform::ToProto(pose_estimate).DebugString() << "Actual: " << transform::ToProto(pose_estimate).DebugString()
<< "\nExpected: " << transform::ToProto(expected_pose).DebugString(); << "\nExpected: " << transform::ToProto(expected_pose).DebugString();
EXPECT_FALSE(fast_correlative_scan_matcher->Match( EXPECT_FALSE(fast_correlative_scan_matcher->Match(
transform::Rigid3d::Identity(), point_cloud_, point_cloud_, kMinScore, transform::Rigid3d::Identity(),
[](const transform::Rigid3f&) { return kFailingLowResolutionScore; }, CreateConstantData({Eigen::Vector3f(42.f, 42.f, 42.f)}), kMinScore,
&score, &pose_estimate, &rotational_score, &low_resolution_score)); &score, &pose_estimate, &rotational_score, &low_resolution_score))
<< low_resolution_score;
} }
} }
@ -158,20 +172,21 @@ TEST_F(FastCorrelativeScanMatcherTest, CorrectPoseForMatchFullSubmap) {
float rotational_score = 0.f; float rotational_score = 0.f;
float low_resolution_score = 0.f; float low_resolution_score = 0.f;
EXPECT_TRUE(fast_correlative_scan_matcher->MatchFullSubmap( EXPECT_TRUE(fast_correlative_scan_matcher->MatchFullSubmap(
Eigen::Quaterniond::Identity(), point_cloud_, point_cloud_, kMinScore, Eigen::Quaterniond::Identity(), CreateConstantData(point_cloud_),
[](const transform::Rigid3f&) { return kPassingLowResolutionScore; }, kMinScore, &score, &pose_estimate, &rotational_score,
&score, &pose_estimate, &rotational_score, &low_resolution_score)); &low_resolution_score));
EXPECT_LT(kMinScore, score); EXPECT_LT(kMinScore, score);
EXPECT_LT(0.09f, rotational_score); EXPECT_LT(0.09f, rotational_score);
EXPECT_LT(0.99f, low_resolution_score); EXPECT_LT(0.14f, low_resolution_score);
EXPECT_THAT(expected_pose, EXPECT_THAT(expected_pose,
transform::IsNearly(pose_estimate.cast<float>(), 0.05f)) transform::IsNearly(pose_estimate.cast<float>(), 0.05f))
<< "Actual: " << transform::ToProto(pose_estimate).DebugString() << "Actual: " << transform::ToProto(pose_estimate).DebugString()
<< "\nExpected: " << transform::ToProto(expected_pose).DebugString(); << "\nExpected: " << transform::ToProto(expected_pose).DebugString();
EXPECT_FALSE(fast_correlative_scan_matcher->MatchFullSubmap( EXPECT_FALSE(fast_correlative_scan_matcher->MatchFullSubmap(
Eigen::Quaterniond::Identity(), point_cloud_, point_cloud_, kMinScore, Eigen::Quaterniond::Identity(),
[](const transform::Rigid3f&) { return kFailingLowResolutionScore; }, CreateConstantData({Eigen::Vector3f(42.f, 42.f, 42.f)}), kMinScore,
&score, &pose_estimate, &rotational_score, &low_resolution_score)); &score, &pose_estimate, &rotational_score, &low_resolution_score))
<< low_resolution_score;
} }
} // namespace } // namespace

View File

@ -29,7 +29,6 @@
#include "cartographer/common/make_unique.h" #include "cartographer/common/make_unique.h"
#include "cartographer/common/math.h" #include "cartographer/common/math.h"
#include "cartographer/common/thread_pool.h" #include "cartographer/common/thread_pool.h"
#include "cartographer/mapping_3d/scan_matching/low_resolution_matcher.h"
#include "cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h" #include "cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h"
#include "cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h" #include "cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h"
#include "cartographer/transform/transform.h" #include "cartographer/transform/transform.h"
@ -142,7 +141,8 @@ void ConstraintBuilder::ConstructSubmapScanMatcher(
const Submap* const submap) { const Submap* const submap) {
auto submap_scan_matcher = auto submap_scan_matcher =
common::make_unique<scan_matching::FastCorrelativeScanMatcher>( common::make_unique<scan_matching::FastCorrelativeScanMatcher>(
submap->high_resolution_hybrid_grid(), submap_nodes, submap->high_resolution_hybrid_grid(),
&submap->low_resolution_hybrid_grid(), submap_nodes,
options_.fast_correlative_scan_matcher_options_3d()); options_.fast_correlative_scan_matcher_options_3d());
common::MutexLocker locker(&mutex_); common::MutexLocker locker(&mutex_);
submap_scan_matchers_[submap_id] = {&submap->high_resolution_hybrid_grid(), submap_scan_matchers_[submap_id] = {&submap->high_resolution_hybrid_grid(),
@ -173,8 +173,6 @@ void ConstraintBuilder::ComputeConstraint(
std::unique_ptr<OptimizationProblem::Constraint>* constraint) { std::unique_ptr<OptimizationProblem::Constraint>* constraint) {
const SubmapScanMatcher* const submap_scan_matcher = const SubmapScanMatcher* const submap_scan_matcher =
GetSubmapScanMatcher(submap_id); GetSubmapScanMatcher(submap_id);
const sensor::PointCloud point_cloud =
constant_data->range_data.returns.Decompress();
// The 'constraint_transform' (submap i <- scan j) is computed from: // The 'constraint_transform' (submap i <- scan j) is computed from:
// - a 'high_resolution_point_cloud' in scan j and // - a 'high_resolution_point_cloud' in scan j and
@ -184,20 +182,15 @@ void ConstraintBuilder::ComputeConstraint(
float rotational_score = 0.f; float rotational_score = 0.f;
float low_resolution_score = 0.f; float low_resolution_score = 0.f;
const auto low_resolution_matcher = scan_matching::CreateLowResolutionMatcher(
submap_scan_matcher->low_resolution_hybrid_grid,
&constant_data->low_resolution_point_cloud);
// Compute 'pose_estimate' in three stages: // Compute 'pose_estimate' in three stages:
// 1. Fast estimate using the fast correlative scan matcher. // 1. Fast estimate using the fast correlative scan matcher.
// 2. Prune if the score is too low. // 2. Prune if the score is too low.
// 3. Refine. // 3. Refine.
if (match_full_submap) { if (match_full_submap) {
if (submap_scan_matcher->fast_correlative_scan_matcher->MatchFullSubmap( if (submap_scan_matcher->fast_correlative_scan_matcher->MatchFullSubmap(
initial_pose.rotation(), constant_data->high_resolution_point_cloud, initial_pose.rotation(), *constant_data,
point_cloud, options_.global_localization_min_score(), options_.global_localization_min_score(), &score, &pose_estimate,
low_resolution_matcher, &score, &pose_estimate, &rotational_score, &rotational_score, &low_resolution_score)) {
&low_resolution_score)) {
CHECK_GT(score, options_.global_localization_min_score()); CHECK_GT(score, options_.global_localization_min_score());
CHECK_GE(node_id.trajectory_id, 0); CHECK_GE(node_id.trajectory_id, 0);
CHECK_GE(submap_id.trajectory_id, 0); CHECK_GE(submap_id.trajectory_id, 0);
@ -208,8 +201,7 @@ void ConstraintBuilder::ComputeConstraint(
} }
} else { } else {
if (submap_scan_matcher->fast_correlative_scan_matcher->Match( if (submap_scan_matcher->fast_correlative_scan_matcher->Match(
initial_pose, constant_data->high_resolution_point_cloud, initial_pose, *constant_data, options_.min_score(), &score,
point_cloud, options_.min_score(), low_resolution_matcher, &score,
&pose_estimate, &rotational_score, &low_resolution_score)) { &pose_estimate, &rotational_score, &low_resolution_score)) {
// We've reported a successful local match. // We've reported a successful local match.
CHECK_GT(score, options_.min_score()); CHECK_GT(score, options_.min_score());
@ -274,7 +266,7 @@ void ConstraintBuilder::FinishComputation(const int computation_index) {
CHECK_EQ(submap_queued_work_items_.size(), 0); CHECK_EQ(submap_queued_work_items_.size(), 0);
if (when_done_ != nullptr) { if (when_done_ != nullptr) {
for (const std::unique_ptr<OptimizationProblem::Constraint>& for (const std::unique_ptr<OptimizationProblem::Constraint>&
constraint : constraints_) { constraint : constraints_) {
if (constraint != nullptr) { if (constraint != nullptr) {
result.push_back(*constraint); result.push_back(*constraint);
} }