Follow googlecartographer/cartographer#288. (#338)
parent
d624b1c250
commit
8c56b44bdd
|
@ -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();
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue