Add TSDF support for RealTimeCorrelativeScanMatcher. (#1389)
Continues https://github.com/googlecartographer/cartographer/pull/1376. [RFC=0019](https://github.com/googlecartographer/rfcs/blob/master/text/0019-probability-grid-and-submap2d-restructuring.md)master
							parent
							
								
									5261c90c34
								
							
						
					
					
						commit
						1c00e8a970
					
				| 
						 | 
				
			
			@ -75,12 +75,9 @@ std::unique_ptr<transform::Rigid2d> LocalTrajectoryBuilder2D::ScanMatch(
 | 
			
		|||
  transform::Rigid2d initial_ceres_pose = pose_prediction;
 | 
			
		||||
 | 
			
		||||
  if (options_.use_online_correlative_scan_matching()) {
 | 
			
		||||
    CHECK_EQ(options_.submaps_options().grid_options_2d().grid_type(),
 | 
			
		||||
             proto::GridOptions2D_GridType_PROBABILITY_GRID);
 | 
			
		||||
    const double score = real_time_correlative_scan_matcher_.Match(
 | 
			
		||||
        pose_prediction, filtered_gravity_aligned_point_cloud,
 | 
			
		||||
        *static_cast<const ProbabilityGrid*>(matching_submap->grid()),
 | 
			
		||||
        &initial_ceres_pose);
 | 
			
		||||
        *matching_submap->grid(), &initial_ceres_pose);
 | 
			
		||||
    kRealTimeCorrelativeScanMatcherScoreMetric->Observe(score);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,22 +71,23 @@ void CeresScanMatcher2D::Match(const Eigen::Vector2d& target_translation,
 | 
			
		|||
                                   initial_pose_estimate.rotation().angle()};
 | 
			
		||||
  ceres::Problem problem;
 | 
			
		||||
  CHECK_GT(options_.occupied_space_weight(), 0.);
 | 
			
		||||
  if (grid.GetGridType() == GridType::PROBABILITY_GRID) {
 | 
			
		||||
    problem.AddResidualBlock(
 | 
			
		||||
        CreateOccupiedSpaceCostFunction2D(
 | 
			
		||||
            options_.occupied_space_weight() /
 | 
			
		||||
                std::sqrt(static_cast<double>(point_cloud.size())),
 | 
			
		||||
            point_cloud, grid),
 | 
			
		||||
        nullptr /* loss function */, ceres_pose_estimate);
 | 
			
		||||
  } else if (grid.GetGridType() == GridType::TSDF) {
 | 
			
		||||
    problem.AddResidualBlock(
 | 
			
		||||
        CreateTSDFMatchCostFunction2D(
 | 
			
		||||
            options_.occupied_space_weight() /
 | 
			
		||||
                std::sqrt(static_cast<double>(point_cloud.size())),
 | 
			
		||||
            point_cloud, static_cast<const TSDF2D&>(grid)),
 | 
			
		||||
        nullptr /* loss function */, ceres_pose_estimate);
 | 
			
		||||
  } else {
 | 
			
		||||
    LOG(FATAL) << "Unknown GridType.";
 | 
			
		||||
  switch (grid.GetGridType()) {
 | 
			
		||||
    case GridType::PROBABILITY_GRID:
 | 
			
		||||
      problem.AddResidualBlock(
 | 
			
		||||
          CreateOccupiedSpaceCostFunction2D(
 | 
			
		||||
              options_.occupied_space_weight() /
 | 
			
		||||
                  std::sqrt(static_cast<double>(point_cloud.size())),
 | 
			
		||||
              point_cloud, grid),
 | 
			
		||||
          nullptr /* loss function */, ceres_pose_estimate);
 | 
			
		||||
      break;
 | 
			
		||||
    case GridType::TSDF:
 | 
			
		||||
      problem.AddResidualBlock(
 | 
			
		||||
          CreateTSDFMatchCostFunction2D(
 | 
			
		||||
              options_.occupied_space_weight() /
 | 
			
		||||
                  std::sqrt(static_cast<double>(point_cloud.size())),
 | 
			
		||||
              point_cloud, static_cast<const TSDF2D&>(grid)),
 | 
			
		||||
          nullptr /* loss function */, ceres_pose_estimate);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  CHECK_GT(options_.translation_weight(), 0.);
 | 
			
		||||
  problem.AddResidualBlock(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@
 | 
			
		|||
#include "cartographer/common/lua_parameter_dictionary.h"
 | 
			
		||||
#include "cartographer/common/math.h"
 | 
			
		||||
#include "cartographer/mapping/2d/probability_grid.h"
 | 
			
		||||
#include "cartographer/mapping/2d/tsdf_2d.h"
 | 
			
		||||
#include "cartographer/sensor/point_cloud.h"
 | 
			
		||||
#include "cartographer/transform/transform.h"
 | 
			
		||||
#include "glog/logging.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +33,48 @@
 | 
			
		|||
namespace cartographer {
 | 
			
		||||
namespace mapping {
 | 
			
		||||
namespace scan_matching {
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
float ComputeCandidateScore(const TSDF2D& tsdf,
 | 
			
		||||
                            const DiscreteScan2D& discrete_scan,
 | 
			
		||||
                            int x_index_offset, int y_index_offset) {
 | 
			
		||||
  float candidate_score = 0.f;
 | 
			
		||||
  float summed_weight = 0.f;
 | 
			
		||||
  for (const Eigen::Array2i& xy_index : discrete_scan) {
 | 
			
		||||
    const Eigen::Array2i proposed_xy_index(xy_index.x() + x_index_offset,
 | 
			
		||||
                                           xy_index.y() + y_index_offset);
 | 
			
		||||
    const std::pair<float, float> tsd_and_weight =
 | 
			
		||||
        tsdf.GetTSDAndWeight(proposed_xy_index);
 | 
			
		||||
    const float normalized_tsd_score =
 | 
			
		||||
        (tsdf.GetMaxCorrespondenceCost() - std::abs(tsd_and_weight.first)) /
 | 
			
		||||
        tsdf.GetMaxCorrespondenceCost();
 | 
			
		||||
    const float weight = tsd_and_weight.second;
 | 
			
		||||
    candidate_score += normalized_tsd_score * weight;
 | 
			
		||||
    summed_weight += weight;
 | 
			
		||||
  }
 | 
			
		||||
  if (summed_weight == 0.f) return 0.f;
 | 
			
		||||
  candidate_score /= summed_weight;
 | 
			
		||||
  CHECK_GE(candidate_score, 0.f);
 | 
			
		||||
  return candidate_score;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ComputeCandidateScore(const ProbabilityGrid& probability_grid,
 | 
			
		||||
                            const DiscreteScan2D& discrete_scan,
 | 
			
		||||
                            int x_index_offset, int y_index_offset) {
 | 
			
		||||
  float candidate_score = 0.f;
 | 
			
		||||
  for (const Eigen::Array2i& xy_index : discrete_scan) {
 | 
			
		||||
    const Eigen::Array2i proposed_xy_index(xy_index.x() + x_index_offset,
 | 
			
		||||
                                           xy_index.y() + y_index_offset);
 | 
			
		||||
    const float probability =
 | 
			
		||||
        probability_grid.GetProbability(proposed_xy_index);
 | 
			
		||||
    candidate_score += probability;
 | 
			
		||||
  }
 | 
			
		||||
  candidate_score /= static_cast<float>(discrete_scan.size());
 | 
			
		||||
  CHECK_GT(candidate_score, 0.f);
 | 
			
		||||
  return candidate_score;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
RealTimeCorrelativeScanMatcher2D::RealTimeCorrelativeScanMatcher2D(
 | 
			
		||||
    const proto::RealTimeCorrelativeScanMatcherOptions& options)
 | 
			
		||||
| 
						 | 
				
			
			@ -73,8 +116,7 @@ RealTimeCorrelativeScanMatcher2D::GenerateExhaustiveSearchCandidates(
 | 
			
		|||
 | 
			
		||||
double RealTimeCorrelativeScanMatcher2D::Match(
 | 
			
		||||
    const transform::Rigid2d& initial_pose_estimate,
 | 
			
		||||
    const sensor::PointCloud& point_cloud,
 | 
			
		||||
    const ProbabilityGrid& probability_grid,
 | 
			
		||||
    const sensor::PointCloud& point_cloud, const Grid2D& grid,
 | 
			
		||||
    transform::Rigid2d* pose_estimate) const {
 | 
			
		||||
  CHECK_NOTNULL(pose_estimate);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,18 +127,17 @@ double RealTimeCorrelativeScanMatcher2D::Match(
 | 
			
		|||
          initial_rotation.cast<float>().angle(), Eigen::Vector3f::UnitZ())));
 | 
			
		||||
  const SearchParameters search_parameters(
 | 
			
		||||
      options_.linear_search_window(), options_.angular_search_window(),
 | 
			
		||||
      rotated_point_cloud, probability_grid.limits().resolution());
 | 
			
		||||
      rotated_point_cloud, grid.limits().resolution());
 | 
			
		||||
 | 
			
		||||
  const std::vector<sensor::PointCloud> rotated_scans =
 | 
			
		||||
      GenerateRotatedScans(rotated_point_cloud, search_parameters);
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans = DiscretizeScans(
 | 
			
		||||
      probability_grid.limits(), rotated_scans,
 | 
			
		||||
      grid.limits(), rotated_scans,
 | 
			
		||||
      Eigen::Translation2f(initial_pose_estimate.translation().x(),
 | 
			
		||||
                           initial_pose_estimate.translation().y()));
 | 
			
		||||
  std::vector<Candidate2D> candidates =
 | 
			
		||||
      GenerateExhaustiveSearchCandidates(search_parameters);
 | 
			
		||||
  ScoreCandidates(probability_grid, discrete_scans, search_parameters,
 | 
			
		||||
                  &candidates);
 | 
			
		||||
  ScoreCandidates(grid, discrete_scans, search_parameters, &candidates);
 | 
			
		||||
 | 
			
		||||
  const Candidate2D& best_candidate =
 | 
			
		||||
      *std::max_element(candidates.begin(), candidates.end());
 | 
			
		||||
| 
						 | 
				
			
			@ -108,29 +149,29 @@ double RealTimeCorrelativeScanMatcher2D::Match(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void RealTimeCorrelativeScanMatcher2D::ScoreCandidates(
 | 
			
		||||
    const ProbabilityGrid& probability_grid,
 | 
			
		||||
    const std::vector<DiscreteScan2D>& discrete_scans,
 | 
			
		||||
    const Grid2D& grid, const std::vector<DiscreteScan2D>& discrete_scans,
 | 
			
		||||
    const SearchParameters& search_parameters,
 | 
			
		||||
    std::vector<Candidate2D>* const candidates) const {
 | 
			
		||||
  for (Candidate2D& candidate : *candidates) {
 | 
			
		||||
    candidate.score = 0.f;
 | 
			
		||||
    for (const Eigen::Array2i& xy_index :
 | 
			
		||||
         discrete_scans[candidate.scan_index]) {
 | 
			
		||||
      const Eigen::Array2i proposed_xy_index(
 | 
			
		||||
          xy_index.x() + candidate.x_index_offset,
 | 
			
		||||
          xy_index.y() + candidate.y_index_offset);
 | 
			
		||||
      const float probability =
 | 
			
		||||
          probability_grid.GetProbability(proposed_xy_index);
 | 
			
		||||
      candidate.score += probability;
 | 
			
		||||
    switch (grid.GetGridType()) {
 | 
			
		||||
      case GridType::PROBABILITY_GRID:
 | 
			
		||||
        candidate.score = ComputeCandidateScore(
 | 
			
		||||
            static_cast<const ProbabilityGrid&>(grid),
 | 
			
		||||
            discrete_scans[candidate.scan_index], candidate.x_index_offset,
 | 
			
		||||
            candidate.y_index_offset);
 | 
			
		||||
        break;
 | 
			
		||||
      case GridType::TSDF:
 | 
			
		||||
        candidate.score = ComputeCandidateScore(
 | 
			
		||||
            static_cast<const TSDF2D&>(grid),
 | 
			
		||||
            discrete_scans[candidate.scan_index], candidate.x_index_offset,
 | 
			
		||||
            candidate.y_index_offset);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    candidate.score /=
 | 
			
		||||
        static_cast<float>(discrete_scans[candidate.scan_index].size());
 | 
			
		||||
    candidate.score *=
 | 
			
		||||
        std::exp(-common::Pow2(std::hypot(candidate.x, candidate.y) *
 | 
			
		||||
                                   options_.translation_delta_cost_weight() +
 | 
			
		||||
                               std::abs(candidate.orientation) *
 | 
			
		||||
                                   options_.rotation_delta_cost_weight()));
 | 
			
		||||
    CHECK_GT(candidate.score, 0.f);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@
 | 
			
		|||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "Eigen/Core"
 | 
			
		||||
#include "cartographer/mapping/2d/probability_grid.h"
 | 
			
		||||
#include "cartographer/mapping/2d/grid_2d.h"
 | 
			
		||||
#include "cartographer/mapping/internal/2d/scan_matching/correlative_scan_matcher_2d.h"
 | 
			
		||||
#include "cartographer/mapping/proto/scan_matching/real_time_correlative_scan_matcher_options.pb.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,20 +60,19 @@ class RealTimeCorrelativeScanMatcher2D {
 | 
			
		|||
  RealTimeCorrelativeScanMatcher2D& operator=(
 | 
			
		||||
      const RealTimeCorrelativeScanMatcher2D&) = delete;
 | 
			
		||||
 | 
			
		||||
  // Aligns 'point_cloud' within the 'probability_grid' given an
 | 
			
		||||
  // Aligns 'point_cloud' within the 'grid' given an
 | 
			
		||||
  // 'initial_pose_estimate' then updates 'pose_estimate' with the result and
 | 
			
		||||
  // returns the score.
 | 
			
		||||
  double Match(const transform::Rigid2d& initial_pose_estimate,
 | 
			
		||||
               const sensor::PointCloud& point_cloud,
 | 
			
		||||
               const ProbabilityGrid& probability_grid,
 | 
			
		||||
               const sensor::PointCloud& point_cloud, const Grid2D& grid,
 | 
			
		||||
               transform::Rigid2d* pose_estimate) const;
 | 
			
		||||
 | 
			
		||||
  // Computes the score for each Candidate2D in a collection. The cost is
 | 
			
		||||
  // computed as the sum of probabilities, different from the Ceres
 | 
			
		||||
  // CostFunctions: http://ceres-solver.org/modeling.html
 | 
			
		||||
  // computed as the sum of probabilities or normalized TSD values, different
 | 
			
		||||
  // from the Ceres CostFunctions: http://ceres-solver.org/modeling.html
 | 
			
		||||
  //
 | 
			
		||||
  // Visible for testing.
 | 
			
		||||
  void ScoreCandidates(const ProbabilityGrid& probability_grid,
 | 
			
		||||
  void ScoreCandidates(const Grid2D& grid,
 | 
			
		||||
                       const std::vector<DiscreteScan2D>& discrete_scans,
 | 
			
		||||
                       const SearchParameters& search_parameters,
 | 
			
		||||
                       std::vector<Candidate2D>* candidates) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,8 @@
 | 
			
		|||
#include "cartographer/common/lua_parameter_dictionary_test_helpers.h"
 | 
			
		||||
#include "cartographer/mapping/2d/probability_grid.h"
 | 
			
		||||
#include "cartographer/mapping/2d/probability_grid_range_data_inserter_2d.h"
 | 
			
		||||
#include "cartographer/mapping/2d/tsdf_2d.h"
 | 
			
		||||
#include "cartographer/mapping/2d/tsdf_range_data_inserter_2d.h"
 | 
			
		||||
#include "cartographer/mapping/internal/scan_matching/real_time_correlative_scan_matcher.h"
 | 
			
		||||
#include "cartographer/sensor/point_cloud.h"
 | 
			
		||||
#include "cartographer/transform/transform.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,12 +36,66 @@ namespace mapping {
 | 
			
		|||
namespace scan_matching {
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
proto::RealTimeCorrelativeScanMatcherOptions
 | 
			
		||||
CreateRealTimeCorrelativeScanMatcherTestOptions2D() {
 | 
			
		||||
  auto parameter_dictionary = common::MakeDictionary(
 | 
			
		||||
      "return {"
 | 
			
		||||
      "linear_search_window = 0.6, "
 | 
			
		||||
      "angular_search_window = 0.16, "
 | 
			
		||||
      "translation_delta_cost_weight = 0., "
 | 
			
		||||
      "rotation_delta_cost_weight = 0., "
 | 
			
		||||
      "}");
 | 
			
		||||
  return CreateRealTimeCorrelativeScanMatcherOptions(
 | 
			
		||||
      parameter_dictionary.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class RealTimeCorrelativeScanMatcherTest : public ::testing::Test {
 | 
			
		||||
 protected:
 | 
			
		||||
  RealTimeCorrelativeScanMatcherTest()
 | 
			
		||||
      : probability_grid_(
 | 
			
		||||
            MapLimits(0.05, Eigen::Vector2d(0.05, 0.25), CellLimits(6, 6)),
 | 
			
		||||
            &conversion_tables_) {
 | 
			
		||||
  RealTimeCorrelativeScanMatcherTest() {
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{0.025f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.025f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.075f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.125f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.075f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.025f, 0.f}});
 | 
			
		||||
    real_time_correlative_scan_matcher_ =
 | 
			
		||||
        absl::make_unique<RealTimeCorrelativeScanMatcher2D>(
 | 
			
		||||
            CreateRealTimeCorrelativeScanMatcherTestOptions2D());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SetUpTSDF() {
 | 
			
		||||
    grid_ = absl::make_unique<TSDF2D>(
 | 
			
		||||
        MapLimits(0.05, Eigen::Vector2d(0.3, 0.5), CellLimits(20, 20)), 0.3,
 | 
			
		||||
        1.0, &conversion_tables_);
 | 
			
		||||
    {
 | 
			
		||||
      auto parameter_dictionary = common::MakeDictionary(R"text(
 | 
			
		||||
      return {
 | 
			
		||||
        truncation_distance = 0.3,
 | 
			
		||||
        maximum_weight = 10.,
 | 
			
		||||
        update_free_space = false,
 | 
			
		||||
        normal_estimation_options = {
 | 
			
		||||
          num_normal_samples = 4,
 | 
			
		||||
          sample_radius = 0.5,
 | 
			
		||||
        },
 | 
			
		||||
        project_sdf_distance_to_scan_normal = true,
 | 
			
		||||
        update_weight_range_exponent = 0,
 | 
			
		||||
        update_weight_angle_scan_normal_to_ray_kernel_bandwith = 0.5,
 | 
			
		||||
        update_weight_distance_cell_to_hit_kernel_bandwith = 0.5,
 | 
			
		||||
      })text");
 | 
			
		||||
      range_data_inserter_ = absl::make_unique<TSDFRangeDataInserter2D>(
 | 
			
		||||
          CreateTSDFRangeDataInserterOptions2D(parameter_dictionary.get()));
 | 
			
		||||
    }
 | 
			
		||||
    range_data_inserter_->Insert(
 | 
			
		||||
        sensor::RangeData{Eigen::Vector3f(0.5f, -0.5f, 0.f), point_cloud_, {}},
 | 
			
		||||
        grid_.get());
 | 
			
		||||
    grid_->FinishUpdate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SetUpProbabilityGrid() {
 | 
			
		||||
    grid_ = absl::make_unique<ProbabilityGrid>(
 | 
			
		||||
        MapLimits(0.05, Eigen::Vector2d(0.05, 0.25), CellLimits(6, 6)),
 | 
			
		||||
        &conversion_tables_);
 | 
			
		||||
    {
 | 
			
		||||
      auto parameter_dictionary = common::MakeDictionary(
 | 
			
		||||
          "return { "
 | 
			
		||||
| 
						 | 
				
			
			@ -52,51 +108,31 @@ class RealTimeCorrelativeScanMatcherTest : public ::testing::Test {
 | 
			
		|||
              CreateProbabilityGridRangeDataInserterOptions2D(
 | 
			
		||||
                  parameter_dictionary.get()));
 | 
			
		||||
    }
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{0.025f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.025f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.075f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.175f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.125f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.075f, 0.f}});
 | 
			
		||||
    point_cloud_.push_back({Eigen::Vector3f{-0.125f, 0.025f, 0.f}});
 | 
			
		||||
    range_data_inserter_->Insert(
 | 
			
		||||
        sensor::RangeData{Eigen::Vector3f::Zero(), point_cloud_, {}},
 | 
			
		||||
        &probability_grid_);
 | 
			
		||||
    probability_grid_.FinishUpdate();
 | 
			
		||||
    {
 | 
			
		||||
      auto parameter_dictionary = common::MakeDictionary(
 | 
			
		||||
          "return {"
 | 
			
		||||
          "linear_search_window = 0.6, "
 | 
			
		||||
          "angular_search_window = 0.16, "
 | 
			
		||||
          "translation_delta_cost_weight = 0., "
 | 
			
		||||
          "rotation_delta_cost_weight = 0., "
 | 
			
		||||
          "}");
 | 
			
		||||
      real_time_correlative_scan_matcher_ =
 | 
			
		||||
          absl::make_unique<RealTimeCorrelativeScanMatcher2D>(
 | 
			
		||||
              CreateRealTimeCorrelativeScanMatcherOptions(
 | 
			
		||||
                  parameter_dictionary.get()));
 | 
			
		||||
    }
 | 
			
		||||
        grid_.get());
 | 
			
		||||
    grid_->FinishUpdate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ValueConversionTables conversion_tables_;
 | 
			
		||||
  ProbabilityGrid probability_grid_;
 | 
			
		||||
  std::unique_ptr<ProbabilityGridRangeDataInserter2D> range_data_inserter_;
 | 
			
		||||
  std::unique_ptr<Grid2D> grid_;
 | 
			
		||||
  std::unique_ptr<RangeDataInserterInterface> range_data_inserter_;
 | 
			
		||||
  sensor::PointCloud point_cloud_;
 | 
			
		||||
  std::unique_ptr<RealTimeCorrelativeScanMatcher2D>
 | 
			
		||||
      real_time_correlative_scan_matcher_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		||||
       ScorePerfectHighResolutionCandidate) {
 | 
			
		||||
       ScorePerfectHighResolutionCandidateProbabilityGrid) {
 | 
			
		||||
  SetUpProbabilityGrid();
 | 
			
		||||
  const std::vector<sensor::PointCloud> scans =
 | 
			
		||||
      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans = DiscretizeScans(
 | 
			
		||||
      probability_grid_.limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans =
 | 
			
		||||
      DiscretizeScans(grid_->limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  std::vector<Candidate2D> candidates;
 | 
			
		||||
  candidates.emplace_back(0, 0, 0, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  real_time_correlative_scan_matcher_->ScoreCandidates(
 | 
			
		||||
      probability_grid_, discrete_scans, SearchParameters(0, 0, 0., 0.),
 | 
			
		||||
      &candidates);
 | 
			
		||||
      *grid_, discrete_scans, SearchParameters(0, 0, 0., 0.), &candidates);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].scan_index);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].x_index_offset);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].y_index_offset);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,16 +141,35 @@ TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		||||
       ScorePartiallyCorrectHighResolutionCandidate) {
 | 
			
		||||
       ScorePerfectHighResolutionCandidateTSDF) {
 | 
			
		||||
  SetUpTSDF();
 | 
			
		||||
  const std::vector<sensor::PointCloud> scans =
 | 
			
		||||
      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans = DiscretizeScans(
 | 
			
		||||
      probability_grid_.limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans =
 | 
			
		||||
      DiscretizeScans(grid_->limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  std::vector<Candidate2D> candidates;
 | 
			
		||||
  candidates.emplace_back(0, 0, 0, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  real_time_correlative_scan_matcher_->ScoreCandidates(
 | 
			
		||||
      *grid_, discrete_scans, SearchParameters(0, 0, 0., 0.), &candidates);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].scan_index);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].x_index_offset);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].y_index_offset);
 | 
			
		||||
  // Every point should align perfectly.
 | 
			
		||||
  EXPECT_NEAR(1.0, candidates[0].score, 1e-1);
 | 
			
		||||
  EXPECT_LT(0.95, candidates[0].score);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		||||
       ScorePartiallyCorrectHighResolutionCandidateProbabilityGrid) {
 | 
			
		||||
  SetUpProbabilityGrid();
 | 
			
		||||
  const std::vector<sensor::PointCloud> scans =
 | 
			
		||||
      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans =
 | 
			
		||||
      DiscretizeScans(grid_->limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  std::vector<Candidate2D> candidates;
 | 
			
		||||
  candidates.emplace_back(0, 0, 1, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  real_time_correlative_scan_matcher_->ScoreCandidates(
 | 
			
		||||
      probability_grid_, discrete_scans, SearchParameters(0, 0, 0., 0.),
 | 
			
		||||
      &candidates);
 | 
			
		||||
      *grid_, discrete_scans, SearchParameters(0, 0, 0., 0.), &candidates);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].scan_index);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].x_index_offset);
 | 
			
		||||
  EXPECT_EQ(1, candidates[0].y_index_offset);
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +178,25 @@ TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		|||
  EXPECT_GT(0.7, candidates[0].score);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RealTimeCorrelativeScanMatcherTest,
 | 
			
		||||
       ScorePartiallyCorrectHighResolutionCandidateTSDF) {
 | 
			
		||||
  SetUpTSDF();
 | 
			
		||||
  const std::vector<sensor::PointCloud> scans =
 | 
			
		||||
      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  const std::vector<DiscreteScan2D> discrete_scans =
 | 
			
		||||
      DiscretizeScans(grid_->limits(), scans, Eigen::Translation2f::Identity());
 | 
			
		||||
  std::vector<Candidate2D> candidates;
 | 
			
		||||
  candidates.emplace_back(0, 0, 1, SearchParameters(0, 0, 0., 0.));
 | 
			
		||||
  real_time_correlative_scan_matcher_->ScoreCandidates(
 | 
			
		||||
      *grid_, discrete_scans, SearchParameters(0, 0, 0., 0.), &candidates);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].scan_index);
 | 
			
		||||
  EXPECT_EQ(0, candidates[0].x_index_offset);
 | 
			
		||||
  EXPECT_EQ(1, candidates[0].y_index_offset);
 | 
			
		||||
  // 3 points should align perfectly.
 | 
			
		||||
  EXPECT_LT(1.0 - 4. / (7. * 6.), candidates[0].score);
 | 
			
		||||
  EXPECT_GT(1.0, candidates[0].score);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
}  // namespace scan_matching
 | 
			
		||||
}  // namespace mapping
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue