2016-08-02 15:07:31 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2016 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_2d/submaps.h"
|
|
|
|
|
|
|
|
#include <cinttypes>
|
|
|
|
#include <cmath>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <fstream>
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
#include "Eigen/Geometry"
|
|
|
|
#include "cartographer/common/make_unique.h"
|
2016-08-25 21:40:05 +08:00
|
|
|
#include "cartographer/common/port.h"
|
2016-08-02 15:07:31 +08:00
|
|
|
#include "glog/logging.h"
|
|
|
|
#include "webp/encode.h"
|
|
|
|
|
|
|
|
namespace cartographer {
|
|
|
|
namespace mapping_2d {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void WriteDebugImage(const string& filename,
|
|
|
|
const ProbabilityGrid& probability_grid) {
|
|
|
|
constexpr int kUnknown = 128;
|
2017-06-07 21:13:56 +08:00
|
|
|
const CellLimits& cell_limits = probability_grid.limits().cell_limits();
|
2016-08-02 15:07:31 +08:00
|
|
|
const int width = cell_limits.num_x_cells;
|
|
|
|
const int height = cell_limits.num_y_cells;
|
|
|
|
std::vector<uint8_t> rgb;
|
2017-06-07 21:13:56 +08:00
|
|
|
for (const Eigen::Array2i& xy_index :
|
|
|
|
XYIndexRangeIterator(probability_grid.limits().cell_limits())) {
|
2016-08-02 15:07:31 +08:00
|
|
|
CHECK(probability_grid.limits().Contains(xy_index));
|
|
|
|
const uint8_t value =
|
|
|
|
probability_grid.IsKnown(xy_index)
|
|
|
|
? common::RoundToInt(
|
|
|
|
(1. - probability_grid.GetProbability(xy_index)) * 255 + 0)
|
|
|
|
: kUnknown;
|
|
|
|
rgb.push_back(value);
|
|
|
|
rgb.push_back(value);
|
|
|
|
rgb.push_back(value);
|
|
|
|
}
|
|
|
|
uint8_t* output = nullptr;
|
|
|
|
size_t output_size =
|
|
|
|
WebPEncodeLosslessRGB(rgb.data(), width, height, 3 * width, &output);
|
|
|
|
std::unique_ptr<uint8_t, void (*)(void*)> output_deleter(output, std::free);
|
|
|
|
std::ofstream output_file(filename, std::ios::out | std::ios::binary);
|
|
|
|
output_file.write(reinterpret_cast<char*>(output), output_size);
|
|
|
|
output_file.close();
|
|
|
|
CHECK(output_file) << "Writing " << filename << " failed.";
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
ProbabilityGrid ComputeCroppedProbabilityGrid(
|
|
|
|
const ProbabilityGrid& probability_grid) {
|
|
|
|
Eigen::Array2i offset;
|
|
|
|
CellLimits limits;
|
|
|
|
probability_grid.ComputeCroppedLimits(&offset, &limits);
|
|
|
|
const double resolution = probability_grid.limits().resolution();
|
2016-08-25 21:40:05 +08:00
|
|
|
const Eigen::Vector2d max =
|
|
|
|
probability_grid.limits().max() -
|
|
|
|
resolution * Eigen::Vector2d(offset.y(), offset.x());
|
|
|
|
ProbabilityGrid cropped_grid(MapLimits(resolution, max, limits));
|
2016-08-02 15:07:31 +08:00
|
|
|
cropped_grid.StartUpdate();
|
|
|
|
for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(limits)) {
|
|
|
|
if (probability_grid.IsKnown(xy_index + offset)) {
|
|
|
|
cropped_grid.SetProbability(
|
|
|
|
xy_index, probability_grid.GetProbability(xy_index + offset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cropped_grid;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto::SubmapsOptions CreateSubmapsOptions(
|
|
|
|
common::LuaParameterDictionary* const parameter_dictionary) {
|
|
|
|
proto::SubmapsOptions options;
|
|
|
|
options.set_resolution(parameter_dictionary->GetDouble("resolution"));
|
|
|
|
options.set_half_length(parameter_dictionary->GetDouble("half_length"));
|
2017-03-23 21:56:18 +08:00
|
|
|
options.set_num_range_data(
|
|
|
|
parameter_dictionary->GetNonNegativeInt("num_range_data"));
|
2016-08-02 15:07:31 +08:00
|
|
|
options.set_output_debug_images(
|
|
|
|
parameter_dictionary->GetBool("output_debug_images"));
|
2017-03-23 21:56:18 +08:00
|
|
|
*options.mutable_range_data_inserter_options() =
|
|
|
|
CreateRangeDataInserterOptions(
|
|
|
|
parameter_dictionary->GetDictionary("range_data_inserter").get());
|
|
|
|
CHECK_GT(options.num_range_data(), 0);
|
2016-08-02 15:07:31 +08:00
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
2017-04-21 20:12:08 +08:00
|
|
|
Submap::Submap(const MapLimits& limits, const Eigen::Vector2f& origin)
|
2017-05-18 22:55:11 +08:00
|
|
|
: mapping::Submap(transform::Rigid3d::Translation(
|
|
|
|
Eigen::Vector3d(origin.x(), origin.y(), 0.))),
|
2016-08-02 15:07:31 +08:00
|
|
|
probability_grid(limits) {}
|
|
|
|
|
2017-06-07 21:13:56 +08:00
|
|
|
void Submap::AddProbabilityGridToResponse(
|
|
|
|
mapping::proto::SubmapQuery::Response* response) {
|
|
|
|
Eigen::Array2i offset;
|
|
|
|
CellLimits limits;
|
|
|
|
probability_grid.ComputeCroppedLimits(&offset, &limits);
|
|
|
|
|
|
|
|
string cells;
|
|
|
|
for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(limits)) {
|
|
|
|
if (probability_grid.IsKnown(xy_index + offset)) {
|
|
|
|
// We would like to add 'delta' but this is not possible using a value and
|
|
|
|
// alpha. We use premultiplied alpha, so when 'delta' is positive we can
|
|
|
|
// add it by setting 'alpha' to zero. If it is negative, we set 'value' to
|
|
|
|
// zero, and use 'alpha' to subtract. This is only correct when the pixel
|
|
|
|
// is currently white, so walls will look too gray. This should be hard to
|
|
|
|
// detect visually for the user, though.
|
|
|
|
const int delta =
|
|
|
|
128 - mapping::ProbabilityToLogOddsInteger(
|
|
|
|
probability_grid.GetProbability(xy_index + offset));
|
|
|
|
const uint8 alpha = delta > 0 ? 0 : -delta;
|
|
|
|
const uint8 value = delta > 0 ? delta : 0;
|
|
|
|
cells.push_back(value);
|
|
|
|
cells.push_back((value || alpha) ? alpha : 1);
|
|
|
|
} else {
|
|
|
|
constexpr uint8 kUnknownLogOdds = 0;
|
|
|
|
cells.push_back(static_cast<uint8>(kUnknownLogOdds)); // value
|
|
|
|
cells.push_back(0); // alpha
|
|
|
|
}
|
|
|
|
}
|
|
|
|
common::FastGzipString(cells, response->mutable_cells());
|
|
|
|
|
|
|
|
response->set_width(limits.num_x_cells);
|
|
|
|
response->set_height(limits.num_y_cells);
|
|
|
|
const double resolution = probability_grid.limits().resolution();
|
|
|
|
response->set_resolution(resolution);
|
|
|
|
const double max_x =
|
|
|
|
probability_grid.limits().max().x() - resolution * offset.y();
|
|
|
|
const double max_y =
|
|
|
|
probability_grid.limits().max().y() - resolution * offset.x();
|
|
|
|
*response->mutable_slice_pose() = transform::ToProto(
|
|
|
|
local_pose.inverse() *
|
|
|
|
transform::Rigid3d::Translation(Eigen::Vector3d(max_x, max_y, 0.)));
|
|
|
|
}
|
|
|
|
|
2016-08-02 15:07:31 +08:00
|
|
|
Submaps::Submaps(const proto::SubmapsOptions& options)
|
|
|
|
: options_(options),
|
2017-03-23 21:56:18 +08:00
|
|
|
range_data_inserter_(options.range_data_inserter_options()) {
|
2016-08-02 15:07:31 +08:00
|
|
|
// We always want to have at least one likelihood field which we can return,
|
|
|
|
// and will create it at the origin in absence of a better choice.
|
|
|
|
AddSubmap(Eigen::Vector2f::Zero());
|
|
|
|
}
|
|
|
|
|
2017-03-23 21:56:18 +08:00
|
|
|
void Submaps::InsertRangeData(const sensor::RangeData& range_data) {
|
2016-08-02 15:07:31 +08:00
|
|
|
for (const int index : insertion_indices()) {
|
|
|
|
Submap* submap = submaps_[index].get();
|
|
|
|
CHECK(submap->finished_probability_grid == nullptr);
|
2017-03-23 21:56:18 +08:00
|
|
|
range_data_inserter_.Insert(range_data, &submap->probability_grid);
|
2017-04-21 20:12:08 +08:00
|
|
|
++submap->num_range_data;
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
2017-04-21 20:12:08 +08:00
|
|
|
const Submap* const last_submap = Get(size() - 1);
|
|
|
|
if (last_submap->num_range_data == options_.num_range_data()) {
|
2017-03-23 21:56:18 +08:00
|
|
|
AddSubmap(range_data.origin.head<2>());
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Submap* Submaps::Get(int index) const {
|
|
|
|
CHECK_GE(index, 0);
|
|
|
|
CHECK_LT(index, size());
|
|
|
|
return submaps_[index].get();
|
|
|
|
}
|
|
|
|
|
|
|
|
int Submaps::size() const { return submaps_.size(); }
|
|
|
|
|
|
|
|
void Submaps::SubmapToProto(
|
2017-05-15 22:57:02 +08:00
|
|
|
const int index, const transform::Rigid3d&,
|
2017-06-07 21:13:56 +08:00
|
|
|
mapping::proto::SubmapQuery::Response* const response) {
|
|
|
|
submaps_.at(index)->AddProbabilityGridToResponse(response);
|
2016-08-02 15:07:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Submaps::FinishSubmap(int index) {
|
|
|
|
// Crop the finished Submap before inserting a new Submap to reduce peak
|
|
|
|
// memory usage a bit.
|
|
|
|
Submap* submap = submaps_[index].get();
|
|
|
|
CHECK(submap->finished_probability_grid == nullptr);
|
|
|
|
submap->probability_grid =
|
|
|
|
ComputeCroppedProbabilityGrid(submap->probability_grid);
|
|
|
|
submap->finished_probability_grid = &submap->probability_grid;
|
|
|
|
if (options_.output_debug_images()) {
|
|
|
|
// Output the Submap that won't be changed from now on.
|
|
|
|
WriteDebugImage("submap" + std::to_string(index) + ".webp",
|
|
|
|
submap->probability_grid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Submaps::AddSubmap(const Eigen::Vector2f& origin) {
|
|
|
|
if (size() > 1) {
|
|
|
|
FinishSubmap(size() - 2);
|
|
|
|
}
|
2016-08-25 21:40:05 +08:00
|
|
|
const int num_cells_per_dimension =
|
|
|
|
common::RoundToInt(2. * options_.half_length() / options_.resolution()) +
|
|
|
|
1;
|
2016-08-02 15:07:31 +08:00
|
|
|
submaps_.push_back(common::make_unique<Submap>(
|
|
|
|
MapLimits(options_.resolution(),
|
2016-08-25 21:40:05 +08:00
|
|
|
origin.cast<double>() +
|
|
|
|
options_.half_length() * Eigen::Vector2d::Ones(),
|
|
|
|
CellLimits(num_cells_per_dimension, num_cells_per_dimension)),
|
2017-04-21 20:12:08 +08:00
|
|
|
origin));
|
2016-08-02 15:07:31 +08:00
|
|
|
LOG(INFO) << "Added submap " << size();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mapping_2d
|
|
|
|
} // namespace cartographer
|