Skip to content

Commit

Permalink
Remove covariance computation from branch-and-bound. (#27)
Browse files Browse the repository at this point in the history
These covariances were only used in 2D and are only an estimate.
Following 3D, we change 2D to use the (local) covariance computed
using Ceres.
  • Loading branch information
wohe committed Sep 23, 2016
1 parent 14355a9 commit e526a70
Show file tree
Hide file tree
Showing 10 changed files with 29 additions and 77 deletions.
2 changes: 0 additions & 2 deletions cartographer/mapping_2d/scan_matching/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ google_library(mapping_2d_scan_matching_fast_correlative_scan_matcher
DEPENDS
common_math
common_port
kalman_filter_pose_tracker
mapping_2d_probability_grid
mapping_2d_scan_matching_correlative_scan_matcher
mapping_2d_scan_matching_proto_fast_correlative_scan_matcher_options
Expand All @@ -74,7 +73,6 @@ google_library(mapping_2d_scan_matching_fast_global_localizer
HDRS
fast_global_localizer.h
DEPENDS
kalman_filter_pose_tracker
mapping_2d_scan_matching_fast_correlative_scan_matcher
sensor_voxel_filter
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

#include "Eigen/Geometry"
#include "cartographer/common/math.h"
#include "cartographer/kalman_filter/pose_tracker.h"
#include "cartographer/mapping_2d/probability_grid.h"
#include "cartographer/sensor/point_cloud.h"
#include "cartographer/transform/transform.h"
Expand Down Expand Up @@ -86,8 +85,6 @@ CreateFastCorrelativeScanMatcherOptions(
parameter_dictionary->GetDouble("angular_search_window"));
options.set_branch_and_bound_depth(
parameter_dictionary->GetInt("branch_and_bound_depth"));
options.set_covariance_scale(
parameter_dictionary->GetDouble("covariance_scale"));
return options;
}

Expand Down Expand Up @@ -212,20 +209,18 @@ FastCorrelativeScanMatcher::~FastCorrelativeScanMatcher() {}
bool FastCorrelativeScanMatcher::Match(
const transform::Rigid2d& initial_pose_estimate,
const sensor::PointCloud2D& point_cloud, const float min_score,
float* score, transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const {
float* score, transform::Rigid2d* pose_estimate) const {
const SearchParameters search_parameters(options_.linear_search_window(),
options_.angular_search_window(),
point_cloud, limits_.resolution());
return MatchWithSearchParameters(search_parameters, initial_pose_estimate,
point_cloud, min_score, score, pose_estimate,
covariance);
point_cloud, min_score, score,
pose_estimate);
}

bool FastCorrelativeScanMatcher::MatchFullSubmap(
const sensor::PointCloud2D& point_cloud, float min_score, float* score,
transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const {
transform::Rigid2d* pose_estimate) const {
// Compute a search window around the center of the submap that includes it
// fully.
const SearchParameters search_parameters(
Expand All @@ -238,18 +233,16 @@ bool FastCorrelativeScanMatcher::MatchFullSubmap(
Eigen::Vector2d(limits_.cell_limits().num_y_cells,
limits_.cell_limits().num_x_cells));
return MatchWithSearchParameters(search_parameters, center, point_cloud,
min_score, score, pose_estimate, covariance);
min_score, score, pose_estimate);
}

bool FastCorrelativeScanMatcher::MatchWithSearchParameters(
SearchParameters search_parameters,
const transform::Rigid2d& initial_pose_estimate,
const sensor::PointCloud2D& point_cloud, float min_score, float* score,
transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const {
transform::Rigid2d* pose_estimate) const {
CHECK_NOTNULL(score);
CHECK_NOTNULL(pose_estimate);
CHECK_NOTNULL(covariance);

const Eigen::Rotation2Dd initial_rotation = initial_pose_estimate.rotation();
const sensor::PointCloud2D rotated_point_cloud =
Expand All @@ -266,22 +259,15 @@ bool FastCorrelativeScanMatcher::MatchWithSearchParameters(

const std::vector<Candidate> lowest_resolution_candidates =
ComputeLowestResolutionCandidates(discrete_scans, search_parameters);
// The following are intermediate results for computing covariance. See
// "Real-Time Correlative Scan Matching" by Olson.
Eigen::Matrix3d K = Eigen::Matrix3d::Zero();
Eigen::Vector3d u = Eigen::Vector3d::Zero();
double s = 0.;
const Candidate best_candidate = BranchAndBound(
discrete_scans, search_parameters, lowest_resolution_candidates,
precomputation_grid_stack_->max_depth(), min_score, &K, &u, &s);
precomputation_grid_stack_->max_depth(), min_score);
if (best_candidate.score > min_score) {
*score = best_candidate.score;
*pose_estimate = transform::Rigid2d(
{initial_pose_estimate.translation().x() + best_candidate.x,
initial_pose_estimate.translation().y() + best_candidate.y},
initial_rotation * Eigen::Rotation2Dd(best_candidate.orientation));
*covariance = (1. / s) * K - (1. / (s * s)) * (u * u.transpose());
*covariance *= options_.covariance_scale();
return true;
}
return false;
Expand Down Expand Up @@ -361,16 +347,8 @@ Candidate FastCorrelativeScanMatcher::BranchAndBound(
const std::vector<DiscreteScan>& discrete_scans,
const SearchParameters& search_parameters,
const std::vector<Candidate>& candidates, const int candidate_depth,
float min_score, Eigen::Matrix3d* K, Eigen::Vector3d* u, double* s) const {
float min_score) const {
if (candidate_depth == 0) {
// Update the covariance computation intermediate values.
for (const Candidate& candidate : candidates) {
const double p = candidate.score;
const Eigen::Vector3d xi(candidate.x, candidate.y, candidate.orientation);
*K += (xi * xi.transpose()) * p;
*u += xi * p;
*s += p;
}
// Return the best candidate.
return *candidates.begin();
}
Expand Down Expand Up @@ -405,7 +383,7 @@ Candidate FastCorrelativeScanMatcher::BranchAndBound(
best_high_resolution_candidate,
BranchAndBound(discrete_scans, search_parameters,
higher_resolution_candidates, candidate_depth - 1,
best_high_resolution_candidate.score, K, u, s));
best_high_resolution_candidate.score));
}
return best_high_resolution_candidate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

#include "Eigen/Core"
#include "cartographer/common/port.h"
#include "cartographer/kalman_filter/pose_tracker.h"
#include "cartographer/mapping_2d/probability_grid.h"
#include "cartographer/mapping_2d/scan_matching/correlative_scan_matcher.h"
#include "cartographer/mapping_2d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h"
Expand Down Expand Up @@ -109,20 +108,18 @@ class FastCorrelativeScanMatcher {

// Aligns 'point_cloud' within the 'probability_grid' given an
// 'initial_pose_estimate'. If a score above 'min_score' (excluding equality)
// is possible, true is returned, and 'score', 'pose_estimate' and
// 'covariance' are updated with the result.
// is possible, true is returned, and 'score' and 'pose_estimate' are updated
// with the result.
bool Match(const transform::Rigid2d& initial_pose_estimate,
const sensor::PointCloud2D& point_cloud, float min_score,
float* score, transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const;
float* score, transform::Rigid2d* pose_estimate) const;

// Aligns 'point_cloud' within the full 'probability_grid', i.e., not
// restricted to the configured search window. If a score above 'min_score'
// (excluding equality) is possible, true is returned, and 'score',
// 'pose_estimate' and 'covariance' are updated with the result.
// (excluding equality) is possible, true is returned, and 'score' and
// 'pose_estimate' are updated with the result.
bool MatchFullSubmap(const sensor::PointCloud2D& point_cloud, float min_score,
float* score, transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const;
float* score, transform::Rigid2d* pose_estimate) const;

private:
// The actual implementation of the scan matcher, called by Match() and
Expand All @@ -132,8 +129,7 @@ class FastCorrelativeScanMatcher {
SearchParameters search_parameters,
const transform::Rigid2d& initial_pose_estimate,
const sensor::PointCloud2D& point_cloud, float min_score, float* score,
transform::Rigid2d* pose_estimate,
kalman_filter::Pose2DCovariance* covariance) const;
transform::Rigid2d* pose_estimate) const;
std::vector<Candidate> ComputeLowestResolutionCandidates(
const std::vector<DiscreteScan>& discrete_scans,
const SearchParameters& search_parameters) const;
Expand All @@ -146,9 +142,7 @@ class FastCorrelativeScanMatcher {
Candidate BranchAndBound(const std::vector<DiscreteScan>& discrete_scans,
const SearchParameters& search_parameters,
const std::vector<Candidate>& candidates,
int candidate_depth, float min_score,
Eigen::Matrix3d* K, Eigen::Vector3d* u,
double* s) const;
int candidate_depth, float min_score) const;

const proto::FastCorrelativeScanMatcherOptions options_;
MapLimits limits_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ CreateFastCorrelativeScanMatcherTestOptions(const int branch_and_bound_depth) {
return {
linear_search_window = 3.,
angular_search_window = 1.,
covariance_scale = 1.,
branch_and_bound_depth = )text" +
std::to_string(branch_and_bound_depth) + "}");
return CreateFastCorrelativeScanMatcherOptions(parameter_dictionary.get());
Expand Down Expand Up @@ -159,11 +158,10 @@ TEST(FastCorrelativeScanMatcherTest, CorrectPose) {
FastCorrelativeScanMatcher fast_correlative_scan_matcher(probability_grid,
options);
transform::Rigid2d pose_estimate;
kalman_filter::Pose2DCovariance unused_covariance;
float score;
EXPECT_TRUE(fast_correlative_scan_matcher.Match(
transform::Rigid2d::Identity(), point_cloud, kMinScore, &score,
&pose_estimate, &unused_covariance));
&pose_estimate));
EXPECT_LT(kMinScore, score);
EXPECT_THAT(expected_pose,
transform::IsNearly(pose_estimate.cast<float>(), 0.03f))
Expand Down Expand Up @@ -211,10 +209,9 @@ TEST(FastCorrelativeScanMatcherTest, FullSubmapMatching) {
FastCorrelativeScanMatcher fast_correlative_scan_matcher(probability_grid,
options);
transform::Rigid2d pose_estimate;
kalman_filter::Pose2DCovariance unused_covariance;
float score;
EXPECT_TRUE(fast_correlative_scan_matcher.MatchFullSubmap(
point_cloud, kMinScore, &score, &pose_estimate, &unused_covariance));
point_cloud, kMinScore, &score, &pose_estimate));
EXPECT_LT(kMinScore, score);
EXPECT_THAT(expected_pose,
transform::IsNearly(pose_estimate.cast<float>(), 0.03f))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#include "cartographer/mapping_2d/scan_matching/fast_global_localizer.h"

#include "cartographer/kalman_filter/pose_tracker.h"
#include "glog/logging.h"

namespace cartographer {
Expand All @@ -36,7 +35,6 @@ bool PerformGlobalLocalization(
CHECK(best_score != nullptr) << "Need a non-null best_score!";
*best_score = cutoff;
transform::Rigid2d pose_estimate;
kalman_filter::Pose2DCovariance best_pose_estimate_covariance;
const sensor::PointCloud2D filtered_point_cloud =
voxel_filter.Filter(point_cloud);
bool success = false;
Expand All @@ -47,13 +45,11 @@ bool PerformGlobalLocalization(
for (auto& matcher : matchers) {
float score = -1;
transform::Rigid2d pose_estimate;
kalman_filter::Pose2DCovariance pose_estimate_covariance;
if (matcher->MatchFullSubmap(filtered_point_cloud, *best_score, &score,
&pose_estimate, &pose_estimate_covariance)) {
&pose_estimate)) {
CHECK_GT(score, *best_score) << "MatchFullSubmap lied!";
*best_score = score;
*best_pose_estimate = pose_estimate;
best_pose_estimate_covariance = pose_estimate_covariance;
success = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,4 @@ message FastCorrelativeScanMatcherOptions {

// Number of precomputed grids to use.
optional int32 branch_and_bound_depth = 2;

// Covariance estimate is multiplied by this scale.
optional double covariance_scale = 5;
};
16 changes: 6 additions & 10 deletions cartographer/mapping_2d/sparse_pose_graph/constraint_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,25 +191,23 @@ void ConstraintBuilder::ComputeConstraint(
// - the ComputeSubmapPose() (map <- i)
float score = 0.;
transform::Rigid2d pose_estimate = transform::Rigid2d::Identity();
kalman_filter::Pose2DCovariance covariance;

if (match_full_submap) {
if (submap_scan_matcher->fast_correlative_scan_matcher->MatchFullSubmap(
filtered_point_cloud, options_.global_localization_min_score(),
&score, &pose_estimate, &covariance)) {
&score, &pose_estimate)) {
trajectory_connectivity->Connect(scan_trajectory, submap_trajectory);
} else {
return;
}
} else {
if (!submap_scan_matcher->fast_correlative_scan_matcher->Match(
initial_pose, filtered_point_cloud, options_.min_score(), &score,
&pose_estimate, &covariance)) {
&pose_estimate)) {
return;
}
// We've reported a successful local match.
CHECK_GT(score, options_.min_score());
// 'covariance' is unchanged as (submap <- map) is a translation.
{
common::MutexLocker locker(&mutex_);
score_histogram_.Add(score);
Expand All @@ -218,15 +216,13 @@ void ConstraintBuilder::ComputeConstraint(

// Use the CSM estimate as both the initial and previous pose. This has the
// effect that, in the absence of better information, we prefer the original
// CSM estimate. We also prefer to use the CSM covariance estimate for loop
// closure constraints since it is representative of a larger area (as opposed
// to the local Ceres estimate of covariance).
// CSM estimate.
ceres::Solver::Summary unused_summary;
kalman_filter::Pose2DCovariance unused_covariance;
kalman_filter::Pose2DCovariance covariance;
ceres_scan_matcher_.Match(pose_estimate, pose_estimate, filtered_point_cloud,
*submap_scan_matcher->probability_grid,
&pose_estimate, &unused_covariance,
&unused_summary);
&pose_estimate, &covariance, &unused_summary);
// 'covariance' is unchanged as (submap <- map) is a translation.

const transform::Rigid2d constraint_transform =
ComputeSubmapPose(*submap).inverse() * pose_estimate;
Expand Down
1 change: 0 additions & 1 deletion cartographer/mapping_2d/sparse_pose_graph_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ class SparsePoseGraphTest : public ::testing::Test {
linear_search_window = 3.,
angular_search_window = 0.1,
branch_and_bound_depth = 3,
covariance_scale = 1.,
},
ceres_scan_matcher = {
occupied_space_cost_functor_weight = 20.,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,7 @@ void ConstraintBuilder::ComputeConstraint(

// Use the CSM estimate as both the initial and previous pose. This has the
// effect that, in the absence of better information, we prefer the original
// CSM estimate. We also prefer to use the CSM covariance estimate for loop
// closure constraints since it is representative of a larger area (as opposed
// to the local Ceres estimate of covariance).
// CSM estimate.
ceres::Solver::Summary unused_summary;
kalman_filter::PoseCovariance covariance;
ceres_scan_matcher_.Match(
Expand Down
5 changes: 2 additions & 3 deletions configuration_files/sparse_pose_graph.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@ SPARSE_POSE_GRAPH = {
linear_search_window = 7.,
angular_search_window = math.rad(30.),
branch_and_bound_depth = 7,
covariance_scale = 1e-6,
},
ceres_scan_matcher = {
occupied_space_cost_functor_weight = 20.,
previous_pose_translation_delta_cost_functor_weight = 10.,
initial_pose_estimate_rotation_delta_cost_functor_weight = 1.,
covariance_scale = 1e-6,
covariance_scale = 1e-4,
ceres_solver_options = {
use_nonmonotonic_steps = true,
max_num_iterations = 10,
Expand Down Expand Up @@ -67,7 +66,7 @@ SPARSE_POSE_GRAPH = {
},
},
optimization_problem = {
huber_scale = 5e2,
huber_scale = 1e1,
acceleration_scale = 7e4,
rotation_scale = 3e6,
consecutive_scan_translation_penalty_factor = 1e5,
Expand Down

0 comments on commit e526a70

Please sign in to comment.