Follow googlecartographer/cartographer#288. (#338)

master
Wolfgang Hess 2017-05-16 11:40:06 +02:00 committed by GitHub
parent d624b1c250
commit 8c56b44bdd
5 changed files with 128 additions and 7 deletions

View File

@ -39,6 +39,7 @@ void WriteTrajectory(const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes, trajectory_nodes,
const std::string& stem) { const std::string& stem) {
carto::mapping::proto::Trajectory trajectory; carto::mapping::proto::Trajectory trajectory;
// TODO(whess): Add multi-trajectory support.
for (const auto& node : trajectory_nodes) { for (const auto& node : trajectory_nodes) {
const auto& data = *node.constant_data; const auto& data = *node.constant_data;
auto* node_proto = trajectory.add_node(); auto* node_proto = trajectory.add_node();

View File

@ -54,8 +54,12 @@ void MapBuilderBridge::FinishTrajectory(const int trajectory_id) {
} }
void MapBuilderBridge::WriteAssets(const string& stem) { void MapBuilderBridge::WriteAssets(const string& stem) {
const auto trajectory_nodes = std::vector<::cartographer::mapping::TrajectoryNode> trajectory_nodes;
map_builder_.sparse_pose_graph()->GetTrajectoryNodes(); for (const auto& single_trajectory :
map_builder_.sparse_pose_graph()->GetTrajectoryNodes()) {
trajectory_nodes.insert(trajectory_nodes.end(), single_trajectory.begin(),
single_trajectory.end());
}
if (trajectory_nodes.empty()) { if (trajectory_nodes.empty()) {
LOG(WARNING) << "No data was collected and no assets will be written."; LOG(WARNING) << "No data was collected and no assets will be written.";
} else { } else {
@ -131,8 +135,12 @@ std::unique_ptr<nav_msgs::OccupancyGrid>
MapBuilderBridge::BuildOccupancyGrid() { MapBuilderBridge::BuildOccupancyGrid() {
CHECK(options_.map_builder_options.use_trajectory_builder_2d()) CHECK(options_.map_builder_options.use_trajectory_builder_2d())
<< "Publishing OccupancyGrids for 3D data is not yet supported"; << "Publishing OccupancyGrids for 3D data is not yet supported";
const auto trajectory_nodes = std::vector<::cartographer::mapping::TrajectoryNode> trajectory_nodes;
map_builder_.sparse_pose_graph()->GetTrajectoryNodes(); for (const auto& single_trajectory :
map_builder_.sparse_pose_graph()->GetTrajectoryNodes()) {
trajectory_nodes.insert(trajectory_nodes.end(), single_trajectory.begin(),
single_trajectory.end());
}
std::unique_ptr<nav_msgs::OccupancyGrid> occupancy_grid; std::unique_ptr<nav_msgs::OccupancyGrid> occupancy_grid;
if (!trajectory_nodes.empty()) { if (!trajectory_nodes.empty()) {
occupancy_grid = occupancy_grid =

View File

@ -16,12 +16,42 @@
#include "cartographer_ros/occupancy_grid.h" #include "cartographer_ros/occupancy_grid.h"
#include "cartographer/mapping_2d/map_limits.h" #include "cartographer/common/port.h"
#include "cartographer/mapping_2d/probability_grid.h" #include "cartographer/mapping_2d/probability_grid.h"
#include "cartographer/mapping_2d/range_data_inserter.h" #include "cartographer/mapping_2d/range_data_inserter.h"
#include "cartographer_ros/time_conversion.h" #include "cartographer_ros/time_conversion.h"
#include "glog/logging.h" #include "glog/logging.h"
namespace {
Eigen::AlignedBox2f ComputeMapBoundingBox(
const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes) {
Eigen::AlignedBox2f bounding_box(Eigen::Vector2f::Zero());
for (const auto& node : trajectory_nodes) {
const auto& data = *node.constant_data;
::cartographer::sensor::RangeData range_data;
if (!data.range_data_3d.returns.empty()) {
range_data = ::cartographer::sensor::TransformRangeData(
::cartographer::sensor::Decompress(data.range_data_3d),
node.pose.cast<float>());
} else {
range_data = ::cartographer::sensor::TransformRangeData(
data.range_data_2d, node.pose.cast<float>());
}
bounding_box.extend(range_data.origin.head<2>());
for (const Eigen::Vector3f& hit : range_data.returns) {
bounding_box.extend(hit.head<2>());
}
for (const Eigen::Vector3f& miss : range_data.misses) {
bounding_box.extend(miss.head<2>());
}
}
return bounding_box;
}
} // namespace
namespace cartographer_ros { namespace cartographer_ros {
void BuildOccupancyGrid2D( void BuildOccupancyGrid2D(
@ -30,10 +60,10 @@ void BuildOccupancyGrid2D(
const string& map_frame, const string& map_frame,
const ::cartographer::mapping_2d::proto::SubmapsOptions& submaps_options, const ::cartographer::mapping_2d::proto::SubmapsOptions& submaps_options,
::nav_msgs::OccupancyGrid* const occupancy_grid) { ::nav_msgs::OccupancyGrid* const occupancy_grid) {
CHECK(!trajectory_nodes.empty());
namespace carto = ::cartographer; namespace carto = ::cartographer;
const carto::mapping_2d::MapLimits map_limits = const carto::mapping_2d::MapLimits map_limits =
carto::mapping_2d::MapLimits::ComputeMapLimits( ComputeMapLimits(submaps_options.resolution(), trajectory_nodes);
submaps_options.resolution(), trajectory_nodes);
carto::mapping_2d::ProbabilityGrid probability_grid(map_limits); carto::mapping_2d::ProbabilityGrid probability_grid(map_limits);
carto::mapping_2d::RangeDataInserter range_data_inserter( carto::mapping_2d::RangeDataInserter range_data_inserter(
submaps_options.range_data_inserter_options()); submaps_options.range_data_inserter_options());
@ -45,6 +75,7 @@ void BuildOccupancyGrid2D(
&probability_grid); &probability_grid);
} }
// TODO(whess): Compute the latest time of in 'trajectory_nodes'.
occupancy_grid->header.stamp = ToRos(trajectory_nodes.back().time()); occupancy_grid->header.stamp = ToRos(trajectory_nodes.back().time());
occupancy_grid->header.frame_id = map_frame; occupancy_grid->header.frame_id = map_frame;
occupancy_grid->info.map_load_time = occupancy_grid->header.stamp; occupancy_grid->info.map_load_time = occupancy_grid->header.stamp;
@ -89,4 +120,23 @@ void BuildOccupancyGrid2D(
} }
} }
::cartographer::mapping_2d::MapLimits ComputeMapLimits(
const double resolution,
const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes) {
Eigen::AlignedBox2f bounding_box = ComputeMapBoundingBox(trajectory_nodes);
// Add some padding to ensure all rays are still contained in the map after
// discretization.
const float kPadding = 3.f * resolution;
bounding_box.min() -= kPadding * Eigen::Vector2f::Ones();
bounding_box.max() += kPadding * Eigen::Vector2f::Ones();
const Eigen::Vector2d pixel_sizes =
bounding_box.sizes().cast<double>() / resolution;
return ::cartographer::mapping_2d::MapLimits(
resolution, bounding_box.max().cast<double>(),
::cartographer::mapping_2d::CellLimits(
::cartographer::common::RoundToInt(pixel_sizes.y()),
::cartographer::common::RoundToInt(pixel_sizes.x())));
}
} // namespace cartographer_ros } // namespace cartographer_ros

View File

@ -19,7 +19,10 @@
#include <vector> #include <vector>
#include "Eigen/Core"
#include "Eigen/Geometry"
#include "cartographer/mapping/trajectory_node.h" #include "cartographer/mapping/trajectory_node.h"
#include "cartographer/mapping_2d/map_limits.h"
#include "cartographer/mapping_2d/proto/submaps_options.pb.h" #include "cartographer/mapping_2d/proto/submaps_options.pb.h"
#include "nav_msgs/OccupancyGrid.h" #include "nav_msgs/OccupancyGrid.h"
@ -32,6 +35,13 @@ void BuildOccupancyGrid2D(
const ::cartographer::mapping_2d::proto::SubmapsOptions& submaps_options, const ::cartographer::mapping_2d::proto::SubmapsOptions& submaps_options,
::nav_msgs::OccupancyGrid* const occupancy_grid); ::nav_msgs::OccupancyGrid* const occupancy_grid);
// Computes MapLimits that contain the origin, and all rays (both returns and
// misses) in the 'trajectory_nodes'.
::cartographer::mapping_2d::MapLimits ComputeMapLimits(
double resolution,
const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes);
} // namespace cartographer_ros } // namespace cartographer_ros
#endif // CARTOGRAPHER_ROS_OCCUPANCY_GRID_H_ #endif // CARTOGRAPHER_ROS_OCCUPANCY_GRID_H_

View File

@ -0,0 +1,52 @@
/*
* 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_ros/occupancy_grid.h"
#include "cartographer/common/time.h"
#include "cartographer/sensor/range_data.h"
#include "gtest/gtest.h"
#include "ros/ros.h"
namespace cartographer_ros {
namespace {
TEST(OccupancyGridTest, ComputeMapLimits) {
const ::cartographer::mapping::TrajectoryNode::ConstantData constant_data{
::cartographer::common::FromUniversal(52),
::cartographer::sensor::RangeData{
Eigen::Vector3f::Zero(),
{Eigen::Vector3f(-30.f, 1.f, 0.f), Eigen::Vector3f(50.f, -10.f, 0.f)},
{}},
::cartographer::sensor::Compress(
::cartographer::sensor::RangeData{Eigen::Vector3f::Zero(), {}, {}}),
nullptr, ::cartographer::transform::Rigid3d::Identity()};
const ::cartographer::mapping::TrajectoryNode trajectory_node{
&constant_data, ::cartographer::transform::Rigid3d::Identity()};
constexpr double kResolution = 0.05;
const ::cartographer::mapping_2d::MapLimits limits =
ComputeMapLimits(kResolution, {trajectory_node});
constexpr float kPaddingAwareTolerance = 5 * kResolution;
EXPECT_NEAR(50.f, limits.max().x(), kPaddingAwareTolerance);
EXPECT_NEAR(1.f, limits.max().y(), kPaddingAwareTolerance);
EXPECT_LT(200, limits.cell_limits().num_x_cells);
EXPECT_LT(1600, limits.cell_limits().num_y_cells);
EXPECT_GT(400, limits.cell_limits().num_x_cells);
EXPECT_GT(2000, limits.cell_limits().num_y_cells);
}
} // namespace
} // namespace cartographer_ros