tsdf_range_data_inserter_2d.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2018 The Cartographer Authors
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "cartographer/mapping/2d/tsdf_range_data_inserter_2d.h"
00018 
00019 #include "cartographer/mapping/internal/2d/normal_estimation_2d.h"
00020 #include "cartographer/mapping/internal/2d/ray_to_pixel_mask.h"
00021 
00022 namespace cartographer {
00023 namespace mapping {
00024 namespace {
00025 
00026 // Factor for subpixel accuracy of start and end point for ray casts.
00027 constexpr int kSubpixelScale = 1000;
00028 // Minimum distance between range observation and origin. Otherwise, range
00029 // observations are discarded.
00030 constexpr float kMinRangeMeters = 1e-6f;
00031 const float kSqrtTwoPi = std::sqrt(2.0 * M_PI);
00032 
00033 void GrowAsNeeded(const sensor::RangeData& range_data,
00034                   const float truncation_distance, TSDF2D* const tsdf) {
00035   Eigen::AlignedBox2f bounding_box(range_data.origin.head<2>());
00036   for (const sensor::RangefinderPoint& hit : range_data.returns) {
00037     const Eigen::Vector3f direction =
00038         (hit.position - range_data.origin).normalized();
00039     const Eigen::Vector3f end_position =
00040         hit.position + truncation_distance * direction;
00041     bounding_box.extend(end_position.head<2>());
00042   }
00043   // Padding around bounding box to avoid numerical issues at cell boundaries.
00044   constexpr float kPadding = 1e-6f;
00045   tsdf->GrowLimits(bounding_box.min() - kPadding * Eigen::Vector2f::Ones());
00046   tsdf->GrowLimits(bounding_box.max() + kPadding * Eigen::Vector2f::Ones());
00047 }
00048 
00049 float GaussianKernel(const float x, const float sigma) {
00050   return 1.0 / (kSqrtTwoPi * sigma) * std::exp(-0.5 * x * x / (sigma * sigma));
00051 }
00052 
00053 std::pair<Eigen::Array2i, Eigen::Array2i> SuperscaleRay(
00054     const Eigen::Vector2f& begin, const Eigen::Vector2f& end,
00055     TSDF2D* const tsdf) {
00056   const MapLimits& limits = tsdf->limits();
00057   const double superscaled_resolution = limits.resolution() / kSubpixelScale;
00058   const MapLimits superscaled_limits(
00059       superscaled_resolution, limits.max(),
00060       CellLimits(limits.cell_limits().num_x_cells * kSubpixelScale,
00061                  limits.cell_limits().num_y_cells * kSubpixelScale));
00062 
00063   const Eigen::Array2i superscaled_begin =
00064       superscaled_limits.GetCellIndex(begin);
00065   const Eigen::Array2i superscaled_end = superscaled_limits.GetCellIndex(end);
00066   return std::make_pair(superscaled_begin, superscaled_end);
00067 }
00068 
00069 struct RangeDataSorter {
00070   RangeDataSorter(Eigen::Vector3f origin) { origin_ = origin.head<2>(); }
00071   bool operator()(const sensor::RangefinderPoint& lhs,
00072                   const sensor::RangefinderPoint& rhs) {
00073     const Eigen::Vector2f delta_lhs =
00074         (lhs.position.head<2>() - origin_).normalized();
00075     const Eigen::Vector2f delta_rhs =
00076         (lhs.position.head<2>() - origin_).normalized();
00077     if ((delta_lhs[1] < 0.f) != (delta_rhs[1] < 0.f)) {
00078       return delta_lhs[1] < 0.f;
00079     } else if (delta_lhs[1] < 0.f) {
00080       return delta_lhs[0] < delta_rhs[0];
00081     } else {
00082       return delta_lhs[0] > delta_rhs[0];
00083     }
00084   }
00085 
00086  private:
00087   Eigen::Vector2f origin_;
00088 };
00089 
00090 float ComputeRangeWeightFactor(float range, int exponent) {
00091   float weight = 0.f;
00092   if (std::abs(range) > kMinRangeMeters) {
00093     weight = 1.f / (std::pow(range, exponent));
00094   }
00095   return weight;
00096 }
00097 }  // namespace
00098 
00099 proto::TSDFRangeDataInserterOptions2D CreateTSDFRangeDataInserterOptions2D(
00100     common::LuaParameterDictionary* parameter_dictionary) {
00101   proto::TSDFRangeDataInserterOptions2D options;
00102   options.set_truncation_distance(
00103       parameter_dictionary->GetDouble("truncation_distance"));
00104   options.set_maximum_weight(parameter_dictionary->GetDouble("maximum_weight"));
00105   options.set_update_free_space(
00106       parameter_dictionary->GetBool("update_free_space"));
00107   *options
00108        .mutable_normal_estimation_options() = CreateNormalEstimationOptions2D(
00109       parameter_dictionary->GetDictionary("normal_estimation_options").get());
00110   options.set_project_sdf_distance_to_scan_normal(
00111       parameter_dictionary->GetBool("project_sdf_distance_to_scan_normal"));
00112   options.set_update_weight_range_exponent(
00113       parameter_dictionary->GetInt("update_weight_range_exponent"));
00114   options.set_update_weight_angle_scan_normal_to_ray_kernel_bandwidth(
00115       parameter_dictionary->GetDouble(
00116           "update_weight_angle_scan_normal_to_ray_kernel_bandwidth"));
00117   options.set_update_weight_distance_cell_to_hit_kernel_bandwidth(
00118       parameter_dictionary->GetDouble(
00119           "update_weight_distance_cell_to_hit_kernel_bandwidth"));
00120   return options;
00121 }
00122 
00123 TSDFRangeDataInserter2D::TSDFRangeDataInserter2D(
00124     const proto::TSDFRangeDataInserterOptions2D& options)
00125     : options_(options) {}
00126 
00127 // Casts a ray from origin towards hit for each hit in range data.
00128 // If 'options.update_free_space' is 'true', all cells along the ray
00129 // until 'truncation_distance' behind hit are updated. Otherwise, only the cells
00130 // within 'truncation_distance' around hit are updated.
00131 void TSDFRangeDataInserter2D::Insert(const sensor::RangeData& range_data,
00132                                      GridInterface* grid) const {
00133   const float truncation_distance =
00134       static_cast<float>(options_.truncation_distance());
00135   TSDF2D* tsdf = static_cast<TSDF2D*>(grid);
00136   GrowAsNeeded(range_data, truncation_distance, tsdf);
00137 
00138   // Compute normals if needed.
00139   bool scale_update_weight_angle_scan_normal_to_ray =
00140       options_.update_weight_angle_scan_normal_to_ray_kernel_bandwidth() != 0.f;
00141   sensor::RangeData sorted_range_data = range_data;
00142   std::vector<float> normals;
00143   if (options_.project_sdf_distance_to_scan_normal() ||
00144       scale_update_weight_angle_scan_normal_to_ray) {
00145     std::sort(sorted_range_data.returns.begin(),
00146               sorted_range_data.returns.end(),
00147               RangeDataSorter(sorted_range_data.origin));
00148     normals = EstimateNormals(sorted_range_data,
00149                               options_.normal_estimation_options());
00150   }
00151 
00152   const Eigen::Vector2f origin = sorted_range_data.origin.head<2>();
00153   for (size_t hit_index = 0; hit_index < sorted_range_data.returns.size();
00154        ++hit_index) {
00155     const Eigen::Vector2f hit =
00156         sorted_range_data.returns[hit_index].position.head<2>();
00157     const float normal = normals.empty()
00158                              ? std::numeric_limits<float>::quiet_NaN()
00159                              : normals[hit_index];
00160     InsertHit(options_, hit, origin, normal, tsdf);
00161   }
00162   tsdf->FinishUpdate();
00163 }
00164 
00165 void TSDFRangeDataInserter2D::InsertHit(
00166     const proto::TSDFRangeDataInserterOptions2D& options,
00167     const Eigen::Vector2f& hit, const Eigen::Vector2f& origin, float normal,
00168     TSDF2D* tsdf) const {
00169   const Eigen::Vector2f ray = hit - origin;
00170   const float range = ray.norm();
00171   const float truncation_distance =
00172       static_cast<float>(options_.truncation_distance());
00173   if (range < truncation_distance) return;
00174   const float truncation_ratio = truncation_distance / range;
00175   const Eigen::Vector2f ray_begin =
00176       options_.update_free_space() ? origin
00177                                    : origin + (1.0f - truncation_ratio) * ray;
00178   const Eigen::Vector2f ray_end = origin + (1.0f + truncation_ratio) * ray;
00179   std::pair<Eigen::Array2i, Eigen::Array2i> superscaled_ray =
00180       SuperscaleRay(ray_begin, ray_end, tsdf);
00181   std::vector<Eigen::Array2i> ray_mask = RayToPixelMask(
00182       superscaled_ray.first, superscaled_ray.second, kSubpixelScale);
00183 
00184   // Precompute weight factors.
00185   float weight_factor_angle_ray_normal = 1.f;
00186   if (options_.update_weight_angle_scan_normal_to_ray_kernel_bandwidth() !=
00187       0.f) {
00188     const Eigen::Vector2f negative_ray = -ray;
00189     float angle_ray_normal =
00190         common::NormalizeAngleDifference(normal - common::atan2(negative_ray));
00191     weight_factor_angle_ray_normal = GaussianKernel(
00192         angle_ray_normal,
00193         options_.update_weight_angle_scan_normal_to_ray_kernel_bandwidth());
00194   }
00195   float weight_factor_range = 1.f;
00196   if (options_.update_weight_range_exponent() != 0) {
00197     weight_factor_range = ComputeRangeWeightFactor(
00198         range, options_.update_weight_range_exponent());
00199   }
00200 
00201   // Update Cells.
00202   for (const Eigen::Array2i& cell_index : ray_mask) {
00203     if (tsdf->CellIsUpdated(cell_index)) continue;
00204     Eigen::Vector2f cell_center = tsdf->limits().GetCellCenter(cell_index);
00205     float distance_cell_to_origin = (cell_center - origin).norm();
00206     float update_tsd = range - distance_cell_to_origin;
00207     if (options_.project_sdf_distance_to_scan_normal()) {
00208       float normal_orientation = normal;
00209       update_tsd = (cell_center - hit)
00210                        .dot(Eigen::Vector2f{std::cos(normal_orientation),
00211                                             std::sin(normal_orientation)});
00212     }
00213     update_tsd =
00214         common::Clamp(update_tsd, -truncation_distance, truncation_distance);
00215     float update_weight = weight_factor_range * weight_factor_angle_ray_normal;
00216     if (options_.update_weight_distance_cell_to_hit_kernel_bandwidth() != 0.f) {
00217       update_weight *= GaussianKernel(
00218           update_tsd,
00219           options_.update_weight_distance_cell_to_hit_kernel_bandwidth());
00220     }
00221     UpdateCell(cell_index, update_tsd, update_weight, tsdf);
00222   }
00223 }
00224 
00225 void TSDFRangeDataInserter2D::UpdateCell(const Eigen::Array2i& cell,
00226                                          float update_sdf, float update_weight,
00227                                          TSDF2D* tsdf) const {
00228   if (update_weight == 0.f) return;
00229   const std::pair<float, float> tsd_and_weight = tsdf->GetTSDAndWeight(cell);
00230   float updated_weight = tsd_and_weight.second + update_weight;
00231   float updated_sdf = (tsd_and_weight.first * tsd_and_weight.second +
00232                        update_sdf * update_weight) /
00233                       updated_weight;
00234   updated_weight =
00235       std::min(updated_weight, static_cast<float>(options_.maximum_weight()));
00236   tsdf->SetCell(cell, updated_sdf, updated_weight);
00237 }
00238 
00239 }  // namespace mapping
00240 }  // namespace cartographer


cartographer
Author(s): The Cartographer Authors
autogenerated on Thu May 9 2019 02:27:36