detect_floors.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright 2016 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/detect_floors.h"
00018 
00019 #include <algorithm>
00020 #include <fstream>
00021 #include <vector>
00022 
00023 #include "Eigen/Core"
00024 #include "cartographer/transform/transform.h"
00025 #include "glog/logging.h"
00026 
00027 namespace cartographer {
00028 namespace mapping {
00029 
00030 namespace {
00031 
00032 // A union-find structure for assigning levels to 'spans' in the trajectory
00033 // implemented as a disjoint-set forest:
00034 // https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests
00035 // TODO(hrapp): We use this elsewhere. Pull out into a common place.
00036 using Levels = std::map<int, int>;
00037 
00038 constexpr double kMaxShortSpanLengthMeters = 25.;
00039 constexpr double kLevelHeightMeters = 2.5;
00040 constexpr double kMinLevelSeparationMeters = 1.;
00041 
00042 // Indices into 'trajectory.node', so that 'start_index' <= i < 'end_index'.
00043 struct Span {
00044   int start_index;
00045   int end_index;
00046   std::vector<double> z_values;
00047 
00048   bool operator<(const Span& other) const {
00049     return std::forward_as_tuple(start_index, end_index) <
00050            std::forward_as_tuple(other.start_index, other.end_index);
00051   }
00052 };
00053 
00054 // Union-find implementation for classifying spans into levels.
00055 int LevelFind(const int i, const Levels& levels) {
00056   auto it = levels.find(i);
00057   CHECK(it != levels.end());
00058   if (it->first == it->second) {
00059     return it->second;
00060   }
00061   return LevelFind(it->second, levels);
00062 }
00063 
00064 void LevelUnion(int i, int j, Levels* levels) {
00065   const int repr_i = LevelFind(i, *levels);
00066   const int repr_j = LevelFind(j, *levels);
00067   (*levels)[repr_i] = repr_j;
00068 }
00069 
00070 void InsertSorted(const double val, std::vector<double>* vals) {
00071   vals->insert(std::upper_bound(vals->begin(), vals->end(), val), val);
00072 }
00073 
00074 double Median(const std::vector<double>& sorted) {
00075   CHECK(!sorted.empty());
00076   return sorted.at(sorted.size() / 2);
00077 }
00078 
00079 // Cut the trajectory at jumps in z. A new span is started when the current
00080 // node's z differes by more than kLevelHeightMeters from the median z values.
00081 std::vector<Span> SliceByAltitudeChange(const proto::Trajectory& trajectory) {
00082   CHECK_GT(trajectory.node_size(), 0);
00083 
00084   std::vector<Span> spans;
00085   spans.push_back(Span{0, 0, {trajectory.node(0).pose().translation().z()}});
00086   for (int i = 1; i < trajectory.node_size(); ++i) {
00087     const auto& node = trajectory.node(i);
00088     const double z = node.pose().translation().z();
00089     if (std::abs(Median(spans.back().z_values) - z) > kLevelHeightMeters) {
00090       spans.push_back(Span{i, i, {}});
00091     }
00092     InsertSorted(z, &spans.back().z_values);
00093     spans.back().end_index = i + 1;
00094   }
00095   return spans;
00096 }
00097 
00098 // Returns the length of 'span' in meters.
00099 double SpanLength(const proto::Trajectory& trajectory, const Span& span) {
00100   double length = 0;
00101   for (int i = span.start_index + 1; i < span.end_index; ++i) {
00102     const auto a =
00103         transform::ToEigen(trajectory.node(i - 1).pose().translation());
00104     const auto b = transform::ToEigen(trajectory.node(i).pose().translation());
00105     length += (a - b).head<2>().norm();
00106   }
00107   return length;
00108 }
00109 
00110 // True if 'span' is considered to be short, i.e. not interesting on its own,
00111 // but should be folded into the levels before and after entering it.
00112 bool IsShort(const proto::Trajectory& trajectory, const Span& span) {
00113   return SpanLength(trajectory, span) < kMaxShortSpanLengthMeters;
00114 }
00115 
00116 // Merges all 'spans' that have similar median z value into the same level.
00117 void GroupSegmentsByAltitude(const proto::Trajectory& trajectory,
00118                              const std::vector<Span>& spans, Levels* levels) {
00119   for (size_t i = 0; i < spans.size(); ++i) {
00120     for (size_t j = i + 1; j < spans.size(); ++j) {
00121       if (std::abs(Median(spans[i].z_values) - Median(spans[j].z_values)) <
00122           kMinLevelSeparationMeters) {
00123         LevelUnion(i, j, levels);
00124       }
00125     }
00126   }
00127 }
00128 
00129 std::vector<Floor> FindFloors(const proto::Trajectory& trajectory,
00130                               const std::vector<Span>& spans,
00131                               const Levels& levels) {
00132   std::map<int, std::vector<Span>> level_spans;
00133 
00134   // Initialize the levels to start out with only long spans.
00135   for (size_t i = 0; i < spans.size(); ++i) {
00136     const Span& span = spans[i];
00137     if (!IsShort(trajectory, span)) {
00138       level_spans[LevelFind(i, levels)].push_back(span);
00139     }
00140   }
00141 
00142   for (size_t i = 0; i < spans.size(); ++i) {
00143     const Span& span = spans[i];
00144     if (!IsShort(trajectory, span)) {
00145       continue;
00146     }
00147 
00148     // If we have a long piece on this floor already, merge this short piece
00149     // into it.
00150     int level = LevelFind(i, levels);
00151     if (!level_spans[level].empty()) {
00152       level_spans[level].push_back(span);
00153       continue;
00154     }
00155 
00156     // Otherwise, add this short piece to the level before and after it. It is
00157     // likely some intermediate level on stairs.
00158     size_t index = i - 1;
00159     if (index < spans.size()) {
00160       level_spans[LevelFind(index, levels)].push_back(span);
00161     }
00162     index = i + 1;
00163     if (index < spans.size()) {
00164       level_spans[LevelFind(index, levels)].push_back(span);
00165     }
00166   }
00167 
00168   // Convert the level_spans structure to 'Floor'.
00169   std::vector<Floor> floors;
00170   for (auto& level : level_spans) {
00171     if (level.second.empty()) {
00172       continue;
00173     }
00174 
00175     std::vector<double> z_values;
00176     std::sort(level.second.begin(), level.second.end());
00177     floors.emplace_back();
00178     for (const auto& span : level.second) {
00179       if (!IsShort(trajectory, span)) {
00180         // To figure out the median height of this floor, we only care for the
00181         // long pieces that are guaranteed to be in the structure. This is a
00182         // heuristic to leave out intermediate (short) levels.
00183         z_values.insert(z_values.end(), span.z_values.begin(),
00184                         span.z_values.end());
00185       }
00186       floors.back().timespans.push_back(Timespan{
00187           common::FromUniversal(trajectory.node(span.start_index).timestamp()),
00188           common::FromUniversal(
00189               trajectory.node(span.end_index - 1).timestamp())});
00190     }
00191     std::sort(z_values.begin(), z_values.end());
00192     floors.back().z = Median(z_values);
00193   }
00194   return floors;
00195 }
00196 
00197 }  // namespace
00198 
00199 std::vector<Floor> DetectFloors(const proto::Trajectory& trajectory) {
00200   const std::vector<Span> spans = SliceByAltitudeChange(trajectory);
00201   Levels levels;
00202   for (size_t i = 0; i < spans.size(); ++i) {
00203     levels[i] = i;
00204   }
00205   GroupSegmentsByAltitude(trajectory, spans, &levels);
00206 
00207   std::vector<Floor> floors = FindFloors(trajectory, spans, levels);
00208   std::sort(floors.begin(), floors.end(),
00209             [](const Floor& a, const Floor& b) { return a.z < b.z; });
00210   return floors;
00211 }
00212 
00213 }  // namespace mapping
00214 }  // namespace cartographer


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