Add value conversion tables (#1255)

Adds value conversion tables to perform lazy computations of lookup tables for mapping from a uint16 value to a float in [`lower_bound`, `upper_bound`].
Owners of the value conversion tables are `SubmapController`, `PoseGraph2D`, `ActiveSubmaps` and `ProbabilityGridPointsProcessor`. For concurrency reasons, having only a single owner is not possible.
Follow up PR in `cartographer_ros`  is prepared.
master
Kevin Daun 2018-07-12 22:11:55 +02:00 committed by Wally B. Feed
parent 8602bf9430
commit 5abd413310
35 changed files with 410 additions and 147 deletions

View File

@ -91,7 +91,8 @@ ProbabilityGridPointsProcessor::ProbabilityGridPointsProcessor(
file_writer_(std::move(file_writer)), file_writer_(std::move(file_writer)),
next_(next), next_(next),
range_data_inserter_(probability_grid_range_data_inserter_options), range_data_inserter_(probability_grid_range_data_inserter_options),
probability_grid_(CreateProbabilityGrid(resolution)) { probability_grid_(
CreateProbabilityGrid(resolution, &conversion_tables_)) {
LOG_IF(WARNING, output_type == OutputType::kPb && LOG_IF(WARNING, output_type == OutputType::kPb &&
draw_trajectories_ == DrawTrajectories::kYes) draw_trajectories_ == DrawTrajectories::kYes)
<< "Drawing the trajectories is not supported when writing the " << "Drawing the trajectories is not supported when writing the "
@ -192,14 +193,17 @@ std::unique_ptr<Image> DrawProbabilityGrid(
return image; return image;
} }
mapping::ProbabilityGrid CreateProbabilityGrid(const double resolution) { mapping::ProbabilityGrid CreateProbabilityGrid(
const double resolution,
mapping::ValueConversionTables* conversion_tables) {
constexpr int kInitialProbabilityGridSize = 100; constexpr int kInitialProbabilityGridSize = 100;
Eigen::Vector2d max = Eigen::Vector2d max =
0.5 * kInitialProbabilityGridSize * resolution * Eigen::Vector2d::Ones(); 0.5 * kInitialProbabilityGridSize * resolution * Eigen::Vector2d::Ones();
return mapping::ProbabilityGrid( return mapping::ProbabilityGrid(
mapping::MapLimits(resolution, max, mapping::MapLimits(resolution, max,
mapping::CellLimits(kInitialProbabilityGridSize, mapping::CellLimits(kInitialProbabilityGridSize,
kInitialProbabilityGridSize))); kInitialProbabilityGridSize)),
conversion_tables);
} }
} // namespace io } // namespace io

View File

@ -27,6 +27,7 @@
#include "cartographer/mapping/2d/probability_grid_range_data_inserter_2d.h" #include "cartographer/mapping/2d/probability_grid_range_data_inserter_2d.h"
#include "cartographer/mapping/proto/2d/probability_grid_range_data_inserter_options_2d.pb.h" #include "cartographer/mapping/proto/2d/probability_grid_range_data_inserter_options_2d.pb.h"
#include "cartographer/mapping/proto/trajectory.pb.h" #include "cartographer/mapping/proto/trajectory.pb.h"
#include "cartographer/mapping/value_conversion_tables.h"
namespace cartographer { namespace cartographer {
namespace io { namespace io {
@ -71,6 +72,7 @@ class ProbabilityGridPointsProcessor : public PointsProcessor {
std::unique_ptr<FileWriter> file_writer_; std::unique_ptr<FileWriter> file_writer_;
PointsProcessor* const next_; PointsProcessor* const next_;
mapping::ProbabilityGridRangeDataInserter2D range_data_inserter_; mapping::ProbabilityGridRangeDataInserter2D range_data_inserter_;
mapping::ValueConversionTables conversion_tables_;
mapping::ProbabilityGrid probability_grid_; mapping::ProbabilityGrid probability_grid_;
}; };
@ -81,7 +83,8 @@ std::unique_ptr<Image> DrawProbabilityGrid(
// Create an initially empty probability grid with 'resolution' and a small // Create an initially empty probability grid with 'resolution' and a small
// size, suitable for a PointsBatchProcessor. // size, suitable for a PointsBatchProcessor.
mapping::ProbabilityGrid CreateProbabilityGrid(const double resolution); mapping::ProbabilityGrid CreateProbabilityGrid(
const double resolution, mapping::ValueConversionTables* conversion_tables);
} // namespace io } // namespace io
} // namespace cartographer } // namespace cartographer

View File

@ -23,6 +23,7 @@
#include "cartographer/io/fake_file_writer.h" #include "cartographer/io/fake_file_writer.h"
#include "cartographer/io/points_processor_pipeline_builder.h" #include "cartographer/io/points_processor_pipeline_builder.h"
#include "cartographer/mapping/2d/probability_grid_range_data_inserter_2d.h" #include "cartographer/mapping/2d/probability_grid_range_data_inserter_2d.h"
#include "cartographer/mapping/value_conversion_tables.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -81,8 +82,9 @@ std::vector<char> CreateExpectedProbabilityGrid(
CreateProbabilityGridRangeDataInserterOptions2D( CreateProbabilityGridRangeDataInserterOptions2D(
probability_grid_options->GetDictionary("range_data_inserter") probability_grid_options->GetDictionary("range_data_inserter")
.get())); .get()));
auto probability_grid = mapping::ValueConversionTables conversion_tables;
CreateProbabilityGrid(probability_grid_options->GetDouble("resolution")); auto probability_grid = CreateProbabilityGrid(
probability_grid_options->GetDouble("resolution"), &conversion_tables);
range_data_inserter.Insert({points_batch->origin, points_batch->points, {}}, range_data_inserter.Insert({points_batch->origin, points_batch->points, {}},
&probability_grid); &probability_grid);

View File

@ -110,7 +110,8 @@ PaintSubmapSlicesResult PaintSubmapSlices(
void FillSubmapSlice( void FillSubmapSlice(
const ::cartographer::transform::Rigid3d& global_submap_pose, const ::cartographer::transform::Rigid3d& global_submap_pose,
const ::cartographer::mapping::proto::Submap& proto, const ::cartographer::mapping::proto::Submap& proto,
SubmapSlice* const submap_slice) { SubmapSlice* const submap_slice,
mapping::ValueConversionTables* conversion_tables) {
::cartographer::mapping::proto::SubmapQuery::Response response; ::cartographer::mapping::proto::SubmapQuery::Response response;
::cartographer::transform::Rigid3d local_pose; ::cartographer::transform::Rigid3d local_pose;
if (proto.has_submap_3d()) { if (proto.has_submap_3d()) {
@ -118,7 +119,8 @@ void FillSubmapSlice(
local_pose = submap.local_pose(); local_pose = submap.local_pose();
submap.ToResponseProto(global_submap_pose, &response); submap.ToResponseProto(global_submap_pose, &response);
} else { } else {
::cartographer::mapping::Submap2D submap(proto.submap_2d()); ::cartographer::mapping::Submap2D submap(proto.submap_2d(),
conversion_tables);
local_pose = submap.local_pose(); local_pose = submap.local_pose();
submap.ToResponseProto(global_submap_pose, &response); submap.ToResponseProto(global_submap_pose, &response);
} }

View File

@ -22,6 +22,7 @@
#include "cartographer/io/image.h" #include "cartographer/io/image.h"
#include "cartographer/mapping/id.h" #include "cartographer/mapping/id.h"
#include "cartographer/mapping/proto/serialization.pb.h" #include "cartographer/mapping/proto/serialization.pb.h"
#include "cartographer/mapping/value_conversion_tables.h"
#include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/rigid_transform.h"
namespace cartographer { namespace cartographer {
@ -80,7 +81,8 @@ PaintSubmapSlicesResult PaintSubmapSlices(
void FillSubmapSlice( void FillSubmapSlice(
const ::cartographer::transform::Rigid3d& global_submap_pose, const ::cartographer::transform::Rigid3d& global_submap_pose,
const ::cartographer::mapping::proto::Submap& proto, const ::cartographer::mapping::proto::Submap& proto,
SubmapSlice* const submap_slice); SubmapSlice* const submap_slice,
mapping::ValueConversionTables* conversion_tables);
// Unpacks cell data as provided by the backend into 'intensity' and 'alpha'. // Unpacks cell data as provided by the backend into 'intensity' and 'alpha'.
SubmapTexture::Pixels UnpackTextureData(const std::string& compressed_cells, SubmapTexture::Pixels UnpackTextureData(const std::string& compressed_cells,

View File

@ -19,6 +19,34 @@
namespace cartographer { namespace cartographer {
namespace mapping { namespace mapping {
namespace {
float MinCorrespondenceCostFromProto(const proto::Grid2D& proto) {
if (proto.min_correspondence_cost() == 0.f &&
proto.max_correspondence_cost() == 0.f) {
LOG(WARNING)
<< "proto::Grid2D: max_correspondence_cost and min_correspondence_cost "
"are initialized with 0 indicating an older version of the "
"protobuf format. Loading default values.";
return kMinCorrespondenceCost;
} else {
return proto.min_correspondence_cost();
}
}
float MaxCorrespondenceCostFromProto(const proto::Grid2D& proto) {
if (proto.min_correspondence_cost() == 0.f &&
proto.max_correspondence_cost() == 0.f) {
LOG(WARNING)
<< "proto::Grid2D: max_correspondence_cost and min_correspondence_cost "
"are initialized with 0 indicating an older version of the "
"protobuf format. Loading default values.";
return kMaxCorrespondenceCost;
} else {
return proto.max_correspondence_cost();
}
}
} // namespace
proto::GridOptions2D CreateGridOptions2D( proto::GridOptions2D CreateGridOptions2D(
common::LuaParameterDictionary* const parameter_dictionary) { common::LuaParameterDictionary* const parameter_dictionary) {
@ -34,18 +62,30 @@ proto::GridOptions2D CreateGridOptions2D(
} }
Grid2D::Grid2D(const MapLimits& limits, float min_correspondence_cost, Grid2D::Grid2D(const MapLimits& limits, float min_correspondence_cost,
float max_correspondence_cost) float max_correspondence_cost,
ValueConversionTables* conversion_tables)
: limits_(limits), : limits_(limits),
correspondence_cost_cells_( correspondence_cost_cells_(
limits_.cell_limits().num_x_cells * limits_.cell_limits().num_y_cells, limits_.cell_limits().num_x_cells * limits_.cell_limits().num_y_cells,
kUnknownCorrespondenceValue), kUnknownCorrespondenceValue),
min_correspondence_cost_(min_correspondence_cost), min_correspondence_cost_(min_correspondence_cost),
max_correspondence_cost_(max_correspondence_cost) { max_correspondence_cost_(max_correspondence_cost),
value_to_correspondence_cost_table_(conversion_tables->GetConversionTable(
max_correspondence_cost, min_correspondence_cost,
max_correspondence_cost)) {
CHECK_LT(min_correspondence_cost_, max_correspondence_cost_); CHECK_LT(min_correspondence_cost_, max_correspondence_cost_);
} }
Grid2D::Grid2D(const proto::Grid2D& proto) Grid2D::Grid2D(const proto::Grid2D& proto,
: limits_(proto.limits()), correspondence_cost_cells_() { ValueConversionTables* conversion_tables)
: limits_(proto.limits()),
correspondence_cost_cells_(),
min_correspondence_cost_(MinCorrespondenceCostFromProto(proto)),
max_correspondence_cost_(MaxCorrespondenceCostFromProto(proto)),
value_to_correspondence_cost_table_(conversion_tables->GetConversionTable(
max_correspondence_cost_, min_correspondence_cost_,
max_correspondence_cost_)) {
CHECK_LT(min_correspondence_cost_, max_correspondence_cost_);
if (proto.has_known_cells_box()) { if (proto.has_known_cells_box()) {
const auto& box = proto.known_cells_box(); const auto& box = proto.known_cells_box();
known_cells_box_ = known_cells_box_ =
@ -57,19 +97,6 @@ Grid2D::Grid2D(const proto::Grid2D& proto)
CHECK_LE(cell, std::numeric_limits<uint16>::max()); CHECK_LE(cell, std::numeric_limits<uint16>::max());
correspondence_cost_cells_.push_back(cell); correspondence_cost_cells_.push_back(cell);
} }
if (proto.min_correspondence_cost() == 0.f &&
proto.max_correspondence_cost() == 0.f) {
LOG(WARNING)
<< "proto::Grid2D: max_correspondence_cost and min_correspondence_cost "
"are initialized with 0 indicating an older version of the "
"protobuf format. Loading default values.";
min_correspondence_cost_ = kMinCorrespondenceCost;
max_correspondence_cost_ = kMaxCorrespondenceCost;
} else {
min_correspondence_cost_ = proto.min_correspondence_cost();
max_correspondence_cost_ = proto.max_correspondence_cost();
}
CHECK_LT(min_correspondence_cost_, max_correspondence_cost_);
} }
// Finishes the update sequence. // Finishes the update sequence.
@ -84,9 +111,9 @@ void Grid2D::FinishUpdate() {
// Returns the correspondence cost of the cell with 'cell_index'. // Returns the correspondence cost of the cell with 'cell_index'.
float Grid2D::GetCorrespondenceCost(const Eigen::Array2i& cell_index) const { float Grid2D::GetCorrespondenceCost(const Eigen::Array2i& cell_index) const {
if (!limits().Contains(cell_index)) return kMaxCorrespondenceCost; if (!limits().Contains(cell_index)) return max_correspondence_cost_;
return ValueToCorrespondenceCost( return (*value_to_correspondence_cost_table_)
correspondence_cost_cells()[ToFlatIndex(cell_index)]); [correspondence_cost_cells()[ToFlatIndex(cell_index)]];
} }
// Returns true if the correspondence cost at the specified index is known. // Returns true if the correspondence cost at the specified index is known.

View File

@ -24,6 +24,7 @@
#include "cartographer/mapping/proto/2d/grid_2d.pb.h" #include "cartographer/mapping/proto/2d/grid_2d.pb.h"
#include "cartographer/mapping/proto/2d/submaps_options_2d.pb.h" #include "cartographer/mapping/proto/2d/submaps_options_2d.pb.h"
#include "cartographer/mapping/proto/submap_visualization.pb.h" #include "cartographer/mapping/proto/submap_visualization.pb.h"
#include "cartographer/mapping/value_conversion_tables.h"
namespace cartographer { namespace cartographer {
namespace mapping { namespace mapping {
@ -34,8 +35,10 @@ proto::GridOptions2D CreateGridOptions2D(
class Grid2D : public GridInterface { class Grid2D : public GridInterface {
public: public:
Grid2D(const MapLimits& limits, float min_correspondence_cost, Grid2D(const MapLimits& limits, float min_correspondence_cost,
float max_correspondence_cost); float max_correspondence_cost,
explicit Grid2D(const proto::Grid2D& proto); ValueConversionTables* conversion_tables);
explicit Grid2D(const proto::Grid2D& proto,
ValueConversionTables* conversion_tables);
// Returns the limits of this Grid2D. // Returns the limits of this Grid2D.
const MapLimits& limits() const { return limits_; } const MapLimits& limits() const { return limits_; }
@ -103,6 +106,7 @@ class Grid2D : public GridInterface {
// Bounding box of known cells to efficiently compute cropping limits. // Bounding box of known cells to efficiently compute cropping limits.
Eigen::AlignedBox2i known_cells_box_; Eigen::AlignedBox2i known_cells_box_;
const std::vector<float>* value_to_correspondence_cost_table_;
}; };
} // namespace mapping } // namespace mapping

View File

@ -24,10 +24,15 @@
namespace cartographer { namespace cartographer {
namespace mapping { namespace mapping {
ProbabilityGrid::ProbabilityGrid(const MapLimits& limits) ProbabilityGrid::ProbabilityGrid(const MapLimits& limits,
: Grid2D(limits, kMinCorrespondenceCost, kMaxCorrespondenceCost) {} ValueConversionTables* conversion_tables)
: Grid2D(limits, kMinCorrespondenceCost, kMaxCorrespondenceCost,
conversion_tables),
conversion_tables_(conversion_tables) {}
ProbabilityGrid::ProbabilityGrid(const proto::Grid2D& proto) : Grid2D(proto) { ProbabilityGrid::ProbabilityGrid(const proto::Grid2D& proto,
ValueConversionTables* conversion_tables)
: Grid2D(proto, conversion_tables), conversion_tables_(conversion_tables) {
CHECK(proto.has_probability_grid_2d()); CHECK(proto.has_probability_grid_2d());
} }
@ -88,7 +93,7 @@ std::unique_ptr<Grid2D> ProbabilityGrid::ComputeCroppedGrid() const {
limits().max() - resolution * Eigen::Vector2d(offset.y(), offset.x()); limits().max() - resolution * Eigen::Vector2d(offset.y(), offset.x());
std::unique_ptr<ProbabilityGrid> cropped_grid = std::unique_ptr<ProbabilityGrid> cropped_grid =
common::make_unique<ProbabilityGrid>( common::make_unique<ProbabilityGrid>(
MapLimits(resolution, max, cell_limits)); MapLimits(resolution, max, cell_limits), conversion_tables_);
for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(cell_limits)) { for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(cell_limits)) {
if (!IsKnown(xy_index + offset)) continue; if (!IsKnown(xy_index + offset)) continue;
cropped_grid->SetProbability(xy_index, GetProbability(xy_index + offset)); cropped_grid->SetProbability(xy_index, GetProbability(xy_index + offset));

View File

@ -30,8 +30,10 @@ namespace mapping {
// Represents a 2D grid of probabilities. // Represents a 2D grid of probabilities.
class ProbabilityGrid : public Grid2D { class ProbabilityGrid : public Grid2D {
public: public:
explicit ProbabilityGrid(const MapLimits& limits); explicit ProbabilityGrid(const MapLimits& limits,
explicit ProbabilityGrid(const proto::Grid2D& proto); ValueConversionTables* conversion_tables);
explicit ProbabilityGrid(const proto::Grid2D& proto,
ValueConversionTables* conversion_tables);
// Sets the probability of the cell at 'cell_index' to the given // Sets the probability of the cell at 'cell_index' to the given
// 'probability'. Only allowed if the cell was unknown before. // 'probability'. Only allowed if the cell was unknown before.
@ -56,6 +58,9 @@ class ProbabilityGrid : public Grid2D {
virtual bool DrawToSubmapTexture( virtual bool DrawToSubmapTexture(
proto::SubmapQuery::Response::SubmapTexture* const texture, proto::SubmapQuery::Response::SubmapTexture* const texture,
transform::Rigid3d local_pose) const override; transform::Rigid3d local_pose) const override;
private:
ValueConversionTables* conversion_tables_;
}; };
} // namespace mapping } // namespace mapping

View File

@ -41,7 +41,8 @@ TEST(ProbabilityGridTest, ProtoConstructor) {
proto.mutable_known_cells_box()->set_min_y(22); proto.mutable_known_cells_box()->set_min_y(22);
proto.mutable_probability_grid_2d(); proto.mutable_probability_grid_2d();
ProbabilityGrid grid(proto); ValueConversionTables conversion_tables;
ProbabilityGrid grid(proto, &conversion_tables);
EXPECT_EQ(proto.limits().DebugString(), ToProto(grid.limits()).DebugString()); EXPECT_EQ(proto.limits().DebugString(), ToProto(grid.limits()).DebugString());
// TODO(macmason): Figure out how to test the contents of cells_ and // TODO(macmason): Figure out how to test the contents of cells_ and
@ -49,8 +50,10 @@ TEST(ProbabilityGridTest, ProtoConstructor) {
} }
TEST(ProbabilityGridTest, ToProto) { TEST(ProbabilityGridTest, ToProto) {
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2))); MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)),
&conversion_tables);
const auto proto = probability_grid.ToProto(); const auto proto = probability_grid.ToProto();
EXPECT_EQ(ToProto(probability_grid.limits()).DebugString(), EXPECT_EQ(ToProto(probability_grid.limits()).DebugString(),
@ -61,8 +64,10 @@ TEST(ProbabilityGridTest, ToProto) {
} }
TEST(ProbabilityGridTest, ApplyOdds) { TEST(ProbabilityGridTest, ApplyOdds) {
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2))); MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)),
&conversion_tables);
const MapLimits& limits = probability_grid.limits(); const MapLimits& limits = probability_grid.limits();
EXPECT_TRUE(limits.Contains(Array2i(0, 0))); EXPECT_TRUE(limits.Contains(Array2i(0, 0)));
@ -108,8 +113,10 @@ TEST(ProbabilityGridTest, ApplyOdds) {
} }
TEST(ProbabilityGridTest, GetProbability) { TEST(ProbabilityGridTest, GetProbability) {
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(1., Eigen::Vector2d(1., 2.), CellLimits(2, 2))); MapLimits(1., Eigen::Vector2d(1., 2.), CellLimits(2, 2)),
&conversion_tables);
const MapLimits& limits = probability_grid.limits(); const MapLimits& limits = probability_grid.limits();
EXPECT_EQ(1., limits.max().x()); EXPECT_EQ(1., limits.max().x());
@ -133,8 +140,10 @@ TEST(ProbabilityGridTest, GetProbability) {
} }
TEST(ProbabilityGridTest, GetCellIndex) { TEST(ProbabilityGridTest, GetCellIndex) {
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(2., Eigen::Vector2d(8., 14.), CellLimits(14, 8))); MapLimits(2., Eigen::Vector2d(8., 14.), CellLimits(14, 8)),
&conversion_tables);
const MapLimits& limits = probability_grid.limits(); const MapLimits& limits = probability_grid.limits();
const CellLimits& cell_limits = limits.cell_limits(); const CellLimits& cell_limits = limits.cell_limits();
@ -167,8 +176,10 @@ TEST(ProbabilityGridTest, CorrectCropping) {
std::mt19937 rng(42); std::mt19937 rng(42);
std::uniform_real_distribution<float> value_distribution(kMinProbability, std::uniform_real_distribution<float> value_distribution(kMinProbability,
kMaxProbability); kMaxProbability);
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(0.05, Eigen::Vector2d(10., 10.), CellLimits(400, 400))); MapLimits(0.05, Eigen::Vector2d(10., 10.), CellLimits(400, 400)),
&conversion_tables);
for (const Array2i& xy_index : for (const Array2i& xy_index :
XYIndexRangeIterator(Array2i(100, 100), Array2i(299, 299))) { XYIndexRangeIterator(Array2i(100, 100), Array2i(299, 299))) {
probability_grid.SetProbability(xy_index, value_distribution(rng)); probability_grid.SetProbability(xy_index, value_distribution(rng));

View File

@ -33,7 +33,8 @@ class RangeDataInserterTest2D : public ::testing::Test {
protected: protected:
RangeDataInserterTest2D() RangeDataInserterTest2D()
: probability_grid_( : probability_grid_(
MapLimits(1., Eigen::Vector2d(1., 5.), CellLimits(5, 5))) { MapLimits(1., Eigen::Vector2d(1., 5.), CellLimits(5, 5)),
&conversion_tables_) {
auto parameter_dictionary = common::MakeDictionary( auto parameter_dictionary = common::MakeDictionary(
"return { " "return { "
"insert_free_space = true, " "insert_free_space = true, "
@ -58,6 +59,7 @@ class RangeDataInserterTest2D : public ::testing::Test {
probability_grid_.FinishUpdate(); probability_grid_.FinishUpdate();
} }
ValueConversionTables conversion_tables_;
ProbabilityGrid probability_grid_; ProbabilityGrid probability_grid_;
std::unique_ptr<ProbabilityGridRangeDataInserter2D> range_data_inserter_; std::unique_ptr<ProbabilityGridRangeDataInserter2D> range_data_inserter_;
proto::ProbabilityGridRangeDataInserterOptions2D options_; proto::ProbabilityGridRangeDataInserterOptions2D options_;

View File

@ -61,17 +61,22 @@ proto::SubmapsOptions2D CreateSubmapsOptions2D(
return options; return options;
} }
Submap2D::Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid) Submap2D::Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid,
ValueConversionTables* conversion_tables)
: Submap(transform::Rigid3d::Translation( : Submap(transform::Rigid3d::Translation(
Eigen::Vector3d(origin.x(), origin.y(), 0.))) { Eigen::Vector3d(origin.x(), origin.y(), 0.))),
conversion_tables_(conversion_tables) {
grid_ = std::move(grid); grid_ = std::move(grid);
} }
Submap2D::Submap2D(const proto::Submap2D& proto) Submap2D::Submap2D(const proto::Submap2D& proto,
: Submap(transform::ToRigid3(proto.local_pose())) { ValueConversionTables* conversion_tables)
: Submap(transform::ToRigid3(proto.local_pose())),
conversion_tables_(conversion_tables) {
if (proto.has_grid()) { if (proto.has_grid()) {
CHECK(proto.grid().has_probability_grid_2d()); CHECK(proto.grid().has_probability_grid_2d());
grid_ = common::make_unique<ProbabilityGrid>(proto.grid()); grid_ =
common::make_unique<ProbabilityGrid>(proto.grid(), conversion_tables_);
} }
set_num_range_data(proto.num_range_data()); set_num_range_data(proto.num_range_data());
set_finished(proto.finished()); set_finished(proto.finished());
@ -96,7 +101,8 @@ void Submap2D::UpdateFromProto(const proto::Submap& proto) {
set_finished(submap_2d.finished()); set_finished(submap_2d.finished());
if (proto.submap_2d().has_grid()) { if (proto.submap_2d().has_grid()) {
CHECK(proto.submap_2d().grid().has_probability_grid_2d()); CHECK(proto.submap_2d().grid().has_probability_grid_2d());
grid_ = common::make_unique<ProbabilityGrid>(submap_2d.grid()); grid_ = common::make_unique<ProbabilityGrid>(submap_2d.grid(),
conversion_tables_);
} }
} }
@ -164,7 +170,8 @@ std::unique_ptr<GridInterface> ActiveSubmaps2D::CreateGrid(
MapLimits(resolution, MapLimits(resolution,
origin.cast<double>() + 0.5 * kInitialSubmapSize * resolution * origin.cast<double>() + 0.5 * kInitialSubmapSize * resolution *
Eigen::Vector2d::Ones(), Eigen::Vector2d::Ones(),
CellLimits(kInitialSubmapSize, kInitialSubmapSize))); CellLimits(kInitialSubmapSize, kInitialSubmapSize)),
&conversion_tables_);
} }
void ActiveSubmaps2D::AddSubmap(const Eigen::Vector2f& origin) { void ActiveSubmaps2D::AddSubmap(const Eigen::Vector2f& origin) {
@ -174,10 +181,11 @@ void ActiveSubmaps2D::AddSubmap(const Eigen::Vector2f& origin) {
CHECK(submaps_.front()->finished()); CHECK(submaps_.front()->finished());
submaps_.erase(submaps_.begin()); submaps_.erase(submaps_.begin());
} }
submaps_.push_back(common::make_unique<Submap2D>( submaps_.push_back(common::make_unique<Submap2D>(
origin, std::unique_ptr<Grid2D>( origin,
static_cast<Grid2D*>(CreateGrid(origin).release())))); std::unique_ptr<Grid2D>(
static_cast<Grid2D*>(CreateGrid(origin).release())),
&conversion_tables_));
} }
} // namespace mapping } // namespace mapping

View File

@ -30,6 +30,7 @@
#include "cartographer/mapping/range_data_inserter_interface.h" #include "cartographer/mapping/range_data_inserter_interface.h"
#include "cartographer/mapping/submaps.h" #include "cartographer/mapping/submaps.h"
#include "cartographer/mapping/trajectory_node.h" #include "cartographer/mapping/trajectory_node.h"
#include "cartographer/mapping/value_conversion_tables.h"
#include "cartographer/sensor/range_data.h" #include "cartographer/sensor/range_data.h"
#include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/rigid_transform.h"
@ -41,8 +42,10 @@ proto::SubmapsOptions2D CreateSubmapsOptions2D(
class Submap2D : public Submap { class Submap2D : public Submap {
public: public:
Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid); Submap2D(const Eigen::Vector2f& origin, std::unique_ptr<Grid2D> grid,
explicit Submap2D(const proto::Submap2D& proto); ValueConversionTables* conversion_tables);
explicit Submap2D(const proto::Submap2D& proto,
ValueConversionTables* conversion_tables);
void ToProto(proto::Submap* proto, void ToProto(proto::Submap* proto,
bool include_probability_grid_data) const override; bool include_probability_grid_data) const override;
@ -61,6 +64,7 @@ class Submap2D : public Submap {
private: private:
std::unique_ptr<Grid2D> grid_; std::unique_ptr<Grid2D> grid_;
ValueConversionTables* conversion_tables_;
}; };
// The first active submap will be created on the insertion of the first range // The first active submap will be created on the insertion of the first range
@ -95,6 +99,7 @@ class ActiveSubmaps2D {
const proto::SubmapsOptions2D options_; const proto::SubmapsOptions2D options_;
std::vector<std::shared_ptr<Submap2D>> submaps_; std::vector<std::shared_ptr<Submap2D>> submaps_;
std::unique_ptr<RangeDataInserterInterface> range_data_inserter_; std::unique_ptr<RangeDataInserterInterface> range_data_inserter_;
ValueConversionTables conversion_tables_;
}; };
} // namespace mapping } // namespace mapping

View File

@ -98,13 +98,16 @@ TEST(Submap2DTest, TheRightNumberOfRangeDataAreInserted) {
TEST(Submap2DTest, ToFromProto) { TEST(Submap2DTest, ToFromProto) {
MapLimits expected_map_limits(1., Eigen::Vector2d(2., 3.), MapLimits expected_map_limits(1., Eigen::Vector2d(2., 3.),
CellLimits(100, 110)); CellLimits(100, 110));
ValueConversionTables conversion_tables;
Submap2D expected(Eigen::Vector2f(4.f, 5.f), Submap2D expected(Eigen::Vector2f(4.f, 5.f),
common::make_unique<ProbabilityGrid>(expected_map_limits)); common::make_unique<ProbabilityGrid>(expected_map_limits,
&conversion_tables),
&conversion_tables);
proto::Submap proto; proto::Submap proto;
expected.ToProto(&proto, true /* include_probability_grid_data */); expected.ToProto(&proto, true /* include_probability_grid_data */);
EXPECT_TRUE(proto.has_submap_2d()); EXPECT_TRUE(proto.has_submap_2d());
EXPECT_FALSE(proto.has_submap_3d()); EXPECT_FALSE(proto.has_submap_3d());
const auto actual = Submap2D(proto.submap_2d()); const auto actual = Submap2D(proto.submap_2d(), &conversion_tables);
EXPECT_TRUE(expected.local_pose().translation().isApprox( EXPECT_TRUE(expected.local_pose().translation().isApprox(
actual.local_pose().translation(), 1e-6)); actual.local_pose().translation(), 1e-6));
EXPECT_TRUE(expected.local_pose().rotation().isApprox( EXPECT_TRUE(expected.local_pose().rotation().isApprox(

View File

@ -20,18 +20,23 @@ namespace cartographer {
namespace mapping { namespace mapping {
TSDF2D::TSDF2D(const MapLimits& limits, float truncation_distance, TSDF2D::TSDF2D(const MapLimits& limits, float truncation_distance,
float max_weight) float max_weight, ValueConversionTables* conversion_tables)
: Grid2D(limits, -truncation_distance, truncation_distance), : Grid2D(limits, -truncation_distance, truncation_distance,
conversion_tables),
conversion_tables_(conversion_tables),
value_converter_(common::make_unique<TSDValueConverter>( value_converter_(common::make_unique<TSDValueConverter>(
truncation_distance, max_weight)), truncation_distance, max_weight, conversion_tables_)),
weight_cells_( weight_cells_(
limits.cell_limits().num_x_cells * limits.cell_limits().num_y_cells, limits.cell_limits().num_x_cells * limits.cell_limits().num_y_cells,
value_converter_->getUnknownWeightValue()) {} value_converter_->getUnknownWeightValue()) {}
TSDF2D::TSDF2D(const proto::Grid2D& proto) : Grid2D(proto) { TSDF2D::TSDF2D(const proto::Grid2D& proto,
ValueConversionTables* conversion_tables)
: Grid2D(proto, conversion_tables), conversion_tables_(conversion_tables) {
CHECK(proto.has_tsdf_2d()); CHECK(proto.has_tsdf_2d());
value_converter_ = common::make_unique<TSDValueConverter>( value_converter_ = common::make_unique<TSDValueConverter>(
proto.tsdf_2d().truncation_distance(), proto.tsdf_2d().max_weight()); proto.tsdf_2d().truncation_distance(), proto.tsdf_2d().max_weight(),
conversion_tables_);
weight_cells_.reserve(proto.tsdf_2d().weight_cells_size()); weight_cells_.reserve(proto.tsdf_2d().weight_cells_size());
for (const auto& cell : proto.tsdf_2d().weight_cells()) { for (const auto& cell : proto.tsdf_2d().weight_cells()) {
CHECK_LE(cell, std::numeric_limits<uint16>::max()); CHECK_LE(cell, std::numeric_limits<uint16>::max());
@ -115,7 +120,7 @@ std::unique_ptr<Grid2D> TSDF2D::ComputeCroppedGrid() const {
limits().max() - resolution * Eigen::Vector2d(offset.y(), offset.x()); limits().max() - resolution * Eigen::Vector2d(offset.y(), offset.x());
std::unique_ptr<TSDF2D> cropped_grid = common::make_unique<TSDF2D>( std::unique_ptr<TSDF2D> cropped_grid = common::make_unique<TSDF2D>(
MapLimits(resolution, max, cell_limits), value_converter_->getMaxTSD(), MapLimits(resolution, max, cell_limits), value_converter_->getMaxTSD(),
value_converter_->getMaxWeight()); value_converter_->getMaxWeight(), conversion_tables_);
for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(cell_limits)) { for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(cell_limits)) {
if (!IsKnown(xy_index + offset)) continue; if (!IsKnown(xy_index + offset)) continue;
cropped_grid->SetCell(xy_index, GetTSD(xy_index + offset), cropped_grid->SetCell(xy_index, GetTSD(xy_index + offset),

View File

@ -30,8 +30,10 @@ namespace mapping {
// Represents a 2D grid of truncated signed distances and weights. // Represents a 2D grid of truncated signed distances and weights.
class TSDF2D : public Grid2D { class TSDF2D : public Grid2D {
public: public:
TSDF2D(const MapLimits& limits, float truncation_distance, float max_weight); TSDF2D(const MapLimits& limits, float truncation_distance, float max_weight,
explicit TSDF2D(const proto::Grid2D& proto); ValueConversionTables* conversion_tables);
explicit TSDF2D(const proto::Grid2D& proto,
ValueConversionTables* conversion_tables);
void SetCell(const Eigen::Array2i& cell_index, const float tsd, void SetCell(const Eigen::Array2i& cell_index, const float tsd,
const float weight); const float weight);
@ -49,6 +51,7 @@ class TSDF2D : public Grid2D {
bool CellIsUpdated(const Eigen::Array2i& cell_index) const; bool CellIsUpdated(const Eigen::Array2i& cell_index) const;
private: private:
ValueConversionTables* conversion_tables_;
std::unique_ptr<TSDValueConverter> value_converter_; std::unique_ptr<TSDValueConverter> value_converter_;
std::vector<uint16> weight_cells_; // Highest bit is update marker. std::vector<uint16> weight_cells_; // Highest bit is update marker.
}; };

View File

@ -18,6 +18,7 @@
#include <random> #include <random>
#include "cartographer/mapping/value_conversion_tables.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace cartographer { namespace cartographer {
@ -45,21 +46,24 @@ TEST(TSDF2DTest, ProtoConstructor) {
proto.set_max_correspondence_cost(1.0); proto.set_max_correspondence_cost(1.0);
proto.set_min_correspondence_cost(-1.0); proto.set_min_correspondence_cost(-1.0);
TSDF2D grid(proto); ValueConversionTables conversion_tables;
TSDF2D grid(proto, &conversion_tables);
EXPECT_EQ(proto.limits().DebugString(), ToProto(grid.limits()).DebugString()); EXPECT_EQ(proto.limits().DebugString(), ToProto(grid.limits()).DebugString());
} }
TEST(TSDF2DTest, ToProto) { TEST(TSDF2DTest, ToProto) {
ValueConversionTables conversion_tables;
TSDF2D tsdf(MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)), 1.0f, TSDF2D tsdf(MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)), 1.0f,
10.0f); 10.0f, &conversion_tables);
const auto proto = tsdf.ToProto(); const auto proto = tsdf.ToProto();
EXPECT_EQ(ToProto(tsdf.limits()).DebugString(), proto.limits().DebugString()); EXPECT_EQ(ToProto(tsdf.limits()).DebugString(), proto.limits().DebugString());
} }
TEST(TSDF2DTest, GetCellIndex) { TEST(TSDF2DTest, GetCellIndex) {
ValueConversionTables conversion_tables;
TSDF2D tsdf(MapLimits(2., Eigen::Vector2d(8., 14.), CellLimits(14, 8)), 1.f, TSDF2D tsdf(MapLimits(2., Eigen::Vector2d(8., 14.), CellLimits(14, 8)), 1.f,
10.f); 10.f, &conversion_tables);
const MapLimits& limits = tsdf.limits(); const MapLimits& limits = tsdf.limits();
const CellLimits& cell_limits = limits.cell_limits(); const CellLimits& cell_limits = limits.cell_limits();
@ -90,8 +94,9 @@ TEST(TSDF2DTest, GetCellIndex) {
TEST(TSDF2DTest, WriteRead) { TEST(TSDF2DTest, WriteRead) {
const float truncation_distance = 1.f; const float truncation_distance = 1.f;
const float max_weight = 10.f; const float max_weight = 10.f;
ValueConversionTables conversion_tables;
TSDF2D tsdf(MapLimits(1., Eigen::Vector2d(1., 2.), CellLimits(2, 2)), TSDF2D tsdf(MapLimits(1., Eigen::Vector2d(1., 2.), CellLimits(2, 2)),
truncation_distance, max_weight); truncation_distance, max_weight, &conversion_tables);
const MapLimits& limits = tsdf.limits(); const MapLimits& limits = tsdf.limits();
EXPECT_EQ(1., limits.max().x()); EXPECT_EQ(1., limits.max().x());
@ -136,8 +141,9 @@ TEST(TSDF2DTest, CorrectCropping) {
std::uniform_real_distribution<float> tsdf_distribution(-truncation_distance, std::uniform_real_distribution<float> tsdf_distribution(-truncation_distance,
truncation_distance); truncation_distance);
std::uniform_real_distribution<float> weight_distribution(0.f, max_weight); std::uniform_real_distribution<float> weight_distribution(0.f, max_weight);
ValueConversionTables conversion_tables;
TSDF2D tsdf(MapLimits(0.05, Eigen::Vector2d(10., 10.), CellLimits(400, 400)), TSDF2D tsdf(MapLimits(0.05, Eigen::Vector2d(10., 10.), CellLimits(400, 400)),
truncation_distance, max_weight); truncation_distance, max_weight, &conversion_tables);
for (const Array2i& xy_index : for (const Array2i& xy_index :
XYIndexRangeIterator(Array2i(100, 100), Array2i(299, 299))) { XYIndexRangeIterator(Array2i(100, 100), Array2i(299, 299))) {
tsdf.SetCell(xy_index, tsdf_distribution(rng), weight_distribution(rng)); tsdf.SetCell(xy_index, tsdf_distribution(rng), weight_distribution(rng));

View File

@ -28,7 +28,7 @@ class RangeDataInserterTest2DTSDF : public ::testing::Test {
protected: protected:
RangeDataInserterTest2DTSDF() RangeDataInserterTest2DTSDF()
: tsdf_(MapLimits(1., Eigen::Vector2d(0., 7.), CellLimits(8, 1)), 2.0, : tsdf_(MapLimits(1., Eigen::Vector2d(0., 7.), CellLimits(8, 1)), 2.0,
10.0) { 10.0, &conversion_tables_) {
auto parameter_dictionary = common::MakeDictionary( auto parameter_dictionary = common::MakeDictionary(
"return { " "return { "
"truncation_distance = 2.0," "truncation_distance = 2.0,"
@ -57,6 +57,7 @@ class RangeDataInserterTest2DTSDF : public ::testing::Test {
tsdf_.FinishUpdate(); tsdf_.FinishUpdate();
} }
ValueConversionTables conversion_tables_;
proto::TSDFRangeDataInserterOptions2D options_; proto::TSDFRangeDataInserterOptions2D options_;
TSDF2D tsdf_; TSDF2D tsdf_;
std::unique_ptr<TSDFRangeDataInserter2D> range_data_inserter_; std::unique_ptr<TSDFRangeDataInserter2D> range_data_inserter_;

View File

@ -69,7 +69,7 @@ class OverlappingSubmapsTrimmer2DTest : public ::testing::Test {
grid->mutable_probability_grid_2d(); grid->mutable_probability_grid_2d();
fake_pose_graph_.mutable_submap_data()->Insert( fake_pose_graph_.mutable_submap_data()->Insert(
{0 /* trajectory_id */, submap_index}, {0 /* trajectory_id */, submap_index},
{std::make_shared<const Submap2D>(submap_2d), {std::make_shared<const Submap2D>(submap_2d, &conversion_tables_),
transform::Embed3D(global_to_submap_frame)}); transform::Embed3D(global_to_submap_frame)});
} }
@ -91,6 +91,7 @@ class OverlappingSubmapsTrimmer2DTest : public ::testing::Test {
: PoseGraphInterface::Constraint::INTER_SUBMAP}); : PoseGraphInterface::Constraint::INTER_SUBMAP});
} }
ValueConversionTables conversion_tables_;
testing::FakeTrimmable fake_pose_graph_; testing::FakeTrimmable fake_pose_graph_;
}; };

View File

@ -547,12 +547,12 @@ void PoseGraph2D::AddSubmapFromProto(
const SubmapId submap_id = {submap.submap_id().trajectory_id(), const SubmapId submap_id = {submap.submap_id().trajectory_id(),
submap.submap_id().submap_index()}; submap.submap_id().submap_index()};
std::shared_ptr<const Submap2D> submap_ptr =
std::make_shared<const Submap2D>(submap.submap_2d());
const transform::Rigid2d global_submap_pose_2d =
transform::Project2D(global_submap_pose);
common::MutexLocker locker(&mutex_); common::MutexLocker locker(&mutex_);
std::shared_ptr<const Submap2D> submap_ptr =
std::make_shared<const Submap2D>(submap.submap_2d(), &conversion_tables_);
const transform::Rigid2d global_submap_pose_2d =
transform::Project2D(global_submap_pose);
AddTrajectoryIfNeeded(submap_id.trajectory_id); AddTrajectoryIfNeeded(submap_id.trajectory_id);
if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return; if (!CanAddWorkItemModifying(submap_id.trajectory_id)) return;
data_.submap_data.Insert(submap_id, InternalSubmapData()); data_.submap_data.Insert(submap_id, InternalSubmapData());

View File

@ -244,6 +244,8 @@ class PoseGraph2D : public PoseGraph {
PoseGraphData data_ GUARDED_BY(mutex_); PoseGraphData data_ GUARDED_BY(mutex_);
ValueConversionTables conversion_tables_;
// Allows querying and manipulating the pose graph by the 'trimmers_'. The // Allows querying and manipulating the pose graph by the 'trimmers_'. The
// 'mutex_' of the pose graph is held while this class is used. // 'mutex_' of the pose graph is held while this class is used.
class TrimmingHandle : public Trimmable { class TrimmingHandle : public Trimmable {

View File

@ -36,7 +36,8 @@ class CeresScanMatcherTest : public ::testing::Test {
protected: protected:
CeresScanMatcherTest() CeresScanMatcherTest()
: probability_grid_( : probability_grid_(
MapLimits(1., Eigen::Vector2d(10., 10.), CellLimits(20, 20))) { MapLimits(1., Eigen::Vector2d(10., 10.), CellLimits(20, 20)),
&conversion_tables_) {
probability_grid_.SetProbability( probability_grid_.SetProbability(
probability_grid_.limits().GetCellIndex(Eigen::Vector2f(-3.5f, 2.5f)), probability_grid_.limits().GetCellIndex(Eigen::Vector2f(-3.5f, 2.5f)),
kMaxProbability); kMaxProbability);
@ -73,6 +74,7 @@ class CeresScanMatcherTest : public ::testing::Test {
<< "\nExpected: " << transform::ToProto(expected_pose).DebugString(); << "\nExpected: " << transform::ToProto(expected_pose).DebugString();
} }
ValueConversionTables conversion_tables_;
ProbabilityGrid probability_grid_; ProbabilityGrid probability_grid_;
sensor::PointCloud point_cloud_; sensor::PointCloud point_cloud_;
std::unique_ptr<CeresScanMatcher2D> ceres_scan_matcher_; std::unique_ptr<CeresScanMatcher2D> ceres_scan_matcher_;

View File

@ -39,8 +39,10 @@ TEST(PrecomputationGridTest, CorrectValues) {
// represented by uint8 values. // represented by uint8 values.
std::mt19937 prng(42); std::mt19937 prng(42);
std::uniform_int_distribution<int> distribution(0, 255); std::uniform_int_distribution<int> distribution(0, 255);
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(250, 250))); MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(250, 250)),
&conversion_tables);
std::vector<float> reusable_intermediate_grid; std::vector<float> reusable_intermediate_grid;
PrecomputationGrid2D precomputation_grid_dummy( PrecomputationGrid2D precomputation_grid_dummy(
probability_grid, probability_grid.limits().cell_limits(), 1, probability_grid, probability_grid.limits().cell_limits(), 1,
@ -77,8 +79,10 @@ TEST(PrecomputationGridTest, CorrectValues) {
TEST(PrecomputationGridTest, TinyProbabilityGrid) { TEST(PrecomputationGridTest, TinyProbabilityGrid) {
std::mt19937 prng(42); std::mt19937 prng(42);
std::uniform_int_distribution<int> distribution(0, 255); std::uniform_int_distribution<int> distribution(0, 255);
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(0.05, Eigen::Vector2d(0.1, 0.1), CellLimits(4, 4))); MapLimits(0.05, Eigen::Vector2d(0.1, 0.1), CellLimits(4, 4)),
&conversion_tables);
std::vector<float> reusable_intermediate_grid; std::vector<float> reusable_intermediate_grid;
PrecomputationGrid2D precomputation_grid_dummy( PrecomputationGrid2D precomputation_grid_dummy(
probability_grid, probability_grid.limits().cell_limits(), 1, probability_grid, probability_grid.limits().cell_limits(), 1,
@ -158,8 +162,10 @@ TEST(FastCorrelativeScanMatcherTest, CorrectPose) {
{2. * distribution(prng), 2. * distribution(prng)}, {2. * distribution(prng), 2. * distribution(prng)},
0.5 * distribution(prng)); 0.5 * distribution(prng));
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200))); MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200)),
&conversion_tables);
range_data_inserter.Insert( range_data_inserter.Insert(
sensor::RangeData{ sensor::RangeData{
Eigen::Vector3f(expected_pose.translation().x(), Eigen::Vector3f(expected_pose.translation().x(),
@ -212,8 +218,10 @@ TEST(FastCorrelativeScanMatcherTest, FullSubmapMatching) {
0.5 * distribution(prng)) * 0.5 * distribution(prng)) *
perturbation.inverse(); perturbation.inverse();
ValueConversionTables conversion_tables;
ProbabilityGrid probability_grid( ProbabilityGrid probability_grid(
MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200))); MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200)),
&conversion_tables);
range_data_inserter.Insert( range_data_inserter.Insert(
sensor::RangeData{ sensor::RangeData{
transform::Embed3D(expected_pose * perturbation).translation(), transform::Embed3D(expected_pose * perturbation).translation(),

View File

@ -31,8 +31,9 @@ using ::testing::DoubleEq;
using ::testing::ElementsAre; using ::testing::ElementsAre;
TEST(OccupiedSpaceCostFunction2DTest, SmokeTest) { TEST(OccupiedSpaceCostFunction2DTest, SmokeTest) {
ProbabilityGrid grid( ValueConversionTables conversion_tables;
MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2))); ProbabilityGrid grid(MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)),
&conversion_tables);
sensor::PointCloud point_cloud = {Eigen::Vector3f{0.f, 0.f, 0.f}}; sensor::PointCloud point_cloud = {Eigen::Vector3f{0.f, 0.f, 0.f}};
ceres::Problem problem; ceres::Problem problem;
std::unique_ptr<ceres::CostFunction> cost_function( std::unique_ptr<ceres::CostFunction> cost_function(

View File

@ -38,7 +38,8 @@ class RealTimeCorrelativeScanMatcherTest : public ::testing::Test {
protected: protected:
RealTimeCorrelativeScanMatcherTest() RealTimeCorrelativeScanMatcherTest()
: probability_grid_( : probability_grid_(
MapLimits(0.05, Eigen::Vector2d(0.05, 0.25), CellLimits(6, 6))) { MapLimits(0.05, Eigen::Vector2d(0.05, 0.25), CellLimits(6, 6)),
&conversion_tables_) {
{ {
auto parameter_dictionary = common::MakeDictionary( auto parameter_dictionary = common::MakeDictionary(
"return { " "return { "
@ -77,6 +78,7 @@ class RealTimeCorrelativeScanMatcherTest : public ::testing::Test {
} }
} }
ValueConversionTables conversion_tables_;
ProbabilityGrid probability_grid_; ProbabilityGrid probability_grid_;
std::unique_ptr<ProbabilityGridRangeDataInserter2D> range_data_inserter_; std::unique_ptr<ProbabilityGridRangeDataInserter2D> range_data_inserter_;
sensor::PointCloud point_cloud_; sensor::PointCloud point_cloud_;

View File

@ -75,8 +75,11 @@ TEST_F(ConstraintBuilder2DTest, FindsConstraints) {
node_data.local_pose = transform::Rigid3d::Identity(); node_data.local_pose = transform::Rigid3d::Identity();
SubmapId submap_id{0, 1}; SubmapId submap_id{0, 1};
MapLimits map_limits(1., Eigen::Vector2d(2., 3.), CellLimits(100, 110)); MapLimits map_limits(1., Eigen::Vector2d(2., 3.), CellLimits(100, 110));
Submap2D submap(Eigen::Vector2f(4.f, 5.f), ValueConversionTables conversion_tables;
common::make_unique<ProbabilityGrid>(map_limits)); Submap2D submap(
Eigen::Vector2f(4.f, 5.f),
common::make_unique<ProbabilityGrid>(map_limits, &conversion_tables),
&conversion_tables);
int expected_nodes = 0; int expected_nodes = 0;
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
EXPECT_EQ(constraint_builder_->GetNumFinishedNodes(), expected_nodes); EXPECT_EQ(constraint_builder_->GetNumFinishedNodes(), expected_nodes);

View File

@ -23,7 +23,8 @@ template <>
std::shared_ptr<mapping::Submap2D> std::shared_ptr<mapping::Submap2D>
SubmapController<mapping::Submap2D>::CreateSubmap( SubmapController<mapping::Submap2D>::CreateSubmap(
const mapping::proto::Submap& proto) { const mapping::proto::Submap& proto) {
return std::make_shared<mapping::Submap2D>(proto.submap_2d()); return std::make_shared<mapping::Submap2D>(proto.submap_2d(),
&conversion_tables_);
} }
template <> template <>

View File

@ -60,6 +60,8 @@ class SubmapController {
mapping::MapById<mapping::SubmapId, std::shared_ptr<SubmapType>> mapping::MapById<mapping::SubmapId, std::shared_ptr<SubmapType>>
unfinished_submaps_; unfinished_submaps_;
ValueConversionTables conversion_tables_;
}; };
template <> template <>

View File

@ -19,59 +19,17 @@
namespace cartographer { namespace cartographer {
namespace mapping { namespace mapping {
TSDValueConverter::TSDValueConverter(float max_tsd, float max_weight) TSDValueConverter::TSDValueConverter(float max_tsd, float max_weight,
ValueConversionTables* conversion_tables)
: max_tsd_(max_tsd), : max_tsd_(max_tsd),
min_tsd_(-max_tsd), min_tsd_(-max_tsd),
max_weight_(max_weight), max_weight_(max_weight),
tsd_resolution_(32766.f / (max_tsd_ - min_tsd_)), tsd_resolution_(32766.f / (max_tsd_ - min_tsd_)),
weight_resolution_(32766.f / (max_weight_ - min_weight_)), weight_resolution_(32766.f / (max_weight_ - min_weight_)),
value_to_tsd_(PrecomputeValueToTSD()), value_to_tsd_(conversion_tables->GetConversionTable(unknown_tsd_value_,
value_to_weight_(PrecomputeValueToWeight()) {} min_tsd_, max_tsd_)),
value_to_weight_(conversion_tables->GetConversionTable(
// 0 is unknown, [1, 32767] maps to [min_tsd_ max_tsd_]. unknown_weight_value_, min_weight_, max_weight)) {}
float TSDValueConverter::SlowValueToTSD(const uint16 value) const {
CHECK_GE(value, 0);
CHECK_LE(value, 32767);
if (value == unknown_tsd_value_) {
return min_tsd_;
}
const float kScale = (max_tsd_ - min_tsd_) / 32766.f;
return value * kScale + (min_tsd_ - kScale);
}
std::vector<float> TSDValueConverter::PrecomputeValueToTSD() {
std::vector<float> result;
size_t num_values = std::numeric_limits<uint16>::max() + 1;
result.reserve(num_values);
for (size_t value = 0; value != num_values; ++value) {
result.push_back(
SlowValueToTSD(static_cast<uint16>(value) & ~update_marker_));
}
return result;
}
// 0 is unknown, [1, 32767] maps to [min_weight_ max_weight_].
float TSDValueConverter::SlowValueToWeight(const uint16 value) const {
CHECK_GE(value, 0);
CHECK_LE(value, 32767);
if (value == unknown_weight_value_) {
// Unknown cells have min_weight_.
return min_weight_;
}
const float kScale = (max_weight_ - min_weight_) / 32766.f;
return value * kScale + (min_weight_ - kScale);
}
std::vector<float> TSDValueConverter::PrecomputeValueToWeight() {
std::vector<float> result;
size_t num_values = std::numeric_limits<uint16>::max() + 1;
result.reserve(num_values);
for (size_t value = 0; value != num_values; ++value) {
result.push_back(
SlowValueToWeight(static_cast<uint16>(value) & ~update_marker_));
}
return result;
}
} // namespace mapping } // namespace mapping
} // namespace cartographer } // namespace cartographer

View File

@ -22,6 +22,7 @@
#include "cartographer/common/math.h" #include "cartographer/common/math.h"
#include "cartographer/common/port.h" #include "cartographer/common/port.h"
#include "cartographer/mapping/value_conversion_tables.h"
#include "glog/logging.h" #include "glog/logging.h"
namespace cartographer { namespace cartographer {
@ -31,7 +32,8 @@ namespace mapping {
// truncated signed distance values and weights. // truncated signed distance values and weights.
class TSDValueConverter { class TSDValueConverter {
public: public:
TSDValueConverter(float max_tsd, float max_weight); TSDValueConverter(float max_tsd, float max_weight,
ValueConversionTables* conversion_tables);
// Converts a tsd to a uint16 in the [1, 32767] range. // Converts a tsd to a uint16 in the [1, 32767] range.
inline uint16 TSDToValue(const float tsd) const { inline uint16 TSDToValue(const float tsd) const {
@ -55,13 +57,13 @@ class TSDValueConverter {
// Converts a uint16 (which may or may not have the update marker set) to a // Converts a uint16 (which may or may not have the update marker set) to a
// value in the range [min_tsd_, max_tsd_]. // value in the range [min_tsd_, max_tsd_].
inline float ValueToTSD(const uint16 value) const { inline float ValueToTSD(const uint16 value) const {
return value_to_tsd_[value]; return (*value_to_tsd_)[value];
} }
// Converts a uint16 (which may or may not have the update marker set) to a // Converts a uint16 (which may or may not have the update marker set) to a
// value in the range [min_weight_, max_weight_]. // value in the range [min_weight_, max_weight_].
inline float ValueToWeight(const uint16 value) const { inline float ValueToWeight(const uint16 value) const {
return value_to_weight_[value]; return (*value_to_weight_)[value];
} }
static uint16 getUnknownTSDValue() { return unknown_tsd_value_; } static uint16 getUnknownTSDValue() { return unknown_tsd_value_; }
@ -81,10 +83,6 @@ class TSDValueConverter {
inline float ClampWeight(const float weight) const { inline float ClampWeight(const float weight) const {
return common::Clamp(weight, min_weight_, max_weight_); return common::Clamp(weight, min_weight_, max_weight_);
} }
float SlowValueToTSD(const uint16 value) const;
std::vector<float> PrecomputeValueToTSD();
float SlowValueToWeight(const uint16 value) const;
std::vector<float> PrecomputeValueToWeight();
float max_tsd_; float max_tsd_;
float min_tsd_; float min_tsd_;
@ -96,8 +94,8 @@ class TSDValueConverter {
static constexpr uint16 unknown_weight_value_ = 0; static constexpr uint16 unknown_weight_value_ = 0;
static constexpr uint16 update_marker_ = 1u << 15; static constexpr uint16 update_marker_ = 1u << 15;
std::vector<float> value_to_tsd_; const std::vector<float>* value_to_tsd_;
std::vector<float> value_to_weight_; const std::vector<float>* value_to_weight_;
}; };
} // namespace mapping } // namespace mapping

View File

@ -27,7 +27,9 @@ class TSDValueConverterTest : public ::testing::Test {
TSDValueConverterTest() TSDValueConverterTest()
: truncation_distance_(0.1f), : truncation_distance_(0.1f),
max_weight_(10.0f), max_weight_(10.0f),
tsd_value_converter_(truncation_distance_, max_weight_) {} tsd_value_converter_(truncation_distance_, max_weight_,
&conversion_tables) {}
ValueConversionTables conversion_tables;
float truncation_distance_; float truncation_distance_;
float max_weight_; float max_weight_;
TSDValueConverter tsd_value_converter_; TSDValueConverter tsd_value_converter_;

View File

@ -28,7 +28,6 @@ float SlowValueToBoundedFloat(const uint16 value, const uint16 unknown_value,
const float unknown_result, const float unknown_result,
const float lower_bound, const float lower_bound,
const float upper_bound) { const float upper_bound) {
CHECK_GE(value, 0);
CHECK_LE(value, 32767); CHECK_LE(value, 32767);
if (value == unknown_value) return unknown_result; if (value == unknown_value) return unknown_result;
const float kScale = (upper_bound - lower_bound) / 32766.f; const float kScale = (upper_bound - lower_bound) / 32766.f;

View File

@ -0,0 +1,70 @@
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cartographer/mapping/value_conversion_tables.h"
#include "cartographer/common/make_unique.h"
#include "glog/logging.h"
namespace cartographer {
namespace mapping {
namespace {
constexpr uint16 kUpdateMarker = 1u << 15;
// 0 is unknown, [1, 32767] maps to [lower_bound, upper_bound].
float SlowValueToBoundedFloat(const uint16 value, const uint16 unknown_value,
const float unknown_result,
const float lower_bound,
const float upper_bound) {
CHECK_LE(value, 32767);
if (value == unknown_value) return unknown_result;
const float kScale = (upper_bound - lower_bound) / 32766.f;
return value * kScale + (lower_bound - kScale);
}
std::unique_ptr<std::vector<float>> PrecomputeValueToBoundedFloat(
const uint16 unknown_value, const float unknown_result,
const float lower_bound, const float upper_bound) {
auto result = common::make_unique<std::vector<float>>();
size_t num_values = std::numeric_limits<uint16>::max() + 1;
result->reserve(num_values);
for (size_t value = 0; value != num_values; ++value) {
result->push_back(SlowValueToBoundedFloat(
static_cast<uint16>(value) & ~kUpdateMarker, unknown_value,
unknown_result, lower_bound, upper_bound));
}
return result;
}
} // namespace
const std::vector<float>* ValueConversionTables::GetConversionTable(
float unknown_result, float lower_bound, float upper_bound) {
std::tuple<float, float, float> bounds =
std::make_tuple(unknown_result, lower_bound, upper_bound);
auto lookup_table_iterator = bounds_to_lookup_table_.find(bounds);
if (lookup_table_iterator == bounds_to_lookup_table_.end()) {
auto insertion_result = bounds_to_lookup_table_.emplace(
bounds, PrecomputeValueToBoundedFloat(0, unknown_result, lower_bound,
upper_bound));
return insertion_result.first->second.get();
} else {
return lookup_table_iterator->second.get();
}
}
} // namespace mapping
} // namespace cartographer

View File

@ -0,0 +1,48 @@
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CARTOGRAPHER_MAPPING_VALUE_CONVERSION_TABLES_H_
#define CARTOGRAPHER_MAPPING_VALUE_CONVERSION_TABLES_H_
#include <map>
#include <vector>
#include "cartographer/common/port.h"
#include "glog/logging.h"
namespace cartographer {
namespace mapping {
// Performs lazy computations of lookup tables for mapping from a uint16 value
// to a float in ['lower_bound', 'upper_bound']. The first element of the table
// is set to 'unknown_result'.
class ValueConversionTables {
public:
const std::vector<float>* GetConversionTable(float unknown_result,
float lower_bound,
float upper_bound);
private:
std::map<const std::tuple<float /* unknown_result */, float /* lower_bound */,
float /* upper_bound */>,
std::unique_ptr<const std::vector<float>>>
bounds_to_lookup_table_;
};
} // namespace mapping
} // namespace cartographer
#endif // CARTOGRAPHER_MAPPING_VALUE_CONVERSION_TABLES_H_

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cartographer/mapping/value_conversion_tables.h"
#include <random>
#include "gtest/gtest.h"
namespace cartographer {
namespace mapping {
namespace {
TEST(ValueConversionTablesTest, EqualTables) {
ValueConversionTables value_conversion_tables;
const std::vector<float>* reference_table =
value_conversion_tables.GetConversionTable(0.1f, 0.1f, 0.5f);
const std::vector<float>* test_table =
value_conversion_tables.GetConversionTable(0.1f, 0.1f, 0.5f);
EXPECT_EQ(reference_table, test_table);
}
TEST(ValueConversionTablesTest, InequalTables) {
ValueConversionTables value_conversion_tables;
const std::vector<float>* reference_table =
value_conversion_tables.GetConversionTable(0.1f, 0.1f, 0.5f);
const std::vector<float>* test_table =
value_conversion_tables.GetConversionTable(0.1f, 0.4f, 0.5f);
EXPECT_FALSE(reference_table == test_table);
}
TEST(ValueConversionTablesTest, ValueConversion) {
ValueConversionTables value_conversion_tables;
std::mt19937 rng(42);
std::uniform_real_distribution<float> bound_distribution(-10.f, 10.f);
for (size_t sample_index = 0; sample_index < 100; ++sample_index) {
const float bound_sample_0 = bound_distribution(rng);
const float bound_sample_1 = bound_distribution(rng);
const float lower_bound = std::min(bound_sample_0, bound_sample_1);
const float upper_bound = std::max(bound_sample_0, bound_sample_1);
const float undefined_value = bound_distribution(rng);
const std::vector<float>* conversion_table =
value_conversion_tables.GetConversionTable(undefined_value, lower_bound,
upper_bound);
EXPECT_EQ((*conversion_table)[0], undefined_value);
const float scale = (upper_bound - lower_bound) / 32766.f;
for (uint16 i = 1; i < 32768; ++i) {
EXPECT_EQ((*conversion_table)[i], i * scale + (lower_bound - scale));
}
}
}
} // namespace
} // namespace mapping
} // namespace cartographer