Use intensities in `Submap3D` and `RangeDataInserter3D`. (#1765)
`Submap3D` now also stores a pointer to `IntensityHybridGrid`. Adapted `RangeDataInserter3D` to also insert intensities. Signed-off-by: Wolfgang Hess <whess@lyft.com>master
parent
aeb8a10c01
commit
a9ad813c26
|
@ -40,7 +40,8 @@ HybridGridPointsProcessor::FromDictionary(
|
||||||
|
|
||||||
void HybridGridPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {
|
void HybridGridPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {
|
||||||
range_data_inserter_.Insert(
|
range_data_inserter_.Insert(
|
||||||
{batch->origin, sensor::PointCloud(batch->points), {}}, &hybrid_grid_);
|
{batch->origin, sensor::PointCloud(batch->points), {}}, &hybrid_grid_,
|
||||||
|
/*intensity_hybrid_grid=*/nullptr);
|
||||||
next_->Process(std::move(batch));
|
next_->Process(std::move(batch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,21 @@ void InsertMissesIntoGrid(const std::vector<uint16>& miss_table,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InsertIntensitiesIntoGrid(const sensor::PointCloud& returns,
|
||||||
|
IntensityHybridGrid* intensity_hybrid_grid,
|
||||||
|
const float intensity_threshold) {
|
||||||
|
if (returns.intensities().size() > 0) {
|
||||||
|
for (size_t i = 0; i < returns.size(); ++i) {
|
||||||
|
if (returns.intensities()[i] > intensity_threshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const Eigen::Array3i hit_cell =
|
||||||
|
intensity_hybrid_grid->GetCellIndex(returns[i].position);
|
||||||
|
intensity_hybrid_grid->AddIntensity(hit_cell, returns.intensities()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
proto::RangeDataInserterOptions3D CreateRangeDataInserterOptions3D(
|
proto::RangeDataInserterOptions3D CreateRangeDataInserterOptions3D(
|
||||||
|
@ -62,6 +77,8 @@ proto::RangeDataInserterOptions3D CreateRangeDataInserterOptions3D(
|
||||||
parameter_dictionary->GetDouble("miss_probability"));
|
parameter_dictionary->GetDouble("miss_probability"));
|
||||||
options.set_num_free_space_voxels(
|
options.set_num_free_space_voxels(
|
||||||
parameter_dictionary->GetInt("num_free_space_voxels"));
|
parameter_dictionary->GetInt("num_free_space_voxels"));
|
||||||
|
options.set_intensity_threshold(
|
||||||
|
parameter_dictionary->GetDouble("intensity_threshold"));
|
||||||
CHECK_GT(options.hit_probability(), 0.5);
|
CHECK_GT(options.hit_probability(), 0.5);
|
||||||
CHECK_LT(options.miss_probability(), 0.5);
|
CHECK_LT(options.miss_probability(), 0.5);
|
||||||
return options;
|
return options;
|
||||||
|
@ -75,9 +92,10 @@ RangeDataInserter3D::RangeDataInserter3D(
|
||||||
miss_table_(
|
miss_table_(
|
||||||
ComputeLookupTableToApplyOdds(Odds(options_.miss_probability()))) {}
|
ComputeLookupTableToApplyOdds(Odds(options_.miss_probability()))) {}
|
||||||
|
|
||||||
void RangeDataInserter3D::Insert(const sensor::RangeData& range_data,
|
void RangeDataInserter3D::Insert(
|
||||||
HybridGrid* hybrid_grid) const {
|
const sensor::RangeData& range_data, HybridGrid* hybrid_grid,
|
||||||
CHECK(hybrid_grid != nullptr);
|
IntensityHybridGrid* intensity_hybrid_grid) const {
|
||||||
|
CHECK_NOTNULL(hybrid_grid);
|
||||||
|
|
||||||
for (const sensor::RangefinderPoint& hit : range_data.returns) {
|
for (const sensor::RangefinderPoint& hit : range_data.returns) {
|
||||||
const Eigen::Array3i hit_cell = hybrid_grid->GetCellIndex(hit.position);
|
const Eigen::Array3i hit_cell = hybrid_grid->GetCellIndex(hit.position);
|
||||||
|
@ -88,6 +106,10 @@ void RangeDataInserter3D::Insert(const sensor::RangeData& range_data,
|
||||||
// (i.e. no hits will be ignored because of a miss in the same cell).
|
// (i.e. no hits will be ignored because of a miss in the same cell).
|
||||||
InsertMissesIntoGrid(miss_table_, range_data.origin, range_data.returns,
|
InsertMissesIntoGrid(miss_table_, range_data.origin, range_data.returns,
|
||||||
hybrid_grid, options_.num_free_space_voxels());
|
hybrid_grid, options_.num_free_space_voxels());
|
||||||
|
if (intensity_hybrid_grid != nullptr) {
|
||||||
|
InsertIntensitiesIntoGrid(range_data.returns, intensity_hybrid_grid,
|
||||||
|
options_.intensity_threshold());
|
||||||
|
}
|
||||||
hybrid_grid->FinishUpdate();
|
hybrid_grid->FinishUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,10 @@ class RangeDataInserter3D {
|
||||||
RangeDataInserter3D(const RangeDataInserter3D&) = delete;
|
RangeDataInserter3D(const RangeDataInserter3D&) = delete;
|
||||||
RangeDataInserter3D& operator=(const RangeDataInserter3D&) = delete;
|
RangeDataInserter3D& operator=(const RangeDataInserter3D&) = delete;
|
||||||
|
|
||||||
// Inserts 'range_data' into 'hybrid_grid'.
|
// Inserts 'range_data' into 'hybrid_grid' and optionally into
|
||||||
void Insert(const sensor::RangeData& range_data,
|
// 'intensity_hybrid_grid'.
|
||||||
HybridGrid* hybrid_grid) const;
|
void Insert(const sensor::RangeData& range_data, HybridGrid* hybrid_grid,
|
||||||
|
IntensityHybridGrid* intensity_hybrid_grid) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const proto::RangeDataInserterOptions3D options_;
|
const proto::RangeDataInserterOptions3D options_;
|
||||||
|
|
|
@ -28,12 +28,13 @@ namespace {
|
||||||
|
|
||||||
class RangeDataInserter3DTest : public ::testing::Test {
|
class RangeDataInserter3DTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
RangeDataInserter3DTest() : hybrid_grid_(1.f) {
|
RangeDataInserter3DTest() : hybrid_grid_(1.f), intensity_hybrid_grid_(1.f) {
|
||||||
auto parameter_dictionary = common::MakeDictionary(
|
auto parameter_dictionary = common::MakeDictionary(
|
||||||
"return { "
|
"return { "
|
||||||
"hit_probability = 0.7, "
|
"hit_probability = 0.7, "
|
||||||
"miss_probability = 0.4, "
|
"miss_probability = 0.4, "
|
||||||
"num_free_space_voxels = 1000, "
|
"num_free_space_voxels = 1000, "
|
||||||
|
"intensity_threshold = 100, "
|
||||||
"}");
|
"}");
|
||||||
options_ = CreateRangeDataInserterOptions3D(parameter_dictionary.get());
|
options_ = CreateRangeDataInserterOptions3D(parameter_dictionary.get());
|
||||||
range_data_inserter_.reset(new RangeDataInserter3D(options_));
|
range_data_inserter_.reset(new RangeDataInserter3D(options_));
|
||||||
|
@ -41,14 +42,28 @@ class RangeDataInserter3DTest : public ::testing::Test {
|
||||||
|
|
||||||
void InsertPointCloud() {
|
void InsertPointCloud() {
|
||||||
const Eigen::Vector3f origin = Eigen::Vector3f(0.f, 0.f, -4.f);
|
const Eigen::Vector3f origin = Eigen::Vector3f(0.f, 0.f, -4.f);
|
||||||
std::vector<sensor::RangefinderPoint> returns = {
|
const std::vector<sensor::RangefinderPoint> returns = {
|
||||||
{Eigen::Vector3f{-3.f, -1.f, 4.f}},
|
{Eigen::Vector3f{-3.f, -1.f, 4.f}},
|
||||||
{Eigen::Vector3f{-2.f, 0.f, 4.f}},
|
{Eigen::Vector3f{-2.f, 0.f, 4.f}},
|
||||||
{Eigen::Vector3f{-1.f, 1.f, 4.f}},
|
{Eigen::Vector3f{-1.f, 1.f, 4.f}},
|
||||||
{Eigen::Vector3f{0.f, 2.f, 4.f}}};
|
{Eigen::Vector3f{0.f, 2.f, 4.f}}};
|
||||||
range_data_inserter_->Insert(
|
range_data_inserter_->Insert(
|
||||||
sensor::RangeData{origin, sensor::PointCloud(returns), {}},
|
sensor::RangeData{origin, sensor::PointCloud(returns), {}},
|
||||||
&hybrid_grid_);
|
&hybrid_grid_,
|
||||||
|
/*intensity_hybrid_grid=*/nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertPointCloudWithIntensities() {
|
||||||
|
const Eigen::Vector3f origin = Eigen::Vector3f(0.f, 0.f, -4.f);
|
||||||
|
const std::vector<sensor::RangefinderPoint> returns = {
|
||||||
|
{Eigen::Vector3f{-3.f, -1.f, 4.f}},
|
||||||
|
{Eigen::Vector3f{-2.f, 0.f, 4.f}},
|
||||||
|
{Eigen::Vector3f{-1.f, 1.f, 4.f}},
|
||||||
|
{Eigen::Vector3f{0.f, 2.f, 4.f}}};
|
||||||
|
const std::vector<float> intensities{7.f, 8.f, 9.f, 10.f};
|
||||||
|
range_data_inserter_->Insert(
|
||||||
|
sensor::RangeData{origin, sensor::PointCloud(returns, intensities), {}},
|
||||||
|
&hybrid_grid_, &intensity_hybrid_grid_);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetProbability(float x, float y, float z) const {
|
float GetProbability(float x, float y, float z) const {
|
||||||
|
@ -56,6 +71,11 @@ class RangeDataInserter3DTest : public ::testing::Test {
|
||||||
hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));
|
hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float GetIntensity(float x, float y, float z) const {
|
||||||
|
return intensity_hybrid_grid_.GetIntensity(
|
||||||
|
intensity_hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));
|
||||||
|
}
|
||||||
|
|
||||||
float IsKnown(float x, float y, float z) const {
|
float IsKnown(float x, float y, float z) const {
|
||||||
return hybrid_grid_.IsKnown(
|
return hybrid_grid_.IsKnown(
|
||||||
hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));
|
hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));
|
||||||
|
@ -65,6 +85,7 @@ class RangeDataInserter3DTest : public ::testing::Test {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HybridGrid hybrid_grid_;
|
HybridGrid hybrid_grid_;
|
||||||
|
IntensityHybridGrid intensity_hybrid_grid_;
|
||||||
std::unique_ptr<RangeDataInserter3D> range_data_inserter_;
|
std::unique_ptr<RangeDataInserter3D> range_data_inserter_;
|
||||||
proto::RangeDataInserterOptions3D options_;
|
proto::RangeDataInserterOptions3D options_;
|
||||||
};
|
};
|
||||||
|
@ -89,6 +110,28 @@ TEST_F(RangeDataInserter3DTest, InsertPointCloud) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RangeDataInserter3DTest, InsertPointCloudWithIntensities) {
|
||||||
|
InsertPointCloudWithIntensities();
|
||||||
|
EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -4.f),
|
||||||
|
1e-4);
|
||||||
|
EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -3.f),
|
||||||
|
1e-4);
|
||||||
|
EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -2.f),
|
||||||
|
1e-4);
|
||||||
|
for (int x = -4; x <= 4; ++x) {
|
||||||
|
for (int y = -4; y <= 4; ++y) {
|
||||||
|
if (x < -3 || x > 0 || y != x + 2) {
|
||||||
|
EXPECT_FALSE(IsKnown(x, y, 4.f));
|
||||||
|
EXPECT_NEAR(0.f, GetIntensity(x, y, 4.f), 1e-6);
|
||||||
|
} else {
|
||||||
|
EXPECT_NEAR(options().hit_probability(), GetProbability(x, y, 4.f),
|
||||||
|
1e-4);
|
||||||
|
EXPECT_NEAR(10 + x, GetIntensity(x, y, 4.f), 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RangeDataInserter3DTest, ProbabilityProgression) {
|
TEST_F(RangeDataInserter3DTest, ProbabilityProgression) {
|
||||||
InsertPointCloud();
|
InsertPointCloud();
|
||||||
EXPECT_NEAR(options().hit_probability(), GetProbability(-2.f, 0.f, 4.f),
|
EXPECT_NEAR(options().hit_probability(), GetProbability(-2.f, 0.f, 4.f),
|
||||||
|
|
|
@ -202,6 +202,8 @@ Submap3D::Submap3D(const float high_resolution, const float low_resolution,
|
||||||
absl::make_unique<HybridGrid>(high_resolution)),
|
absl::make_unique<HybridGrid>(high_resolution)),
|
||||||
low_resolution_hybrid_grid_(
|
low_resolution_hybrid_grid_(
|
||||||
absl::make_unique<HybridGrid>(low_resolution)),
|
absl::make_unique<HybridGrid>(low_resolution)),
|
||||||
|
high_resolution_intensity_hybrid_grid_(
|
||||||
|
absl::make_unique<IntensityHybridGrid>(high_resolution)),
|
||||||
rotational_scan_matcher_histogram_(rotational_scan_matcher_histogram) {}
|
rotational_scan_matcher_histogram_(rotational_scan_matcher_histogram) {}
|
||||||
|
|
||||||
Submap3D::Submap3D(const proto::Submap3D& proto)
|
Submap3D::Submap3D(const proto::Submap3D& proto)
|
||||||
|
@ -278,9 +280,11 @@ void Submap3D::InsertData(const sensor::RangeData& range_data_in_local,
|
||||||
range_data_inserter.Insert(
|
range_data_inserter.Insert(
|
||||||
FilterRangeDataByMaxRange(transformed_range_data,
|
FilterRangeDataByMaxRange(transformed_range_data,
|
||||||
high_resolution_max_range),
|
high_resolution_max_range),
|
||||||
high_resolution_hybrid_grid_.get());
|
high_resolution_hybrid_grid_.get(),
|
||||||
|
high_resolution_intensity_hybrid_grid_.get());
|
||||||
range_data_inserter.Insert(transformed_range_data,
|
range_data_inserter.Insert(transformed_range_data,
|
||||||
low_resolution_hybrid_grid_.get());
|
low_resolution_hybrid_grid_.get(),
|
||||||
|
/*intensity_hybrid_grid=*/nullptr);
|
||||||
set_num_range_data(num_range_data() + 1);
|
set_num_range_data(num_range_data() + 1);
|
||||||
const float yaw_in_submap_from_gravity = transform::GetYaw(
|
const float yaw_in_submap_from_gravity = transform::GetYaw(
|
||||||
local_pose().inverse().rotation() * local_from_gravity_aligned);
|
local_pose().inverse().rotation() * local_from_gravity_aligned);
|
||||||
|
@ -332,6 +336,11 @@ void ActiveSubmaps3D::AddSubmap(
|
||||||
// This will crop the finished Submap before inserting a new Submap to
|
// This will crop the finished Submap before inserting a new Submap to
|
||||||
// reduce peak memory usage a bit.
|
// reduce peak memory usage a bit.
|
||||||
CHECK(submaps_.front()->insertion_finished());
|
CHECK(submaps_.front()->insertion_finished());
|
||||||
|
// We use `ForgetIntensityHybridGrid` to reduce memory usage. Since we use
|
||||||
|
// active submaps and their associated intensity hybrid grids for scan
|
||||||
|
// matching, we call `ForgetIntensityHybridGrid` once we remove the submap
|
||||||
|
// from active submaps and no longer need the intensity hybrid grid.
|
||||||
|
submaps_.front()->ForgetIntensityHybridGrid();
|
||||||
submaps_.erase(submaps_.begin());
|
submaps_.erase(submaps_.begin());
|
||||||
}
|
}
|
||||||
const Eigen::VectorXf initial_rotational_scan_matcher_histogram =
|
const Eigen::VectorXf initial_rotational_scan_matcher_histogram =
|
||||||
|
|
|
@ -60,6 +60,14 @@ class Submap3D : public Submap {
|
||||||
const HybridGrid& low_resolution_hybrid_grid() const {
|
const HybridGrid& low_resolution_hybrid_grid() const {
|
||||||
return *low_resolution_hybrid_grid_;
|
return *low_resolution_hybrid_grid_;
|
||||||
}
|
}
|
||||||
|
const IntensityHybridGrid& high_resolution_intensity_hybrid_grid() const {
|
||||||
|
CHECK(high_resolution_intensity_hybrid_grid_ != nullptr);
|
||||||
|
return *high_resolution_intensity_hybrid_grid_;
|
||||||
|
}
|
||||||
|
void ForgetIntensityHybridGrid() {
|
||||||
|
high_resolution_intensity_hybrid_grid_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
const Eigen::VectorXf& rotational_scan_matcher_histogram() const {
|
const Eigen::VectorXf& rotational_scan_matcher_histogram() const {
|
||||||
return rotational_scan_matcher_histogram_;
|
return rotational_scan_matcher_histogram_;
|
||||||
}
|
}
|
||||||
|
@ -79,6 +87,7 @@ class Submap3D : public Submap {
|
||||||
|
|
||||||
std::unique_ptr<HybridGrid> high_resolution_hybrid_grid_;
|
std::unique_ptr<HybridGrid> high_resolution_hybrid_grid_;
|
||||||
std::unique_ptr<HybridGrid> low_resolution_hybrid_grid_;
|
std::unique_ptr<HybridGrid> low_resolution_hybrid_grid_;
|
||||||
|
std::unique_ptr<IntensityHybridGrid> high_resolution_intensity_hybrid_grid_;
|
||||||
Eigen::VectorXf rotational_scan_matcher_histogram_;
|
Eigen::VectorXf rotational_scan_matcher_histogram_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ class LocalTrajectoryBuilderTest : public ::testing::Test {
|
||||||
hit_probability = 0.7,
|
hit_probability = 0.7,
|
||||||
miss_probability = 0.4,
|
miss_probability = 0.4,
|
||||||
num_free_space_voxels = 0,
|
num_free_space_voxels = 0,
|
||||||
|
intensity_threshold = 100.0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ class FastCorrelativeScanMatcher3DTest : public ::testing::Test {
|
||||||
"hit_probability = 0.7, "
|
"hit_probability = 0.7, "
|
||||||
"miss_probability = 0.4, "
|
"miss_probability = 0.4, "
|
||||||
"num_free_space_voxels = 5, "
|
"num_free_space_voxels = 5, "
|
||||||
|
"intensity_threshold = 100.0, "
|
||||||
"}");
|
"}");
|
||||||
return CreateRangeDataInserterOptions3D(parameter_dictionary.get());
|
return CreateRangeDataInserterOptions3D(parameter_dictionary.get());
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,8 @@ class FastCorrelativeScanMatcher3DTest : public ::testing::Test {
|
||||||
sensor::RangeData{pose.translation(),
|
sensor::RangeData{pose.translation(),
|
||||||
sensor::TransformPointCloud(point_cloud_, pose),
|
sensor::TransformPointCloud(point_cloud_, pose),
|
||||||
{}},
|
{}},
|
||||||
hybrid_grid_.get());
|
hybrid_grid_.get(),
|
||||||
|
/*intensity_hybrid_grid=*/nullptr);
|
||||||
hybrid_grid_->FinishUpdate();
|
hybrid_grid_->FinishUpdate();
|
||||||
|
|
||||||
return absl::make_unique<FastCorrelativeScanMatcher3D>(
|
return absl::make_unique<FastCorrelativeScanMatcher3D>(
|
||||||
|
|
|
@ -28,4 +28,7 @@ message RangeDataInserterOptions3D {
|
||||||
// Up to how many free space voxels are updated for scan matching.
|
// Up to how many free space voxels are updated for scan matching.
|
||||||
// 0 disables free space.
|
// 0 disables free space.
|
||||||
int32 num_free_space_voxels = 3;
|
int32 num_free_space_voxels = 3;
|
||||||
|
|
||||||
|
// Do not insert intensities above this threshold into IntensityHybridGrid.
|
||||||
|
float intensity_threshold = 4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
|
|
||||||
MAX_3D_RANGE = 60.
|
MAX_3D_RANGE = 60.
|
||||||
|
INTENSITY_THRESHOLD = 40
|
||||||
|
|
||||||
TRAJECTORY_BUILDER_3D = {
|
TRAJECTORY_BUILDER_3D = {
|
||||||
min_range = 1.,
|
min_range = 1.,
|
||||||
|
@ -96,6 +97,7 @@ TRAJECTORY_BUILDER_3D = {
|
||||||
hit_probability = 0.55,
|
hit_probability = 0.55,
|
||||||
miss_probability = 0.49,
|
miss_probability = 0.49,
|
||||||
num_free_space_voxels = 2,
|
num_free_space_voxels = 2,
|
||||||
|
intensity_threshold = INTENSITY_THRESHOLD,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue