00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "cartographer/mapping/internal/3d/scan_matching/ceres_scan_matcher_3d.h"
00018
00019 #include <memory>
00020
00021 #include "Eigen/Core"
00022 #include "cartographer/common/lua_parameter_dictionary_test_helpers.h"
00023 #include "cartographer/mapping/3d/hybrid_grid.h"
00024 #include "cartographer/sensor/point_cloud.h"
00025 #include "cartographer/transform/rigid_transform.h"
00026 #include "cartographer/transform/rigid_transform_test_helpers.h"
00027 #include "gtest/gtest.h"
00028
00029 namespace cartographer {
00030 namespace mapping {
00031 namespace scan_matching {
00032 namespace {
00033
00034 class CeresScanMatcher3DTest : public ::testing::Test {
00035 protected:
00036 CeresScanMatcher3DTest()
00037 : hybrid_grid_(1.f),
00038 expected_pose_(
00039 transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., 0.))) {
00040 for (const Eigen::Vector3f& point :
00041 {Eigen::Vector3f(-3.f, 2.f, 0.f), Eigen::Vector3f(-4.f, 2.f, 0.f),
00042 Eigen::Vector3f(-5.f, 2.f, 0.f), Eigen::Vector3f(-6.f, 2.f, 0.f),
00043 Eigen::Vector3f(-6.f, 3.f, 1.f), Eigen::Vector3f(-6.f, 4.f, 2.f),
00044 Eigen::Vector3f(-7.f, 3.f, 1.f)}) {
00045 point_cloud_.push_back({point});
00046 hybrid_grid_.SetProbability(
00047 hybrid_grid_.GetCellIndex(expected_pose_.cast<float>() * point), 1.);
00048 }
00049
00050 auto parameter_dictionary = common::MakeDictionary(R"text(
00051 return {
00052 occupied_space_weight_0 = 1.,
00053 translation_weight = 0.01,
00054 rotation_weight = 0.1,
00055 only_optimize_yaw = false,
00056 ceres_solver_options = {
00057 use_nonmonotonic_steps = true,
00058 max_num_iterations = 10,
00059 num_threads = 1,
00060 },
00061 })text");
00062 options_ = CreateCeresScanMatcherOptions3D(parameter_dictionary.get());
00063 ceres_scan_matcher_.reset(new CeresScanMatcher3D(options_));
00064 }
00065
00066 void TestFromInitialPose(const transform::Rigid3d& initial_pose) {
00067 transform::Rigid3d pose;
00068
00069 ceres::Solver::Summary summary;
00070 ceres_scan_matcher_->Match(initial_pose.translation(), initial_pose,
00071 {{&point_cloud_, &hybrid_grid_}}, &pose,
00072 &summary);
00073 EXPECT_NEAR(0., summary.final_cost, 1e-2) << summary.FullReport();
00074 EXPECT_THAT(pose, transform::IsNearly(expected_pose_, 3e-2));
00075 }
00076
00077 HybridGrid hybrid_grid_;
00078 transform::Rigid3d expected_pose_;
00079 sensor::PointCloud point_cloud_;
00080 proto::CeresScanMatcherOptions3D options_;
00081 std::unique_ptr<CeresScanMatcher3D> ceres_scan_matcher_;
00082 };
00083
00084 TEST_F(CeresScanMatcher3DTest, PerfectEstimate) {
00085 TestFromInitialPose(
00086 transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., 0.)));
00087 }
00088
00089 TEST_F(CeresScanMatcher3DTest, AlongX) {
00090 ceres_scan_matcher_.reset(new CeresScanMatcher3D(options_));
00091 TestFromInitialPose(
00092 transform::Rigid3d::Translation(Eigen::Vector3d(-0.8, 0., 0.)));
00093 }
00094
00095 TEST_F(CeresScanMatcher3DTest, AlongZ) {
00096 TestFromInitialPose(
00097 transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., -0.2)));
00098 }
00099
00100 TEST_F(CeresScanMatcher3DTest, AlongXYZ) {
00101 TestFromInitialPose(
00102 transform::Rigid3d::Translation(Eigen::Vector3d(-0.9, -0.2, 0.2)));
00103 }
00104
00105 TEST_F(CeresScanMatcher3DTest, FullPoseCorrection) {
00106
00107 const auto additional_transform = transform::Rigid3d::Rotation(
00108 Eigen::AngleAxisd(0.05, Eigen::Vector3d(0., 0., 1.)));
00109 point_cloud_ = sensor::TransformPointCloud(
00110 point_cloud_, additional_transform.cast<float>());
00111 expected_pose_ = expected_pose_ * additional_transform.inverse();
00112
00113 TestFromInitialPose(
00114 transform::Rigid3d(Eigen::Vector3d(-0.95, -0.05, 0.05),
00115 Eigen::AngleAxisd(0.05, Eigen::Vector3d(1., 0., 0.))));
00116 }
00117
00118 }
00119 }
00120 }
00121 }