Move NodeOptions and OccupancyGrid building. (#72)

Constructing NodeOptions and nav_msgs::OccupancyGrids
is moved out of the ROS node implementation file.
master
Wolfgang Hess 2016-09-29 15:45:24 +02:00 committed by GitHub
parent 337607692a
commit 0fda88b8d6
7 changed files with 282 additions and 150 deletions

View File

@ -83,6 +83,10 @@ add_executable(cartographer_node
src/cartographer_node_main.cc
src/msg_conversion.cc
src/msg_conversion.h
src/node_options.cc
src/node_options.h
src/occupancy_grid.cc
src/occupancy_grid.h
src/sensor_data.cc
src/sensor_data.h
src/sensor_data_producer.cc

View File

@ -23,7 +23,6 @@
#include "Eigen/Core"
#include "cartographer/common/configuration_file_resolver.h"
#include "cartographer/common/lua_parameter_dictionary.h"
#include "cartographer/common/lua_parameter_dictionary_test_helpers.h"
#include "cartographer/common/make_unique.h"
#include "cartographer/common/mutex.h"
#include "cartographer/common/port.h"
@ -64,6 +63,8 @@
#include "tf2_ros/transform_listener.h"
#include "msg_conversion.h"
#include "node_options.h"
#include "occupancy_grid.h"
#include "sensor_data.h"
#include "sensor_data_producer.h"
#include "time_conversion.h"
@ -102,93 +103,6 @@ constexpr char kScanMatchedPointCloudTopic[] = "scan_matched_points2";
constexpr char kSubmapListTopic[] = "submap_list";
constexpr char kSubmapQueryServiceName[] = "submap_query";
struct NodeOptions {
carto::mapping::proto::MapBuilderOptions map_builder_options;
string map_frame;
string tracking_frame;
string published_frame;
string odom_frame;
bool publish_occupancy_grid;
bool provide_odom_frame;
bool use_odometry_data;
bool use_constant_odometry_variance;
double constant_odometry_translational_variance;
double constant_odometry_rotational_variance;
bool use_horizontal_laser;
bool use_horizontal_multi_echo_laser;
double horizontal_laser_min_range;
double horizontal_laser_max_range;
double horizontal_laser_missing_echo_ray_length;
int num_lasers_3d;
double lookup_transform_timeout_sec;
double submap_publish_period_sec;
double pose_publish_period_sec;
};
NodeOptions CreateNodeOptions(
carto::common::LuaParameterDictionary* const lua_parameter_dictionary) {
NodeOptions options;
options.map_builder_options = carto::mapping::CreateMapBuilderOptions(
lua_parameter_dictionary->GetDictionary("map_builder").get());
options.map_frame = lua_parameter_dictionary->GetString("map_frame");
options.tracking_frame =
lua_parameter_dictionary->GetString("tracking_frame");
options.published_frame =
lua_parameter_dictionary->GetString("published_frame");
options.odom_frame = lua_parameter_dictionary->GetString("odom_frame");
options.publish_occupancy_grid =
lua_parameter_dictionary->GetBool("publish_occupancy_grid");
options.provide_odom_frame =
lua_parameter_dictionary->GetBool("provide_odom_frame");
options.use_odometry_data =
lua_parameter_dictionary->GetBool("use_odometry_data");
options.use_constant_odometry_variance =
lua_parameter_dictionary->GetBool("use_constant_odometry_variance");
options.constant_odometry_translational_variance =
lua_parameter_dictionary->GetDouble(
"constant_odometry_translational_variance");
options.constant_odometry_rotational_variance =
lua_parameter_dictionary->GetDouble(
"constant_odometry_rotational_variance");
options.use_horizontal_laser =
lua_parameter_dictionary->GetBool("use_horizontal_laser");
options.use_horizontal_multi_echo_laser =
lua_parameter_dictionary->GetBool("use_horizontal_multi_echo_laser");
options.horizontal_laser_min_range =
lua_parameter_dictionary->GetDouble("horizontal_laser_min_range");
options.horizontal_laser_max_range =
lua_parameter_dictionary->GetDouble("horizontal_laser_max_range");
options.horizontal_laser_missing_echo_ray_length =
lua_parameter_dictionary->GetDouble(
"horizontal_laser_missing_echo_ray_length");
options.num_lasers_3d =
lua_parameter_dictionary->GetNonNegativeInt("num_lasers_3d");
options.lookup_transform_timeout_sec =
lua_parameter_dictionary->GetDouble("lookup_transform_timeout_sec");
options.submap_publish_period_sec =
lua_parameter_dictionary->GetDouble("submap_publish_period_sec");
options.pose_publish_period_sec =
lua_parameter_dictionary->GetDouble("pose_publish_period_sec");
CHECK_EQ(options.use_horizontal_laser +
options.use_horizontal_multi_echo_laser +
(options.num_lasers_3d > 0),
1)
<< "Configuration error: 'use_horizontal_laser', "
"'use_horizontal_multi_echo_laser' and 'num_lasers_3d' are "
"mutually exclusive, but one is required.";
CHECK_EQ(
options.map_builder_options.use_trajectory_builder_2d(),
options.use_horizontal_laser || options.use_horizontal_multi_echo_laser);
CHECK_EQ(options.map_builder_options.use_trajectory_builder_3d(),
options.num_lasers_3d > 0);
if (options.publish_occupancy_grid) {
CHECK(options.map_builder_options.use_trajectory_builder_2d())
<< "Publishing OccupancyGrids for 3D data is not yet supported";
}
return options;
}
// Node that listens to all the sensor data that we are interested in and wires
// it up to the SLAM.
class Node {
@ -633,68 +547,8 @@ void Node::SpinOccupancyGridThreadForever() {
std::this_thread::sleep_for(carto::common::FromMilliseconds(1000));
continue;
}
const auto& submaps_options =
options_.map_builder_options.trajectory_builder_2d_options()
.submaps_options();
const carto::mapping_2d::MapLimits map_limits =
carto::mapping_2d::MapLimits::ComputeMapLimits(
submaps_options.resolution(), trajectory_nodes);
carto::mapping_2d::ProbabilityGrid probability_grid(map_limits);
carto::mapping_2d::LaserFanInserter laser_fan_inserter(
submaps_options.laser_fan_inserter_options());
for (const auto& node : trajectory_nodes) {
CHECK(node.constant_data->laser_fan_3d.returns.empty()); // No 3D yet.
laser_fan_inserter.Insert(
carto::sensor::TransformLaserFan(
node.constant_data->laser_fan,
carto::transform::Project2D(node.pose).cast<float>()),
&probability_grid);
}
::nav_msgs::OccupancyGrid occupancy_grid;
occupancy_grid.header.stamp = ToRos(trajectory_nodes.back().time());
occupancy_grid.header.frame_id = options_.map_frame;
occupancy_grid.info.map_load_time = occupancy_grid.header.stamp;
Eigen::Array2i offset;
carto::mapping_2d::CellLimits cell_limits;
probability_grid.ComputeCroppedLimits(&offset, &cell_limits);
const double resolution = probability_grid.limits().resolution();
occupancy_grid.info.resolution = resolution;
occupancy_grid.info.width = cell_limits.num_y_cells;
occupancy_grid.info.height = cell_limits.num_x_cells;
occupancy_grid.info.origin.position.x =
probability_grid.limits().max().x() -
(offset.y() + cell_limits.num_y_cells) * resolution;
occupancy_grid.info.origin.position.y =
probability_grid.limits().max().y() -
(offset.x() + cell_limits.num_x_cells) * resolution;
occupancy_grid.info.origin.position.z = 0.;
occupancy_grid.info.origin.orientation.w = 1.;
occupancy_grid.info.origin.orientation.x = 0.;
occupancy_grid.info.origin.orientation.y = 0.;
occupancy_grid.info.origin.orientation.z = 0.;
occupancy_grid.data.resize(
cell_limits.num_x_cells * cell_limits.num_y_cells, -1);
for (const Eigen::Array2i& xy_index :
carto::mapping_2d::XYIndexRangeIterator(cell_limits)) {
if (probability_grid.IsKnown(xy_index + offset)) {
const int value = carto::common::RoundToInt(
(probability_grid.GetProbability(xy_index + offset) -
carto::mapping::kMinProbability) *
100. / (carto::mapping::kMaxProbability -
carto::mapping::kMinProbability));
CHECK_LE(0, value);
CHECK_GE(100, value);
occupancy_grid.data[(cell_limits.num_x_cells - xy_index.x()) *
cell_limits.num_y_cells -
xy_index.y() - 1] = value;
}
}
BuildOccupancyGrid(trajectory_nodes, options_, &occupancy_grid);
occupancy_grid_publisher_.publish(occupancy_grid);
}
}

View File

@ -0,0 +1,89 @@
/*
* 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 "node_options.h"
#include "glog/logging.h"
namespace cartographer_ros {
NodeOptions CreateNodeOptions(
::cartographer::common::LuaParameterDictionary* const
lua_parameter_dictionary) {
NodeOptions options;
options.map_builder_options =
::cartographer::mapping::CreateMapBuilderOptions(
lua_parameter_dictionary->GetDictionary("map_builder").get());
options.map_frame = lua_parameter_dictionary->GetString("map_frame");
options.tracking_frame =
lua_parameter_dictionary->GetString("tracking_frame");
options.published_frame =
lua_parameter_dictionary->GetString("published_frame");
options.odom_frame = lua_parameter_dictionary->GetString("odom_frame");
options.publish_occupancy_grid =
lua_parameter_dictionary->GetBool("publish_occupancy_grid");
options.provide_odom_frame =
lua_parameter_dictionary->GetBool("provide_odom_frame");
options.use_odometry_data =
lua_parameter_dictionary->GetBool("use_odometry_data");
options.use_constant_odometry_variance =
lua_parameter_dictionary->GetBool("use_constant_odometry_variance");
options.constant_odometry_translational_variance =
lua_parameter_dictionary->GetDouble(
"constant_odometry_translational_variance");
options.constant_odometry_rotational_variance =
lua_parameter_dictionary->GetDouble(
"constant_odometry_rotational_variance");
options.use_horizontal_laser =
lua_parameter_dictionary->GetBool("use_horizontal_laser");
options.use_horizontal_multi_echo_laser =
lua_parameter_dictionary->GetBool("use_horizontal_multi_echo_laser");
options.horizontal_laser_min_range =
lua_parameter_dictionary->GetDouble("horizontal_laser_min_range");
options.horizontal_laser_max_range =
lua_parameter_dictionary->GetDouble("horizontal_laser_max_range");
options.horizontal_laser_missing_echo_ray_length =
lua_parameter_dictionary->GetDouble(
"horizontal_laser_missing_echo_ray_length");
options.num_lasers_3d =
lua_parameter_dictionary->GetNonNegativeInt("num_lasers_3d");
options.lookup_transform_timeout_sec =
lua_parameter_dictionary->GetDouble("lookup_transform_timeout_sec");
options.submap_publish_period_sec =
lua_parameter_dictionary->GetDouble("submap_publish_period_sec");
options.pose_publish_period_sec =
lua_parameter_dictionary->GetDouble("pose_publish_period_sec");
CHECK_EQ(options.use_horizontal_laser +
options.use_horizontal_multi_echo_laser +
(options.num_lasers_3d > 0),
1)
<< "Configuration error: 'use_horizontal_laser', "
"'use_horizontal_multi_echo_laser' and 'num_lasers_3d' are "
"mutually exclusive, but one is required.";
CHECK_EQ(
options.map_builder_options.use_trajectory_builder_2d(),
options.use_horizontal_laser || options.use_horizontal_multi_echo_laser);
CHECK_EQ(options.map_builder_options.use_trajectory_builder_3d(),
options.num_lasers_3d > 0);
if (options.publish_occupancy_grid) {
CHECK(options.map_builder_options.use_trajectory_builder_2d())
<< "Publishing OccupancyGrids for 3D data is not yet supported";
}
return options;
}
} // namespace cartographer_ros

View File

@ -0,0 +1,57 @@
/*
* 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.
*/
#ifndef CARTOGRAHPER_ROS_NODE_OPTIONS_H_
#define CARTOGRAHPER_ROS_NODE_OPTIONS_H_
#include <string>
#include "cartographer/common/lua_parameter_dictionary.h"
#include "cartographer/common/port.h"
#include "cartographer/mapping/map_builder.h"
namespace cartographer_ros {
// Top-level options of Cartographer's ROS integration.
struct NodeOptions {
::cartographer::mapping::proto::MapBuilderOptions map_builder_options;
string map_frame;
string tracking_frame;
string published_frame;
string odom_frame;
bool publish_occupancy_grid;
bool provide_odom_frame;
bool use_odometry_data;
bool use_constant_odometry_variance;
double constant_odometry_translational_variance;
double constant_odometry_rotational_variance;
bool use_horizontal_laser;
bool use_horizontal_multi_echo_laser;
double horizontal_laser_min_range;
double horizontal_laser_max_range;
double horizontal_laser_missing_echo_ray_length;
int num_lasers_3d;
double lookup_transform_timeout_sec;
double submap_publish_period_sec;
double pose_publish_period_sec;
};
NodeOptions CreateNodeOptions(
::cartographer::common::LuaParameterDictionary* lua_parameter_dictionary);
} // namespace cartographer_ros
#endif // CARTOGRAHPER_ROS_NODE_OPTIONS_H_

View File

@ -0,0 +1,93 @@
/*
* 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 "occupancy_grid.h"
#include "time_conversion.h"
#include "glog/logging.h"
namespace cartographer_ros {
void BuildOccupancyGrid(
const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes,
const NodeOptions& options,
::nav_msgs::OccupancyGrid* const occupancy_grid) {
namespace carto = ::cartographer;
const auto& submaps_options =
options.map_builder_options.trajectory_builder_2d_options()
.submaps_options();
const carto::mapping_2d::MapLimits map_limits =
carto::mapping_2d::MapLimits::ComputeMapLimits(
submaps_options.resolution(), trajectory_nodes);
carto::mapping_2d::ProbabilityGrid probability_grid(map_limits);
carto::mapping_2d::LaserFanInserter laser_fan_inserter(
submaps_options.laser_fan_inserter_options());
for (const auto& node : trajectory_nodes) {
CHECK(node.constant_data->laser_fan_3d.returns.empty()); // No 3D yet.
laser_fan_inserter.Insert(
carto::sensor::TransformLaserFan(
node.constant_data->laser_fan,
carto::transform::Project2D(node.pose).cast<float>()),
&probability_grid);
}
occupancy_grid->header.stamp = ToRos(trajectory_nodes.back().time());
occupancy_grid->header.frame_id = options.map_frame;
occupancy_grid->info.map_load_time = occupancy_grid->header.stamp;
Eigen::Array2i offset;
carto::mapping_2d::CellLimits cell_limits;
probability_grid.ComputeCroppedLimits(&offset, &cell_limits);
const double resolution = probability_grid.limits().resolution();
occupancy_grid->info.resolution = resolution;
occupancy_grid->info.width = cell_limits.num_y_cells;
occupancy_grid->info.height = cell_limits.num_x_cells;
occupancy_grid->info.origin.position.x =
probability_grid.limits().max().x() -
(offset.y() + cell_limits.num_y_cells) * resolution;
occupancy_grid->info.origin.position.y =
probability_grid.limits().max().y() -
(offset.x() + cell_limits.num_x_cells) * resolution;
occupancy_grid->info.origin.position.z = 0.;
occupancy_grid->info.origin.orientation.w = 1.;
occupancy_grid->info.origin.orientation.x = 0.;
occupancy_grid->info.origin.orientation.y = 0.;
occupancy_grid->info.origin.orientation.z = 0.;
occupancy_grid->data.resize(cell_limits.num_x_cells * cell_limits.num_y_cells,
-1);
for (const Eigen::Array2i& xy_index :
carto::mapping_2d::XYIndexRangeIterator(cell_limits)) {
if (probability_grid.IsKnown(xy_index + offset)) {
const int value = carto::common::RoundToInt(
(probability_grid.GetProbability(xy_index + offset) -
carto::mapping::kMinProbability) *
100. /
(carto::mapping::kMaxProbability - carto::mapping::kMinProbability));
CHECK_LE(0, value);
CHECK_GE(100, value);
occupancy_grid->data[(cell_limits.num_x_cells - xy_index.x()) *
cell_limits.num_y_cells -
xy_index.y() - 1] = value;
}
}
}
} // namespace cartographer_ros

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
#ifndef CARTOGRAHPER_ROS_OCCUPANCY_GRID_H_
#define CARTOGRAHPER_ROS_OCCUPANCY_GRID_H_
#include <vector>
#include "cartographer/mapping/trajectory_node.h"
#include "nav_msgs/OccupancyGrid.h"
#include "node_options.h"
namespace cartographer_ros {
void BuildOccupancyGrid(
const std::vector<::cartographer::mapping::TrajectoryNode>&
trajectory_nodes,
const NodeOptions& options, ::nav_msgs::OccupancyGrid* occupancy_grid);
} // namespace cartographer_ros
#endif // CARTOGRAHPER_ROS_OCCUPANCY_GRID_H_

View File

@ -56,4 +56,4 @@ SensorData::SensorData(const string& frame_id, proto::LaserFan3D laser_fan_3d)
SensorData::SensorData(const string& frame_id, const Odometry& odometry)
: type(SensorType::kOdometry), frame_id(frame_id), odometry(odometry) {}
} // namespace cartorapher_ros
} // namespace cartographer_ros