[
  {
    "path": ".gitignore",
    "content": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n \n# 忽略*.o和*.a文件\n *.[oa] \n *.[out]\n*build\nbuild    #过滤整个build文件夹\ndevel\n*.class    #过滤所有.class文件\n.idea \n cartographer\n cartographer_ros\n ceres-solver\nEigen \nsrc \n.zip\norig\nbackup\npictures\nhtml\nnote"
  },
  {
    "path": "README.md",
    "content": "# cartographer\n关于谷歌slam地图库 [cartographer](https://github.com/googlecartographer/cartographer)的源码注释。(只含源码)\n\n \n\n博客文章：\n\n\n\n* [知乎](https://zhuanlan.zhihu.com/learnmoreonce) (简略)\n \n* [CSDN](http://blog.csdn.net/learnmoreonce)       (详细)\n\n\n源码分析的markdown文件可在 [这里]( https://github.com/slam4code/SLAM/tree/master/cartographer-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90 )\n查找。\n\n\n我的微信公众号:slamcode\n\n<div  align=\"center\">    \n <img src=\"https://raw.githubusercontent.com/slam4code/SLAM/master/slamcode.jpg\" width = \"200\" height = \"200\" alt=\"slamcode\" align=center />\n</div>"
  },
  {
    "path": "common/blocking_queue.h",
    "content": "\n#ifndef CARTOGRAPHER_COMMON_BLOCKING_QUEUE_H_\n#define CARTOGRAPHER_COMMON_BLOCKING_QUEUE_H_\n\n#include <cstddef>\n#include <deque>\n#include <memory>\n\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"glog/logging.h\"\n/*\n * BlockingQueue是线程安全的阻塞队列,(生产者消费者模式)\n * 不可拷贝不可赋值\n * 构造函数初始化队列大小,kInfiniteQueueSize=0默认不限制容量。queue_size限制容量：通过条件变量做到.\n * Push()添加元素,容量不够时,阻塞等待\n * Pop()删除元素,没有元素时,阻塞等待\n * Peek()返回下一个应该弹出的元素\n * PushWithTimeout(),添加元素,若超时则返回false\n * PopWithTimeout(),删除元素，若超时则返回false\n *\n * */\nnamespace cartographer {\nnamespace common {\n\n// A thread-safe blocking queue that is useful for producer/consumer patterns.\n// 'T' must be movable.\ntemplate <typename T>\nclass BlockingQueue {\n public:\n  static constexpr size_t kInfiniteQueueSize = 0;\n\n  // Constructs a blocking queue with infinite queue size.\n  BlockingQueue() : BlockingQueue(kInfiniteQueueSize) {} //默认不限制容量\n\n  BlockingQueue(const BlockingQueue&) = delete;\n  BlockingQueue& operator=(const BlockingQueue&) = delete;\n\n  // Constructs a blocking queue with a size of 'queue_size'.限制容量\n  explicit BlockingQueue(const size_t queue_size) : queue_size_(queue_size) {}\n\n  // Pushes a value onto the queue. Blocks if the queue is full.\n  void Push(T t) {\n    MutexLocker lock(&mutex_);//加锁\n    lock.Await([this]() REQUIRES(mutex_) { return QueueNotFullCondition(); });//队列未满才能push\n    deque_.push_back(std::move(t)); //移动而非拷贝\n  }\n\n  // Like push, but returns false if 'timeout' is reached.\n  bool PushWithTimeout(T t, const common::Duration timeout) { //最多等待timeout时间\n    MutexLocker lock(&mutex_);\n    if (!lock.AwaitWithTimeout(\n            [this]() REQUIRES(mutex_) { return QueueNotFullCondition(); },\n            timeout)) {\n      return false;\n    }\n    deque_.push_back(std::move(t));\n    return true;\n  }\n\n  // Pops the next value from the queue. Blocks until a value is available.\n  T Pop() {\n    MutexLocker lock(&mutex_); //加锁\n    lock.Await([this]() REQUIRES(mutex_) { return QueueNotEmptyCondition(); });//队列不为空才能pop()\n\n    T t = std::move(deque_.front());\n    deque_.pop_front();\n    return t;\n  }\n\n  // Like Pop, but can timeout. Returns nullptr in this case.\n  T PopWithTimeout(const common::Duration timeout) {\n    MutexLocker lock(&mutex_);\n    if (!lock.AwaitWithTimeout(\n            [this]() REQUIRES(mutex_) { return QueueNotEmptyCondition(); },\n            timeout)) {\n      return nullptr;\n    }\n    T t = std::move(deque_.front());\n    deque_.pop_front();\n    return t;\n  }\n\n  // Returns the next value in the queue or nullptr if the queue is empty.\n  // Maintains ownership. This assumes a member function get() that returns\n  // a pointer to the given type R.\n  template <typename R>\n  const R* Peek() {\n    MutexLocker lock(&mutex_);\n    if (deque_.empty()) {\n      return nullptr;\n    }\n    return deque_.front().get();\n  }\n\n  // Returns the number of items currently in the queue.\n  size_t Size() {\n    MutexLocker lock(&mutex_);\n    return deque_.size();\n  }\n\n private:\n  // Returns true iff the queue is not empty.\n  bool QueueNotEmptyCondition() REQUIRES(mutex_) { return !deque_.empty(); }\n\n  // Returns true iff the queue is not full.\n  bool QueueNotFullCondition() REQUIRES(mutex_) {\n    return queue_size_ == kInfiniteQueueSize || deque_.size() < queue_size_;\n  }\n\n  Mutex mutex_;\n  const size_t queue_size_ GUARDED_BY(mutex_);\n  std::deque<T> deque_ GUARDED_BY(mutex_);\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_BLOCKING_QUEUE_H_\n"
  },
  {
    "path": "common/blocking_queue_test.cc",
    "content": "\n#include \"cartographer/common/blocking_queue.h\"\n\n#include <memory>\n#include <thread>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/time.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace common {\nnamespace {\n\nTEST(BlockingQueueTest, testPushPeekPop) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue;//元素是智能指针,不限容\n  blocking_queue.Push(common::make_unique<int>(42)); //添加\n  ASSERT_EQ(1, blocking_queue.Size());               //size==1\n  blocking_queue.Push(common::make_unique<int>(24)); //添加\n  ASSERT_EQ(2, blocking_queue.Size());               //size==2\n  EXPECT_EQ(42, *blocking_queue.Peek<int>());        //顶元素==42\n  ASSERT_EQ(2, blocking_queue.Size());               //队列大小是2\n  EXPECT_EQ(42, *blocking_queue.Pop());\n  ASSERT_EQ(1, blocking_queue.Size());\n  EXPECT_EQ(24, *blocking_queue.Pop());\n  ASSERT_EQ(0, blocking_queue.Size());\n  EXPECT_EQ(nullptr, blocking_queue.Peek<int>());\n  ASSERT_EQ(0, blocking_queue.Size());\n}\n\nTEST(BlockingQueueTest, testPushPopSharedPtr) {\n  BlockingQueue<std::shared_ptr<int>> blocking_queue;  //不限容\n  blocking_queue.Push(std::make_shared<int>(42));\n  blocking_queue.Push(std::make_shared<int>(24));\n  EXPECT_EQ(42, *blocking_queue.Pop());\n  EXPECT_EQ(24, *blocking_queue.Pop());\n}\n\nTEST(BlockingQueueTest, testPopWithTimeout) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue;\n  EXPECT_EQ(nullptr,                                 //没有插入,所以是nullptr\n            blocking_queue.PopWithTimeout(common::FromMilliseconds(150)));\n}\n\nTEST(BlockingQueueTest, testPushWithTimeout) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue(1);//限容\n  EXPECT_EQ(true,                                   //插入成功\n            blocking_queue.PushWithTimeout(common::make_unique<int>(42),\n                                           common::FromMilliseconds(150)));\n  EXPECT_EQ(false,                                  //失败,因为队列大小是1\n            blocking_queue.PushWithTimeout(common::make_unique<int>(15),\n                                           common::FromMilliseconds(150)));\n  EXPECT_EQ(42, *blocking_queue.Pop());\n  EXPECT_EQ(0, blocking_queue.Size());\n}\n\nTEST(BlockingQueueTest, testPushWithTimeoutInfinteQueue) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue; //不限容量\n  EXPECT_EQ(true,\n            blocking_queue.PushWithTimeout(common::make_unique<int>(42),\n                                           common::FromMilliseconds(150)));\n  EXPECT_EQ(true,\n            blocking_queue.PushWithTimeout(common::make_unique<int>(45),\n                                           common::FromMilliseconds(150)));\n  EXPECT_EQ(42, *blocking_queue.Pop());\n  EXPECT_EQ(45, *blocking_queue.Pop());\n  EXPECT_EQ(0, blocking_queue.Size());\n}\n\nTEST(BlockingQueueTest, testBlockingPop) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue;\n  ASSERT_EQ(0, blocking_queue.Size());\n\n  int pop = 0;\n  //多线程测试\n  std::thread thread([&blocking_queue, &pop] { pop = *blocking_queue.Pop(); });\n\n  std::this_thread::sleep_for(common::FromMilliseconds(100));\n  blocking_queue.Push(common::make_unique<int>(42));\n  thread.join();\n  ASSERT_EQ(0, blocking_queue.Size());\n  EXPECT_EQ(42, pop);\n}\n\nTEST(BlockingQueueTest, testBlockingPopWithTimeout) {\n  BlockingQueue<std::unique_ptr<int>> blocking_queue;\n  ASSERT_EQ(0, blocking_queue.Size());\n\n  int pop = 0;\n  //多线程测试\n  std::thread thread([&blocking_queue, &pop] {\n    pop = *blocking_queue.PopWithTimeout(common::FromMilliseconds(2500));\n  });\n\n  std::this_thread::sleep_for(common::FromMilliseconds(100));\n  blocking_queue.Push(common::make_unique<int>(42));\n  thread.join();\n  ASSERT_EQ(0, blocking_queue.Size());\n  EXPECT_EQ(42, pop);\n}\n\n}  // namespace\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/ceres_solver_options.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/common/ceres_solver_options.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\n根据lua文件设置\n*/\nproto::CeresSolverOptions CreateCeresSolverOptionsProto(\n    common::LuaParameterDictionary* parameter_dictionary) \n\n{\n  proto::CeresSolverOptions proto;\n  proto.set_use_nonmonotonic_steps(\n      parameter_dictionary->GetBool(\"use_nonmonotonic_steps\"));//是否设置使用nonmonotonic\n  proto.set_max_num_iterations(\n      parameter_dictionary->GetNonNegativeInt(\"max_num_iterations\"));//迭代次数\n  proto.set_num_threads(parameter_dictionary->GetNonNegativeInt(\"num_threads\"));//线程数\n  CHECK_GT(proto.max_num_iterations(), 0);\n  CHECK_GT(proto.num_threads(), 0);\n  return proto;\n}\n/*\n根据proto文件设置\n*/\nceres::Solver::Options CreateCeresSolverOptions(\n    const proto::CeresSolverOptions& proto) \n\n{\n  ceres::Solver::Options options;//建立临时对象\n  options.use_nonmonotonic_steps = proto.use_nonmonotonic_steps();//为临时对象赋值\n  options.max_num_iterations = proto.max_num_iterations();        //赋值\n  options.num_threads = proto.num_threads();                      //赋值\n  return options;\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/ceres_solver_options.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_CERES_SOLVER_OPTIONS_H_\n#define CARTOGRAPHER_COMMON_CERES_SOLVER_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/proto/ceres_solver_options.pb.h\"\n#include \"ceres/ceres.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\n配置加载类, 根据lua设置 CeresSolver的选项\n\n*/\nproto::CeresSolverOptions CreateCeresSolverOptionsProto(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n//根据proto设置 CeresSolver的选项\nceres::Solver::Options CreateCeresSolverOptions(\n    const proto::CeresSolverOptions& proto);\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_CERES_SOLVER_OPTIONS_H_\n"
  },
  {
    "path": "common/config.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_CONFIG_H_\n#define CARTOGRAPHER_COMMON_CONFIG_H_\n\nnamespace cartographer {\nnamespace common {\n\nconstexpr char kConfigurationFilesDirectory[] =  //设置配置文件的路径\n    \"\";\nconstexpr char kSourceDirectory[] = \"./\";\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_CONFIG_H_\n"
  },
  {
    "path": "common/config.h.cmake",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_CONFIG_H_\n#define CARTOGRAPHER_COMMON_CONFIG_H_\n\nnamespace cartographer {\nnamespace common {\n\nconstexpr char kConfigurationFilesDirectory[] =  //设置配置文件的路径\n    \"@CARTOGRAPHER_CONFIGURATION_FILES_DIRECTORY@\";\nconstexpr char kSourceDirectory[] = \"@PROJECT_SOURCE_DIR@\";\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_CONFIG_H_\n"
  },
  {
    "path": "common/configuration_file_resolver.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/common/configuration_file_resolver.h\"\n\n#include <fstream>\n#include <iostream>\n#include <streambuf>\n\n#include \"cartographer/common/config.h\"//根据config.h.cmake生成\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace common {\n\nConfigurationFileResolver::ConfigurationFileResolver(\n    const std::vector<string>& configuration_files_directories)\n    : configuration_files_directories_(configuration_files_directories) {\n  configuration_files_directories_.push_back(kConfigurationFilesDirectory);//配置文件夹路径\n}\n\n\nstring ConfigurationFileResolver::GetFullPathOrDie(const string& basename) { //根据文件 夹和basename生成文件名\n  for (const auto& path : configuration_files_directories_) {\n    const string filename = path + \"/\" + basename;\n    std::ifstream stream(filename.c_str());\n    if (stream.good()) {\n      LOG(INFO) << \"Found '\" << filename << \"' for '\" << basename << \"'.\";\n      return filename;\n    }\n  }\n  LOG(FATAL) << \"File '\" << basename << \"' was not found.\";\n}\n\nstring ConfigurationFileResolver::GetFileContentOrDie(const string& basename) {//根据文件名读取file内容\n  const string filename = GetFullPathOrDie(basename);\n  std::ifstream stream(filename.c_str()); //文件输入流\n  return string((std::istreambuf_iterator<char>(stream)),//使用迭代器初始化string对象\n                std::istreambuf_iterator<char>());\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/configuration_file_resolver.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_CONFIGURATION_FILE_RESOLVER_H_\n#define CARTOGRAPHER_COMMON_CONFIGURATION_FILE_RESOLVER_H_\n\n#include <vector>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\nConfigurationFileResolver是配置文件解析类,继承自FileResolver类\n在给定的目录下搜索配置文件,\n\n\n*/\n\n// A 'FileResolver' for the 'LuaParameterDictionary' that reads files from disk.\n// It searches the 'configuration_files_directories' in order to find the\n// requested filename. The last place searched is always the\n// 'configuration_files/' directory installed with Cartographer. It contains\n// reasonable configuration for the various Cartographer components which\n// provide a good starting ground for new platforms.\nclass ConfigurationFileResolver : public FileResolver {\n public:\n  explicit ConfigurationFileResolver(\n      const std::vector<string>& configuration_files_directories);\n\n  string GetFullPathOrDie(const string& basename) override;//获取文件的全部路径\n  string GetFileContentOrDie(const string& basename) override; //获取文件内容\n\n private:\n  std::vector<string> configuration_files_directories_;\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_CONFIGURATION_FILE_RESOLVER_H_\n"
  },
  {
    "path": "common/fixed_ratio_sampler.cc",
    "content": "\n\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n\nnamespace cartographer {\nnamespace common {\n\nFixedRatioSampler::FixedRatioSampler(const double ratio) : ratio_(ratio) {}\n\nFixedRatioSampler::~FixedRatioSampler() {}\n\nbool FixedRatioSampler::Pulse() { \n\n    //如果当前采样率 samples/ pulses低于固定采样率,此次事件计入采样.\n  ++num_pulses_;\n  if (static_cast<double>(num_samples_) / num_pulses_ < ratio_) {\n    ++num_samples_;\n    return true;\n  }\n  return false;\n}\n\nstring FixedRatioSampler::DebugString() {\n  return std::to_string(num_samples_) + \" (\" +\n         std::to_string(100. * num_samples_ / num_pulses_) + \"%)\";\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/fixed_ratio_sampler.h",
    "content": "\n#ifndef CARTOGRAPHER_COMMON_FIXED_RATIO_SAMPLER_H_\n#define CARTOGRAPHER_COMMON_FIXED_RATIO_SAMPLER_H_\n\n#include <string>\n\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\nFixedRatioSampler是频率固定的采样器类，目的是从数据流中均匀的按照固定频率采样数据\nFixedRatioSampler不可拷贝,不可赋值.\n成员函数提供2种操作:\nPulse()产生一个事件pulses,并且:如果计入采样samples，返回true\nDebugString():以string形式输出采样率\n\n*/\n// Signals when a sample should be taken from a stream of data to select a\n// uniformly distributed fraction of the data.\nclass FixedRatioSampler {\n public:\n  explicit FixedRatioSampler(double ratio);\n  ~FixedRatioSampler();\n\n  FixedRatioSampler(const FixedRatioSampler&) = delete;\n  FixedRatioSampler& operator=(const FixedRatioSampler&) = delete;\n\n  // Returns true if this pulse should result in an sample.\n  bool Pulse();\n\n  // Returns a debug string describing the current ratio of samples to pulses.\n  string DebugString();\n\n private:\n  // Sampling occurs if the proportion of samples to pulses drops below this\n  // number.\n  const double ratio_;\n\n  int64 num_pulses_ = 0; //产生的脉冲次数\n  int64 num_samples_ = 0;//记录的采样次数\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_FIXED_RATIO_SAMPLER_H_\n"
  },
  {
    "path": "common/fixed_ratio_sampler_test.cc",
    "content": "\n\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace common {\nnamespace {\n\nTEST(FixedRatioSamplerTest, AlwaysTrue) {\n  FixedRatioSampler fixed_ratio_sampler(1.);//固定采样率是1hz，每次Pulse都采集samples\n  for (int i = 0; i < 100; ++i) {\n    EXPECT_TRUE(fixed_ratio_sampler.Pulse());\n  }\n}\n\nTEST(FixedRatioSamplerTest, AlwaysFalse) {\n  FixedRatioSampler fixed_ratio_sampler(0.);//0 hz，不采集samples\n  for (int i = 0; i < 100; ++i) {\n    EXPECT_FALSE(fixed_ratio_sampler.Pulse());\n  }\n}\n\nTEST(FixedRatioSamplerTest, SometimesTrue) {\n  FixedRatioSampler fixed_ratio_sampler(0.5); //0.5hz\n  for (int i = 0; i < 100; ++i) {\n    EXPECT_EQ(i % 2 == 0, fixed_ratio_sampler.Pulse());//每2次Pulse采集一次samples\n  }\n}\n\nTEST(FixedRatioSamplerTest, FirstPulseIsTrue) {\n  // Choose a very very small positive number for the ratio.\n  FixedRatioSampler fixed_ratio_sampler(1e-20); //0.000000...001hz\n  EXPECT_TRUE(fixed_ratio_sampler.Pulse());\n  for (int i = 0; i < 100; ++i) {\n    EXPECT_FALSE(fixed_ratio_sampler.Pulse()); //每100000000次Pulse采集一次samples\n  }\n}\n\n}  // namespace\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/histogram.cc",
    "content": "\n\n#include \"cartographer/common/histogram.h\"\n\n#include <algorithm>\n#include <numeric>\n#include <string>\n\n#include \"cartographer/common/port.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace common {\n\n//添加元素\nvoid Histogram::Add(const float value) { values_.push_back(value); }\n\n//以bin大小是buckets转成 string,输出 直方图信息.\nstring Histogram::ToString(const int buckets) const {\n  CHECK_GE(buckets, 1);      //篮子个数必须>=1\n  if (values_.empty()) {\n    return \"Count: 0\";\n  }\n  const float min = *std::min_element(values_.begin(), values_.end());\n  const float max = *std::max_element(values_.begin(), values_.end());\n  const float mean =\n      std::accumulate(values_.begin(), values_.end(), 0.f) / values_.size();\n  string result = \"Count: \" + std::to_string(values_.size()) +\n                  \"  Min: \" + std::to_string(min) +\n                  \"  Max: \" + std::to_string(max) +\n                  \"  Mean: \" + std::to_string(mean);\n  if (min == max) { //最大等于最小直接返回.\n    return result;\n  }\n  CHECK_LT(min, max);\n  float lower_bound = min; //bin区间下界\n  int total_count = 0;\n  for (int i = 0; i != buckets; ++i) { //根据bin个数\n    const float upper_bound =  //bin区间上界\n        (i + 1 == buckets)\n            ? max\n            : (max * (i + 1) / buckets + min * (buckets - i - 1) / buckets);\n    int count = 0;\n    for (const float value : values_) { //对每一个vector<float>的值\n      if (lower_bound <= value &&\n          (i + 1 == buckets ? value <= upper_bound : value < upper_bound)) {\n        ++count;\n      }\n    }\n    total_count += count;\n    result += \"\\n[\" + std::to_string(lower_bound) + \", \" +\n              std::to_string(upper_bound) + ((i + 1 == buckets) ? \"]\" : \")\");\n    constexpr int kMaxBarChars = 20;\n    const int bar =\n        (count * kMaxBarChars + values_.size() / 2) / values_.size();\n    result += \"\\t\";\n    for (int i = 0; i != kMaxBarChars; ++i) {\n      result += (i < (kMaxBarChars - bar)) ? \" \" : \"#\";\n    }\n    result += \"\\tCount: \" + std::to_string(count) + \" (\" +\n              std::to_string(count * 1e2f / values_.size()) + \"%)\";\n    result += \"\\tTotal: \" + std::to_string(total_count) + \" (\" +\n              std::to_string(total_count * 1e2f / values_.size()) + \"%)\";\n    lower_bound = upper_bound;\n  }\n  return result;\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/histogram.h",
    "content": "\n\n#ifndef CARTOGRAPHER_COMMON_HISTOGRAM_H_\n#define CARTOGRAPHER_COMMON_HISTOGRAM_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace common {\n/*\nHistogram直方图类,\n提供2个操作:\n1，Add()添加元素\n2，ToString(buckets )以字符串的形式输出buckets个直方图信息,bin大小是篮子个数.\n\nHistogram只有一个数据成员，用vector<float>表示\n\n*/\nclass Histogram {\n public:\n  void Add(float value);\t\t\t //添加value,可乱序\n  string ToString(int buckets) const;//分为几块\n\n private:\n  std::vector<float> values_;\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_HISTOGRAM_H_\n\n\n\n/*测试：\n若bin为2:[2,1,2,4,5,5]:\nCount: 6  Min: 1.000000  Max: 5.000000  Mean: 3.166667\n[1.000000, 3.000000)              ##########    Count: 3 (50.000000%)   Total: 3 (50.000000%)\n[3.000000, 5.000000]              ##########    Count: 3 (50.000000%)   Total: 6 (100.000000%)\n\n\n若bin为3:[2,1,2,4,5,5,6,7,8]:\nCount: 10  Min: 1.000000  Max: 8.000000  Mean: 4.500000\n[1.000000, 3.333333)                  ######    Count: 3 (30.000000%)   Total: 3 (30.000000%)\n[3.333333, 5.666667)                ########    Count: 4 (40.000000%)   Total: 7 (70.000000%)\n[5.666667, 8.000000]                  ######    Count: 3 (30.000000%)   Total: 10 (100.000000%)\n\n*/"
  },
  {
    "path": "common/lua.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_LUA_H_\n#define CARTOGRAPHER_COMMON_LUA_H_\n\n#include <lua.hpp>\n\n#endif  // CARTOGRAPHER_COMMON_LUA_H_\n"
  },
  {
    "path": "common/lua_parameter_dictionary.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*LuaParameterDictionary构造函数启动一个lua解释器,此解释器运行lua code,\n并返回一个key/val键值表的lua Table\n\n*/\n// When a LuaParameterDictionary is constructed, a new Lua state (i.e. an\n// independent Lua interpreter) is fired up to evaluate the Lua code. The code\n// is expected to return a Lua table that contains key/value pairs that are the\n// key/value pairs of our parameter dictionary.\n//\n\n/*\n\nkeep around:继续运行,lua table保持在栈上,c++引用这个table,而不是加载到c++ 的map中去.\n\n*/\n// We keep the Lua interpreter around and the table on the stack and reference\n// it in the Get* methods instead of moving its contents from Lua into a C++ map\n// since we can only know in the Get* methods what data type to expect in the\n// table.\n//\n// Some of the methods below documentation the current stack with the following\n// notation: S: <bottom> ... <top>\n\n/*\n以下文档中的stack表示:<bottom> ..<top>\nlua中的\n\n*/\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <functional>\n#include <memory>\n\nnamespace cartographer {\nnamespace common {\n\nnamespace {\n\n/*\nlua_isstring()):当给定索引的值是一个字符串或是一个数字（数字总能转换成字符串）时，返回 1 ，否则返回 0 。\nlua_gettop()返回栈顶元素的索引。因为索引是从 1 开始编号的，所以这个结果等于堆栈上的元素个数（因此返回 0 表示堆栈为空）\n\n\n */\n// Replace the string at the top of the stack through a quoted version that Lua\n// can read back.\nvoid QuoteStringOnStack(lua_State* L) { //加\"\"引号,方便lua回调\n  CHECK(lua_isstring(L, -1)) << \"Top of stack is not a string value.\";\n  int current_index = lua_gettop(L);\n\n  // S: ... string\n  lua_pushglobaltable(L);         // S: ... string globals\n  lua_getfield(L, -1, \"string\");  // S: ... string globals <string module>\n  lua_getfield(L, -1,\n               \"format\");   // S: ... string globals <string module> format\n  lua_pushstring(L, \"%q\");  // S: ... string globals <string module> format \"%q\"\n  lua_pushvalue(L, current_index);  // S: ... string globals <string module>\n                                    // format \"%q\" string\n\n  lua_call(L, 2, 1);  // S: ... string globals <string module> quoted\n  lua_replace(L, current_index);  // S: ... quoted globals <string module>\n\n  lua_pop(L, 2);  // S: ... quoted\n}\n\n// Sets the given 'dictionary' as the value of the \"this\" key in Lua's registry\n// table.\nvoid SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {\n  lua_pushstring(L, \"this\");\n  lua_pushlightuserdata(L, dictionary);\n  lua_settable(L, LUA_REGISTRYINDEX);\n}\n\n// Gets the 'dictionary' from the \"this\" key in Lua's registry table.\nLuaParameterDictionary* GetDictionaryFromRegistry(lua_State* L) {\n  lua_pushstring(L, \"this\");\n  lua_gettable(L, LUA_REGISTRYINDEX);\n  void* return_value = lua_isnil(L, -1) ? nullptr : lua_touserdata(L, -1);\n  lua_pop(L, 1);\n  CHECK(return_value != nullptr);\n  return reinterpret_cast<LuaParameterDictionary*>(return_value);\n}\n\n// CHECK()s if a Lua method returned an error.\nvoid CheckForLuaErrors(lua_State* L, int status) {\n  CHECK_EQ(status, 0) << lua_tostring(L, -1);\n}\n\n// Returns 'a' if 'condition' is true, else 'b'.\nint LuaChoose(lua_State* L) {\n  CHECK_EQ(lua_gettop(L), 3) << \"choose() takes (condition, a, b).\";\n  CHECK(lua_isboolean(L, 1)) << \"condition is not a boolean value.\";\n\n  const bool condition = lua_toboolean(L, 1);\n  if (condition) {\n    lua_pushvalue(L, 2);\n  } else {\n    lua_pushvalue(L, 3);\n  }\n  return 1;\n}\n\n// Pushes a value to the Lua stack.\nvoid PushValue(lua_State* L, const int key) { lua_pushinteger(L, key); }\nvoid PushValue(lua_State* L, const string& key) {\n  lua_pushstring(L, key.c_str());\n}\n\n// Reads the value with the given 'key' from the Lua dictionary and pushes it to\n// the top of the stack.\ntemplate <typename T>\nvoid GetValueFromLuaTable(lua_State* L, const T& key) {\n  PushValue(L, key);\n  lua_rawget(L, -2);\n}\n\n// CHECK() that the topmost parameter on the Lua stack is a table.\nvoid CheckTableIsAtTopOfStack(lua_State* L) {\n  CHECK(lua_istable(L, -1)) << \"Topmost item on Lua stack is not a table!\";\n}\n\n// Returns true if 'key' is in the table at the top of the Lua stack.\ntemplate <typename T>\nbool HasKeyOfType(lua_State* L, const T& key) {\n  CheckTableIsAtTopOfStack(L);\n  PushValue(L, key);\n  lua_rawget(L, -2);\n  const bool key_not_found = lua_isnil(L, -1);\n  lua_pop(L, 1);  // Pop the item again.\n  return !key_not_found;\n}\n\n// Iterates over the integer keys of the table at the top of the stack of 'L•\n// and pushes the values one by one. 'pop_value' is expected to pop a value and\n// put them into a C++ container.\nvoid GetArrayValues(lua_State* L, const std::function<void()>& pop_value) {\n  int idx = 1;\n  while (true) {\n    GetValueFromLuaTable(L, idx);\n    if (lua_isnil(L, -1)) {\n      lua_pop(L, 1);\n      break;\n    }\n    pop_value();\n    ++idx;\n  }\n}\n\n}  // namespace\n\nstd::unique_ptr<LuaParameterDictionary>\nLuaParameterDictionary::NonReferenceCounted(\n    const string& code, std::unique_ptr<FileResolver> file_resolver) {\n  return std::unique_ptr<LuaParameterDictionary>(new LuaParameterDictionary(\n      code, ReferenceCount::NO, std::move(file_resolver)));\n}\n\nLuaParameterDictionary::LuaParameterDictionary(\n    const string& code, std::unique_ptr<FileResolver> file_resolver)\n    : LuaParameterDictionary(code, ReferenceCount::YES,\n                             std::move(file_resolver)) {}\n\nLuaParameterDictionary::LuaParameterDictionary(\n    const string& code, ReferenceCount reference_count,\n    std::unique_ptr<FileResolver> file_resolver)\n    : L_(luaL_newstate()),\n      index_into_reference_table_(-1),\n      file_resolver_(std::move(file_resolver)),\n      reference_count_(reference_count) {\n  CHECK_NOTNULL(L_);\n  SetDictionaryInRegistry(L_, this);\n\n  luaL_openlibs(L_);\n\n  lua_register(L_, \"choose\", LuaChoose);\n  lua_register(L_, \"include\", LuaInclude);\n  lua_register(L_, \"read\", LuaRead);\n\n  CheckForLuaErrors(L_, luaL_loadstring(L_, code.c_str()));\n  CheckForLuaErrors(L_, lua_pcall(L_, 0, 1, 0));\n  CheckTableIsAtTopOfStack(L_);\n}\n\nLuaParameterDictionary::LuaParameterDictionary(\n    lua_State* const L, ReferenceCount reference_count,\n    std::shared_ptr<FileResolver> file_resolver)\n    : L_(lua_newthread(L)),\n      file_resolver_(std::move(file_resolver)),\n      reference_count_(reference_count) {\n  CHECK_NOTNULL(L_);\n\n  // Make sure this is never garbage collected.\n  CHECK(lua_isthread(L, -1));\n  index_into_reference_table_ = luaL_ref(L, LUA_REGISTRYINDEX);\n\n  CHECK(lua_istable(L, -1)) << \"Topmost item on Lua stack is not a table!\";\n  lua_xmove(L, L_, 1);  // Moves the table and the coroutine over.\n  CheckTableIsAtTopOfStack(L_);\n}\n\nLuaParameterDictionary::~LuaParameterDictionary() {\n  if (reference_count_ == ReferenceCount::YES) {\n    CheckAllKeysWereUsedExactlyOnceAndReset();\n  }\n  if (index_into_reference_table_ > 0) {\n    luaL_unref(L_, LUA_REGISTRYINDEX, index_into_reference_table_);\n  } else {\n    lua_close(L_);\n  }\n}\n\nstd::vector<string> LuaParameterDictionary::GetKeys() const {\n  CheckTableIsAtTopOfStack(L_);\n  std::vector<string> keys;\n\n  lua_pushnil(L_);  // Push the first key\n  while (lua_next(L_, -2) != 0) {\n    lua_pop(L_, 1);  // Pop value, keep key.\n    if (!lua_isnumber(L_, -1)) {\n      keys.emplace_back(lua_tostring(L_, -1));\n    }\n  }\n  return keys;\n}\n\nbool LuaParameterDictionary::HasKey(const string& key) const {\n  return HasKeyOfType(L_, key);\n}\n\nstring LuaParameterDictionary::GetString(const string& key) {\n  CheckHasKeyAndReference(key);\n  GetValueFromLuaTable(L_, key);\n  return PopString(Quoted::NO);\n}\n\nstring LuaParameterDictionary::PopString(Quoted quoted) const {\n  CHECK(lua_isstring(L_, -1)) << \"Top of stack is not a string value.\";\n  if (quoted == Quoted::YES) {\n    QuoteStringOnStack(L_);\n  }\n\n  const string value = lua_tostring(L_, -1);\n  lua_pop(L_, 1);\n  return value;\n}\n\ndouble LuaParameterDictionary::GetDouble(const string& key) {\n  CheckHasKeyAndReference(key);\n  GetValueFromLuaTable(L_, key);\n  return PopDouble();\n}\n\ndouble LuaParameterDictionary::PopDouble() const {\n  CHECK(lua_isnumber(L_, -1)) << \"Top of stack is not a number value.\";\n  const double value = lua_tonumber(L_, -1);\n  lua_pop(L_, 1);\n  return value;\n}\n\nint LuaParameterDictionary::GetInt(const string& key) {\n  CheckHasKeyAndReference(key);\n  GetValueFromLuaTable(L_, key);\n  return PopInt();\n}\n\nint LuaParameterDictionary::PopInt() const {\n  CHECK(lua_isnumber(L_, -1)) << \"Top of stack is not a number value.\";\n  const int value = lua_tointeger(L_, -1);\n  lua_pop(L_, 1);\n  return value;\n}\n\nbool LuaParameterDictionary::GetBool(const string& key) {\n  CheckHasKeyAndReference(key);\n  GetValueFromLuaTable(L_, key);\n  return PopBool();\n}\n\nbool LuaParameterDictionary::PopBool() const {\n  CHECK(lua_isboolean(L_, -1)) << \"Top of stack is not a boolean value.\";\n  const bool value = lua_toboolean(L_, -1);\n  lua_pop(L_, 1);\n  return value;\n}\n\nstd::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::GetDictionary(\n    const string& key) {\n  CheckHasKeyAndReference(key);\n  GetValueFromLuaTable(L_, key);\n  return PopDictionary(reference_count_);\n}\n\nstd::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::PopDictionary(\n    ReferenceCount reference_count) const {\n  CheckTableIsAtTopOfStack(L_);\n  std::unique_ptr<LuaParameterDictionary> value(\n      new LuaParameterDictionary(L_, reference_count, file_resolver_));\n  // The constructor lua_xmove()s the value, no need to pop it.\n  CheckTableIsAtTopOfStack(L_);\n  return value;\n}\n\nstring LuaParameterDictionary::DoToString(const string& indent) const {\n  string result = \"{\";\n  bool dictionary_is_empty = true;\n\n  const auto top_of_stack_to_string = [this, indent,\n                                       &dictionary_is_empty]() -> string {\n    dictionary_is_empty = false;\n\n    const int value_type = lua_type(L_, -1);\n    switch (value_type) {\n      case LUA_TBOOLEAN:\n        return PopBool() ? \"true\" : \"false\";\n        break;\n      case LUA_TSTRING:\n        return PopString(Quoted::YES);\n        break;\n      case LUA_TNUMBER: {\n        const double value = PopDouble();\n        if (std::isinf(value)) {\n          return value < 0 ? \"-math.huge\" : \"math.huge\";\n        } else {\n          return std::to_string(value);\n        }\n      } break;\n      case LUA_TTABLE: {\n        std::unique_ptr<LuaParameterDictionary> subdict(\n            PopDictionary(ReferenceCount::NO));\n        return subdict->DoToString(indent + \"  \");\n      } break;\n      default:\n        LOG(FATAL) << \"Unhandled type \" << lua_typename(L_, value_type);\n    }\n  };\n\n  // Integer (array) keys.\n  for (int i = 1; i; ++i) {\n    GetValueFromLuaTable(L_, i);\n    if (lua_isnil(L_, -1)) {\n      lua_pop(L_, 1);\n      break;\n    }\n    result.append(\"\\n\");\n    result.append(indent);\n    result.append(\"  \");\n    result.append(top_of_stack_to_string());\n    result.append(\",\");\n  }\n\n  // String keys.\n  std::vector<string> keys = GetKeys();\n  if (!keys.empty()) {\n    std::sort(keys.begin(), keys.end());\n    for (const string& key : keys) {\n      GetValueFromLuaTable(L_, key);\n      result.append(\"\\n\");\n      result.append(indent);\n      result.append(\"  \");\n      result.append(key);\n      result.append(\" = \");\n      result.append(top_of_stack_to_string());\n      result.append(\",\");\n    }\n  }\n  result.append(\"\\n\");\n  result.append(indent);\n  result.append(\"}\");\n\n  if (dictionary_is_empty) {\n    return \"{}\";\n  }\n  return result;\n}\n\nstring LuaParameterDictionary::ToString() const { return DoToString(\"\"); }\n\nstd::vector<double> LuaParameterDictionary::GetArrayValuesAsDoubles() {\n  std::vector<double> values;\n  GetArrayValues(L_, [&values, this] { values.push_back(PopDouble()); });\n  return values;\n}\n\nstd::vector<std::unique_ptr<LuaParameterDictionary>>\nLuaParameterDictionary::GetArrayValuesAsDictionaries() {\n  std::vector<std::unique_ptr<LuaParameterDictionary>> values;\n  GetArrayValues(L_, [&values, this] {\n    values.push_back(PopDictionary(reference_count_));\n  });\n  return values;\n}\n\nstd::vector<string> LuaParameterDictionary::GetArrayValuesAsStrings() {\n  std::vector<string> values;\n  GetArrayValues(L_,\n                 [&values, this] { values.push_back(PopString(Quoted::NO)); });\n  return values;\n}\n\nvoid LuaParameterDictionary::CheckHasKey(const string& key) const {\n  CHECK(HasKey(key)) << \"Key '\" << key << \"' not in dictionary:\\n\"\n                     << ToString();\n}\n\nvoid LuaParameterDictionary::CheckHasKeyAndReference(const string& key) {\n  CheckHasKey(key);\n  reference_counts_[key]++;\n}\n\nvoid LuaParameterDictionary::CheckAllKeysWereUsedExactlyOnceAndReset() {\n  for (const auto& key : GetKeys()) {\n    CHECK_EQ(1, reference_counts_.count(key))\n        << \"Key '\" << key << \"' was used the wrong number of times.\";\n    CHECK_EQ(1, reference_counts_.at(key))\n        << \"Key '\" << key << \"' was used the wrong number of times.\";\n  }\n  reference_counts_.clear();\n}\n\nint LuaParameterDictionary::GetNonNegativeInt(const string& key) {\n  const int temp = GetInt(key);  // Will increase reference count.\n  CHECK_GE(temp, 0) << temp << \" is negative.\";\n  return temp;\n}\n\n// Lua function to run a script in the current Lua context. Takes the filename\n// as its only argument.\nint LuaParameterDictionary::LuaInclude(lua_State* L) {\n  CHECK_EQ(lua_gettop(L), 1);\n  CHECK(lua_isstring(L, -1)) << \"include takes a filename.\";\n\n  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);\n  const string basename = lua_tostring(L, -1);\n  const string filename =\n      parameter_dictionary->file_resolver_->GetFullPathOrDie(basename);\n  if (std::find(parameter_dictionary->included_files_.begin(),\n                parameter_dictionary->included_files_.end(),\n                filename) != parameter_dictionary->included_files_.end()) {\n    string error_msg = \"Tried to include \" + filename +\n                       \" twice. Already included files in order of inclusion: \";\n    for (const string& filename : parameter_dictionary->included_files_) {\n      error_msg.append(filename);\n      error_msg.append(\"\\n\");\n    }\n    LOG(FATAL) << error_msg;\n  }\n  parameter_dictionary->included_files_.push_back(filename);\n  lua_pop(L, 1);\n  CHECK_EQ(lua_gettop(L), 0);\n\n  const string content =\n      parameter_dictionary->file_resolver_->GetFileContentOrDie(basename);\n  CheckForLuaErrors(\n      L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));\n  CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));\n\n  return lua_gettop(L);\n}\n\n// Lua function to read a file into a string.\nint LuaParameterDictionary::LuaRead(lua_State* L) {\n  CHECK_EQ(lua_gettop(L), 1);\n  CHECK(lua_isstring(L, -1)) << \"read takes a filename.\";\n\n  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);\n  const string file_content =\n      parameter_dictionary->file_resolver_->GetFileContentOrDie(\n          lua_tostring(L, -1));\n  lua_pushstring(L, file_content.c_str());\n  return 1;\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/lua_parameter_dictionary.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_H_\n#define CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_H_\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/lua.h\"\n#include \"cartographer/common/port.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace common {\n/*\nFileResolver是一个文件解析的虚基类,利用lua加载文件内容\n\n*/\n// Resolves file paths and file content for the Lua 'read' and 'include'\n// functions. Use this to configure where those functions load other files from.\nclass FileResolver {\n public:\n  virtual ~FileResolver() {}\n  virtual string GetFullPathOrDie(const string& basename) = 0;\n  virtual string GetFileContentOrDie(const string& basename) = 0;\n};\n\n/*\nLuaParameterDictionary类继承自FileResolver,\n目的是从lua配置文件加载参数.该类不可拷贝不可赋值.\n\n1,构造函数从file_resolver中加载一个带lua table的字典dictionary\n2,\ncode是以字符串代表的lua代码code\n\n*/\n// A parameter dictionary that gets loaded from Lua code.\nclass LuaParameterDictionary {\n public:\n  // Constructs the dictionary from a Lua Table specification.\n  LuaParameterDictionary(const string& code,\n                         std::unique_ptr<FileResolver> file_resolver);\n\n  LuaParameterDictionary(const LuaParameterDictionary&) = delete;\n  LuaParameterDictionary& operator=(const LuaParameterDictionary&) = delete;\n\n  // Constructs a LuaParameterDictionary without reference counting.\n  static std::unique_ptr<LuaParameterDictionary> NonReferenceCounted(\n      const string& code, std::unique_ptr<FileResolver> file_resolver);\n\n  ~LuaParameterDictionary();\n\n  // Returns all available keys.\n  std::vector<string> GetKeys() const;\n\n  // Returns true if the key is in this dictionary.\n  bool HasKey(const string& key) const;\n\n  // These methods CHECK() that the 'key' exists.\n  string GetString(const string& key);\n  double GetDouble(const string& key);\n  int GetInt(const string& key);\n  bool GetBool(const string& key);\n  std::unique_ptr<LuaParameterDictionary> GetDictionary(const string& key);\n\n  // Gets an int from the dictionary and CHECK()s that it is non-negative.\n  int GetNonNegativeInt(const string& key);\n\n  // Returns a string representation for this LuaParameterDictionary.\n  string ToString() const;\n\n  // Returns the values of the keys '1', '2', '3' as the given types.\n  std::vector<double> GetArrayValuesAsDoubles();\n  std::vector<string> GetArrayValuesAsStrings();\n  std::vector<std::unique_ptr<LuaParameterDictionary>>\n  GetArrayValuesAsDictionaries();\n\n private:\n  enum class ReferenceCount { YES, NO };\n  LuaParameterDictionary(const string& code, ReferenceCount reference_count,\n                         std::unique_ptr<FileResolver> file_resolver);\n\n  // For GetDictionary().\n  LuaParameterDictionary(lua_State* L, ReferenceCount reference_count,\n                         std::shared_ptr<FileResolver> file_resolver);\n\n  // Function that recurses to keep track of indent for ToString().\n  string DoToString(const string& indent) const;\n\n  // Pop the top of the stack and CHECKs that the type is correct.\n  double PopDouble() const;\n  int PopInt() const;\n  bool PopBool() const;\n\n  // Pop the top of the stack and CHECKs that it is a string. The returned value\n  // is either quoted to be suitable to be read back by a Lua interpretor or\n  // not.\n  enum class Quoted { YES, NO };\n  string PopString(Quoted quoted) const;\n\n  // Creates a LuaParameterDictionary from the Lua table at the top of the\n  // stack, either with or without reference counting.\n  std::unique_ptr<LuaParameterDictionary> PopDictionary(\n      ReferenceCount reference_count) const;\n\n  // CHECK() that 'key' is in the dictionary.\n  void CheckHasKey(const string& key) const;\n\n  // CHECK() that 'key' is in this dictionary and reference it as being used.\n  void CheckHasKeyAndReference(const string& key);\n\n  // If desired, this can be called in the destructor of a derived class. It\n  // will CHECK() that all keys defined in the configuration have been used\n  // exactly once and resets the reference counter.\n  void CheckAllKeysWereUsedExactlyOnceAndReset();\n\n  // Reads a file into a Lua string.\n  static int LuaRead(lua_State* L);\n\n  // Handles inclusion of other Lua files and prevents double inclusion.\n  static int LuaInclude(lua_State* L);\n\n  lua_State* L_;  // The name is by convention in the Lua World.\n  int index_into_reference_table_;\n\n  // This is shared with all the sub dictionaries.\n  const std::shared_ptr<FileResolver> file_resolver_;\n\n  // If true will check that all keys were used on destruction.\n  const ReferenceCount reference_count_;\n\n  // This is modified with every call to Get* in order to verify that all\n  // parameters are read exactly once.\n  std::map<string, int> reference_counts_;\n\n  // List of all included files in order of inclusion. Used to prevent double\n  // inclusion.\n  std::vector<string> included_files_;\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_H_\n"
  },
  {
    "path": "common/lua_parameter_dictionary_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace common {\nnamespace {\n\n/*返回一个智能指针,指向DummyFileResolver*/\nstd::unique_ptr<LuaParameterDictionary> MakeNonReferenceCounted(\n    const string& code) {\n  return LuaParameterDictionary::NonReferenceCounted(\n      code, common::make_unique<DummyFileResolver>());\n}\n\nclass LuaParameterDictionaryTest : public ::testing::Test {\n protected:\n  void ReferenceAllKeysAsIntegers(LuaParameterDictionary* dict) {\n    for (const string& key : dict->GetKeys()) {\n      dict->GetInt(key);\n    }\n  }\n};\n\nTEST_F(LuaParameterDictionaryTest, GetInt) {\n  auto dict = MakeDictionary(\"return { blah = 100 }\");\n  ASSERT_EQ(dict->GetInt(\"blah\"), 100);             // blah==100\n}\n\nTEST_F(LuaParameterDictionaryTest, GetString) {\n  auto dict = MakeDictionary(\"return { blah = 'is_a_string' }\\n\");\n  ASSERT_EQ(dict->GetString(\"blah\"), \"is_a_string\");//blah = 'is_a_string'  \n}\n\nTEST_F(LuaParameterDictionaryTest, GetDouble) {\n  auto dict = MakeDictionary(\"return { blah = 3.1415 }\");\n  ASSERT_DOUBLE_EQ(dict->GetDouble(\"blah\"), 3.1415); //blah = 3.1415\n}\n\nTEST_F(LuaParameterDictionaryTest, GetBoolTrue) {\n  auto dict = MakeDictionary(\"return { blah = true }\");\n  ASSERT_TRUE(dict->GetBool(\"blah\"));                //blah = true\n}\n\nTEST_F(LuaParameterDictionaryTest, GetBoolFalse) {\n  auto dict = MakeDictionary(\"return { blah = false }\");\n  ASSERT_FALSE(dict->GetBool(\"blah\"));              // blah = false \n}\n\nTEST_F(LuaParameterDictionaryTest, GetDictionary) {\n  auto dict =\n      MakeDictionary(\"return { blah = { blue = 100, red = 200 }, fasel = 10 }\");\n\n  //sub_dict 是一个子字典\n  std::unique_ptr<LuaParameterDictionary> sub_dict(dict->GetDictionary(\"blah\"));\n  std::vector<string> keys = sub_dict->GetKeys();//blah下的key\n  ASSERT_EQ(keys.size(), 2);                     //2个key,blue,red\n  std::sort(keys.begin(), keys.end());           //顺序不定\n  ASSERT_EQ(keys[0], \"blue\");\n  ASSERT_EQ(keys[1], \"red\");\n  ASSERT_TRUE(sub_dict->HasKey(\"blue\"));\n  ASSERT_TRUE(sub_dict->HasKey(\"red\"));\n  ASSERT_EQ(sub_dict->GetInt(\"blue\"), 100);      //blue==100\n  ASSERT_EQ(sub_dict->GetInt(\"red\"), 200);       //red==10\n\n  ASSERT_EQ(dict->GetString(\"fasel\"), \"10\");\n}\n\nTEST_F(LuaParameterDictionaryTest, GetKeys) {\n  auto dict = MakeDictionary(\"return { blah = 100, blah1 = 200}\");\n\n  std::vector<string> keys = dict->GetKeys(); //返回key组成的string\n  ASSERT_EQ(keys.size(), 2);\n  std::sort(keys.begin(), keys.end());\n  ASSERT_EQ(keys[0], \"blah\");\n  ASSERT_EQ(keys[1], \"blah1\");\n\n  ReferenceAllKeysAsIntegers(dict.get());\n}\n\nTEST_F(LuaParameterDictionaryTest, NonExistingKey) {\n  auto dict = MakeDictionary(\"return { blah = 100 }\");\n  ReferenceAllKeysAsIntegers(dict.get());\n  ASSERT_DEATH(dict->GetInt(\"blah_fasel\"), \"Key.* not in dictionary.\");//key不存在\n}\n\nTEST_F(LuaParameterDictionaryTest, UintNegative) {\n  auto dict = MakeDictionary(\"return { blah = -100}\");\n  ASSERT_DEATH(dict->GetNonNegativeInt(\"blah\"), \".*-100 is negative.\");//key对应的value是负数\n  ReferenceAllKeysAsIntegers(dict.get());\n}\n\nTEST_F(LuaParameterDictionaryTest, ToString) {\n  auto dict = MakeDictionary(R\"(return {\n  ceta = { yolo = \"hurray\" },\n  fasel = 1234.456786,\n  fasel1 = -math.huge,\n  fasel2 = math.huge,\n  blubber = 123,\n  blub = 'hello',\n  alpha = true,\n  alpha1 = false,\n  })\");\n\n  const string golden = R\"({\n  alpha = true,\n  alpha1 = false,\n  blub = \"hello\",\n  blubber = 123.000000,\n  ceta = {\n    yolo = \"hurray\",\n  },\n  fasel = 1234.456786,\n  fasel1 = -math.huge,\n  fasel2 = math.huge,\n})\";\n  EXPECT_EQ(golden, dict->ToString()); //相等,dict 开始的return 不包含在内\n\n  auto dict1 = MakeDictionary(\"return \" + dict->ToString());\n\n  EXPECT_EQ(dict1->GetBool(\"alpha\"), true);\n  EXPECT_EQ(dict1->GetBool(\"alpha1\"), false);\n  EXPECT_EQ(dict1->GetInt(\"blubber\"), 123);  //获取整数\n  EXPECT_EQ(dict1->GetString(\"blub\"), \"hello\");\n  EXPECT_EQ(dict1->GetDictionary(\"ceta\")->GetString(\"yolo\"), \"hurray\");\n  EXPECT_NEAR(dict1->GetDouble(\"fasel\"), 1234.456786, 1e-3);\n  EXPECT_TRUE(std::isinf(-dict1->GetDouble(\"fasel1\")));\n  EXPECT_TRUE(std::isinf(dict1->GetDouble(\"fasel2\")));\n\n  EXPECT_EQ(dict->GetBool(\"alpha\"), true);\n  EXPECT_EQ(dict->GetBool(\"alpha1\"), false);\n  EXPECT_EQ(dict->GetInt(\"blubber\"), 123);\n  EXPECT_EQ(dict->GetString(\"blub\"), \"hello\");\n  EXPECT_EQ(dict->GetDictionary(\"ceta\")->GetString(\"yolo\"), \"hurray\");\n  EXPECT_NEAR(dict->GetDouble(\"fasel\"), 1234.456786, 1e-3);\n  EXPECT_TRUE(std::isinf(-dict->GetDouble(\"fasel1\")));\n  EXPECT_TRUE(std::isinf(dict->GetDouble(\"fasel2\")));\n}\n\nTEST_F(LuaParameterDictionaryTest, ToStringForArrays) {\n  auto dict = MakeNonReferenceCounted(\n      R\"(return {\n      \"blub\", 3, 3.1,\n      foo = \"ups\",\n  })\");\n\n  const string golden = R\"({\n  \"blub\",\n  3.000000,\n  3.100000,\n  foo = \"ups\",\n})\";\n  EXPECT_EQ(golden, dict->ToString());\n}\n\nTEST_F(LuaParameterDictionaryTest, GetArrayValuesAsStrings) {\n  auto dict = MakeDictionary(\"return { 'a', 'b', 'c' }\");\n  EXPECT_EQ(0, dict->GetKeys().size());\n  const std::vector<string> values = dict->GetArrayValuesAsStrings();\n  EXPECT_EQ(3, values.size());\n  EXPECT_EQ(\"a\", values[0]);\n  EXPECT_EQ(\"b\", values[1]);\n  EXPECT_EQ(\"c\", values[2]);\n}\n\nTEST_F(LuaParameterDictionaryTest, GetArrayValuesAsDoubles) {\n  auto dict = MakeDictionary(\"return { 1., 2., 3. }\");\n  EXPECT_EQ(0, dict->GetKeys().size());\n  const std::vector<double> values = dict->GetArrayValuesAsDoubles();\n  EXPECT_EQ(3, values.size());\n  EXPECT_NEAR(1., values[0], 1e-7);\n  EXPECT_NEAR(2., values[1], 1e-7);\n  EXPECT_NEAR(3., values[2], 1e-7);\n}\n\nTEST_F(LuaParameterDictionaryTest, GetArrayValuesAsDictionaries) {\n  auto dict = MakeDictionary(\"return { { a = 1 }, { b = 3 } }\");\n  EXPECT_EQ(0, dict->GetKeys().size());\n  const std::vector<std::unique_ptr<LuaParameterDictionary>> values =\n      dict->GetArrayValuesAsDictionaries();\n  EXPECT_EQ(2, values.size());\n  EXPECT_EQ(1., values[0]->GetInt(\"a\"));\n  EXPECT_EQ(3., values[1]->GetInt(\"b\"));\n}\n\nTEST_F(LuaParameterDictionaryTest, TestChooseTrue) {\n  auto dict = MakeDictionary(\"return { a = choose(true, 1, 0) }\");\n  EXPECT_EQ(1, dict->GetInt(\"a\"));\n}\n\nTEST_F(LuaParameterDictionaryTest, TestChoseFalse) {\n  auto dict = MakeDictionary(\"return { a = choose(false, 1, 0) }\");\n  EXPECT_EQ(0, dict->GetInt(\"a\"));\n}\n\nTEST_F(LuaParameterDictionaryTest, TestChooseInvalidArgument) {\n  EXPECT_DEATH(MakeDictionary(\"return { a = choose('truish', 1, 0) }\"),\n               \"condition is not a boolean value.\");\n}\n\n}  // namespace\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/lua_parameter_dictionary_test_helpers.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_TEST_HELPERS_H_\n#define CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_TEST_HELPERS_H_\n\n#include <memory>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/port.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace common {\n\nclass DummyFileResolver : public FileResolver {\n public:\n  DummyFileResolver() {}\n\n  DummyFileResolver(const DummyFileResolver&) = delete;\n  DummyFileResolver& operator=(const DummyFileResolver&) = delete;\n\n  ~DummyFileResolver() override {}\n\n  string GetFileContentOrDie(const string& unused_basename) override {\n    LOG(FATAL) << \"Not implemented\";\n  }\n\n  string GetFullPathOrDie(const string& unused_basename) override {\n    LOG(FATAL) << \"Not implemented\";\n  }\n};\n\nstd::unique_ptr<LuaParameterDictionary> MakeDictionary(const string& code) {\n  return common::make_unique<LuaParameterDictionary>(\n      code, common::make_unique<DummyFileResolver>());\n}\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_LUA_PARAMETER_DICTIONARY_TEST_HELPERS_H_\n"
  },
  {
    "path": "common/make_unique.h",
    "content": "\n\n#ifndef CARTOGRAPHER_COMMON_MAKE_UNIQUE_H_\n#define CARTOGRAPHER_COMMON_MAKE_UNIQUE_H_\n\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n#include <utility>\n\nnamespace cartographer {\nnamespace common {\n\n/*\nmake_unique.h在不支持c++14的环境下实现 std::make_unique的功能\n实现细节:完美转发和移动语义\n\n*/\n// Implementation of c++14's std::make_unique, taken from\n// https://isocpp.org/files/papers/N3656.txt\ntemplate <class T>\nstruct _Unique_if {\n  typedef std::unique_ptr<T> _Single_object;\n};\n\ntemplate <class T>\nstruct _Unique_if<T[]> {\n  typedef std::unique_ptr<T[]> _Unknown_bound; //不支持数组(不定长)\n};\n\ntemplate <class T, size_t N>\nstruct _Unique_if<T[N]> {                      //不支持数组(定长)\n  typedef void _Known_bound;\n};\n\ntemplate <class T, class... Args>\ntypename _Unique_if<T>::_Single_object make_unique(Args&&... args) {\n  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));  //完美转发参数\n}\n\ntemplate <class T>\ntypename _Unique_if<T>::_Unknown_bound make_unique(size_t n) {\n  typedef typename std::remove_extent<T>::type U;\n  return std::unique_ptr<T>(new U[n]());\n}\n\ntemplate <class T, class... Args>\ntypename _Unique_if<T>::_Known_bound make_unique(Args&&...) = delete;//不能使用定长数组\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_MAKE_UNIQUE_H_\n\n/*\n完美转发:\n完美的传递函数参数而不修改参数类型，左值依然是左值，右值依然是右值。\n*/"
  },
  {
    "path": "common/make_unique_test.cc",
    "content": "\n#include <iostream>\n#include <string>\n#include \"cartographer/common/make_unique.h\"\n#include \"gtest/gtest.h\"\n//using namespace std;\nusing std::cout;\nusing std::cin;\nusing std::endl;\nusing std::string;\nnamespace cartographer {\nnamespace common {\nTEST(MAKE_UNIQUE ,make_unique) {\n    cout << *make_unique<int>() << endl;\n    cout << *make_unique<int>(1729) << endl;\n    cout << \"\\\"\" << *make_unique<string>() << \"\\\"\" << endl;\n    cout << \"\\\"\" << *make_unique<string>(\"meow\") << \"\\\"\" << endl;\n    cout << \"\\\"\" << *make_unique<string>(6, 'z') << \"\\\"\" << endl;\n\n    auto up = make_unique<int[]>(5);\n\n    for (int i = 0; i < 5; ++i) {\n        cout << up[i] << \" \";\n    }\n\n    cout << endl;\n\n    #if defined(ERROR1)\n        auto up1 = make_unique<string[]>(\"error\");\n    #elif defined(ERROR2)\n        auto up2 = make_unique<int[]>(10, 20, 30, 40);\n    #elif defined(ERROR3)\n        auto up3 = make_unique<int[5]>();\n    #elif defined(ERROR4)\n        auto up4 = make_unique<int[5]>(11, 22, 33, 44, 55);\n    #endif  \n}\n\n/*\n输出:\n\n0\n1729\n\"\"\n\"meow\"\n\"zzzzzz\"\n0 0 0 0 0\n\n\n*/\n}\n}"
  },
  {
    "path": "common/math.h",
    "content": "/*\ncommon/math.h文件主要实现数学计算，包括：\n区间截断.求n次方.求平方.幅度角度转换.归一化.反正切值\n*/\n\n#ifndef CARTOGRAPHER_COMMON_MATH_H_\n#define CARTOGRAPHER_COMMON_MATH_H_\n\n#include <cmath>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/port.h\"\n#include \"ceres/ceres.h\"\n\nnamespace cartographer {\nnamespace common {\n\n// Clamps 'value' to be in the range ['min', 'max'].\n//将val截取到区间min至max中.\ntemplate <typename T>\nT Clamp(const T value, const T min, const T max) {\n  if (value > max) {\n    return max;\n  }\n  if (value < min) {\n    return min;\n  }\n  return value;\n}\n\n// Calculates 'base'^'exponent'. 计算base的exp次方\ntemplate <typename T>\nconstexpr T Power(T base, int exponent) {\n  return (exponent != 0) ? base * Power(base, exponent - 1) : T(1);\n}\n\n// Calculates a^2.  特化,求平方\ntemplate <typename T>\nconstexpr T Pow2(T a) {\n  return Power(a, 2);\n}\n\n// Converts from degrees to radians.角度到弧度的转换. 60° -> pi/3\nconstexpr double DegToRad(double deg) { return M_PI * deg / 180.; }\n\n// Converts form radians to degrees.弧度到角度的转换, pi/3 -> 60°\nconstexpr double RadToDeg(double rad) { return 180. * rad / M_PI; }\n\n// Bring the 'difference' between two angles into [-pi; pi]\n//将角度差转换为[-pi;pi] \ntemplate <typename T>\nT NormalizeAngleDifference(T difference) {\n  while (difference > M_PI) {\n    difference -= T(2. * M_PI);\n  }\n  while (difference < -M_PI) {\n    difference += T(2. * M_PI);\n  }\n  return difference;\n}\n\n\n/*\natan2 返回原点至点(x,y)的方位角，即与 x 轴的夹角，\n也可以理解为计算复数 x+yi 的辐角,范围是[-pi,pi]\nATAN2(1,1) -> pi/4:以弧度表示点(1,1)的反正切值，即pi/4(0.785398)\n*/\ntemplate <typename T>\nT atan2(const Eigen::Matrix<T, 2, 1>& vector) {  //范围是[-pi,pi]\n  return ceres::atan2(vector.y(), vector.x());\n}\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_MATH_H_\n"
  },
  {
    "path": "common/math_test.cc",
    "content": "\n\n#include \"cartographer/common/math.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace common {\nnamespace {\n\nTEST(MathTest, testPower) {\n  EXPECT_EQ(0., Power(0, 42));// 0的42次方==0\n  EXPECT_EQ(1., Power(0, 0)); //0^0 ==0\n  EXPECT_EQ(1., Power(1, 0)); // 1^0 ==0\n  EXPECT_EQ(1., Power(1, 42));//1^42==1\n  EXPECT_EQ(4., Power(2, 2)); //2^2==4\n}\n\nTEST(MathTest, testPow2) {\n  EXPECT_EQ(0., Pow2(0)); //0^2==0\n  EXPECT_EQ(1., Pow2(1)); //1^2==1\n  EXPECT_EQ(4., Pow2(2)); //2^2==4\n  EXPECT_EQ(49., Pow2(7));//7^2==49\n}\n\nTEST(MathTest, testDeg2rad) {\n  EXPECT_NEAR(M_PI, DegToRad(180.), 1e-9);\t\t\t\t// 180° ==pi\n  EXPECT_NEAR(2. * M_PI, DegToRad(360. - 1e-9), 1e-6);//360° ==2pi\n}\n\nTEST(MathTest, testRad2deg) {\n  EXPECT_NEAR(180., RadToDeg(M_PI), 1e-9);\t\t\t   //pi ==180°\n  EXPECT_NEAR(360., RadToDeg(2. * M_PI - 1e-9), 1e-6);//2pi ==360°\n}\n\nTEST(MathTest, testNormalizeAngleDifference) {\n  EXPECT_NEAR(0., NormalizeAngleDifference(0.), 1e-9);     \t\t//0==0\n  EXPECT_NEAR(M_PI, NormalizeAngleDifference(M_PI), 1e-9); \t\t//pi==oi\n  EXPECT_NEAR(-M_PI, NormalizeAngleDifference(-M_PI), 1e-9);\t//-pi==-pi\n  EXPECT_NEAR(0., NormalizeAngleDifference(2. * M_PI), 1e-9);   //2pi==0\n  EXPECT_NEAR(M_PI, NormalizeAngleDifference(5. * M_PI), 1e-9); //5pi==pi\n  EXPECT_NEAR(-M_PI, NormalizeAngleDifference(-5. * M_PI), 1e-9);//-5pi=-pi\n}\n\n}  // namespace\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/mutex.h",
    "content": "\n/*\n\ncommon/mutex.h主要是对c++11 的mutex的封装。\n\n*/\n#ifndef CARTOGRAPHER_COMMON_MUTEX_H_\n#define CARTOGRAPHER_COMMON_MUTEX_H_\n\n#include <condition_variable>\n#include <mutex>\n\n#include \"cartographer/common/time.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*不使用clang时,以下宏定义全部为空操作.故看见宏定义时可暂时忽略*/\n// Enable thread safety attributes only with clang.\n// The attributes can be safely erased when compiling with other compilers.\n#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)\n#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))\n#else\n#define THREAD_ANNOTATION_ATTRIBUTE__(x)  // no-op,空操作\n#endif\n\n#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))\n\n#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)\n\n#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))\n\n#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))\n\n#define REQUIRES(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))\n\n#define ACQUIRE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))\n\n#define RELEASE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))\n\n#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))\n\n#define NO_THREAD_SAFETY_ANALYSIS \\\n  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)\n\n/*\nMutex是c++11 mutex的封装，\nMutex类有一个内部类Locker。\n\nLocker 是一个RAII的类型.\n在构造Locker时,对mutex上锁,在析构Locker时对mutex解锁.\n本质是使用std::unique_lock和std::condition_variable实现的\n\nLocker类提供2个成员函数Await()和AwaitWithTimeout()\n功能是利用c++11的条件变量和unique_lock实现在谓词predicate为真的情况下对mutex解锁。\n*/\n\n\n// Defines an annotated mutex that can only be locked through its scoped locker\n// implementation.\nclass CAPABILITY(\"mutex\") Mutex {\n public:\n\n  // A RAII class that acquires a mutex in its constructor, and\n  // releases it in its destructor. It also implements waiting functionality on\n  // conditions that get checked whenever the mutex is released.\n  class SCOPED_CAPABILITY Locker {\n   public:\n    Locker(Mutex* mutex) ACQUIRE(mutex) : mutex_(mutex), lock_(mutex->mutex_) {}//上锁\n\n    ~Locker() RELEASE() {\n      lock_.unlock();                 //解锁\n      mutex_->condition_.notify_all();//条件变量通知解锁\n    }\n\n    template <typename Predicate>\n    void Await(Predicate predicate) REQUIRES(this) { \n      /*wait()实质是分3步：\n      1.对lock_解锁\n      2.等待predicate谓词为真，此时调用端阻塞。\n      3.对lock_ 重新上锁。\n      */\n      mutex_->condition_.wait(lock_, predicate);\n    }\n\n    template <typename Predicate>\n    bool AwaitWithTimeout(Predicate predicate, common::Duration timeout)\n        REQUIRES(this) {       //等待谓词为真或者直到超时timeout返回。\n      return mutex_->condition_.wait_for(lock_, timeout, predicate);\n    }\n\n   private:\n    Mutex* mutex_;\n    std::unique_lock<std::mutex> lock_;\n  };\n\n private:\n  std::condition_variable condition_;\n  std::mutex mutex_;\n};\n\nusing MutexLocker = Mutex::Locker;\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_MUTEX_H_\n\n/*\n谓词.断言.Predicate:\n一个用于比较的函数（或函数对象），输入两个T类型，返回bool类型。\n*/"
  },
  {
    "path": "common/port.h",
    "content": "\n/*\n  common-port.h文件主要实现2大功能：\n  1，使用std::lround对浮点数进行四舍五入取整运算\n  2，利用boost的iostreams/filter/gzip对字符串压缩与解压缩\n*/\n\n#ifndef CARTOGRAPHER_COMMON_PORT_H_\n#define CARTOGRAPHER_COMMON_PORT_H_\n\n#include <cinttypes>\n#include <cmath>\n#include <string>\n\n#include <boost/iostreams/device/back_inserter.hpp>\n#include <boost/iostreams/filter/gzip.hpp>     //包含多种解压与压缩算法\n#include <boost/iostreams/filtering_stream.hpp>//配合filter实现流过滤\n\nusing int8 = int8_t;\nusing int16 = int16_t;\nusing int32 = int32_t;\nusing int64 = int64_t;\nusing uint8 = uint8_t;\nusing uint16 = uint16_t;\nusing uint32 = uint32_t;\nusing uint64 = uint64_t;\n\nusing std::string;\nnamespace cartographer {\nnamespace common {\n\n/*\nlround(+2.3) = 2  lround(+2.5) = 3  lround(+2.7) = 3\nlround(-2.3) = -2  lround(-2.5) = -3  lround(-2.7) = -3\nlround(-0.0) = 0\n*/\ninline int RoundToInt(const float x) { return std::lround(x); }//四舍五入取整运算\n\ninline int RoundToInt(const double x) { return std::lround(x); }\n\ninline int64 RoundToInt64(const float x) { return std::lround(x); }\n\ninline int64 RoundToInt64(const double x) { return std::lround(x); }\n\n/*\n利用gzip_compressor对string进行压缩，第一参数是未压缩string，第二参数是完成压缩string\n*/\ninline void FastGzipString(const string& uncompressed, string* compressed) {\n  boost::iostreams::filtering_ostream out;//创建过滤流\n  out.push(\n      boost::iostreams::gzip_compressor(boost::iostreams::zlib::best_speed));//使用快速压缩算法\n  out.push(boost::iostreams::back_inserter(*compressed));//对compressed 使用后插迭代器\n  boost::iostreams::write(out,\n                          reinterpret_cast<const char*>(uncompressed.data()),\n                          uncompressed.size());//压缩 char *,插入compressed\n}\n\n/*\n利用gzip_decompressor解压缩string，第一参数是待解压的string，第二参数是解压后的string\n*/\ninline void FastGunzipString(const string& compressed, string* decompressed) {\n  boost::iostreams::filtering_ostream out;        //创建过滤流\n  out.push(boost::iostreams::gzip_decompressor());//指定解压缩算法\n  out.push(boost::iostreams::back_inserter(*decompressed));//对decompressed使用后插入迭代器\n  boost::iostreams::write(out, reinterpret_cast<const char*>(compressed.data()),\n                          compressed.size());//解压缩char*，插入decompressed\n}\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_PORT_H_\n\n\n/*\n\nc++11 已经支持back_inserter。\nstd::back_inserter执行push_back操作, 返回值back_insert_iterator, 并实现自增.\n具体用法可见 port_le_test.cpp\n*/\n\n\n/*\nreinterpret_cast的转换格式：reinterpret_cast <type-id> (expression)\n允许将任何指针类型转换为指定的指针类型\n*/\n\n\n/*\nboost::iostreams/filtering_ostream用法：\n \n压缩时\nfiltering_ostream out;\nout.push(gzip_compressor()); //gzip OutputFilter\nout.push(bzip2_compressor());//bzip2 OutputFilter\nout.push(boost::iostreams::file_sink(\"test.txt\"));//以file_sink device结束\n将流的数据先按gzip压缩，然后再按bzip2压缩之后，才输出到text.txt文件中。\n\n解压时\nfiltering_istream in;\nin.push(gzip_decompressor());/gzip InputFilter\nin.push(bzip2_decompressor());/bzip2 InputFilter\nin.push(file_source(\"test.txt\"));\n先将test.txt文件中数据读出，然后按bzip2解压，然后再按gzip解压，存入in流中，正好是压缩的逆序。\n*/"
  },
  {
    "path": "common/port_le_test.cpp",
    "content": "#include <vector>\n#include <iostream>\n#include <vector> \n /*\n c++11 已经支持back_inserter。\n  */\n\t//#include<boost/iostreams/device/back_inserter.hpp>\n\nusing std::cout;\nusing std::endl;\nint main(int argc, char *argv[])\n\t{\n\t    std::vector<int> vec;\n\t    for (int i = 0 ; i < 3; ++i)\n\t\tvec.push_back(i);\n\n\t    std::vector <int>::iterator vIter;\n\t    std::cout << \"The initial vector vec is: ( \";\n\t    for (vIter = vec.begin(); vIter != vec.end(); vIter++)\n\t\tstd::cout << *vIter << \" \";\n\t    std::cout << \").\" << std::endl;\n\n\t    // Insertions can be done with template function\n\t    std::back_insert_iterator<std::vector<int> > backiter(vec);\n\t    *backiter = 30;\n\t    backiter++;\n\t    *backiter = 40;\n\n\t    // Alternatively, insertions can be done with the\n\t    // back_insert_iterator member function\n\t    std::back_inserter(vec) = 500;//从vector后插入\n\t    std::back_inserter(vec) = 600;\n\n\t    std::cout << \"After the insertions, the vector vec is: ( \";\n\t    for (vIter = vec.begin(); vIter != vec.end(); vIter++)\n\t\tstd::cout << *vIter << \" \";\n\t    std::cout << \").\" << std::endl<<\"----\\n\\n\";\n\n\n\tstd::vector<int> firstvector, secondvector;  \n    for (int i=1; i<=5; i++)  \n    {   \n        firstvector.push_back(i);   \n        secondvector.push_back(i*10);  \n    }  \n      \n    // 在firstvector向量后面加入secondvector的值.  \n    std::copy(secondvector.begin(), secondvector.end(), std::back_inserter(firstvector));  \n      \n    std::vector<int>::iterator it;  \n    for ( it = firstvector.begin(); it!= firstvector.end(); ++it )  \n        std::cout << *it << \" \";  \n    std::cout << std::endl;\n\n\t    return 0;\n\t}"
  },
  {
    "path": "common/proto/ceres_solver_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.common.proto;\n\nmessage CeresSolverOptions {\n  // Configure the Ceres solver. See the Ceres documentation for more\n  // information: https://code.google.com/p/ceres-solver/\n  optional bool use_nonmonotonic_steps = 1;\n  optional int32 max_num_iterations = 2;\n  optional int32 num_threads = 3;\n}\n"
  },
  {
    "path": "common/rate_timer.h",
    "content": "\n#ifndef CARTOGRAPHER_COMMON_RATE_TIMER_H_\n#define CARTOGRAPHER_COMMON_RATE_TIMER_H_\n\n#include <chrono>\n#include <deque>\n#include <iomanip>\n#include <numeric>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n\n/*\n * RateTimer是,脉冲频率计数类,计算在一段时间内的脉冲率\n * RateTimer不可拷贝和赋值\n * RateTimer只有一个构造函数,提供时间段Duration\n * ComputeRate()返回事件脉冲率,单位hz\n * ComputeWallTimeRateRatio()返回真实时间与墙上挂钟时间的比率\n * 内部类Event封装了某个事件发生的时间点.\n * 调用一次Pulse()即产生了一次事件\n * */\nnamespace cartographer {\nnamespace common {\n\n// Computes the rate at which pulses come in.默认模板参数是steady_clock\ntemplate <typename ClockType = std::chrono::steady_clock>\nclass RateTimer {\n public:\n  // Computes the rate at which pulses come in over 'window_duration' in wall\n  // time.\n  explicit RateTimer(const common::Duration window_duration)\n      : window_duration_(window_duration) {}\n  ~RateTimer() {}\n\n  RateTimer(const RateTimer&) = delete; //不可拷贝/不可赋值\n  RateTimer& operator=(const RateTimer&) = delete;\n\n  // Returns the pulse rate in Hz.\n  double ComputeRate() const { //计算频率\n    if (events_.empty()) {\n      return 0.;\n    }\n     //事件次数除以时间即为每秒钟发生多少次事件\n    return static_cast<double>(events_.size() - 1) / \n           common::ToSeconds((events_.back().time - events_.front().time));\n     //最晚发生的时间-最早发生的时间 (事件产生时的真实时间)\n  }\n\n  // Returns the ratio of the pulse rate (with supplied times) to the wall time\n  // rate. For example, if a sensor produces pulses at 10 Hz, but we call Pulse\n  // at 20 Hz wall time, this will return 2.\n  double ComputeWallTimeRateRatio() const { //返回比率\n    if (events_.empty()) {\n      return 0.;\n    }\n    return common::ToSeconds((events_.back().time - events_.front().time)) / //真实时间\n            //墙上挂钟时间,->调用Pulse时的系统的时间\n           std::chrono::duration_cast<std::chrono::duration<double>>(        \n               events_.back().wall_time - events_.front().wall_time)\n               .count();\n  }\n\n  // Records an event that will contribute to the computed rate.\n  void Pulse(common::Time time) { //产生一个脉冲\n    events_.push_back(Event{time, ClockType::now()});\n    while (events_.size() > 2 &&\n           (events_.back().wall_time - events_.front().wall_time) >\n               window_duration_) {\n      events_.pop_front();\n    }\n  }\n\n  // Returns a debug string representation.\n  string DebugString() const {\n    if (events_.size() < 2) {\n      return \"unknown\";\n    }\n    std::ostringstream out;\n    out << std::fixed << std::setprecision(2) << ComputeRate() << \" Hz \"\n        << DeltasDebugString() << \" (pulsed at \"\n        << ComputeWallTimeRateRatio() * 100. << \"% real time)\";\n    return out.str();\n  }\n\n private:\n  struct Event {\n    common::Time time;\n    typename ClockType::time_point wall_time;\n  };\n\n  // Computes all differences in seconds between consecutive pulses.\n  std::vector<double> ComputeDeltasInSeconds() const {\n    CHECK_GT(events_.size(), 1);\n    const size_t count = events_.size() - 1;\n    std::vector<double> result;\n    result.reserve(count);\n    for (size_t i = 0; i != count; ++i) {\n      result.push_back(\n          common::ToSeconds(events_[i + 1].time - events_[i].time));\n    }\n    return result;\n  }\n\n  // Returns the average and standard deviation of the deltas.\n  string DeltasDebugString() const {\n    const auto deltas = ComputeDeltasInSeconds();\n    const double sum = std::accumulate(deltas.begin(), deltas.end(), 0.);\n    const double mean = sum / deltas.size();\n\n    double squared_sum = 0.;\n    for (const double x : deltas) {\n      squared_sum += common::Pow2(x - mean);\n    }\n    const double sigma = std::sqrt(squared_sum / (deltas.size() - 1));\n\n    std::ostringstream out;\n    out << std::scientific << std::setprecision(2) << mean << \" s +/- \" << sigma\n        << \" s\";\n    return out.str();\n  }\n\n  std::deque<Event> events_;\n  const common::Duration window_duration_;\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_RATE_TIMER_H_\n"
  },
  {
    "path": "common/rate_timer_test.cc",
    "content": "\n#include \"cartographer/common/rate_timer.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace common {\nnamespace {\n\nTEST(RateTimerTest, ComputeRate) {\n  RateTimer<> rate_timer(common::FromSeconds(1.));//默认模板参数是steady_clock,设置时间段是1s\n  common::Time time = common::FromUniversal(42);  // 时间点,42us\n  for (int i = 0; i < 100; ++i) {                 //每隔0.1s产生一次事件,产生100次\n    rate_timer.Pulse(time);\n    time += common::FromSeconds(0.1);\n  }\n  EXPECT_NEAR(10., rate_timer.ComputeRate(), 1e-3);//频率应该等于10次每s\n}\n\nstruct SimulatedClock { /*模拟的时间clock类*/\n  using rep = std::chrono::steady_clock::rep;\n  using period = std::chrono::steady_clock::period;\n  using duration = std::chrono::steady_clock::duration;\n  using time_point = std::chrono::steady_clock::time_point;\n  static constexpr bool is_steady = true;\n\n  static time_point time;\n  static time_point now() noexcept { return time; }\n};\n\nSimulatedClock::time_point SimulatedClock::time;\n\nTEST(RateTimerTest, ComputeWallTimeRateRatio) {\n  common::Time time = common::FromUniversal(42);                 //时间点 42us\n  RateTimer<SimulatedClock> rate_timer(common::FromSeconds(1.)); //时间段是1s\n  for (int i = 0; i < 100; ++i) {                                //100次事件,0.1s一次.\n    rate_timer.Pulse(time);\n    time += common::FromSeconds(0.1);                           //加0.1s,代表事件发生的时间(调用了Pulse)\n    SimulatedClock::time +=                                     //模仿的时间,加0.05s,代表流失了0.05秒.\n        std::chrono::duration_cast<SimulatedClock::duration>(\n            std::chrono::duration<double>(0.05));\n  }\n  EXPECT_NEAR(2., rate_timer.ComputeWallTimeRateRatio(), 1e-3); //0.1/0.05==2\n}\n\n}  // namespace\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/thread_pool.cc",
    "content": "\n\n#include \"cartographer/common/thread_pool.h\"\n\n#include <unistd.h>\n#include <algorithm>\n#include <chrono>\n#include <numeric>\n\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\npool_是vector,每个线程初始化时,执行DoWork()函数.\n\n*/\nThreadPool::ThreadPool(int num_threads) {\n  MutexLocker locker(&mutex_);\n  for (int i = 0; i != num_threads; ++i) {\n    pool_.emplace_back([this]() { ThreadPool::DoWork(); });\n  }\n}\n\n/*\n只有线程池的work_queue_=0时,才能析构,此时所有的线程已经开始被pool执行,\n只有等待pool结束所有的线程执行函数(join结束),ThreadPool才能析构完成\n*/\nThreadPool::~ThreadPool() {\n  {\n    MutexLocker locker(&mutex_);\n    CHECK(running_);\n    running_ = false;\n    CHECK_EQ(work_queue_.size(), 0);\n  }\n  for (std::thread& thread : pool_) {\n    thread.join();\n  }\n}\n\n/*\n使想要线程池执行的work_item函数加入到等待队列中,然后排队等待空闲线程\"领取\"该函数.\n*/\nvoid ThreadPool::Schedule(std::function<void()> work_item) {\n  MutexLocker locker(&mutex_);\n  CHECK(running_);\n  work_queue_.push_back(work_item);\n}\n\n/*\n每个线程都要执行这个函数.\nThreadPool利用条件变量通知此函数,work_queue_不为零时,说明有需要执行的函数,此时加入执行\n*/\nvoid ThreadPool::DoWork() {\n#ifdef __linux__\n  // This changes the per-thread nice level of the current thread on Linux. We\n  // do this so that the background work done by the thread pool is not taking\n  // away CPU resources from more important foreground threads.\n  CHECK_NE(nice(10), -1);\n#endif\n  for (;;) {\n    std::function<void()> work_item;\n    {\n      //加锁,同一时刻只能一个线程\"领取\"某个函数,何时执行这个函数呢? \n      // 最后一行即为抢到的线程开始执行work_item\n      MutexLocker locker(&mutex_);\n      locker.Await([this]() REQUIRES(mutex_) {\n        return !work_queue_.empty() || !running_;   \n        //注意,队列不为空或者不在running_状态,条件变量都需要通知该函数\n      });\n      if (!work_queue_.empty()) {\n        work_item = work_queue_.front();    //领取\n        work_queue_.pop_front();            //删除\n      } else if (!running_) { \n        return;\n      }\n    }\n    CHECK(work_item);\n    work_item();\n  }\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/thread_pool.h",
    "content": "\n#ifndef CARTOGRAPHER_COMMON_THREAD_POOL_H_\n#define CARTOGRAPHER_COMMON_THREAD_POOL_H_\n\n#include <deque>\n#include <functional>\n#include <thread>\n#include <vector>\n\n#include \"cartographer/common/mutex.h\"\n\nnamespace cartographer {\nnamespace common {\n\n/*\nThreadPool 是对c++11 thread的封装.\nThreadPool是线程数量固定的线程池，不可拷贝 和复制.\n\n1，构造函数ThreadPool(int num_threads) 初始化一个线程数量固定的线程池。\n2，Schedule(std::function<void()> work_item)添加想要ThreadPool执行的函数,\n   std::thread会在线程后台依次排队执行相关函数.\n3，数据成员pool_是具体的线程，work_queue_是待执行的函数队列。\n\n\n*/\n// A fixed number of threads working on a work queue of work items. Adding a\n// new work item does not block, and will be executed by a background thread\n// eventually. The queue must be empty before calling the destructor. The thread\n// pool will then wait for the currently executing work items to finish and then\n// destroy the threads.\nclass ThreadPool {\n public:\n  explicit ThreadPool(int num_threads);\n  ~ThreadPool();\n\n  ThreadPool(const ThreadPool&) = delete;\n  ThreadPool& operator=(const ThreadPool&) = delete;\n\n  void Schedule(std::function<void()> work_item);\n\n private:\n  void DoWork();\n\n  Mutex mutex_; //互斥锁，保证线程安全\n\n  //running_只是一个监视哨,只有线程池在running_状态时,才能往work_queue_加入函数.\n  bool running_ GUARDED_BY(mutex_) = true;     \n  std::vector<std::thread> pool_ GUARDED_BY(mutex_);\n  std::deque<std::function<void()>> work_queue_ GUARDED_BY(mutex_);\n};\n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_THREAD_POOL_H_\n"
  },
  {
    "path": "common/time.cc",
    "content": "\n\n#include \"cartographer/common/time.h\"\n\n#include <string>\n\nnamespace cartographer {\nnamespace common {\n\n//duration_cast是c++ 11的时间显式转换函数.\nDuration FromSeconds(const double seconds) {\n  return std::chrono::duration_cast<Duration>(\n    //将double类型的秒数转化为duration对象\n      std::chrono::duration<double>(seconds));  \n}\n\ndouble ToSeconds(const Duration duration) {\n//反转化,count()返回时钟周期数,ticks\n  return std::chrono::duration_cast<std::chrono::duration<double>>(duration)\n      .count();\n}\n\n//先构造一个临时duration对象,再将其转化为time_point对象\n//Duration(ticks)调用的是UniversalTimeScaleClock的构造函数     \nTime FromUniversal(const int64 ticks) { return Time(Duration(ticks)); } \n\n\n//count()返回time_point自epoch以来的时钟周期数\nint64 ToUniversal(const Time time) { return time.time_since_epoch().count(); }\n\n//先将Time转化为 int64 , 再转为字符串形式\nstd::ostream& operator<<(std::ostream& os, const Time time) {\n  os << std::to_string(ToUniversal(time));\n  return os;\n}\n\n//mill是ms,micro是us,先将毫秒转为以毫秒计时的duration对象,再转化为以微妙计.\ncommon::Duration FromMilliseconds(const int64 milliseconds) {\n  return std::chrono::duration_cast<Duration>(\n      std::chrono::milliseconds(milliseconds));\n}\n\n}  // namespace common\n}  // namespace cartographer\n"
  },
  {
    "path": "common/time.h",
    "content": "\n /*\n预备知识:\nc++11 提供了语言级别的时间函数.包括duration和time_point\nduration是时间段,指的是某单位时间上的一个明确的tick(片刻数)：\n3分钟->\"3个1分钟\",\n1.5个\"1/3秒\" :1.5是tick,1/3秒是时间单位\n\ntime_point是一个duration和一个epoch(起点)的组合：\n2017年5月4日是\"自1970,01,01\"以来的126200000秒数\n\n\ncommon/time.h主要功能是提供时间转换函数：\n\n\n */\n\n#ifndef CARTOGRAPHER_COMMON_TIME_H_\n#define CARTOGRAPHER_COMMON_TIME_H_\n\n#include <chrono>\n#include <ostream>\n#include <ratio>\n\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace common {\n\n//719162 是0001年1月1日到1970年1月1日所经历的天数\nconstexpr int64 kUtsEpochOffsetFromUnixEpochInSeconds =\n    (719162ll * 24ll * 60ll * 60ll);\n\nstruct UniversalTimeScaleClock {\n  using rep = int64;\n  using period = std::ratio<1, 10000000>; //百万分之一秒,1us --》错了，应该是0.1us.\n  //以下涉及到us的均应该纠正为0.1us\n\n  using duration = std::chrono::duration<rep, period>;\n  using time_point = std::chrono::time_point<UniversalTimeScaleClock>;\n  /*time_point的模板参数是UniversalTimeScaleClock,\n  那为何其可以做模板参数呢：？符合std::关于clock的类型定义和static成员*/\n  static constexpr bool is_steady = true;\n};\n\n// Represents Universal Time Scale durations and timestamps which are 64-bit\n// integers representing the 100 nanosecond ticks since the Epoch which is\n// January 1, 1 at the start of day in UTC.\nusing Duration = UniversalTimeScaleClock::duration;//微秒,us\nusing Time = UniversalTimeScaleClock::time_point;  //时间点\n\n/*Time::min()是chrono自带的函数。返回一个低于1970.01.01的数。\n\n编译运行cpp/cppstdlib_2nd/util/chrono1.cpp:\nepoch: Thu Jan  1 08:00:00 1970\nnow:   Tue Jul  4 19:39:29 2017\nmin:   Tue Sep 21 08:18:27 1677\nmax:   Sat Apr 12 07:47:16 2262\n\n*/\n// Convenience functions to create common::Durations.\n//将秒数seconds转为c++的duration实例对象\nDuration FromSeconds(double seconds);              \nDuration FromMilliseconds(int64 milliseconds);     //毫秒\n\n// Returns the given duration in seconds.\n//将的duration实例对象转为 秒数 \ndouble ToSeconds(Duration duration);               \n\n// Creates a time from a Universal Time Scale.     \n//将TUC时间(微秒)转化为c++的time_point对象\nTime FromUniversal(int64 ticks);\n\n// Outputs the Universal Time Scale timestamp for a given Time.\n//将c++的time_point对象转为TUC时间,单位是us\nint64 ToUniversal(Time time);                      \n\n// For logging and unit tests, outputs the timestamp integer.\n//重载<<操作符,将time_point以string输出\nstd::ostream& operator<<(std::ostream& os, Time time); \n\n}  // namespace common\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_COMMON_TIME_H_\n\n\n\n/*\nlinux下关于time转换：\nhttp://blog.chinaunix.net/uid-20532339-id-1931780.html\nhttps://stackoverflow.com/questions/2883576/how-do-you-convert-epoch-time-in-c/7844741#7844741\nhttps://www.epochconverter.com/batch*/"
  },
  {
    "path": "ground_truth/compute_relations_metrics_main.cc",
    "content": "\n#include <algorithm>\n#include <cmath>\n#include <fstream>\n#include <iomanip>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"cartographer/transform/transform_interpolation_buffer.h\"\n#include \"gflags/gflags.h\"\n#include \"glog/logging.h\"\n\nDEFINE_string(\n    trajectory_filename, \"\",\n    \"Proto containing the trajectory of which to assess the quality.\");\nDEFINE_string(relations_filename, \"\",\n              \"Relations file containing the ground truth.\");\n\nnamespace cartographer {\nnamespace ground_truth {\nnamespace {\n\ntransform::Rigid3d LookupPose(const transform::TransformInterpolationBuffer&\n                                  transform_interpolation_buffer,\n                              const double time) {\n  constexpr int64 kUtsTicksPerSecond = 10000000;\n  common::Time common_time =\n      common::FromUniversal(common::kUtsEpochOffsetFromUnixEpochInSeconds *\n                            kUtsTicksPerSecond) +\n      common::FromSeconds(time);\n  return transform_interpolation_buffer.Lookup(common_time);\n}\n\nstruct Error {\n  double translational_squared;\n  double rotational_squared;\n};\n\nError ComputeError(const transform::Rigid3d& pose1,\n                   const transform::Rigid3d& pose2,\n                   const transform::Rigid3d& expected) {\n  const transform::Rigid3d error =\n      (pose1.inverse() * pose2) * expected.inverse();\n  return Error{error.translation().squaredNorm(),\n               common::Pow2(transform::GetAngle(error))};\n}\n\nstring MeanAndStdDevString(const std::vector<double>& values) {\n  CHECK_GE(values.size(), 2);\n  const double mean =\n      std::accumulate(values.begin(), values.end(), 0.) / values.size();\n  double sum_of_squared_differences = 0.;\n  for (const double value : values) {\n    sum_of_squared_differences += common::Pow2(value - mean);\n  }\n  const double standard_deviation =\n      std::sqrt(sum_of_squared_differences / (values.size() - 1));\n  std::ostringstream out;\n  out << std::fixed << std::setprecision(5) << mean << \" +/- \"\n      << standard_deviation;\n  return string(out.str());\n}\n\nstring StatisticsString(const std::vector<Error>& errors) {\n  std::vector<double> translational_errors;\n  std::vector<double> squared_translational_errors;\n  std::vector<double> rotational_errors_degrees;\n  std::vector<double> squared_rotational_errors_degrees;\n  for (const Error& error : errors) {\n    translational_errors.push_back(std::sqrt(error.translational_squared));\n    squared_translational_errors.push_back(error.translational_squared);\n    rotational_errors_degrees.push_back(\n        common::RadToDeg(std::sqrt(error.rotational_squared)));\n    squared_rotational_errors_degrees.push_back(\n        common::Pow2(rotational_errors_degrees.back()));\n  }\n  return \"Abs translational error \" +\n         MeanAndStdDevString(translational_errors) +\n         \" m\\n\"\n         \"Sqr translational error \" +\n         MeanAndStdDevString(squared_translational_errors) +\n         \" m^2\\n\"\n         \"Abs rotational error \" +\n         MeanAndStdDevString(rotational_errors_degrees) +\n         \" deg\\n\"\n         \"Sqr rotational error \" +\n         MeanAndStdDevString(squared_rotational_errors_degrees) + \" deg^2\\n\";\n}\n\nvoid Run(const string& trajectory_filename, const string& relations_filename) {\n  LOG(INFO) << \"Reading trajectory from '\" << trajectory_filename << \"'...\";\n  mapping::proto::Trajectory trajectory_proto;\n  {\n    std::ifstream trajectory_stream(trajectory_filename.c_str(),\n                                    std::ios::binary);\n    CHECK(trajectory_proto.ParseFromIstream(&trajectory_stream));\n  }\n\n  const auto transform_interpolation_buffer =\n      transform::TransformInterpolationBuffer::FromTrajectory(trajectory_proto);\n\n  LOG(INFO) << \"Reading relations from '\" << relations_filename << \"'...\";\n  std::vector<Error> errors;\n  {\n    std::ifstream relations_stream(relations_filename.c_str());\n    double time1, time2, x, y, z, roll, pitch, yaw;\n    while (relations_stream >> time1 >> time2 >> x >> y >> z >> roll >> pitch >>\n           yaw) {\n      const auto pose1 = LookupPose(*transform_interpolation_buffer, time1);\n      const auto pose2 = LookupPose(*transform_interpolation_buffer, time2);\n      const transform::Rigid3d expected =\n          transform::Rigid3d(transform::Rigid3d::Vector(x, y, z),\n                             transform::RollPitchYaw(roll, pitch, yaw));\n      errors.push_back(ComputeError(pose1, pose2, expected));\n    }\n    CHECK(relations_stream.eof());\n  }\n\n  LOG(INFO) << \"Result:\\n\" << StatisticsString(errors);\n}\n\n}  // namespace\n}  // namespace ground_truth\n}  // namespace cartographer\n\n\nint main(int argc, char** argv) {\n  google::InitGoogleLogging(argv[0]);\n  FLAGS_logtostderr = true;\n  google::SetUsageMessage(\n      \"\\n\\n\"\n      \"This program computes the relation based metric described in:\\n\"\n      \"R. Kuemmerle, B. Steder, C. Dornhege, M. Ruhnke, G. Grisetti,\\n\"\n      \"C. Stachniss, and A. Kleiner, \\\"On measuring the accuracy of SLAM\\n\"\n      \"algorithms,\\\" Autonomous Robots, vol. 27, no. 4, pp. 387–407, 2009.\\n\"\n      \"\\n\"\n      \"Note: Timestamps in the relations_file are interpreted relative to\\n\"\n      \"      the Unix epoch.\");\n  google::ParseCommandLineFlags(&argc, &argv, true);\n\n  if (FLAGS_trajectory_filename.empty() || FLAGS_relations_filename.empty()) {\n    google::ShowUsageWithFlagsRestrict(argv[0], \"compute_relations_metrics\");\n    return EXIT_FAILURE;\n  }\n  ::cartographer::ground_truth::Run(FLAGS_trajectory_filename,\n                                    FLAGS_relations_filename);\n}\n"
  },
  {
    "path": "io/cairo_types.h",
    "content": "#ifndef CARTOGRAPHER_IO_CAIRO_TYPES_H_\n#define CARTOGRAPHER_IO_CAIRO_TYPES_H_\n\n#include <memory>\n\n#include \"cairo/cairo.h\"\n/*\nCairo是一个2D图形库，支持多种输出设备。\ncairo 是一个免费的矢量绘图软件库，它可以绘制多种输出格式。\n\nhttps://www.ibm.com/developerworks/cn/linux/l-cairo/\nhttp://liyanrui.is-programmer.com/2009/3/18/cairo-tutorial-05.7742.html\nhttp://blog.csdn.net/turingo/article/details/8131057\n\n*/\nnamespace cartographer {\nnamespace io {\nnamespace cairo {\n\n//智能指针管理对象生命期.在unique_ptr销毁时释放指向的对象资源.\n\n\n//对象类型是cairo_surface_t,自定义删除器deleter是函数指针.\n// std::unique_ptr for Cairo surfaces. The surface is destroyed when the\n// std::unique_ptr is reset or destroyed.\nusing UniqueSurfacePtr =\n    std::unique_ptr<cairo_surface_t, void (*)(cairo_surface_t*)>;\n\n//对象类型是cairo_t,自定义删除器deleter是函数指针.\n// std::unique_ptr for Cairo contexts. The context is destroyed when the\n// std::unique_ptr is reset or destroyed.\nusing UniqueContextPtr = std::unique_ptr<cairo_t, void (*)(cairo_t*)>;\n\n//对象类型是cairo_path_t,自定义删除器deleter是函数指针.\n// std::unique_ptr for Cairo paths. The path is destroyed when the\n// std::unique_ptr is reset or destroyed.\nusing UniquePathPtr = std::unique_ptr<cairo_path_t, void (*)(cairo_path_t*)>;\n\n}  // namespace cairo\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_CAIRO_TYPES_H_\n"
  },
  {
    "path": "io/coloring_points_processor.cc",
    "content": "\n#include \"cartographer/io/coloring_points_processor.h\"\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\n根据assets_writer_backpack_2d.lua配置:.lua文件概览:\n-- Now we recolor our points by frame and write another batch of X-Rays. It\n-- is visible in them what was seen by the horizontal and the vertical\n-- laser.\n    {\n      action = \"color_points\",\n      frame_id = \"horizontal_laser_link\",\n      color = { 255., 0., 0. },\n    },\n    {\n      action = \"color_points\",\n      frame_id = \"vertical_laser_link\",\n      color = { 0., 255., 0. },\n    },\nFromDictionary()根据.lua配置文件获取frame_id下color的{r,g,b}\n*/\n\n\nstd::unique_ptr<ColoringPointsProcessor>\nColoringPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n/*\nframe_id:horizontal_laser_link或者vertical_laser_link\ncolor:{ 255., 0., 0. },或者{ 0., 255., 0. },\n*/\n  const string frame_id = dictionary->GetString(\"frame_id\");\n  const std::vector<double> color_values =\n      dictionary->GetDictionary(\"color\")->GetArrayValuesAsDoubles();\n  const Color color = {{static_cast<uint8_t>(color_values[0]),\n                        static_cast<uint8_t>(color_values[1]),\n                        static_cast<uint8_t>(color_values[2])}};\n  return common::make_unique<ColoringPointsProcessor>(color, frame_id, next);\n}\n\nColoringPointsProcessor::ColoringPointsProcessor(const Color& color,\n                                                 const string& frame_id,\n                                                 PointsProcessor* const next)\n    : color_(color), frame_id_(frame_id), next_(next) {}\n\n/*\n只对相同的frame_id_处理：着色\n*/\nvoid ColoringPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {\n  if (batch->frame_id == frame_id_) {\n    batch->colors.clear();\n    for (size_t i = 0; i < batch->points.size(); ++i) {\n      batch->colors.push_back(color_);//用color 填充batch的colors\n    }\n  }\n  next_->Process(std::move(batch));\n}\n\nPointsProcessor::FlushResult ColoringPointsProcessor::Flush() {\n  return next_->Flush();\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/coloring_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_COLORING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_COLORING_POINTS_PROCESSOR_H_\n\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_batch.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\n  {\n      action = \"color_points\",\n      frame_id = \"horizontal_laser_link\",\n      color = { 255., 0., 0. },\n    },\n    {\n      action = \"color_points\",\n      frame_id = \"vertical_laser_link\",\n      color = { 0., 255., 0. },\n    },\n\nColoringPointsProcessor是PointsProcessor的第六子类(6).\n功能: 用固定的Color填充PointsBatch的Color分量。\n\n数据成员:\n1),color_：rgb值\n2),frame_id_:只有相同的id才填充Color，color一般为[255,0,0],[0,255,0]\n2),next_下一阶段的PointsProcessor.\n\n*/\n\n// Colors points with a fixed color by frame_id.\nclass ColoringPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName = \"color_points\";\n\n  ColoringPointsProcessor(const Color& color, const string& frame_id,\n                          PointsProcessor* next);\n\n  static std::unique_ptr<ColoringPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~ColoringPointsProcessor() override{};\n\n  ColoringPointsProcessor(const ColoringPointsProcessor&) = delete;\n  ColoringPointsProcessor& operator=(const ColoringPointsProcessor&) = delete;\n\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  const Color color_;\n  const string frame_id_;\n  PointsProcessor* const next_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_COLORING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/counting_points_processor.cc",
    "content": "\n#include \"cartographer/io/counting_points_processor.h\"\n\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\n//初始化是num是0\nCountingPointsProcessor::CountingPointsProcessor(PointsProcessor* next)\n    : num_points_(0), next_(next) {}\n\n//与其他FromDictionary()函数统一写法\nstd::unique_ptr<CountingPointsProcessor>\nCountingPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  return common::make_unique<CountingPointsProcessor>(next);\n}\n\n//不处理points,而是将num_points_加上batch.size(),，即统计点云数据。然后直接流水线到下一PointsProcessor\nvoid CountingPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {\n  num_points_ += batch->points.size();//相加\n  next_->Process(std::move(batch));\n}\n\n//依据下一阶段的状态重置本阶段的状态。\nPointsProcessor::FlushResult CountingPointsProcessor::Flush() {\n  switch (next_->Flush()) {\n    case FlushResult::kFinished:\n      LOG(INFO) << \"Processed \" << num_points_ << \" and finishing.\";\n      return FlushResult::kFinished;\n\n    case FlushResult::kRestartStream:\n      LOG(INFO) << \"Processed \" << num_points_ << \" and restarting stream.\";\n      num_points_ = 0; //重启,则置为0\n      return FlushResult::kRestartStream;\n  }\n  LOG(FATAL);\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/counting_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_COUNTING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_COUNTING_POINTS_PROCESSOR_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\n\nPointsProcessor的第二个子类(2).\n功能:记录有多少 point被输出处理\n\n数据成员:\n1),num_points_记录points数量\n2),next_下一阶段的PointsProcessor.\n\n*/\n// Passes through points, but keeps track of how many points it saw and output\n// that on Flush.\nclass CountingPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName = \"dump_num_points\";\n  explicit CountingPointsProcessor(PointsProcessor* next);\n\n  static std::unique_ptr<CountingPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~CountingPointsProcessor() override {}\n\n  CountingPointsProcessor(const CountingPointsProcessor&) = delete;\n  CountingPointsProcessor& operator=(const CountingPointsProcessor&) = delete;\n\n  void Process(std::unique_ptr<PointsBatch> points) override;\n  FlushResult Flush() override;\n\n private:\n  int64 num_points_;\n  PointsProcessor* next_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_COUNTING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/file_writer.cc",
    "content": "\n#include \"cartographer/io/file_writer.h\"\n\nnamespace cartographer {\nnamespace io {\n\n//out:File open for writing: the internal stream buffer supports output operations.\n//binary:Operations are performed in binary mode rather than text.\n\nStreamFileWriter::StreamFileWriter(const string& filename)\n    : out_(filename, std::ios::out | std::ios::binary) {}//打开,并以2进制方式写入\n\nStreamFileWriter::~StreamFileWriter() {}\n\n//写入len个char\nbool StreamFileWriter::Write(const char* const data, const size_t len) {\n  if (out_.bad()) {\n    return false;\n  }\n  out_.write(data, len);\n  return !out_.bad();\n}\n\nbool StreamFileWriter::Close() { //关闭文件\n  if (out_.bad()) {\n    return false;\n  }\n  out_.close();\n  return !out_.bad();\n}\n\nbool StreamFileWriter::WriteHeader(const char* const data, const size_t len) {\n  if (out_.bad()) {\n    return false;\n  }\n  out_.flush();  //清空buffer\n  out_.seekp(0); //偏移量为0\n  return Write(data, len);\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/file_writer.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_FILE_WRITER_H_\n#define CARTOGRAPHER_IO_FILE_WRITER_H_\n\n#include <fstream>\n#include <functional>\n#include <memory>\n\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\nFileWriter负责文件写入的虚基类,不可拷贝/赋值\n没有数据成员.只提供一系列抽象接口.\n1),WriteHeader(),写入文件head\n2),Write(),写入数据\n3),Close(),关闭文件\n*/\n// Simple abstraction for a file.\nclass FileWriter {\n public:\n  FileWriter() {}\n  FileWriter(const FileWriter&) = delete;\n  FileWriter& operator=(const FileWriter&) = delete;\n\n  virtual ~FileWriter() {}\n\n  // Write 'data' to the beginning of the file. This is required to overwrite\n  // fixed size headers which contain the number of points once we actually know\n  // how many points there are.\n  virtual bool WriteHeader(const char* data, size_t len) = 0;\n\n  virtual bool Write(const char* data, size_t len) = 0;\n  virtual bool Close() = 0;\n};\n\n/*\nStreamFileWriter 文件流写入类,继承自FileWriter\n数据成员只有一个std::ofstream out_负责将文件写入磁盘\n\n*/\n// An Implementation of file using std::ofstream.\nclass StreamFileWriter : public FileWriter {\n public:\n  ~StreamFileWriter() override;\n\n  StreamFileWriter(const string& filename);\n\n  bool Write(const char* data, size_t len) override;\n  bool WriteHeader(const char* data, size_t len) override; //从文件开始处写入,覆盖旧数据\n  bool Close() override;\n\n private:\n  std::ofstream out_;\n};\n\n/*工厂模式,\n创建一个FileWriter对象,由智能指针管理生命期,\n返回值是std::unique_ptr<FileWriter>,\n函数参数是string.\n*/\nusing FileWriterFactory =\n    std::function<std::unique_ptr<FileWriter>(const string& filename)>;\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_FILE_WRITER_H_\n"
  },
  {
    "path": "io/fixed_ratio_sampling_points_processor.cc",
    "content": "\n\n#include \"cartographer/io/fixed_ratio_sampling_points_processor.h\"\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\n静态成员函数,利用参数构造一个智能指针指向FixedRatioSamplingPointsProcessor的对象\n*/\nstd::unique_ptr<FixedRatioSamplingPointsProcessor>\nFixedRatioSamplingPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n\n  const double sampling_ratio(dictionary->GetDouble(\"sampling_ratio\"));\n   //sparse_pose_graph.lua:0.3,0.03\n  CHECK_LT(0., sampling_ratio) << \"Sampling ratio <= 0 makes no sense.\";\n  CHECK_LT(sampling_ratio, 1.) << \"Sampling ratio >= 1 makes no sense.\";\n  return common::make_unique<FixedRatioSamplingPointsProcessor>(sampling_ratio,\n                                                                next);\n\n}\n\n/*\n构造函数用采样率 sampling_ratio和PointsProcessor* next初始化\n*/\nFixedRatioSamplingPointsProcessor::FixedRatioSamplingPointsProcessor(\n    const double sampling_ratio, PointsProcessor* next)\n    : sampling_ratio_(sampling_ratio),\n      next_(next),\n      sampler_(new common::FixedRatioSampler(sampling_ratio_)) {}\n\n/*\n该类的核心函数,有几个为了提高效率的地方:\nProcess()传递智能指针,指向堆上的点集PointsBatch,\n理论上该点集组成的vector是不断增加的.但是同时也有其他Process()在删除PointsBatch\n\n\n*/\nvoid FixedRatioSamplingPointsProcessor::Process(\n    std::unique_ptr<PointsBatch> batch) {\n  std::vector<int> to_remove;                         //保存索引\n  for (size_t i = 0; i < batch->points.size(); ++i) { //points是vector\n    if (!sampler_->Pulse()) {  \n//调用一次Pulse()其实影响到了sampler_的内部状态.\n//模拟的传感器sampler_没有产生一个脉冲,则说明当前的batch[i]应该删除.\n      to_remove.push_back(i);\n    }\n  }\n  RemovePoints(to_remove, batch.get());\n  next_->Process(std::move(batch));//虚函数,调用子类的Process(),也就是这个函数本身\n}\n\nPointsProcessor::FlushResult FixedRatioSamplingPointsProcessor::Flush() {\n  switch (next_->Flush()) {\n    case PointsProcessor::FlushResult::kFinished: \n    //下一步已经完成,析构时则这一步也标记完成\n      return PointsProcessor::FlushResult::kFinished;\n\n    case PointsProcessor::FlushResult::kRestartStream://重启采样\n\n    //为何要重新赋值?:重启,不能带有以前的状态,所以重新初始化.\n      sampler_ =\n          common::make_unique<common::FixedRatioSampler>(sampling_ratio_);\n      return PointsProcessor::FlushResult::kRestartStream;\n  }\n  LOG(FATAL);\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/fixed_ratio_sampling_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_FIXED_RATIO_SAMPLING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_FIXED_RATIO_SAMPLING_POINTS_PROCESSOR_H_\n\n#include <memory>\n\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\nFixedRatioSamplingPointsProcessor是PointsProcessor的第七个子类(7).\nFixedRatioSamplingPointsProcessor是采样过滤器,\n该class只让具有固定采样频率的点通过,其余全部 remove.\n\nsampling_ratio=1,即无任何操作,全通滤波.\n\nsparse_pose_graph.lua:\n sampling_ratio = 0.3,\n\n*/\n// Only let a fixed 'sampling_ratio' of points through. A 'sampling_ratio' of 1.\n// makes this filter a no-op.\nclass FixedRatioSamplingPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName =\n      \"fixed_ratio_sampler\";\n\n  FixedRatioSamplingPointsProcessor(double sampling_ratio,\n                                    PointsProcessor* next);\n\n  static std::unique_ptr<FixedRatioSamplingPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~FixedRatioSamplingPointsProcessor() override{};\n\n  FixedRatioSamplingPointsProcessor(const FixedRatioSamplingPointsProcessor&) =\n      delete;\n  FixedRatioSamplingPointsProcessor& operator=(\n      const FixedRatioSamplingPointsProcessor&) = delete;\n\n//根据采样率采集点云，不在采样点上的全部删除。\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  const double sampling_ratio_;//采样率\n  PointsProcessor* const next_;\n  std::unique_ptr<common::FixedRatioSampler> sampler_;//具有固定采样率的采样函数\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_FIXED_RATIO_SAMPLING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/intensity_to_color_points_processor.cc",
    "content": "\n\n#include \"cartographer/io/intensity_to_color_points_processor.h\"\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\nstd::unique_ptr<IntensityToColorPointsProcessor>\nIntensityToColorPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  const string frame_id =\n      dictionary->HasKey(\"frame_id\") ? dictionary->GetString(\"frame_id\") : \"\";\n  const float min_intensity = dictionary->GetDouble(\"min_intensity\");\n  const float max_intensity = dictionary->GetDouble(\"max_intensity\");\n  return common::make_unique<IntensityToColorPointsProcessor>(\n      min_intensity, max_intensity, frame_id, next);\n}\n\nIntensityToColorPointsProcessor::IntensityToColorPointsProcessor(\n    const float min_intensity, const float max_intensity,\n    const string& frame_id, PointsProcessor* const next)\n    : min_intensity_(min_intensity),\n      max_intensity_(max_intensity),\n      frame_id_(frame_id),\n      next_(next) {}\n\nvoid IntensityToColorPointsProcessor::Process(\n    std::unique_ptr<PointsBatch> batch) {\n  if (!batch->intensities.empty() &&\n      (frame_id_.empty() || batch->frame_id == frame_id_)) {\n    batch->colors.clear();\n    for (const float intensity : batch->intensities) {\n      const uint8_t gray =\n          cartographer::common::Clamp(\n              (intensity - min_intensity_) / (max_intensity_ - min_intensity_),\n              0.f, 1.f) *\n          255;\n      batch->colors.push_back(Color{{gray, gray, gray}});\n    }\n  }\n  next_->Process(std::move(batch));\n}\n\nPointsProcessor::FlushResult IntensityToColorPointsProcessor::Flush() {\n  return next_->Flush();\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/intensity_to_color_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_INTENSITY_TO_COLOR_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_INTENSITY_TO_COLOR_POINTS_PROCESSOR_H_\n\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_batch.h\"\n#include \"cartographer/io/points_processor.h\"\n\n\n\n\nnamespace cartographer {\nnamespace io {\n/*\n  {\n      action = \"intensity_to_color\",\n      min_intensity = 0.,\n      max_intensity = 4095.,\n    },\n\nPointsProcessor的第四个子类(4).\n功能:将光强度转换为color\n\nIntensity,图像亮度\nIntensityToColorPointsProcessor：处理强度到color point 的强度变换类\n成员函数执行转换:('intensity' - min ) / (max - min) * 255 \n\n*/\nclass IntensityToColorPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName =\n      \"intensity_to_color\";\n\n  // Applies ('intensity' - min ) / (max - min) * 255 and color the point grey\n  // with this value for each point that comes from the sensor with 'frame_id'.\n  // If 'frame_id' is empty, this applies to all points.\n  IntensityToColorPointsProcessor(float min_intensity, float max_intensity,\n                                  const string& frame_id,\n                                  PointsProcessor* next);\n\n  static std::unique_ptr<IntensityToColorPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~IntensityToColorPointsProcessor() override{};\n\n  IntensityToColorPointsProcessor(const IntensityToColorPointsProcessor&) =\n      delete;\n  IntensityToColorPointsProcessor& operator=(\n      const IntensityToColorPointsProcessor&) = delete;\n\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  const float min_intensity_;\n  const float max_intensity_;\n  const string frame_id_;\n  PointsProcessor* const next_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_INTENSITY_TO_COLOR_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/min_max_range_filtering_points_processor.cc",
    "content": "\n#include \"cartographer/io/min_max_range_filtering_points_processor.h\"\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/io/points_batch.h\"\n\nnamespace cartographer {\nnamespace io {\n\n//从.lua文件加载min_range和max_range,如:0-30\nstd::unique_ptr<MinMaxRangeFiteringPointsProcessor>\nMinMaxRangeFiteringPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  return common::make_unique<MinMaxRangeFiteringPointsProcessor>(\n      dictionary->GetDouble(\"min_range\"), dictionary->GetDouble(\"max_range\"), //trajectory_builder_2d.lua:0,30\n      //assets_writer_backpack_2d.lua 1-60\n      next);\n}\n\n/*\n构造函数,传递min ,max2个范围参数和 PointsProcessor* next指针\n\n*/\nMinMaxRangeFiteringPointsProcessor::MinMaxRangeFiteringPointsProcessor(\n    const double min_range, const double max_range, PointsProcessor* next)\n    : min_range_(min_range), max_range_(max_range), next_(next) {}\n\nvoid MinMaxRangeFiteringPointsProcessor::Process(\n    std::unique_ptr<PointsBatch> batch)\n\n     {\n  std::vector<int> to_remove;    //待移除的索引\n  for (size_t i = 0; i < batch->points.size(); ++i) {\n    //计算sensor到远点的l2距离\n    const float range = (batch->points[i] - batch->origin).norm(); \n    if (!(min_range_ <= range && range <= max_range_)) {        \n      //不在给定范围内,加入移除队列.\n      to_remove.push_back(i);\n    }\n  }\n  RemovePoints(to_remove, batch.get());\n  next_->Process(std::move(batch));//完成本轮过滤,接下来进行下一次过滤.即模拟流水线操作.\n}\n\nPointsProcessor::FlushResult MinMaxRangeFiteringPointsProcessor::Flush() {\n  return next_->Flush();\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/min_max_range_filtering_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_MIN_MAX_RANGE_FILTERING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_MIN_MAX_RANGE_FILTERING_POINTS_PROCESSOR_H_\n\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\n\nPointsProcessor的第一个子类(1).\n功能:继承自PointsProcessor点云虚基类.距离过滤器,L2 距离不在Min-Max范围内的都过滤掉.\n数据成员:\n1),min_range_ 最小范围\n2),max_range_ 最大范围\n3),PointsProcessor* const next_:完成本轮Processor,接下来进行下一次Processor.\n*/\n\n// Filters all points that are farther away from their 'origin' as 'max_range'\n// or closer than 'min_range'.\nclass MinMaxRangeFiteringPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName =\n      \"min_max_range_filter\";\n\n   \n  MinMaxRangeFiteringPointsProcessor(double min_range, double max_range,\n                                     PointsProcessor* next);\n\n  //从.lua文件加载min_range和max_range,如:0-30 \n  static std::unique_ptr<MinMaxRangeFiteringPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~MinMaxRangeFiteringPointsProcessor() override {}//覆盖\n\n  MinMaxRangeFiteringPointsProcessor(\n      const MinMaxRangeFiteringPointsProcessor&) = delete;\n  MinMaxRangeFiteringPointsProcessor& operator=(\n      const MinMaxRangeFiteringPointsProcessor&) = delete;\n\n//点云处理,删除[min,max]以外的point，并把数据传递到下一轮Processor处理。\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n\n  FlushResult Flush() override;\n  //调用next_->Flush();父类调用Flush(),但在运行时才会决定是否调用子类的Flush()\n\n private:\n  const double min_range_;\n  const double max_range_;\n  PointsProcessor* const next_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_MIN_MAX_RANGE_FILTERING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/null_points_processor.h",
    "content": "\n\n#ifndef CARTOGRAPHER_IO_NULL_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_NULL_POINTS_PROCESSOR_H_\n\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\nNullPointsProcessor是PointsProcessor的第十个子类(10)\n空操作类。通常用于pipline的尾端，丢弃所有的点云，表示整个处理流程已经完毕。\n*/\n\n// A points processor that just drops all points. The end of a pipeline usually.\nclass NullPointsProcessor : public PointsProcessor {\n public:\n  NullPointsProcessor() {}\n  ~NullPointsProcessor() override {}\n    \n  //不作任何事情\n  void Process(std::unique_ptr<PointsBatch> points_batch) override {}\n  //返回\"kFinished\"状态，此操作会传递给上一个流水线。\n  FlushResult Flush() override { return FlushResult::kFinished; }\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_NULL_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/outlier_removing_points_processor.cc",
    "content": "\n\n#include \"cartographer/io/outlier_removing_points_processor.h\"\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\nVOXEL_SIZE = 5e-2\n*/\nstd::unique_ptr<OutlierRemovingPointsProcessor>\nOutlierRemovingPointsProcessor::FromDictionary(\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  return common::make_unique<OutlierRemovingPointsProcessor>(\n      dictionary->GetDouble(\"voxel_size\"), next); //构造一个对象,返回一个智能指针\n}\n\n/*\n构造函数,传递一个 voxel_size 和  PointsProcessor* next\n*/\nOutlierRemovingPointsProcessor::OutlierRemovingPointsProcessor(\n    const double voxel_size, PointsProcessor* next)\n    : voxel_size_(voxel_size),\n      next_(next),\n      state_(State::kPhase1),\n      voxels_(voxel_size_) {\n  LOG(INFO) << \"Marking hits...\";\n}\n\n/*\n根据3个不同的state分别处理 points\n*/\nvoid OutlierRemovingPointsProcessor::Process(\n    std::unique_ptr<PointsBatch> points) {\n  switch (state_) {\n    case State::kPhase1:\n      ProcessInPhaseOne(*points);\n      break;\n\n    case State::kPhase2:\n      ProcessInPhaseTwo(*points);\n      break;\n\n    case State::kPhase3:\n      ProcessInPhaseThree(std::move(points));\n      break;\n  }\n}\n\n/*\n更新state,并返回 FlushResult结果\n*/\nPointsProcessor::FlushResult OutlierRemovingPointsProcessor::Flush() {\n  switch (state_) {\n    case State::kPhase1:\n      LOG(INFO) << \"Counting rays...\";\n      state_ = State::kPhase2;\n      return FlushResult::kRestartStream;\n\n    case State::kPhase2:\n      LOG(INFO) << \"Filtering outliers...\";\n      state_ = State::kPhase3;\n      return FlushResult::kRestartStream;\n\n    case State::kPhase3:\n      CHECK(next_->Flush() == FlushResult::kFinished)\n          << \"Voxel filtering and outlier removal must be configured to occur \"\n             \"after any stages that require multiple passes.\";\n             // multiple passes：多次传输。\n      return FlushResult::kFinished;\n  }\n  LOG(FATAL);\n}\n\n\n/*\nstate 1，统计光线的数量。\n*/\nvoid OutlierRemovingPointsProcessor::ProcessInPhaseOne(\n    const PointsBatch& batch) {\n  for (size_t i = 0; i < batch.points.size(); ++i) {\n    ++voxels_.mutable_value(voxels_.GetCellIndex(batch.points[i]))->hits;\n  }\n}\n\n/*\n\n\n*/\nvoid OutlierRemovingPointsProcessor::ProcessInPhaseTwo(\n    const PointsBatch& batch) {\n  // TODO(whess): This samples every 'voxel_size' distance and could be improved\n  // by better ray casting, and also by marking the hits of the current range\n  // data to be excluded.\n  for (size_t i = 0; i < batch.points.size(); ++i) {\n    const Eigen::Vector3f delta = batch.points[i] - batch.origin;\n    const float length = delta.norm();\n    for (float x = 0; x < length; x += voxel_size_) {\n      const auto index =\n          voxels_.GetCellIndex(batch.origin + (x / length) * delta);\n      if (voxels_.value(index).hits > 0) {\n        ++voxels_.mutable_value(index)->rays;\n      }\n    }\n  }\n}\n\nvoid OutlierRemovingPointsProcessor::ProcessInPhaseThree(\n    std::unique_ptr<PointsBatch> batch) {\n  constexpr double kMissPerHitLimit = 3;\n  std::vector<int> to_remove;\n  for (size_t i = 0; i < batch->points.size(); ++i) {\n    const auto voxel = voxels_.value(voxels_.GetCellIndex(batch->points[i]));\n    if (!(voxel.rays < kMissPerHitLimit * voxel.hits)) {\n      to_remove.push_back(i);\n    }\n  }\n  RemovePoints(to_remove, batch.get());\n  next_->Process(std::move(batch));\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/outlier_removing_points_processor.h",
    "content": "\n\n#ifndef CARTOGRAPHER_IO_OUTLIER_REMOVING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_OUTLIER_REMOVING_POINTS_PROCESSOR_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/points_processor.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\nOutlierRemovingPointsProcessor是PointsProcessor的第八个子类(8).\nOutlierRemovingPointsProcessor：\n作用：体素过滤器,Remove有移动痕迹的体素。只保留没有移动的体素\n\n数据成员：\n\nconst double voxel_size_;\nPointsProcessor* const next_;\nState state_;\nmapping_3d::HybridGridBase<VoxelData> voxels_;\n*/\n// Voxel filters the data and only passes on points that we believe are on\n// non-moving objects.\nclass OutlierRemovingPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName =\n      \"voxel_filter_and_remove_moving_objects\";\n\n  OutlierRemovingPointsProcessor(double voxel_size, PointsProcessor* next);\n\n  static std::unique_ptr<OutlierRemovingPointsProcessor> FromDictionary(\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~OutlierRemovingPointsProcessor() override {}\n\n  OutlierRemovingPointsProcessor(const OutlierRemovingPointsProcessor&) =\n      delete;\n  OutlierRemovingPointsProcessor& operator=(\n      const OutlierRemovingPointsProcessor&) = delete;\n//删除移动的体素。\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  // To reduce memory consumption by not having to keep all rays in memory, we\n  // filter outliers in three phases each going over all data: First we compute\n  // all voxels containing any hits, then we compute the rays passing through\n  // each of these voxels, and finally we output all hits in voxels that are\n  // considered obstructed.\n  struct VoxelData {\n    int hits = 0;\n    int rays = 0;\n  };\n  enum class State {\n    kPhase1,\n    kPhase2,\n    kPhase3,\n  };\n\n  // First phase counts the number of hits per voxel.\n  void ProcessInPhaseOne(const PointsBatch& batch);\n\n  // Second phase counts how many rays pass through each voxel. This is only\n  // done for voxels that contain hits. This is to reduce memory consumption by\n  // not adding data to free voxels.\n  void ProcessInPhaseTwo(const PointsBatch& batch);\n\n  // Third phase produces the output containing all inliers. We consider each\n  // hit an inlier if it is inside a voxel that has a sufficiently high\n  // hit-to-ray ratio.\n  void ProcessInPhaseThree(std::unique_ptr<PointsBatch> batch);\n\n  const double voxel_size_; //体素大小\n  PointsProcessor* const next_;\n  State state_;\n  mapping_3d::HybridGridBase<VoxelData> voxels_; //包含多个体素的网格Grid。\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_OUTLIER_REMOVING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/pcd_writing_points_processor.cc",
    "content": "\n#include \"cartographer/io/pcd_writing_points_processor.h\"\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/io/points_batch.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\nnamespace {\n\n/*\nPCD文件由文件头和数据域组成，文件头存储了点云数据的字段格式信息，如： \n======================================== \n# .PCD v0.7 - Point Cloud Data file format \nVERSION 0.7 \nFIELDS x y z rgba \nSIZE 4 4 4 4 \nTYPE F F F U \nCOUNT 1 1 1 1 \nWIDTH 640 \nHEIGHT 480 \nVIEWPOINT 0 0 0 0 1 0 0 \nPOINTS 307200 \nDATA binary \n//下面是点云数据段 \n123.1 21.1 64.0\n...\n======================================== \n\n*/\n// Writes the PCD header claiming 'num_points' will follow it into\n// 'output_file'.\nvoid WriteBinaryPcdHeader(const bool has_color, const int64 num_points,\n                          FileWriter* const file_writer) {\n  string color_header_field = !has_color ? \"\" : \" rgb\";\n  string color_header_type = !has_color ? \"\" : \" U\";\n  string color_header_size = !has_color ? \"\" : \" 4\";\n  string color_header_count = !has_color ? \"\" : \" 1\";\n\n  std::ostringstream stream;\n  stream << \"# generated by Cartographer\\n\"\n         << \"VERSION .7\\n\"\n         << \"FIELDS x y z\" << color_header_field << \"\\n\"\n         << \"SIZE 4 4 4\" << color_header_size << \"\\n\"\n         << \"TYPE F F F\" << color_header_type << \"\\n\"\n         << \"COUNT 1 1 1\" << color_header_count << \"\\n\"\n         << \"WIDTH \" << std::setw(15) << std::setfill('0') << num_points << \"\\n\"\n         << \"HEIGHT 1\\n\"\n         << \"VIEWPOINT 0 0 0 1 0 0 0\\n\"\n         << \"POINTS \" << std::setw(15) << std::setfill('0') << num_points\n         << \"\\n\"\n         << \"DATA binary\\n\";\n  const string out = stream.str();\n  file_writer->WriteHeader(out.data(), out.size());\n}\n\n//{x,y,z}写入文件\nvoid WriteBinaryPcdPointCoordinate(const Eigen::Vector3f& point,\n                                   FileWriter* const file_writer) {\n  char buffer[12];\n  memcpy(buffer, &point[0], sizeof(float));\n  memcpy(buffer + 4, &point[1], sizeof(float)); //64机,float:4位\n  memcpy(buffer + 8, &point[2], sizeof(float));\n  CHECK(file_writer->Write(buffer, 12));\n}\n\nvoid WriteBinaryPcdPointColor(const Color& color,\n                              FileWriter* const file_writer) {\n  char buffer[4];\n  buffer[0] = color[2];\n  buffer[1] = color[1];\n  buffer[2] = color[0];\n  buffer[3] = 0;\n  CHECK(file_writer->Write(buffer, 4));\n}\n\n}  // namespace\n\nstd::unique_ptr<PcdWritingPointsProcessor>\nPcdWritingPointsProcessor::FromDictionary(\n    FileWriterFactory file_writer_factory,\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) \n\n{\n  return common::make_unique<PcdWritingPointsProcessor>(\n      file_writer_factory(dictionary->GetString(\"filename\")), next);\n}\n\nPcdWritingPointsProcessor::PcdWritingPointsProcessor(\n    std::unique_ptr<FileWriter> file_writer, PointsProcessor* const next)\n    : next_(next),\n      num_points_(0),\n      has_colors_(false),\n      file_writer_(std::move(file_writer)) {}\n\nPointsProcessor::FlushResult PcdWritingPointsProcessor::Flush()\n\n {\n  WriteBinaryPcdHeader(has_colors_, num_points_, file_writer_.get());\n  //调用Flush()才写入head信息:此时才知道num_points_的值\n  CHECK(file_writer_->Close());\n\n  switch (next_->Flush()) {\n    case FlushResult::kFinished:\n      return FlushResult::kFinished;\n\n    case FlushResult::kRestartStream://不能多次传递同一数据.\n      LOG(FATAL) << \"PCD generation must be configured to occur after any \"\n                    \"stages that require multiple passes.\";\n  }\n  LOG(FATAL);\n}\n\n/*\n核心函数,按照{x,y,z}写入文件\n*/\nvoid PcdWritingPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) \n\n{\n  if (batch->points.empty()) {\n    next_->Process(std::move(batch));\n    return;\n  }\n\n  if (num_points_ == 0) {\n    has_colors_ = !batch->colors.empty();\n    WriteBinaryPcdHeader(has_colors_, 0, file_writer_.get());\n  }\n  for (size_t i = 0; i < batch->points.size(); ++i) {\n    WriteBinaryPcdPointCoordinate(batch->points[i], file_writer_.get());\n    if (!batch->colors.empty()) {\n      WriteBinaryPcdPointColor(batch->colors[i], file_writer_.get());\n    }\n    ++num_points_;//统计处理了多少points\n  }\n  next_->Process(std::move(batch));\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/pcd_writing_points_processor.h",
    "content": "\n#include <fstream>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/file_writer.h\"\n#include \"cartographer/io/points_processor.h\"\n/*\n点云是目标表面特性的海量点集合。\n根据激光测量原理得到的点云，包括三维坐标（XYZ）和激光反射强度（Intensity）。\n根据摄影测量原理得到的点云，包括三维坐标（XYZ）和颜色信息（RGB）。\n结合激光测量和摄影测量原理得到点云，包括三维坐标（XYZ）、激光反射强度（Intensity）和颜色信息（RGB）。\n在获取物体表面每个采样点的空间坐标后，得到的是一个点的集合，称之为“点云”(Point Cloud)。\n\npcd文件存储了每个点的x,y,z坐标以及r,g,b,a颜色信息\n\n*/\nnamespace cartographer {\nnamespace io {\n\n/*\nPcdWritingPointsProcessor是PointsProcessor的第九个子类(9)\n.\nPcdWritingPointsProcessor类将点云按照pcd格式存储在pcd文件中.\n1,构造函数初始化一个文件类名,\n2,成员函数FromDictionary 从配置文件读取 文件名\n3,Process()和Flush()负责写入文件\n4,数据成员next_指向 下一阶段的PointsProcessor点云处理类\n*/\n// Streams a PCD file to disk. The header is written in 'Flush'.\nclass PcdWritingPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName = \"write_pcd\";\n  PcdWritingPointsProcessor(std::unique_ptr<FileWriter> file_writer,\n                            PointsProcessor* next);\n\n  static std::unique_ptr<PcdWritingPointsProcessor> FromDictionary(\n      FileWriterFactory file_writer_factory,\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~PcdWritingPointsProcessor() override {}\n\n  PcdWritingPointsProcessor(const PcdWritingPointsProcessor&) = delete;\n  PcdWritingPointsProcessor& operator=(const PcdWritingPointsProcessor&) =\n      delete;\n\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  PointsProcessor* const next_;\n\n  int64 num_points_;\n  bool has_colors_;\n  std::unique_ptr<FileWriter> file_writer_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/ply_writing_points_processor.cc",
    "content": "\n#include \"cartographer/io/ply_writing_points_processor.h\"\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/io/points_batch.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\nnamespace {\n\n/*\n按照ply格式写入ply的head信息.此函数在最后写入.\nwhy?num_points_只有最后才知道.之前尚未统计完成.\n\n*/\n// Writes the PLY header claiming 'num_points' will follow it into\n// 'output_file'.\nvoid WriteBinaryPlyHeader(const bool has_color, const int64 num_points,\n                          FileWriter* const file_writer) {\n  string color_header = !has_color ? \"\"\n                                   : \"property uchar red\\n\"\n                                     \"property uchar green\\n\"\n                                     \"property uchar blue\\n\";\n  std::ostringstream stream;\n  stream << \"ply\\n\"\n         << \"format binary_little_endian 1.0\\n\"\n         << \"comment generated by Cartographer\\n\"\n         << \"element vertex \" << std::setw(15) << std::setfill('0')\n         << num_points << \"\\n\"\n         << \"property float x\\n\"\n         << \"property float y\\n\"\n         << \"property float z\\n\"\n         << color_header << \"end_header\\n\";\n  const string out = stream.str();\n  CHECK(file_writer->WriteHeader(out.data(), out.size()));\n}\n\n/*\n往file_writer写入某一point对应的{x,y,z}\n*/\nvoid WriteBinaryPlyPointCoordinate(const Eigen::Vector3f& point,\n                                   FileWriter* const file_writer) {\n  char buffer[12];\n  memcpy(buffer, &point[0], sizeof(float));\n  memcpy(buffer + 4, &point[1], sizeof(float));\n  memcpy(buffer + 8, &point[2], sizeof(float));\n  CHECK(file_writer->Write(buffer, 12));\n}\n\n/*\n往file_writer写入rgb值\n*/\nvoid WriteBinaryPlyPointColor(const Color& color,\n                              FileWriter* const file_writer) {\n  CHECK(file_writer->Write(reinterpret_cast<const char*>(color.data()),\n                           color.size()));\n}\n\n}  // namespace\n\n/*\n根据.lua文件的filename创建file_writer_factory\n*/\nstd::unique_ptr<PlyWritingPointsProcessor>\nPlyWritingPointsProcessor::FromDictionary(\n    FileWriterFactory file_writer_factory,\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  return common::make_unique<PlyWritingPointsProcessor>(\n      file_writer_factory(dictionary->GetString(\"filename\")), next);\n}\n\n/*\n构造函数,默认点云数是0,无rgb值\n*/\nPlyWritingPointsProcessor::PlyWritingPointsProcessor(\n    std::unique_ptr<FileWriter> file_writer, PointsProcessor* const next)\n    : next_(next),\n      num_points_(0),\n      has_colors_(false),\n      file_(std::move(file_writer)) {}\n\n/*\n在最后阶段才写入head,但不是在文件最后写入head,而是out_.seekp(0); 偏移到0处\n*/\nPointsProcessor::FlushResult PlyWritingPointsProcessor::Flush() {\n  WriteBinaryPlyHeader(has_colors_, num_points_, file_.get());\n  CHECK(file_->Close()) << \"Closing PLY file_writer failed.\";\n\n  switch (next_->Flush()) {\n    case FlushResult::kFinished:\n      return FlushResult::kFinished;\n\n    case FlushResult::kRestartStream:\n      LOG(FATAL) << \"PLY generation must be configured to occur after any \"\n                    \"stages that require multiple passes.\";\n  }\n  LOG(FATAL);\n}\n\n/*\n真正的写入点云PointsBatch,整个类的核心在这个函数.\n*/\nvoid PlyWritingPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {\n  if (batch->points.empty()) { //点云为空,则不写入磁盘,直接进行下一Process环节.\n    next_->Process(std::move(batch));\n    return;\n  }\n\n  if (num_points_ == 0) {//点云num为0则写入rgb,和head\n    has_colors_ = !batch->colors.empty();\n    WriteBinaryPlyHeader(has_colors_, 0, file_.get());\n    //file_.get()获取指针指针对应的raw指针\n  }\n  if (has_colors_) { //rgb的vector长度和point的vecor长度必须一致.\n    CHECK_EQ(batch->points.size(), batch->colors.size())\n        << \"First PointsBatch had colors, but encountered one without. \"\n           \"frame_id: \"\n        << batch->frame_id;\n  }\n\n//函数核心,开始写入每一个point.\n  for (size_t i = 0; i < batch->points.size(); ++i) {\n    WriteBinaryPlyPointCoordinate(batch->points[i], file_.get());\n    if (has_colors_) {\n      WriteBinaryPlyPointColor(batch->colors[i], file_.get());\n    }\n    ++num_points_;\n  }\n  next_->Process(std::move(batch)); //继续流水线操作.\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/ply_writing_points_processor.h",
    "content": "\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/file_writer.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\nPlyWritingPointsProcessor是PointsProcessor的第五个子类(5).\nPlyWritingPointsProcessor负责将点云point以PLY的格式写入磁盘.\n\n*/\n// Streams a PLY file to disk. The header is written in 'Flush'.\nclass PlyWritingPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName = \"write_ply\";\n  \n  // 指定文件写入的工厂和流水线next指针。\n  PlyWritingPointsProcessor(std::unique_ptr<FileWriter> file,\n                            PointsProcessor* next);\n\n  static std::unique_ptr<PlyWritingPointsProcessor> FromDictionary(\n      FileWriterFactory file_writer_factory,\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~PlyWritingPointsProcessor() override {}\n\n  PlyWritingPointsProcessor(const PlyWritingPointsProcessor&) = delete;\n  PlyWritingPointsProcessor& operator=(const PlyWritingPointsProcessor&) =\n      delete;\n//按照ply格式写点云信息 。然后直接流水线到下一PointsProcessor\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  PointsProcessor* const next_;\n\n  int64 num_points_;\n  bool has_colors_;//是否是RGB\n  std::unique_ptr<FileWriter> file_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/points_batch.cc",
    "content": "\n#include \"cartographer/io/points_batch.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\n先对to_remove做降序排列,然后按照to_remove依次移除某些point.\n\nbatch->points是vector,index 是要移除的vector元素的索引.\n\n时间复杂度:o(m*n),m是to_remove的大小,n是to_remove[0]到batch的end()的距离.\n\n注:erase()的复杂度是:\nLinear on the number of elements erased (destructions)*\nthe number of elements after the last element deleted (moving).\n返回值是一个迭代器，指向删除元素下一个元素;\n使用erase()要注意迭代器失效的问题.\n\n使用降序排序,即从end开始erase()可避免失效\n\n*/\nvoid RemovePoints(std::vector<int> to_remove, PointsBatch* batch) {\n  std::sort(to_remove.begin(), to_remove.end(), std::greater<int>());//降序排列\n  for (const int index : to_remove) {\n    batch->points.erase(batch->points.begin() + index);   //移除point\n    if (!batch->colors.empty()) {\n      batch->colors.erase(batch->colors.begin() + index); //point对应的rgb\n    }\n    if (!batch->intensities.empty()) {                    //移除光强度\n      batch->intensities.erase(batch->intensities.begin() + index); \n    }\n  }\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/points_batch.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_POINTS_BATCH_H_\n#define CARTOGRAPHER_IO_POINTS_BATCH_H_\n\n#include <array>\n#include <cstdint>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/time.h\"\n\nnamespace cartographer {\nnamespace io {\n\n// A point's color.\nusing Color = std::array<uint8_t, 3>;\n\n/*\nPointsBatch类是对多个点云point的抽象.这些point在由“同一时刻”,“同一机器人坐标地点”的传感器采集而得。\n数据成员主要描述了point的特性.\n\n1,time:point采集时间.\n2,origin:sensor的世界坐标\n3,frame_id:帧id\n4,trajectory_index:轨迹线id\n5,points:几何参数,vector<{x,y,z}>\n6,intensities:光强\n7,colors:point的rgb值\n*/\n\n\n// A number of points, captured around the same 'time' and by a\n// sensor at the same 'origin'.\nstruct PointsBatch {\n  PointsBatch() {\n    origin = Eigen::Vector3f::Zero();\n    trajectory_index = 0;\n  }\n\n  // Time at which this batch has been acquired. point被采集的时间点\n  common::Time time; \n\n  // Origin of the data, i.e. the location of the sensor in the world at\n  // 'time'. 传感器位姿\n  Eigen::Vector3f origin; \n\n  // Sensor that generated this data's 'frame_id' or empty if this information\n  // is unknown. 关键帧的id\n  string frame_id;\n\n  // Trajectory index that produced this point. 轨迹线\n  int trajectory_index;\n\n  // Geometry of the points in a metric frame. 公制，point的几何参数\n  std::vector<Eigen::Vector3f> points;\n\n  // Intensities are optional and may be unspecified. The meaning of these\n  // intensity values varies by device. For example, the VLP16 provides values\n  // in the range [0, 100] for non-specular return values and values up to 255\n  // for specular returns. On the other hand, Hokuyo lasers provide a 16-bit\n  // value that rarely peaks above 4096.\n  std::vector<float> intensities; //光强度，可选项\n\n  // Colors are optional. If set, they are RGB values.彩色rgb数字\n  std::vector<Color> colors;\n};\n\n// Removes the indices in 'to_remove' from 'batch'.\n// 按照to_temove中的索引,在batch中移除某些point.\nvoid RemovePoints(std::vector<int> to_remove, PointsBatch* batch);\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_POINTS_BATCH_H_\n"
  },
  {
    "path": "io/points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_POINTS_PROCESSOR_H_\n\n#include <memory>\n\n#include \"cartographer/io/points_batch.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\nPointsProcessor 点云虚基类,提供一种批量处理points的抽象接口,不可拷贝/赋值\n2个抽象接口:\n1),Process()负责对PointsBatch进行处理\n2),Flush()刷新.\n在assets_writer_backpack_2d.lua文件中有各个pipeline的处理流程.\n\n*/\n// A processor in a pipeline. It processes a 'points_batch' and hands it to the\n// next processor in the pipeline.\nclass PointsProcessor {\n public:\n  enum class FlushResult {\n    kRestartStream,\n    kFinished,\n  };\n\n  PointsProcessor() {}\n  virtual ~PointsProcessor() {} //必须为虚函数,不然子类无法正确析构.\n\n  PointsProcessor(const PointsProcessor&) = delete;\n  PointsProcessor& operator=(const PointsProcessor&) = delete;\n\n  // Receive a 'points_batch', process it and pass it on.\n  virtual void Process(std::unique_ptr<PointsBatch> points_batch) = 0;//纯虚函数\n\n  // Some implementations will perform expensive computations and others that do\n  // multiple passes over the data might ask for restarting the stream.\n  virtual FlushResult Flush() = 0;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/points_processor_pipeline_builder.cc",
    "content": "\n\n#include \"cartographer/io/points_processor_pipeline_builder.h\"\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/io/coloring_points_processor.h\"\n#include \"cartographer/io/counting_points_processor.h\"\n#include \"cartographer/io/fixed_ratio_sampling_points_processor.h\"\n#include \"cartographer/io/intensity_to_color_points_processor.h\"\n#include \"cartographer/io/min_max_range_filtering_points_processor.h\"\n#include \"cartographer/io/null_points_processor.h\"\n#include \"cartographer/io/outlier_removing_points_processor.h\"\n#include \"cartographer/io/pcd_writing_points_processor.h\"\n#include \"cartographer/io/ply_writing_points_processor.h\"\n#include \"cartographer/io/xray_points_processor.h\"\n#include \"cartographer/io/xyz_writing_points_processor.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\n注册内置的points Processor处理类,\n只有1个参数，类型是PointsProcessorPipelineBuilder。\n\n具体做法是调用Builder的成员函数\nRegister(const std::string& name, FactoryFunction factory);\n\n第一参数是kConfigurationFileActionName\n\n第二参数是FactoryFunction,即为函数unique_ptr<> f(dictionary,next),\n返回值是调用每个Processor的成员函数FromDictionary()\n得到unique_ptr<PointsProcessor>.\n\n*/\ntemplate <typename PointsProcessorType>\nvoid RegisterPlainPointsProcessor(\n    PointsProcessorPipelineBuilder* const builder) \n\n{\n  builder->Register(\n      PointsProcessorType::kConfigurationFileActionName,\n\n      [](common::LuaParameterDictionary* const dictionary,\n         PointsProcessor* const next) \n      -> std::unique_ptr<PointsProcessor>//返回值是unique_ptr\n       {\n        return PointsProcessorType::FromDictionary(dictionary, next);\n      });\n}\n\n/*\nRegisterFileWritingPointsProcessor负责注册文件写入的Processor类。\n用于将points写入到文件。\n第一参数：管理文件写入的FileWriterFactory\n第二参数：PointsProcessorPipelineBuilder*\n\n*/\ntemplate <typename PointsProcessorType>\nvoid RegisterFileWritingPointsProcessor(\n    FileWriterFactory file_writer_factory,\n    PointsProcessorPipelineBuilder* const builder)\n     {\n  builder->Register(\n      PointsProcessorType::kConfigurationFileActionName,\n      [file_writer_factory](\n          common::LuaParameterDictionary* const dictionary,\n          PointsProcessor* const next)\n\n           -> std::unique_ptr<PointsProcessor> //返回值是unique_ptr\n\n           {\n        return PointsProcessorType::FromDictionary(file_writer_factory,\n                                                   dictionary, next);\n      });\n\n}\n\n/*.h文件对外的接口,主要的代码逻辑\n作用：依次注册所有的内置Processor类。\n\n*/\nvoid RegisterBuiltInPointsProcessors(\n    const mapping::proto::Trajectory& trajectory,\n    FileWriterFactory file_writer_factory,\n    PointsProcessorPipelineBuilder* builder) \n\n{\n  //注册多个非文件相关的Processor实例对象\n  RegisterPlainPointsProcessor<CountingPointsProcessor>(builder);\n  \n  RegisterPlainPointsProcessor<FixedRatioSamplingPointsProcessor>(builder);\n  RegisterPlainPointsProcessor<MinMaxRangeFiteringPointsProcessor>(builder);\n  RegisterPlainPointsProcessor<OutlierRemovingPointsProcessor>(builder);\n  RegisterPlainPointsProcessor<ColoringPointsProcessor>(builder);\n  RegisterPlainPointsProcessor<IntensityToColorPointsProcessor>(builder);\n    \n    //注册多个file Processor实例对象\n  RegisterFileWritingPointsProcessor<PcdWritingPointsProcessor>(        \n      file_writer_factory, builder);\n  RegisterFileWritingPointsProcessor<PlyWritingPointsProcessor>(\n      file_writer_factory, builder);\n  RegisterFileWritingPointsProcessor<XyzWriterPointsProcessor>(   \n      file_writer_factory, builder);\n\n//特殊：轨迹线传引用&id，用于区别不同建图楼层。\n  // X-Ray is an odd ball(古怪的，特殊的) since it requires the trajectory to figure out the\n  // different building levels we walked on to separate the images.\n  builder->Register(\n      XRayPointsProcessor::kConfigurationFileActionName,\n      [&trajectory, file_writer_factory]\n\n      (\n          common::LuaParameterDictionary* const dictionary,\n          PointsProcessor* const next\n          ) \n\n      -> std::unique_ptr<PointsProcessor> \n\n      {\n        return XRayPointsProcessor::FromDictionary(\n            trajectory, file_writer_factory, dictionary, next);\n      });\n}\n\n\n//将用name标识的class添加到hash表中\nvoid PointsProcessorPipelineBuilder::Register(const std::string& name,\n                                              FactoryFunction factory) {\n  CHECK(factories_.count(name) == 0) << \"A points processor named '\" << name\n                                     << \"' has already been registered.\";\n  factories_[name] = factory;\n}\n\nPointsProcessorPipelineBuilder::PointsProcessorPipelineBuilder() {}\n\n\n/*\n\n1）整个sensor文件夹下最核心的代码。根据.lua文件\n用于创建pipeline流水线，登记所有的PointsProcessor类。\n返回值pipeline 是points 处理类的集合。内含所有的class集合。\n\n2）pipeline是vector，元素类型是unique_ptr,指向处理point的class。\n\n3）第一个是空指针NullPointsProcessor类,最后调用,用于丢弃所有的points,\n其余的按照逆序添加到pipeline中\n*/\nstd::vector<std::unique_ptr<PointsProcessor>>\nPointsProcessorPipelineBuilder::CreatePipeline(\n    common::LuaParameterDictionary* const dictionary) const {\n  std::vector<std::unique_ptr<PointsProcessor>> pipeline;\n  // The last consumer in the pipeline must exist, so that the one created after\n  // it (and being before it in the pipeline) has a valid 'next' to point to.\n  // The last consumer will just drop all points.\n  pipeline.emplace_back(common::make_unique<NullPointsProcessor>());\n\n  std::vector<std::unique_ptr<common::LuaParameterDictionary>> configurations =\n      dictionary->GetArrayValuesAsDictionaries();\n\n//按action逆序，rbegin()，从尾部添加元素到pipeline中。\n  // We construct the pipeline starting at the back.\n  for (auto it = configurations.rbegin(); it != configurations.rend(); it++) {\n\nconst string action = (*it)->GetString(\"action\");//min_max_range_filter\nauto factory_it = factories_.find(action);   \n//类型是pair<,>，second是min_max对应的FactoryFunction\n\n    //只有在hash表中的action在可被添加：否则找不到函数实现。\n    CHECK(factory_it != factories_.end())\n        << \"Unknown action '\" << action\n        << \"'. Did you register the correspoinding PointsProcessor?\";\n    pipeline.push_back(factory_it->second(it->get(), pipeline.back().get()));\n/*注意：\n上述代码是vector.push_back( f(dict,next));\n内部是一个函数，返回unique_ptr,然后插入vector.\nvector.back()总是next的Processor。\n*/\n  }\n  return pipeline;\n}\n\n}  // namespace io\n}  // namespace cartographer\n\n/*\n\npipeline = {\n    {\n      action = \"min_max_range_filter\",--1\n      min_range = 1.,\n      max_range = 60.,\n    },\n    {\n      action = \"dump_num_points\",    --2\n    },\n\n    -- Gray X-Rays. These only use geometry to color pixels.\n    {\n      action = \"write_xray_image\",   --3\n      voxel_size = VOXEL_SIZE,\n      filename = \"xray_yz_all\",\n      transform = YZ_TRANSFORM,\n    },\n\n    ....\n}\n\n*/"
  },
  {
    "path": "io/points_processor_pipeline_builder.h",
    "content": "\n\n#ifndef CARTOGRAPHER_IO_POINTS_PROCESSOR_PIPELINE_BUILDER_H_\n#define CARTOGRAPHER_IO_POINTS_PROCESSOR_PIPELINE_BUILDER_H_\n\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/file_writer.h\"\n#include \"cartographer/io/points_processor.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n\nnamespace cartographer {\nnamespace io {\n\n/*\n使用Builder模式创建PointsProcessor对象：\n\nPointsProcessorPipelineBuilder类用于创建各个ProcessorPiont类对象\n\n调用RegisterBuiltInPointsProcessors()\n函数可用于创建所有cartographer内置的PointsProcessor类.\n*/\n\n// Builder to create a points processor pipeline out of a Lua configuration.\n// You can register all built-in PointsProcessors using\n// 'RegisterBuiltInPointsProcessors'. Non-built-in PointsProcessors must define\n// a name and a factory method for building itself from a\n// LuaParameterDictionary. See the various built-in PointsProcessors for\n// examples.\nclass PointsProcessorPipelineBuilder {\n public:\n\n  //FactoryFunction是函数f(dictionary,next); \n  //返回值为std::unique_ptr<PointsProcessor>\n  using FactoryFunction = std::function<std::unique_ptr<PointsProcessor> (\n      common::LuaParameterDictionary*, PointsProcessor* next) >;\n\n  PointsProcessorPipelineBuilder();\n\n  PointsProcessorPipelineBuilder(const PointsProcessorPipelineBuilder&) =\n      delete;\n  PointsProcessorPipelineBuilder& operator=(\n      const PointsProcessorPipelineBuilder&) = delete;\n\n  // Register a new PointsProcessor type uniquly identified by 'name' which will\n  // be created using 'factory'.\n  void Register(const std::string& name, FactoryFunction factory);\n\n  std::vector<std::unique_ptr<PointsProcessor>> CreatePipeline(\n      common::LuaParameterDictionary* dictionary) const;\n\n private:\n\n  //hash表,记录已有代码实现的points processor及其对应的function\n  //key是每个class都有的name。\n  std::unordered_map<std::string, FactoryFunction> factories_; \n\n};\n\n// Register all 'PointsProcessor' that ship with Cartographer with this\n// 'builder'.\nvoid RegisterBuiltInPointsProcessors(\n    const mapping::proto::Trajectory& trajectory,\n    FileWriterFactory file_writer_factory,\n    PointsProcessorPipelineBuilder* builder);\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_POINTS_PROCESSOR_PIPELINE_BUILDER_H_\n"
  },
  {
    "path": "io/proto_stream.cc",
    "content": "\n#include \"cartographer/io/proto_stream.h\"\n\nnamespace cartographer {\nnamespace io {\n\nnamespace {\n\n// First eight bytes to identify our proto stream format.\nconst size_t kMagic = 0x7b1d1f7b5bf501db;//文件头标识符\n\nvoid WriteSizeAsLittleEndian(size_t size, std::ostream* out) {\n  for (int i = 0; i != 8; ++i) {\n    out->put(size & 0xff);\n    size >>= 8;\n  }\n}\n\nbool ReadSizeAsLittleEndian(std::istream* in, size_t* size) {\n  *size = 0;\n  for (int i = 0; i != 8; ++i) {\n    *size >>= 8;\n    *size += static_cast<size_t>(in->get()) << 56;\n  }\n  return !in->fail();\n}\n\n}  // namespace\n\nProtoStreamWriter::ProtoStreamWriter(const string& filename)\n    : out_(filename, std::ios::out | std::ios::binary) {\n  WriteSizeAsLittleEndian(kMagic, &out_);\n}\n\nProtoStreamWriter::~ProtoStreamWriter() {}\n\nvoid ProtoStreamWriter::Write(const string& uncompressed_data) {\n  string compressed_data;\n  common::FastGzipString(uncompressed_data, &compressed_data);\n  WriteSizeAsLittleEndian(compressed_data.size(), &out_);\n  out_.write(compressed_data.data(), compressed_data.size());\n}\n\nbool ProtoStreamWriter::Close() {\n  out_.close();\n  return !out_.fail();\n}\n\nProtoStreamReader::ProtoStreamReader(const string& filename)\n    : in_(filename, std::ios::in | std::ios::binary) {\n  size_t magic;\n  if (!ReadSizeAsLittleEndian(&in_, &magic) || magic != kMagic) {\n    in_.setstate(std::ios::failbit);\n  }\n}\n\nProtoStreamReader::~ProtoStreamReader() {}\n\nbool ProtoStreamReader::Read(string* decompressed_data) {\n  size_t compressed_size;\n  if (!ReadSizeAsLittleEndian(&in_, &compressed_size)) {\n    return false;\n  }\n  string compressed_data(compressed_size, '\\0');\n  if (!in_.read(&compressed_data.front(), compressed_size)) {\n    return false;\n  }\n  common::FastGunzipString(compressed_data, decompressed_data);\n  return true;\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/proto_stream.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_PROTO_STREAM_H_\n#define CARTOGRAPHER_IO_PROTO_STREAM_H_\n\n#include <fstream>\n\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace io {\n\n\n/*\nProtoStreamWriter类用于将压缩的序列化数据protocol messages 写入文件\n\nProtoStreamReader类用于读取上述已经compressed的文件\n*/\n// A simple writer of a compressed sequence of protocol buffer messages to a\n// file. The format is not intended to be compatible with any other format used\n// outside of Cartographer.\n//\n// TODO(whess): Compress the file instead of individual messages for better\n// compression performance? Should we use LZ4?\nclass ProtoStreamWriter {\n public:\n  ProtoStreamWriter(const string& filename);\n  ~ProtoStreamWriter();\n\n  ProtoStreamWriter(const ProtoStreamWriter&) = delete;\n  ProtoStreamWriter& operator=(const ProtoStreamWriter&) = delete;\n\n  // Serializes, compressed and writes the 'proto' to the file.\n  template <typename MessageType>\n  void WriteProto(const MessageType& proto) {\n    string uncompressed_data;\n    proto.SerializeToString(&uncompressed_data);\n    Write(uncompressed_data); //Write()函数执行压缩过程。\n  }\n\n  // This should be called to check whether writing was successful.\n  bool Close();\n\n private:\n  void Write(const string& uncompressed_data);\n\n  std::ofstream out_;\n};\n\n// A reader of the format produced by ProtoStreamWriter.\nclass ProtoStreamReader {\n public:\n  ProtoStreamReader(const string& filename);\n  ~ProtoStreamReader();\n\n  ProtoStreamReader(const ProtoStreamReader&) = delete;\n  ProtoStreamReader& operator=(const ProtoStreamReader&) = delete;\n\n  template <typename MessageType>\n  bool ReadProto(MessageType* proto) {\n    string decompressed_data;\n    return Read(&decompressed_data) &&\n           proto->ParseFromString(decompressed_data);\n  }\n\n private:\n  bool Read(string* decompressed_data);\n\n  std::ifstream in_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_PROTO_STREAM_H_\n"
  },
  {
    "path": "io/proto_stream_test.cc",
    "content": "\n#include \"cartographer/io/proto_stream.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace io {\nnamespace {\n\nclass ProtoStreamTest : public ::testing::Test {\n protected:\n  void SetUp() override {\n    const string tmpdir = P_tmpdir;\n    test_directory_ = tmpdir + \"/proto_stream_test_XXXXXX\";\n    ASSERT_NE(mkdtemp(&test_directory_[0]), nullptr) << strerror(errno);\n  }\n\n  void TearDown() override { remove(test_directory_.c_str()); }\n\n  string test_directory_;\n};\n\nTEST_F(ProtoStreamTest, WriteAndReadBack) {\n  const string test_file = test_directory_ + \"/test_trajectory.pbstream\";\n  {\n    ProtoStreamWriter writer(test_file);\n    for (int i = 0; i != 10; ++i) {\n      mapping::proto::Trajectory trajectory;\n      trajectory.add_node()->set_timestamp(i);\n      writer.WriteProto(trajectory);\n    }\n    ASSERT_TRUE(writer.Close());\n  }\n  {\n    ProtoStreamReader reader(test_file);\n    for (int i = 0; i != 10; ++i) {\n      mapping::proto::Trajectory trajectory;\n      ASSERT_TRUE(reader.ReadProto(&trajectory));\n      ASSERT_EQ(1, trajectory.node_size());\n      EXPECT_EQ(i, trajectory.node(0).timestamp());\n    }\n    mapping::proto::Trajectory trajectory;\n    EXPECT_FALSE(reader.ReadProto(&trajectory));\n  }\n  remove(test_file.c_str());\n}\n\n}  // namespace\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/xray_points_processor.cc",
    "content": "\n#include \"cartographer/io/xray_points_processor.h\"\n\n#include <cmath>\n#include <string>\n\n#include \"Eigen/Core\"\n#include \"cairo/cairo.h\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/io/cairo_types.h\"\n#include \"cartographer/mapping/detect_floors.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\nnamespace cartographer {\nnamespace io {\nnamespace {\n\nstruct PixelData {\n  size_t num_occupied_cells_in_column = 0;\n  double mean_r = 0.;\n  double mean_g = 0.;\n  double mean_b = 0.;\n};\n\nusing PixelDataMatrix =\n    Eigen::Matrix<PixelData, Eigen::Dynamic, Eigen::Dynamic>;\n\ndouble Mix(const double a, const double b, const double t) {\n  return a * (1. - t) + t * b;\n}\n\n//cairo回调函数，将data写入FileWriter\ncairo_status_t CairoWriteCallback(void* const closure,\n                                  const unsigned char* data,\n                                  const unsigned int length) {\n  if (static_cast<FileWriter*>(closure)->Write(\n          reinterpret_cast<const char*>(data), length)) {\n    return CAIRO_STATUS_SUCCESS;\n  }\n  return CAIRO_STATUS_WRITE_ERROR;\n}\n\n/*\n使用cairo库将mat以png文件写入filename\n*/\n// Write 'mat' as a pleasing-to-look-at PNG into 'filename'\nvoid WritePng(const PixelDataMatrix& mat, FileWriter* const file_writer) {\n  const int stride =\n      cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, mat.cols());\n  CHECK_EQ(stride % 4, 0);\n  std::vector<uint32_t> pixels(stride / 4 * mat.rows(), 0.);\n\n  float max = std::numeric_limits<float>::min();\n  for (int y = 0; y < mat.rows(); ++y) {\n    for (int x = 0; x < mat.cols(); ++x) {\n      const PixelData& cell = mat(y, x);\n      if (cell.num_occupied_cells_in_column == 0.) {\n        continue;\n      }\n      max = std::max<float>(max, std::log(cell.num_occupied_cells_in_column));\n    }\n  }\n\n  for (int y = 0; y < mat.rows(); ++y) {\n    for (int x = 0; x < mat.cols(); ++x) {\n      const PixelData& cell = mat(y, x);\n      if (cell.num_occupied_cells_in_column == 0.) {\n        pixels[y * stride / 4 + x] =\n            (255 << 24) | (255 << 16) | (255 << 8) | 255;\n        continue;\n      }\n\n      // We use a logarithmic weighting for how saturated a pixel will be. The\n      // basic idea here was that walls (full height) are fully saturated, but\n      // details like chairs and tables are still well visible.\n      const float saturation =\n          std::log(cell.num_occupied_cells_in_column) / max;\n      double mean_r_in_column = (cell.mean_r / 255.);\n      double mean_g_in_column = (cell.mean_g / 255.);\n      double mean_b_in_column = (cell.mean_b / 255.);\n\n      double mix_r = Mix(1., mean_r_in_column, saturation);\n      double mix_g = Mix(1., mean_g_in_column, saturation);\n      double mix_b = Mix(1., mean_b_in_column, saturation);\n\n      const int r = common::RoundToInt(mix_r * 255.);\n      const int g = common::RoundToInt(mix_g * 255.);\n      const int b = common::RoundToInt(mix_b * 255.);\n      pixels[y * stride / 4 + x] = (255 << 24) | (r << 16) | (g << 8) | b;\n    }\n  }\n\n  // TODO(hrapp): cairo_image_surface_create_for_data does not take ownership of\n  // the data until the surface is finalized. Once it is finalized though,\n  // cairo_surface_write_to_png fails, complaining that the surface is already\n  // finalized. This makes it pretty hard to pass back ownership of the image to\n  // the caller.\n  cairo::UniqueSurfacePtr surface(\n      cairo_image_surface_create_for_data(\n          reinterpret_cast<unsigned char*>(pixels.data()), CAIRO_FORMAT_ARGB32,\n          mat.cols(), mat.rows(), stride),\n      cairo_surface_destroy);\n  CHECK_EQ(cairo_surface_status(surface.get()), CAIRO_STATUS_SUCCESS);\n  CHECK_EQ(cairo_surface_write_to_png_stream(surface.get(), &CairoWriteCallback,\n                                             file_writer),\n           CAIRO_STATUS_SUCCESS);\n  CHECK(file_writer->Close());\n}\n\nbool ContainedIn(const common::Time& time,\n                 const std::vector<mapping::Timespan>& timespans) {\n  for (const mapping::Timespan& timespan : timespans) {\n    if (timespan.start <= time && time <= timespan.end) {\n      return true;\n    }\n  }\n  return false;\n}\n\n}  // namespace\n\nXRayPointsProcessor::XRayPointsProcessor(\n    const double voxel_size, const transform::Rigid3f& transform,\n    const std::vector<mapping::Floor>& floors, const string& output_filename,\n    FileWriterFactory file_writer_factory, PointsProcessor* const next)\n    : next_(next),\n      file_writer_factory_(file_writer_factory),\n      floors_(floors),\n      output_filename_(output_filename),\n      transform_(transform) {\n  for (size_t i = 0; i < (floors_.empty() ? 1 : floors.size()); ++i) {\n    aggregations_.emplace_back(\n        Aggregation{mapping_3d::HybridGridBase<bool>(voxel_size), {}});\n  }\n}\n\nstd::unique_ptr<XRayPointsProcessor> XRayPointsProcessor::FromDictionary(\n    const mapping::proto::Trajectory& trajectory,\n    FileWriterFactory file_writer_factory,\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) {\n  std::vector<mapping::Floor> floors;\n  if (dictionary->HasKey(\"separate_floors\") &&\n      dictionary->GetBool(\"separate_floors\")) {\n    floors = mapping::DetectFloors(trajectory);\n  }\n\n  return common::make_unique<XRayPointsProcessor>(\n      dictionary->GetDouble(\"voxel_size\"),\n      transform::FromDictionary(dictionary->GetDictionary(\"transform\").get())\n          .cast<float>(),\n      floors, dictionary->GetString(\"filename\"), file_writer_factory, next);\n}\n\nvoid XRayPointsProcessor::WriteVoxels(const Aggregation& aggregation,\n                                      FileWriter* const file_writer) {\n  if (bounding_box_.isEmpty()) {\n    LOG(WARNING) << \"Not writing output: bounding box is empty.\";\n    return;\n  }\n\n  // Returns the (x, y) pixel of the given 'index'.\n  const auto voxel_index_to_pixel = [this](const Eigen::Array3i& index) {\n    // We flip the y axis, since matrices rows are counted from the top.\n    return Eigen::Array2i(bounding_box_.max()[1] - index[1],\n                          bounding_box_.max()[2] - index[2]);\n  };\n\n  // Hybrid grid uses X: forward, Y: left, Z: up.\n  // For the screen we are using. X: right, Y: up\n  const int xsize = bounding_box_.sizes()[1] + 1;\n  const int ysize = bounding_box_.sizes()[2] + 1;\n  PixelDataMatrix image = PixelDataMatrix(ysize, xsize);\n  for (mapping_3d::HybridGridBase<bool>::Iterator it(aggregation.voxels);\n       !it.Done(); it.Next()) {\n    const Eigen::Array3i cell_index = it.GetCellIndex();\n    const Eigen::Array2i pixel = voxel_index_to_pixel(cell_index);\n    PixelData& pixel_data = image(pixel.y(), pixel.x());\n    const auto& column_data = aggregation.column_data.at(\n        std::make_pair(cell_index[1], cell_index[2]));\n    pixel_data.mean_r = column_data.sum_r / column_data.count;\n    pixel_data.mean_g = column_data.sum_g / column_data.count;\n    pixel_data.mean_b = column_data.sum_b / column_data.count;\n    ++pixel_data.num_occupied_cells_in_column;\n  }\n  WritePng(image, file_writer);\n}\n\nvoid XRayPointsProcessor::Insert(const PointsBatch& batch,\n                                 const transform::Rigid3f& transform,\n                                 Aggregation* const aggregation) {\n  constexpr Color kDefaultColor = {{0, 0, 0}};\n  for (size_t i = 0; i < batch.points.size(); ++i) {\n    const Eigen::Vector3f camera_point = transform * batch.points[i];\n    const Eigen::Array3i cell_index =\n        aggregation->voxels.GetCellIndex(camera_point);\n    *aggregation->voxels.mutable_value(cell_index) = true;\n    bounding_box_.extend(cell_index.matrix());\n    ColumnData& column_data =\n        aggregation->column_data[std::make_pair(cell_index[1], cell_index[2])];\n    const auto& color =\n        batch.colors.empty() ? kDefaultColor : batch.colors.at(i);\n    column_data.sum_r += color[0];\n    column_data.sum_g += color[1];\n    column_data.sum_b += color[2];\n    ++column_data.count;\n  }\n}\n\nvoid XRayPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {\n  if (floors_.empty()) {\n    CHECK_EQ(aggregations_.size(), 1);\n    Insert(*batch, transform_, &aggregations_[0]);\n  } else {\n    for (size_t i = 0; i < floors_.size(); ++i) {\n      if (!ContainedIn(batch->time, floors_[i].timespans)) {\n        continue;\n      }\n      Insert(*batch, transform_, &aggregations_[i]);\n    }\n  }\n  next_->Process(std::move(batch));\n}\n\nPointsProcessor::FlushResult XRayPointsProcessor::Flush() {\n  if (floors_.empty()) {\n    CHECK_EQ(aggregations_.size(), 1);\n    WriteVoxels(aggregations_[0],\n                file_writer_factory_(output_filename_ + \".png\").get());\n  } else {\n    for (size_t i = 0; i < floors_.size(); ++i) {\n      WriteVoxels(\n          aggregations_[i],\n          file_writer_factory_(output_filename_ + std::to_string(i) + \".png\")\n              .get());\n    }\n  }\n\n  switch (next_->Flush()) {\n    case FlushResult::kRestartStream:\n      LOG(FATAL) << \"X-Ray generation must be configured to occur after any \"\n                    \"stages that require multiple passes.\";\n\n    case FlushResult::kFinished:\n      return FlushResult::kFinished;\n  }\n  LOG(FATAL);\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/xray_points_processor.h",
    "content": "#ifndef CARTOGRAPHER_IO_XRAY_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_XRAY_POINTS_PROCESSOR_H_\n\n#include <map>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/file_writer.h\"\n#include \"cartographer/io/points_processor.h\"\n#include \"cartographer/mapping/detect_floors.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\n/*配置文件概览：\nVOXEL_SIZE = 5e-2\n\nYZ_TRANSFORM =  {\n  translation = { 0., 0., 0. },\n  rotation = { 0. , 0., math.pi, },\n}\n\n{\n    action = \"write_xray_image\",\n    voxel_size = VOXEL_SIZE,\n    filename = \"xray_yz_all_intensity\",\n    transform = YZ_TRANSFORM,\n},\n\n*/\n\nnamespace cartographer {\nnamespace io {\n/*\nxray_points_processor.h是PointsProcessor的第三个子类(3).\n功能:将x射线以体素为voxel_size写入image。\n\n数据成员:\n1), \n2), \n\n*/\n// Creates X-ray cuts through the points with pixels being 'voxel_size' big.\nclass XRayPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName =\n      \"write_xray_image\";\n  XRayPointsProcessor(double voxel_size, const transform::Rigid3f& transform,\n                      const std::vector<mapping::Floor>& floors,\n                      const string& output_filename,\n                      FileWriterFactory file_writer_factory,\n                      PointsProcessor* next);\n\n  static std::unique_ptr<XRayPointsProcessor> FromDictionary(\n      const mapping::proto::Trajectory& trajectory,\n      FileWriterFactory file_writer_factory,\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~XRayPointsProcessor() override {}\n\n//将点云写入.png文件，然后流水到下一处理器\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n  Eigen::AlignedBox3i bounding_box() const { return bounding_box_; }\n\n private:\n  struct ColumnData {\n    double sum_r = 0.;\n    double sum_g = 0.;\n    double sum_b = 0.;\n    uint32_t count = 0;\n  };\n\n  struct Aggregation {\n    mapping_3d::HybridGridBase<bool> voxels;\n    std::map<std::pair<int, int>, ColumnData> column_data;\n  };\n\n  void WriteVoxels(const Aggregation& aggregation,\n                   FileWriter* const file_writer);\n  void Insert(const PointsBatch& batch, const transform::Rigid3f& transform,\n              Aggregation* aggregation);\n\n  PointsProcessor* const next_;\n  FileWriterFactory file_writer_factory_; //负责文件写入的工厂\n\n  // If empty, we do not separate into floors.\n  std::vector<mapping::Floor> floors_; //楼层\n\n  const string output_filename_;       //文件名称\n  const transform::Rigid3f transform_; //点云涉及的变换\n\n  // Only has one entry if we do not separate into floors.\n  std::vector<Aggregation> aggregations_; //聚集数据\n\n  // Bounding box containing all cells with data in all 'aggregations_'.\n  Eigen::AlignedBox3i bounding_box_; //边界框\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_XRAY_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "io/xyz_writing_points_processor.cc",
    "content": "#include \"cartographer/io/xyz_writing_points_processor.h\"\n\n#include <iomanip>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace io {\n\nnamespace {\n\n/*\n将x,y,x以字符串形式写入FileWriter\n*/\nvoid WriteXyzPoint(const Eigen::Vector3f& point,\n                   FileWriter* const file_writer) {\n  std::ostringstream stream;\n  stream << std::setprecision(6);\n  stream << point.x() << \" \" << point.y() << \" \" << point.z() << \"\\n\";\n  const string out = stream.str();\n  CHECK(file_writer->Write(out.data(), out.size()));\n}\n\n}  // namespace\n\n/*\n构造函数初始化\n*/\nXyzWriterPointsProcessor::XyzWriterPointsProcessor(\n    std::unique_ptr<FileWriter> file_writer, PointsProcessor* const next)\n    : next_(next), file_writer_(std::move(file_writer)) {}\n\n/*\n根据filename返回指向文件名的file_writer_factory\n*/\nstd::unique_ptr<XyzWriterPointsProcessor>\nXyzWriterPointsProcessor::FromDictionary\n(\n    FileWriterFactory file_writer_factory,\n    common::LuaParameterDictionary* const dictionary,\n    PointsProcessor* const next) \n{\n  return common::make_unique<XyzWriterPointsProcessor>(\n      file_writer_factory(dictionary->GetString(\"filename\")), next);\n}\n\n/*刷新缓冲*/\nPointsProcessor::FlushResult XyzWriterPointsProcessor::Flush() {\n  CHECK(file_writer_->Close()) << \"Closing XYZ file failed.\";\n  switch (next_->Flush()) {\n    case FlushResult::kFinished:\n      return FlushResult::kFinished;\n\n    case FlushResult::kRestartStream:\n      LOG(FATAL) << \"XYZ generation must be configured to occur after any \"\n                    \"stages that require multiple passes.\";\n  }\n  LOG(FATAL);\n}\n\n/*\n主逻辑,以{x,y,z}写入文件\n*/\nvoid XyzWriterPointsProcessor::Process(std::unique_ptr<PointsBatch> batch) {\n  for (const Eigen::Vector3f& point : batch->points) {\n    WriteXyzPoint(point, file_writer_.get());\n  }\n  next_->Process(std::move(batch));\n}\n\n}  // namespace io\n}  // namespace cartographer\n"
  },
  {
    "path": "io/xyz_writing_points_processor.h",
    "content": "\n#ifndef CARTOGRAPHER_IO_XYZ_WRITING_POINTS_PROCESSOR_H_\n#define CARTOGRAPHER_IO_XYZ_WRITING_POINTS_PROCESSOR_H_\n\n#include <fstream>\n#include <memory>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/io/file_writer.h\"\n#include \"cartographer/io/points_processor.h\"\n\nnamespace cartographer {\nnamespace io {\n/*\n\nXyzWriterPointsProcessor将points以{x,y,z}形式写入 FileWriter\n\n*/\n// Writes ASCII xyz points.\nclass XyzWriterPointsProcessor : public PointsProcessor {\n public:\n  constexpr static const char* kConfigurationFileActionName = \"write_xyz\";\n\n  XyzWriterPointsProcessor(std::unique_ptr<FileWriter>, PointsProcessor* next);\n\n  static std::unique_ptr<XyzWriterPointsProcessor> FromDictionary(\n      FileWriterFactory file_writer_factory,\n      common::LuaParameterDictionary* dictionary, PointsProcessor* next);\n\n  ~XyzWriterPointsProcessor() override {}\n\n  XyzWriterPointsProcessor(const XyzWriterPointsProcessor&) = delete;\n  XyzWriterPointsProcessor& operator=(const XyzWriterPointsProcessor&) = delete;\n\n  void Process(std::unique_ptr<PointsBatch> batch) override;\n  FlushResult Flush() override;\n\n private:\n  PointsProcessor* const next_;\n  std::unique_ptr<FileWriter> file_writer_;\n};\n\n}  // namespace io\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_IO_XYZ_WRITING_POINTS_PROCESSOR_H_\n"
  },
  {
    "path": "kalman_filter/gaussian_distribution.h",
    "content": "\n#ifndef CARTOGRAPHER_KALMAN_FILTER_GAUSSIAN_DISTRIBUTION_H_\n#define CARTOGRAPHER_KALMAN_FILTER_GAUSSIAN_DISTRIBUTION_H_\n\n#include \"Eigen/Cholesky\"\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n\nnamespace cartographer {\nnamespace kalman_filter {\n/*\n高斯分布类\n构造函数是N*1的均值矩阵和N*N的协方差矩阵\n\n*/\ntemplate <typename T, int N>\nclass GaussianDistribution {\n public:\n  GaussianDistribution(const Eigen::Matrix<T, N, 1>& mean,\n                       const Eigen::Matrix<T, N, N>& covariance)\n      : mean_(mean), covariance_(covariance) {}\n\n  const Eigen::Matrix<T, N, 1>& GetMean() const { return mean_; }\n\n  const Eigen::Matrix<T, N, N>& GetCovariance() const { return covariance_; }\n\n private:\n  Eigen::Matrix<T, N, 1> mean_;       //N*1，均值\n  Eigen::Matrix<T, N, N> covariance_; //N*N。协方差\n};\n\n/*\n重载+加号操作符,\n高斯+高斯=对应均值+均值,对应协方差+协方差\n返回值：新高斯对象\n高斯分布性质:\nhttps://zh.wikipedia.org/wiki/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83\n*/\ntemplate <typename T, int N>\nGaussianDistribution<T, N> operator+(const GaussianDistribution<T, N>& lhs,\n                                     const GaussianDistribution<T, N>& rhs) {\n  return GaussianDistribution<T, N>(lhs.GetMean() + rhs.GetMean(),\n                                    lhs.GetCovariance() + rhs.GetCovariance());\n}\n\n/*\n重载乘法运算符\n1,矩阵N*M\n2,高斯分布M*M\n\n返回值:高斯分布：N*N\n\n\n*/\ntemplate <typename T, int N, int M>\nGaussianDistribution<T, N> operator*(const Eigen::Matrix<T, N, M>& lhs,\n                                     const GaussianDistribution<T, M>& rhs) {\n  return GaussianDistribution<T, N>(\n      lhs * rhs.GetMean(),                          // N*M || M*1 -> N*1\n\n      lhs * rhs.GetCovariance() * lhs.transpose()); // N*M ||M*M || M*N ->  N*N\n}\n\n}  // namespace kalman_filter\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_KALMAN_FILTER_GAUSSIAN_DISTRIBUTION_H_\n"
  },
  {
    "path": "kalman_filter/gaussian_distribution_test.cc",
    "content": "#include \"cartographer/kalman_filter/gaussian_distribution.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace kalman_filter {\nnamespace {\n\nTEST(GaussianDistributionTest, testConstructor) {\n  Eigen::Matrix2d covariance;//2*2\n  covariance << 1., 2., 3., 4.;\n  GaussianDistribution<double, 2> distribution(Eigen::Vector2d(0., 1.),\n  //均值是0,1\n                                               covariance);\n  EXPECT_NEAR(0., distribution.GetMean()[0], 1e-9);//0列的的均值是0\n  EXPECT_NEAR(1., distribution.GetMean()[1], 1e-9);\n  EXPECT_NEAR(2., distribution.GetCovariance()(0, 1), 1e-9);//0行1列的方差是2\n}\n\n}  // namespace\n}  // namespace kalman_filter\n}  // namespace cartographer\n"
  },
  {
    "path": "kalman_filter/pose_tracker.cc",
    "content": "\n#include \"cartographer/kalman_filter/pose_tracker.h\"\n\n#include <cmath>\n#include <limits>\n#include <utility>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/kalman_filter/gaussian_distribution.h\"\n#include \"cartographer/kalman_filter/unscented_kalman_filter.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace kalman_filter {\n\nnamespace {\n\n/*定义函数AddDelta,当前状态State加上delta.\n*/\nPoseTracker::State AddDelta(const PoseTracker::State& state,\n                            const PoseTracker::State& delta) {\n  PoseTracker::State new_state = state + delta;\n  const Eigen::Quaterniond orientation =\n      transform::AngleAxisVectorToRotationQuaternion(\n          Eigen::Vector3d(state[PoseTracker::kMapOrientationX],\n                          state[PoseTracker::kMapOrientationY],\n                          state[PoseTracker::kMapOrientationZ]));\n  const Eigen::Vector3d rotation_vector(delta[PoseTracker::kMapOrientationX],\n                                        delta[PoseTracker::kMapOrientationY],\n                                        delta[PoseTracker::kMapOrientationZ]);\n  CHECK_LT(rotation_vector.norm(), M_PI / 2.)\n      << \"Sigma point is far from the mean, recovered delta may be incorrect.\";\n  const Eigen::Quaterniond rotation =\n      transform::AngleAxisVectorToRotationQuaternion(rotation_vector);\n  const Eigen::Vector3d new_orientation =\n      transform::RotationQuaternionToAngleAxisVector(orientation * rotation);\n  new_state[PoseTracker::kMapOrientationX] = new_orientation.x();\n  new_state[PoseTracker::kMapOrientationY] = new_orientation.y();\n  new_state[PoseTracker::kMapOrientationZ] = new_orientation.z();\n  return new_state;\n}\n\n/*定义ComputeDelta()函数,计算State的差值delta\n*/\nPoseTracker::State ComputeDelta(const PoseTracker::State& origin,\n                                const PoseTracker::State& target) {\n  PoseTracker::State delta = target - origin;\n  const Eigen::Quaterniond origin_orientation =\n      transform::AngleAxisVectorToRotationQuaternion(\n          Eigen::Vector3d(origin[PoseTracker::kMapOrientationX],\n                          origin[PoseTracker::kMapOrientationY],\n                          origin[PoseTracker::kMapOrientationZ]));\n  const Eigen::Quaterniond target_orientation =\n      transform::AngleAxisVectorToRotationQuaternion(\n          Eigen::Vector3d(target[PoseTracker::kMapOrientationX],\n                          target[PoseTracker::kMapOrientationY],\n                          target[PoseTracker::kMapOrientationZ]));\n  const Eigen::Vector3d rotation =\n      transform::RotationQuaternionToAngleAxisVector(\n          origin_orientation.inverse() * target_orientation);\n  delta[PoseTracker::kMapOrientationX] = rotation.x();\n  delta[PoseTracker::kMapOrientationY] = rotation.y();\n  delta[PoseTracker::kMapOrientationZ] = rotation.z();\n  return delta;\n}\n\n// Build a model matrix for the given time delta.返回delta时间后的状态\nPoseTracker::State ModelFunction(const PoseTracker::State& state,\n                                 const double delta_t) {\n  CHECK_GT(delta_t, 0.);\n\n  PoseTracker::State new_state;\n  new_state[PoseTracker::kMapPositionX] =  //x=x+delta * v\n      state[PoseTracker::kMapPositionX] +\n      delta_t * state[PoseTracker::kMapVelocityX];\n\n  new_state[PoseTracker::kMapPositionY] =  //y=y+delta* v\n      state[PoseTracker::kMapPositionY] +\n      delta_t * state[PoseTracker::kMapVelocityY];\n\n  new_state[PoseTracker::kMapPositionZ] =  //z=z+ delta* v\n      state[PoseTracker::kMapPositionZ] +\n      delta_t * state[PoseTracker::kMapVelocityZ];\n\n  new_state[PoseTracker::kMapOrientationX] =//不变\n      state[PoseTracker::kMapOrientationX];\n  new_state[PoseTracker::kMapOrientationY] =\n      state[PoseTracker::kMapOrientationY];\n  new_state[PoseTracker::kMapOrientationZ] =\n      state[PoseTracker::kMapOrientationZ];\n\n  new_state[PoseTracker::kMapVelocityX] = state[PoseTracker::kMapVelocityX];//不变\n  new_state[PoseTracker::kMapVelocityY] = state[PoseTracker::kMapVelocityY];\n  new_state[PoseTracker::kMapVelocityZ] = state[PoseTracker::kMapVelocityZ];\n\n  return new_state;\n}\n\n}  // namespace\n\n/*\n定义乘法操作,C=A*B\n*/\nPoseAndCovariance operator*(const transform::Rigid3d& transform,\n                            const PoseAndCovariance& pose_and_covariance) {\n\n  GaussianDistribution<double, 6> distribution(                             // 6行1列\n      Eigen::Matrix<double, 6, 1>::Zero(), pose_and_covariance.covariance);\n\n  Eigen::Matrix<double, 6, 6> linear_transform;                             //6行6列\n\n  linear_transform << transform.rotation().matrix(), Eigen::Matrix3d::Zero(),\n      Eigen::Matrix3d::Zero(), transform.rotation().matrix();\n\n  return {transform * pose_and_covariance.pose,\n          (linear_transform * distribution).GetCovariance()};\n}\n/*\ntrajectory_builder_3d.lua:\n   pose_tracker = {\n      orientation_model_variance = 5e-3,\n      position_model_variance = 0.00654766,\n      velocity_model_variance = 0.53926,\n      -- This disables gravity alignment in local SLAM.\n      imu_gravity_time_constant = 1e9,\n      imu_gravity_variance = 0,\n      num_odometry_states = 1,\n    },\n*/\nproto::PoseTrackerOptions CreatePoseTrackerOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::PoseTrackerOptions options;\n  options.set_position_model_variance(\n      parameter_dictionary->GetDouble(\"position_model_variance\"));\n  options.set_orientation_model_variance(\n      parameter_dictionary->GetDouble(\"orientation_model_variance\"));\n  options.set_velocity_model_variance(\n      parameter_dictionary->GetDouble(\"velocity_model_variance\"));\n  options.set_imu_gravity_time_constant(\n      parameter_dictionary->GetDouble(\"imu_gravity_time_constant\"));\n  options.set_imu_gravity_variance(\n      parameter_dictionary->GetDouble(\"imu_gravity_variance\"));\n  options.set_num_odometry_states(\n      parameter_dictionary->GetNonNegativeInt(\"num_odometry_states\"));\n  CHECK_GT(options.num_odometry_states(), 0);\n  return options;\n}\n\n\nPoseTracker::Distribution PoseTracker::KalmanFilterInit() {\n  State initial_state = State::Zero();\n  // We are certain about the complete state at the beginning. We define the\n  // initial pose to be at the origin and axis aligned. Additionally, we claim\n  // that we are not moving.\n  StateCovariance initial_covariance = 1e-9 * StateCovariance::Identity();\n  return Distribution(initial_state, initial_covariance);\n}\n\n\nPoseTracker::PoseTracker(const proto::PoseTrackerOptions& options,\n                         const common::Time time)\n    : options_(options),\n      time_(time),\n      kalman_filter_(KalmanFilterInit(), AddDelta, ComputeDelta),\n      imu_tracker_(options.imu_gravity_time_constant(), time),\n      odometry_state_tracker_(options.num_odometry_states()) {}\n\nPoseTracker::~PoseTracker() {}\n\nPoseTracker::Distribution PoseTracker::GetBelief(const common::Time time) {\n  Predict(time);\n  return kalman_filter_.GetBelief();\n}\n\nvoid PoseTracker::GetPoseEstimateMeanAndCovariance(const common::Time time,\n                                                   transform::Rigid3d* pose,\n                                                   PoseCovariance* covariance) {\n  const Distribution belief = GetBelief(time);\n  *pose = RigidFromState(belief.GetMean());\n  static_assert(kMapPositionX == 0, \"Cannot extract PoseCovariance.\");\n  static_assert(kMapPositionY == 1, \"Cannot extract PoseCovariance.\");\n  static_assert(kMapPositionZ == 2, \"Cannot extract PoseCovariance.\");\n  static_assert(kMapOrientationX == 3, \"Cannot extract PoseCovariance.\");\n  static_assert(kMapOrientationY == 4, \"Cannot extract PoseCovariance.\");\n  static_assert(kMapOrientationZ == 5, \"Cannot extract PoseCovariance.\");\n  *covariance = belief.GetCovariance().block<6, 6>(0, 0);\n  covariance->block<2, 2>(3, 3) +=\n      options_.imu_gravity_variance() * Eigen::Matrix2d::Identity();\n}\n\nconst PoseTracker::Distribution PoseTracker::BuildModelNoise(\n    const double delta_t) const {\n  // Position is constant, but orientation changes.\n  StateCovariance model_noise = StateCovariance::Zero();\n\n  model_noise.diagonal() <<\n      // Position in map.\n      options_.position_model_variance() * delta_t,\n      options_.position_model_variance() * delta_t,\n      options_.position_model_variance() * delta_t,\n\n      // Orientation in map.\n      options_.orientation_model_variance() * delta_t,\n      options_.orientation_model_variance() * delta_t,\n      options_.orientation_model_variance() * delta_t,\n\n      // Linear velocities in map.\n      options_.velocity_model_variance() * delta_t,\n      options_.velocity_model_variance() * delta_t,\n      options_.velocity_model_variance() * delta_t;\n\n  return Distribution(State::Zero(), model_noise);\n}\n\nvoid PoseTracker::Predict(const common::Time time) {\n  imu_tracker_.Advance(time);\n  CHECK_LE(time_, time);\n  const double delta_t = common::ToSeconds(time - time_);\n  if (delta_t == 0.) {\n    return;\n  }\n  kalman_filter_.Predict(\n      [this, delta_t](const State& state) -> State {\n        return ModelFunction(state, delta_t);\n      },\n      BuildModelNoise(delta_t));\n  time_ = time;\n}\n\nvoid PoseTracker::AddImuLinearAccelerationObservation(\n    const common::Time time, const Eigen::Vector3d& imu_linear_acceleration) {\n  imu_tracker_.Advance(time);\n  imu_tracker_.AddImuLinearAccelerationObservation(imu_linear_acceleration);\n  Predict(time);\n}\n\nvoid PoseTracker::AddImuAngularVelocityObservation(\n    const common::Time time, const Eigen::Vector3d& imu_angular_velocity) {\n  imu_tracker_.Advance(time);\n  imu_tracker_.AddImuAngularVelocityObservation(imu_angular_velocity);\n  Predict(time);\n}\n\nvoid PoseTracker::AddPoseObservation(const common::Time time,\n                                     const transform::Rigid3d& pose,\n                                     const PoseCovariance& covariance) {\n  Predict(time);\n\n  // Noise covariance is taken directly from the input values.\n  const GaussianDistribution<double, 6> delta(\n      Eigen::Matrix<double, 6, 1>::Zero(), covariance);\n\n  kalman_filter_.Observe<6>(\n      [this, &pose](const State& state) -> Eigen::Matrix<double, 6, 1> {\n        const transform::Rigid3d state_pose = RigidFromState(state);\n        const Eigen::Vector3d delta_orientation =\n            transform::RotationQuaternionToAngleAxisVector(\n                pose.rotation().inverse() * state_pose.rotation());\n        const Eigen::Vector3d delta_translation =\n            state_pose.translation() - pose.translation();\n        Eigen::Matrix<double, 6, 1> return_value;\n        return_value << delta_translation, delta_orientation;\n        return return_value;\n      },\n      delta);\n}\n\n// Updates from the odometer are in the odometer's map-like frame, called the\n// 'odometry' frame. The odometer_pose converts data from the map frame\n// into the odometry frame.\nvoid PoseTracker::AddOdometerPoseObservation(\n    const common::Time time, const transform::Rigid3d& odometer_pose,\n    const PoseCovariance& covariance) {\n  if (!odometry_state_tracker_.empty()) {\n    const auto& previous_odometry_state = odometry_state_tracker_.newest();\n    const transform::Rigid3d delta =\n        previous_odometry_state.odometer_pose.inverse() * odometer_pose;\n    const transform::Rigid3d new_pose =\n        previous_odometry_state.state_pose * delta;\n    AddPoseObservation(time, new_pose, covariance);\n  }\n\n  const Distribution belief = GetBelief(time);\n\n  odometry_state_tracker_.AddOdometryState(\n      {time, odometer_pose, RigidFromState(belief.GetMean())});\n}\n\nconst mapping::OdometryStateTracker::OdometryStates&\nPoseTracker::odometry_states() const {\n  return odometry_state_tracker_.odometry_states();\n}\n\ntransform::Rigid3d PoseTracker::RigidFromState(\n    const PoseTracker::State& state) {\n  return transform::Rigid3d(\n      Eigen::Vector3d(state[PoseTracker::kMapPositionX],\n                      state[PoseTracker::kMapPositionY],\n                      state[PoseTracker::kMapPositionZ]),\n      transform::AngleAxisVectorToRotationQuaternion(\n          Eigen::Vector3d(state[PoseTracker::kMapOrientationX],\n                          state[PoseTracker::kMapOrientationY],\n                          state[PoseTracker::kMapOrientationZ])) *\n          imu_tracker_.orientation());\n}\n\nPoseCovariance BuildPoseCovariance(const double translational_variance,\n                                   const double rotational_variance) {\n  const Eigen::Matrix3d translational =\n      Eigen::Matrix3d::Identity() * translational_variance;\n  const Eigen::Matrix3d rotational =\n      Eigen::Matrix3d::Identity() * rotational_variance;\n  // clang-format off\n  PoseCovariance covariance;\n  covariance <<\n      translational, Eigen::Matrix3d::Zero(),\n      Eigen::Matrix3d::Zero(), rotational;\n  // clang-format on\n  return covariance;\n}\n\n}  // namespace kalman_filter\n}  // namespace cartographer\n"
  },
  {
    "path": "kalman_filter/pose_tracker.h",
    "content": "\n#ifndef CARTOGRAPHER_KALMAN_FILTER_POSE_TRACKER_H_\n#define CARTOGRAPHER_KALMAN_FILTER_POSE_TRACKER_H_\n\n#include <deque>\n#include <memory>\n\n#include \"Eigen/Cholesky\"\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/kalman_filter/gaussian_distribution.h\"\n#include \"cartographer/kalman_filter/proto/pose_tracker_options.pb.h\"\n#include \"cartographer/kalman_filter/unscented_kalman_filter.h\"\n#include \"cartographer/mapping/imu_tracker.h\"\n#include \"cartographer/mapping/odometry_state_tracker.h\"\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace kalman_filter {\n\ntypedef Eigen::Matrix3d Pose2DCovariance; //3*3矩阵\ntypedef Eigen::Matrix<double, 6, 6> PoseCovariance;// 6*6 矩阵\n\nstruct PoseAndCovariance {\n  transform::Rigid3d pose;\n  PoseCovariance covariance; //6*6\n};\n\nPoseAndCovariance operator*(const transform::Rigid3d& transform,\n                            const PoseAndCovariance& pose_and_covariance);\n\nPoseCovariance BuildPoseCovariance(double translational_variance,\n                                   double rotational_variance);\n\nproto::PoseTrackerOptions CreatePoseTrackerOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n/*\n基于卡尔曼滤波的3维位置和方向估计\n\n*/\n// A Kalman filter for a 3D state of position and orientation.\n// Includes functions to update from IMU and range data matches.\nclass PoseTracker {\n public:\n  enum {\n    kMapPositionX = 0,//位置信息{X,Y,Z}\n    kMapPositionY,\n    kMapPositionZ,\n    kMapOrientationX,//方向信息,3\n    kMapOrientationY,\n    kMapOrientationZ,\n    kMapVelocityX,   //速度信息,6\n    kMapVelocityY,\n    kMapVelocityZ,\n    kDimension  //9, We terminate loops with this. 只追踪9个维度\n  };\n\n  using KalmanFilter = UnscentedKalmanFilter<double, kDimension>;//9维的卡尔曼滤波\n  using State = KalmanFilter::StateType;                         //N*1矩阵\n  using StateCovariance = Eigen::Matrix<double, kDimension, kDimension>;//9*9\n  using Distribution = GaussianDistribution<double, kDimension>;\n   //参数类型的double,9*1的矩阵\n\n  // Create a new Kalman filter starting at the origin with a standard normal\n  // distribution at 'time'.\n  //在给定的time时刻初始化卡尔曼滤波参数\n  PoseTracker(const proto::PoseTrackerOptions& options, common::Time time);\n  virtual ~PoseTracker();\n\n  // Sets 'pose' and 'covariance' to the mean and covariance of the belief at\n  // 'time' which must be >= to the current time. Must not be nullptr.\n  //通过指针获取pose的旋转参数和covariance方差\n  void GetPoseEstimateMeanAndCovariance(common::Time time,\n                                        transform::Rigid3d* pose,\n                                        PoseCovariance* covariance);\n\n  // Updates from an IMU reading (in the IMU frame).\n  //根据imu观测值更新\n  void AddImuLinearAccelerationObservation(\n      common::Time time, const Eigen::Vector3d& imu_linear_acceleration);\n  void AddImuAngularVelocityObservation(\n      common::Time time, const Eigen::Vector3d& imu_angular_velocity);\n\n  // Updates from a pose estimate in the map frame.\n  //根据map-frame的位姿估计更新\n  void AddPoseObservation(common::Time time, const transform::Rigid3d& pose,\n                          const PoseCovariance& covariance);\n\n  // Updates from a pose estimate in the odometer's map-like frame.\n  //根据里程计的map-like frame位姿估计更新\n  void AddOdometerPoseObservation(common::Time time,\n                                  const transform::Rigid3d& pose,\n                                  const PoseCovariance& covariance);\n\n  common::Time time() const { return time_; } //最新有效时间\n\n  // Returns the belief at the 'time' which must be >= to the current time.\n  Distribution GetBelief(common::Time time); //未来某一时刻的状态估计值\n\n  const mapping::OdometryStateTracker::OdometryStates& odometry_states() const;\n\n//imu的重力方向\n  Eigen::Quaterniond gravity_orientation() const {\n    return imu_tracker_.orientation();\n  }\n\n private:\n  //返回初始状态的状态变量的高斯分布\n  // Returns the distribution required to initialize the KalmanFilter.\n  static Distribution KalmanFilterInit();\n\n  //建立零均值噪声模型\n  // Build a model noise distribution (zero mean) for the given time delta.\n  const Distribution BuildModelNoise(double delta_t) const;\n\n  // Predict the state forward in time. This is a no-op if 'time' has not moved\n  // forward.\n  //根据当前状态预测time时刻的状态\n  void Predict(common::Time time);\n\n  // Computes a pose combining the given 'state' with the 'imu_tracker_'\n  // orientation.\n  //结合 imu_tracker_和state,计算位姿pose的旋转变换。\n  transform::Rigid3d RigidFromState(const PoseTracker::State& state);\n\n  const proto::PoseTrackerOptions options_; //用于位姿估计的传感器特性\n  common::Time time_;                       //测量时间\n  KalmanFilter kalman_filter_;              //卡尔曼滤波\n  mapping::ImuTracker imu_tracker_;         //imu跟踪\n  mapping::OdometryStateTracker odometry_state_tracker_;//里程计跟踪\n};\n\n}  // namespace kalman_filter\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_KALMAN_FILTER_POSE_TRACKER_H_\n"
  },
  {
    "path": "kalman_filter/pose_tracker_test.cc",
    "content": "\n#include \"cartographer/kalman_filter/pose_tracker.h\"\n\n#include <random>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace kalman_filter {\nnamespace {\n\nconstexpr double kOdometerVariance = 1e-12;\n\nusing transform::IsNearly;\nusing transform::Rigid3d;\nusing ::testing::Not;\n\nclass PoseTrackerTest : public ::testing::Test {\n protected:\n  PoseTrackerTest() {\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n            orientation_model_variance = 1e-8,\n            position_model_variance = 1e-8,\n            velocity_model_variance = 1e-8,\n            imu_gravity_time_constant = 100.,\n            imu_gravity_variance = 1e-9,\n            num_odometry_states = 1,\n        }\n        )text\");\n    const proto::PoseTrackerOptions options =\n        CreatePoseTrackerOptions(parameter_dictionary.get());\n    pose_tracker_ =\n        common::make_unique<PoseTracker>(options, common::FromUniversal(1000)); //创建卡尔曼滤波跟踪器\n  }\n\n  std::unique_ptr<PoseTracker> pose_tracker_;\n};\n\nTEST_F(PoseTrackerTest, SaveAndRestore) {\n  std::vector<Rigid3d> poses(3);\n  std::vector<PoseCovariance> covariances(3);\n  //0号，获取t=1500处的预测值\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(common::FromUniversal(1500),\n                                                  &poses[0], &covariances[0]);\n\n  //0号，添加t=2000的测量值\n  pose_tracker_->AddImuLinearAccelerationObservation(\n      common::FromUniversal(2000), Eigen::Vector3d(1, 1, 9));\n\n  PoseTracker copy_of_pose_tracker = *pose_tracker_;//默认copy构造函数，1号\n\n  const Eigen::Vector3d observation(2, 0, 8);\n  //0号，添加t=3000的测量值\n  pose_tracker_->AddImuLinearAccelerationObservation(\n      common::FromUniversal(3000), observation);\n\n  //0号，获取t=3500的预测值\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(common::FromUniversal(3500),\n                                                  &poses[1], &covariances[1]);\n\n  //1号，添加t=3000的测量值\n  copy_of_pose_tracker.AddImuLinearAccelerationObservation(\n      common::FromUniversal(3000), observation);\n  //1号，获取t=3500的测量值\n  copy_of_pose_tracker.GetPoseEstimateMeanAndCovariance(\n      common::FromUniversal(3500), &poses[2], &covariances[2]);\n\n  EXPECT_THAT(poses[0], Not(IsNearly(poses[1], 1e-6)));\n  EXPECT_FALSE((covariances[0].array() == covariances[1].array()).all());\n\n  //相同的测量，导致相同的结果 \n  EXPECT_THAT(poses[1], IsNearly(poses[2], 1e-6));\n  EXPECT_TRUE((covariances[1].array() == covariances[2].array()).all());\n}\n\nTEST_F(PoseTrackerTest, AddImuLinearAccelerationObservation) {\n  auto time = common::FromUniversal(1000);\n\n  for (int i = 0; i < 300; ++i) {\n    time += std::chrono::seconds(5);\n    //循环300次，添加测量值\n    pose_tracker_->AddImuLinearAccelerationObservation(\n        time, Eigen::Vector3d(0., 0., 10.));\n  }\n\n  {\n    Rigid3d pose;\n    PoseCovariance covariance;\n    pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &pose, &covariance);\n    const Eigen::Quaterniond actual = Eigen::Quaterniond(pose.rotation());\n    const Eigen::Quaterniond expected = Eigen::Quaterniond::Identity();\n    //expected.coeffs():0,0,0,1\n    //预测值和真实值相等\n    EXPECT_TRUE(actual.isApprox(expected, 1e-3)) << expected.coeffs() << \" vs\\n\"\n                                                 << actual.coeffs();\n  }\n\n  for (int i = 0; i < 300; ++i) {\n    time += std::chrono::seconds(5);\n    pose_tracker_->AddImuLinearAccelerationObservation(\n        time, Eigen::Vector3d(0., 10., 0.));\n  }\n\n  time += std::chrono::milliseconds(5);\n\n  Rigid3d pose;\n  PoseCovariance covariance;\n  //获取预测值\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &pose, &covariance);\n  const Eigen::Quaterniond actual = Eigen::Quaterniond(pose.rotation());\n  const Eigen::Quaterniond expected = Eigen::Quaterniond(\n      Eigen::AngleAxisd(M_PI / 2., Eigen::Vector3d::UnitX()));\n  //预测值与真实值比较\n  EXPECT_TRUE(actual.isApprox(expected, 1e-3)) << expected.coeffs() << \" vs\\n\"\n                                               << actual.coeffs();\n}\n\nTEST_F(PoseTrackerTest, AddImuAngularVelocityObservation) {\n  auto time = common::FromUniversal(1000);\n\n  for (int i = 0; i < 300; ++i) {\n    time += std::chrono::milliseconds(5);\n    pose_tracker_->AddImuAngularVelocityObservation(time,\n                                                    Eigen::Vector3d::Zero());\n  }\n\n  {\n    Rigid3d pose;\n    PoseCovariance covariance;\n    pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &pose, &covariance);\n    const Eigen::Quaterniond actual = Eigen::Quaterniond(pose.rotation());\n    const Eigen::Quaterniond expected = Eigen::Quaterniond::Identity();\n    EXPECT_TRUE(actual.isApprox(expected, 1e-3)) << expected.coeffs() << \" vs\\n\"\n                                                 << actual.coeffs();\n  }\n\n  const double target_radians = M_PI / 2.;\n  const double num_observations = 300.;\n  const double angular_velocity = target_radians / (num_observations * 5e-3);\n  for (int i = 0; i < num_observations; ++i) {\n    time += std::chrono::milliseconds(5);\n    pose_tracker_->AddImuAngularVelocityObservation(\n        time, Eigen::Vector3d(angular_velocity, 0., 0.));\n  }\n\n  time += std::chrono::milliseconds(5);\n\n  Rigid3d pose;\n  PoseCovariance covariance;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &pose, &covariance);\n  const Eigen::Quaterniond actual = Eigen::Quaterniond(pose.rotation());\n  const Eigen::Quaterniond expected = Eigen::Quaterniond(\n      Eigen::AngleAxisd(M_PI / 2., Eigen::Vector3d::UnitX()));\n  EXPECT_TRUE(actual.isApprox(expected, 1e-3)) << expected.coeffs() << \" vs\\n\"\n                                               << actual.coeffs();\n}\n\nTEST_F(PoseTrackerTest, AddPoseObservation) {\n  auto time = common::FromUniversal(1000);\n\n  for (int i = 0; i < 300; ++i) {\n    time += std::chrono::milliseconds(5);\n    pose_tracker_->AddPoseObservation(\n        time, Rigid3d::Identity(),\n        Eigen::Matrix<double, 6, 6>::Identity() * 1e-6);\n  }\n\n  {\n    Rigid3d actual;\n    PoseCovariance covariance;\n    pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &actual, &covariance);\n    EXPECT_THAT(actual, IsNearly(Rigid3d::Identity(), 1e-3));\n  }\n\n  const Rigid3d expected =\n      Rigid3d::Translation(Eigen::Vector3d(1., 2., 3.)) *\n      Rigid3d::Rotation(Eigen::AngleAxisd(\n          M_PI / 2., Eigen::Vector3d(0., 0., 3.).normalized()));\n\n  for (int i = 0; i < 300; ++i) {\n    time += std::chrono::milliseconds(15);\n    pose_tracker_->AddPoseObservation(\n        time, expected, Eigen::Matrix<double, 6, 6>::Identity() * 1e-9);\n  }\n\n  time += std::chrono::milliseconds(15);\n\n  Rigid3d actual;\n  PoseCovariance covariance;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &actual, &covariance);\n  EXPECT_THAT(actual, IsNearly(expected, 1e-3));\n}\n\nTEST_F(PoseTrackerTest, AddOdometerPoseObservation) {\n  common::Time time = common::FromUniversal(0);\n\n  std::vector<Rigid3d> odometer_track;\n  odometer_track.push_back(Rigid3d::Identity());\n  odometer_track.push_back(\n      Rigid3d::Rotation(Eigen::AngleAxisd(0.3, Eigen::Vector3d::UnitZ())));\n  odometer_track.push_back(\n      Rigid3d::Translation(Eigen::Vector3d(0.2, 0., 0.)) *\n      Rigid3d::Rotation(Eigen::AngleAxisd(0.3, Eigen::Vector3d::UnitZ())));\n  odometer_track.push_back(\n      Rigid3d::Translation(Eigen::Vector3d(0.3, 0.1, 0.)) *\n      Rigid3d::Rotation(Eigen::AngleAxisd(0.6, Eigen::Vector3d::UnitZ())));\n  odometer_track.push_back(\n      Rigid3d::Translation(Eigen::Vector3d(0.2, 0.2, 0.1)) *\n      Rigid3d::Rotation(Eigen::AngleAxisd(0.3, Eigen::Vector3d::UnitZ())));\n  odometer_track.push_back(\n      Rigid3d::Translation(Eigen::Vector3d(0.1, 0.2, 0.2)) *\n      Rigid3d::Rotation(Eigen::AngleAxisd(0.6, Eigen::Vector3d::UnitZ())));\n  odometer_track.push_back(Rigid3d::Translation(Eigen::Vector3d(0., 0.1, 0.2)));\n\n  Rigid3d actual;\n  PoseCovariance unused_covariance;\n  for (const Rigid3d& pose : odometer_track) {\n    time += std::chrono::seconds(1);\n    pose_tracker_->AddOdometerPoseObservation(\n        time, pose, kOdometerVariance * PoseCovariance::Identity());\n    pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &actual,\n                                                    &unused_covariance);\n    EXPECT_THAT(actual, IsNearly(pose, 1e-2));\n  }\n  // Sanity check that the test has signal:\n  EXPECT_THAT(actual, Not(IsNearly(odometer_track[0], 1e-2)));\n}\n\n}  // namespace\n}  // namespace kalman_filter\n}  // namespace cartographer\n"
  },
  {
    "path": "kalman_filter/proto/pose_tracker_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.kalman_filter.proto;\n\nmessage PoseTrackerOptions {\n  // Model variances depend linearly on time.\n  optional double position_model_variance = 1;    //位置方差\n  optional double orientation_model_variance = 2; //方向方差\n  optional double velocity_model_variance = 3;    //速度\n\n  // Time constant for the orientation moving average based on observed gravity\n  // via linear acceleration.\n  optional double imu_gravity_time_constant = 4; //时间\n  optional double imu_gravity_variance = 5;      //重力加速度方差 g\n\n  // Maximum number of previous odometry states to keep.\n  optional int32 num_odometry_states = 6;\n}\n"
  },
  {
    "path": "kalman_filter/unscented_kalman_filter.h",
    "content": "\n#ifndef CARTOGRAPHER_KALMAN_FILTER_UNSCENTED_KALMAN_FILTER_H_\n#define CARTOGRAPHER_KALMAN_FILTER_UNSCENTED_KALMAN_FILTER_H_\n\n#include <algorithm>\n#include <cmath>\n#include <functional>\n#include <vector>\n\n#include \"Eigen/Cholesky\"\n#include \"Eigen/Core\"\n#include \"Eigen/Eigenvalues\"\n#include \"cartographer/kalman_filter/gaussian_distribution.h\"\n#include \"glog/logging.h\"\n/*\n无损卡尔曼滤波,去芳香卡尔曼滤波https://en.wikipedia.org/wiki/Kalman_filter\n*/\nnamespace cartographer {\nnamespace kalman_filter {\n\n//平方,常量表达式\ntemplate <typename FloatType>\nconstexpr FloatType sqr(FloatType a) {\n  return a * a;\n}\n\n// N*1 || 1*N -> 外积,N*N\ntemplate <typename FloatType, int N>\nEigen::Matrix<FloatType, N, N> OuterProduct( \n    const Eigen::Matrix<FloatType, N, 1>& v) {\n  return v * v.transpose();\n}\n\n// Checks if 'A' is a symmetric matrix.检查A是否是对称矩阵,A减去A的转置~=0\ntemplate <typename FloatType, int N>\nvoid CheckSymmetric(const Eigen::Matrix<FloatType, N, N>& A) {\n  // This should be pretty much Eigen::Matrix<>::Zero() if the matrix is\n  // symmetric.\n\n  //The NaN values are used to identify undefined or non-representable values for floating-point elements, \n  //such as the square root of negative numbers or the result of 0/0.\n\n  const FloatType norm = (A - A.transpose()).norm();\n  CHECK(!std::isnan(norm) && std::abs(norm) < 1e-5)\n      << \"Symmetry check failed with norm: '\" << norm << \"' from matrix:\\n\"\n      << A;\n}\n\n/*\nhttps://zh.wikipedia.org/wiki/%E6%AD%A3%E5%AE%9A%E7%9F%A9%E9%98%B5\n\n几个术语: Av=λv.  λ 为一标量，称为 v 对应的特征值。也称 v 为特征值 λ 对应的特征向量。\neigendecomposition,特征分解,谱分解,是将矩阵分解为由其特征值和特征向量表示的矩阵之积的方法。\n需要注意只有对可对角化矩阵才可以施以特征分解。\n\neigenvalues 特征值,\neigenvectors 特征向量 \n\n\n返回对称半正定矩阵的平方根B,M=B*B\n*/\n// Returns the matrix square root of a symmetric positive semidefinite matrix.\ntemplate <typename FloatType, int N>\nEigen::Matrix<FloatType, N, N> MatrixSqrt(\n    const Eigen::Matrix<FloatType, N, N>& A) {\n  CheckSymmetric(A);//必须是对称矩阵\n\n  Eigen::SelfAdjointEigenSolver<Eigen::Matrix<FloatType, N, N>>\n      adjoint_eigen_solver((A + A.transpose()) / 2.);\n      //A==A的转置，构造特征分解对象。\n\n  const auto& eigenvalues = adjoint_eigen_solver.eigenvalues();//特征值\n  CHECK_GT(eigenvalues.minCoeff(), -1e-5)\n      << \"MatrixSqrt failed with negative eigenvalues: \"\n      << eigenvalues.transpose();\n//Cholesky分解：把一个对称正定的矩阵表示成一个下三角矩阵L和其转置的乘积的分解。\n  return adjoint_eigen_solver.eigenvectors() *\n         adjoint_eigen_solver.eigenvalues()\n             .cwiseMax(Eigen::Matrix<FloatType, N, 1>::Zero())\n             .cwiseSqrt()\n             .asDiagonal() *\n         adjoint_eigen_solver.eigenvectors().transpose();\n}\n\n/*\n无损卡尔曼滤波\n对于雷达来说，人们感兴趣的是其能够跟踪目标。但目标的位置、速度、加速度的测量值往往在任何时候都有噪声。\n卡尔曼滤波利用目标的动态信息，设法去掉噪声的影响，得到一个关于目标位置的好的估计。\n这个估计可以是对当前目标位置的估计（滤波），也可以是对于将来位置的估计（预测），\n也可以是对过去位置的估计（插值或平滑）。\n\n卡尔曼滤波是一种递归的估计，即只要获知上一时刻状态的估计值以及当前状态的观测值就可以计算出当前状态的估计值，\n因此不需要记录观测或者估计的历史信息。\n\n卡尔曼滤波器的操作包括两个阶段：预测与更新。在预测阶段，滤波器使用上一状态的估计，做出对当前状态的估计。\n在更新阶段，滤波器利用对当前状态的观测值优化在预测阶段获得的预测值，以获得一个更精确的新估计值。\n\n\nUnscentedKalmanFilter类是根据《Probabilistic Robotics》实现的卡尔曼滤波类，\n并且扩展到了处理非线性噪声和传感器。\n数据成员:\n1),add_delta_\n2),compute_delta_\n\n成员函数：\n1),Predict():预测\n*/\n\n/*\nImplementation of a Kalman filter. We follow the nomenclature (过程)from\nThrun, S. et al., Probabilistic Robotics, 2006.\nExtended to handle non-additive noise/sensors inspired by Kraft, E., A\nQuaternion-based Unscented Kalman Filter for Orientation Tracking.\n\n*/\ntemplate <typename FloatType, int N>\nclass UnscentedKalmanFilter {\n public:\n  using StateType = Eigen::Matrix<FloatType, N, 1>;           //状态矩阵N*1\n  using StateCovarianceType = Eigen::Matrix<FloatType, N, N>; //协方差矩阵N*N\n\n/*\n构造函数,\n参数1,N*1矩阵,\n参数2,stl函数对象 add_delta(默认),\n参数3,stl函数对象 compute_delta(默认),\n*/\n  explicit UnscentedKalmanFilter(\n      const GaussianDistribution<FloatType, N>& initial_belief,                 //参数1\n\n      std::function<StateType(const StateType& state, const StateType& delta)>  //参数2\n          add_delta = [](const StateType& state,\n                         const StateType& delta) { return state + delta; },\n\n      std::function<StateType(const StateType& origin, const StateType& target)> //参数3\n          compute_delta =\n              [](const StateType& origin, const StateType& target) {\n                return target - origin;\n              })\n      : belief_(initial_belief),\n        add_delta_(add_delta),\n        compute_delta_(compute_delta) {}\n\n/*\n预测step。使用卡尔曼滤波对当前状态进行预测。\n\n参数1是std函数对象g, 作用是状态转移方程\n参数2是高斯分布的“ε”,控制和模型噪声的线性组合。\n\n*/\n  // Does the control/prediction step for the filter. The control must be\n  // implicitly added by the function g which also does the state transition.\n  // 'epsilon' is the additive combination of control and model noise.\n  void Predict(std::function<StateType(const StateType&)> g,\n               const GaussianDistribution<FloatType, N>& epsilon) {\n    CheckSymmetric(epsilon.GetCovariance());\n\n    // Get the state mean and matrix root of its covariance.\n    const StateType& mu = belief_.GetMean();\n    const StateCovarianceType sqrt_sigma = MatrixSqrt(belief_.GetCovariance());//N*N\n\n    std::vector<StateType> Y;//需要计算的状态矩阵，N*1矩阵。\n    Y.reserve(2 * N + 1);//公式：p65\n    Y.emplace_back(g(mu));//状态转移方程,公式p65:3.68,p70:3\n/*\n按照公式p65,3.66计算：\n1),mu是u,\n2),add_delta_()得到Xi,\n3),g(Xi)得到Yi,\n4),ComputeMean()得到 u',\n5),更新Σ得到new_sigma\n6),返回新的高斯分布。均值是new_mu,方差是new_sigma。二者再加上epsilon。\n*/\n    const FloatType kSqrtNPlusLambda = std::sqrt(N + kLambda);\n    for (int i = 0; i < N; ++i) {\n      // Order does not matter here as all have the same weights in the\n      // summation later on anyways.\n      Y.emplace_back(g(add_delta_(mu, kSqrtNPlusLambda * sqrt_sigma.col(i))));\n      Y.emplace_back(g(add_delta_(mu, -kSqrtNPlusLambda * sqrt_sigma.col(i))));\n    }\n    const StateType new_mu = ComputeMean(Y);\n\n    StateCovarianceType new_sigma =\n        kCovWeight0 * OuterProduct<FloatType, N>(compute_delta_(new_mu, Y[0]));\n    for (int i = 0; i < N; ++i) {\n      new_sigma += kCovWeightI * OuterProduct<FloatType, N>(\n                                     compute_delta_(new_mu, Y[2 * i + 1]));\n      new_sigma += kCovWeightI * OuterProduct<FloatType, N>(\n                                     compute_delta_(new_mu, Y[2 * i + 2]));\n    }\n    CheckSymmetric(new_sigma);\n\n    belief_ = GaussianDistribution<FloatType, N>(new_mu, new_sigma) + epsilon;//更新状态\n  }\n\n/*\n观察step.\n参数1:stl函数对象h\n参数2:高斯分布的测量噪声delta,均值为0\n\n按照《Probabilistic Robotics》p70,的公式(7)-(14)计算。\n\n  template <int K>:k是欲观测的变量数。\n1),看书\n2),...\n3),...\n4),...\n5),...\n6),...\n*/\n  // The observation step of the Kalman filter. 'h' transfers the state\n  // into an observation that should be zero, i.e., the sensor readings should\n  // be included in this function already. 'delta' is the measurement noise and\n  // must have zero mean.\n  template <int K>\n  void Observe(\n      std::function<Eigen::Matrix<FloatType, K, 1>(const StateType&)> h,\n      const GaussianDistribution<FloatType, K>& delta) {\n    CheckSymmetric(delta.GetCovariance());\n    // We expect zero mean delta.\n    CHECK_NEAR(delta.GetMean().norm(), 0., 1e-9);\n\n    // Get the state mean and matrix root of its covariance.\n    const StateType& mu = belief_.GetMean();\n    const StateCovarianceType sqrt_sigma = MatrixSqrt(belief_.GetCovariance());\n\n    // As in Kraft's paper, we compute W containing the zero-mean sigma points,\n    // since this is all we need.\n    std::vector<StateType> W;\n    W.reserve(2 * N + 1);\n    W.emplace_back(StateType::Zero());\n\n    std::vector<Eigen::Matrix<FloatType, K, 1>> Z;\n    Z.reserve(2 * N + 1);\n    Z.emplace_back(h(mu));\n\n    Eigen::Matrix<FloatType, K, 1> z_hat = kMeanWeight0 * Z[0];\n    const FloatType kSqrtNPlusLambda = std::sqrt(N + kLambda);\n    for (int i = 0; i < N; ++i) {\n      // Order does not matter here as all have the same weights in the\n      // summation later on anyways.\n      W.emplace_back(kSqrtNPlusLambda * sqrt_sigma.col(i));\n      Z.emplace_back(h(add_delta_(mu, W.back())));\n\n      W.emplace_back(-kSqrtNPlusLambda * sqrt_sigma.col(i));\n      Z.emplace_back(h(add_delta_(mu, W.back())));\n\n      z_hat += kMeanWeightI * Z[2 * i + 1];\n      z_hat += kMeanWeightI * Z[2 * i + 2];\n    }\n\n    Eigen::Matrix<FloatType, K, K> S =\n        kCovWeight0 * OuterProduct<FloatType, K>(Z[0] - z_hat);\n    for (int i = 0; i < N; ++i) {\n      S += kCovWeightI * OuterProduct<FloatType, K>(Z[2 * i + 1] - z_hat);\n      S += kCovWeightI * OuterProduct<FloatType, K>(Z[2 * i + 2] - z_hat);\n    }\n    CheckSymmetric(S);\n    S += delta.GetCovariance();\n\n    Eigen::Matrix<FloatType, N, K> sigma_bar_xz =\n        kCovWeight0 * W[0] * (Z[0] - z_hat).transpose();\n    for (int i = 0; i < N; ++i) {\n      sigma_bar_xz +=\n          kCovWeightI * W[2 * i + 1] * (Z[2 * i + 1] - z_hat).transpose();\n      sigma_bar_xz +=\n          kCovWeightI * W[2 * i + 2] * (Z[2 * i + 2] - z_hat).transpose();\n    }\n\n    const Eigen::Matrix<FloatType, N, K> kalman_gain =\n        sigma_bar_xz * S.inverse();\n    const StateCovarianceType new_sigma =\n        belief_.GetCovariance() - kalman_gain * S * kalman_gain.transpose();\n    CheckSymmetric(new_sigma);\n\n    belief_ = GaussianDistribution<FloatType, N>(\n        add_delta_(mu, kalman_gain * -z_hat), new_sigma);\n  }\n\n  const GaussianDistribution<FloatType, N>& GetBelief() const {\n    return belief_;\n  }\n\n private:\n  //计算带权重的偏差\n  StateType ComputeWeightedError(const StateType& mean_estimate,\n                                 const std::vector<StateType>& states) {\n    StateType weighted_error =\n        kMeanWeight0 * compute_delta_(mean_estimate, states[0]);\n    for (int i = 1; i != 2 * N + 1; ++i) {\n      weighted_error += kMeanWeightI * compute_delta_(mean_estimate, states[i]);\n    }\n    return weighted_error;\n  }\n\n  // Algorithm for computing the mean of non-additive states taken from Kraft's\n  // Section 3.4, adapted to our implementation.\n  //计算均值\n  StateType ComputeMean(const std::vector<StateType>& states) {\n    CHECK_EQ(states.size(), 2 * N + 1);\n    StateType current_estimate = states[0];\n    StateType weighted_error = ComputeWeightedError(current_estimate, states);\n    int iterations = 0;\n    while (weighted_error.norm() > 1e-9) {\n      double step_size = 1.;\n      while (true) {\n        const StateType next_estimate =\n            add_delta_(current_estimate, step_size * weighted_error);\n        const StateType next_error =\n            ComputeWeightedError(next_estimate, states);\n        if (next_error.norm() < weighted_error.norm()) {\n          current_estimate = next_estimate;\n          weighted_error = next_error;\n          break;\n        }\n        step_size *= 0.5;\n        CHECK_GT(step_size, 1e-3) << \"Step size too small, line search failed.\";\n      }\n      ++iterations;\n      CHECK_LT(iterations, 20) << \"Too many iterations.\";\n    }\n    return current_estimate;\n  }\n\n  // According to Wikipedia these are the normal values. Thrun does not\n  // mention those.\n  constexpr static FloatType kAlpha = 1e-3;\n  constexpr static FloatType kKappa = 0.;\n  constexpr static FloatType kBeta = 2.;\n  constexpr static FloatType kLambda = sqr(kAlpha) * (N + kKappa) - N;\n  constexpr static FloatType kMeanWeight0 = kLambda / (N + kLambda);\n  constexpr static FloatType kCovWeight0 =\n      kLambda / (N + kLambda) + (1. - sqr(kAlpha) + kBeta);\n  constexpr static FloatType kMeanWeightI = 1. / (2. * (N + kLambda));\n  constexpr static FloatType kCovWeightI = kMeanWeightI;\n\n//1),N*1矩阵,对N个变量的估计\n  GaussianDistribution<FloatType, N> belief_; \n//2),add_delta_，加法操作\n  const std::function<StateType(const StateType& state, const StateType& delta)>\n      add_delta_;\n//3),compute_delta_，计算偏差操作\n  const std::function<StateType(const StateType& origin,\n                                const StateType& target)>\n      compute_delta_;\n};\n\n//外部声明。\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kAlpha;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kKappa;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kBeta;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kLambda;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kMeanWeight0;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kCovWeight0;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kMeanWeightI;\ntemplate <typename FloatType, int N>\nconstexpr FloatType UnscentedKalmanFilter<FloatType, N>::kCovWeightI;\n\n}  // namespace kalman_filter\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_KALMAN_FILTER_UNSCENTED_KALMAN_FILTER_H_\n"
  },
  {
    "path": "kalman_filter/unscented_kalman_filter_test.cc",
    "content": "\n#include \"cartographer/kalman_filter/unscented_kalman_filter.h\"\n\n#include \"cartographer/kalman_filter/gaussian_distribution.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace kalman_filter {\nnamespace {\n\n//返回1*1的矩阵[val]\nEigen::Matrix<double, 1, 1> Scalar(double value) {\n  return value * Eigen::Matrix<double, 1, 1>::Identity();\n}\n\n//motion运动函数/状态转移函数,g，可以理解为自身的状态变化\n// A simple model that copies the first to the second state variable.\nEigen::Matrix<double, 2, 1> g(const Eigen::Matrix<double, 2, 1>& state) {\n//值拷贝,返回矩阵[state[0],state[0]],即只修改第二参数\n  Eigen::Matrix<double, 2, 1> new_state;\n  new_state << state[0], state[0];\n  return new_state;\n}\n\n//观察/测量矩阵,h: state[0]-5.,可以理解为传感器到landmark的测量距离。\n// A simple observation of the first state variable.\nEigen::Matrix<double, 1, 1> h(const Eigen::Matrix<double, 2, 1>& state) {//\n  return Scalar(state[0]) - Scalar(5.);\n}\n\nTEST(KalmanFilterTest, testConstructor) {\n  /*\n  构造一个卡尔曼滤波,参数是:\n  1.长度为2的高斯分布,均值是0,42,协方差是10*[1,0;0,1]\n  2.默认\n  3.默认\n  */\n  UnscentedKalmanFilter<double, 2> filter(GaussianDistribution<double, 2>(\n      Eigen::Vector2d(0., 42.), 10. * Eigen::Matrix2d::Identity()));\n  EXPECT_NEAR(42., filter.GetBelief().GetMean()[1], 1e-9);\n  //均值第1列是42,index 从0开始。\n}\n\nTEST(KalmanFilterTest, testPredict) {\n//创建一个含2个变量的卡尔曼滤波。均值是42,0,协方差是10*[1,0;0,1]\n  UnscentedKalmanFilter<double, 2> filter(GaussianDistribution<double, 2>(\n      Eigen::Vector2d(42., 0.), 10. * Eigen::Matrix2d::Identity()));\n\n/*\n执行预测step,在预测阶段，滤波器使用上一状态的估计，做出对当前状态的估计。\n测量噪声是0均值单位阵。\n预测值应该与[42,42]相等：g没有修改第一参数，只修改第二参数使其等于第一参数。\n*/\n  filter.Predict(\n      g, GaussianDistribution<double, 2>(Eigen::Vector2d(0., 0.),\n                                         Eigen::Matrix2d::Identity() * 1e-9));\n  EXPECT_NEAR(filter.GetBelief().GetMean()[0], 42., 1e-2);\n  EXPECT_NEAR(filter.GetBelief().GetMean()[1], 42., 1e-2);\n}\n\nTEST(KalmanFilterTest, testObserve) {\n\n//创建一个含2个变量的卡尔曼滤波。均值是0,42,协方差是10*[1,0;0,1]\n  UnscentedKalmanFilter<double, 2> filter(GaussianDistribution<double, 2>(\n      Eigen::Vector2d(0., 42.), 10. * Eigen::Matrix2d::Identity()));\n\n/*\n预测+观察,重复500次,最后均值是[5,5]：\n观测矩阵h表示landmark到state[0]的距离永远是5，故稳定后state[0],state[1]应都为5\n \n卡尔曼滤波：总是先预测，再根据传感器进行数据融合(校正)。\n*/\n  for (int i = 0; i < 500; ++i) {\n    //预测。motion函数是g，control噪声的均值和方差是0\n    filter.Predict(\n        g, GaussianDistribution<double, 2>(Eigen::Vector2d(0., 0.),\n                                           Eigen::Matrix2d::Identity() * 1e-9));\n    //测量/观察。传感器噪声的均值和方差是0\n    filter.Observe<1>(\n        h, GaussianDistribution<double, 1>(Scalar(0.), Scalar(1e-2)));\n  }\n  EXPECT_NEAR(filter.GetBelief().GetMean()[0], 5, 1e-2);\n  EXPECT_NEAR(filter.GetBelief().GetMean()[1], 5, 1e-2);\n}\n\n}  // namespace\n}  // namespace kalman_filter\n}  // namespace cartographer\n/*\n 运行unscented_kalman_filter_le_test.cc:\n\n before:0 42  #滤波前\n Predict:0 0  # 执行预测\n Observe:4.995 4.995 # 执行校正\n --\n before:4.995 4.995\n Predict4.995 4.995\n Observe: 4.9975 4.9975\n可见经过2次迭代就已经较为准确了。\n\n*/"
  },
  {
    "path": "mapping/collated_trajectory_builder.cc",
    "content": "\n#include \"cartographer/mapping/collated_trajectory_builder.h\"\n\n#include \"cartographer/common/time.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nnamespace {\n\nconstexpr double kSensorDataRatesLoggingPeriodSeconds = 15.;\n\n}  // namespace\n\nCollatedTrajectoryBuilder::CollatedTrajectoryBuilder(\n    sensor::Collator* const sensor_collator, const int trajectory_id,\n    const std::unordered_set<string>& expected_sensor_ids,\n    std::unique_ptr<GlobalTrajectoryBuilderInterface>\n        wrapped_trajectory_builder)\n\n    : sensor_collator_(sensor_collator),\n      trajectory_id_(trajectory_id),\n      wrapped_trajectory_builder_(std::move(wrapped_trajectory_builder)),\n      last_logging_time_(std::chrono::steady_clock::now()) {\n\n  sensor_collator_->AddTrajectory(\n      trajectory_id, expected_sensor_ids,\n      [this](const string& sensor_id, std::unique_ptr<sensor::Data> data) {\n        HandleCollatedSensorData(sensor_id, std::move(data));\n      });\n\n}\n\nCollatedTrajectoryBuilder::~CollatedTrajectoryBuilder() {}\n\nconst Submaps* CollatedTrajectoryBuilder::submaps() const {\n  return wrapped_trajectory_builder_->submaps();\n}\n\nconst TrajectoryBuilder::PoseEstimate&\nCollatedTrajectoryBuilder::pose_estimate() const {\n  return wrapped_trajectory_builder_->pose_estimate();\n}\n\nvoid CollatedTrajectoryBuilder::AddSensorData(\n    const string& sensor_id, std::unique_ptr<sensor::Data> data) {\n  sensor_collator_->AddSensorData(trajectory_id_, sensor_id, std::move(data));\n}\n\nvoid CollatedTrajectoryBuilder::HandleCollatedSensorData(\n    const string& sensor_id, std::unique_ptr<sensor::Data> data) {\n  auto it = rate_timers_.find(sensor_id);\n  if (it == rate_timers_.end()) {\n    it = rate_timers_\n             .emplace(\n                 std::piecewise_construct, std::forward_as_tuple(sensor_id),\n                 std::forward_as_tuple(\n                     common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds)))\n             .first;\n  }\n  it->second.Pulse(data->time);\n\n  if (std::chrono::steady_clock::now() - last_logging_time_ >\n      common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds)) {\n    for (const auto& pair : rate_timers_) {\n      LOG(INFO) << pair.first << \" rate: \" << pair.second.DebugString();\n    }\n    last_logging_time_ = std::chrono::steady_clock::now();\n  }\n\n  switch (data->type) {\n    case sensor::Data::Type::kImu:\n      wrapped_trajectory_builder_->AddImuData(data->time,\n                                              data->imu.linear_acceleration,\n                                              data->imu.angular_velocity);\n      return;\n\n    case sensor::Data::Type::kRangefinder:\n      wrapped_trajectory_builder_->AddRangefinderData(\n          data->time, data->rangefinder.origin, data->rangefinder.ranges);\n      return;\n\n    case sensor::Data::Type::kOdometer:\n      wrapped_trajectory_builder_->AddOdometerData(data->time,\n                                                   data->odometer_pose);\n      return;\n  }\n  LOG(FATAL);\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/collated_trajectory_builder.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_COLLATED_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_COLLATED_TRAJECTORY_BUILDER_H_\n\n#include <chrono>\n#include <map>\n#include <memory>\n#include <string>\n#include <unordered_set>\n\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/rate_timer.h\"\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/mapping/trajectory_builder.h\"\n#include \"cartographer/sensor/collator.h\"\n#include \"cartographer/sensor/data.h\"\n\nnamespace cartographer {\nnamespace mapping {\n/*\n\nCollatedTrajectoryBuilder类继承自TrajectoryBuilder,\n作用：使用Collator处理从传感器收集而来的数据.\n并传递给GlobalTrajectoryBuilderInterface。\n\n数据成员包括\n\nsensor::Collator* const sensor_collator_; 传感器收集类实例\nconst int trajectory_id_;                 轨迹id\nstd::unique_ptr<GlobalTrajectoryBuilderInterface> wrapped_trajectory_builder_;全局的建图接口\n \nstd::chrono::steady_clock::time_point last_logging_time_; 上一次传递数据时间\nstd::map<string, common::RateTimer<>> rate_timers_;频率\n\n成员函数,重写了父类的接口\nsubmaps()用于建立子图\npose_estimate() 位姿估计\nAddSensorData() 添加传感器数据\nHandleCollatedSensorData() 处理收集得来的传感器数据\n\n\nHandles collating sensor data using a sensor::Collator,\nthen passing it on toa mapping::GlobalTrajectoryBuilderInterface which is common for 2D and 3D.\n\n*/\nclass CollatedTrajectoryBuilder : public TrajectoryBuilder {\n public:\n  CollatedTrajectoryBuilder(\n      sensor::Collator* sensor_collator, int trajectory_id,\n      const std::unordered_set<string>& expected_sensor_ids,\n      std::unique_ptr<GlobalTrajectoryBuilderInterface>\n          wrapped_trajectory_builder);\n  ~CollatedTrajectoryBuilder() override;\n\n  CollatedTrajectoryBuilder(const CollatedTrajectoryBuilder&) = delete;\n  CollatedTrajectoryBuilder& operator=(const CollatedTrajectoryBuilder&) =\n      delete;\n\n  const Submaps* submaps() const override;\n  const PoseEstimate& pose_estimate() const override;\n\n  void AddSensorData(const string& sensor_id,\n                     std::unique_ptr<sensor::Data> data) override;\n\n private:\n  void HandleCollatedSensorData(const string& sensor_id,\n                                std::unique_ptr<sensor::Data> data);\n\n  sensor::Collator* const sensor_collator_;\n  const int trajectory_id_;\n  std::unique_ptr<GlobalTrajectoryBuilderInterface> wrapped_trajectory_builder_;\n\n  // Time at which we last logged the rates of incoming sensor data.\n  std::chrono::steady_clock::time_point last_logging_time_;\n  std::map<string, common::RateTimer<>> rate_timers_;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_COLLATED_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping/detect_floors.cc",
    "content": "\n#include \"cartographer/mapping/detect_floors.h\"\n\n#include <algorithm>\n#include <fstream>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nnamespace {\n\n// A union-find structure for assigning levels to 'spans' in the trajectory\n// implemented as a disjoint-set forest:\n// https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests\n// TODO(hrapp): We use this elsewhere. Pull out into a common place.\nusing Levels = std::map<int, int>;\n\nconstexpr double kMaxShortSpanLengthMeters = 25.;\nconstexpr double kLevelHeightMeters = 2.5;\nconstexpr double kMinLevelSeparationMeters = 1.0;\n\n// Indices into 'trajectory.node', so that 'start_index' <= i < 'end_index'.\nstruct Span {\n  int start_index;\n  int end_index;\n  std::vector<double> z_values;\n\n  bool operator<(const Span& other) const {\n    return std::forward_as_tuple(start_index, end_index) <\n           std::forward_as_tuple(other.start_index, other.end_index);\n  }\n};\n\n// Union-find implementation for classifying spans into levels.\nint LevelFind(const int i, const Levels& levels) {\n  auto it = levels.find(i);\n  CHECK(it != levels.end());\n  if (it->first == it->second) {\n    return it->second;\n  }\n  return LevelFind(it->second, levels);\n}\n\nvoid LevelUnion(int i, int j, Levels* levels) {\n  const int repr_i = LevelFind(i, *levels);\n  const int repr_j = LevelFind(j, *levels);\n  (*levels)[repr_i] = repr_j;\n}\n\nvoid InsertSorted(const double val, std::vector<double>* vals) {\n  vals->insert(std::upper_bound(vals->begin(), vals->end(), val), val);\n}\n\ndouble Median(const std::vector<double>& sorted) {\n  CHECK(!sorted.empty());\n  return sorted.at(sorted.size() / 2);\n}\n\n// Cut the trajectory at jumps in z. A new span is started when the current\n// node's z differes by more than kLevelHeightMeters from the median z values.\nstd::vector<Span> SliceByAltitudeChange(const proto::Trajectory& trajectory) {\n  CHECK_GT(trajectory.node_size(), 0);\n\n  std::vector<Span> spans;\n  spans.push_back(Span{0, 0, {trajectory.node(0).pose().translation().z()}});\n  for (int i = 1; i < trajectory.node_size(); ++i) {\n    const auto& node = trajectory.node(i);\n    const double z = node.pose().translation().z();\n    if (std::abs(Median(spans.back().z_values) - z) > kLevelHeightMeters) {\n      spans.push_back(Span{i, i, {}});\n    }\n    InsertSorted(z, &spans.back().z_values);\n    spans.back().end_index = i + 1;\n  }\n  return spans;\n}\n\n// Returns the length of 'span' in meters.\ndouble SpanLength(const proto::Trajectory& trajectory, const Span& span) {\n  double length = 0;\n  for (int i = span.start_index + 1; i < span.end_index; ++i) {\n    const auto a =\n        transform::ToEigen(trajectory.node(i - 1).pose().translation());\n    const auto b = transform::ToEigen(trajectory.node(i).pose().translation());\n    length += (a - b).head<2>().norm();\n  }\n  return length;\n}\n\n// True if 'span' is considered to be short, i.e. not interesting on its own,\n// but should be folded into the levels before and after entering it.\nbool IsShort(const proto::Trajectory& trajectory, const Span& span) {\n  return SpanLength(trajectory, span) < kMaxShortSpanLengthMeters;\n}\n\n// Merges all 'spans' that have similar median z value into the same level.\nvoid GroupSegmentsByAltitude(const proto::Trajectory& trajectory,\n                             const std::vector<Span>& spans, Levels* levels) {\n  for (size_t i = 0; i < spans.size(); ++i) {\n    for (size_t j = i + 1; j < spans.size(); ++j) {\n      if (std::abs(Median(spans[i].z_values) - Median(spans[j].z_values)) <\n          kMinLevelSeparationMeters) {\n        LevelUnion(i, j, levels);\n      }\n    }\n  }\n}\n\nstd::vector<Floor> FindFloors(const proto::Trajectory& trajectory,\n                              const std::vector<Span>& spans,\n                              const Levels& levels) {\n  std::map<int, std::vector<Span>> level_spans;\n\n  // Initialize the levels to start out with only long spans.\n  for (size_t i = 0; i < spans.size(); ++i) {\n    const Span& span = spans[i];\n    if (!IsShort(trajectory, span)) {\n      level_spans[LevelFind(i, levels)].push_back(span);\n    }\n  }\n\n  for (size_t i = 0; i < spans.size(); ++i) {\n    const Span& span = spans[i];\n    if (!IsShort(trajectory, span)) {\n      continue;\n    }\n\n    // If we have a long piece on this floor already, merge this short piece\n    // into it.\n    int level = LevelFind(i, levels);\n    if (!level_spans[level].empty()) {\n      level_spans[level].push_back(span);\n      continue;\n    }\n\n    // Otherwise, add this short piece to the level before and after it. It is\n    // likely some intermediate level on stairs.\n    size_t index = i - 1;\n    if (index < spans.size()) {\n      level_spans[LevelFind(index, levels)].push_back(span);\n    }\n    index = i + 1;\n    if (index < spans.size()) {\n      level_spans[LevelFind(index, levels)].push_back(span);\n    }\n  }\n\n  // Convert the level_spans structure to 'Floor'.\n  std::vector<Floor> floors;\n  for (auto& level : level_spans) {\n    if (level.second.empty()) {\n      continue;\n    }\n\n    std::vector<double> z_values;\n    std::sort(level.second.begin(), level.second.end());\n    floors.emplace_back();\n    for (const auto& span : level.second) {\n      if (!IsShort(trajectory, span)) {\n        // To figure out the median height of this floor, we only care for the\n        // long pieces that are guaranteed to be in the structure. This is a\n        // heuristic to leave out intermediate (short) levels.\n        z_values.insert(z_values.end(), span.z_values.begin(),\n                        span.z_values.end());\n      }\n      floors.back().timespans.push_back(Timespan{\n          common::FromUniversal(trajectory.node(span.start_index).timestamp()),\n          common::FromUniversal(\n              trajectory.node(span.end_index - 1).timestamp())});\n    }\n    std::sort(z_values.begin(), z_values.end());\n    floors.back().z = Median(z_values);\n  }\n  return floors;\n}\n\n}  // namespace，只能本文件访问以上函数。\n\nstd::vector<Floor> DetectFloors(const proto::Trajectory& trajectory) {\n  const std::vector<Span> spans = SliceByAltitudeChange(trajectory);\n  Levels levels;\n  for (size_t i = 0; i < spans.size(); ++i) {\n    levels[i] = i;\n  }\n  GroupSegmentsByAltitude(trajectory, spans, &levels);\n\n  std::vector<Floor> floors = FindFloors(trajectory, spans, levels);\n  std::sort(floors.begin(), floors.end(),\n            [](const Floor& a, const Floor& b) { return a.z < b.z; });\n  return floors;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/detect_floors.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_DETECT_FLOORS_H_\n#define CARTOGRAPHER_MAPPING_DETECT_FLOORS_H_\n\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n#include \"cartographer/common/time.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\nspan,范围\nTimespan表征时间范围。\n*/\nstruct Timespan {\n  common::Time start;\n  common::Time end;\n};\n\n/*\n一个楼层对应多个扫描timespan：有可能重复的扫描多次\n但只有一个高度z。\n*/\nstruct Floor {\n// The spans of time we spent on this floor. Since we might have \n//walked up and down many times in this place, there can be many spans\n// of time we spent on a particular floor.\n  std::vector<Timespan> timespans;\n\n  // The median z-value of this floor.\n  double z; //z轴的中值\n};\n\n/*\nheuristic:启发式,\n使用启发式搜索寻找building的不同楼层的z值。\n对楼层的要求：同一floor同一z值，只要有“楼梯”出现，即为“产生”一层\n*/\n// Uses a heuristic which looks at z-values of the poses to detect individual\n// floors of a building. This requires that floors are *mostly* on the same\n// z-height and that level changes happen *relatively* abrubtly, e.g. by taking\n// the stairs.\nstd::vector<Floor> DetectFloors(const proto::Trajectory& trajectory);\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_DETECT_FLOORS_H_\n"
  },
  {
    "path": "mapping/global_trajectory_builder_interface.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_GLOBAL_TRAJECTORY_BUILDER_INTERFACE_H_\n#define CARTOGRAPHER_MAPPING_GLOBAL_TRAJECTORY_BUILDER_INTERFACE_H_\n\n#include <functional>\n#include <memory>\n#include <string>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/mapping/trajectory_builder.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n虚基类,提供一个接口用于2d和3d的slam,不可拷贝/赋值\n\n*/\n// This interface is used for both 2D and 3D SLAM. Implementations wire up a\n// global SLAM stack, i.e. local SLAM for initial pose estimates, scan matching\n// to detect loop closure, and a sparse pose graph optimization to compute\n// optimized pose estimates.\nclass GlobalTrajectoryBuilderInterface {\n public:\n  using PoseEstimate = TrajectoryBuilder::PoseEstimate;//位姿估计\n\n  GlobalTrajectoryBuilderInterface() {}\n  virtual ~GlobalTrajectoryBuilderInterface() {}\n\n  GlobalTrajectoryBuilderInterface(const GlobalTrajectoryBuilderInterface&) =\n      delete;\n  GlobalTrajectoryBuilderInterface& operator=(\n      const GlobalTrajectoryBuilderInterface&) = delete;\n\n  virtual const Submaps* submaps() const = 0;\n  virtual const PoseEstimate& pose_estimate() const = 0;\n\n  virtual void AddRangefinderData(common::Time time,\n                                  const Eigen::Vector3f& origin,\n                                  const sensor::PointCloud& ranges) = 0;\n  virtual void AddImuData(common::Time time,\n                          const Eigen::Vector3d& linear_acceleration,\n                          const Eigen::Vector3d& angular_velocity) = 0;\n  virtual void AddOdometerData(common::Time time,\n                               const transform::Rigid3d& pose) = 0;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_GLOBAL_TRAJECTORY_BUILDER_INTERFACE_H_\n"
  },
  {
    "path": "mapping/id.h",
    "content": "#ifndef CARTOGRAPHER_MAPPING_ID_H_\n#define CARTOGRAPHER_MAPPING_ID_H_\n\n#include <algorithm>\n#include <ostream>\n#include <tuple>\n#include <vector>\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n每一个轨迹trajectory上有多个节点node。\n如何唯一的表达这些节点node？节点标号:轨迹id+{0,1,2,...}\n*/\n// Uniquely identifies a trajectory node using a combination of a unique\n// trajectory ID and a zero-based index of the node inside that trajectory.\nstruct NodeId {\n  int trajectory_id;\n  int node_index;\n\n//比较<符号，成员函数\n  bool operator<(const NodeId& other) const {\n    return std::forward_as_tuple(trajectory_id, node_index) <\n           std::forward_as_tuple(other.trajectory_id, other.node_index);\n  }\n};\n\n//<<运算符，友元函数\ninline std::ostream& operator<<(std::ostream& os, const NodeId& v) {\n  return os << \"(\" << v.trajectory_id << \", \" << v.node_index << \")\";\n}\n\n/*\n一般来说，重建全局地图global map时，是由多个submap组成。\n如何给这些submap标号? 轨迹id+ {0,1,2,3...}\n*/\n// Uniquely identifies a submap using a combination of a unique trajectory ID\n// and a zero-based index of the submap inside that trajectory.\nstruct SubmapId {\n  int trajectory_id;\n  int submap_index;\n\n  bool operator<(const SubmapId& other) const {\n    return std::forward_as_tuple(trajectory_id, submap_index) <\n           std::forward_as_tuple(other.trajectory_id, other.submap_index);\n  }\n};\n\n//友元函数\ninline std::ostream& operator<<(std::ostream& os, const SubmapId& v) {\n  return os << \"(\" << v.trajectory_id << \", \" << v.submap_index << \")\";\n}\n\n/*\nNested,嵌套.\nNestedVectorsById类只有1个数据成员,vector<std::vector<ValueType>> data_;\n\n作用：嵌套存储多条轨迹线上的数据\n对外提供3个操作\n1,Append()根据轨迹id和data添加数据\n2,at()查询某轨迹对应的vector的某值\n3,num_indices(),某轨迹对应的数据的大小\n*/\ntemplate <typename ValueType, typename IdType>\nclass NestedVectorsById {\n public:\n\n/*\ndata_是vector,size()是轨迹的数量。data_存储的是轨迹id\ndata_[i]也是vector.存储的是轨迹上的data\n向data_[i]添加数据。\n*/\n  // Appends data to a trajectory, creating trajectories as needed.\n  IdType Append(int trajectory_id, const ValueType& value) {\n    data_.resize(std::max<size_t>(data_.size(), trajectory_id + 1));\n\n  //构造一个IdType对象用于返回，该id是轨迹id+{n}\n    const IdType id{trajectory_id,\n                    static_cast<int>(data_[trajectory_id].size())};\n    data_[trajectory_id].push_back(value);\n    return id;\n  }\n\n//在某一轨迹id上对应的data\n  const ValueType& at(const IdType& id) const {\n    //vector的成员函数:at(size_type n);即v[x][y]\n    return data_.at(id.trajectory_id).at(GetIndex(id));\n  }\n  ValueType& at(const IdType& id) {\n    return data_.at(id.trajectory_id).at(GetIndex(id));\n  }\n\n  //轨迹数量data_的size()\n  int num_trajectories() const { return static_cast<int>(data_.size()); }\n\n  //某个轨迹对应的的大小,data_[i].size()\n  int num_indices(int trajectory_id) const {\n    return static_cast<int>(data_.at(trajectory_id).size());\n  }\n\n  // TODO(whess): Remove once no longer needed.\n  const std::vector<std::vector<ValueType>> data() const { return data_; }\n\n private:\n  //返回id对应的index，即{0,1,2,...}\n  static int GetIndex(const NodeId& id) { return id.node_index; }\n  static int GetIndex(const SubmapId& id) { return id.submap_index; }\n\n  std::vector<std::vector<ValueType>> data_;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_ID_H_\n"
  },
  {
    "path": "mapping/imu_tracker.cc",
    "content": "\n#include \"cartographer/mapping/imu_tracker.h\"\n\n#include <cmath>\n#include <limits>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n\nimu_gravity_time_constant = 10.,\nnum_odometry_states = 1000,\n构造函数初始化\n*/\nImuTracker::ImuTracker(const double imu_gravity_time_constant,\n                       const common::Time time)\n    : imu_gravity_time_constant_(imu_gravity_time_constant),\n      time_(time),\n      last_linear_acceleration_time_(common::Time::min()),// 1796年\n      orientation_(Eigen::Quaterniond::Identity()),\n      gravity_vector_(Eigen::Vector3d::UnitZ()), //0,0,1，只有z轴有重力加速度。\n      imu_angular_velocity_(Eigen::Vector3d::Zero()) {}\n\n/*\n从time_增加到time。\ndt=t-t_;\nds=v*dt\n\n*/\nvoid ImuTracker::Advance(const common::Time time) {\n  CHECK_LE(time_, time);\n  const double delta_t = common::ToSeconds(time - time_);\n\n  //方向角\n  const Eigen::Quaterniond rotation =\n      transform::AngleAxisVectorToRotationQuaternion(\n          Eigen::Vector3d(imu_angular_velocity_ * delta_t)); //ds=v*dt\n\n  orientation_ = (orientation_ * rotation).normalized();//更新角度\n  gravity_vector_ = rotation.inverse() * gravity_vector_;//根据方向角，更新加速度\n\n  time_ = time;\n}\n\n/*\n更新imu测量得到的加速度。\n参数：vector3d，测量值\n1),dt=t_-t;\n2),alpha=1-e^(-dt/g);\n3),gravity_vector_=(1-alpha)*gv_+alpha*imu_line;\n4),更新orientation_\n\n\nexponential,指数.\n为何是指数变化：\n*/\nvoid ImuTracker::AddImuLinearAccelerationObservation(\n    const Eigen::Vector3d& imu_linear_acceleration) {\n  // Update the 'gravity_vector_' with an exponential moving average using the\n  // 'imu_gravity_time_constant'.\n  const double delta_t =\n      last_linear_acceleration_time_ > common::Time::min()\n          ? common::ToSeconds(time_ - last_linear_acceleration_time_)\n          : std::numeric_limits<double>::infinity();\n  last_linear_acceleration_time_ = time_;\n  const double alpha = 1. - std::exp(-delta_t / imu_gravity_time_constant_);\n  gravity_vector_ =\n      (1. - alpha) * gravity_vector_ + alpha * imu_linear_acceleration;\n  // Change the 'orientation_' so that it agrees with the current\n  // 'gravity_vector_'.\n  const Eigen::Quaterniond rotation = Eigen::Quaterniond::FromTwoVectors(\n      gravity_vector_, orientation_.inverse() * Eigen::Vector3d::UnitZ());\n\n  orientation_ = (orientation_ * rotation).normalized();//更新方向角\n\n  CHECK_GT((orientation_ * gravity_vector_).z(), 0.);\n  CHECK_GT((orientation_ * gravity_vector_).normalized().z(), 0.99);\n}\n\n/*\n更新imu测量得到的角速度\n*/\nvoid ImuTracker::AddImuAngularVelocityObservation(\n    const Eigen::Vector3d& imu_angular_velocity) {\n  imu_angular_velocity_ = imu_angular_velocity;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/imu_tracker.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_IMU_TRACKER_H_\n#define CARTOGRAPHER_MAPPING_IMU_TRACKER_H_\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/time.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n\n/*\nImuTracker\n作用：使用来自imu的角速度+加速度用于跟踪pose的orientation\n\n\n构造函数指定重力加速度g和最新测量时间。\n数据成员包含\n1,加速度g\n2,测量时间\n3,方向,四元数\n4,三个加速度3d\n5,imu角速度\n\n*/\n// Keeps track of the orientation using angular velocities and linear\n// accelerations from an IMU. Because averaged linear acceleration (assuming\n// slow movement) is a direct measurement of gravity, \n// roll/pitch does not drift,though yaw does.\n//x,y轴不会drift漂移。z轴可能会产生漂移。(有累计误差)\nclass ImuTracker {\n public:\n  ImuTracker(double imu_gravity_time_constant, common::Time time);\n\n\n  // Advances to the given 'time' and updates the orientation to reflect this.\n  // 系统时间增加t，更新方向角\n  void Advance(common::Time time);\n\n  // Updates from an IMU reading (in the IMU frame).\n  void AddImuLinearAccelerationObservation(   //加速度\n      const Eigen::Vector3d& imu_linear_acceleration);\n\n  void AddImuAngularVelocityObservation(      //角速度\n      const Eigen::Vector3d& imu_angular_velocity);  \n\n  // Query the current orientation estimate.返回目前估计pose的方向角。\n  Eigen::Quaterniond orientation() const { return orientation_; }\n\n private:\n  const double imu_gravity_time_constant_;    // g，10.0\n  common::Time time_;                         //最新一次测量时间\n  common::Time last_linear_acceleration_time_;//加速度测量时间\n  Eigen::Quaterniond orientation_;            //pose的方向角\n  Eigen::Vector3d gravity_vector_;            //加速度测量的方向\n  Eigen::Vector3d imu_angular_velocity_;      //角速度\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_IMU_TRACKER_H_\n/*\n预备知识：\n惯性测量单元（英文：Inertial measurement unit，简称IMU）是测量物体三轴姿态角(或角速率)以及加速度的装置。\n陀螺仪及加速度计是IMU的主要元件，其精度直接影响到惯性系统的精度。在实际工作中，由于不可避免的各种干扰因素，而导致陀螺仪及加速度计产生误差，从初始对准开始，其导航误差就随时间而增长，尤其是位置误差，这是惯导系统的主要缺点。所以需要利用外部信息进行辅助，实现组合导航，使其有效地减小误差随时间积累的问题。为了提高可靠性，还可以为每个轴配备更多的传感器。一般而言IMU要安装在被测物体的重心上。\n一般情况，一个IMU包含了三个单轴的加速度计和三个单轴的陀螺仪，加速度计检测物体在载体坐标系统独立三轴的加速度信号，而陀螺仪检测载体相对于导航坐标系的角速度信号，测量物体在三维空间中的角速度和加速度，并以此解算出物体的姿态。在导航中有着很重要的应用价值。\nIMU大多用在需要进行运动控制的设备，如汽车和机器人上。也被用在需要用姿态进行精密位移推算的场合，如潜艇、飞机、导弹和航天器的惯性导航设备等。\n\n利用三轴地磁解耦和三轴加速度计，受外力加速度影响很大，在运动/振动等环境中，输出方向角误差较大,此外地磁传感器有缺点，它的绝对参照物是地磁场的磁力线,地磁的特点是使用范围大，但强度较低，约零点几高斯，非常容易受到其它磁体的干扰， 如果融合了Z轴陀螺仪的瞬时角度，就可以使系统数据更加稳定。加速度测量的是重力方向，在无外力加速度的情况下，能准确输出ROLL/PITCH两轴姿态角度 并且此角度不会有累积误差，在更长的时间尺度内都是准确的。但是加速度传感器测角度的缺点是加速度传感器实际上是用MEMS技术检测惯性力造成的微小形变，而惯性力与重力本质是一样的,所以加速度计就不会区分重力加速度与外力加速度，当系统在三维空间做变速运动时，它的输出就不正确了。\n陀螺仪输出角速度是瞬时量，角速度在姿态平衡上不能直接使用， 需要角速度与时间积分计算角度，得到的角度变化量与初始角度相加，就得到目标角度，其中积分时间Dt越小输出的角度越精确。但陀螺仪的原理决定了它的测量基准是自身，并没有系统外的绝对参照物，加上Dt是不可能无限小的，所以积分的累积误差会随着时间的流逝迅速增加，最终导致输出角度与实际不符，所以陀螺仪只能工作在相对较短的时间尺度内[1]  。\n所以在没有其它参照物的基础上，要得到较为真实的姿态角，就要利用加权算法扬长避短，结合两者的优点，摈弃其各自缺点,设计算法在短时间尺度内增加陀螺仪的权值，在更长时间尺度内增加加速度权值，这样系统输出角度就接近真实值了。\n\n\nroll/pitch does not drift,though yaw does：\n如果机器人移动缓慢，那么加速度测量影响因素直接来源于G/g：\nf=ma，a=f/m，m=G（重力）/g 。（9.8N/kg)\n\n\n\n*/"
  },
  {
    "path": "mapping/map_builder.cc",
    "content": "\n#include \"cartographer/mapping/map_builder.h\"\n\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping/collated_trajectory_builder.h\"\n#include \"cartographer/mapping_2d/global_trajectory_builder.h\"\n#include \"cartographer/mapping_3d/global_trajectory_builder.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n//backpack_3d.lua/revo_lds.lua/taurob_tracker.lua\n/*\n\ninclude \"map_builder.lua\"\ninclude \"trajectory_builder.lua\"\n\noptions = {\n  map_builder = MAP_BUILDER,\n  trajectory_builder = TRAJECTORY_BUILDER,\n  map_frame = \"map\",\n  tracking_frame = \"base_link\",\n  published_frame = \"base_link\",\n  odom_frame = \"odom\",\n  provide_odom_frame = true,\n  use_odometry = false,\n  use_laser_scan = false,\n  use_multi_echo_laser_scan = false,\n  num_point_clouds = 2,\n  lookup_transform_timeout_sec = 0.2,\n  submap_publish_period_sec = 0.3,\n  pose_publish_period_sec = 5e-3,\n}\n\nTRAJECTORY_BUILDER_3D.scans_per_accumulation = 160\n\nMAP_BUILDER.use_trajectory_builder_3d = true\nMAP_BUILDER.num_background_threads = 7\nMAP_BUILDER.sparse_pose_graph.optimization_problem.huber_scale = 5e2\nMAP_BUILDER.sparse_pose_graph.optimize_every_n_scans = 320\nMAP_BUILDER.sparse_pose_graph.constraint_builder.sampling_ratio = 0.03\nMAP_BUILDER.sparse_pose_graph.optimization_problem.ceres_solver_options.max_num_iterations = 10\n-- Reuse the coarser 3D voxel filter to speed up the computation of loop closure\n-- constraints.\nMAP_BUILDER.sparse_pose_graph.constraint_builder.adaptive_voxel_filter = TRAJECTORY_BUILDER_3D.high_resolution_adaptive_voxel_filter\nMAP_BUILDER.sparse_pose_graph.constraint_builder.min_score = 0.62\n\nreturn options\n\n*/\nproto::MapBuilderOptions CreateMapBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::MapBuilderOptions options;\n  options.set_use_trajectory_builder_2d(\n      parameter_dictionary->GetBool(\"use_trajectory_builder_2d\"));//false\n  options.set_use_trajectory_builder_3d(\n      parameter_dictionary->GetBool(\"use_trajectory_builder_3d\"));//true\n  options.set_num_background_threads(\n      parameter_dictionary->GetNonNegativeInt(\"num_background_threads\"));//7\n\n  //sparse_pose_graph.lua\n  *options.mutable_sparse_pose_graph_options() = CreateSparsePoseGraphOptions(\n      parameter_dictionary->GetDictionary(\"sparse_pose_graph\").get());\n  CHECK_NE(options.use_trajectory_builder_2d(),\n           options.use_trajectory_builder_3d());\n  return options;\n}\n\nMapBuilder::MapBuilder(const proto::MapBuilderOptions& options)\n    : options_(options), thread_pool_(options.num_background_threads()) {\n  if (options.use_trajectory_builder_2d()) {\n    sparse_pose_graph_2d_ = common::make_unique<mapping_2d::SparsePoseGraph>(\n        options_.sparse_pose_graph_options(), &thread_pool_);\n    sparse_pose_graph_ = sparse_pose_graph_2d_.get();\n  }\n  if (options.use_trajectory_builder_3d()) {\n    sparse_pose_graph_3d_ = common::make_unique<mapping_3d::SparsePoseGraph>(\n        options_.sparse_pose_graph_options(), &thread_pool_);\n    sparse_pose_graph_ = sparse_pose_graph_3d_.get();\n  }\n}\n\nMapBuilder::~MapBuilder() {}\n\nint MapBuilder::AddTrajectoryBuilder(\n    const std::unordered_set<string>& expected_sensor_ids,\n    const proto::TrajectoryBuilderOptions& trajectory_options) {\n  const int trajectory_id = trajectory_builders_.size();\n  if (options_.use_trajectory_builder_3d()) {\n    CHECK(trajectory_options.has_trajectory_builder_3d_options());\n    trajectory_builders_.push_back(\n        common::make_unique<CollatedTrajectoryBuilder>(\n            &sensor_collator_, trajectory_id, expected_sensor_ids,\n            common::make_unique<mapping_3d::GlobalTrajectoryBuilder>(\n                trajectory_options.trajectory_builder_3d_options(),\n                trajectory_id, sparse_pose_graph_3d_.get())));\n  } else {\n    CHECK(trajectory_options.has_trajectory_builder_2d_options());\n    trajectory_builders_.push_back(\n        common::make_unique<CollatedTrajectoryBuilder>(\n            &sensor_collator_, trajectory_id, expected_sensor_ids,\n            common::make_unique<mapping_2d::GlobalTrajectoryBuilder>(\n                trajectory_options.trajectory_builder_2d_options(),\n                trajectory_id, sparse_pose_graph_2d_.get())));\n  }\n  return trajectory_id;\n}\n\nTrajectoryBuilder* MapBuilder::GetTrajectoryBuilder(\n    const int trajectory_id) const {\n  return trajectory_builders_.at(trajectory_id).get();\n}\n\nvoid MapBuilder::FinishTrajectory(const int trajectory_id) {\n  sensor_collator_.FinishTrajectory(trajectory_id);\n}\n\nint MapBuilder::GetBlockingTrajectoryId() const {\n  return sensor_collator_.GetBlockingTrajectoryId();\n}\n\nproto::TrajectoryConnectivity MapBuilder::GetTrajectoryConnectivity() {\n  return ToProto(sparse_pose_graph_->GetConnectedTrajectories());\n}\n\nstring MapBuilder::SubmapToProto(const int trajectory_id,\n                                 const int submap_index,\n                                 proto::SubmapQuery::Response* const response) {\n  if (trajectory_id < 0 || trajectory_id >= num_trajectory_builders()) {\n    return \"Requested submap from trajectory \" + std::to_string(trajectory_id) +\n           \" but there are only \" + std::to_string(num_trajectory_builders()) +\n           \" trajectories.\";\n  }\n\n  const std::vector<transform::Rigid3d> submap_transforms =\n      sparse_pose_graph_->GetSubmapTransforms(trajectory_id);\n  if (submap_index < 0 ||\n      static_cast<size_t>(submap_index) >= submap_transforms.size()) {\n    return \"Requested submap \" + std::to_string(submap_index) +\n           \" from trajectory \" + std::to_string(trajectory_id) +\n           \" but there are only \" + std::to_string(submap_transforms.size()) +\n           \" submaps in this trajectory.\";\n  }\n\n  const Submaps* const submaps =\n      trajectory_builders_.at(trajectory_id)->submaps();\n  response->set_submap_version(submaps->Get(submap_index)->num_range_data);\n  submaps->SubmapToProto(submap_index, submap_transforms[submap_index],\n                         response);\n  return \"\";\n}\n\nint MapBuilder::num_trajectory_builders() const {\n  return trajectory_builders_.size();\n}\n\nSparsePoseGraph* MapBuilder::sparse_pose_graph() { return sparse_pose_graph_; }\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/map_builder.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_MAP_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_MAP_BUILDER_H_\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/mapping/proto/map_builder_options.pb.h\"\n#include \"cartographer/mapping/proto/submap_visualization.pb.h\"\n#include \"cartographer/mapping/proto/trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/mapping/trajectory_builder.h\"\n#include \"cartographer/mapping_2d/sparse_pose_graph.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph.h\"\n#include \"cartographer/sensor/collator.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nproto::MapBuilderOptions CreateMapBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary);\n\n/*\nMapBuilder类,建图,不可拷贝/赋值\n\n\nMapBuilder类和TrajectoryBuilder类即真正的开始重建局部子图submaps,\n并且采集稀疏位姿图用于闭环检测。\n\n1)构造函数根据options初始化数据成员，包括线程数量和sparse_pose_graph_options。\n\n\nWires up（为…） 接通电源;\n\nWires up the complete SLAM stack with TrajectoryBuilders (for local submaps)\nand a SparsePoseGraph for loop closure.\n\n*/\nclass MapBuilder {\n public:\n  MapBuilder(const proto::MapBuilderOptions& options);\n  ~MapBuilder();\n\n  MapBuilder(const MapBuilder&) = delete;\n  MapBuilder& operator=(const MapBuilder&) = delete;\n\n  // Create a new trajectory and return its index.\n  //根据传感器id和options新建一个轨迹线，返回轨迹线的索引\n  int AddTrajectoryBuilder(\n      const std::unordered_set<string>& expected_sensor_ids,\n      const proto::TrajectoryBuilderOptions& trajectory_options);\n\n  // Returns the TrajectoryBuilder corresponding to the specified\n  // 'trajectory_id'.\n  //根据轨迹id返回指向该轨迹的TrajectoryBuilder对象指针。\n  mapping::TrajectoryBuilder* GetTrajectoryBuilder(int trajectory_id) const;\n\n  // Marks the TrajectoryBuilder corresponding to 'trajectory_id' as finished,\n  // i.e. no further sensor data is expected.\n  //标记该轨迹已完成data采集，后续不再接收data\n  void FinishTrajectory(int trajectory_id);\n\n  // Must only be called if at least one unfinished trajectory exists. Returns\n  // the ID of the trajectory that needs more data before the MapBuilder is\n  // unblocked.\n  //阻塞的轨迹，常见于该条轨迹上的传感器迟迟不提交data。\n  int GetBlockingTrajectoryId() const;\n\n  // Returns the trajectory connectivity.获得一系列轨迹的连通域\n  proto::TrajectoryConnectivity GetTrajectoryConnectivity();\n\n  // Fills the SubmapQuery::Response corresponding to 'submap_index' from\n  // 'trajectory_id'. Returns an error string on failure, or an empty string on\n  // success.\n  //把轨迹id和子图索引对应的submap，序列化到文件\n  string SubmapToProto(int trajectory_id, int submap_index,\n                       proto::SubmapQuery::Response* response);\n\n  //在建图的轨迹数量\n  int num_trajectory_builders() const;\n\n  mapping::SparsePoseGraph* sparse_pose_graph();\n\n private:\n  const proto::MapBuilderOptions options_; // 建图选项\n  common::ThreadPool thread_pool_;         //线程数量，不可变。\n\n  std::unique_ptr<mapping_2d::SparsePoseGraph> sparse_pose_graph_2d_; //稀疏2D图\n  std::unique_ptr<mapping_3d::SparsePoseGraph> sparse_pose_graph_3d_; //稀疏3D图\n  mapping::SparsePoseGraph* sparse_pose_graph_;  //稀疏位姿\n\n  sensor::Collator sensor_collator_;  //收集传感器采集的数据\n  std::vector<std::unique_ptr<mapping::TrajectoryBuilder>> trajectory_builders_;//轨迹线集合\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_MAP_BUILDER_H_\n"
  },
  {
    "path": "mapping/odometry_state_tracker.cc",
    "content": "\n#include \"cartographer/mapping/odometry_state_tracker.h\"\n\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nOdometryState::OdometryState(const common::Time time,\n                             const transform::Rigid3d& odometer_pose,\n                             const transform::Rigid3d& state_pose)\n    : time(time), odometer_pose(odometer_pose), state_pose(state_pose) {}\n\nOdometryStateTracker::OdometryStateTracker(const int window_size)\n    : window_size_(window_size) {\n  CHECK_GT(window_size, 0);\n}\n\nconst OdometryStateTracker::OdometryStates&\nOdometryStateTracker::odometry_states() const {\n  return odometry_states_;\n}\n\n//添新删旧\nvoid OdometryStateTracker::AddOdometryState(\n    const OdometryState& odometry_state) {\n  odometry_states_.push_back(odometry_state);\n  while (odometry_states_.size() > window_size_) {\n    odometry_states_.pop_front();\n  }\n}\n\nbool OdometryStateTracker::empty() const { return odometry_states_.empty(); }\n\n//返回最后一个\nconst OdometryState& OdometryStateTracker::newest() const {\n  return odometry_states_.back();\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/odometry_state_tracker.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_ODOMETRY_STATE_TRACKER_H_\n#define CARTOGRAPHER_MAPPING_ODOMETRY_STATE_TRACKER_H_\n\n#include <deque>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping {\n/*\n.lua设置：\nimu_gravity_time_constant = 10.,\nnum_odometry_states = 1000,\n\nOdometryState类含3个数据成员\n1,time，时间\n2,Rigid3d，里程计的位置\n3,Rigid3d，状态位置\n\n*/\nstruct OdometryState {\n  OdometryState(common::Time time, const transform::Rigid3d& odometer_pose,\n                const transform::Rigid3d& state_pose);\n  OdometryState() {}\n\n  common::Time time = common::Time::min();\n  transform::Rigid3d odometer_pose = transform::Rigid3d::Identity();\n  transform::Rigid3d state_pose = transform::Rigid3d::Identity();\n};\n\n/*\nOdometryStateTracker:记录跟踪里程计的多个状态类,maxsize :window_size\n含义2个数据成员\n1,OdometryStates，记录多个里程计状态，是一个双端队列deque\n2,window_size_,滑动窗大小,即队列大小\n成员函数：\n1，构造函数初始化滑动窗大小.\n2，AddOdometryState()添加一个新的里程计状态，若deque已满则删除旧有的States\n\n\n*/\n// Keeps track of the odometry states by keeping sliding window over some\n// number of them.\nclass OdometryStateTracker {\n public:\n  using OdometryStates = std::deque<OdometryState>;\n\n  explicit OdometryStateTracker(int window_size);\n\n  const OdometryStates& odometry_states() const;\n\n/*添加新的里程计状态,超出滑动窗大小时,旧的删除*/\n  // Adds a new 'odometry_state' and makes sure the maximum number of previous\n  // odometry states is not exceeded.\n  void AddOdometryState(const OdometryState& odometry_state);\n\n  // Returns true if no elements are present in the odometry queue.\n  bool empty() const;\n\n  // Retrieves the most recent OdometryState. Must not be called when empty.\n  const OdometryState& newest() const;\n\n private:\n  OdometryStates odometry_states_;\n  size_t window_size_;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_ODOMETRY_STATE_TRACKER_H_\n"
  },
  {
    "path": "mapping/probability_values.cc",
    "content": "\n#include \"cartographer/mapping/probability_values.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nnamespace {\n\n\n//直接使用math计算法,相比于查表法，较慢。所以先用math计算好，\n//然后储存到table中，以后直接查找value对应的p\n\n// 0 is unknown, [1, 32767] maps to [kMinProbability, kMaxProbability].\nfloat SlowValueToProbability(const uint16 value) {\n  CHECK_GE(value, 0);\n  CHECK_LE(value, 32767);\n  if (value == kUnknownProbabilityValue) { //没有概率就返回最低概率\n    // Unknown cells have kMinProbability.\n    return kMinProbability;\n  }\n  const float kScale = (kMaxProbability - kMinProbability) / 32766.f;\n  return value * kScale + (kMinProbability - kScale);//返回[0.1,0.9]\n}\n\n/*\n\n查表法,快速.类似于9*9乘法表的作用,vector存储的是提前计算好的value到p的映射。\n\n提前计算好[1,32767]到[0.1,0.9]之间的映射关系。\n返回值：vector<float>: 长度size()=32767*2.\n\n即[0,1,2,...,32767,\n   0,1,2,...,32767]\n*/\nconst std::vector<float>* PrecomputeValueToProbability() {\n  std::vector<float>* result = new std::vector<float>;\n  // Repeat two times, so that both values with and without the update marker\n  // can be converted to a probability.\n  for (int repeat = 0; repeat != 2; ++repeat) {\n    for (int value = 0; value != 32768; ++value) {\n      result->push_back(SlowValueToProbability(value));\n    }\n  }\n  return result;\n}\n\n}  // namespace\n\n//定义,计算value到p的映射：[0,32767]->[0.1,0.9]\nconst std::vector<float>* const kValueToProbability =\n    PrecomputeValueToProbability();\n\n\n/*\n参数：odds，\n返回值：vector<uint16>,size()=1+32767.\n\n之前没有hit过，则没有update，按论文公式(2)计算：\n求p,\n求[1,32767],\n求[1,32767]+32768\npush_back()\n\n之前有hit过，则有update，按论文公式(3)计算：\n求(*kValueToProbability)[cell]->[0.1,0.9]即原始p\n求p'=odds*Odds(p)\n求p'映射到[1,32767]\npush_back():[1,32767]+32768.\n\n*/\nstd::vector<uint16> ComputeLookupTableToApplyOdds(const float odds) {\n  std::vector<uint16> result;\n  result.push_back(ProbabilityToValue(ProbabilityFromOdds(odds)) +\n                   kUpdateMarker);//标记没有update。\n  //标记有update。\n  for (int cell = 1; cell != 32768; ++cell) {\n    result.push_back(ProbabilityToValue(ProbabilityFromOdds(\n                         odds * Odds((*kValueToProbability)[cell]))) +\n                     kUpdateMarker);\n  }\n  return result;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n\n/*\nkUpdateMarker:32768\n*/"
  },
  {
    "path": "mapping/probability_values.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_PROBABILITY_VALUES_H_\n#define CARTOGRAPHER_MAPPING_PROBABILITY_VALUES_H_\n\n#include <cmath>\n#include <vector>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n多个用于计算概率的mapping命名空间下的全局函数\n\n*/\ninline float Odds(float probability) {          //论文公式(2),求胜负比。y=x/(1-x)\n  return probability / (1.f - probability);\n}\n\ninline float ProbabilityFromOdds(const float odds) { //求概率，即x=y/(1+y)\n  return odds / (odds + 1.f);\n}\n\nconstexpr float kMinProbability = 0.1f;//p最小是0.1\nconstexpr float kMaxProbability = 1.f - kMinProbability;//最大是0.9\n\n/*\n限制概率p在[0.1,0.9]之间\n*/\n// Clamps probability to be in the range [kMinProbability, kMaxProbability].\ninline float ClampProbability(const float probability) {\n  return common::Clamp(probability, kMinProbability, kMaxProbability);\n}\n\nconstexpr uint16 kUnknownProbabilityValue = 0;//标记未初始化的概率\nconstexpr uint16 kUpdateMarker = 1u << 15;// 32768\n\n/*\n将概率p映射为整数Value，\n[0.0,1.0]:\n->[0.1,0.9]\n->[1,32767]\n*/\n// Converts a probability to a uint16 in the [1, 32767] range.\ninline uint16 ProbabilityToValue(const float probability) {\n  const int value =\n      common::RoundToInt((ClampProbability(probability) - kMinProbability) *\n                         (32766.f / (kMaxProbability - kMinProbability))) +\n      1;\n  // DCHECK for performance.\n  DCHECK_GE(value, 1);\n  DCHECK_LE(value, 32767);\n  return value;\n}\n\n//声明，定义在.cc文件。vector是value到p的映射\nextern const std::vector<float>* const kValueToProbability;\n\n\n/*\n反映射 [1,32767]->[0.1,0.9]\n利用查表法，快速获得值，而不是用math函数求值。\n*/\n// Converts a uint16 (which may or may not have the update marker set) to a\n// probability in the range [kMinProbability, kMaxProbability].\ninline float ValueToProbability(const uint16 value) {\n  return (*kValueToProbability)[value];\n}\n\n//2份value:前一半对应没有hit，后一半对应hit。\nstd::vector<uint16> ComputeLookupTableToApplyOdds(float odds);\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_PROBABILITY_VALUES_H_\n\n/*\n\nOdds()含义:\nOdds are ratios of a player’s chances of losing to his or her chances of winning, or the average frequency of a loss to the average frequency of a win. \nhttp://www.problemgambling.ca/en/resourcesforprofessionals/pages/probabilityoddsandrandomchance.aspx\n\n\n*/"
  },
  {
    "path": "mapping/probability_values_test.cc",
    "content": "\n#include \"cartographer/mapping/probability_values.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace {\n\nTEST(ProbabilityValuesTest, OddsConversions) {\n  EXPECT_NEAR(ProbabilityFromOdds(Odds(kMinProbability)), kMinProbability,\n              1e-6);\n  EXPECT_NEAR(ProbabilityFromOdds(Odds(kMaxProbability)), kMaxProbability,\n              1e-6);\n  EXPECT_NEAR(ProbabilityFromOdds(Odds(0.5)), 0.5, 1e-6);\n}\n\n}  // namespace\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/proto/map_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping/proto/sparse_pose_graph_options.proto\";\n\npackage cartographer.mapping.proto;\n\nmessage MapBuilderOptions {\n  optional bool use_trajectory_builder_2d = 1;\n  optional bool use_trajectory_builder_3d = 2;\n\n  // Number of threads to use for background computations.\n  optional int32 num_background_threads = 3;\n  optional SparsePoseGraphOptions sparse_pose_graph_options = 4;\n}\n"
  },
  {
    "path": "mapping/proto/sparse_pose_graph.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.proto;\n\nimport \"cartographer/mapping/proto/trajectory.proto\";\nimport \"cartographer/transform/proto/transform.proto\";\n\nmessage SparsePoseGraph {\n  message Constraint {\n    message SubmapId {\n      optional int32 trajectory_id = 1;\n      optional int32 submap_index = 2;  // Submap index in the given trajectory.\n    }\n\n    message ScanId {\n      optional int32 trajectory_id = 1;\n      optional int32 scan_index = 2;  // Scan index in the given trajectory.\n    }\n\n    // Differentiates between intra-submap (where the scan was inserted into the\n    // submap) and inter-submap constraints (where the scan was not inserted\n    // into the submap).\n    enum Tag {\n      INTRA_SUBMAP = 0;\n      INTER_SUBMAP = 1;\n    }\n\n    optional SubmapId submap_id = 1;  // Submap ID.\n    optional ScanId scan_id = 2;  // Scan ID.\n    // Pose of the scan relative to submap, i.e. taking data from the scan frame\n    // into the submap frame.\n    optional transform.proto.Rigid3d relative_pose = 3;\n    // Weight of the translational part of the constraint.\n    optional double translation_weight = 6;\n    // Weight of the rotational part of the constraint.\n    optional double rotation_weight = 7;\n    optional Tag tag = 5;\n  }\n\n  repeated Constraint constraint = 2;\n  repeated Trajectory trajectory = 4;\n}\n"
  },
  {
    "path": "mapping/proto/sparse_pose_graph_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.proto;\n\nimport \"cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.proto\";\nimport \"cartographer/mapping/sparse_pose_graph/proto/optimization_problem_options.proto\";\n\nmessage SparsePoseGraphOptions {\n  // Online loop closure: If positive, will run the loop closure while the map\n  // is built.\n  optional int32 optimize_every_n_scans = 1;\n\n  // Options for the constraint builder.\n  optional mapping.sparse_pose_graph.proto.ConstraintBuilderOptions\n      constraint_builder_options = 3;\n\n  // Weight used in the optimization problem for the translational component of\n  // non-loop-closure scan matcher constraints.\n  optional double matcher_translation_weight = 7;\n\n  // Weight used in the optimization problem for the rotational component of\n  // non-loop-closure scan matcher constraints.\n  optional double matcher_rotation_weight = 8;\n\n  // Options for the optimization problem.\n  optional mapping.sparse_pose_graph.proto.OptimizationProblemOptions\n      optimization_problem_options = 4;\n\n  // Number of iterations to use in 'optimization_problem_options' for the final\n  // optimization.\n  optional int32 max_num_final_iterations = 6;\n\n  // Rate at which we sample a single trajectory's scans for global\n  // localization.\n  optional double global_sampling_ratio = 5;\n}\n"
  },
  {
    "path": "mapping/proto/submap_visualization.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/transform/proto/transform.proto\";\n\npackage cartographer.mapping.proto;\n\nmessage SubmapList {\n  message SubmapEntry {\n    optional int32 submap_version = 1;\n    optional transform.proto.Rigid3d pose = 3;\n  }\n\n  message TrajectorySubmapList {\n    repeated SubmapEntry submap = 1;\n  }\n\n  repeated TrajectorySubmapList trajectory = 2;\n}\n\nmessage SubmapQuery {\n  message Request {\n    // Index into 'SubmapList.trajectory(trajectory_id).submap'.\n    optional int32 submap_index = 1;\n    // Index into 'TrajectoryList.trajectory'.\n    optional int32 trajectory_id = 2;\n  }\n\n  message Response {\n    // Version of the given submap, higher means newer.\n    optional int32 submap_version = 2;\n\n    // GZipped map data, in row-major order, starting with (0,0). Each cell\n    // consists of two bytes: value (premultiplied by alpha) and alpha.\n    optional bytes cells = 3;\n\n    // Dimensions of the grid in cells.\n    optional int32 width = 4;\n    optional int32 height = 5;\n\n    // Size of one cell in meters.\n    optional double resolution = 6;\n\n    // Pose of the resolution*width x resolution*height rectangle in the submap\n    // frame.\n    optional transform.proto.Rigid3d slice_pose = 9;\n\n    // Error message in response to malformed requests.\n    optional string error_message = 8;\n  }\n\n  optional Request request = 1;\n  optional Response response = 2;\n}\n"
  },
  {
    "path": "mapping/proto/trajectory.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.proto;\n\nimport \"cartographer/transform/proto/transform.proto\";\n\noption java_outer_classname = \"TrajectoryOuterClass\";\n\nmessage Trajectory {\n  // NEXT_ID: 7\n  message Node {\n    optional int64 timestamp = 1;\n    // Transform from tracking to map frame.\n    optional transform.proto.Rigid3d pose = 5;\n  }\n\n  message Submap {\n    optional transform.proto.Rigid3d pose = 1;\n  }\n\n  // Time-ordered sequence of Nodes.\n  repeated Node node = 1;\n\n  // Submaps associated with the trajectory.\n  repeated Submap submap = 2;\n}\n"
  },
  {
    "path": "mapping/proto/trajectory_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_2d/proto/local_trajectory_builder_options.proto\";\nimport \"cartographer/mapping_3d/proto/local_trajectory_builder_options.proto\";\n\npackage cartographer.mapping.proto;\n\nmessage TrajectoryBuilderOptions {\n  optional mapping_2d.proto.LocalTrajectoryBuilderOptions\n      trajectory_builder_2d_options = 1;\n  optional mapping_3d.proto.LocalTrajectoryBuilderOptions\n      trajectory_builder_3d_options = 2;\n}\n"
  },
  {
    "path": "mapping/proto/trajectory_connectivity.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.proto;\n\n// This is how proto2 calls the outer class since there is already a message\n// with the same name in the file.\noption java_outer_classname = \"TrajectoryConnectivityOuterClass\";\n\n// Connectivity structure between trajectories.\nmessage TrajectoryConnectivity {\n  message ConnectedComponent {\n    repeated int32 trajectory_id = 1;\n  }\n\n  repeated ConnectedComponent connected_component = 1;\n}\n"
  },
  {
    "path": "mapping/sparse_pose_graph/constraint_builder.cc",
    "content": "\n#include \"cartographer/mapping/sparse_pose_graph/constraint_builder.h\"\n\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_3d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\n  namespace mapping {\n    namespace sparse_pose_graph {\n//参数值可以在sparse_pose_graph.lua查看。\n      proto::ConstraintBuilderOptions CreateConstraintBuilderOptions(\n        common::LuaParameterDictionary* const parameter_dictionary) {\n        proto::ConstraintBuilderOptions options;\n        options.set_sampling_ratio(parameter_dictionary->GetDouble(\"sampling_ratio\"));\n        options.set_max_constraint_distance(\n          parameter_dictionary->GetDouble(\"max_constraint_distance\"));\n        *options.mutable_adaptive_voxel_filter_options() =\n        sensor::CreateAdaptiveVoxelFilterOptions(\n          parameter_dictionary->GetDictionary(\"adaptive_voxel_filter\").get());\n        options.set_min_score(parameter_dictionary->GetDouble(\"min_score\"));\n        options.set_global_localization_min_score(\n          parameter_dictionary->GetDouble(\"global_localization_min_score\"));\n        options.set_loop_closure_translation_weight(\n          parameter_dictionary->GetDouble(\"loop_closure_translation_weight\"));\n        options.set_loop_closure_rotation_weight(\n          parameter_dictionary->GetDouble(\"loop_closure_rotation_weight\"));\n        options.set_log_matches(parameter_dictionary->GetBool(\"log_matches\"));\n        *options.mutable_fast_correlative_scan_matcher_options() =\n        mapping_2d::scan_matching::CreateFastCorrelativeScanMatcherOptions(\n          parameter_dictionary->GetDictionary(\"fast_correlative_scan_matcher\")\n          .get());\n        *options.mutable_ceres_scan_matcher_options() =\n        mapping_2d::scan_matching::CreateCeresScanMatcherOptions(\n          parameter_dictionary->GetDictionary(\"ceres_scan_matcher\").get());\n        *options.mutable_fast_correlative_scan_matcher_options_3d() =\n        mapping_3d::scan_matching::CreateFastCorrelativeScanMatcherOptions(\n          parameter_dictionary\n          ->GetDictionary(\"fast_correlative_scan_matcher_3d\")\n          .get());\n        *options.mutable_ceres_scan_matcher_options_3d() =\n        mapping_3d::scan_matching::CreateCeresScanMatcherOptions(\n          parameter_dictionary->GetDictionary(\"ceres_scan_matcher_3d\").get());\n        return options;\n      }\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping\n}  // namespace cartographer\n\n/*\nsparse_pose_graph.lua参数：\n constraint_builder = {\n    sampling_ratio = 0.3,\n    max_constraint_distance = 15.,\n    adaptive_voxel_filter = {\n      max_length = 0.9,\n      min_num_points = 100,\n      max_range = 50.,\n    },\n    min_score = 0.55,\n    global_localization_min_score = 0.6,\n    loop_closure_translation_weight = 1.1e4,\n    loop_closure_rotation_weight = 1e5,\n    log_matches = true,\n    fast_correlative_scan_matcher = {\n      linear_search_window = 7.,\n      angular_search_window = math.rad(30.),\n      branch_and_bound_depth = 7,\n    },\n    ceres_scan_matcher = {\n      occupied_space_weight = 20.,\n      translation_weight = 10.,\n      rotation_weight = 1.,\n      ceres_solver_options = {\n        use_nonmonotonic_steps = true,\n        max_num_iterations = 10,\n        num_threads = 1,\n      },\n    },\n    fast_correlative_scan_matcher_3d = {\n      branch_and_bound_depth = 8,\n      full_resolution_depth = 3,\n      rotational_histogram_size = 120,\n      min_rotational_score = 0.77,\n      linear_xy_search_window = 4.,\n      linear_z_search_window = 1.,\n      angular_search_window = math.rad(15.),\n    },\n    ceres_scan_matcher_3d = {\n      occupied_space_weight_0 = 20.,\n      translation_weight = 10.,\n      rotation_weight = 1.,\n      only_optimize_yaw = false,\n      ceres_solver_options = {\n        use_nonmonotonic_steps = false,\n        max_num_iterations = 10,\n        num_threads = 1,\n      },\n    },\n  },\n\n*/"
  },
  {
    "path": "mapping/sparse_pose_graph/constraint_builder.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace sparse_pose_graph {\n\n//获取sparse_pose_graph.lua配置参数\nproto::ConstraintBuilderOptions CreateConstraintBuilderOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n"
  },
  {
    "path": "mapping/sparse_pose_graph/optimization_problem_options.cc",
    "content": "\n#include \"cartographer/mapping/sparse_pose_graph/optimization_problem_options.h\"\n\n#include \"cartographer/common/ceres_solver_options.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace sparse_pose_graph {\n/*\nsparse_pose_graph.lua:\n\noptimization_problem = {\n    huber_scale = 1e1,\n    acceleration_weight = 1e3,\n    rotation_weight = 3e5,\n    consecutive_scan_translation_penalty_factor = 1e5,\n    consecutive_scan_rotation_penalty_factor = 1e5,\n    log_solver_summary = false,\n    ceres_solver_options = {\n      use_nonmonotonic_steps = false,\n      max_num_iterations = 50,\n      num_threads = 7,\n    },\n  },\n\n参数配置如上。\n*/\nproto::OptimizationProblemOptions CreateOptimizationProblemOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::OptimizationProblemOptions options;\n  options.set_huber_scale(parameter_dictionary->GetDouble(\"huber_scale\"));\n  options.set_acceleration_weight(\n      parameter_dictionary->GetDouble(\"acceleration_weight\"));\n  options.set_rotation_weight(\n      parameter_dictionary->GetDouble(\"rotation_weight\"));\n  options.set_consecutive_scan_translation_penalty_factor(\n      parameter_dictionary->GetDouble(\n          \"consecutive_scan_translation_penalty_factor\"));\n  options.set_consecutive_scan_rotation_penalty_factor(\n      parameter_dictionary->GetDouble(\n          \"consecutive_scan_rotation_penalty_factor\"));\n  options.set_log_solver_summary(\n      parameter_dictionary->GetBool(\"log_solver_summary\"));\n  *options.mutable_ceres_solver_options() =\n      common::CreateCeresSolverOptionsProto(\n          parameter_dictionary->GetDictionary(\"ceres_solver_options\").get());\n  return options;\n}\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/sparse_pose_graph/optimization_problem_options.h",
    "content": "\n\n#ifndef CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_OPTIONS_H_\n#define CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/optimization_problem_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace sparse_pose_graph {\n/*\n从sparse_pose_graph.lua文件配置 获取参数\n\nmessage OptimizationProblemOptions {\n  optional double huber_scale = 1;\n  optional double acceleration_weight = 8;\n  optional double rotation_weight = 9;\n  \n  optional double consecutive_scan_translation_penalty_factor = 2;\n  optional double consecutive_scan_rotation_penalty_factor = 3;\n  optional bool log_solver_summary = 5;\n\n  optional common.proto.CeresSolverOptions ceres_solver_options = 7;\n}\n\n*/\nproto::OptimizationProblemOptions CreateOptimizationProblemOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_OPTIONS_H_\n"
  },
  {
    "path": "mapping/sparse_pose_graph/proto/constraint_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.sparse_pose_graph.proto;\n\nimport \"cartographer/sensor/proto/adaptive_voxel_filter_options.proto\";\nimport \"cartographer/mapping_2d/scan_matching/proto/ceres_scan_matcher_options.proto\";\nimport \"cartographer/mapping_2d/scan_matching/proto/fast_correlative_scan_matcher_options.proto\";\nimport \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.proto\";\nimport \"cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.proto\";\n\nmessage ConstraintBuilderOptions {\n  // A constraint will be added if the proportion of added constraints to\n  // potential constraints drops below this number.\n  optional double sampling_ratio = 1;\n\n  // Threshold for poses to be considered near a submap.\n  optional double max_constraint_distance = 2;\n\n  // Voxel filter used to compute a sparser point cloud for matching.\n  optional sensor.proto.AdaptiveVoxelFilterOptions\n      adaptive_voxel_filter_options = 3;\n\n  // Threshold for the scan match score below which a match is not considered.\n  // Low scores indicate that the scan and map do not look similar.\n  optional double min_score = 4;\n\n  // Threshold below which global localizations are not trusted.\n  optional double global_localization_min_score = 5;\n\n  // Weight used in the optimization problem for the translational component of\n  // loop closure constraints.\n  optional double loop_closure_translation_weight = 13;\n\n  // Weight used in the optimization problem for the rotational component of\n  // loop closure constraints.\n  optional double loop_closure_rotation_weight = 14;\n\n  // If enabled, logs information of loop-closing constraints for debugging.\n  optional bool log_matches = 8;\n\n  // Options for the internally used scan matchers.\n  optional mapping_2d.scan_matching.proto.FastCorrelativeScanMatcherOptions\n      fast_correlative_scan_matcher_options = 9;\n  optional mapping_2d.scan_matching.proto.CeresScanMatcherOptions\n      ceres_scan_matcher_options = 11;\n  optional mapping_3d.scan_matching.proto.FastCorrelativeScanMatcherOptions\n      fast_correlative_scan_matcher_options_3d = 10;\n  optional mapping_3d.scan_matching.proto.CeresScanMatcherOptions\n      ceres_scan_matcher_options_3d = 12;\n}\n"
  },
  {
    "path": "mapping/sparse_pose_graph/proto/optimization_problem_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping.sparse_pose_graph.proto;\n\nimport \"cartographer/common/proto/ceres_solver_options.proto\";\n\n// NEXT ID: 11\nmessage OptimizationProblemOptions {\n  // Scaling parameter for Huber loss function.\n  optional double huber_scale = 1;\n\n  // Scaling parameter for the IMU acceleration term.\n  optional double acceleration_weight = 8;\n\n  // Scaling parameter for the IMU rotation term.\n  optional double rotation_weight = 9;\n\n  // Penalty factors for changes to the relative pose between consecutive scans.\n  optional double consecutive_scan_translation_penalty_factor = 2;\n  optional double consecutive_scan_rotation_penalty_factor = 3;\n\n  // If true, the Ceres solver summary will be logged for every optimization.\n  optional bool log_solver_summary = 5;\n\n  optional common.proto.CeresSolverOptions ceres_solver_options = 7;\n}\n"
  },
  {
    "path": "mapping/sparse_pose_graph.cc",
    "content": "\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n\n#include \"cartographer/mapping/sparse_pose_graph/constraint_builder.h\"\n#include \"cartographer/mapping/sparse_pose_graph/optimization_problem_options.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nproto::SparsePoseGraph::Constraint::Tag ToProto(\n    const SparsePoseGraph::Constraint::Tag& tag) {\n  switch (tag) {\n    case SparsePoseGraph::Constraint::Tag::INTRA_SUBMAP:\n      return proto::SparsePoseGraph::Constraint::INTRA_SUBMAP;\n    case SparsePoseGraph::Constraint::Tag::INTER_SUBMAP:\n      return proto::SparsePoseGraph::Constraint::INTER_SUBMAP;\n  }\n  LOG(FATAL) << \"Unsupported tag.\";\n}\n\n//sparse_pose_graph.lua\nproto::SparsePoseGraphOptions CreateSparsePoseGraphOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::SparsePoseGraphOptions options;\n  options.set_optimize_every_n_scans(\n      parameter_dictionary->GetInt(\"optimize_every_n_scans\"));\n  *options.mutable_constraint_builder_options() =\n      sparse_pose_graph::CreateConstraintBuilderOptions(\n          parameter_dictionary->GetDictionary(\"constraint_builder\").get());\n  options.set_matcher_translation_weight(\n      parameter_dictionary->GetDouble(\"matcher_translation_weight\"));//5e2\n  options.set_matcher_rotation_weight(\n      parameter_dictionary->GetDouble(\"matcher_rotation_weight\"));// 1.6e3\n  *options.mutable_optimization_problem_options() =\n      sparse_pose_graph::CreateOptimizationProblemOptions(\n          parameter_dictionary->GetDictionary(\"optimization_problem\").get());\n  options.set_max_num_final_iterations(\n      parameter_dictionary->GetNonNegativeInt(\"max_num_final_iterations\"));\n      //200\n  \n  CHECK_GT(options.max_num_final_iterations(), 0);\n  options.set_global_sampling_ratio(\n      parameter_dictionary->GetDouble(\"global_sampling_ratio\"));//0.003\n  return options;\n}\n\nproto::SparsePoseGraph SparsePoseGraph::ToProto() {\n  proto::SparsePoseGraph proto;\n\n  for (const auto& constraint : constraints()) {\n    auto* const constraint_proto = proto.add_constraint();\n    *constraint_proto->mutable_relative_pose() =\n        transform::ToProto(constraint.pose.zbar_ij);\n    constraint_proto->set_translation_weight(\n        constraint.pose.translation_weight);\n    constraint_proto->set_rotation_weight(constraint.pose.rotation_weight);\n    constraint_proto->mutable_submap_id()->set_trajectory_id(\n        constraint.submap_id.trajectory_id);\n    constraint_proto->mutable_submap_id()->set_submap_index(\n        constraint.submap_id.submap_index);\n\n    constraint_proto->mutable_scan_id()->set_trajectory_id(\n        constraint.node_id.trajectory_id);\n    constraint_proto->mutable_scan_id()->set_scan_index(\n        constraint.node_id.node_index);\n\n    constraint_proto->set_tag(mapping::ToProto(constraint.tag));\n  }\n\n  for (const auto& trajectory_nodes : GetTrajectoryNodes()) {\n    auto* trajectory_proto = proto.add_trajectory();\n    for (const auto& node : trajectory_nodes) {\n      auto* node_proto = trajectory_proto->add_node();\n      node_proto->set_timestamp(common::ToUniversal(node.constant_data->time));\n      *node_proto->mutable_pose() =\n          transform::ToProto(node.pose * node.constant_data->tracking_to_pose);\n    }\n\n    if (!trajectory_nodes.empty()) {\n      for (const auto& transform : GetSubmapTransforms(\n               trajectory_nodes[0].constant_data->trajectory_id)) {\n        *trajectory_proto->add_submap()->mutable_pose() =\n            transform::ToProto(transform);\n      }\n    }\n  }\n\n  return proto;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/sparse_pose_graph.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_H_\n#define CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_H_\n\n#include <set>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping/id.h\"\n#include \"cartographer/mapping/proto/sparse_pose_graph.pb.h\"\n#include \"cartographer/mapping/proto/sparse_pose_graph_options.pb.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n根据sparse_pose_graph.lua文件设置option\n\n*/\nproto::SparsePoseGraphOptions CreateSparsePoseGraphOptions(\n    common::LuaParameterDictionary* const parameter_dictionary);\n/*\n\nSparsePoseGraph:稀疏位姿图模型,虚基类,提供多个抽象接口,不可拷贝/赋值\n有一个Constraint的内部类,\n\n*/\nclass SparsePoseGraph {\n public:\n  // A \"constraint\" as in the paper by Konolige, Kurt, et al. \"Efficient sparse\n  // pose adjustment for 2d mapping.\" Intelligent Robots and Systems (IROS),\n  // 2010 IEEE/RSJ International Conference on (pp. 22--29). IEEE, 2010.\n\n  struct Constraint { //约束\n    struct Pose {\n      transform::Rigid3d zbar_ij; //paper-公式(2)\n      double translation_weight;\n      double rotation_weight;\n    };\n\n    mapping::SubmapId submap_id;  // 'i' in the paper.\n    mapping::NodeId node_id;      // 'j' in the paper.\n\n    // Pose of the scan 'j' relative to submap 'i'.\n    Pose pose;\n\n    // Differentiates between intra-submap (where scan 'j' was inserted into\n    // submap 'i') and inter-submap constraints (where scan 'j' was not inserted\n    // into submap 'i').\n    enum Tag { INTRA_SUBMAP, INTER_SUBMAP } tag;\n  };\n\n  SparsePoseGraph() {}\n  virtual ~SparsePoseGraph() {}\n\n  SparsePoseGraph(const SparsePoseGraph&) = delete;\n  SparsePoseGraph& operator=(const SparsePoseGraph&) = delete;\n\n  // Computes optimized poses.\n  //计算优化后的位姿估计\n  virtual void RunFinalOptimization() = 0;\n\n  // Get the current trajectory clusters.\n  //获取已连接的轨迹集合\n  virtual std::vector<std::vector<int>> GetConnectedTrajectories() = 0;\n\n  // Returns the current optimized transforms for the given 'trajectory_id'.\n  //获取优化后的位姿估计的3D变换\n  virtual std::vector<transform::Rigid3d> GetSubmapTransforms(\n      int trajectory_id) = 0;\n\n  // Returns the transform converting data in the local map frame (i.e. the\n  // continuous, non-loop-closed frame) into the global map frame (i.e. the\n  // discontinuous, loop-closed frame).\n  virtual transform::Rigid3d GetLocalToGlobalTransform(int trajectory_id) = 0;\n\n  // Returns the current optimized trajectories.\n  //优化后的轨迹线\n  virtual std::vector<std::vector<TrajectoryNode>> GetTrajectoryNodes() = 0;\n\n  // Serializes the constraints and trajectories.\n  proto::SparsePoseGraph ToProto();\n\n  // Returns the collection of constraints.\n  //获取约束集\n  virtual std::vector<Constraint> constraints() = 0;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_SPARSE_POSE_GRAPH_H_\n"
  },
  {
    "path": "mapping/submaps.cc",
    "content": "\n#include \"cartographer/mapping/submaps.h\"\n\n#include <vector>\n\n#include \"cartographer/common/port.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nconstexpr uint8 Submaps::kUnknownLogOdds;\n\nSubmaps::Submaps() {}\n\nSubmaps::~Submaps() {}\n\nint Submaps::matching_index() const {\n  if (size() > 1) {\n    return size() - 2;\n  }\n  return size() - 1;\n}\n\nstd::vector<int> Submaps::insertion_indices() const {\n  if (size() > 1) {\n    return {size() - 2, size() - 1};\n  }\n  return {size() - 1};\n}\n\nvoid Submaps::AddProbabilityGridToResponse(\n    const transform::Rigid3d& local_submap_pose,\n    const mapping_2d::ProbabilityGrid& probability_grid,\n    proto::SubmapQuery::Response* response) {\n  Eigen::Array2i offset;\n  mapping_2d::CellLimits limits;\n  probability_grid.ComputeCroppedLimits(&offset, &limits);\n\n  string cells;\n  for (const Eigen::Array2i& xy_index :\n       mapping_2d::XYIndexRangeIterator(limits)) {\n    if (probability_grid.IsKnown(xy_index + offset)) {\n      // We would like to add 'delta' but this is not possible using a value and\n      // alpha. We use premultiplied alpha, so when 'delta' is positive we can\n      // add it by setting 'alpha' to zero. If it is negative, we set 'value' to\n      // zero, and use 'alpha' to subtract. This is only correct when the pixel\n      // is currently white, so walls will look too gray. This should be hard to\n      // detect visually for the user, though.\n      const int delta =\n          128 - ProbabilityToLogOddsInteger(\n                    probability_grid.GetProbability(xy_index + offset));\n      const uint8 alpha = delta > 0 ? 0 : -delta;\n      const uint8 value = delta > 0 ? delta : 0;\n      cells.push_back(value);\n      cells.push_back((value || alpha) ? alpha : 1);\n    } else {\n      cells.push_back(static_cast<uint8>(kUnknownLogOdds));  // value\n      cells.push_back(0);                                    // alpha\n    }\n  }\n  common::FastGzipString(cells, response->mutable_cells());\n\n  response->set_width(limits.num_x_cells);\n  response->set_height(limits.num_y_cells);\n  const double resolution = probability_grid.limits().resolution();\n  response->set_resolution(resolution);\n  const double max_x =\n      probability_grid.limits().max().x() - resolution * offset.y();\n  const double max_y =\n      probability_grid.limits().max().y() - resolution * offset.x();\n  *response->mutable_slice_pose() = transform::ToProto(\n      local_submap_pose.inverse() *\n      transform::Rigid3d::Translation(Eigen::Vector3d(max_x, max_y, 0.)));\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/submaps.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_SUBMAPS_H_\n#define CARTOGRAPHER_MAPPING_SUBMAPS_H_\n\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/id.h\"\n#include \"cartographer/mapping/probability_values.h\"\n#include \"cartographer/mapping/proto/submap_visualization.pb.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n\n//求odds(p)的log对数\n// Converts the given probability to log odds.\ninline float Logit(float probability) {\n  return std::log(probability / (1.f - probability));\n}\n\nconst float kMaxLogOdds = Logit(kMaxProbability);\nconst float kMinLogOdds = Logit(kMinProbability);\n\n\n/*\n将[0.1,0.9]映射为0-255之间的数\n*/\n// Converts a probability to a log odds integer. 0 means unknown, [kMinLogOdds,\n// kMaxLogOdds] is mapped to [1, 255].\ninline uint8 ProbabilityToLogOddsInteger(const float probability) {\n  const int value = common::RoundToInt((Logit(probability) - kMinLogOdds) *\n                                       254.f / (kMaxLogOdds - kMinLogOdds)) +\n                    1;\n  CHECK_LE(1, value);\n  CHECK_GE(255, value);\n  return value;\n}\n\n/*\n一个独立的子图,在局部slam frame中有一个local_pose,\n追踪有多少range data 添加到其中.并设置finished_probability_grid用于闭环检查\n\n数据成员有\n1,local_pose                 位姿\n2,num_range_data             已经插入的测量数据数量\n3,finished_probability_grid  完成建图的概率网格\n\n*/\n\n// An individual submap, which has a 'local_pose' in the local SLAM frame, keeps\n// track of how many range data were inserted into it, and sets the\n// 'finished_probability_grid' to be used for loop closing once the map no\n// longer changes.\nstruct Submap {\n  Submap(const transform::Rigid3d& local_pose) : local_pose(local_pose) {}\n\n  // Local SLAM pose of this submap.\n  const transform::Rigid3d local_pose;//子图的位姿\n\n  // Number of RangeData inserted.\n  int num_range_data = 0;\n\n  // The 'finished_probability_grid' when this submap is finished and will not\n  // change anymore. Otherwise, this is nullptr and the next call to\n  // InsertRangeData() will change the submap.\n  //当子图不再变化时，指向该子图的概率分布网格。\n  const mapping_2d::ProbabilityGrid* finished_probability_grid = nullptr;\n};\n\n/*\n\n Submaps is a sequence of maps to which scans are matched and into which scans are inserted.\n Except during initialization when only a single submap exists, there are\n always two submaps into which scans are inserted: an old submap that is used\n for matching, and a new one, which will be used for matching next, that is\n being initialized.\n\n Once a certain number of scans have been inserted, the new submap is\n considered initialized: the old submap is no longer changed, the \"new\" submap\n is now the \"old\" submap and is used for scan-to-map matching. Moreover,\n a \"new\" submap gets inserted.\n\nSubmaps是一连串的子图,初始化以后任何阶段均有2个子图被当前scan point影响：\nold submap 用于now match,new submap用于next match,一直交替下去。\n一旦new submap有足够多的scan point，那么old submap不再更新。\n此时new submap变为old，用于 scan-to-map匹配。\n\nSubmaps不可拷贝/赋值,是虚基类\n\n虚基类,没有数据成员,只提供成员函数用于实现接口。\n\n成员函数 \n1,matching_index()。返回最后一个submap的索引,用于配准:len-2\n2,insertion_indices()。返回最后2个submap的索引,用于点云插入;{len-2,len-1}\n3,Get()返回给定索引的子图\n4,AddProbabilityGridToResponse()\n*/\nclass Submaps {\n public:\n  static constexpr uint8 kUnknownLogOdds = 0;\n\n  Submaps();\n  virtual ~Submaps();\n\n  Submaps(const Submaps&) = delete;\n  Submaps& operator=(const Submaps&) = delete;\n\n  // Returns the index of the newest initialized Submap which can be\n  // used for scan-to-map matching.\n  // size() - 2;\n  int matching_index() const;//最新的初始化的子图索引，用于scan-to-map-match\n\n  // Returns the indices of the Submap into which point clouds will\n  // be inserted.\n  // {size() - 2, size() - 1};\n  std::vector<int> insertion_indices() const;//待插入的子图的索引\n\n  // Returns the Submap with the given 'index'. The same 'index' will always\n  // return the same pointer, so that Submaps can be identified by it.\n  virtual const Submap* Get(int index) const = 0;//纯虚函数，按索引返回子图指针。\n\n  // Returns the number of Submaps.\n  virtual int size() const = 0;\n\n  //将对应的子图序列化到proto文件中\n  // Fills data about the Submap with 'index' into the 'response'.\n  virtual void SubmapToProto(int index,\n                             const transform::Rigid3d& global_submap_pose,\n                             proto::SubmapQuery::Response* response) const = 0;\n\n protected:\n  //将子图对应的概率网格序列化到proto文件中\n  static void AddProbabilityGridToResponse(\n      const transform::Rigid3d& local_submap_pose,\n      const mapping_2d::ProbabilityGrid& probability_grid,\n      proto::SubmapQuery::Response* response);\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_SUBMAPS_H_\n"
  },
  {
    "path": "mapping/submaps_test.cc",
    "content": "\n#include \"cartographer/mapping/submaps.h\"\n\n#include <cmath>\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace {\n\n// Converts the given log odds to a probability. This function is known to be\n// very slow, because expf is incredibly slow.\ninline float Expit(float log_odds) {  //求指数倍\n  const float exp_log_odds = std::exp(log_odds);\n  return exp_log_odds / (1.f + exp_log_odds);\n}\n\nTEST(SubmapsTest, LogOddsConversions) {\n  EXPECT_NEAR(Expit(Logit(kMinProbability)), kMinProbability, 1e-6);\n  EXPECT_NEAR(Expit(Logit(kMaxProbability)), kMaxProbability, 1e-6);\n  EXPECT_NEAR(Expit(Logit(0.5)), 0.5, 1e-6); //变换再反变换- >不变\n}\n\n}  // namespace\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/trajectory_builder.cc",
    "content": "\n#include \"cartographer/mapping/trajectory_builder.h\"\n\n#include \"cartographer/mapping_2d/local_trajectory_builder_options.h\"\n#include \"cartographer/mapping_3d/local_trajectory_builder_options.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n//获取trajectory_builder.lua的内容\nproto::TrajectoryBuilderOptions CreateTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::TrajectoryBuilderOptions options;\n  *options.mutable_trajectory_builder_2d_options() =\n      mapping_2d::CreateLocalTrajectoryBuilderOptions(\n          parameter_dictionary->GetDictionary(\"trajectory_builder_2d\").get());\n  *options.mutable_trajectory_builder_3d_options() =\n      mapping_3d::CreateLocalTrajectoryBuilderOptions(\n          parameter_dictionary->GetDictionary(\"trajectory_builder_3d\").get());\n  return options;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/trajectory_builder.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_TRAJECTORY_BUILDER_H_\n\n#include <functional>\n#include <memory>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/proto/trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/sensor/data.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n//trajectory_builder.lua\nproto::TrajectoryBuilderOptions CreateTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary);\n\n/*\nTrajectoryBuilder:虚基类,提供多个抽象接口\n作用：根据轨迹Builder收集data。\n\n成员函数有：\n虚函数：\n1),submaps()\n2),AddSensorData()\n3),pose_estimate()\n\n非虚函数：\n1),AddRangefinderData()\n2),AddImuData()\n3),AddOdometerData()\n\n有一个内部类PoseEstimate,标识当前关键帧的Pose，包括估计位姿的时间,位置,点云\n\n*/\n// This interface is used for both 2D and 3D SLAM.\nclass TrajectoryBuilder {\n public:\n/*\nPoseEstimate代表一个已经估计好的位姿,\npose表示从start点看去的视觉定位,\npoint_cloud表示局部帧的点云\n*/\n  // Represents a newly computed pose. 'pose' is the end-user visualization of\n  // orientation and 'point_cloud' is the point cloud, in the local map frame.\n  struct PoseEstimate { \n\n    PoseEstimate() = default;\n    PoseEstimate(common::Time time, const transform::Rigid3d& pose,\n                 const sensor::PointCloud& point_cloud)\n        : time(time), pose(pose), point_cloud(point_cloud) {}\n\n    common::Time time = common::Time::min(); //测量时间\n    transform::Rigid3d pose = transform::Rigid3d::Identity();//世界坐标转换\n    sensor::PointCloud point_cloud;//子图local map frame的点云\n  };\n\n  TrajectoryBuilder() {}\n  virtual ~TrajectoryBuilder() {}\n\n  TrajectoryBuilder(const TrajectoryBuilder&) = delete;\n  TrajectoryBuilder& operator=(const TrajectoryBuilder&) = delete;\n\n  virtual const Submaps* submaps() const = 0;//一系列子图\n  virtual const PoseEstimate& pose_estimate() const = 0;//子图位姿及其采集的点云\n\n/*\n根据sensor_id添加data，虚函数。\n*/\n  virtual void AddSensorData(const string& sensor_id,\n                             std::unique_ptr<sensor::Data> data) = 0;\n\n/*\n下面3个函数都是非虚函数。\n分别是添加雷达/imu/里程计的data。\n\n参数：\n1),sensor_id,标识传感器。\n2),time 测量时间\n3),PointCloud/Vector3d  /Rigid/Rigid3d 测量得到的数据\n*/\n  void AddRangefinderData(const string& sensor_id, common::Time time,\n                          const Eigen::Vector3f& origin,\n                          const sensor::PointCloud& ranges) {\n    AddSensorData(sensor_id,\n                  common::make_unique<sensor::Data>(\n                      time, sensor::Data::Rangefinder{origin, ranges}));\n  }\n\n  void AddImuData(const string& sensor_id, common::Time time,\n                  const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity) {\n    AddSensorData(sensor_id, common::make_unique<sensor::Data>(\n                                 time, sensor::Data::Imu{linear_acceleration,\n                                                         angular_velocity}));\n  }\n\n  void AddOdometerData(const string& sensor_id, common::Time time,\n                       const transform::Rigid3d& odometer_pose) {\n    AddSensorData(sensor_id,\n                  common::make_unique<sensor::Data>(time, odometer_pose));\n  }\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping/trajectory_connectivity.cc",
    "content": "\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n\n#include <algorithm>\n#include <unordered_set>\n\n#include \"cartographer/mapping/proto/trajectory_connectivity.pb.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nTrajectoryConnectivity::TrajectoryConnectivity()\n    : lock_(), forest_(), connection_map_() {}\n\nvoid TrajectoryConnectivity::Add(const int trajectory_id) {\n  common::MutexLocker locker(&lock_);\n  forest_.emplace(trajectory_id, trajectory_id);\n}\n\nvoid TrajectoryConnectivity::Connect(const int trajectory_id_a,\n                                     const int trajectory_id_b) {\n  common::MutexLocker locker(&lock_);\n  Union(trajectory_id_a, trajectory_id_b);\n  auto sorted_pair = std::minmax(trajectory_id_a, trajectory_id_b);\n  ++connection_map_[sorted_pair];\n}\n\nvoid TrajectoryConnectivity::Union(const int trajectory_id_a,\n                                   const int trajectory_id_b) {\n  forest_.emplace(trajectory_id_a, trajectory_id_a);\n  forest_.emplace(trajectory_id_b, trajectory_id_b);\n  const int representative_a = FindSet(trajectory_id_a);\n  const int representative_b = FindSet(trajectory_id_b);\n  forest_[representative_a] = representative_b;\n}\n\nint TrajectoryConnectivity::FindSet(const int trajectory_id) {\n  auto it = forest_.find(trajectory_id);\n  CHECK(it != forest_.end());\n  if (it->first != it->second) {\n    it->second = FindSet(it->second);\n  }\n  return it->second;\n}\n\nbool TrajectoryConnectivity::TransitivelyConnected(const int trajectory_id_a,\n                                                   const int trajectory_id_b) {\n  common::MutexLocker locker(&lock_);\n\n  if (forest_.count(trajectory_id_a) == 0 ||\n      forest_.count(trajectory_id_b) == 0) {\n    return false;\n  }\n  return FindSet(trajectory_id_a) == FindSet(trajectory_id_b);\n}\n\nstd::vector<std::vector<int>> TrajectoryConnectivity::ConnectedComponents() {\n  // Map from cluster exemplar -> growing cluster.\n  std::unordered_map<int, std::vector<int>> map;\n  common::MutexLocker locker(&lock_);\n  for (const auto& trajectory_id_entry : forest_) {\n    map[FindSet(trajectory_id_entry.first)].push_back(\n        trajectory_id_entry.first);\n  }\n\n  std::vector<std::vector<int>> result;\n  result.reserve(map.size());\n  for (auto& pair : map) {\n    result.emplace_back(std::move(pair.second));\n  }\n  return result;\n}\n\nint TrajectoryConnectivity::ConnectionCount(const int trajectory_id_a,\n                                            const int trajectory_id_b) {\n  common::MutexLocker locker(&lock_);\n  const auto it =\n      connection_map_.find(std::minmax(trajectory_id_a, trajectory_id_b));\n  return it != connection_map_.end() ? it->second : 0;\n}\n\nproto::TrajectoryConnectivity ToProto(\n    std::vector<std::vector<int>> connected_components) {\n  proto::TrajectoryConnectivity proto;\n  for (auto& connected_component : connected_components) {\n    std::sort(connected_component.begin(), connected_component.end());\n  }\n  std::sort(connected_components.begin(), connected_components.end());\n  for (const auto& connected_component : connected_components) {\n    auto* proto_connected_component = proto.add_connected_component();\n    for (const int trajectory_id : connected_component) {\n      proto_connected_component->add_trajectory_id(trajectory_id);\n    }\n  }\n  return proto;\n}\n\nproto::TrajectoryConnectivity::ConnectedComponent FindConnectedComponent(\n    const proto::TrajectoryConnectivity& trajectory_connectivity,\n    const int trajectory_id) {\n  for (const auto& connected_component :\n       trajectory_connectivity.connected_component()) {\n    if (std::find(connected_component.trajectory_id().begin(),\n                  connected_component.trajectory_id().end(),\n                  trajectory_id) != connected_component.trajectory_id().end()) {\n      return connected_component;\n    }\n  }\n\n  proto::TrajectoryConnectivity::ConnectedComponent connected_component;\n  connected_component.add_trajectory_id(trajectory_id);\n  return connected_component;\n}\n\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/trajectory_connectivity.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_TRAJECTORY_CONNECTIVITY_H_\n#define CARTOGRAPHER_MAPPING_TRAJECTORY_CONNECTIVITY_H_\n\n#include <map>\n#include <unordered_map>\n\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/mapping/proto/trajectory_connectivity.pb.h\"\n#include \"cartographer/mapping/submaps.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\n/*\n\nTrajectoryConnectivity用于解决不同轨迹线的连通性问题.\n\n多条轨迹构成一颗森林，而相互联通的轨迹应该合并。\n\n不可拷贝/赋值\n包含3个数据成员\n1,互斥锁lock_\n2,forest_  不同轨迹线组成的森林\n3,connection_map_ 连通图\n成员函数\nAdd():添加一条轨迹线\nConnect():将2条轨迹线联通\nTransitivelyConnected():判断是否处于同一个连通域\nConnectionCount():返回直接联通的数量\n\nthe transitive connectivity:传递连通性\n\nConnectedComponents():由联通分量id组成的已联通分类组\n*/\n// A class that tracks the connectivity structure between trajectories.\n//\n// Connectivity includes both the count (\"How many times have I _directly_\n// connected trajectories i and j?\") and the transitive connectivity.\n//\n// This class is thread-safe.\nclass TrajectoryConnectivity {\n public:\n  TrajectoryConnectivity();\n\n  TrajectoryConnectivity(const TrajectoryConnectivity&) = delete;\n  TrajectoryConnectivity& operator=(const TrajectoryConnectivity&) = delete;\n\n  // Add a trajectory which is initially connected to nothing.\n  //添加一条轨迹线，默认不连接到任何轨迹线\n  void Add(int trajectory_id) EXCLUDES(lock_);\n\n /*\n\n Connect two trajectories. If either trajectory is untracked, it will be  tracked.  This function is invariant to the order of its arguments. \n Repeated calls to Connect increment the connectivity count.\n将轨迹a和轨迹b联通\n  */\n  void Connect(int trajectory_id_a, int trajectory_id_b) EXCLUDES(lock_);\n\n  // Determines if two trajectories have been (transitively) connected. If\n  // either trajectory is not being tracked, returns false. This function is\n  // invariant to the order of its arguments.\n  //:判断是否处于同一个连通域\n  bool TransitivelyConnected(int trajectory_id_a, int trajectory_id_b)\n      EXCLUDES(lock_);\n\n  // Return the number of _direct_ connections between 'trajectory_id_a' and\n  // 'trajectory_id_b'. If either trajectory is not being tracked, returns 0.\n  // This function is invariant to the order of its arguments.\n  //返回直接联通的数量\n  int ConnectionCount(int trajectory_id_a, int trajectory_id_b) EXCLUDES(lock_);\n\n  // The trajectory IDs, grouped by connectivity.\n  std::vector<std::vector<int>> ConnectedComponents() EXCLUDES(lock_);\n\n private:\n  // Find the representative and compresses the path to it.\n  int FindSet(int trajectory_id) REQUIRES(lock_);\n  void Union(int trajectory_id_a, int trajectory_id_b) REQUIRES(lock_);\n\n  common::Mutex lock_; //互斥锁\n\n  // Tracks transitive connectivity using a disjoint set forest, i.e. each\n  // entry points towards the representative for the given trajectory.\n  // 不同轨迹线组成的森林，即连通域问题。\n  std::map<int, int> forest_ GUARDED_BY(lock_); \n\n  \n  // Tracks the number of direct connections between a pair of trajectories.\n  //直接链接的轨迹线\n  std::map<std::pair<int, int>, int> connection_map_ GUARDED_BY(lock_);\n};\n\n\n// Returns a proto encoding connected components.编码已联通成分到proto文件\nproto::TrajectoryConnectivity ToProto(\n    std::vector<std::vector<int>> connected_components);\n\n// Returns the connected component containing 'trajectory_index'. \n//返回连接到联通id的所有联通分量。\nproto::TrajectoryConnectivity::ConnectedComponent FindConnectedComponent(\n    const cartographer::mapping::proto::TrajectoryConnectivity&\n        trajectory_connectivity,\n    int trajectory_id);\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_TRAJECTORY_CONNECTIVITY_H_\n"
  },
  {
    "path": "mapping/trajectory_connectivity_test.cc",
    "content": "\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n\n#include <algorithm>\n#include <memory>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping {\nnamespace {\n\nconstexpr int kNumTrajectories = 10;//轨迹数\n\nTEST(TrajectoryConnectivityTest, TransitivelyConnected) {\n  TrajectoryConnectivity trajectory_connectivity;\n\n  // Make sure nothing's connected until we connect some things.\n  for (int trajectory_a = 0; trajectory_a < kNumTrajectories; ++trajectory_a) {\n    for (int trajectory_b = 0; trajectory_b < kNumTrajectories;\n         ++trajectory_b) {\n      //默认不连通\n      EXPECT_FALSE(trajectory_connectivity.TransitivelyConnected(trajectory_a,\n                                                                 trajectory_b));\n    }\n  }\n\n  // Connect some stuff up.\n  trajectory_connectivity.Connect(0, 1);                            //联通0-1\n  EXPECT_TRUE(trajectory_connectivity.TransitivelyConnected(0, 1)); //true \n  trajectory_connectivity.Connect(8, 9);                            //联通8-9\n  EXPECT_TRUE(trajectory_connectivity.TransitivelyConnected(8, 9)); //true\n  EXPECT_FALSE(trajectory_connectivity.TransitivelyConnected(0, 9));//联通0-9\n\n  trajectory_connectivity.Connect(1, 8);                            //联通1-8  \n  for (int i : {0, 1}) {\n    for (int j : {8, 9}) {\n      EXPECT_TRUE(trajectory_connectivity.TransitivelyConnected(i, j));//均联通\n    }\n  }\n}\n\nTEST(TrajectoryConnectivityTest, EmptyConnectedComponents) {\n  TrajectoryConnectivity trajectory_connectivity;\n  auto connections = trajectory_connectivity.ConnectedComponents();\n  EXPECT_EQ(0, connections.size());                    //0分量\n}\n\nTEST(TrajectoryConnectivityTest, ConnectedComponents) {\n  TrajectoryConnectivity trajectory_connectivity;\n  for (int i = 0; i <= 4; ++i) {\n    trajectory_connectivity.Connect(0, i); //联通0-1,0-2,0-3,0-4\n  }\n  for (int i = 5; i <= 9; ++i) {\n    trajectory_connectivity.Connect(5, i);//联通5-6,5-7,5-8,5-9\n  }\n  auto connections = trajectory_connectivity.ConnectedComponents();\n  ASSERT_EQ(2, connections.size());                    //2个分量/分组\n\n\n  //connections的顺序未定 ，所以所以需要使用find函数。          \n  // The clustering is arbitrary; we need to figure out which one is which.\n  const std::vector<int>* zero_cluster = nullptr;\n  const std::vector<int>* five_cluster = nullptr;\n  if (std::find(connections[0].begin(), connections[0].end(), 0) !=\n      connections[0].end()) {\n    zero_cluster = &connections[0];\n    five_cluster = &connections[1];\n  } else {\n    zero_cluster = &connections[1];\n    five_cluster = &connections[0];\n  }\n  for (int i = 0; i <= 9; ++i) {\n    //查找联通分量\n    EXPECT_EQ(i <= 4, std::find(zero_cluster->begin(), zero_cluster->end(),\n                                i) != zero_cluster->end());\n    EXPECT_EQ(i > 4, std::find(five_cluster->begin(), five_cluster->end(), i) !=\n                         five_cluster->end());\n  }\n}\n\nTEST(TrajectoryConnectivityTest, ConnectionCount) {\n  TrajectoryConnectivity trajectory_connectivity;\n\n  //对同一个连线调用多次\"Connect()\"函数\n  for (int i = 0; i < kNumTrajectories; ++i) {\n    trajectory_connectivity.Connect(0, 1);\n    // Permute the arguments to check invariance.\n    EXPECT_EQ(i + 1, trajectory_connectivity.ConnectionCount(1, 0));\n  }\n  for (int i = 1; i < 9; ++i) {\n    EXPECT_EQ(0, trajectory_connectivity.ConnectionCount(i, i + 1));\n  }\n}\n\n}  // namespace\n}  // namespace mapping\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping/trajectory_node.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_TRAJECTORY_NODE_H_\n#define CARTOGRAPHER_MAPPING_TRAJECTORY_NODE_H_\n\n#include <deque>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping {\n\nclass Submaps;\n/*\n轨迹节点TrajectoryNode类，含有一个内部类ConstantData。\nTrajectoryNode作用：在连续的轨迹上采样一些离散的点用于key frame，标识pose frame。\n\n数据成员：\n1), ConstantData* \n2) 位姿，pose\n\n\nConstantData类：\n\n数据成员：\n1，time ：时间\n2，range_data_2d和range_data_3d：测量数据\n3,trajectory_id:本节点所属的轨迹\n4，Rigid3d ：tracking frame 到 pose frame的矩阵变换。\n*/\nstruct TrajectoryNode {\n\n  struct ConstantData {\n    common::Time time;\n\n    // Range data in 'pose' frame. Only used in the 2D case.\n    sensor::RangeData range_data_2d;//测量得到的2D range数据\n\n    // Range data in 'pose' frame. Only used in the 3D case.\n    sensor::CompressedRangeData range_data_3d;//测量得到的3D range数据\n\n    // Trajectory this node belongs to.\n    int trajectory_id; //节点所属id\n\n    // Transform from the 3D 'tracking' frame to the 'pose' frame of the range\n    // data, which contains roll, pitch and height for 2D. In 3D this is always\n    // identity.\n    transform::Rigid3d tracking_to_pose; //涉及的3D变换\n  };\n\n  common::Time time() const { return constant_data->time; }\n\n  const ConstantData* constant_data;\n  //常指针.指向某块内存,该内存块的数据不变，指针本身可以变。\n\n  transform::Rigid3d pose;\n};\n\n}  // namespace mapping\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_TRAJECTORY_NODE_H_\n"
  },
  {
    "path": "mapping_2d/global_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/global_trajectory_builder.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nGlobalTrajectoryBuilder::GlobalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions& options,\n    const int trajectory_id, SparsePoseGraph* sparse_pose_graph)\n    : trajectory_id_(trajectory_id),\n      sparse_pose_graph_(sparse_pose_graph),\n      local_trajectory_builder_(options) {}\n\nGlobalTrajectoryBuilder::~GlobalTrajectoryBuilder() {}\n\nconst Submaps* GlobalTrajectoryBuilder::submaps() const {\n  return local_trajectory_builder_.submaps();\n}\n\nvoid GlobalTrajectoryBuilder::AddRangefinderData(\n    const common::Time time, const Eigen::Vector3f& origin,\n    const sensor::PointCloud& ranges) {\n  std::unique_ptr<LocalTrajectoryBuilder::InsertionResult> insertion_result =\n      local_trajectory_builder_.AddHorizontalRangeData(\n          time, sensor::RangeData{origin, ranges, {}});\n  if (insertion_result != nullptr) {\n    sparse_pose_graph_->AddScan(\n        insertion_result->time, insertion_result->tracking_to_tracking_2d,\n        insertion_result->range_data_in_tracking_2d,\n        insertion_result->pose_estimate_2d, trajectory_id_,\n        insertion_result->matching_submap, insertion_result->insertion_submaps);\n  }\n}\n\nvoid GlobalTrajectoryBuilder::AddImuData(\n    const common::Time time, const Eigen::Vector3d& linear_acceleration,\n    const Eigen::Vector3d& angular_velocity) {\n  local_trajectory_builder_.AddImuData(time, linear_acceleration,\n                                       angular_velocity);\n  sparse_pose_graph_->AddImuData(trajectory_id_, time, linear_acceleration,\n                                 angular_velocity);\n}\n\nvoid GlobalTrajectoryBuilder::AddOdometerData(const common::Time time,\n                                              const transform::Rigid3d& pose) {\n  local_trajectory_builder_.AddOdometerData(time, pose);\n}\n\nconst mapping::GlobalTrajectoryBuilderInterface::PoseEstimate&\nGlobalTrajectoryBuilder::pose_estimate() const {\n  return local_trajectory_builder_.pose_estimate();\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/global_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_GLOBAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_2D_GLOBAL_TRAJECTORY_BUILDER_H_\n\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_2d/local_trajectory_builder.h\"\n#include \"cartographer/mapping_2d/sparse_pose_graph.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nclass GlobalTrajectoryBuilder\n    : public mapping::GlobalTrajectoryBuilderInterface {\n public:\n  GlobalTrajectoryBuilder(const proto::LocalTrajectoryBuilderOptions& options,\n                          int trajectory_id,\n                          SparsePoseGraph* sparse_pose_graph);\n  ~GlobalTrajectoryBuilder() override;\n\n  GlobalTrajectoryBuilder(const GlobalTrajectoryBuilder&) = delete;\n  GlobalTrajectoryBuilder& operator=(const GlobalTrajectoryBuilder&) = delete;\n\n  const Submaps* submaps() const override;\n  const mapping::GlobalTrajectoryBuilderInterface::PoseEstimate& pose_estimate()\n      const override;\n\n  // Projects 'ranges' into 2D. Therefore, 'ranges' should be approximately\n  // parallel to the ground plane.\n  void AddRangefinderData(common::Time time, const Eigen::Vector3f& origin,\n                          const sensor::PointCloud& ranges) override;\n  void AddImuData(common::Time time, const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity) override;\n  void AddOdometerData(common::Time time,\n                       const transform::Rigid3d& pose) override;\n\n private:\n  const int trajectory_id_;\n  SparsePoseGraph* const sparse_pose_graph_;\n  LocalTrajectoryBuilder local_trajectory_builder_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_GLOBAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_2d/local_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/local_trajectory_builder.h\"\n\n#include <limits>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/sensor/range_data.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nLocalTrajectoryBuilder::LocalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions& options)\n    : options_(options),\n      submaps_(options.submaps_options()),\n      motion_filter_(options_.motion_filter_options()),\n      real_time_correlative_scan_matcher_(\n          options_.real_time_correlative_scan_matcher_options()),\n      ceres_scan_matcher_(options_.ceres_scan_matcher_options()),\n      odometry_state_tracker_(options_.num_odometry_states()) {}\n\nLocalTrajectoryBuilder::~LocalTrajectoryBuilder() {}\n\nconst Submaps* LocalTrajectoryBuilder::submaps() const { return &submaps_; }\n\nsensor::RangeData LocalTrajectoryBuilder::TransformAndFilterRangeData(\n    const transform::Rigid3f& tracking_to_tracking_2d,\n    const sensor::RangeData& range_data) const {\n  // Drop any returns below the minimum range and convert returns beyond the\n  // maximum range into misses.\n  sensor::RangeData returns_and_misses{range_data.origin, {}, {}};\n  for (const Eigen::Vector3f& hit : range_data.returns) {\n    const float range = (hit - range_data.origin).norm();\n    if (range >= options_.min_range()) {\n      if (range <= options_.max_range()) {\n        returns_and_misses.returns.push_back(hit);\n      } else {\n        returns_and_misses.misses.push_back(\n            range_data.origin + options_.missing_data_ray_length() *\n                                    (hit - range_data.origin).normalized());\n      }\n    }\n  }\n  const sensor::RangeData cropped = sensor::CropRangeData(\n      sensor::TransformRangeData(returns_and_misses, tracking_to_tracking_2d),\n      options_.min_z(), options_.max_z());\n  return sensor::RangeData{\n      cropped.origin,\n      sensor::VoxelFiltered(cropped.returns, options_.voxel_filter_size()),\n      sensor::VoxelFiltered(cropped.misses, options_.voxel_filter_size())};\n}\n\nvoid LocalTrajectoryBuilder::ScanMatch(\n    common::Time time, const transform::Rigid3d& pose_prediction,\n    const transform::Rigid3d& tracking_to_tracking_2d,\n    const sensor::RangeData& range_data_in_tracking_2d,\n    transform::Rigid3d* pose_observation) {\n  const ProbabilityGrid& probability_grid =\n      submaps_.Get(submaps_.matching_index())->probability_grid;\n  transform::Rigid2d pose_prediction_2d =\n      transform::Project2D(pose_prediction * tracking_to_tracking_2d.inverse());\n  // The online correlative scan matcher will refine the initial estimate for\n  // the Ceres scan matcher.\n  transform::Rigid2d initial_ceres_pose = pose_prediction_2d;\n  sensor::AdaptiveVoxelFilter adaptive_voxel_filter(\n      options_.adaptive_voxel_filter_options());\n  const sensor::PointCloud filtered_point_cloud_in_tracking_2d =\n      adaptive_voxel_filter.Filter(range_data_in_tracking_2d.returns);\n  if (options_.use_online_correlative_scan_matching()) {\n    real_time_correlative_scan_matcher_.Match(\n        pose_prediction_2d, filtered_point_cloud_in_tracking_2d,\n        probability_grid, &initial_ceres_pose);\n  }\n\n  transform::Rigid2d tracking_2d_to_map;\n  ceres::Solver::Summary summary;\n  ceres_scan_matcher_.Match(pose_prediction_2d, initial_ceres_pose,\n                            filtered_point_cloud_in_tracking_2d,\n                            probability_grid, &tracking_2d_to_map, &summary);\n\n  *pose_observation =\n      transform::Embed3D(tracking_2d_to_map) * tracking_to_tracking_2d;\n}\n\nstd::unique_ptr<LocalTrajectoryBuilder::InsertionResult>\nLocalTrajectoryBuilder::AddHorizontalRangeData(\n    const common::Time time, const sensor::RangeData& range_data) {\n  // Initialize IMU tracker now if we do not ever use an IMU.\n  if (!options_.use_imu_data()) {\n    InitializeImuTracker(time);\n  }\n\n  if (imu_tracker_ == nullptr) {\n    // Until we've initialized the IMU tracker with our first IMU message, we\n    // cannot compute the orientation of the rangefinder.\n    LOG(INFO) << \"ImuTracker not yet initialized.\";\n    return nullptr;\n  }\n\n  Predict(time);\n  const transform::Rigid3d odometry_prediction =\n      pose_estimate_ * odometry_correction_;\n  const transform::Rigid3d model_prediction = pose_estimate_;\n  // TODO(whess): Prefer IMU over odom orientation if configured?\n  const transform::Rigid3d& pose_prediction = odometry_prediction;\n\n  // Computes the rotation without yaw, as defined by GetYaw().\n  const transform::Rigid3d tracking_to_tracking_2d =\n      transform::Rigid3d::Rotation(\n          Eigen::Quaterniond(Eigen::AngleAxisd(\n              -transform::GetYaw(pose_prediction), Eigen::Vector3d::UnitZ())) *\n          pose_prediction.rotation());\n\n  const sensor::RangeData range_data_in_tracking_2d =\n      TransformAndFilterRangeData(tracking_to_tracking_2d.cast<float>(),\n                                  range_data);\n\n  if (range_data_in_tracking_2d.returns.empty()) {\n    LOG(WARNING) << \"Dropped empty horizontal range data.\";\n    return nullptr;\n  }\n\n  ScanMatch(time, pose_prediction, tracking_to_tracking_2d,\n            range_data_in_tracking_2d, &pose_estimate_);\n  odometry_correction_ = transform::Rigid3d::Identity();\n  if (!odometry_state_tracker_.empty()) {\n    // We add an odometry state, so that the correction from the scan matching\n    // is not removed by the next odometry data we get.\n    odometry_state_tracker_.AddOdometryState(\n        {time, odometry_state_tracker_.newest().odometer_pose,\n         odometry_state_tracker_.newest().state_pose *\n             odometry_prediction.inverse() * pose_estimate_});\n  }\n\n  // Improve the velocity estimate.\n  if (last_scan_match_time_ > common::Time::min() &&\n      time > last_scan_match_time_) {\n    const double delta_t = common::ToSeconds(time - last_scan_match_time_);\n    velocity_estimate_ += (pose_estimate_.translation().head<2>() -\n                           model_prediction.translation().head<2>()) /\n                          delta_t;\n  }\n  last_scan_match_time_ = time_;\n\n  // Remove the untracked z-component which floats around 0 in the UKF.\n  const auto translation = pose_estimate_.translation();\n  pose_estimate_ = transform::Rigid3d(\n      transform::Rigid3d::Vector(translation.x(), translation.y(), 0.),\n      pose_estimate_.rotation());\n\n  const transform::Rigid3d tracking_2d_to_map =\n      pose_estimate_ * tracking_to_tracking_2d.inverse();\n  last_pose_estimate_ = {\n      time, pose_estimate_,\n      sensor::TransformPointCloud(range_data_in_tracking_2d.returns,\n                                  tracking_2d_to_map.cast<float>())};\n\n  const transform::Rigid2d pose_estimate_2d =\n      transform::Project2D(tracking_2d_to_map);\n  if (motion_filter_.IsSimilar(time, transform::Embed3D(pose_estimate_2d))) {\n    return nullptr;\n  }\n\n  const mapping::Submap* const matching_submap =\n      submaps_.Get(submaps_.matching_index());\n  std::vector<const mapping::Submap*> insertion_submaps;\n  for (int insertion_index : submaps_.insertion_indices()) {\n    insertion_submaps.push_back(submaps_.Get(insertion_index));\n  }\n  submaps_.InsertRangeData(\n      TransformRangeData(range_data_in_tracking_2d,\n                         transform::Embed3D(pose_estimate_2d.cast<float>())));\n\n  return common::make_unique<InsertionResult>(InsertionResult{\n      time, matching_submap, insertion_submaps, tracking_to_tracking_2d,\n      range_data_in_tracking_2d, pose_estimate_2d});\n}\n\nconst mapping::GlobalTrajectoryBuilderInterface::PoseEstimate&\nLocalTrajectoryBuilder::pose_estimate() const {\n  return last_pose_estimate_;\n}\n\nvoid LocalTrajectoryBuilder::AddImuData(\n    const common::Time time, const Eigen::Vector3d& linear_acceleration,\n    const Eigen::Vector3d& angular_velocity) {\n  CHECK(options_.use_imu_data()) << \"An unexpected IMU packet was added.\";\n\n  InitializeImuTracker(time);\n  Predict(time);\n  imu_tracker_->AddImuLinearAccelerationObservation(linear_acceleration);\n  imu_tracker_->AddImuAngularVelocityObservation(angular_velocity);\n}\n\nvoid LocalTrajectoryBuilder::AddOdometerData(\n    const common::Time time, const transform::Rigid3d& odometer_pose) {\n  if (imu_tracker_ == nullptr) {\n    // Until we've initialized the IMU tracker we do not want to call Predict().\n    LOG(INFO) << \"ImuTracker not yet initialized.\";\n    return;\n  }\n\n  Predict(time);\n  if (!odometry_state_tracker_.empty()) {\n    const auto& previous_odometry_state = odometry_state_tracker_.newest();\n    const transform::Rigid3d delta =\n        previous_odometry_state.odometer_pose.inverse() * odometer_pose;\n    const transform::Rigid3d new_pose =\n        previous_odometry_state.state_pose * delta;\n    odometry_correction_ = pose_estimate_.inverse() * new_pose;\n  }\n  odometry_state_tracker_.AddOdometryState(\n      {time, odometer_pose, pose_estimate_ * odometry_correction_});\n}\n\nvoid LocalTrajectoryBuilder::InitializeImuTracker(const common::Time time) {\n  if (imu_tracker_ == nullptr) {\n    imu_tracker_ = common::make_unique<mapping::ImuTracker>(\n        options_.imu_gravity_time_constant(), time);\n  }\n}\n\nvoid LocalTrajectoryBuilder::Predict(const common::Time time) {\n  CHECK(imu_tracker_ != nullptr);\n  CHECK_LE(time_, time);\n  const double last_yaw = transform::GetYaw(imu_tracker_->orientation());\n  imu_tracker_->Advance(time);\n  if (time_ > common::Time::min()) {\n    const double delta_t = common::ToSeconds(time - time_);\n    // Constant velocity model.\n    const Eigen::Vector3d translation =\n        pose_estimate_.translation() +\n        delta_t *\n            Eigen::Vector3d(velocity_estimate_.x(), velocity_estimate_.y(), 0.);\n    // Use the current IMU tracker roll and pitch for gravity alignment, and\n    // apply its change in yaw.\n    const Eigen::Quaterniond rotation =\n        Eigen::AngleAxisd(\n            transform::GetYaw(pose_estimate_.rotation()) - last_yaw,\n            Eigen::Vector3d::UnitZ()) *\n        imu_tracker_->orientation();\n    pose_estimate_ = transform::Rigid3d(translation, rotation);\n  }\n  time_ = time;\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/local_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_H_\n\n#include <memory>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping/imu_tracker.h\"\n#include \"cartographer/mapping/odometry_state_tracker.h\"\n#include \"cartographer/mapping_2d/proto/local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_2d/submaps.h\"\n#include \"cartographer/mapping_3d/motion_filter.h\"\n#include \"cartographer/sensor/configuration.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\n// Wires up the local SLAM stack (i.e. UKF, scan matching, etc.) without loop\n// closure.\nclass LocalTrajectoryBuilder {\n public:\n  struct InsertionResult {\n    common::Time time;\n    const mapping::Submap* matching_submap;\n    std::vector<const mapping::Submap*> insertion_submaps;\n    transform::Rigid3d tracking_to_tracking_2d;\n    sensor::RangeData range_data_in_tracking_2d;\n    transform::Rigid2d pose_estimate_2d;\n  };\n\n  explicit LocalTrajectoryBuilder(\n      const proto::LocalTrajectoryBuilderOptions& options);\n  ~LocalTrajectoryBuilder();\n\n  LocalTrajectoryBuilder(const LocalTrajectoryBuilder&) = delete;\n  LocalTrajectoryBuilder& operator=(const LocalTrajectoryBuilder&) = delete;\n\n  const mapping::GlobalTrajectoryBuilderInterface::PoseEstimate& pose_estimate()\n      const;\n  std::unique_ptr<InsertionResult> AddHorizontalRangeData(\n      common::Time, const sensor::RangeData& range_data);\n  void AddImuData(common::Time time, const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity);\n  void AddOdometerData(common::Time time, const transform::Rigid3d& pose);\n\n  const Submaps* submaps() const;\n\n private:\n  sensor::RangeData TransformAndFilterRangeData(\n      const transform::Rigid3f& tracking_to_tracking_2d,\n      const sensor::RangeData& range_data) const;\n\n  // Scan matches 'range_data_in_tracking_2d' and fill in the 'pose_observation'\n  // with the result.\n  void ScanMatch(common::Time time, const transform::Rigid3d& pose_prediction,\n                 const transform::Rigid3d& tracking_to_tracking_2d,\n                 const sensor::RangeData& range_data_in_tracking_2d,\n                 transform::Rigid3d* pose_observation);\n\n  // Lazily constructs an ImuTracker.\n  void InitializeImuTracker(common::Time time);\n\n  // Updates the current estimate to reflect the given 'time'.\n  void Predict(common::Time time);\n\n  const proto::LocalTrajectoryBuilderOptions options_;\n  Submaps submaps_;\n  mapping::GlobalTrajectoryBuilderInterface::PoseEstimate last_pose_estimate_;\n\n  // Current 'pose_estimate_' and 'velocity_estimate_' at 'time_'.\n  common::Time time_ = common::Time::min();\n  transform::Rigid3d pose_estimate_ = transform::Rigid3d::Identity();\n  Eigen::Vector2d velocity_estimate_ = Eigen::Vector2d::Zero();\n  common::Time last_scan_match_time_ = common::Time::min();\n  // This is the difference between the model (constant velocity, IMU)\n  // prediction 'pose_estimate_' and the odometry prediction. To get the\n  // odometry prediction, right-multiply this to 'pose_estimate_'.\n  transform::Rigid3d odometry_correction_ = transform::Rigid3d::Identity();\n\n  mapping_3d::MotionFilter motion_filter_;\n  scan_matching::RealTimeCorrelativeScanMatcher\n      real_time_correlative_scan_matcher_;\n  scan_matching::CeresScanMatcher ceres_scan_matcher_;\n\n  std::unique_ptr<mapping::ImuTracker> imu_tracker_;\n  mapping::OdometryStateTracker odometry_state_tracker_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_2d/local_trajectory_builder_options.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/local_trajectory_builder_options.h\"\n\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_2d/submaps.h\"\n#include \"cartographer/mapping_3d/motion_filter.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nproto::LocalTrajectoryBuilderOptions CreateLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::LocalTrajectoryBuilderOptions options;\n  options.set_min_range(parameter_dictionary->GetDouble(\"min_range\"));\n  options.set_max_range(parameter_dictionary->GetDouble(\"max_range\"));\n  options.set_min_z(parameter_dictionary->GetDouble(\"min_z\"));\n  options.set_max_z(parameter_dictionary->GetDouble(\"max_z\"));\n  options.set_missing_data_ray_length(\n      parameter_dictionary->GetDouble(\"missing_data_ray_length\"));\n  options.set_voxel_filter_size(\n      parameter_dictionary->GetDouble(\"voxel_filter_size\"));\n  options.set_use_online_correlative_scan_matching(\n      parameter_dictionary->GetBool(\"use_online_correlative_scan_matching\"));\n  *options.mutable_adaptive_voxel_filter_options() =\n      sensor::CreateAdaptiveVoxelFilterOptions(\n          parameter_dictionary->GetDictionary(\"adaptive_voxel_filter\").get());\n  *options.mutable_real_time_correlative_scan_matcher_options() =\n      scan_matching::CreateRealTimeCorrelativeScanMatcherOptions(\n          parameter_dictionary\n              ->GetDictionary(\"real_time_correlative_scan_matcher\")\n              .get());\n  *options.mutable_ceres_scan_matcher_options() =\n      scan_matching::CreateCeresScanMatcherOptions(\n          parameter_dictionary->GetDictionary(\"ceres_scan_matcher\").get());\n  *options.mutable_motion_filter_options() =\n      mapping_3d::CreateMotionFilterOptions(\n          parameter_dictionary->GetDictionary(\"motion_filter\").get());\n  options.set_imu_gravity_time_constant(\n      parameter_dictionary->GetDouble(\"imu_gravity_time_constant\"));\n  options.set_num_odometry_states(\n      parameter_dictionary->GetNonNegativeInt(\"num_odometry_states\"));\n  CHECK_GT(options.num_odometry_states(), 0);\n  *options.mutable_submaps_options() = CreateSubmapsOptions(\n      parameter_dictionary->GetDictionary(\"submaps\").get());\n  options.set_use_imu_data(parameter_dictionary->GetBool(\"use_imu_data\"));\n  return options;\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/local_trajectory_builder_options.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n#define CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_2d/proto/local_trajectory_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nproto::LocalTrajectoryBuilderOptions CreateLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n"
  },
  {
    "path": "mapping_2d/map_limits.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_MAP_LIMITS_H_\n#define CARTOGRAPHER_MAPPING_2D_MAP_LIMITS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/mapping_2d/proto/map_limits.pb.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\n// Defines the limits of a grid map. This class must remain inlined for\n// performance reasons.\nclass MapLimits {\n public:\n  MapLimits(const double resolution, const Eigen::Vector2d& max,\n            const CellLimits& cell_limits)\n      : resolution_(resolution), max_(max), cell_limits_(cell_limits) {\n    CHECK_GT(resolution_, 0.);\n    CHECK_GT(cell_limits.num_x_cells, 0.);\n    CHECK_GT(cell_limits.num_y_cells, 0.);\n  }\n\n  explicit MapLimits(const proto::MapLimits& map_limits)\n      : resolution_(map_limits.resolution()),\n        max_(transform::ToEigen(map_limits.max())),\n        cell_limits_(map_limits.cell_limits()) {}\n\n  // Returns the cell size in meters. All cells are square and the resolution is\n  // the length of one side.\n  double resolution() const { return resolution_; }\n\n  // Returns the corner of the limits, i.e., all pixels have positions with\n  // smaller coordinates.\n  const Eigen::Vector2d& max() const { return max_; }\n\n  // Returns the limits of the grid in number of cells.\n  const CellLimits& cell_limits() const { return cell_limits_; }\n\n  // Returns the index of the cell containing the point ('x', 'y') which may be\n  // outside the map, i.e., negative or too large indices that will return\n  // false for Contains().\n  Eigen::Array2i GetXYIndexOfCellContainingPoint(const double x,\n                                                 const double y) const {\n    // Index values are row major and the top left has Eigen::Array2i::Zero()\n    // and contains (centered_max_x, centered_max_y). We need to flip and\n    // rotate.\n    return Eigen::Array2i(\n        common::RoundToInt((max_.y() - y) / resolution_ - 0.5),\n        common::RoundToInt((max_.x() - x) / resolution_ - 0.5));\n  }\n\n  // Returns true of the ProbabilityGrid contains 'xy_index'.\n  bool Contains(const Eigen::Array2i& xy_index) const {\n    return (Eigen::Array2i(0, 0) <= xy_index).all() &&\n           (xy_index <\n            Eigen::Array2i(cell_limits_.num_x_cells, cell_limits_.num_y_cells))\n               .all();\n  }\n\n private:\n  double resolution_;\n  Eigen::Vector2d max_;\n  CellLimits cell_limits_;\n};\n\ninline proto::MapLimits ToProto(const MapLimits& map_limits) {\n  proto::MapLimits result;\n  result.set_resolution(map_limits.resolution());\n  *result.mutable_max() = transform::ToProto(map_limits.max());\n  *result.mutable_cell_limits() = ToProto(map_limits.cell_limits());\n  return result;\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_MAP_LIMITS_H_\n"
  },
  {
    "path": "mapping_2d/map_limits_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/map_limits.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nTEST(MapLimitsTest, ToProto) {\n  const MapLimits limits(42., Eigen::Vector2d(3., 0.), CellLimits(2, 3));\n  const auto proto = ToProto(limits);\n  EXPECT_EQ(limits.resolution(), proto.resolution());\n  EXPECT_EQ(limits.max().x(), proto.max().x());\n  EXPECT_EQ(limits.max().y(), proto.max().y());\n  EXPECT_EQ(ToProto(limits.cell_limits()).DebugString(),\n            proto.cell_limits().DebugString());\n}\n\nTEST(MapLimitsTest, ProtoConstructor) {\n  proto::MapLimits limits;\n  limits.set_resolution(1.);\n  limits.mutable_max()->set_x(2.);\n  limits.mutable_max()->set_y(3.);\n  limits.mutable_cell_limits()->set_num_x_cells(4);\n  limits.mutable_cell_limits()->set_num_y_cells(5);\n\n  const MapLimits native(limits);\n  EXPECT_EQ(limits.resolution(), native.resolution());\n  EXPECT_EQ(limits.max().x(), native.max().x());\n  EXPECT_EQ(limits.max().y(), native.max().y());\n  EXPECT_EQ(limits.cell_limits().DebugString(),\n            ToProto(native.cell_limits()).DebugString());\n}\n\nTEST(MapLimitsTest, ConstructAndGet) {\n  const MapLimits limits(42., Eigen::Vector2d(3., 0.), CellLimits(2, 3));\n\n  const CellLimits& cell_limits = limits.cell_limits();\n  EXPECT_EQ(2, cell_limits.num_x_cells);\n  EXPECT_EQ(3, cell_limits.num_y_cells);\n\n  const Eigen::Vector2d& max = limits.max();\n  EXPECT_EQ(3., max.x());\n  EXPECT_EQ(0., max.y());\n\n  EXPECT_EQ(42., limits.resolution());\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/probability_grid.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_PROBABILITY_GRID_H_\n#define CARTOGRAPHER_MAPPING_2D_PROBABILITY_GRID_H_\n\n#include <algorithm>\n#include <cmath>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/probability_values.h\"\n#include \"cartographer/mapping_2d/map_limits.h\"\n#include \"cartographer/mapping_2d/proto/probability_grid.pb.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\n// Represents a 2D grid of probabilities.\nclass ProbabilityGrid {\n public:\n  explicit ProbabilityGrid(const MapLimits& limits)\n      : limits_(limits),\n        cells_(limits_.cell_limits().num_x_cells *\n                   limits_.cell_limits().num_y_cells,\n               mapping::kUnknownProbabilityValue),\n        max_x_(0),\n        max_y_(0),\n        min_x_(limits_.cell_limits().num_x_cells - 1),\n        min_y_(limits_.cell_limits().num_y_cells - 1) {}\n\n  explicit ProbabilityGrid(const proto::ProbabilityGrid& proto)\n      : limits_(proto.limits()),\n        cells_(),\n        update_indices_(proto.update_indices().begin(),\n                        proto.update_indices().end()),\n        max_x_(proto.max_x()),\n        max_y_(proto.max_y()),\n        min_x_(proto.min_x()),\n        min_y_(proto.min_y()) {\n    cells_.reserve(proto.cells_size());\n    for (const auto cell : proto.cells()) {\n      CHECK_LE(cell, std::numeric_limits<uint16>::max());\n      cells_.push_back(cell);\n    }\n  }\n\n  // Returns the limits of this ProbabilityGrid.\n  const MapLimits& limits() const { return limits_; }\n\n  // Starts the next update sequence.\n  void StartUpdate() {\n    while (!update_indices_.empty()) {\n      DCHECK_GE(cells_[update_indices_.back()], mapping::kUpdateMarker);\n      cells_[update_indices_.back()] -= mapping::kUpdateMarker;\n      update_indices_.pop_back();\n    }\n  }\n\n  // Sets the probability of the cell at 'xy_index' to the given 'probability'.\n  // Only allowed if the cell was unknown before.\n  void SetProbability(const Eigen::Array2i& xy_index, const float probability) {\n    uint16& cell = cells_[GetIndexOfCell(xy_index)];\n    CHECK_EQ(cell, mapping::kUnknownProbabilityValue);\n    cell = mapping::ProbabilityToValue(probability);\n    UpdateBounds(xy_index);\n  }\n\n  // Applies the 'odds' specified when calling ComputeLookupTableToApplyOdds()\n  // to the probability of the cell at 'xy_index' if the cell has not already\n  // been updated. Multiple updates of the same cell will be ignored until\n  // StartUpdate() is called. Returns true if the cell was updated.\n  //\n  // If this is the first call to ApplyOdds() for the specified cell, its value\n  // will be set to probability corresponding to 'odds'.\n  bool ApplyLookupTable(const Eigen::Array2i& xy_index,\n                        const std::vector<uint16>& table) {\n    DCHECK_EQ(table.size(), mapping::kUpdateMarker);\n    const int cell_index = GetIndexOfCell(xy_index);\n    uint16& cell = cells_[cell_index];\n    if (cell >= mapping::kUpdateMarker) {\n      return false;\n    }\n    update_indices_.push_back(cell_index);\n    cell = table[cell];\n    DCHECK_GE(cell, mapping::kUpdateMarker);\n    UpdateBounds(xy_index);\n    return true;\n  }\n\n  // Returns the probability of the cell with 'xy_index'.\n  float GetProbability(const Eigen::Array2i& xy_index) const {\n    if (limits_.Contains(xy_index)) {\n      return mapping::ValueToProbability(cells_[GetIndexOfCell(xy_index)]);\n    }\n    return mapping::kMinProbability;\n  }\n\n  // Returns the probability of the cell containing the point ('x', 'y').\n  float GetProbability(const double x, const double y) const {\n    return GetProbability(limits_.GetXYIndexOfCellContainingPoint(x, y));\n  }\n\n  // Returns true if the probability at the specified index is known.\n  bool IsKnown(const Eigen::Array2i& xy_index) const {\n    return limits_.Contains(xy_index) && cells_[GetIndexOfCell(xy_index)] !=\n                                             mapping::kUnknownProbabilityValue;\n  }\n\n  // Fills in 'offset' and 'limits' to define a subregion of that contains all\n  // known cells.\n  void ComputeCroppedLimits(Eigen::Array2i* const offset,\n                            CellLimits* const limits) const {\n    *offset = Eigen::Array2i(min_x_, min_y_);\n    *limits = CellLimits(std::max(max_x_, min_x_) - min_x_ + 1,\n                         std::max(max_y_, min_y_) - min_y_ + 1);\n  }\n\n  // Grows the map as necessary to include 'x' and 'y'. This changes the meaning\n  // of these coordinates going forward. This method must be called immediately\n  // after 'StartUpdate', before any calls to 'ApplyLookupTable'.\n  void GrowLimits(const double x, const double y) {\n    CHECK(update_indices_.empty());\n    while (!limits_.Contains(limits_.GetXYIndexOfCellContainingPoint(x, y))) {\n      const int x_offset = limits_.cell_limits().num_x_cells / 2;\n      const int y_offset = limits_.cell_limits().num_y_cells / 2;\n      const MapLimits new_limits(\n          limits_.resolution(),\n          limits_.max() +\n              limits_.resolution() * Eigen::Vector2d(y_offset, x_offset),\n          CellLimits(2 * limits_.cell_limits().num_x_cells,\n                     2 * limits_.cell_limits().num_y_cells));\n      const int stride = new_limits.cell_limits().num_x_cells;\n      const int offset = x_offset + stride * y_offset;\n      const int new_size = new_limits.cell_limits().num_x_cells *\n                           new_limits.cell_limits().num_y_cells;\n      std::vector<uint16> new_cells(new_size,\n                                    mapping::kUnknownProbabilityValue);\n      for (int i = 0; i < limits_.cell_limits().num_y_cells; ++i) {\n        for (int j = 0; j < limits_.cell_limits().num_x_cells; ++j) {\n          new_cells[offset + j + i * stride] =\n              cells_[j + i * limits_.cell_limits().num_x_cells];\n        }\n      }\n      cells_ = new_cells;\n      limits_ = new_limits;\n      min_x_ += x_offset;\n      min_y_ += y_offset;\n      max_x_ += x_offset;\n      max_y_ += y_offset;\n    }\n  }\n\n  proto::ProbabilityGrid ToProto() const {\n    proto::ProbabilityGrid result;\n    *result.mutable_limits() = cartographer::mapping_2d::ToProto(limits_);\n    result.mutable_cells()->Reserve(cells_.size());\n    for (const auto cell : cells_) {\n      result.mutable_cells()->Add(cell);\n    }\n    result.mutable_update_indices()->Reserve(update_indices_.size());\n    for (const auto update : update_indices_) {\n      result.mutable_update_indices()->Add(update);\n    }\n    result.set_max_x(max_x_);\n    result.set_max_y(max_y_);\n    result.set_min_x(min_x_);\n    result.set_min_y(min_y_);\n    return result;\n  }\n\n private:\n  // Returns the index of the cell at 'xy_index'.\n  int GetIndexOfCell(const Eigen::Array2i& xy_index) const {\n    CHECK(limits_.Contains(xy_index)) << xy_index;\n    return limits_.cell_limits().num_x_cells * xy_index.y() + xy_index.x();\n  }\n\n  void UpdateBounds(const Eigen::Array2i& xy_index) {\n    min_x_ = std::min(min_x_, xy_index.x());\n    min_y_ = std::min(min_y_, xy_index.y());\n    max_x_ = std::max(max_x_, xy_index.x());\n    max_y_ = std::max(max_y_, xy_index.y());\n  }\n\n  MapLimits limits_;\n  std::vector<uint16> cells_;  // Highest bit is update marker.\n  std::vector<int> update_indices_;\n\n  // Minimum and maximum cell coordinates of known cells to efficiently compute\n  // cropping limits.\n  int max_x_;\n  int max_y_;\n  int min_x_;\n  int min_y_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_PROBABILITY_GRID_H_\n"
  },
  {
    "path": "mapping_2d/probability_grid_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/probability_grid.h\"\n\n#include <random>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nTEST(ProbabilityGridTest, ProtoConstructor) {\n  proto::ProbabilityGrid proto;\n  const MapLimits limits(1., {2., 3.}, CellLimits(4., 5.));\n  *proto.mutable_limits() = ToProto(limits);\n  for (int i = 6; i < 12; ++i) {\n    proto.mutable_cells()->Add(static_cast<uint16>(i));\n  }\n  for (int i = 13; i < 18; ++i) {\n    proto.mutable_update_indices()->Add(i);\n  }\n  proto.set_max_x(19);\n  proto.set_max_y(20);\n  proto.set_min_x(21);\n  proto.set_min_y(22);\n\n  ProbabilityGrid grid(proto);\n  EXPECT_EQ(proto.limits().DebugString(), ToProto(grid.limits()).DebugString());\n\n  // TODO(macmason): Figure out how to test the contents of cells_,\n  // update_indices_, and {min, max}_{x, y}_ gracefully.\n}\n\nTEST(ProbabilityGridTest, ToProto) {\n  ProbabilityGrid probability_grid(\n      MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)));\n\n  const auto proto = probability_grid.ToProto();\n  EXPECT_EQ(ToProto(probability_grid.limits()).DebugString(),\n            proto.limits().DebugString());\n\n  // TODO(macmason): Figure out how to test the contents of cells_,\n  // update_indices_, and {min, max}_{x, y}_ gracefully.\n}\n\nTEST(ProbabilityGridTest, ApplyOdds) {\n  ProbabilityGrid probability_grid(\n      MapLimits(1., Eigen::Vector2d(1., 1.), CellLimits(2, 2)));\n  const MapLimits& limits = probability_grid.limits();\n\n  EXPECT_TRUE(limits.Contains(Eigen::Array2i(0, 0)));\n  EXPECT_TRUE(limits.Contains(Eigen::Array2i(0, 1)));\n  EXPECT_TRUE(limits.Contains(Eigen::Array2i(1, 0)));\n  EXPECT_TRUE(limits.Contains(Eigen::Array2i(1, 1)));\n  EXPECT_FALSE(probability_grid.IsKnown(Eigen::Array2i(0, 0)));\n  EXPECT_FALSE(probability_grid.IsKnown(Eigen::Array2i(0, 1)));\n  EXPECT_FALSE(probability_grid.IsKnown(Eigen::Array2i(1, 0)));\n  EXPECT_FALSE(probability_grid.IsKnown(Eigen::Array2i(1, 1)));\n\n  probability_grid.SetProbability(Eigen::Array2i(1, 0), 0.5);\n\n  probability_grid.StartUpdate();\n  probability_grid.ApplyLookupTable(\n      Eigen::Array2i(1, 0),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9)));\n  EXPECT_GT(probability_grid.GetProbability(Eigen::Array2i(1, 0)), 0.5);\n\n  probability_grid.StartUpdate();\n  probability_grid.SetProbability(Eigen::Array2i(0, 1), 0.5);\n\n  probability_grid.StartUpdate();\n  probability_grid.ApplyLookupTable(\n      Eigen::Array2i(0, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.1)));\n  EXPECT_LT(probability_grid.GetProbability(Eigen::Array2i(0, 1)), 0.5);\n\n  // Tests adding odds to an unknown cell.\n  probability_grid.StartUpdate();\n  probability_grid.ApplyLookupTable(\n      Eigen::Array2i(1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.42)));\n  EXPECT_NEAR(probability_grid.GetProbability(Eigen::Array2i(1, 1)), 0.42,\n              1e-4);\n\n  // Tests that further updates are ignored if StartUpdate() isn't called.\n  probability_grid.ApplyLookupTable(\n      Eigen::Array2i(1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9)));\n  EXPECT_NEAR(probability_grid.GetProbability(Eigen::Array2i(1, 1)), 0.42,\n              1e-4);\n  probability_grid.StartUpdate();\n  probability_grid.ApplyLookupTable(\n      Eigen::Array2i(1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9)));\n  EXPECT_GT(probability_grid.GetProbability(Eigen::Array2i(1, 1)), 0.42);\n}\n\nTEST(ProbabilityGridTest, GetProbability) {\n  ProbabilityGrid probability_grid(\n      MapLimits(1., Eigen::Vector2d(1., 2.), CellLimits(2, 2)));\n\n  const MapLimits& limits = probability_grid.limits();\n  EXPECT_EQ(1., limits.max().x());\n  EXPECT_EQ(2., limits.max().y());\n\n  const CellLimits& cell_limits = limits.cell_limits();\n  ASSERT_EQ(2, cell_limits.num_x_cells);\n  ASSERT_EQ(2, cell_limits.num_y_cells);\n\n  probability_grid.StartUpdate();\n  probability_grid.SetProbability(\n      limits.GetXYIndexOfCellContainingPoint(-0.5, 0.5),\n      mapping::kMaxProbability);\n  EXPECT_NEAR(probability_grid.GetProbability(-0.5, 0.5),\n              mapping::kMaxProbability, 1e-6);\n  for (const Eigen::Array2i& xy_index :\n       {limits.GetXYIndexOfCellContainingPoint(-0.5, 1.5),\n        limits.GetXYIndexOfCellContainingPoint(0.5, 0.5),\n        limits.GetXYIndexOfCellContainingPoint(0.5, 1.5)}) {\n    EXPECT_TRUE(limits.Contains(xy_index));\n    EXPECT_FALSE(probability_grid.IsKnown(xy_index));\n  }\n}\n\nTEST(ProbabilityGridTest, GetXYIndexOfCellContainingPoint) {\n  ProbabilityGrid probability_grid(\n      MapLimits(2., Eigen::Vector2d(8., 14.), CellLimits(14, 8)));\n\n  const MapLimits& limits = probability_grid.limits();\n  const CellLimits& cell_limits = limits.cell_limits();\n  ASSERT_EQ(14, cell_limits.num_x_cells);\n  ASSERT_EQ(8, cell_limits.num_y_cells);\n  EXPECT_TRUE(\n      (Eigen::Array2i(0, 0) == limits.GetXYIndexOfCellContainingPoint(7, 13))\n          .all());\n  EXPECT_TRUE(\n      (Eigen::Array2i(13, 0) == limits.GetXYIndexOfCellContainingPoint(7, -13))\n          .all());\n  EXPECT_TRUE(\n      (Eigen::Array2i(0, 7) == limits.GetXYIndexOfCellContainingPoint(-7, 13))\n          .all());\n  EXPECT_TRUE(\n      (Eigen::Array2i(13, 7) == limits.GetXYIndexOfCellContainingPoint(-7, -13))\n          .all());\n\n  // Check around the origin.\n  EXPECT_TRUE(\n      (Eigen::Array2i(6, 3) == limits.GetXYIndexOfCellContainingPoint(0.5, 0.5))\n          .all());\n  EXPECT_TRUE(\n      (Eigen::Array2i(6, 3) == limits.GetXYIndexOfCellContainingPoint(1.5, 1.5))\n          .all());\n  EXPECT_TRUE((Eigen::Array2i(7, 3) ==\n               limits.GetXYIndexOfCellContainingPoint(0.5, -0.5))\n                  .all());\n  EXPECT_TRUE((Eigen::Array2i(6, 4) ==\n               limits.GetXYIndexOfCellContainingPoint(-0.5, 0.5))\n                  .all());\n  EXPECT_TRUE((Eigen::Array2i(7, 4) ==\n               limits.GetXYIndexOfCellContainingPoint(-0.5, -0.5))\n                  .all());\n}\n\nTEST(ProbabilityGridTest, CorrectCropping) {\n  // Create a probability grid with random values.\n  std::mt19937 rng(42);\n  std::uniform_real_distribution<float> value_distribution(\n      mapping::kMinProbability, mapping::kMaxProbability);\n  ProbabilityGrid probability_grid(\n      MapLimits(0.05, Eigen::Vector2d(10., 10.), CellLimits(400, 400)));\n  probability_grid.StartUpdate();\n  for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(\n           Eigen::Array2i(100, 100), Eigen::Array2i(299, 299))) {\n    probability_grid.SetProbability(xy_index, value_distribution(rng));\n  }\n  Eigen::Array2i offset;\n  CellLimits limits;\n  probability_grid.ComputeCroppedLimits(&offset, &limits);\n  EXPECT_TRUE((offset == Eigen::Array2i(100, 100)).all());\n  EXPECT_EQ(limits.num_x_cells, 200);\n  EXPECT_EQ(limits.num_y_cells, 200);\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/proto/cell_limits.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.proto;\n\nmessage CellLimits {\n  optional int32 num_x_cells = 1;\n  optional int32 num_y_cells = 2;\n}\n"
  },
  {
    "path": "mapping_2d/proto/local_trajectory_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.proto;\n\nimport \"cartographer/mapping_3d/proto/motion_filter_options.proto\";\nimport \"cartographer/sensor/proto/adaptive_voxel_filter_options.proto\";\nimport \"cartographer/mapping_2d/proto/submaps_options.proto\";\nimport \"cartographer/mapping_2d/scan_matching/proto/ceres_scan_matcher_options.proto\";\nimport \"cartographer/mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.proto\";\n\nmessage LocalTrajectoryBuilderOptions {\n  // Rangefinder points outside these ranges will be dropped.\n  optional float min_range = 14;\n  optional float max_range = 15;\n  optional float min_z = 1;\n  optional float max_z = 2;\n\n  // Points beyond 'max_range' will be inserted with this length as empty space.\n  optional float missing_data_ray_length = 16;\n\n  // Voxel filter that gets applied to the range data immediately after\n  // cropping.\n  optional float voxel_filter_size = 3;\n\n  // Whether to solve the online scan matching first using the correlative scan\n  // matcher to generate a good starting point for Ceres.\n  optional bool use_online_correlative_scan_matching = 5;\n\n  // Voxel filter used to compute a sparser point cloud for matching.\n  optional sensor.proto.AdaptiveVoxelFilterOptions\n      adaptive_voxel_filter_options = 6;\n\n  optional scan_matching.proto.RealTimeCorrelativeScanMatcherOptions\n      real_time_correlative_scan_matcher_options = 7;\n  optional scan_matching.proto.CeresScanMatcherOptions\n      ceres_scan_matcher_options = 8;\n  optional mapping_3d.proto.MotionFilterOptions motion_filter_options = 13;\n\n  // Time constant in seconds for the orientation moving average based on\n  // observed gravity via the IMU. It should be chosen so that the error\n  // 1. from acceleration measurements not due to gravity (which gets worse when\n  // the constant is reduced) and\n  // 2. from integration of angular velocities (which gets worse when the\n  // constant is increased) is balanced.\n  optional double imu_gravity_time_constant = 17;\n\n  // Maximum number of previous odometry states to keep.\n  optional int32 num_odometry_states = 18;\n\n  optional mapping_2d.proto.SubmapsOptions submaps_options = 11;\n\n  // True if IMU data should be expected and used.\n  optional bool use_imu_data = 12;\n}\n"
  },
  {
    "path": "mapping_2d/proto/map_limits.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_2d/proto/cell_limits.proto\";\nimport \"cartographer/transform/proto/transform.proto\";\n\npackage cartographer.mapping_2d.proto;\n\nmessage MapLimits {\n  optional double resolution = 1;\n  optional cartographer.transform.proto.Vector2d max = 2;\n  optional CellLimits cell_limits = 3;\n}\n"
  },
  {
    "path": "mapping_2d/proto/probability_grid.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_2d/proto/map_limits.proto\";\n\npackage cartographer.mapping_2d.proto;\n\nmessage ProbabilityGrid {\n  optional MapLimits limits = 1;\n  // These values are actually int16s, but protos don't have a native int16\n  // type.\n  repeated int32 cells = 2;\n  repeated int32 update_indices = 3;\n  optional int32 max_x = 4;\n  optional int32 max_y = 5;\n  optional int32 min_x = 6;\n  optional int32 min_y = 7;\n}\n"
  },
  {
    "path": "mapping_2d/proto/range_data_inserter_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.proto;\n\nmessage RangeDataInserterOptions {\n  // Probability change for a hit (this will be converted to odds and therefore\n  // must be greater than 0.5).\n  optional double hit_probability = 1;\n\n  // Probability change for a miss (this will be converted to odds and therefore\n  // must be less than 0.5).\n  optional double miss_probability = 2;\n\n  // If 'false', free space will not change the probabilities in the occupancy\n  // grid.\n  optional bool insert_free_space = 3;\n}\n"
  },
  {
    "path": "mapping_2d/proto/submaps_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_2d/proto/range_data_inserter_options.proto\";\n\npackage cartographer.mapping_2d.proto;\n\nmessage SubmapsOptions {\n  // Resolution of the map in meters.\n  optional double resolution = 1;\n\n  // Half the width/height of each submap, its \"radius\".\n  optional double half_length = 2;\n\n  // Number of scans before adding a new submap. Each submap will get twice the\n  // number of scans inserted: First for initialization without being matched\n  // against, then while being matched.\n  optional int32 num_range_data = 3;\n\n  // If enabled, submap%d.png images are written for debugging.\n  optional bool output_debug_images = 4;\n\n  optional RangeDataInserterOptions range_data_inserter_options = 5;\n}\n"
  },
  {
    "path": "mapping_2d/range_data_inserter.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n\n#include <cstdlib>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/mapping_2d/ray_casting.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nproto::RangeDataInserterOptions CreateRangeDataInserterOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::RangeDataInserterOptions options;\n  options.set_hit_probability(\n      parameter_dictionary->GetDouble(\"hit_probability\"));\n  options.set_miss_probability(\n      parameter_dictionary->GetDouble(\"miss_probability\"));\n  options.set_insert_free_space(\n      parameter_dictionary->HasKey(\"insert_free_space\")\n          ? parameter_dictionary->GetBool(\"insert_free_space\")\n          : true);\n  CHECK_GT(options.hit_probability(), 0.5);\n  CHECK_LT(options.miss_probability(), 0.5);\n  return options;\n}\n\nRangeDataInserter::RangeDataInserter(\n    const proto::RangeDataInserterOptions& options)\n    : options_(options),\n      hit_table_(mapping::ComputeLookupTableToApplyOdds(\n          mapping::Odds(options.hit_probability()))),\n      miss_table_(mapping::ComputeLookupTableToApplyOdds(\n          mapping::Odds(options.miss_probability()))) {}\n\nvoid RangeDataInserter::Insert(const sensor::RangeData& range_data,\n                               ProbabilityGrid* const probability_grid) const {\n  CHECK_NOTNULL(probability_grid)->StartUpdate();\n\n  // By not starting a new update after hits are inserted, we give hits priority\n  // (i.e. no hits will be ignored because of a miss in the same cell).\n  CastRays(range_data, probability_grid->limits(),\n           [this, &probability_grid](const Eigen::Array2i& hit) {\n             probability_grid->ApplyLookupTable(hit, hit_table_);\n           },\n           [this, &probability_grid](const Eigen::Array2i& miss) {\n             if (options_.insert_free_space()) {\n               probability_grid->ApplyLookupTable(miss, miss_table_);\n             }\n           });\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/range_data_inserter.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_RANGE_DATA_INSERTER_H_\n#define CARTOGRAPHER_MAPPING_2D_RANGE_DATA_INSERTER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/proto/range_data_inserter_options.pb.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nproto::RangeDataInserterOptions CreateRangeDataInserterOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nclass RangeDataInserter {\n public:\n  explicit RangeDataInserter(const proto::RangeDataInserterOptions& options);\n\n  RangeDataInserter(const RangeDataInserter&) = delete;\n  RangeDataInserter& operator=(const RangeDataInserter&) = delete;\n\n  // Inserts 'range_data' into 'probability_grid'.\n  void Insert(const sensor::RangeData& range_data,\n              ProbabilityGrid* probability_grid) const;\n\n  const std::vector<uint16>& hit_table() const { return hit_table_; }\n  const std::vector<uint16>& miss_table() const { return miss_table_; }\n\n private:\n  const proto::RangeDataInserterOptions options_;\n  const std::vector<uint16> hit_table_;\n  const std::vector<uint16> miss_table_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_RANGE_DATA_INSERTER_H_\n"
  },
  {
    "path": "mapping_2d/range_data_inserter_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nclass RangeDataInserterTest : public ::testing::Test {\n protected:\n  RangeDataInserterTest()\n      : probability_grid_(\n            MapLimits(1., Eigen::Vector2d(1., 5.), CellLimits(5, 5))) {\n    auto parameter_dictionary = common::MakeDictionary(\n        \"return { \"\n        \"insert_free_space = true, \"\n        \"hit_probability = 0.7, \"\n        \"miss_probability = 0.4, \"\n        \"}\");\n    options_ = CreateRangeDataInserterOptions(parameter_dictionary.get());\n    range_data_inserter_ = common::make_unique<RangeDataInserter>(options_);\n  }\n\n  void InsertPointCloud() {\n    sensor::RangeData range_data;\n    range_data.returns.emplace_back(-3.5, 0.5, 0.f);\n    range_data.returns.emplace_back(-2.5, 1.5, 0.f);\n    range_data.returns.emplace_back(-1.5, 2.5, 0.f);\n    range_data.returns.emplace_back(-0.5, 3.5, 0.f);\n    range_data.origin.x() = -0.5;\n    range_data.origin.y() = 0.5;\n    probability_grid_.StartUpdate();\n    range_data_inserter_->Insert(range_data, &probability_grid_);\n  }\n\n  ProbabilityGrid probability_grid_;\n  std::unique_ptr<RangeDataInserter> range_data_inserter_;\n  proto::RangeDataInserterOptions options_;\n};\n\nTEST_F(RangeDataInserterTest, InsertPointCloud) {\n  InsertPointCloud();\n\n  EXPECT_NEAR(1., probability_grid_.limits().max().x(), 1e-9);\n  EXPECT_NEAR(5., probability_grid_.limits().max().y(), 1e-9);\n\n  const CellLimits& cell_limits = probability_grid_.limits().cell_limits();\n  EXPECT_EQ(5, cell_limits.num_x_cells);\n  EXPECT_EQ(5, cell_limits.num_y_cells);\n\n  enum class State { UNKNOWN, MISS, HIT };\n  State expected_states[5][5] = {\n      {State::UNKNOWN, State::UNKNOWN, State::UNKNOWN, State::UNKNOWN,\n       State::UNKNOWN},\n      {State::UNKNOWN, State::HIT, State::MISS, State::MISS, State::MISS},\n      {State::UNKNOWN, State::UNKNOWN, State::HIT, State::MISS, State::MISS},\n      {State::UNKNOWN, State::UNKNOWN, State::UNKNOWN, State::HIT, State::MISS},\n      {State::UNKNOWN, State::UNKNOWN, State::UNKNOWN, State::UNKNOWN,\n       State::HIT}};\n  for (int row = 0; row != 5; ++row) {\n    for (int column = 0; column != 5; ++column) {\n      Eigen::Array2i xy_index(row, column);\n      EXPECT_TRUE(probability_grid_.limits().Contains(xy_index));\n      switch (expected_states[column][row]) {\n        case State::UNKNOWN:\n          EXPECT_FALSE(probability_grid_.IsKnown(xy_index));\n          break;\n        case State::MISS:\n          EXPECT_NEAR(options_.miss_probability(),\n                      probability_grid_.GetProbability(xy_index), 1e-4);\n          break;\n        case State::HIT:\n          EXPECT_NEAR(options_.hit_probability(),\n                      probability_grid_.GetProbability(xy_index), 1e-4);\n          break;\n      }\n    }\n  }\n}\n\nTEST_F(RangeDataInserterTest, ProbabilityProgression) {\n  InsertPointCloud();\n  EXPECT_NEAR(options_.hit_probability(),\n              probability_grid_.GetProbability(-3.5, 0.5), 1e-4);\n  EXPECT_NEAR(options_.miss_probability(),\n              probability_grid_.GetProbability(-2.5, 0.5), 1e-4);\n\n  for (int i = 0; i < 1000; ++i) {\n    InsertPointCloud();\n  }\n  EXPECT_NEAR(mapping::kMaxProbability,\n              probability_grid_.GetProbability(-3.5, 0.5), 1e-3);\n  EXPECT_NEAR(mapping::kMinProbability,\n              probability_grid_.GetProbability(-2.5, 0.5), 1e-3);\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/ray_casting.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/ray_casting.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nnamespace {\n\n// Factor for subpixel accuracy of start and end point.\nconstexpr int kSubpixelScale = 1000;\n\n// We divide each pixel in kSubpixelScale x kSubpixelScale subpixels. 'begin'\n// and 'end' are coordinates at subpixel precision. We compute all pixels in\n// which some part of the line segment connecting 'begin' and 'end' lies.\nvoid CastRay(const Eigen::Array2i& begin, const Eigen::Array2i& end,\n             const std::function<void(const Eigen::Array2i&)>& visitor) {\n  // For simplicity, we order 'begin' and 'end' by their x coordinate.\n  if (begin.x() > end.x()) {\n    CastRay(end, begin, visitor);\n    return;\n  }\n\n  CHECK_GE(begin.x(), 0);\n  CHECK_GE(begin.y(), 0);\n  CHECK_GE(end.y(), 0);\n\n  // Special case: We have to draw a vertical line in full pixels, as 'begin'\n  // and 'end' have the same full pixel x coordinate.\n  if (begin.x() / kSubpixelScale == end.x() / kSubpixelScale) {\n    Eigen::Array2i current(begin.x() / kSubpixelScale,\n                           std::min(begin.y(), end.y()) / kSubpixelScale);\n    const int end_y = std::max(begin.y(), end.y()) / kSubpixelScale;\n    for (; current.y() <= end_y; ++current.y()) {\n      visitor(current);\n    }\n    return;\n  }\n\n  const int64 dx = end.x() - begin.x();\n  const int64 dy = end.y() - begin.y();\n  const int64 denominator = 2 * kSubpixelScale * dx;\n\n  // The current full pixel coordinates. We begin at 'begin'.\n  Eigen::Array2i current = begin / kSubpixelScale;\n\n  // To represent subpixel centers, we use a factor of 2 * 'kSubpixelScale' in\n  // the denominator.\n  // +-+-+-+ -- 1 = (2 * kSubpixelScale) / (2 * kSubpixelScale)\n  // | | | |\n  // +-+-+-+\n  // | | | |\n  // +-+-+-+ -- top edge of first subpixel = 2 / (2 * kSubpixelScale)\n  // | | | | -- center of first subpixel = 1 / (2 * kSubpixelScale)\n  // +-+-+-+ -- 0 = 0 / (2 * kSubpixelScale)\n\n  // The center of the subpixel part of 'begin.y()' assuming the\n  // 'denominator', i.e., sub_y / denominator is in (0, 1).\n  int64 sub_y = (2 * (begin.y() % kSubpixelScale) + 1) * dx;\n\n  // The distance from the from 'begin' to the right pixel border, to be divided\n  // by 2 * 'kSubpixelScale'.\n  const int first_pixel =\n      2 * kSubpixelScale - 2 * (begin.x() % kSubpixelScale) - 1;\n  // The same from the left pixel border to 'end'.\n  const int last_pixel = 2 * (end.x() % kSubpixelScale) + 1;\n\n  // The full pixel x coordinate of 'end'.\n  const int end_x = std::max(begin.x(), end.x()) / kSubpixelScale;\n\n  // Move from 'begin' to the next pixel border to the right.\n  sub_y += dy * first_pixel;\n  if (dy > 0) {\n    while (true) {\n      visitor(current);\n      while (sub_y > denominator) {\n        sub_y -= denominator;\n        ++current.y();\n        visitor(current);\n      }\n      ++current.x();\n      if (sub_y == denominator) {\n        sub_y -= denominator;\n        ++current.y();\n      }\n      if (current.x() == end_x) {\n        break;\n      }\n      // Move from one pixel border to the next.\n      sub_y += dy * 2 * kSubpixelScale;\n    }\n    // Move from the pixel border on the right to 'end'.\n    sub_y += dy * last_pixel;\n    visitor(current);\n    while (sub_y > denominator) {\n      sub_y -= denominator;\n      ++current.y();\n      visitor(current);\n    }\n    CHECK_NE(sub_y, denominator);\n    CHECK_EQ(current.y(), end.y() / kSubpixelScale);\n    return;\n  }\n\n  // Same for lines non-ascending in y coordinates.\n  while (true) {\n    visitor(current);\n    while (sub_y < 0) {\n      sub_y += denominator;\n      --current.y();\n      visitor(current);\n    }\n    ++current.x();\n    if (sub_y == 0) {\n      sub_y += denominator;\n      --current.y();\n    }\n    if (current.x() == end_x) {\n      break;\n    }\n    sub_y += dy * 2 * kSubpixelScale;\n  }\n  sub_y += dy * last_pixel;\n  visitor(current);\n  while (sub_y < 0) {\n    sub_y += denominator;\n    --current.y();\n    visitor(current);\n  }\n  CHECK_NE(sub_y, 0);\n  CHECK_EQ(current.y(), end.y() / kSubpixelScale);\n}\n\n}  // namespace\n\nvoid CastRays(const sensor::RangeData& range_data, const MapLimits& limits,\n              const std::function<void(const Eigen::Array2i&)>& hit_visitor,\n              const std::function<void(const Eigen::Array2i&)>& miss_visitor) {\n  const double superscaled_resolution = limits.resolution() / kSubpixelScale;\n  const MapLimits superscaled_limits(\n      superscaled_resolution, limits.max(),\n      CellLimits(limits.cell_limits().num_x_cells * kSubpixelScale,\n                 limits.cell_limits().num_y_cells * kSubpixelScale));\n  const Eigen::Array2i begin =\n      superscaled_limits.GetXYIndexOfCellContainingPoint(range_data.origin.x(),\n                                                         range_data.origin.y());\n\n  // Compute and add the end points.\n  std::vector<Eigen::Array2i> ends;\n  ends.reserve(range_data.returns.size());\n  for (const Eigen::Vector3f& hit : range_data.returns) {\n    ends.push_back(\n        superscaled_limits.GetXYIndexOfCellContainingPoint(hit.x(), hit.y()));\n    hit_visitor(ends.back() / kSubpixelScale);\n  }\n\n  // Now add the misses.\n  for (const Eigen::Array2i& end : ends) {\n    CastRay(begin, end, miss_visitor);\n  }\n\n  // Finally, compute and add empty rays based on misses in the scan.\n  for (const Eigen::Vector3f& missing_echo : range_data.misses) {\n    CastRay(begin,\n            superscaled_limits.GetXYIndexOfCellContainingPoint(\n                missing_echo.x(), missing_echo.y()),\n            miss_visitor);\n  }\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/ray_casting.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_RAY_CASTING_H_\n#define CARTOGRAPHER_MAPPING_2D_RAY_CASTING_H_\n\n#include <functional>\n\n#include \"cartographer/mapping_2d/map_limits.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\n// For each ray in 'range_data', calls 'hit_visitor' and 'miss_visitor' on the\n// appropriate cells. Hits are handled before misses.\nvoid CastRays(const sensor::RangeData& range_data, const MapLimits& limits,\n              const std::function<void(const Eigen::Array2i&)>& hit_visitor,\n              const std::function<void(const Eigen::Array2i&)>& miss_visitor);\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_RAY_CASTING_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/ceres_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/ceres_solver_options.h\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/scan_matching/occupied_space_cost_functor.h\"\n#include \"cartographer/mapping_2d/scan_matching/rotation_delta_cost_functor.h\"\n#include \"cartographer/mapping_2d/scan_matching/translation_delta_cost_functor.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nproto::CeresScanMatcherOptions CreateCeresScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::CeresScanMatcherOptions options;\n  options.set_occupied_space_weight(\n      parameter_dictionary->GetDouble(\"occupied_space_weight\"));\n  options.set_translation_weight(\n      parameter_dictionary->GetDouble(\"translation_weight\"));\n  options.set_rotation_weight(\n      parameter_dictionary->GetDouble(\"rotation_weight\"));\n  *options.mutable_ceres_solver_options() =\n      common::CreateCeresSolverOptionsProto(\n          parameter_dictionary->GetDictionary(\"ceres_solver_options\").get());\n  return options;\n}\n\nCeresScanMatcher::CeresScanMatcher(\n    const proto::CeresScanMatcherOptions& options)\n    : options_(options),\n      ceres_solver_options_(\n          common::CreateCeresSolverOptions(options.ceres_solver_options())) {\n  ceres_solver_options_.linear_solver_type = ceres::DENSE_QR;\n}\n\nCeresScanMatcher::~CeresScanMatcher() {}\n\nvoid CeresScanMatcher::Match(const transform::Rigid2d& previous_pose,\n                             const transform::Rigid2d& initial_pose_estimate,\n                             const sensor::PointCloud& point_cloud,\n                             const ProbabilityGrid& probability_grid,\n                             transform::Rigid2d* const pose_estimate,\n                             ceres::Solver::Summary* const summary) const {\n  double ceres_pose_estimate[3] = {initial_pose_estimate.translation().x(),\n                                   initial_pose_estimate.translation().y(),\n                                   initial_pose_estimate.rotation().angle()};\n  ceres::Problem problem;\n  CHECK_GT(options_.occupied_space_weight(), 0.);\n  problem.AddResidualBlock(\n      new ceres::AutoDiffCostFunction<OccupiedSpaceCostFunctor, ceres::DYNAMIC,\n                                      3>(\n          new OccupiedSpaceCostFunctor(\n              options_.occupied_space_weight() /\n                  std::sqrt(static_cast<double>(point_cloud.size())),\n              point_cloud, probability_grid),\n          point_cloud.size()),\n      nullptr, ceres_pose_estimate);\n  CHECK_GT(options_.translation_weight(), 0.);\n  problem.AddResidualBlock(\n      new ceres::AutoDiffCostFunction<TranslationDeltaCostFunctor, 2, 3>(\n          new TranslationDeltaCostFunctor(options_.translation_weight(),\n                                          previous_pose)),\n      nullptr, ceres_pose_estimate);\n  CHECK_GT(options_.rotation_weight(), 0.);\n  problem.AddResidualBlock(\n      new ceres::AutoDiffCostFunction<RotationDeltaCostFunctor, 1, 3>(\n          new RotationDeltaCostFunctor(options_.rotation_weight(),\n                                       ceres_pose_estimate[2])),\n      nullptr, ceres_pose_estimate);\n\n  ceres::Solve(ceres_solver_options_, &problem, summary);\n\n  *pose_estimate = transform::Rigid2d(\n      {ceres_pose_estimate[0], ceres_pose_estimate[1]}, ceres_pose_estimate[2]);\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/ceres_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"ceres/ceres.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nproto::CeresScanMatcherOptions CreateCeresScanMatcherOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n// Align scans with an existing map using Ceres.\nclass CeresScanMatcher {\n public:\n  explicit CeresScanMatcher(const proto::CeresScanMatcherOptions& options);\n  virtual ~CeresScanMatcher();\n\n  CeresScanMatcher(const CeresScanMatcher&) = delete;\n  CeresScanMatcher& operator=(const CeresScanMatcher&) = delete;\n\n  // Aligns 'point_cloud' within the 'probability_grid' given an\n  // 'initial_pose_estimate' and returns a 'pose_estimate' and the solver\n  // 'summary'.\n  void Match(const transform::Rigid2d& previous_pose,\n             const transform::Rigid2d& initial_pose_estimate,\n             const sensor::PointCloud& point_cloud,\n             const ProbabilityGrid& probability_grid,\n             transform::Rigid2d* pose_estimate,\n             ceres::Solver::Summary* summary) const;\n\n private:\n  const proto::CeresScanMatcherOptions options_;\n  ceres::Solver::Options ceres_solver_options_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/ceres_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\nnamespace {\n\nclass CeresScanMatcherTest : public ::testing::Test {\n protected:\n  CeresScanMatcherTest()\n      : probability_grid_(\n            MapLimits(1., Eigen::Vector2d(10., 10.), CellLimits(20, 20))) {\n    probability_grid_.SetProbability(\n        probability_grid_.limits().GetXYIndexOfCellContainingPoint(-3.5, 2.5),\n        mapping::kMaxProbability);\n\n    point_cloud_.emplace_back(-3.f, 2.f, 0.f);\n\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n          occupied_space_weight = 1.,\n          translation_weight = 0.1,\n          rotation_weight = 1.5,\n          ceres_solver_options = {\n            use_nonmonotonic_steps = true,\n            max_num_iterations = 50,\n            num_threads = 1,\n          },\n        })text\");\n    const proto::CeresScanMatcherOptions options =\n        CreateCeresScanMatcherOptions(parameter_dictionary.get());\n    ceres_scan_matcher_ = common::make_unique<CeresScanMatcher>(options);\n  }\n\n  void TestFromInitialPose(const transform::Rigid2d& initial_pose) {\n    transform::Rigid2d pose;\n    const transform::Rigid2d expected_pose =\n        transform::Rigid2d::Translation({-0.5, 0.5});\n    ceres::Solver::Summary summary;\n    ceres_scan_matcher_->Match(initial_pose, initial_pose, point_cloud_,\n                               probability_grid_, &pose, &summary);\n    EXPECT_NEAR(0., summary.final_cost, 1e-2) << summary.FullReport();\n    EXPECT_THAT(pose, transform::IsNearly(expected_pose, 1e-2))\n        << \"Actual: \" << transform::ToProto(pose).DebugString()\n        << \"\\nExpected: \" << transform::ToProto(expected_pose).DebugString();\n  }\n\n  ProbabilityGrid probability_grid_;\n  sensor::PointCloud point_cloud_;\n  std::unique_ptr<CeresScanMatcher> ceres_scan_matcher_;\n};\n\nTEST_F(CeresScanMatcherTest, testPerfectEstimate) {\n  TestFromInitialPose(transform::Rigid2d::Translation({-0.5, 0.5}));\n}\n\nTEST_F(CeresScanMatcherTest, testOptimizeAlongX) {\n  TestFromInitialPose(transform::Rigid2d::Translation({-0.3, 0.5}));\n}\n\nTEST_F(CeresScanMatcherTest, testOptimizeAlongY) {\n  TestFromInitialPose(transform::Rigid2d::Translation({-0.45, 0.3}));\n}\n\nTEST_F(CeresScanMatcherTest, testOptimizeAlongXY) {\n  TestFromInitialPose(transform::Rigid2d::Translation({-0.3, 0.3}));\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/correlative_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/correlative_scan_matcher.h\"\n\n#include <cmath>\n\n#include \"cartographer/common/math.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nSearchParameters::SearchParameters(const double linear_search_window,\n                                   const double angular_search_window,\n                                   const sensor::PointCloud& point_cloud,\n                                   const double resolution)\n    : resolution(resolution) {\n  // We set this value to something on the order of resolution to make sure that\n  // the std::acos() below is defined.\n  float max_scan_range = 3.f * resolution;\n  for (const Eigen::Vector3f& point : point_cloud) {\n    const float range = point.head<2>().norm();\n    max_scan_range = std::max(range, max_scan_range);\n  }\n  const double kSafetyMargin = 1. - 1e-3;\n  angular_perturbation_step_size =\n      kSafetyMargin * std::acos(1. - common::Pow2(resolution) /\n                                         (2. * common::Pow2(max_scan_range)));\n  num_angular_perturbations =\n      std::ceil(angular_search_window / angular_perturbation_step_size);\n  num_scans = 2 * num_angular_perturbations + 1;\n\n  const int num_linear_perturbations =\n      std::ceil(linear_search_window / resolution);\n  linear_bounds.reserve(num_scans);\n  for (int i = 0; i != num_scans; ++i) {\n    linear_bounds.push_back(\n        LinearBounds{-num_linear_perturbations, num_linear_perturbations,\n                     -num_linear_perturbations, num_linear_perturbations});\n  }\n}\n\nSearchParameters::SearchParameters(const int num_linear_perturbations,\n                                   const int num_angular_perturbations,\n                                   const double angular_perturbation_step_size,\n                                   const double resolution)\n    : num_angular_perturbations(num_angular_perturbations),\n      angular_perturbation_step_size(angular_perturbation_step_size),\n      resolution(resolution),\n      num_scans(2 * num_angular_perturbations + 1) {\n  linear_bounds.reserve(num_scans);\n  for (int i = 0; i != num_scans; ++i) {\n    linear_bounds.push_back(\n        LinearBounds{-num_linear_perturbations, num_linear_perturbations,\n                     -num_linear_perturbations, num_linear_perturbations});\n  }\n}\n\nvoid SearchParameters::ShrinkToFit(const std::vector<DiscreteScan>& scans,\n                                   const CellLimits& cell_limits) {\n  CHECK_EQ(scans.size(), num_scans);\n  CHECK_EQ(linear_bounds.size(), num_scans);\n  for (int i = 0; i != num_scans; ++i) {\n    Eigen::Array2i min_bound = Eigen::Array2i::Zero();\n    Eigen::Array2i max_bound = Eigen::Array2i::Zero();\n    for (const Eigen::Array2i& xy_index : scans[i]) {\n      min_bound = min_bound.min(-xy_index);\n      max_bound = max_bound.max(Eigen::Array2i(cell_limits.num_x_cells - 1,\n                                               cell_limits.num_y_cells - 1) -\n                                xy_index);\n    }\n    linear_bounds[i].min_x = std::max(linear_bounds[i].min_x, min_bound.x());\n    linear_bounds[i].max_x = std::min(linear_bounds[i].max_x, max_bound.x());\n    linear_bounds[i].min_y = std::max(linear_bounds[i].min_y, min_bound.y());\n    linear_bounds[i].max_y = std::min(linear_bounds[i].max_y, max_bound.y());\n  }\n}\n\nstd::vector<sensor::PointCloud> GenerateRotatedScans(\n    const sensor::PointCloud& point_cloud,\n    const SearchParameters& search_parameters) {\n  std::vector<sensor::PointCloud> rotated_scans;\n  rotated_scans.reserve(search_parameters.num_scans);\n\n  double delta_theta = -search_parameters.num_angular_perturbations *\n                       search_parameters.angular_perturbation_step_size;\n  for (int scan_index = 0; scan_index < search_parameters.num_scans;\n       ++scan_index,\n           delta_theta += search_parameters.angular_perturbation_step_size) {\n    rotated_scans.push_back(sensor::TransformPointCloud(\n        point_cloud, transform::Rigid3f::Rotation(Eigen::AngleAxisf(\n                         delta_theta, Eigen::Vector3f::UnitZ()))));\n  }\n  return rotated_scans;\n}\n\nstd::vector<DiscreteScan> DiscretizeScans(\n    const MapLimits& map_limits, const std::vector<sensor::PointCloud>& scans,\n    const Eigen::Translation2f& initial_translation) {\n  std::vector<DiscreteScan> discrete_scans;\n  discrete_scans.reserve(scans.size());\n  for (const sensor::PointCloud& scan : scans) {\n    discrete_scans.emplace_back();\n    discrete_scans.back().reserve(scan.size());\n    for (const Eigen::Vector3f& point : scan) {\n      const Eigen::Vector2f translated_point =\n          Eigen::Affine2f(initial_translation) * point.head<2>();\n      discrete_scans.back().push_back(\n          map_limits.GetXYIndexOfCellContainingPoint(translated_point.x(),\n                                                     translated_point.y()));\n    }\n  }\n  return discrete_scans;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/correlative_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CORRELATIVE_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CORRELATIVE_SCAN_MATCHER_H_\n\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_2d/map_limits.h\"\n#include \"cartographer/mapping_2d/xy_index.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\ntypedef std::vector<Eigen::Array2i> DiscreteScan;\n\n// Describes the search space.\nstruct SearchParameters {\n  // Linear search window in pixel offsets; bounds are inclusive.\n  struct LinearBounds {\n    int min_x;\n    int max_x;\n    int min_y;\n    int max_y;\n  };\n\n  SearchParameters(double linear_search_window, double angular_search_window,\n                   const sensor::PointCloud& point_cloud, double resolution);\n\n  // For testing.\n  SearchParameters(int num_linear_perturbations, int num_angular_perturbations,\n                   double angular_perturbation_step_size, double resolution);\n\n  // Tightens the search window as much as possible.\n  void ShrinkToFit(const std::vector<DiscreteScan>& scans,\n                   const CellLimits& cell_limits);\n\n  int num_angular_perturbations;\n  double angular_perturbation_step_size;\n  double resolution;\n  int num_scans;\n  std::vector<LinearBounds> linear_bounds;  // Per rotated scans.\n};\n\n// Generates a collection of rotated scans.\nstd::vector<sensor::PointCloud> GenerateRotatedScans(\n    const sensor::PointCloud& point_cloud,\n    const SearchParameters& search_parameters);\n\n// Translates and discretizes the rotated scans into a vector of integer\n// indices.\nstd::vector<DiscreteScan> DiscretizeScans(\n    const MapLimits& map_limits, const std::vector<sensor::PointCloud>& scans,\n    const Eigen::Translation2f& initial_translation);\n\n// A possible solution.\nstruct Candidate {\n  Candidate(const int init_scan_index, const int init_x_index_offset,\n            const int init_y_index_offset,\n            const SearchParameters& search_parameters)\n      : scan_index(init_scan_index),\n        x_index_offset(init_x_index_offset),\n        y_index_offset(init_y_index_offset),\n        x(-y_index_offset * search_parameters.resolution),\n        y(-x_index_offset * search_parameters.resolution),\n        orientation((scan_index - search_parameters.num_angular_perturbations) *\n                    search_parameters.angular_perturbation_step_size) {}\n\n  // Index into the rotated scans vector.\n  int scan_index = 0;\n\n  // Linear offset from the initial pose.\n  int x_index_offset = 0;\n  int y_index_offset = 0;\n\n  // Pose of this Candidate relative to the initial pose.\n  double x = 0.;\n  double y = 0.;\n  double orientation = 0.;\n\n  // Score, higher is better.\n  float score = 0.f;\n\n  bool operator<(const Candidate& other) const { return score < other.score; }\n  bool operator>(const Candidate& other) const { return score > other.score; }\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_CORRELATIVE_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/correlative_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/correlative_scan_matcher.h\"\n\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\nnamespace {\n\nTEST(SearchParameters, Construction) {\n  const SearchParameters search_parameters(4, 5, 0.03, 0.05);\n  EXPECT_EQ(5, search_parameters.num_angular_perturbations);\n  EXPECT_NEAR(0.03, search_parameters.angular_perturbation_step_size, 1e-9);\n  EXPECT_NEAR(0.05, search_parameters.resolution, 1e-9);\n  EXPECT_EQ(11, search_parameters.num_scans);\n  EXPECT_EQ(11, search_parameters.linear_bounds.size());\n  for (const SearchParameters::LinearBounds linear_bounds :\n       search_parameters.linear_bounds) {\n    EXPECT_EQ(-4, linear_bounds.min_x);\n    EXPECT_EQ(4, linear_bounds.max_x);\n    EXPECT_EQ(-4, linear_bounds.min_y);\n    EXPECT_EQ(4, linear_bounds.max_y);\n  }\n}\n\nTEST(Candidate, Construction) {\n  const SearchParameters search_parameters(4, 5, 0.03, 0.05);\n  const Candidate candidate(3, 4, -5, search_parameters);\n  EXPECT_EQ(3, candidate.scan_index);\n  EXPECT_EQ(4, candidate.x_index_offset);\n  EXPECT_EQ(-5, candidate.y_index_offset);\n  EXPECT_NEAR(0.25, candidate.x, 1e-9);\n  EXPECT_NEAR(-0.2, candidate.y, 1e-9);\n  EXPECT_NEAR(-0.06, candidate.orientation, 1e-9);\n  EXPECT_NEAR(0., candidate.score, 1e-9);\n\n  Candidate bigger_candidate(3, 4, 5, search_parameters);\n  bigger_candidate.score = 1.;\n  EXPECT_LT(candidate, bigger_candidate);\n}\n\nTEST(GenerateRotatedScans, GenerateRotatedScans) {\n  sensor::PointCloud point_cloud;\n  point_cloud.emplace_back(-1.f, 1.f, 0.f);\n  const std::vector<sensor::PointCloud> scans =\n      GenerateRotatedScans(point_cloud, SearchParameters(0, 1, M_PI / 2., 0.));\n  EXPECT_EQ(3, scans.size());\n  EXPECT_NEAR(1., scans[0][0].x(), 1e-6);\n  EXPECT_NEAR(1., scans[0][0].y(), 1e-6);\n  EXPECT_NEAR(-1., scans[1][0].x(), 1e-6);\n  EXPECT_NEAR(1., scans[1][0].y(), 1e-6);\n  EXPECT_NEAR(-1., scans[2][0].x(), 1e-6);\n  EXPECT_NEAR(-1., scans[2][0].y(), 1e-6);\n}\n\nTEST(DiscretizeScans, DiscretizeScans) {\n  sensor::PointCloud point_cloud;\n  point_cloud.emplace_back(0.025f, 0.175f, 0.f);\n  point_cloud.emplace_back(-0.025f, 0.175f, 0.f);\n  point_cloud.emplace_back(-0.075f, 0.175f, 0.f);\n  point_cloud.emplace_back(-0.125f, 0.175f, 0.f);\n  point_cloud.emplace_back(-0.125f, 0.125f, 0.f);\n  point_cloud.emplace_back(-0.125f, 0.075f, 0.f);\n  point_cloud.emplace_back(-0.125f, 0.025f, 0.f);\n  const MapLimits map_limits(0.05, Eigen::Vector2d(0.05, 0.25),\n                             CellLimits(6, 6));\n  const std::vector<sensor::PointCloud> scans =\n      GenerateRotatedScans(point_cloud, SearchParameters(0, 0, 0., 0.));\n  const std::vector<DiscreteScan> discrete_scans =\n      DiscretizeScans(map_limits, scans, Eigen::Translation2f::Identity());\n  EXPECT_EQ(1, discrete_scans.size());\n  EXPECT_EQ(7, discrete_scans[0].size());\n  EXPECT_TRUE((Eigen::Array2i(1, 0) == discrete_scans[0][0]).all());\n  EXPECT_TRUE((Eigen::Array2i(1, 1) == discrete_scans[0][1]).all());\n  EXPECT_TRUE((Eigen::Array2i(1, 2) == discrete_scans[0][2]).all());\n  EXPECT_TRUE((Eigen::Array2i(1, 3) == discrete_scans[0][3]).all());\n  EXPECT_TRUE((Eigen::Array2i(2, 3) == discrete_scans[0][4]).all());\n  EXPECT_TRUE((Eigen::Array2i(3, 3) == discrete_scans[0][5]).all());\n  EXPECT_TRUE((Eigen::Array2i(4, 3) == discrete_scans[0][6]).all());\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/fast_correlative_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <deque>\n#include <functional>\n#include <limits>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nnamespace {\n\n// A collection of values which can be added and later removed, and the maximum\n// of the current values in the collection can be retrieved.\n// All of it in (amortized) O(1).\nclass SlidingWindowMaximum {\n public:\n  void AddValue(const float value) {\n    while (!non_ascending_maxima_.empty() &&\n           value > non_ascending_maxima_.back()) {\n      non_ascending_maxima_.pop_back();\n    }\n    non_ascending_maxima_.push_back(value);\n  }\n\n  void RemoveValue(const float value) {\n    // DCHECK for performance, since this is done for every value in the\n    // precomputation grid.\n    DCHECK(!non_ascending_maxima_.empty());\n    DCHECK_LE(value, non_ascending_maxima_.front());\n    if (value == non_ascending_maxima_.front()) {\n      non_ascending_maxima_.pop_front();\n    }\n  }\n\n  float GetMaximum() const {\n    // DCHECK for performance, since this is done for every value in the\n    // precomputation grid.\n    DCHECK_GT(non_ascending_maxima_.size(), 0);\n    return non_ascending_maxima_.front();\n  }\n\n  void CheckIsEmpty() const { CHECK_EQ(non_ascending_maxima_.size(), 0); }\n\n private:\n  // Maximum of the current sliding window at the front. Then the maximum of the\n  // remaining window that came after this values first occurence, and so on.\n  std::deque<float> non_ascending_maxima_;\n};\n\n}  // namespace\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::FastCorrelativeScanMatcherOptions options;\n  options.set_linear_search_window(\n      parameter_dictionary->GetDouble(\"linear_search_window\"));\n  options.set_angular_search_window(\n      parameter_dictionary->GetDouble(\"angular_search_window\"));\n  options.set_branch_and_bound_depth(\n      parameter_dictionary->GetInt(\"branch_and_bound_depth\"));\n  return options;\n}\n\nPrecomputationGrid::PrecomputationGrid(\n    const ProbabilityGrid& probability_grid, const CellLimits& limits,\n    const int width, std::vector<float>* reusable_intermediate_grid)\n    : offset_(-width + 1, -width + 1),\n      wide_limits_(limits.num_x_cells + width - 1,\n                   limits.num_y_cells + width - 1),\n      cells_(wide_limits_.num_x_cells * wide_limits_.num_y_cells) {\n  CHECK_GE(width, 1);\n  CHECK_GE(limits.num_x_cells, 1);\n  CHECK_GE(limits.num_y_cells, 1);\n  const int stride = wide_limits_.num_x_cells;\n  // First we compute the maximum probability for each (x0, y) achieved in the\n  // span defined by x0 <= x < x0 + width.\n  std::vector<float>& intermediate = *reusable_intermediate_grid;\n  intermediate.resize(wide_limits_.num_x_cells * limits.num_y_cells);\n  for (int y = 0; y != limits.num_y_cells; ++y) {\n    SlidingWindowMaximum current_values;\n    current_values.AddValue(\n        probability_grid.GetProbability(Eigen::Array2i(0, y)));\n    for (int x = -width + 1; x != 0; ++x) {\n      intermediate[x + width - 1 + y * stride] = current_values.GetMaximum();\n      if (x + width < limits.num_x_cells) {\n        current_values.AddValue(\n            probability_grid.GetProbability(Eigen::Array2i(x + width, y)));\n      }\n    }\n    for (int x = 0; x < limits.num_x_cells - width; ++x) {\n      intermediate[x + width - 1 + y * stride] = current_values.GetMaximum();\n      current_values.RemoveValue(\n          probability_grid.GetProbability(Eigen::Array2i(x, y)));\n      current_values.AddValue(\n          probability_grid.GetProbability(Eigen::Array2i(x + width, y)));\n    }\n    for (int x = std::max(limits.num_x_cells - width, 0);\n         x != limits.num_x_cells; ++x) {\n      intermediate[x + width - 1 + y * stride] = current_values.GetMaximum();\n      current_values.RemoveValue(\n          probability_grid.GetProbability(Eigen::Array2i(x, y)));\n    }\n    current_values.CheckIsEmpty();\n  }\n  // For each (x, y), we compute the maximum probability in the width x width\n  // region starting at each (x, y) and precompute the resulting bound on the\n  // score.\n  for (int x = 0; x != wide_limits_.num_x_cells; ++x) {\n    SlidingWindowMaximum current_values;\n    current_values.AddValue(intermediate[x]);\n    for (int y = -width + 1; y != 0; ++y) {\n      cells_[x + (y + width - 1) * stride] =\n          ComputeCellValue(current_values.GetMaximum());\n      if (y + width < limits.num_y_cells) {\n        current_values.AddValue(intermediate[x + (y + width) * stride]);\n      }\n    }\n    for (int y = 0; y < limits.num_y_cells - width; ++y) {\n      cells_[x + (y + width - 1) * stride] =\n          ComputeCellValue(current_values.GetMaximum());\n      current_values.RemoveValue(intermediate[x + y * stride]);\n      current_values.AddValue(intermediate[x + (y + width) * stride]);\n    }\n    for (int y = std::max(limits.num_y_cells - width, 0);\n         y != limits.num_y_cells; ++y) {\n      cells_[x + (y + width - 1) * stride] =\n          ComputeCellValue(current_values.GetMaximum());\n      current_values.RemoveValue(intermediate[x + y * stride]);\n    }\n    current_values.CheckIsEmpty();\n  }\n}\n\nuint8 PrecomputationGrid::ComputeCellValue(const float probability) const {\n  const int cell_value = common::RoundToInt(\n      (probability - mapping::kMinProbability) *\n      (255.f / (mapping::kMaxProbability - mapping::kMinProbability)));\n  CHECK_GE(cell_value, 0);\n  CHECK_LE(cell_value, 255);\n  return cell_value;\n}\n\nclass PrecomputationGridStack {\n public:\n  PrecomputationGridStack(\n      const ProbabilityGrid& probability_grid,\n      const proto::FastCorrelativeScanMatcherOptions& options) {\n    CHECK_GE(options.branch_and_bound_depth(), 1);\n    const int max_width = 1 << (options.branch_and_bound_depth() - 1);\n    precomputation_grids_.reserve(options.branch_and_bound_depth());\n    std::vector<float> reusable_intermediate_grid;\n    const CellLimits limits = probability_grid.limits().cell_limits();\n    reusable_intermediate_grid.reserve((limits.num_x_cells + max_width - 1) *\n                                       limits.num_y_cells);\n    for (int i = 0; i != options.branch_and_bound_depth(); ++i) {\n      const int width = 1 << i;\n      precomputation_grids_.emplace_back(probability_grid, limits, width,\n                                         &reusable_intermediate_grid);\n    }\n  }\n\n  const PrecomputationGrid& Get(int index) {\n    return precomputation_grids_[index];\n  }\n\n  int max_depth() const { return precomputation_grids_.size() - 1; }\n\n private:\n  std::vector<PrecomputationGrid> precomputation_grids_;\n};\n\nFastCorrelativeScanMatcher::FastCorrelativeScanMatcher(\n    const ProbabilityGrid& probability_grid,\n    const proto::FastCorrelativeScanMatcherOptions& options)\n    : options_(options),\n      limits_(probability_grid.limits()),\n      precomputation_grid_stack_(\n          new PrecomputationGridStack(probability_grid, options)) {}\n\nFastCorrelativeScanMatcher::~FastCorrelativeScanMatcher() {}\n\nbool FastCorrelativeScanMatcher::Match(\n    const transform::Rigid2d& initial_pose_estimate,\n    const sensor::PointCloud& point_cloud, const float min_score, float* score,\n    transform::Rigid2d* pose_estimate) const {\n  const SearchParameters search_parameters(options_.linear_search_window(),\n                                           options_.angular_search_window(),\n                                           point_cloud, limits_.resolution());\n  return MatchWithSearchParameters(search_parameters, initial_pose_estimate,\n                                   point_cloud, min_score, score,\n                                   pose_estimate);\n}\n\nbool FastCorrelativeScanMatcher::MatchFullSubmap(\n    const sensor::PointCloud& point_cloud, float min_score, float* score,\n    transform::Rigid2d* pose_estimate) const {\n  // Compute a search window around the center of the submap that includes it\n  // fully.\n  const SearchParameters search_parameters(\n      1e6 * limits_.resolution(),  // Linear search window, 1e6 cells/direction.\n      M_PI,  // Angular search window, 180 degrees in both directions.\n      point_cloud, limits_.resolution());\n  const transform::Rigid2d center = transform::Rigid2d::Translation(\n      limits_.max() - 0.5 * limits_.resolution() *\n                          Eigen::Vector2d(limits_.cell_limits().num_y_cells,\n                                          limits_.cell_limits().num_x_cells));\n  return MatchWithSearchParameters(search_parameters, center, point_cloud,\n                                   min_score, score, pose_estimate);\n}\n\nbool FastCorrelativeScanMatcher::MatchWithSearchParameters(\n    SearchParameters search_parameters,\n    const transform::Rigid2d& initial_pose_estimate,\n    const sensor::PointCloud& point_cloud, float min_score, float* score,\n    transform::Rigid2d* pose_estimate) const {\n  CHECK_NOTNULL(score);\n  CHECK_NOTNULL(pose_estimate);\n\n  const Eigen::Rotation2Dd initial_rotation = initial_pose_estimate.rotation();\n  const sensor::PointCloud rotated_point_cloud = sensor::TransformPointCloud(\n      point_cloud,\n      transform::Rigid3f::Rotation(Eigen::AngleAxisf(\n          initial_rotation.cast<float>().angle(), Eigen::Vector3f::UnitZ())));\n  const std::vector<sensor::PointCloud> rotated_scans =\n      GenerateRotatedScans(rotated_point_cloud, search_parameters);\n  const std::vector<DiscreteScan> discrete_scans = DiscretizeScans(\n      limits_, rotated_scans,\n      Eigen::Translation2f(initial_pose_estimate.translation().x(),\n                           initial_pose_estimate.translation().y()));\n  search_parameters.ShrinkToFit(discrete_scans, limits_.cell_limits());\n\n  const std::vector<Candidate> lowest_resolution_candidates =\n      ComputeLowestResolutionCandidates(discrete_scans, search_parameters);\n  const Candidate best_candidate = BranchAndBound(\n      discrete_scans, search_parameters, lowest_resolution_candidates,\n      precomputation_grid_stack_->max_depth(), min_score);\n  if (best_candidate.score > min_score) {\n    *score = best_candidate.score;\n    *pose_estimate = transform::Rigid2d(\n        {initial_pose_estimate.translation().x() + best_candidate.x,\n         initial_pose_estimate.translation().y() + best_candidate.y},\n        initial_rotation * Eigen::Rotation2Dd(best_candidate.orientation));\n    return true;\n  }\n  return false;\n}\n\nstd::vector<Candidate>\nFastCorrelativeScanMatcher::ComputeLowestResolutionCandidates(\n    const std::vector<DiscreteScan>& discrete_scans,\n    const SearchParameters& search_parameters) const {\n  std::vector<Candidate> lowest_resolution_candidates =\n      GenerateLowestResolutionCandidates(search_parameters);\n  ScoreCandidates(\n      precomputation_grid_stack_->Get(precomputation_grid_stack_->max_depth()),\n      discrete_scans, search_parameters, &lowest_resolution_candidates);\n  return lowest_resolution_candidates;\n}\n\nstd::vector<Candidate>\nFastCorrelativeScanMatcher::GenerateLowestResolutionCandidates(\n    const SearchParameters& search_parameters) const {\n  const int linear_step_size = 1 << precomputation_grid_stack_->max_depth();\n  int num_candidates = 0;\n  for (int scan_index = 0; scan_index != search_parameters.num_scans;\n       ++scan_index) {\n    const int num_lowest_resolution_linear_x_candidates =\n        (search_parameters.linear_bounds[scan_index].max_x -\n         search_parameters.linear_bounds[scan_index].min_x + linear_step_size) /\n        linear_step_size;\n    const int num_lowest_resolution_linear_y_candidates =\n        (search_parameters.linear_bounds[scan_index].max_y -\n         search_parameters.linear_bounds[scan_index].min_y + linear_step_size) /\n        linear_step_size;\n    num_candidates += num_lowest_resolution_linear_x_candidates *\n                      num_lowest_resolution_linear_y_candidates;\n  }\n  std::vector<Candidate> candidates;\n  candidates.reserve(num_candidates);\n  for (int scan_index = 0; scan_index != search_parameters.num_scans;\n       ++scan_index) {\n    for (int x_index_offset = search_parameters.linear_bounds[scan_index].min_x;\n         x_index_offset <= search_parameters.linear_bounds[scan_index].max_x;\n         x_index_offset += linear_step_size) {\n      for (int y_index_offset =\n               search_parameters.linear_bounds[scan_index].min_y;\n           y_index_offset <= search_parameters.linear_bounds[scan_index].max_y;\n           y_index_offset += linear_step_size) {\n        candidates.emplace_back(scan_index, x_index_offset, y_index_offset,\n                                search_parameters);\n      }\n    }\n  }\n  CHECK_EQ(candidates.size(), num_candidates);\n  return candidates;\n}\n\nvoid FastCorrelativeScanMatcher::ScoreCandidates(\n    const PrecomputationGrid& precomputation_grid,\n    const std::vector<DiscreteScan>& discrete_scans,\n    const SearchParameters& search_parameters,\n    std::vector<Candidate>* const candidates) const {\n  for (Candidate& candidate : *candidates) {\n    int sum = 0;\n    for (const Eigen::Array2i& xy_index :\n         discrete_scans[candidate.scan_index]) {\n      const Eigen::Array2i proposed_xy_index(\n          xy_index.x() + candidate.x_index_offset,\n          xy_index.y() + candidate.y_index_offset);\n      sum += precomputation_grid.GetValue(proposed_xy_index);\n    }\n    candidate.score = PrecomputationGrid::ToProbability(\n        sum / static_cast<float>(discrete_scans[candidate.scan_index].size()));\n  }\n  std::sort(candidates->begin(), candidates->end(), std::greater<Candidate>());\n}\n\nCandidate FastCorrelativeScanMatcher::BranchAndBound(\n    const std::vector<DiscreteScan>& discrete_scans,\n    const SearchParameters& search_parameters,\n    const std::vector<Candidate>& candidates, const int candidate_depth,\n    float min_score) const {\n  if (candidate_depth == 0) {\n    // Return the best candidate.\n    return *candidates.begin();\n  }\n\n  Candidate best_high_resolution_candidate(0, 0, 0, search_parameters);\n  best_high_resolution_candidate.score = min_score;\n  for (const Candidate& candidate : candidates) {\n    if (candidate.score <= min_score) {\n      break;\n    }\n    std::vector<Candidate> higher_resolution_candidates;\n    const int half_width = 1 << (candidate_depth - 1);\n    for (int x_offset : {0, half_width}) {\n      if (candidate.x_index_offset + x_offset >\n          search_parameters.linear_bounds[candidate.scan_index].max_x) {\n        break;\n      }\n      for (int y_offset : {0, half_width}) {\n        if (candidate.y_index_offset + y_offset >\n            search_parameters.linear_bounds[candidate.scan_index].max_y) {\n          break;\n        }\n        higher_resolution_candidates.emplace_back(\n            candidate.scan_index, candidate.x_index_offset + x_offset,\n            candidate.y_index_offset + y_offset, search_parameters);\n      }\n    }\n    ScoreCandidates(precomputation_grid_stack_->Get(candidate_depth - 1),\n                    discrete_scans, search_parameters,\n                    &higher_resolution_candidates);\n    best_high_resolution_candidate = std::max(\n        best_high_resolution_candidate,\n        BranchAndBound(discrete_scans, search_parameters,\n                       higher_resolution_candidates, candidate_depth - 1,\n                       best_high_resolution_candidate.score));\n  }\n  return best_high_resolution_candidate;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/fast_correlative_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an implementation of the algorithm described in \"Real-Time\n// Correlative Scan Matching\" by Olson.\n//\n// It is similar to the RealTimeCorrelativeScanMatcher but has a different\n// trade-off: Scan matching is faster because more effort is put into the\n// precomputation done for a given map. However, this map is immutable after\n// construction.\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/scan_matching/correlative_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n// A precomputed grid that contains in each cell (x0, y0) the maximum\n// probability in the width x width area defined by x0 <= x < x0 + width and\n// y0 <= y < y0.\nclass PrecomputationGrid {\n public:\n  PrecomputationGrid(const ProbabilityGrid& probability_grid,\n                     const CellLimits& limits, int width,\n                     std::vector<float>* reusable_intermediate_grid);\n\n  // Returns a value between 0 and 255 to represent probabilities between\n  // kMinProbability and kMaxProbability.\n  int GetValue(const Eigen::Array2i& xy_index) const {\n    const Eigen::Array2i local_xy_index = xy_index - offset_;\n    // The static_cast<unsigned> is for performance to check with 2 comparisons\n    // xy_index.x() < offset_.x() || xy_index.y() < offset_.y() ||\n    // local_xy_index.x() >= wide_limits_.num_x_cells ||\n    // local_xy_index.y() >= wide_limits_.num_y_cells\n    // instead of using 4 comparisons.\n    if (static_cast<unsigned>(local_xy_index.x()) >=\n            static_cast<unsigned>(wide_limits_.num_x_cells) ||\n        static_cast<unsigned>(local_xy_index.y()) >=\n            static_cast<unsigned>(wide_limits_.num_y_cells)) {\n      return 0;\n    }\n    const int stride = wide_limits_.num_x_cells;\n    return cells_[local_xy_index.x() + local_xy_index.y() * stride];\n  }\n\n  // Maps values from [0, 255] to [kMinProbability, kMaxProbability].\n  static float ToProbability(float value) {\n    return mapping::kMinProbability +\n           value *\n               ((mapping::kMaxProbability - mapping::kMinProbability) / 255.f);\n  }\n\n private:\n  uint8 ComputeCellValue(float probability) const;\n\n  // Offset of the precomputation grid in relation to the 'probability_grid'\n  // including the additional 'width' - 1 cells.\n  const Eigen::Array2i offset_;\n\n  // Size of the precomputation grid.\n  const CellLimits wide_limits_;\n\n  // Probabilites mapped to 0 to 255.\n  std::vector<uint8> cells_;\n};\n\nclass PrecomputationGridStack;\n\n// An implementation of \"Real-Time Correlative Scan Matching\" by Olson.\nclass FastCorrelativeScanMatcher {\n public:\n  FastCorrelativeScanMatcher(\n      const ProbabilityGrid& probability_grid,\n      const proto::FastCorrelativeScanMatcherOptions& options);\n  ~FastCorrelativeScanMatcher();\n\n  FastCorrelativeScanMatcher(const FastCorrelativeScanMatcher&) = delete;\n  FastCorrelativeScanMatcher& operator=(const FastCorrelativeScanMatcher&) =\n      delete;\n\n  // Aligns 'point_cloud' within the 'probability_grid' given an\n  // 'initial_pose_estimate'. If a score above 'min_score' (excluding equality)\n  // is possible, true is returned, and 'score' and 'pose_estimate' are updated\n  // with the result.\n  bool Match(const transform::Rigid2d& initial_pose_estimate,\n             const sensor::PointCloud& point_cloud, float min_score,\n             float* score, transform::Rigid2d* pose_estimate) const;\n\n  // Aligns 'point_cloud' within the full 'probability_grid', i.e., not\n  // restricted to the configured search window. If a score above 'min_score'\n  // (excluding equality) is possible, true is returned, and 'score' and\n  // 'pose_estimate' are updated with the result.\n  bool MatchFullSubmap(const sensor::PointCloud& point_cloud, float min_score,\n                       float* score, transform::Rigid2d* pose_estimate) const;\n\n private:\n  // The actual implementation of the scan matcher, called by Match() and\n  // MatchFullSubmap() with appropriate 'initial_pose_estimate' and\n  // 'search_parameters'.\n  bool MatchWithSearchParameters(\n      SearchParameters search_parameters,\n      const transform::Rigid2d& initial_pose_estimate,\n      const sensor::PointCloud& point_cloud, float min_score, float* score,\n      transform::Rigid2d* pose_estimate) const;\n  std::vector<Candidate> ComputeLowestResolutionCandidates(\n      const std::vector<DiscreteScan>& discrete_scans,\n      const SearchParameters& search_parameters) const;\n  std::vector<Candidate> GenerateLowestResolutionCandidates(\n      const SearchParameters& search_parameters) const;\n  void ScoreCandidates(const PrecomputationGrid& precomputation_grid,\n                       const std::vector<DiscreteScan>& discrete_scans,\n                       const SearchParameters& search_parameters,\n                       std::vector<Candidate>* const candidates) const;\n  Candidate BranchAndBound(const std::vector<DiscreteScan>& discrete_scans,\n                           const SearchParameters& search_parameters,\n                           const std::vector<Candidate>& candidates,\n                           int candidate_depth, float min_score) const;\n\n  const proto::FastCorrelativeScanMatcherOptions options_;\n  MapLimits limits_;\n  std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/fast_correlative_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <limits>\n#include <random>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\nnamespace {\n\nTEST(PrecomputationGridTest, CorrectValues) {\n  // Create a probability grid with random values that can be exactly\n  // represented by uint8 values.\n  std::mt19937 prng(42);\n  std::uniform_int_distribution<int> distribution(0, 255);\n  ProbabilityGrid probability_grid(\n      MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(250, 250)));\n  probability_grid.StartUpdate();\n  for (const Eigen::Array2i& xy_index :\n       XYIndexRangeIterator(Eigen::Array2i(50, 50), Eigen::Array2i(249, 249))) {\n    probability_grid.SetProbability(\n        xy_index, PrecomputationGrid::ToProbability(distribution(prng)));\n  }\n\n  std::vector<float> reusable_intermediate_grid;\n  for (const int width : {1, 2, 3, 8}) {\n    PrecomputationGrid precomputation_grid(\n        probability_grid, probability_grid.limits().cell_limits(), width,\n        &reusable_intermediate_grid);\n    for (const Eigen::Array2i& xy_index :\n         XYIndexRangeIterator(probability_grid.limits().cell_limits())) {\n      float max_score = -std::numeric_limits<float>::infinity();\n      for (int y = 0; y != width; ++y) {\n        for (int x = 0; x != width; ++x) {\n          max_score = std::max<float>(\n              max_score,\n              probability_grid.GetProbability(xy_index + Eigen::Array2i(x, y)));\n        }\n      }\n      EXPECT_NEAR(max_score,\n                  PrecomputationGrid::ToProbability(\n                      precomputation_grid.GetValue(xy_index)),\n                  1e-4);\n    }\n  }\n}\n\nTEST(PrecomputationGridTest, TinyProbabilityGrid) {\n  std::mt19937 prng(42);\n  std::uniform_int_distribution<int> distribution(0, 255);\n  ProbabilityGrid probability_grid(\n      MapLimits(0.05, Eigen::Vector2d(0.1, 0.1), CellLimits(4, 4)));\n  probability_grid.StartUpdate();\n  for (const Eigen::Array2i& xy_index :\n       XYIndexRangeIterator(probability_grid.limits().cell_limits())) {\n    probability_grid.SetProbability(\n        xy_index, PrecomputationGrid::ToProbability(distribution(prng)));\n  }\n\n  std::vector<float> reusable_intermediate_grid;\n  for (const int width : {1, 2, 3, 8, 200}) {\n    PrecomputationGrid precomputation_grid(\n        probability_grid, probability_grid.limits().cell_limits(), width,\n        &reusable_intermediate_grid);\n    for (const Eigen::Array2i& xy_index :\n         XYIndexRangeIterator(probability_grid.limits().cell_limits())) {\n      float max_score = -std::numeric_limits<float>::infinity();\n      for (int y = 0; y != width; ++y) {\n        for (int x = 0; x != width; ++x) {\n          max_score = std::max<float>(\n              max_score,\n              probability_grid.GetProbability(xy_index + Eigen::Array2i(x, y)));\n        }\n      }\n      EXPECT_NEAR(max_score,\n                  PrecomputationGrid::ToProbability(\n                      precomputation_grid.GetValue(xy_index)),\n                  1e-4);\n    }\n  }\n}\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherTestOptions(const int branch_and_bound_depth) {\n  auto parameter_dictionary =\n      common::MakeDictionary(R\"text(\n      return {\n         linear_search_window = 3.,\n         angular_search_window = 1.,\n         branch_and_bound_depth = )text\" +\n                             std::to_string(branch_and_bound_depth) + \"}\");\n  return CreateFastCorrelativeScanMatcherOptions(parameter_dictionary.get());\n}\n\nmapping_2d::proto::RangeDataInserterOptions\nCreateRangeDataInserterTestOptions() {\n  auto parameter_dictionary = common::MakeDictionary(R\"text(\n      return {\n        insert_free_space = true,\n        hit_probability = 0.7,\n        miss_probability = 0.4,\n      })text\");\n  return mapping_2d::CreateRangeDataInserterOptions(parameter_dictionary.get());\n}\n\nTEST(FastCorrelativeScanMatcherTest, CorrectPose) {\n  std::mt19937 prng(42);\n  std::uniform_real_distribution<float> distribution(-1.f, 1.f);\n  RangeDataInserter range_data_inserter(CreateRangeDataInserterTestOptions());\n  constexpr float kMinScore = 0.1f;\n  const auto options = CreateFastCorrelativeScanMatcherTestOptions(3);\n\n  sensor::PointCloud point_cloud;\n  point_cloud.emplace_back(-2.5f, 0.5f, 0.f);\n  point_cloud.emplace_back(-2.f, 0.5f, 0.f);\n  point_cloud.emplace_back(0.f, -0.5f, 0.f);\n  point_cloud.emplace_back(0.5f, -1.6f, 0.f);\n  point_cloud.emplace_back(2.5f, 0.5f, 0.f);\n  point_cloud.emplace_back(2.5f, 1.7f, 0.f);\n\n  for (int i = 0; i != 50; ++i) {\n    const transform::Rigid2f expected_pose(\n        {2. * distribution(prng), 2. * distribution(prng)},\n        0.5 * distribution(prng));\n\n    ProbabilityGrid probability_grid(\n        MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200)));\n    probability_grid.StartUpdate();\n    range_data_inserter.Insert(\n        sensor::RangeData{\n            Eigen::Vector3f(expected_pose.translation().x(),\n                            expected_pose.translation().y(), 0.f),\n            sensor::TransformPointCloud(\n                point_cloud, transform::Embed3D(expected_pose.cast<float>())),\n            {}},\n        &probability_grid);\n\n    FastCorrelativeScanMatcher fast_correlative_scan_matcher(probability_grid,\n                                                             options);\n    transform::Rigid2d pose_estimate;\n    float score;\n    EXPECT_TRUE(fast_correlative_scan_matcher.Match(\n        transform::Rigid2d::Identity(), point_cloud, kMinScore, &score,\n        &pose_estimate));\n    EXPECT_LT(kMinScore, score);\n    EXPECT_THAT(expected_pose,\n                transform::IsNearly(pose_estimate.cast<float>(), 0.03f))\n        << \"Actual: \" << transform::ToProto(pose_estimate).DebugString()\n        << \"\\nExpected: \" << transform::ToProto(expected_pose).DebugString();\n  }\n}\n\nTEST(FastCorrelativeScanMatcherTest, FullSubmapMatching) {\n  std::mt19937 prng(42);\n  std::uniform_real_distribution<float> distribution(-1.f, 1.f);\n  RangeDataInserter range_data_inserter(CreateRangeDataInserterTestOptions());\n  constexpr float kMinScore = 0.1f;\n  const auto options = CreateFastCorrelativeScanMatcherTestOptions(6);\n\n  sensor::PointCloud unperturbed_point_cloud;\n  unperturbed_point_cloud.emplace_back(-2.5f, 0.5f, 0.f);\n  unperturbed_point_cloud.emplace_back(-2.25f, 0.5f, 0.f);\n  unperturbed_point_cloud.emplace_back(0.f, 0.5f, 0.f);\n  unperturbed_point_cloud.emplace_back(0.25f, 1.6f, 0.f);\n  unperturbed_point_cloud.emplace_back(2.5f, 0.5f, 0.f);\n  unperturbed_point_cloud.emplace_back(2.0f, 1.8f, 0.f);\n\n  for (int i = 0; i != 20; ++i) {\n    const transform::Rigid2f perturbation(\n        {10. * distribution(prng), 10. * distribution(prng)},\n        1.6 * distribution(prng));\n    const sensor::PointCloud point_cloud = sensor::TransformPointCloud(\n        unperturbed_point_cloud, transform::Embed3D(perturbation));\n    const transform::Rigid2f expected_pose =\n        transform::Rigid2f({2. * distribution(prng), 2. * distribution(prng)},\n                           0.5 * distribution(prng)) *\n        perturbation.inverse();\n\n    ProbabilityGrid probability_grid(\n        MapLimits(0.05, Eigen::Vector2d(5., 5.), CellLimits(200, 200)));\n    probability_grid.StartUpdate();\n    range_data_inserter.Insert(\n        sensor::RangeData{\n            transform::Embed3D(expected_pose * perturbation).translation(),\n            sensor::TransformPointCloud(point_cloud,\n                                        transform::Embed3D(expected_pose)),\n            {}},\n        &probability_grid);\n\n    FastCorrelativeScanMatcher fast_correlative_scan_matcher(probability_grid,\n                                                             options);\n    transform::Rigid2d pose_estimate;\n    float score;\n    EXPECT_TRUE(fast_correlative_scan_matcher.MatchFullSubmap(\n        point_cloud, kMinScore, &score, &pose_estimate));\n    EXPECT_LT(kMinScore, score);\n    EXPECT_THAT(expected_pose,\n                transform::IsNearly(pose_estimate.cast<float>(), 0.03f))\n        << \"Actual: \" << transform::ToProto(pose_estimate).DebugString()\n        << \"\\nExpected: \" << transform::ToProto(expected_pose).DebugString();\n  }\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/fast_global_localizer.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/fast_global_localizer.h\"\n\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nbool PerformGlobalLocalization(\n    const float cutoff,\n    const cartographer::sensor::AdaptiveVoxelFilter& voxel_filter,\n    const std::vector<\n        cartographer::mapping_2d::scan_matching::FastCorrelativeScanMatcher*>&\n        matchers,\n    const cartographer::sensor::PointCloud& point_cloud,\n    transform::Rigid2d* const best_pose_estimate, float* const best_score) {\n  CHECK(best_pose_estimate != nullptr)\n      << \"Need a non-null output_pose_estimate!\";\n  CHECK(best_score != nullptr) << \"Need a non-null best_score!\";\n  *best_score = cutoff;\n  transform::Rigid2d pose_estimate;\n  const sensor::PointCloud filtered_point_cloud =\n      voxel_filter.Filter(point_cloud);\n  bool success = false;\n  if (matchers.size() == 0) {\n    LOG(WARNING) << \"Map not yet large enough to localize in!\";\n    return false;\n  }\n  for (auto& matcher : matchers) {\n    float score = -1;\n    transform::Rigid2d pose_estimate;\n    if (matcher->MatchFullSubmap(filtered_point_cloud, *best_score, &score,\n                                 &pose_estimate)) {\n      CHECK_GT(score, *best_score) << \"MatchFullSubmap lied!\";\n      *best_score = score;\n      *best_pose_estimate = pose_estimate;\n      success = true;\n    }\n  }\n  return success;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/fast_global_localizer.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_GLOBAL_LOCALIZER_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_GLOBAL_LOCALIZER_H_\n\n#include <vector>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\n// Perform global localization against the provided 'matchers'. The 'cutoff'\n// specifies the minimum correlation that will be accepted.\n// This function does not take ownership of the pointers passed in\n// 'matchers'; they are passed as a vector of raw pointers to give maximum\n// flexibility to callers.\n//\n// Returns true in the case of successful localization. The output parameters\n// should not be trusted if the function returns false. The 'cutoff' and\n// 'best_score' are in the range [0.0, 1.0].\nbool PerformGlobalLocalization(\n    float cutoff, const cartographer::sensor::AdaptiveVoxelFilter& voxel_filter,\n    const std::vector<\n        cartographer::mapping_2d::scan_matching::FastCorrelativeScanMatcher*>&\n        matchers,\n    const cartographer::sensor::PointCloud& point_cloud,\n    transform::Rigid2d* best_pose_estimate, float* best_score);\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_FAST_GLOBAL_LOCALIZER_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/occupied_space_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"ceres/ceres.h\"\n#include \"ceres/cubic_interpolation.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\n// Computes the cost of inserting occupied space described by the point cloud\n// into the map. The cost increases with the amount of free space that would be\n// replaced by occupied space.\nclass OccupiedSpaceCostFunctor {\n public:\n  // Creates an OccupiedSpaceCostFunctor using the specified map, resolution\n  // level, and point cloud.\n  OccupiedSpaceCostFunctor(const double scaling_factor,\n                           const sensor::PointCloud& point_cloud,\n                           const ProbabilityGrid& probability_grid)\n      : scaling_factor_(scaling_factor),\n        point_cloud_(point_cloud),\n        probability_grid_(probability_grid) {}\n\n  OccupiedSpaceCostFunctor(const OccupiedSpaceCostFunctor&) = delete;\n  OccupiedSpaceCostFunctor& operator=(const OccupiedSpaceCostFunctor&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const pose, T* residual) const {\n    Eigen::Matrix<T, 2, 1> translation(pose[0], pose[1]);\n    Eigen::Rotation2D<T> rotation(pose[2]);\n    Eigen::Matrix<T, 2, 2> rotation_matrix = rotation.toRotationMatrix();\n    Eigen::Matrix<T, 3, 3> transform;\n    transform << rotation_matrix, translation, T(0.), T(0.), T(1.);\n\n    const GridArrayAdapter adapter(probability_grid_);\n    ceres::BiCubicInterpolator<GridArrayAdapter> interpolator(adapter);\n    const MapLimits& limits = probability_grid_.limits();\n\n    for (size_t i = 0; i < point_cloud_.size(); ++i) {\n      // Note that this is a 2D point. The third component is a scaling factor.\n      const Eigen::Matrix<T, 3, 1> point((T(point_cloud_[i].x())),\n                                         (T(point_cloud_[i].y())), T(1.));\n      const Eigen::Matrix<T, 3, 1> world = transform * point;\n      interpolator.Evaluate(\n          (limits.max().x() - world[0]) / limits.resolution() - 0.5 +\n              T(kPadding),\n          (limits.max().y() - world[1]) / limits.resolution() - 0.5 +\n              T(kPadding),\n          &residual[i]);\n      residual[i] = scaling_factor_ * (1. - residual[i]);\n    }\n    return true;\n  }\n\n private:\n  static constexpr int kPadding = INT_MAX / 4;\n  class GridArrayAdapter {\n   public:\n    enum { DATA_DIMENSION = 1 };\n\n    explicit GridArrayAdapter(const ProbabilityGrid& probability_grid)\n        : probability_grid_(probability_grid) {}\n\n    void GetValue(const int row, const int column, double* const value) const {\n      if (row < kPadding || column < kPadding || row >= NumRows() - kPadding ||\n          column >= NumCols() - kPadding) {\n        *value = mapping::kMinProbability;\n      } else {\n        *value = static_cast<double>(probability_grid_.GetProbability(\n            Eigen::Array2i(column - kPadding, row - kPadding)));\n      }\n    }\n\n    int NumRows() const {\n      return probability_grid_.limits().cell_limits().num_y_cells +\n             2 * kPadding;\n    }\n\n    int NumCols() const {\n      return probability_grid_.limits().cell_limits().num_x_cells +\n             2 * kPadding;\n    }\n\n   private:\n    const ProbabilityGrid& probability_grid_;\n  };\n\n  const double scaling_factor_;\n  const sensor::PointCloud& point_cloud_;\n  const ProbabilityGrid& probability_grid_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/proto/ceres_scan_matcher_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.scan_matching.proto;\n\nimport \"cartographer/common/proto/ceres_solver_options.proto\";\n\n// NEXT ID: 10\nmessage CeresScanMatcherOptions {\n  // Scaling parameters for each cost functor.\n  optional double occupied_space_weight = 1;\n  optional double translation_weight = 2;\n  optional double rotation_weight = 3;\n\n  // Configure the Ceres solver. See the Ceres documentation for more\n  // information: https://code.google.com/p/ceres-solver/\n  optional common.proto.CeresSolverOptions ceres_solver_options = 9;\n}\n"
  },
  {
    "path": "mapping_2d/scan_matching/proto/fast_correlative_scan_matcher_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.scan_matching.proto;\n\nmessage FastCorrelativeScanMatcherOptions {\n  // Minimum linear search window in which the best possible scan alignment\n  // will be found.\n  optional double linear_search_window = 3;\n\n  // Minimum angular search window in which the best possible scan alignment\n  // will be found.\n  optional double angular_search_window = 4;\n\n  // Number of precomputed grids to use.\n  optional int32 branch_and_bound_depth = 2;\n}\n"
  },
  {
    "path": "mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_2d.scan_matching.proto;\n\nmessage RealTimeCorrelativeScanMatcherOptions {\n  // Minimum linear search window in which the best possible scan alignment\n  // will be found.\n  optional double linear_search_window = 1;\n\n  // Minimum angular search window in which the best possible scan alignment\n  // will be found.\n  optional double angular_search_window = 2;\n\n  // Weights applied to each part of the score.\n  optional double translation_delta_cost_weight = 3;\n  optional double rotation_delta_cost_weight = 4;\n}\n"
  },
  {
    "path": "mapping_2d/scan_matching/real_time_correlative_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <functional>\n#include <limits>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nproto::RealTimeCorrelativeScanMatcherOptions\nCreateRealTimeCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::RealTimeCorrelativeScanMatcherOptions options;\n  options.set_linear_search_window(\n      parameter_dictionary->GetDouble(\"linear_search_window\"));\n  options.set_angular_search_window(\n      parameter_dictionary->GetDouble(\"angular_search_window\"));\n  options.set_translation_delta_cost_weight(\n      parameter_dictionary->GetDouble(\"translation_delta_cost_weight\"));\n  options.set_rotation_delta_cost_weight(\n      parameter_dictionary->GetDouble(\"rotation_delta_cost_weight\"));\n  CHECK_GE(options.translation_delta_cost_weight(), 0.);\n  CHECK_GE(options.rotation_delta_cost_weight(), 0.);\n  return options;\n}\n\nRealTimeCorrelativeScanMatcher::RealTimeCorrelativeScanMatcher(\n    const proto::RealTimeCorrelativeScanMatcherOptions& options)\n    : options_(options) {}\n\nstd::vector<Candidate>\nRealTimeCorrelativeScanMatcher::GenerateExhaustiveSearchCandidates(\n    const SearchParameters& search_parameters) const {\n  int num_candidates = 0;\n  for (int scan_index = 0; scan_index != search_parameters.num_scans;\n       ++scan_index) {\n    const int num_linear_x_candidates =\n        (search_parameters.linear_bounds[scan_index].max_x -\n         search_parameters.linear_bounds[scan_index].min_x + 1);\n    const int num_linear_y_candidates =\n        (search_parameters.linear_bounds[scan_index].max_y -\n         search_parameters.linear_bounds[scan_index].min_y + 1);\n    num_candidates += num_linear_x_candidates * num_linear_y_candidates;\n  }\n  std::vector<Candidate> candidates;\n  candidates.reserve(num_candidates);\n  for (int scan_index = 0; scan_index != search_parameters.num_scans;\n       ++scan_index) {\n    for (int x_index_offset = search_parameters.linear_bounds[scan_index].min_x;\n         x_index_offset <= search_parameters.linear_bounds[scan_index].max_x;\n         ++x_index_offset) {\n      for (int y_index_offset =\n               search_parameters.linear_bounds[scan_index].min_y;\n           y_index_offset <= search_parameters.linear_bounds[scan_index].max_y;\n           ++y_index_offset) {\n        candidates.emplace_back(scan_index, x_index_offset, y_index_offset,\n                                search_parameters);\n      }\n    }\n  }\n  CHECK_EQ(candidates.size(), num_candidates);\n  return candidates;\n}\n\ndouble RealTimeCorrelativeScanMatcher::Match(\n    const transform::Rigid2d& initial_pose_estimate,\n    const sensor::PointCloud& point_cloud,\n    const ProbabilityGrid& probability_grid,\n    transform::Rigid2d* pose_estimate) const {\n  CHECK_NOTNULL(pose_estimate);\n\n  const Eigen::Rotation2Dd initial_rotation = initial_pose_estimate.rotation();\n  const sensor::PointCloud rotated_point_cloud = sensor::TransformPointCloud(\n      point_cloud,\n      transform::Rigid3f::Rotation(Eigen::AngleAxisf(\n          initial_rotation.cast<float>().angle(), Eigen::Vector3f::UnitZ())));\n  const SearchParameters search_parameters(\n      options_.linear_search_window(), options_.angular_search_window(),\n      rotated_point_cloud, probability_grid.limits().resolution());\n\n  const std::vector<sensor::PointCloud> rotated_scans =\n      GenerateRotatedScans(rotated_point_cloud, search_parameters);\n  const std::vector<DiscreteScan> discrete_scans = DiscretizeScans(\n      probability_grid.limits(), rotated_scans,\n      Eigen::Translation2f(initial_pose_estimate.translation().x(),\n                           initial_pose_estimate.translation().y()));\n  std::vector<Candidate> candidates =\n      GenerateExhaustiveSearchCandidates(search_parameters);\n  ScoreCandidates(probability_grid, discrete_scans, search_parameters,\n                  &candidates);\n\n  const Candidate& best_candidate =\n      *std::max_element(candidates.begin(), candidates.end());\n  *pose_estimate = transform::Rigid2d(\n      {initial_pose_estimate.translation().x() + best_candidate.x,\n       initial_pose_estimate.translation().y() + best_candidate.y},\n      initial_rotation * Eigen::Rotation2Dd(best_candidate.orientation));\n  return best_candidate.score;\n}\n\nvoid RealTimeCorrelativeScanMatcher::ScoreCandidates(\n    const ProbabilityGrid& probability_grid,\n    const std::vector<DiscreteScan>& discrete_scans,\n    const SearchParameters& search_parameters,\n    std::vector<Candidate>* const candidates) const {\n  for (Candidate& candidate : *candidates) {\n    candidate.score = 0.f;\n    for (const Eigen::Array2i& xy_index :\n         discrete_scans[candidate.scan_index]) {\n      const Eigen::Array2i proposed_xy_index(\n          xy_index.x() + candidate.x_index_offset,\n          xy_index.y() + candidate.y_index_offset);\n      const float probability =\n          probability_grid.GetProbability(proposed_xy_index);\n      candidate.score += probability;\n    }\n    candidate.score /=\n        static_cast<float>(discrete_scans[candidate.scan_index].size());\n    candidate.score *=\n        std::exp(-common::Pow2(std::hypot(candidate.x, candidate.y) *\n                                   options_.translation_delta_cost_weight() +\n                               std::abs(candidate.orientation) *\n                                   options_.rotation_delta_cost_weight()));\n    CHECK_GT(candidate.score, 0.f);\n  }\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/real_time_correlative_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an implementation of the algorithm described in \"Real-Time\n// Correlative Scan Matching\" by Olson.\n//\n// The correlative scan matching algorithm is exhaustively evaluating the scan\n// matching search space. As described by the paper, the basic steps are:\n//\n// 1) Evaluate the probability p(z|xi, m) over the entire 3D search window using\n// the low-resolution table.\n// 2) Find the best voxel in the low-resolution 3D space that has not already\n// been considered. Denote this value as Li. If Li < Hbest, terminate: Hbest is\n// the best scan matching alignment.\n// 3) Evaluate the search volume inside voxel i using the high resolution table.\n// Suppose the log-likelihood of this voxel is Hi. Note that Hi <= Li since the\n// low-resolution map overestimates the log likelihoods. If Hi > Hbest, set\n// Hbest = Hi.\n//\n// This can be made even faster by transforming the scan exactly once over some\n// discretized range.\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n\n#include <iostream>\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/scan_matching/correlative_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\nproto::RealTimeCorrelativeScanMatcherOptions\nCreateRealTimeCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary);\n\n// An implementation of \"Real-Time Correlative Scan Matching\" by Olson.\nclass RealTimeCorrelativeScanMatcher {\n public:\n  explicit RealTimeCorrelativeScanMatcher(\n      const proto::RealTimeCorrelativeScanMatcherOptions& options);\n\n  RealTimeCorrelativeScanMatcher(const RealTimeCorrelativeScanMatcher&) =\n      delete;\n  RealTimeCorrelativeScanMatcher& operator=(\n      const RealTimeCorrelativeScanMatcher&) = delete;\n\n  // Aligns 'point_cloud' within the 'probability_grid' given an\n  // 'initial_pose_estimate' then updates 'pose_estimate' with the result and\n  // returns the score.\n  double Match(const transform::Rigid2d& initial_pose_estimate,\n               const sensor::PointCloud& point_cloud,\n               const ProbabilityGrid& probability_grid,\n               transform::Rigid2d* pose_estimate) const;\n\n  // Computes the score for each Candidate in a collection. The cost is computed\n  // as the sum of probabilities, different from the Ceres CostFunctions:\n  // http://ceres-solver.org/modeling.html\n  //\n  // Visible for testing.\n  void ScoreCandidates(const ProbabilityGrid& probability_grid,\n                       const std::vector<DiscreteScan>& discrete_scans,\n                       const SearchParameters& search_parameters,\n                       std::vector<Candidate>* candidates) const;\n\n private:\n  std::vector<Candidate> GenerateExhaustiveSearchCandidates(\n      const SearchParameters& search_parameters) const;\n\n  const proto::RealTimeCorrelativeScanMatcherOptions options_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/real_time_correlative_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n\n#include <cmath>\n#include <memory>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\nnamespace {\n\nclass RealTimeCorrelativeScanMatcherTest : public ::testing::Test {\n protected:\n  RealTimeCorrelativeScanMatcherTest()\n      : probability_grid_(\n            MapLimits(0.05, Eigen::Vector2d(0.05, 0.25), CellLimits(6, 6))) {\n    {\n      auto parameter_dictionary = common::MakeDictionary(\n          \"return { \"\n          \"insert_free_space = true, \"\n          \"hit_probability = 0.7, \"\n          \"miss_probability = 0.4, \"\n          \"}\");\n      range_data_inserter_ = common::make_unique<RangeDataInserter>(\n          CreateRangeDataInserterOptions(parameter_dictionary.get()));\n    }\n    point_cloud_.emplace_back(0.025f, 0.175f, 0.f);\n    point_cloud_.emplace_back(-0.025f, 0.175f, 0.f);\n    point_cloud_.emplace_back(-0.075f, 0.175f, 0.f);\n    point_cloud_.emplace_back(-0.125f, 0.175f, 0.f);\n    point_cloud_.emplace_back(-0.125f, 0.125f, 0.f);\n    point_cloud_.emplace_back(-0.125f, 0.075f, 0.f);\n    point_cloud_.emplace_back(-0.125f, 0.025f, 0.f);\n    probability_grid_.StartUpdate();\n    range_data_inserter_->Insert(\n        sensor::RangeData{Eigen::Vector3f::Zero(), point_cloud_, {}},\n        &probability_grid_);\n    {\n      auto parameter_dictionary = common::MakeDictionary(\n          \"return {\"\n          \"linear_search_window = 0.6, \"\n          \"angular_search_window = 0.16, \"\n          \"translation_delta_cost_weight = 0., \"\n          \"rotation_delta_cost_weight = 0., \"\n          \"}\");\n      real_time_correlative_scan_matcher_ =\n          common::make_unique<RealTimeCorrelativeScanMatcher>(\n              CreateRealTimeCorrelativeScanMatcherOptions(\n                  parameter_dictionary.get()));\n    }\n  }\n\n  ProbabilityGrid probability_grid_;\n  std::unique_ptr<RangeDataInserter> range_data_inserter_;\n  sensor::PointCloud point_cloud_;\n  std::unique_ptr<RealTimeCorrelativeScanMatcher>\n      real_time_correlative_scan_matcher_;\n};\n\nTEST_F(RealTimeCorrelativeScanMatcherTest,\n       ScorePerfectHighResolutionCandidate) {\n  const std::vector<sensor::PointCloud> scans =\n      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));\n  const std::vector<DiscreteScan> discrete_scans = DiscretizeScans(\n      probability_grid_.limits(), scans, Eigen::Translation2f::Identity());\n  std::vector<Candidate> candidates;\n  candidates.emplace_back(0, 0, 0, SearchParameters(0, 0, 0., 0.));\n  real_time_correlative_scan_matcher_->ScoreCandidates(\n      probability_grid_, discrete_scans, SearchParameters(0, 0, 0., 0.),\n      &candidates);\n  EXPECT_EQ(0, candidates[0].scan_index);\n  EXPECT_EQ(0, candidates[0].x_index_offset);\n  EXPECT_EQ(0, candidates[0].y_index_offset);\n  // Every point should align perfectly.\n  EXPECT_NEAR(0.7, candidates[0].score, 1e-2);\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest,\n       ScorePartiallyCorrectHighResolutionCandidate) {\n  const std::vector<sensor::PointCloud> scans =\n      GenerateRotatedScans(point_cloud_, SearchParameters(0, 0, 0., 0.));\n  const std::vector<DiscreteScan> discrete_scans = DiscretizeScans(\n      probability_grid_.limits(), scans, Eigen::Translation2f::Identity());\n  std::vector<Candidate> candidates;\n  candidates.emplace_back(0, 0, 1, SearchParameters(0, 0, 0., 0.));\n  real_time_correlative_scan_matcher_->ScoreCandidates(\n      probability_grid_, discrete_scans, SearchParameters(0, 0, 0., 0.),\n      &candidates);\n  EXPECT_EQ(0, candidates[0].scan_index);\n  EXPECT_EQ(0, candidates[0].x_index_offset);\n  EXPECT_EQ(1, candidates[0].y_index_offset);\n  // 3 points should align perfectly.\n  EXPECT_LT(0.7 * 3. / 7., candidates[0].score);\n  EXPECT_GT(0.7, candidates[0].score);\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/scan_matching/rotation_delta_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\n// Computes the cost of rotating the initial pose estimate. Cost increases with\n// the solution's distance from the initial estimate.\nclass RotationDeltaCostFunctor {\n public:\n  // Constructs a new RotationDeltaCostFunctor for the given 'angle'.\n  explicit RotationDeltaCostFunctor(const double scaling_factor,\n                                    const double angle)\n      : scaling_factor_(scaling_factor), angle_(angle) {}\n\n  RotationDeltaCostFunctor(const RotationDeltaCostFunctor&) = delete;\n  RotationDeltaCostFunctor& operator=(const RotationDeltaCostFunctor&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const pose, T* residual) const {\n    residual[0] = scaling_factor_ * (pose[2] - angle_);\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const double angle_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_2d/scan_matching/translation_delta_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace scan_matching {\n\n// Computes the cost of translating the initial pose estimate. Cost increases\n// with the solution's distance from the initial estimate.\nclass TranslationDeltaCostFunctor {\n public:\n  // Constructs a new TranslationDeltaCostFunctor from the given\n  // 'initial_pose_estimate' (x, y, theta).\n  explicit TranslationDeltaCostFunctor(\n      const double scaling_factor,\n      const transform::Rigid2d& initial_pose_estimate)\n      : scaling_factor_(scaling_factor),\n        x_(initial_pose_estimate.translation().x()),\n        y_(initial_pose_estimate.translation().y()) {}\n\n  TranslationDeltaCostFunctor(const TranslationDeltaCostFunctor&) = delete;\n  TranslationDeltaCostFunctor& operator=(const TranslationDeltaCostFunctor&) =\n      delete;\n\n  template <typename T>\n  bool operator()(const T* const pose, T* residual) const {\n    residual[0] = scaling_factor_ * (pose[0] - x_);\n    residual[1] = scaling_factor_ * (pose[1] - y_);\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const double x_;\n  const double y_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph/constraint_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/sparse_pose_graph/constraint_builder.h\"\n\n#include <cmath>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"Eigen/Eigenvalues\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace sparse_pose_graph {\n\ntransform::Rigid2d ComputeSubmapPose(const mapping::Submap& submap) {\n  return transform::Project2D(submap.local_pose);\n}\n\nConstraintBuilder::ConstraintBuilder(\n    const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions& options,\n    common::ThreadPool* const thread_pool)\n    : options_(options),\n      thread_pool_(thread_pool),\n      sampler_(options.sampling_ratio()),\n      adaptive_voxel_filter_(options.adaptive_voxel_filter_options()),\n      ceres_scan_matcher_(options.ceres_scan_matcher_options()) {}\n\nConstraintBuilder::~ConstraintBuilder() {\n  common::MutexLocker locker(&mutex_);\n  CHECK_EQ(constraints_.size(), 0) << \"WhenDone() was not called\";\n  CHECK_EQ(pending_computations_.size(), 0);\n  CHECK_EQ(submap_queued_work_items_.size(), 0);\n  CHECK(when_done_ == nullptr);\n}\n\nvoid ConstraintBuilder::MaybeAddConstraint(\n    const mapping::SubmapId& submap_id, const mapping::Submap* const submap,\n    const mapping::NodeId& node_id, const sensor::PointCloud* const point_cloud,\n    const transform::Rigid2d& initial_relative_pose) {\n  if (initial_relative_pose.translation().norm() >\n      options_.max_constraint_distance()) {\n    return;\n  }\n  if (sampler_.Pulse()) {\n    common::MutexLocker locker(&mutex_);\n    constraints_.emplace_back();\n    auto* const constraint = &constraints_.back();\n    ++pending_computations_[current_computation_];\n    const int current_computation = current_computation_;\n    ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n        submap_id, submap->finished_probability_grid, [=]() EXCLUDES(mutex_) {\n          ComputeConstraint(submap_id, submap, node_id,\n                            false,   /* match_full_submap */\n                            nullptr, /* trajectory_connectivity */\n                            point_cloud, initial_relative_pose, constraint);\n          FinishComputation(current_computation);\n        });\n  }\n}\n\nvoid ConstraintBuilder::MaybeAddGlobalConstraint(\n    const mapping::SubmapId& submap_id, const mapping::Submap* const submap,\n    const mapping::NodeId& node_id, const sensor::PointCloud* const point_cloud,\n    mapping::TrajectoryConnectivity* const trajectory_connectivity) {\n  common::MutexLocker locker(&mutex_);\n  constraints_.emplace_back();\n  auto* const constraint = &constraints_.back();\n  ++pending_computations_[current_computation_];\n  const int current_computation = current_computation_;\n  ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n      submap_id, submap->finished_probability_grid, [=]() EXCLUDES(mutex_) {\n        ComputeConstraint(submap_id, submap, node_id,\n                          true, /* match_full_submap */\n                          trajectory_connectivity, point_cloud,\n                          transform::Rigid2d::Identity(), constraint);\n        FinishComputation(current_computation);\n      });\n}\n\nvoid ConstraintBuilder::NotifyEndOfScan() {\n  common::MutexLocker locker(&mutex_);\n  ++current_computation_;\n}\n\nvoid ConstraintBuilder::WhenDone(\n    const std::function<void(const ConstraintBuilder::Result&)> callback) {\n  common::MutexLocker locker(&mutex_);\n  CHECK(when_done_ == nullptr);\n  when_done_ =\n      common::make_unique<std::function<void(const Result&)>>(callback);\n  ++pending_computations_[current_computation_];\n  const int current_computation = current_computation_;\n  thread_pool_->Schedule(\n      [this, current_computation] { FinishComputation(current_computation); });\n}\n\nvoid ConstraintBuilder::ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n    const mapping::SubmapId& submap_id, const ProbabilityGrid* const submap,\n    const std::function<void()> work_item) {\n  if (submap_scan_matchers_[submap_id].fast_correlative_scan_matcher !=\n      nullptr) {\n    thread_pool_->Schedule(work_item);\n  } else {\n    submap_queued_work_items_[submap_id].push_back(work_item);\n    if (submap_queued_work_items_[submap_id].size() == 1) {\n      thread_pool_->Schedule(\n          [=]() { ConstructSubmapScanMatcher(submap_id, submap); });\n    }\n  }\n}\n\nvoid ConstraintBuilder::ConstructSubmapScanMatcher(\n    const mapping::SubmapId& submap_id, const ProbabilityGrid* const submap) {\n  auto submap_scan_matcher =\n      common::make_unique<scan_matching::FastCorrelativeScanMatcher>(\n          *submap, options_.fast_correlative_scan_matcher_options());\n  common::MutexLocker locker(&mutex_);\n  submap_scan_matchers_[submap_id] = {submap, std::move(submap_scan_matcher)};\n  for (const std::function<void()>& work_item :\n       submap_queued_work_items_[submap_id]) {\n    thread_pool_->Schedule(work_item);\n  }\n  submap_queued_work_items_.erase(submap_id);\n}\n\nconst ConstraintBuilder::SubmapScanMatcher*\nConstraintBuilder::GetSubmapScanMatcher(const mapping::SubmapId& submap_id) {\n  common::MutexLocker locker(&mutex_);\n  const SubmapScanMatcher* submap_scan_matcher =\n      &submap_scan_matchers_[submap_id];\n  CHECK(submap_scan_matcher->fast_correlative_scan_matcher != nullptr);\n  return submap_scan_matcher;\n}\n\nvoid ConstraintBuilder::ComputeConstraint(\n    const mapping::SubmapId& submap_id, const mapping::Submap* const submap,\n    const mapping::NodeId& node_id, bool match_full_submap,\n    mapping::TrajectoryConnectivity* trajectory_connectivity,\n    const sensor::PointCloud* const point_cloud,\n    const transform::Rigid2d& initial_relative_pose,\n    std::unique_ptr<ConstraintBuilder::Constraint>* constraint) {\n  const transform::Rigid2d initial_pose =\n      ComputeSubmapPose(*submap) * initial_relative_pose;\n  const SubmapScanMatcher* const submap_scan_matcher =\n      GetSubmapScanMatcher(submap_id);\n  const sensor::PointCloud filtered_point_cloud =\n      adaptive_voxel_filter_.Filter(*point_cloud);\n\n  // The 'constraint_transform' (i <- j) is computed from:\n  // - a 'filtered_point_cloud' in j,\n  // - the initial guess 'initial_pose' for (map <- j),\n  // - the result 'pose_estimate' of Match() (map <- j).\n  // - the ComputeSubmapPose() (map <- i)\n  float score = 0.;\n  transform::Rigid2d pose_estimate = transform::Rigid2d::Identity();\n\n  if (match_full_submap) {\n    if (submap_scan_matcher->fast_correlative_scan_matcher->MatchFullSubmap(\n            filtered_point_cloud, options_.global_localization_min_score(),\n            &score, &pose_estimate)) {\n      CHECK_GT(score, options_.global_localization_min_score());\n      CHECK_GE(node_id.trajectory_id, 0);\n      CHECK_GE(submap_id.trajectory_id, 0);\n      trajectory_connectivity->Connect(node_id.trajectory_id,\n                                       submap_id.trajectory_id);\n    } else {\n      return;\n    }\n  } else {\n    if (submap_scan_matcher->fast_correlative_scan_matcher->Match(\n            initial_pose, filtered_point_cloud, options_.min_score(), &score,\n            &pose_estimate)) {\n      // We've reported a successful local match.\n      CHECK_GT(score, options_.min_score());\n    } else {\n      return;\n    }\n  }\n  {\n    common::MutexLocker locker(&mutex_);\n    score_histogram_.Add(score);\n  }\n\n  // Use the CSM estimate as both the initial and previous pose. This has the\n  // effect that, in the absence of better information, we prefer the original\n  // CSM estimate.\n  ceres::Solver::Summary unused_summary;\n  ceres_scan_matcher_.Match(pose_estimate, pose_estimate, filtered_point_cloud,\n                            *submap_scan_matcher->probability_grid,\n                            &pose_estimate, &unused_summary);\n\n  const transform::Rigid2d constraint_transform =\n      ComputeSubmapPose(*submap).inverse() * pose_estimate;\n  constraint->reset(new Constraint{submap_id,\n                                   node_id,\n                                   {transform::Embed3D(constraint_transform),\n                                    options_.loop_closure_translation_weight(),\n                                    options_.loop_closure_rotation_weight()},\n                                   Constraint::INTER_SUBMAP});\n\n  if (options_.log_matches()) {\n    std::ostringstream info;\n    info << \"Node \" << node_id << \" with \" << filtered_point_cloud.size()\n         << \" points on submap \" << submap_id << std::fixed;\n    if (match_full_submap) {\n      info << \" matches\";\n    } else {\n      const transform::Rigid2d difference =\n          initial_pose.inverse() * pose_estimate;\n      info << \" differs by translation \" << std::setprecision(2)\n           << difference.translation().norm() << \" rotation \"\n           << std::setprecision(3) << std::abs(difference.normalized_angle());\n    }\n    info << \" with score \" << std::setprecision(1) << 100. * score << \"%.\";\n    LOG(INFO) << info.str();\n  }\n}\n\nvoid ConstraintBuilder::FinishComputation(const int computation_index) {\n  Result result;\n  std::unique_ptr<std::function<void(const Result&)>> callback;\n  {\n    common::MutexLocker locker(&mutex_);\n    if (--pending_computations_[computation_index] == 0) {\n      pending_computations_.erase(computation_index);\n    }\n    if (pending_computations_.empty()) {\n      CHECK_EQ(submap_queued_work_items_.size(), 0);\n      if (when_done_ != nullptr) {\n        for (const std::unique_ptr<Constraint>& constraint : constraints_) {\n          if (constraint != nullptr) {\n            result.push_back(*constraint);\n          }\n        }\n        if (options_.log_matches()) {\n          LOG(INFO) << constraints_.size() << \" computations resulted in \"\n                    << result.size() << \" additional constraints.\";\n          LOG(INFO) << \"Score histogram:\\n\" << score_histogram_.ToString(10);\n        }\n        constraints_.clear();\n        callback = std::move(when_done_);\n        when_done_.reset();\n      }\n    }\n  }\n  if (callback != nullptr) {\n    (*callback)(result);\n  }\n}\n\nint ConstraintBuilder::GetNumFinishedScans() {\n  common::MutexLocker locker(&mutex_);\n  if (pending_computations_.empty()) {\n    return current_computation_;\n  }\n  return pending_computations_.begin()->first;\n}\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph/constraint_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n\n#include <array>\n#include <deque>\n#include <functional>\n#include <limits>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n#include \"cartographer/common/histogram.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.pb.h\"\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n#include \"cartographer/mapping_2d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_3d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace sparse_pose_graph {\n\n// Returns (map <- submap) where 'submap' is a coordinate system at the origin\n// of the Submap.\ntransform::Rigid2d ComputeSubmapPose(const mapping::Submap& submap);\n\n// Asynchronously computes constraints.\n//\n// Intermingle an arbitrary number of calls to MaybeAddConstraint() or\n// MaybeAddGlobalConstraint, then call WhenDone(). After all computations are\n// done the 'callback' will be called with the result and another\n// MaybeAdd(Global)Constraint()/WhenDone() cycle can follow.\n//\n// This class is thread-safe.\nclass ConstraintBuilder {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n  using Result = std::vector<Constraint>;\n\n  ConstraintBuilder(\n      const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions&\n          options,\n      common::ThreadPool* thread_pool);\n  ~ConstraintBuilder();\n\n  ConstraintBuilder(const ConstraintBuilder&) = delete;\n  ConstraintBuilder& operator=(const ConstraintBuilder&) = delete;\n\n  // Schedules exploring a new constraint between 'submap' identified by\n  // 'submap_id', and the 'point_cloud' for 'node_id'. The 'initial_pose' is\n  // relative to the 'submap'.\n  //\n  // The pointees of 'submap' and 'point_cloud' must stay valid until all\n  // computations are finished.\n  void MaybeAddConstraint(const mapping::SubmapId& submap_id,\n                          const mapping::Submap* submap,\n                          const mapping::NodeId& node_id,\n                          const sensor::PointCloud* point_cloud,\n                          const transform::Rigid2d& initial_relative_pose);\n\n  // Schedules exploring a new constraint between 'submap' identified by\n  // 'submap_id' and the 'point_cloud' for 'node_id'. This performs full-submap\n  // matching.\n  //\n  // The 'trajectory_connectivity' is updated if the full-submap match succeeds.\n  //\n  // The pointees of 'submap' and 'point_cloud' must stay valid until all\n  // computations are finished.\n  void MaybeAddGlobalConstraint(\n      const mapping::SubmapId& submap_id, const mapping::Submap* submap,\n      const mapping::NodeId& node_id, const sensor::PointCloud* point_cloud,\n      mapping::TrajectoryConnectivity* trajectory_connectivity);\n\n  // Must be called after all computations related to one node have been added.\n  void NotifyEndOfScan();\n\n  // Registers the 'callback' to be called with the results, after all\n  // computations triggered by MaybeAddConstraint() have finished.\n  void WhenDone(std::function<void(const Result&)> callback);\n\n  // Returns the number of consecutive finished scans.\n  int GetNumFinishedScans();\n\n private:\n  struct SubmapScanMatcher {\n    const ProbabilityGrid* probability_grid;\n    std::unique_ptr<scan_matching::FastCorrelativeScanMatcher>\n        fast_correlative_scan_matcher;\n  };\n\n  // Either schedules the 'work_item', or if needed, schedules the scan matcher\n  // construction and queues the 'work_item'.\n  void ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n      const mapping::SubmapId& submap_id, const ProbabilityGrid* submap,\n      std::function<void()> work_item) REQUIRES(mutex_);\n\n  // Constructs the scan matcher for a 'submap', then schedules its work items.\n  void ConstructSubmapScanMatcher(const mapping::SubmapId& submap_id,\n                                  const ProbabilityGrid* submap)\n      EXCLUDES(mutex_);\n\n  // Returns the scan matcher for a submap, which has to exist.\n  const SubmapScanMatcher* GetSubmapScanMatcher(\n      const mapping::SubmapId& submap_id) EXCLUDES(mutex_);\n\n  // Runs in a background thread and does computations for an additional\n  // constraint, assuming 'submap' and 'point_cloud' do not change anymore.\n  // If 'match_full_submap' is true, and global localization succeeds, will\n  // connect 'node_id.trajectory_id' and 'submap_id.trajectory_id' in\n  // 'trajectory_connectivity'.\n  // As output, it may create a new Constraint in 'constraint'.\n  void ComputeConstraint(\n      const mapping::SubmapId& submap_id, const mapping::Submap* submap,\n      const mapping::NodeId& node_id, bool match_full_submap,\n      mapping::TrajectoryConnectivity* trajectory_connectivity,\n      const sensor::PointCloud* point_cloud,\n      const transform::Rigid2d& initial_relative_pose,\n      std::unique_ptr<Constraint>* constraint) EXCLUDES(mutex_);\n\n  // Decrements the 'pending_computations_' count. If all computations are done,\n  // runs the 'when_done_' callback and resets the state.\n  void FinishComputation(int computation_index) EXCLUDES(mutex_);\n\n  const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions options_;\n  common::ThreadPool* thread_pool_;\n  common::Mutex mutex_;\n\n  // 'callback' set by WhenDone().\n  std::unique_ptr<std::function<void(const Result&)>> when_done_\n      GUARDED_BY(mutex_);\n\n  // Index of the scan in reaction to which computations are currently\n  // added. This is always the highest scan index seen so far, even when older\n  // scans are matched against a new submap.\n  int current_computation_ GUARDED_BY(mutex_) = 0;\n\n  // For each added scan, maps to the number of pending computations that were\n  // added for it.\n  std::map<int, int> pending_computations_ GUARDED_BY(mutex_);\n\n  // Constraints currently being computed in the background. A deque is used to\n  // keep pointers valid when adding more entries.\n  std::deque<std::unique_ptr<Constraint>> constraints_ GUARDED_BY(mutex_);\n\n  // Map of already constructed scan matchers by 'submap_id'.\n  std::map<mapping::SubmapId, SubmapScanMatcher> submap_scan_matchers_\n      GUARDED_BY(mutex_);\n\n  // Map by 'submap_id' of scan matchers under construction, and the work\n  // to do once construction is done.\n  std::map<mapping::SubmapId, std::vector<std::function<void()>>>\n      submap_queued_work_items_ GUARDED_BY(mutex_);\n\n  common::FixedRatioSampler sampler_;\n  const sensor::AdaptiveVoxelFilter adaptive_voxel_filter_;\n  scan_matching::CeresScanMatcher ceres_scan_matcher_;\n\n  // Histogram of scan matcher scores.\n  common::Histogram score_histogram_ GUARDED_BY(mutex_);\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph/optimization_problem.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/sparse_pose_graph/optimization_problem.h\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"cartographer/common/ceres_solver_options.h\"\n#include \"cartographer/common/histogram.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping_2d/sparse_pose_graph/spa_cost_function.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace sparse_pose_graph {\n\nnamespace {\n\n// Converts a pose into the 3 optimization variable format used for Ceres:\n// translation in x and y, followed by the rotation angle representing the\n// orientation.\nstd::array<double, 3> FromPose(const transform::Rigid2d& pose) {\n  return {{pose.translation().x(), pose.translation().y(),\n           pose.normalized_angle()}};\n}\n\n// Converts a pose as represented for Ceres back to an transform::Rigid2d pose.\ntransform::Rigid2d ToPose(const std::array<double, 3>& values) {\n  return transform::Rigid2d({values[0], values[1]}, values[2]);\n}\n\n}  // namespace\n\nOptimizationProblem::OptimizationProblem(\n    const mapping::sparse_pose_graph::proto::OptimizationProblemOptions&\n        options)\n    : options_(options) {}\n\nOptimizationProblem::~OptimizationProblem() {}\n\nvoid OptimizationProblem::AddImuData(const int trajectory_id,\n                                     const common::Time time,\n                                     const Eigen::Vector3d& linear_acceleration,\n                                     const Eigen::Vector3d& angular_velocity) {\n  CHECK_GE(trajectory_id, 0);\n  imu_data_.resize(\n      std::max(imu_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  imu_data_[trajectory_id].push_back(\n      mapping_3d::ImuData{time, linear_acceleration, angular_velocity});\n}\n\nvoid OptimizationProblem::AddTrajectoryNode(\n    const int trajectory_id, const common::Time time,\n    const transform::Rigid2d& initial_point_cloud_pose,\n    const transform::Rigid2d& point_cloud_pose) {\n  CHECK_GE(trajectory_id, 0);\n  node_data_.resize(\n      std::max(node_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  node_data_[trajectory_id].push_back(\n      NodeData{time, initial_point_cloud_pose, point_cloud_pose});\n}\n\nvoid OptimizationProblem::AddSubmap(const int trajectory_id,\n                                    const transform::Rigid2d& submap_pose) {\n  CHECK_GE(trajectory_id, 0);\n  submap_data_.resize(\n      std::max(submap_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  submap_data_[trajectory_id].push_back(SubmapData{submap_pose});\n}\n\nvoid OptimizationProblem::SetMaxNumIterations(const int32 max_num_iterations) {\n  options_.mutable_ceres_solver_options()->set_max_num_iterations(\n      max_num_iterations);\n}\n\nvoid OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {\n  if (node_data_.empty()) {\n    // Nothing to optimize.\n    return;\n  }\n\n  ceres::Problem::Options problem_options;\n  ceres::Problem problem(problem_options);\n\n  // Set the starting point.\n  // TODO(hrapp): Move ceres data into SubmapData.\n  std::vector<std::deque<std::array<double, 3>>> C_submaps(submap_data_.size());\n  std::vector<std::deque<std::array<double, 3>>> C_nodes(node_data_.size());\n  for (size_t trajectory_id = 0; trajectory_id != submap_data_.size();\n       ++trajectory_id) {\n    for (size_t submap_index = 0;\n         submap_index != submap_data_[trajectory_id].size(); ++submap_index) {\n      if (trajectory_id == 0 && submap_index == 0) {\n        // Fix the pose of the first submap of the first trajectory.\n        C_submaps[trajectory_id].push_back(\n            FromPose(transform::Rigid2d::Identity()));\n        problem.AddParameterBlock(C_submaps[trajectory_id].back().data(), 3);\n        problem.SetParameterBlockConstant(\n            C_submaps[trajectory_id].back().data());\n      } else {\n        C_submaps[trajectory_id].push_back(\n            FromPose(submap_data_[trajectory_id][submap_index].pose));\n        problem.AddParameterBlock(C_submaps[trajectory_id].back().data(), 3);\n      }\n    }\n  }\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    C_nodes[trajectory_id].resize(node_data_[trajectory_id].size());\n    for (size_t node_index = 0; node_index != node_data_[trajectory_id].size();\n         ++node_index) {\n      C_nodes[trajectory_id][node_index] =\n          FromPose(node_data_[trajectory_id][node_index].point_cloud_pose);\n      problem.AddParameterBlock(C_nodes[trajectory_id][node_index].data(), 3);\n    }\n  }\n\n  // Add cost functions for intra- and inter-submap constraints.\n  for (const Constraint& constraint : constraints) {\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<SpaCostFunction, 3, 3, 3>(\n            new SpaCostFunction(constraint.pose)),\n        // Only loop closure constraints should have a loss function.\n        constraint.tag == Constraint::INTER_SUBMAP\n            ? new ceres::HuberLoss(options_.huber_scale())\n            : nullptr,\n        C_submaps.at(constraint.submap_id.trajectory_id)\n            .at(constraint.submap_id.submap_index)\n            .data(),\n        C_nodes.at(constraint.node_id.trajectory_id)\n            .at(constraint.node_id.node_index)\n            .data());\n  }\n\n  // Add penalties for changes between consecutive scans.\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    for (size_t node_index = 1; node_index < node_data_[trajectory_id].size();\n         ++node_index) {\n      problem.AddResidualBlock(\n          new ceres::AutoDiffCostFunction<SpaCostFunction, 3, 3, 3>(\n              new SpaCostFunction(Constraint::Pose{\n                  transform::Embed3D(node_data_[trajectory_id][node_index - 1]\n                                         .initial_point_cloud_pose.inverse() *\n                                     node_data_[trajectory_id][node_index]\n                                         .initial_point_cloud_pose),\n                  options_.consecutive_scan_translation_penalty_factor(),\n                  options_.consecutive_scan_rotation_penalty_factor()})),\n          nullptr /* loss function */,\n          C_nodes[trajectory_id][node_index - 1].data(),\n          C_nodes[trajectory_id][node_index].data());\n    }\n  }\n\n  // Solve.\n  ceres::Solver::Summary summary;\n  ceres::Solve(\n      common::CreateCeresSolverOptions(options_.ceres_solver_options()),\n      &problem, &summary);\n\n  if (options_.log_solver_summary()) {\n    LOG(INFO) << summary.FullReport();\n  }\n\n  // Store the result.\n  for (size_t trajectory_id = 0; trajectory_id != submap_data_.size();\n       ++trajectory_id) {\n    for (size_t submap_index = 0;\n         submap_index != submap_data_[trajectory_id].size(); ++submap_index) {\n      submap_data_[trajectory_id][submap_index].pose =\n          ToPose(C_submaps[trajectory_id][submap_index]);\n    }\n  }\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    for (size_t node_index = 0; node_index != node_data_[trajectory_id].size();\n         ++node_index) {\n      node_data_[trajectory_id][node_index].point_cloud_pose =\n          ToPose(C_nodes[trajectory_id][node_index]);\n    }\n  }\n}\n\nconst std::vector<std::vector<NodeData>>& OptimizationProblem::node_data()\n    const {\n  return node_data_;\n}\n\nconst std::vector<std::vector<SubmapData>>& OptimizationProblem::submap_data()\n    const {\n  return submap_data_;\n}\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph/optimization_problem.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n#define CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n\n#include <array>\n#include <deque>\n#include <map>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/optimization_problem_options.pb.h\"\n#include \"cartographer/mapping_3d/imu_integration.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace sparse_pose_graph {\n\nstruct NodeData {\n  common::Time time;\n  transform::Rigid2d initial_point_cloud_pose;\n  transform::Rigid2d point_cloud_pose;\n};\n\nstruct SubmapData {\n  transform::Rigid2d pose;\n};\n\n// Implements the SPA loop closure method.\nclass OptimizationProblem {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n\n  explicit OptimizationProblem(\n      const mapping::sparse_pose_graph::proto::OptimizationProblemOptions&\n          options);\n  ~OptimizationProblem();\n\n  OptimizationProblem(const OptimizationProblem&) = delete;\n  OptimizationProblem& operator=(const OptimizationProblem&) = delete;\n\n  void AddImuData(int trajectory_id, common::Time time,\n                  const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity);\n  void AddTrajectoryNode(int trajectory_id, common::Time time,\n                         const transform::Rigid2d& initial_point_cloud_pose,\n                         const transform::Rigid2d& point_cloud_pose);\n  void AddSubmap(int trajectory_id, const transform::Rigid2d& submap_pose);\n\n  void SetMaxNumIterations(int32 max_num_iterations);\n\n  // Computes the optimized poses.\n  void Solve(const std::vector<Constraint>& constraints);\n\n  const std::vector<std::vector<NodeData>>& node_data() const;\n  const std::vector<std::vector<SubmapData>>& submap_data() const;\n\n private:\n  mapping::sparse_pose_graph::proto::OptimizationProblemOptions options_;\n  std::vector<std::deque<mapping_3d::ImuData>> imu_data_;\n  std::vector<std::vector<NodeData>> node_data_;\n  std::vector<std::vector<SubmapData>> submap_data_;\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph/spa_cost_function.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n#define CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n\n#include <array>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"ceres/jet.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace sparse_pose_graph {\n\nclass SpaCostFunction {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n\n  explicit SpaCostFunction(const Constraint::Pose& pose) : pose_(pose) {}\n\n  // Computes the error between the scan-to-submap alignment 'zbar_ij' and the\n  // difference of submap pose 'c_i' and scan pose 'c_j' which are both in an\n  // arbitrary common frame.\n  template <typename T>\n  static std::array<T, 3> ComputeUnscaledError(\n      const transform::Rigid2d& zbar_ij, const T* const c_i,\n      const T* const c_j) {\n    const T cos_theta_i = cos(c_i[2]);\n    const T sin_theta_i = sin(c_i[2]);\n    const T delta_x = c_j[0] - c_i[0];\n    const T delta_y = c_j[1] - c_i[1];\n    const T h[3] = {cos_theta_i * delta_x + sin_theta_i * delta_y,\n                    -sin_theta_i * delta_x + cos_theta_i * delta_y,\n                    c_j[2] - c_i[2]};\n    return {{T(zbar_ij.translation().x()) - h[0],\n             T(zbar_ij.translation().y()) - h[1],\n             common::NormalizeAngleDifference(T(zbar_ij.rotation().angle()) -\n                                              h[2])}};\n  }\n\n  // Computes the error scaled by 'translation_weight' and 'rotation_weight',\n  // storing it in 'e'.\n  template <typename T>\n  static void ComputeScaledError(const Constraint::Pose& pose,\n                                 const T* const c_i, const T* const c_j,\n                                 T* const e) {\n    const std::array<T, 3> e_ij =\n        ComputeUnscaledError(transform::Project2D(pose.zbar_ij), c_i, c_j);\n    e[0] = e_ij[0] * T(pose.translation_weight);\n    e[1] = e_ij[1] * T(pose.translation_weight);\n    e[2] = e_ij[2] * T(pose.rotation_weight);\n  }\n\n  template <typename T>\n  bool operator()(const T* const c_i, const T* const c_j, T* e) const {\n    ComputeScaledError(pose_, c_i, c_j, e);\n    return true;\n  }\n\n private:\n  const Constraint::Pose pose_;\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/sparse_pose_graph.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstdio>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"Eigen/Eigenvalues\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.pb.h\"\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nSparsePoseGraph::SparsePoseGraph(\n    const mapping::proto::SparsePoseGraphOptions& options,\n    common::ThreadPool* thread_pool)\n    : options_(options),\n      optimization_problem_(options_.optimization_problem_options()),\n      constraint_builder_(options_.constraint_builder_options(), thread_pool) {}\n\nSparsePoseGraph::~SparsePoseGraph() {\n  WaitForAllComputations();\n  common::MutexLocker locker(&mutex_);\n  CHECK(scan_queue_ == nullptr);\n}\n\nvoid SparsePoseGraph::GrowSubmapTransformsAsNeeded(\n    const std::vector<const mapping::Submap*>& insertion_submaps) {\n  CHECK(!insertion_submaps.empty());\n  const mapping::SubmapId first_submap_id = GetSubmapId(insertion_submaps[0]);\n  const int trajectory_id = first_submap_id.trajectory_id;\n  CHECK_GE(trajectory_id, 0);\n  const auto& submap_data = optimization_problem_.submap_data();\n  if (insertion_submaps.size() == 1) {\n    // If we don't already have an entry for the first submap, add one.\n    CHECK_EQ(first_submap_id.submap_index, 0);\n    if (static_cast<size_t>(trajectory_id) >= submap_data.size() ||\n        submap_data[trajectory_id].empty()) {\n      optimization_problem_.AddSubmap(trajectory_id,\n                                      transform::Rigid2d::Identity());\n    }\n    return;\n  }\n  CHECK_EQ(2, insertion_submaps.size());\n  const int next_submap_index = submap_data.at(trajectory_id).size();\n  // CHECK that we have a index for the second submap.\n  const mapping::SubmapId second_submap_id = GetSubmapId(insertion_submaps[1]);\n  CHECK_EQ(second_submap_id.trajectory_id, trajectory_id);\n  CHECK_LE(second_submap_id.submap_index, next_submap_index);\n  // Extrapolate if necessary.\n  if (second_submap_id.submap_index == next_submap_index) {\n    const auto& first_submap_pose =\n        submap_data.at(trajectory_id).at(first_submap_id.submap_index).pose;\n    optimization_problem_.AddSubmap(\n        trajectory_id,\n        first_submap_pose *\n            sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[0])\n                .inverse() *\n            sparse_pose_graph::ComputeSubmapPose(*insertion_submaps[1]));\n  }\n}\n\nvoid SparsePoseGraph::AddScan(\n    common::Time time, const transform::Rigid3d& tracking_to_pose,\n    const sensor::RangeData& range_data_in_pose, const transform::Rigid2d& pose,\n    const int trajectory_id, const mapping::Submap* const matching_submap,\n    const std::vector<const mapping::Submap*>& insertion_submaps) {\n  const transform::Rigid3d optimized_pose(\n      GetLocalToGlobalTransform(trajectory_id) * transform::Embed3D(pose));\n\n  common::MutexLocker locker(&mutex_);\n  constant_node_data_.push_back(mapping::TrajectoryNode::ConstantData{\n      time, range_data_in_pose,\n      Compress(sensor::RangeData{Eigen::Vector3f::Zero(), {}, {}}),\n      trajectory_id, tracking_to_pose});\n  trajectory_nodes_.Append(trajectory_id,\n                           mapping::TrajectoryNode{\n                               &constant_node_data_.back(), optimized_pose,\n                           });\n  ++num_trajectory_nodes_;\n  trajectory_connectivity_.Add(trajectory_id);\n\n  if (submap_ids_.count(insertion_submaps.back()) == 0) {\n    const mapping::SubmapId submap_id =\n        submap_states_.Append(trajectory_id, SubmapState());\n    submap_ids_.emplace(insertion_submaps.back(), submap_id);\n    submap_states_.at(submap_id).submap = insertion_submaps.back();\n  }\n  const mapping::Submap* const finished_submap =\n      insertion_submaps.front()->finished_probability_grid != nullptr\n          ? insertion_submaps.front()\n          : nullptr;\n\n  // Make sure we have a sampler for this trajectory.\n  if (!global_localization_samplers_[trajectory_id]) {\n    global_localization_samplers_[trajectory_id] =\n        common::make_unique<common::FixedRatioSampler>(\n            options_.global_sampling_ratio());\n  }\n\n  AddWorkItem([=]() REQUIRES(mutex_) {\n    ComputeConstraintsForScan(matching_submap, insertion_submaps,\n                              finished_submap, pose);\n  });\n}\n\nvoid SparsePoseGraph::AddWorkItem(std::function<void()> work_item) {\n  if (scan_queue_ == nullptr) {\n    work_item();\n  } else {\n    scan_queue_->push_back(work_item);\n  }\n}\n\nvoid SparsePoseGraph::AddImuData(const int trajectory_id, common::Time time,\n                                 const Eigen::Vector3d& linear_acceleration,\n                                 const Eigen::Vector3d& angular_velocity) {\n  common::MutexLocker locker(&mutex_);\n  AddWorkItem([=]() REQUIRES(mutex_) {\n    optimization_problem_.AddImuData(trajectory_id, time, linear_acceleration,\n                                     angular_velocity);\n  });\n}\n\nvoid SparsePoseGraph::ComputeConstraint(const mapping::NodeId& node_id,\n                                        const mapping::SubmapId& submap_id) {\n  // Only globally match against submaps not in this trajectory.\n  if (node_id.trajectory_id != submap_id.trajectory_id &&\n      global_localization_samplers_[node_id.trajectory_id]->Pulse()) {\n    constraint_builder_.MaybeAddGlobalConstraint(\n        submap_id, submap_states_.at(submap_id).submap, node_id,\n        &trajectory_nodes_.at(node_id).constant_data->range_data_2d.returns,\n        &trajectory_connectivity_);\n  } else {\n    const bool scan_and_submap_trajectories_connected =\n        reverse_connected_components_.count(node_id.trajectory_id) > 0 &&\n        reverse_connected_components_.count(submap_id.trajectory_id) > 0 &&\n        reverse_connected_components_.at(node_id.trajectory_id) ==\n            reverse_connected_components_.at(submap_id.trajectory_id);\n    if (node_id.trajectory_id == submap_id.trajectory_id ||\n        scan_and_submap_trajectories_connected) {\n      const transform::Rigid2d initial_relative_pose =\n          optimization_problem_.submap_data()\n              .at(submap_id.trajectory_id)\n              .at(submap_id.submap_index)\n              .pose.inverse() *\n          optimization_problem_.node_data()\n              .at(node_id.trajectory_id)\n              .at(node_id.node_index)\n              .point_cloud_pose;\n\n      constraint_builder_.MaybeAddConstraint(\n          submap_id, submap_states_.at(submap_id).submap, node_id,\n          &trajectory_nodes_.at(node_id).constant_data->range_data_2d.returns,\n          initial_relative_pose);\n    }\n  }\n}\n\nvoid SparsePoseGraph::ComputeConstraintsForOldScans(\n    const mapping::Submap* submap) {\n  const auto submap_id = GetSubmapId(submap);\n  const auto& submap_state = submap_states_.at(submap_id);\n\n  const auto& node_data = optimization_problem_.node_data();\n  for (size_t trajectory_id = 0; trajectory_id != node_data.size();\n       ++trajectory_id) {\n    for (size_t node_index = 0; node_index != node_data[trajectory_id].size();\n         ++node_index) {\n      const mapping::NodeId node_id{static_cast<int>(trajectory_id),\n                                    static_cast<int>(node_index)};\n      if (submap_state.node_ids.count(node_id) == 0) {\n        ComputeConstraint(node_id, submap_id);\n      }\n    }\n  }\n}\n\nvoid SparsePoseGraph::ComputeConstraintsForScan(\n    const mapping::Submap* matching_submap,\n    std::vector<const mapping::Submap*> insertion_submaps,\n    const mapping::Submap* finished_submap, const transform::Rigid2d& pose) {\n  GrowSubmapTransformsAsNeeded(insertion_submaps);\n  const mapping::SubmapId matching_id = GetSubmapId(matching_submap);\n  const transform::Rigid2d optimized_pose =\n      optimization_problem_.submap_data()\n          .at(matching_id.trajectory_id)\n          .at(matching_id.submap_index)\n          .pose *\n      sparse_pose_graph::ComputeSubmapPose(*matching_submap).inverse() * pose;\n  const mapping::NodeId node_id{\n      matching_id.trajectory_id,\n      static_cast<size_t>(matching_id.trajectory_id) <\n              optimization_problem_.node_data().size()\n          ? static_cast<int>(optimization_problem_.node_data()\n                                 .at(matching_id.trajectory_id)\n                                 .size())\n          : 0};\n  const mapping::TrajectoryNode::ConstantData* const scan_data =\n      trajectory_nodes_.at(node_id).constant_data;\n  CHECK_EQ(scan_data->trajectory_id, matching_id.trajectory_id);\n  optimization_problem_.AddTrajectoryNode(\n      matching_id.trajectory_id, scan_data->time, pose, optimized_pose);\n  for (const mapping::Submap* submap : insertion_submaps) {\n    const mapping::SubmapId submap_id = GetSubmapId(submap);\n    CHECK(!submap_states_.at(submap_id).finished);\n    submap_states_.at(submap_id).node_ids.emplace(node_id);\n    const transform::Rigid2d constraint_transform =\n        sparse_pose_graph::ComputeSubmapPose(*submap).inverse() * pose;\n    constraints_.push_back(Constraint{submap_id,\n                                      node_id,\n                                      {transform::Embed3D(constraint_transform),\n                                       options_.matcher_translation_weight(),\n                                       options_.matcher_rotation_weight()},\n                                      Constraint::INTRA_SUBMAP});\n  }\n\n  for (int trajectory_id = 0; trajectory_id < submap_states_.num_trajectories();\n       ++trajectory_id) {\n    for (int submap_index = 0;\n         submap_index < submap_states_.num_indices(trajectory_id);\n         ++submap_index) {\n      const mapping::SubmapId submap_id{trajectory_id, submap_index};\n      if (submap_states_.at(submap_id).finished) {\n        CHECK_EQ(submap_states_.at(submap_id).node_ids.count(node_id), 0);\n        ComputeConstraint(node_id, submap_id);\n      }\n    }\n  }\n\n  if (finished_submap != nullptr) {\n    const mapping::SubmapId finished_submap_id = GetSubmapId(finished_submap);\n    SubmapState& finished_submap_state = submap_states_.at(finished_submap_id);\n    CHECK(!finished_submap_state.finished);\n    // We have a new completed submap, so we look into adding constraints for\n    // old scans.\n    ComputeConstraintsForOldScans(finished_submap);\n    finished_submap_state.finished = true;\n  }\n  constraint_builder_.NotifyEndOfScan();\n  ++num_scans_since_last_loop_closure_;\n  if (options_.optimize_every_n_scans() > 0 &&\n      num_scans_since_last_loop_closure_ > options_.optimize_every_n_scans()) {\n    CHECK(!run_loop_closure_);\n    run_loop_closure_ = true;\n    // If there is a 'scan_queue_' already, some other thread will take care.\n    if (scan_queue_ == nullptr) {\n      scan_queue_ = common::make_unique<std::deque<std::function<void()>>>();\n      HandleScanQueue();\n    }\n  }\n}\n\nvoid SparsePoseGraph::HandleScanQueue() {\n  constraint_builder_.WhenDone(\n      [this](const sparse_pose_graph::ConstraintBuilder::Result& result) {\n        {\n          common::MutexLocker locker(&mutex_);\n          constraints_.insert(constraints_.end(), result.begin(), result.end());\n        }\n        RunOptimization();\n\n        common::MutexLocker locker(&mutex_);\n        num_scans_since_last_loop_closure_ = 0;\n        run_loop_closure_ = false;\n        while (!run_loop_closure_) {\n          if (scan_queue_->empty()) {\n            LOG(INFO) << \"We caught up. Hooray!\";\n            scan_queue_.reset();\n            return;\n          }\n          scan_queue_->front()();\n          scan_queue_->pop_front();\n        }\n        // We have to optimize again.\n        HandleScanQueue();\n      });\n}\n\nvoid SparsePoseGraph::WaitForAllComputations() {\n  bool notification = false;\n  common::MutexLocker locker(&mutex_);\n  const int num_finished_scans_at_start =\n      constraint_builder_.GetNumFinishedScans();\n  while (!locker.AwaitWithTimeout(\n      [this]() REQUIRES(mutex_) {\n        return constraint_builder_.GetNumFinishedScans() ==\n               num_trajectory_nodes_;\n      },\n      common::FromSeconds(1.))) {\n    std::ostringstream progress_info;\n    progress_info << \"Optimizing: \" << std::fixed << std::setprecision(1)\n                  << 100. *\n                         (constraint_builder_.GetNumFinishedScans() -\n                          num_finished_scans_at_start) /\n                         (num_trajectory_nodes_ - num_finished_scans_at_start)\n                  << \"%...\";\n    std::cout << \"\\r\\x1b[K\" << progress_info.str() << std::flush;\n  }\n  std::cout << \"\\r\\x1b[KOptimizing: Done.     \" << std::endl;\n  constraint_builder_.WhenDone(\n      [this, &notification](\n          const sparse_pose_graph::ConstraintBuilder::Result& result) {\n        common::MutexLocker locker(&mutex_);\n        constraints_.insert(constraints_.end(), result.begin(), result.end());\n        notification = true;\n      });\n  locker.Await([&notification]() { return notification; });\n}\n\nvoid SparsePoseGraph::RunFinalOptimization() {\n  WaitForAllComputations();\n  optimization_problem_.SetMaxNumIterations(\n      options_.max_num_final_iterations());\n  RunOptimization();\n  optimization_problem_.SetMaxNumIterations(\n      options_.optimization_problem_options()\n          .ceres_solver_options()\n          .max_num_iterations());\n}\n\nvoid SparsePoseGraph::RunOptimization() {\n  if (optimization_problem_.submap_data().empty()) {\n    return;\n  }\n  optimization_problem_.Solve(constraints_);\n  common::MutexLocker locker(&mutex_);\n\n  const auto& node_data = optimization_problem_.node_data();\n  for (int trajectory_id = 0;\n       trajectory_id != static_cast<int>(node_data.size()); ++trajectory_id) {\n    int node_index = 0;\n    const int num_nodes = trajectory_nodes_.num_indices(trajectory_id);\n    for (; node_index != static_cast<int>(node_data[trajectory_id].size());\n         ++node_index) {\n      const mapping::NodeId node_id{trajectory_id, node_index};\n      trajectory_nodes_.at(node_id).pose = transform::Embed3D(\n          node_data[trajectory_id][node_index].point_cloud_pose);\n    }\n    // Extrapolate all point cloud poses that were added later.\n    const auto new_submap_transforms = ExtrapolateSubmapTransforms(\n        optimization_problem_.submap_data(), trajectory_id);\n    const auto old_submap_transforms = ExtrapolateSubmapTransforms(\n        optimized_submap_transforms_, trajectory_id);\n    CHECK_EQ(new_submap_transforms.size(), old_submap_transforms.size());\n    const transform::Rigid3d extrapolation_transform =\n        new_submap_transforms.back() * old_submap_transforms.back().inverse();\n    for (; node_index < num_nodes; ++node_index) {\n      const mapping::NodeId node_id{trajectory_id, node_index};\n      trajectory_nodes_.at(node_id).pose =\n          extrapolation_transform * trajectory_nodes_.at(node_id).pose;\n    }\n  }\n  optimized_submap_transforms_ = optimization_problem_.submap_data();\n  connected_components_ = trajectory_connectivity_.ConnectedComponents();\n  reverse_connected_components_.clear();\n  for (size_t i = 0; i != connected_components_.size(); ++i) {\n    for (const int trajectory_id : connected_components_[i]) {\n      reverse_connected_components_.emplace(trajectory_id, i);\n    }\n  }\n}\n\nstd::vector<std::vector<mapping::TrajectoryNode>>\nSparsePoseGraph::GetTrajectoryNodes() {\n  common::MutexLocker locker(&mutex_);\n  return trajectory_nodes_.data();\n}\n\nstd::vector<SparsePoseGraph::Constraint> SparsePoseGraph::constraints() {\n  common::MutexLocker locker(&mutex_);\n  return constraints_;\n}\n\ntransform::Rigid3d SparsePoseGraph::GetLocalToGlobalTransform(\n    const int trajectory_id) {\n  common::MutexLocker locker(&mutex_);\n  if (submap_states_.num_trajectories() <= trajectory_id ||\n      submap_states_.num_indices(trajectory_id) == 0) {\n    return transform::Rigid3d::Identity();\n  }\n  const auto extrapolated_submap_transforms =\n      ExtrapolateSubmapTransforms(optimized_submap_transforms_, trajectory_id);\n  return extrapolated_submap_transforms.back() *\n         submap_states_\n             .at(mapping::SubmapId{\n                 trajectory_id,\n                 static_cast<int>(extrapolated_submap_transforms.size()) - 1})\n             .submap->local_pose.inverse();\n}\n\nstd::vector<std::vector<int>> SparsePoseGraph::GetConnectedTrajectories() {\n  common::MutexLocker locker(&mutex_);\n  return connected_components_;\n}\n\nstd::vector<transform::Rigid3d> SparsePoseGraph::GetSubmapTransforms(\n    const int trajectory_id) {\n  common::MutexLocker locker(&mutex_);\n  return ExtrapolateSubmapTransforms(optimized_submap_transforms_,\n                                     trajectory_id);\n}\n\nstd::vector<transform::Rigid3d> SparsePoseGraph::ExtrapolateSubmapTransforms(\n    const std::vector<std::vector<sparse_pose_graph::SubmapData>>&\n        submap_transforms,\n    const int trajectory_id) const {\n  if (trajectory_id >= submap_states_.num_trajectories()) {\n    return {transform::Rigid3d::Identity()};\n  }\n\n  // Submaps for which we have optimized poses.\n  std::vector<transform::Rigid3d> result;\n  for (int submap_index = 0;\n       submap_index != submap_states_.num_indices(trajectory_id);\n       ++submap_index) {\n    const mapping::SubmapId submap_id{trajectory_id, submap_index};\n    const auto& state = submap_states_.at(submap_id);\n    if (static_cast<size_t>(trajectory_id) < submap_transforms.size() &&\n        result.size() < submap_transforms.at(trajectory_id).size()) {\n      // Submaps for which we have optimized poses.\n      result.push_back(\n          Embed3D(submap_transforms.at(trajectory_id).at(result.size()).pose));\n    } else if (result.empty()) {\n      result.push_back(transform::Rigid3d::Identity());\n    } else {\n      // Extrapolate to the remaining submaps. Accessing 'local_pose' in Submaps\n      // is okay, since the member is const.\n      const mapping::SubmapId previous_submap_id{\n          trajectory_id, static_cast<int>(result.size()) - 1};\n      result.push_back(\n          result.back() *\n          submap_states_.at(previous_submap_id).submap->local_pose.inverse() *\n          state.submap->local_pose);\n    }\n  }\n\n  if (result.empty()) {\n    result.push_back(transform::Rigid3d::Identity());\n  }\n  return result;\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_H_\n#define CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_H_\n\n#include <deque>\n#include <functional>\n#include <limits>\n#include <map>\n#include <unordered_map>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n#include \"cartographer/mapping_2d/sparse_pose_graph/constraint_builder.h\"\n#include \"cartographer/mapping_2d/sparse_pose_graph/optimization_problem.h\"\n#include \"cartographer/mapping_2d/submaps.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\n// Implements the loop closure method called Sparse Pose Adjustment (SPA) from\n// Konolige, Kurt, et al. \"Efficient sparse pose adjustment for 2d mapping.\"\n// Intelligent Robots and Systems (IROS), 2010 IEEE/RSJ International Conference\n// on (pp. 22--29). IEEE, 2010.\n//\n// It is extended for submapping:\n// Each scan has been matched against one or more submaps (adding a constraint\n// for each match), both poses of scans and of submaps are to be optimized.\n// All constraints are between a submap i and a scan j.\nclass SparsePoseGraph : public mapping::SparsePoseGraph {\n public:\n  SparsePoseGraph(const mapping::proto::SparsePoseGraphOptions& options,\n                  common::ThreadPool* thread_pool);\n  ~SparsePoseGraph() override;\n\n  SparsePoseGraph(const SparsePoseGraph&) = delete;\n  SparsePoseGraph& operator=(const SparsePoseGraph&) = delete;\n\n  // Adds a new 'range_data_in_pose' observation at 'time', and a 'pose'\n  // that will later be optimized. The 'tracking_to_pose' is remembered so\n  // that the optimized pose can be embedded into 3D. The 'pose' was determined\n  // by scan matching against the 'matching_submap' and the scan was inserted\n  // into the 'insertion_submaps'.\n  void AddScan(common::Time time, const transform::Rigid3d& tracking_to_pose,\n               const sensor::RangeData& range_data_in_pose,\n               const transform::Rigid2d& pose, int trajectory_id,\n               const mapping::Submap* matching_submap,\n               const std::vector<const mapping::Submap*>& insertion_submaps)\n      EXCLUDES(mutex_);\n\n  // Adds new IMU data to be used in the optimization.\n  void AddImuData(int trajectory_id, common::Time time,\n                  const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity);\n\n  void RunFinalOptimization() override;\n  std::vector<std::vector<int>> GetConnectedTrajectories() override;\n  std::vector<transform::Rigid3d> GetSubmapTransforms(int trajectory_id)\n      EXCLUDES(mutex_) override;\n  transform::Rigid3d GetLocalToGlobalTransform(int trajectory_id)\n      EXCLUDES(mutex_) override;\n  std::vector<std::vector<mapping::TrajectoryNode>> GetTrajectoryNodes()\n      override EXCLUDES(mutex_);\n  std::vector<Constraint> constraints() override EXCLUDES(mutex_);\n\n private:\n  struct SubmapState {\n    const mapping::Submap* submap = nullptr;\n\n    // IDs of the scans that were inserted into this map together with\n    // constraints for them. They are not to be matched again when this submap\n    // becomes 'finished'.\n    std::set<mapping::NodeId> node_ids;\n\n    // Whether in the current state of the background thread this submap is\n    // finished. When this transitions to true, all scans are tried to match\n    // against this submap. Likewise, all new scans are matched against submaps\n    // which are finished.\n    bool finished = false;\n  };\n\n  // Handles a new work item.\n  void AddWorkItem(std::function<void()> work_item) REQUIRES(mutex_);\n\n  mapping::SubmapId GetSubmapId(const mapping::Submap* submap) const\n      REQUIRES(mutex_) {\n    const auto iterator = submap_ids_.find(submap);\n    CHECK(iterator != submap_ids_.end());\n    return iterator->second;\n  }\n\n  // Grows the optimization problem to have an entry for every element of\n  // 'insertion_submaps'.\n  void GrowSubmapTransformsAsNeeded(\n      const std::vector<const mapping::Submap*>& insertion_submaps)\n      REQUIRES(mutex_);\n\n  // Adds constraints for a scan, and starts scan matching in the background.\n  void ComputeConstraintsForScan(\n      const mapping::Submap* matching_submap,\n      std::vector<const mapping::Submap*> insertion_submaps,\n      const mapping::Submap* finished_submap, const transform::Rigid2d& pose)\n      REQUIRES(mutex_);\n\n  // Computes constraints for a scan and submap pair.\n  void ComputeConstraint(const mapping::NodeId& node_id,\n                         const mapping::SubmapId& submap_id) REQUIRES(mutex_);\n\n  // Adds constraints for older scans whenever a new submap is finished.\n  void ComputeConstraintsForOldScans(const mapping::Submap* submap)\n      REQUIRES(mutex_);\n\n  // Registers the callback to run the optimization once all constraints have\n  // been computed, that will also do all work that queue up in 'scan_queue_'.\n  void HandleScanQueue() REQUIRES(mutex_);\n\n  // Waits until we caught up (i.e. nothing is waiting to be scheduled), and\n  // all computations have finished.\n  void WaitForAllComputations() EXCLUDES(mutex_);\n\n  // Runs the optimization. Callers have to make sure, that there is only one\n  // optimization being run at a time.\n  void RunOptimization() EXCLUDES(mutex_);\n\n  // Adds extrapolated transforms, so that there are transforms for all submaps.\n  std::vector<transform::Rigid3d> ExtrapolateSubmapTransforms(\n      const std::vector<std::vector<sparse_pose_graph::SubmapData>>&\n          submap_transforms,\n      int trajectory_id) const REQUIRES(mutex_);\n\n  const mapping::proto::SparsePoseGraphOptions options_;\n  common::Mutex mutex_;\n\n  // If it exists, further scans must be added to this queue, and will be\n  // considered later.\n  std::unique_ptr<std::deque<std::function<void()>>> scan_queue_\n      GUARDED_BY(mutex_);\n\n  // How our various trajectories are related.\n  mapping::TrajectoryConnectivity trajectory_connectivity_ GUARDED_BY(mutex_);\n\n  // We globally localize a fraction of the scans from each trajectory.\n  std::unordered_map<int, std::unique_ptr<common::FixedRatioSampler>>\n      global_localization_samplers_ GUARDED_BY(mutex_);\n\n  // Number of scans added since last loop closure.\n  int num_scans_since_last_loop_closure_ GUARDED_BY(mutex_) = 0;\n\n  // Whether the optimization has to be run before more data is added.\n  bool run_loop_closure_ GUARDED_BY(mutex_) = false;\n\n  // Current optimization problem.\n  sparse_pose_graph::OptimizationProblem optimization_problem_;\n  sparse_pose_graph::ConstraintBuilder constraint_builder_ GUARDED_BY(mutex_);\n  std::vector<Constraint> constraints_ GUARDED_BY(mutex_);\n\n  // Submaps get assigned an ID and state as soon as they are seen, even\n  // before they take part in the background computations.\n  std::map<const mapping::Submap*, mapping::SubmapId> submap_ids_\n      GUARDED_BY(mutex_);\n  mapping::NestedVectorsById<SubmapState, mapping::SubmapId> submap_states_\n      GUARDED_BY(mutex_);\n\n  // Connectivity structure of our trajectories by IDs.\n  std::vector<std::vector<int>> connected_components_;\n  // Trajectory ID to connected component ID.\n  std::map<int, size_t> reverse_connected_components_;\n\n  // Data that are currently being shown.\n  //\n  // Deque to keep references valid for the background computation when adding\n  // new data.\n  std::deque<mapping::TrajectoryNode::ConstantData> constant_node_data_;\n  mapping::NestedVectorsById<mapping::TrajectoryNode, mapping::NodeId>\n      trajectory_nodes_ GUARDED_BY(mutex_);\n  int num_trajectory_nodes_ GUARDED_BY(mutex_) = 0;\n\n  // Current submap transforms used for displaying data.\n  std::vector<std::vector<sparse_pose_graph::SubmapData>>\n      optimized_submap_transforms_ GUARDED_BY(mutex_);\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SPARSE_POSE_GRAPH_H_\n"
  },
  {
    "path": "mapping_2d/sparse_pose_graph_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/sparse_pose_graph.h\"\n\n#include <cmath>\n#include <memory>\n#include <random>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n#include \"cartographer/mapping_2d/submaps.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nclass SparsePoseGraphTest : public ::testing::Test {\n protected:\n  SparsePoseGraphTest() : thread_pool_(1) {\n    // Builds a wavy, irregularly circular point cloud that is unique\n    // rotationally. This gives us good rotational texture and avoids any\n    // possibility of the CeresScanMatcher preferring free space (>\n    // kMinProbability) to unknown space (== kMinProbability).\n    for (float t = 0.f; t < 2.f * M_PI; t += 0.005f) {\n      const float r = (std::sin(20.f * t) + 2.f) * std::sin(t + 2.f);\n      point_cloud_.emplace_back(r * std::sin(t), r * std::cos(t), 0.f);\n    }\n\n    {\n      auto parameter_dictionary = common::MakeDictionary(R\"text(\n          return {\n            resolution = 0.05,\n            half_length = 21.,\n            num_range_data = 1,\n            output_debug_images = false,\n            range_data_inserter = {\n              insert_free_space = true,\n              hit_probability = 0.53,\n              miss_probability = 0.495,\n            },\n          })text\");\n      submaps_ = common::make_unique<Submaps>(\n          CreateSubmapsOptions(parameter_dictionary.get()));\n    }\n\n    {\n      auto parameter_dictionary = common::MakeDictionary(R\"text(\n          return {\n            optimize_every_n_scans = 1000,\n            constraint_builder = {\n              sampling_ratio = 1.,\n              max_constraint_distance = 6.,\n              adaptive_voxel_filter = {\n                max_length = 1e-2,\n                min_num_points = 1000,\n                max_range = 50.,\n              },\n              min_score = 0.5,\n              global_localization_min_score = 0.6,\n              loop_closure_translation_weight = 1.,\n              loop_closure_rotation_weight = 1.,\n              log_matches = true,\n              fast_correlative_scan_matcher = {\n                linear_search_window = 3.,\n                angular_search_window = 0.1,\n                branch_and_bound_depth = 3,\n              },\n              ceres_scan_matcher = {\n                occupied_space_weight = 20.,\n                translation_weight = 10.,\n                rotation_weight = 1.,\n                ceres_solver_options = {\n                  use_nonmonotonic_steps = true,\n                  max_num_iterations = 50,\n                  num_threads = 1,\n                },\n              },\n              fast_correlative_scan_matcher_3d = {\n                branch_and_bound_depth = 3,\n                full_resolution_depth = 3,\n                rotational_histogram_size = 30,\n                min_rotational_score = 0.1,\n                linear_xy_search_window = 4.,\n                linear_z_search_window = 4.,\n                angular_search_window = 0.1,\n              },\n              ceres_scan_matcher_3d = {\n                occupied_space_weight_0 = 20.,\n                translation_weight = 10.,\n                rotation_weight = 1.,\n                only_optimize_yaw = true,\n                ceres_solver_options = {\n                  use_nonmonotonic_steps = true,\n                  max_num_iterations = 50,\n                  num_threads = 1,\n                },\n              },\n            },\n            matcher_translation_weight = 1.,\n            matcher_rotation_weight = 1.,\n            optimization_problem = {\n              acceleration_weight = 1.,\n              rotation_weight = 1e2,\n              huber_scale = 1.,\n              consecutive_scan_translation_penalty_factor = 0.,\n              consecutive_scan_rotation_penalty_factor = 0.,\n              log_solver_summary = true,\n              ceres_solver_options = {\n                use_nonmonotonic_steps = false,\n                max_num_iterations = 200,\n                num_threads = 1,\n              },\n            },\n            max_num_final_iterations = 200,\n            global_sampling_ratio = 0.01,\n          })text\");\n      sparse_pose_graph_ = common::make_unique<SparsePoseGraph>(\n          mapping::CreateSparsePoseGraphOptions(parameter_dictionary.get()),\n          &thread_pool_);\n    }\n\n    current_pose_ = transform::Rigid2d::Identity();\n  }\n\n  void MoveRelativeWithNoise(const transform::Rigid2d& movement,\n                             const transform::Rigid2d& noise) {\n    current_pose_ = current_pose_ * movement;\n    const sensor::PointCloud new_point_cloud = sensor::TransformPointCloud(\n        point_cloud_,\n        transform::Embed3D(current_pose_.inverse().cast<float>()));\n    const mapping::Submap* const matching_submap =\n        submaps_->Get(submaps_->matching_index());\n    std::vector<const mapping::Submap*> insertion_submaps;\n    for (int insertion_index : submaps_->insertion_indices()) {\n      insertion_submaps.push_back(submaps_->Get(insertion_index));\n    }\n    const sensor::RangeData range_data{\n        Eigen::Vector3f::Zero(), new_point_cloud, {}};\n    const transform::Rigid2d pose_estimate = noise * current_pose_;\n    constexpr int kTrajectoryId = 0;\n    submaps_->InsertRangeData(TransformRangeData(\n        range_data, transform::Embed3D(pose_estimate.cast<float>())));\n    sparse_pose_graph_->AddScan(\n        common::FromUniversal(0), transform::Rigid3d::Identity(), range_data,\n        pose_estimate, kTrajectoryId, matching_submap, insertion_submaps);\n  }\n\n  void MoveRelative(const transform::Rigid2d& movement) {\n    MoveRelativeWithNoise(movement, transform::Rigid2d::Identity());\n  }\n\n  sensor::PointCloud point_cloud_;\n  std::unique_ptr<Submaps> submaps_;\n  common::ThreadPool thread_pool_;\n  std::unique_ptr<SparsePoseGraph> sparse_pose_graph_;\n  transform::Rigid2d current_pose_;\n};\n\nTEST_F(SparsePoseGraphTest, EmptyMap) {\n  sparse_pose_graph_->RunFinalOptimization();\n  const auto nodes = sparse_pose_graph_->GetTrajectoryNodes();\n  EXPECT_THAT(nodes.size(), ::testing::Eq(0));\n}\n\nTEST_F(SparsePoseGraphTest, NoMovement) {\n  MoveRelative(transform::Rigid2d::Identity());\n  MoveRelative(transform::Rigid2d::Identity());\n  MoveRelative(transform::Rigid2d::Identity());\n  sparse_pose_graph_->RunFinalOptimization();\n  const auto nodes = sparse_pose_graph_->GetTrajectoryNodes();\n  ASSERT_THAT(nodes.size(), ::testing::Eq(1));\n  EXPECT_THAT(nodes[0].size(), ::testing::Eq(3));\n  EXPECT_THAT(nodes[0][0].pose,\n              transform::IsNearly(transform::Rigid3d::Identity(), 1e-2));\n  EXPECT_THAT(nodes[0][1].pose,\n              transform::IsNearly(transform::Rigid3d::Identity(), 1e-2));\n  EXPECT_THAT(nodes[0][2].pose,\n              transform::IsNearly(transform::Rigid3d::Identity(), 1e-2));\n}\n\nTEST_F(SparsePoseGraphTest, NoOverlappingScans) {\n  std::mt19937 rng(0);\n  std::uniform_real_distribution<double> distribution(-1., 1.);\n  std::vector<transform::Rigid2d> poses;\n  for (int i = 0; i != 4; ++i) {\n    MoveRelative(transform::Rigid2d({0.25 * distribution(rng), 5.}, 0.));\n    poses.emplace_back(current_pose_);\n  }\n  sparse_pose_graph_->RunFinalOptimization();\n  const auto nodes = sparse_pose_graph_->GetTrajectoryNodes();\n  ASSERT_THAT(nodes.size(), ::testing::Eq(1));\n  for (int i = 0; i != 4; ++i) {\n    EXPECT_THAT(poses[i],\n                IsNearly(transform::Project2D(nodes[0][i].pose), 1e-2))\n        << i;\n  }\n}\n\nTEST_F(SparsePoseGraphTest, ConsecutivelyOverlappingScans) {\n  std::mt19937 rng(0);\n  std::uniform_real_distribution<double> distribution(-1., 1.);\n  std::vector<transform::Rigid2d> poses;\n  for (int i = 0; i != 5; ++i) {\n    MoveRelative(transform::Rigid2d({0.25 * distribution(rng), 2.}, 0.));\n    poses.emplace_back(current_pose_);\n  }\n  sparse_pose_graph_->RunFinalOptimization();\n  const auto nodes = sparse_pose_graph_->GetTrajectoryNodes();\n  ASSERT_THAT(nodes.size(), ::testing::Eq(1));\n  for (int i = 0; i != 5; ++i) {\n    EXPECT_THAT(poses[i],\n                IsNearly(transform::Project2D(nodes[0][i].pose), 1e-2))\n        << i;\n  }\n}\n\nTEST_F(SparsePoseGraphTest, OverlappingScans) {\n  std::mt19937 rng(0);\n  std::uniform_real_distribution<double> distribution(-1., 1.);\n  std::vector<transform::Rigid2d> ground_truth;\n  std::vector<transform::Rigid2d> poses;\n  for (int i = 0; i != 5; ++i) {\n    const double noise_x = 0.1 * distribution(rng);\n    const double noise_y = 0.1 * distribution(rng);\n    const double noise_orientation = 0.1 * distribution(rng);\n    transform::Rigid2d noise({noise_x, noise_y}, noise_orientation);\n    MoveRelativeWithNoise(\n        transform::Rigid2d({0.15 * distribution(rng), 0.4}, 0.), noise);\n    ground_truth.emplace_back(current_pose_);\n    poses.emplace_back(noise * current_pose_);\n  }\n  sparse_pose_graph_->RunFinalOptimization();\n  const auto nodes = sparse_pose_graph_->GetTrajectoryNodes();\n  ASSERT_THAT(nodes.size(), ::testing::Eq(1));\n  transform::Rigid2d true_movement =\n      ground_truth.front().inverse() * ground_truth.back();\n  transform::Rigid2d movement_before = poses.front().inverse() * poses.back();\n  transform::Rigid2d error_before = movement_before.inverse() * true_movement;\n  transform::Rigid3d optimized_movement =\n      nodes[0].front().pose.inverse() * nodes[0].back().pose;\n  transform::Rigid2d optimized_error =\n      transform::Project2D(optimized_movement).inverse() * true_movement;\n  EXPECT_THAT(std::abs(optimized_error.normalized_angle()),\n              ::testing::Lt(std::abs(error_before.normalized_angle())));\n  EXPECT_THAT(optimized_error.translation().norm(),\n              ::testing::Lt(error_before.translation().norm()));\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/submaps.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/submaps.h\"\n\n#include <cinttypes>\n#include <cmath>\n#include <cstdlib>\n#include <fstream>\n#include <limits>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/port.h\"\n#include \"glog/logging.h\"\n#include \"webp/encode.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nnamespace {\n\nvoid WriteDebugImage(const string& filename,\n                     const ProbabilityGrid& probability_grid) {\n  constexpr int kUnknown = 128;\n  const mapping_2d::CellLimits& cell_limits =\n      probability_grid.limits().cell_limits();\n  const int width = cell_limits.num_x_cells;\n  const int height = cell_limits.num_y_cells;\n  std::vector<uint8_t> rgb;\n  for (const Eigen::Array2i& xy_index : mapping_2d::XYIndexRangeIterator(\n           probability_grid.limits().cell_limits())) {\n    CHECK(probability_grid.limits().Contains(xy_index));\n    const uint8_t value =\n        probability_grid.IsKnown(xy_index)\n            ? common::RoundToInt(\n                  (1. - probability_grid.GetProbability(xy_index)) * 255 + 0)\n            : kUnknown;\n    rgb.push_back(value);\n    rgb.push_back(value);\n    rgb.push_back(value);\n  }\n  uint8_t* output = nullptr;\n  size_t output_size =\n      WebPEncodeLosslessRGB(rgb.data(), width, height, 3 * width, &output);\n  std::unique_ptr<uint8_t, void (*)(void*)> output_deleter(output, std::free);\n  std::ofstream output_file(filename, std::ios::out | std::ios::binary);\n  output_file.write(reinterpret_cast<char*>(output), output_size);\n  output_file.close();\n  CHECK(output_file) << \"Writing \" << filename << \" failed.\";\n}\n\n}  // namespace\n\nProbabilityGrid ComputeCroppedProbabilityGrid(\n    const ProbabilityGrid& probability_grid) {\n  Eigen::Array2i offset;\n  CellLimits limits;\n  probability_grid.ComputeCroppedLimits(&offset, &limits);\n  const double resolution = probability_grid.limits().resolution();\n  const Eigen::Vector2d max =\n      probability_grid.limits().max() -\n      resolution * Eigen::Vector2d(offset.y(), offset.x());\n  ProbabilityGrid cropped_grid(MapLimits(resolution, max, limits));\n  cropped_grid.StartUpdate();\n  for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(limits)) {\n    if (probability_grid.IsKnown(xy_index + offset)) {\n      cropped_grid.SetProbability(\n          xy_index, probability_grid.GetProbability(xy_index + offset));\n    }\n  }\n  return cropped_grid;\n}\n\nproto::SubmapsOptions CreateSubmapsOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::SubmapsOptions options;\n  options.set_resolution(parameter_dictionary->GetDouble(\"resolution\"));\n  options.set_half_length(parameter_dictionary->GetDouble(\"half_length\"));\n  options.set_num_range_data(\n      parameter_dictionary->GetNonNegativeInt(\"num_range_data\"));\n  options.set_output_debug_images(\n      parameter_dictionary->GetBool(\"output_debug_images\"));\n  *options.mutable_range_data_inserter_options() =\n      CreateRangeDataInserterOptions(\n          parameter_dictionary->GetDictionary(\"range_data_inserter\").get());\n  CHECK_GT(options.num_range_data(), 0);\n  return options;\n}\n\nSubmap::Submap(const MapLimits& limits, const Eigen::Vector2f& origin)\n    : mapping::Submap(transform::Rigid3d::Translation(\n          Eigen::Vector3d(origin.x(), origin.y(), 0.))),\n      probability_grid(limits) {}\n\nSubmaps::Submaps(const proto::SubmapsOptions& options)\n    : options_(options),\n      range_data_inserter_(options.range_data_inserter_options()) {\n  // We always want to have at least one likelihood field which we can return,\n  // and will create it at the origin in absence of a better choice.\n  AddSubmap(Eigen::Vector2f::Zero());\n}\n\nvoid Submaps::InsertRangeData(const sensor::RangeData& range_data) {\n  for (const int index : insertion_indices()) {\n    Submap* submap = submaps_[index].get();\n    CHECK(submap->finished_probability_grid == nullptr);\n    range_data_inserter_.Insert(range_data, &submap->probability_grid);\n    ++submap->num_range_data;\n  }\n  const Submap* const last_submap = Get(size() - 1);\n  if (last_submap->num_range_data == options_.num_range_data()) {\n    AddSubmap(range_data.origin.head<2>());\n  }\n}\n\nconst Submap* Submaps::Get(int index) const {\n  CHECK_GE(index, 0);\n  CHECK_LT(index, size());\n  return submaps_[index].get();\n}\n\nint Submaps::size() const { return submaps_.size(); }\n\nvoid Submaps::SubmapToProto(\n    const int index, const transform::Rigid3d&,\n    mapping::proto::SubmapQuery::Response* const response) const {\n  AddProbabilityGridToResponse(Get(index)->local_pose,\n                               Get(index)->probability_grid, response);\n}\n\nvoid Submaps::FinishSubmap(int index) {\n  // Crop the finished Submap before inserting a new Submap to reduce peak\n  // memory usage a bit.\n  Submap* submap = submaps_[index].get();\n  CHECK(submap->finished_probability_grid == nullptr);\n  submap->probability_grid =\n      ComputeCroppedProbabilityGrid(submap->probability_grid);\n  submap->finished_probability_grid = &submap->probability_grid;\n  if (options_.output_debug_images()) {\n    // Output the Submap that won't be changed from now on.\n    WriteDebugImage(\"submap\" + std::to_string(index) + \".webp\",\n                    submap->probability_grid);\n  }\n}\n\nvoid Submaps::AddSubmap(const Eigen::Vector2f& origin) {\n  if (size() > 1) {\n    FinishSubmap(size() - 2);\n  }\n  const int num_cells_per_dimension =\n      common::RoundToInt(2. * options_.half_length() / options_.resolution()) +\n      1;\n  submaps_.push_back(common::make_unique<Submap>(\n      MapLimits(options_.resolution(),\n                origin.cast<double>() +\n                    options_.half_length() * Eigen::Vector2d::Ones(),\n                CellLimits(num_cells_per_dimension, num_cells_per_dimension)),\n      origin));\n  LOG(INFO) << \"Added submap \" << size();\n}\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/submaps.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_SUBMAPS_H_\n#define CARTOGRAPHER_MAPPING_2D_SUBMAPS_H_\n\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping/proto/submap_visualization.pb.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/mapping_2d/map_limits.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/proto/submaps_options.pb.h\"\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nProbabilityGrid ComputeCroppedProbabilityGrid(\n    const ProbabilityGrid& probability_grid);\n\nproto::SubmapsOptions CreateSubmapsOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nstruct Submap : public mapping::Submap {\n  Submap(const MapLimits& limits, const Eigen::Vector2f& origin);\n\n  ProbabilityGrid probability_grid;\n};\n\n// A container of Submaps.\nclass Submaps : public mapping::Submaps {\n public:\n  explicit Submaps(const proto::SubmapsOptions& options);\n\n  Submaps(const Submaps&) = delete;\n  Submaps& operator=(const Submaps&) = delete;\n\n  const Submap* Get(int index) const override;\n  int size() const override;\n  void SubmapToProto(\n      int index, const transform::Rigid3d& global_submap_pose,\n      mapping::proto::SubmapQuery::Response* response) const override;\n\n  // Inserts 'range_data' into the Submap collection.\n  void InsertRangeData(const sensor::RangeData& range_data);\n\n private:\n  void FinishSubmap(int index);\n  void AddSubmap(const Eigen::Vector2f& origin);\n\n  const proto::SubmapsOptions options_;\n\n  std::vector<std::unique_ptr<Submap>> submaps_;\n  RangeDataInserter range_data_inserter_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_SUBMAPS_H_\n"
  },
  {
    "path": "mapping_2d/submaps_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/submaps.h\"\n\n#include <map>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nTEST(SubmapsTest, TheRightNumberOfScansAreInserted) {\n  constexpr int kNumRangeData = 10;\n  auto parameter_dictionary = common::MakeDictionary(\n      \"return {\"\n      \"resolution = 0.05, \"\n      \"half_length = 10., \"\n      \"num_range_data = \" +\n      std::to_string(kNumRangeData) +\n      \", \"\n      \"output_debug_images = false, \"\n      \"range_data_inserter = {\"\n      \"insert_free_space = true, \"\n      \"hit_probability = 0.53, \"\n      \"miss_probability = 0.495, \"\n      \"},\"\n      \"}\");\n  Submaps submaps{CreateSubmapsOptions(parameter_dictionary.get())};\n  for (int i = 0; i != 1000; ++i) {\n    submaps.InsertRangeData({Eigen::Vector3f::Zero(), {}, {}});\n    const int matching = submaps.matching_index();\n    // Except for the first, maps should only be returned after enough scans.\n    if (matching != 0) {\n      EXPECT_LE(kNumRangeData, submaps.Get(matching)->num_range_data);\n    }\n  }\n  for (int i = 0; i != submaps.size() - 2; ++i) {\n    // Submaps should not be left without the right number of scans in them.\n    EXPECT_EQ(kNumRangeData * 2, submaps.Get(i)->num_range_data);\n  }\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_2d/xy_index.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_2D_XY_INDEX_H_\n#define CARTOGRAPHER_MAPPING_2D_XY_INDEX_H_\n\n#include <algorithm>\n#include <cmath>\n#include <iostream>\n#include <iterator>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping_2d/proto/cell_limits.pb.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\n\nstruct CellLimits {\n  CellLimits() = default;\n  CellLimits(int init_num_x_cells, int init_num_y_cells)\n      : num_x_cells(init_num_x_cells), num_y_cells(init_num_y_cells) {}\n\n  explicit CellLimits(const proto::CellLimits& cell_limits)\n      : num_x_cells(cell_limits.num_x_cells()),\n        num_y_cells(cell_limits.num_y_cells()) {}\n\n  int num_x_cells = 0;\n  int num_y_cells = 0;\n};\n\ninline proto::CellLimits ToProto(const CellLimits& cell_limits) {\n  proto::CellLimits result;\n  result.set_num_x_cells(cell_limits.num_x_cells);\n  result.set_num_y_cells(cell_limits.num_y_cells);\n  return result;\n}\n\n// Iterates in row-major order through a range of xy-indices.\nclass XYIndexRangeIterator\n    : public std::iterator<std::input_iterator_tag, Eigen::Array2i> {\n public:\n  // Constructs a new iterator for the specified range.\n  XYIndexRangeIterator(const Eigen::Array2i& min_xy_index,\n                       const Eigen::Array2i& max_xy_index)\n      : min_xy_index_(min_xy_index),\n        max_xy_index_(max_xy_index),\n        xy_index_(min_xy_index) {}\n\n  // Constructs a new iterator for everything contained in 'cell_limits'.\n  explicit XYIndexRangeIterator(const CellLimits& cell_limits)\n      : XYIndexRangeIterator(Eigen::Array2i::Zero(),\n                             Eigen::Array2i(cell_limits.num_x_cells - 1,\n                                            cell_limits.num_y_cells - 1)) {}\n\n  XYIndexRangeIterator& operator++() {\n    // This is a necessary evil. Bounds checking is very expensive and needs to\n    // be avoided in production. We have unit tests that exercise this check\n    // in debug mode.\n    DCHECK(*this != end());\n    if (xy_index_.x() < max_xy_index_.x()) {\n      ++xy_index_.x();\n    } else {\n      xy_index_.x() = min_xy_index_.x();\n      ++xy_index_.y();\n    }\n    return *this;\n  }\n\n  Eigen::Array2i& operator*() { return xy_index_; }\n\n  bool operator==(const XYIndexRangeIterator& other) const {\n    return (xy_index_ == other.xy_index_).all();\n  }\n\n  bool operator!=(const XYIndexRangeIterator& other) const {\n    return !operator==(other);\n  }\n\n  XYIndexRangeIterator begin() {\n    return XYIndexRangeIterator(min_xy_index_, max_xy_index_);\n  }\n\n  XYIndexRangeIterator end() {\n    XYIndexRangeIterator it = begin();\n    it.xy_index_ = Eigen::Array2i(min_xy_index_.x(), max_xy_index_.y() + 1);\n    return it;\n  }\n\n private:\n  Eigen::Array2i min_xy_index_;\n  Eigen::Array2i max_xy_index_;\n  Eigen::Array2i xy_index_;\n};\n\n}  // namespace mapping_2d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_2D_XY_INDEX_H_\n"
  },
  {
    "path": "mapping_2d/xy_index_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_2d/xy_index.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_2d {\nnamespace {\n\nTEST(XYIndexTest, CellLimitsToProto) {\n  const CellLimits limits(1, 2);\n  const auto proto = ToProto(limits);\n  EXPECT_EQ(limits.num_x_cells, proto.num_x_cells());\n  EXPECT_EQ(limits.num_y_cells, proto.num_y_cells());\n}\n\nTEST(XYIndexTest, CellLimitsProtoConstructor) {\n  proto::CellLimits limits;\n  limits.set_num_x_cells(1);\n  limits.set_num_y_cells(2);\n\n  auto native = CellLimits(limits);\n  EXPECT_EQ(limits.num_x_cells(), native.num_x_cells);\n  EXPECT_EQ(limits.num_y_cells(), native.num_y_cells);\n}\n\nTEST(XYIndexTest, XYIndexRangeIterator) {\n  const Eigen::Array2i min(1, 2);\n  const Eigen::Array2i max(3, 4);\n  XYIndexRangeIterator it(min, max);\n  EXPECT_TRUE((min == *it.begin()).all()) << *it.begin();\n  EXPECT_TRUE((Eigen::Array2i(1, 5) == *it.end()).all()) << *it.end();\n  EXPECT_TRUE((min == *it).all()) << *it;\n  int num_indices = 0;\n  for (const Eigen::Array2i& xy_index : XYIndexRangeIterator(min, max)) {\n    LOG(INFO) << xy_index;\n    EXPECT_TRUE((xy_index >= min).all());\n    EXPECT_TRUE((xy_index <= max).all());\n    ++num_indices;\n  }\n  EXPECT_EQ(9, num_indices);\n}\n\n}  // namespace\n}  // namespace mapping_2d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/acceleration_cost_function.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_ACCELERATION_COST_FUNCTION_H_\n#define CARTOGRAPHER_MAPPING_3D_ACCELERATION_COST_FUNCTION_H_\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Penalizes differences between IMU data and optimized accelerations.\nclass AccelerationCostFunction {\n public:\n  AccelerationCostFunction(const double scaling_factor,\n                           const Eigen::Vector3d& delta_velocity_imu_frame,\n                           const double first_delta_time_seconds,\n                           const double second_delta_time_seconds)\n      : scaling_factor_(scaling_factor),\n        delta_velocity_imu_frame_(delta_velocity_imu_frame),\n        first_delta_time_seconds_(first_delta_time_seconds),\n        second_delta_time_seconds_(second_delta_time_seconds) {}\n\n  AccelerationCostFunction(const AccelerationCostFunction&) = delete;\n  AccelerationCostFunction& operator=(const AccelerationCostFunction&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const middle_rotation, const T* const start_position,\n                  const T* const middle_position, const T* const end_position,\n                  const T* const gravity_constant, T* residual) const {\n    const Eigen::Matrix<T, 3, 1> imu_delta_velocity =\n        ToEigen(middle_rotation) * delta_velocity_imu_frame_.cast<T>() -\n        *gravity_constant *\n            (0.5 * (first_delta_time_seconds_ + second_delta_time_seconds_) *\n             Eigen::Vector3d::UnitZ())\n                .cast<T>();\n    const Eigen::Matrix<T, 3, 1> start_velocity =\n        (Eigen::Map<const Eigen::Matrix<T, 3, 1>>(middle_position) -\n         Eigen::Map<const Eigen::Matrix<T, 3, 1>>(start_position)) /\n        T(first_delta_time_seconds_);\n    const Eigen::Matrix<T, 3, 1> end_velocity =\n        (Eigen::Map<const Eigen::Matrix<T, 3, 1>>(end_position) -\n         Eigen::Map<const Eigen::Matrix<T, 3, 1>>(middle_position)) /\n        T(second_delta_time_seconds_);\n    const Eigen::Matrix<T, 3, 1> delta_velocity = end_velocity - start_velocity;\n\n    (Eigen::Map<Eigen::Matrix<T, 3, 1>>(residual) =\n         T(scaling_factor_) * (imu_delta_velocity - delta_velocity));\n    return true;\n  }\n\n private:\n  template <typename T>\n  static Eigen::Quaternion<T> ToEigen(const T* const quaternion) {\n    return Eigen::Quaternion<T>(quaternion[0], quaternion[1], quaternion[2],\n                                quaternion[3]);\n  }\n\n  const double scaling_factor_;\n  const Eigen::Vector3d delta_velocity_imu_frame_;\n  const double first_delta_time_seconds_;\n  const double second_delta_time_seconds_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_ACCELERATION_COST_FUNCTION_H_\n"
  },
  {
    "path": "mapping_3d/ceres_pose.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/ceres_pose.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nCeresPose::CeresPose(\n    const transform::Rigid3d& rigid,\n    std::unique_ptr<ceres::LocalParameterization> translation_parametrization,\n    std::unique_ptr<ceres::LocalParameterization> rotation_parametrization,\n    ceres::Problem* problem)\n    : translation_({{rigid.translation().x(), rigid.translation().y(),\n                     rigid.translation().z()}}),\n      rotation_({{rigid.rotation().w(), rigid.rotation().x(),\n                  rigid.rotation().y(), rigid.rotation().z()}}) {\n  problem->AddParameterBlock(translation_.data(), 3,\n                             translation_parametrization.release());\n  problem->AddParameterBlock(rotation_.data(), 4,\n                             rotation_parametrization.release());\n}\n\nconst transform::Rigid3d CeresPose::ToRigid() const {\n  return transform::Rigid3d(\n      Eigen::Map<const Eigen::Vector3d>(translation_.data()),\n      Eigen::Quaterniond(rotation_[0], rotation_[1], rotation_[2],\n                         rotation_[3]));\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/ceres_pose.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_CERES_POSE_H_\n#define CARTOGRAPHER_MAPPING_3D_CERES_POSE_H_\n\n#include <array>\n#include <memory>\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"ceres/ceres.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nclass CeresPose {\n public:\n  CeresPose(\n      const transform::Rigid3d& rigid,\n      std::unique_ptr<ceres::LocalParameterization> translation_parametrization,\n      std::unique_ptr<ceres::LocalParameterization> rotation_parametrization,\n      ceres::Problem* problem);\n\n  CeresPose(const CeresPose&) = delete;\n  CeresPose& operator=(const CeresPose&) = delete;\n\n  const transform::Rigid3d ToRigid() const;\n\n  double* translation() { return translation_.data(); }\n  double* rotation() { return rotation_.data(); }\n\n private:\n  std::array<double, 3> translation_;\n  // Rotation quaternion as (w, x, y, z).\n  std::array<double, 4> rotation_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_CERES_POSE_H_\n"
  },
  {
    "path": "mapping_3d/global_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/global_trajectory_builder.h\"\n\n#include \"cartographer/mapping_3d/local_trajectory_builder.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nGlobalTrajectoryBuilder::GlobalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions& options,\n    const int trajectory_id, SparsePoseGraph* sparse_pose_graph)\n    : trajectory_id_(trajectory_id),\n      sparse_pose_graph_(sparse_pose_graph),\n      local_trajectory_builder_(CreateLocalTrajectoryBuilder(options)) {}\n\nGlobalTrajectoryBuilder::~GlobalTrajectoryBuilder() {}\n\nconst mapping_3d::Submaps* GlobalTrajectoryBuilder::submaps() const {\n  return local_trajectory_builder_->submaps();\n}\n\nvoid GlobalTrajectoryBuilder::AddImuData(\n    const common::Time time, const Eigen::Vector3d& linear_acceleration,\n    const Eigen::Vector3d& angular_velocity) {\n  local_trajectory_builder_->AddImuData(time, linear_acceleration,\n                                        angular_velocity);\n  sparse_pose_graph_->AddImuData(trajectory_id_, time, linear_acceleration,\n                                 angular_velocity);\n}\n\nvoid GlobalTrajectoryBuilder::AddRangefinderData(\n    const common::Time time, const Eigen::Vector3f& origin,\n    const sensor::PointCloud& ranges) {\n  auto insertion_result =\n      local_trajectory_builder_->AddRangefinderData(time, origin, ranges);\n\n  if (insertion_result == nullptr) {\n    return;\n  }\n\n  sparse_pose_graph_->AddScan(\n      insertion_result->time, insertion_result->range_data_in_tracking,\n      insertion_result->pose_observation, trajectory_id_,\n      insertion_result->matching_submap, insertion_result->insertion_submaps);\n}\n\nvoid GlobalTrajectoryBuilder::AddOdometerData(const common::Time time,\n                                              const transform::Rigid3d& pose) {\n  local_trajectory_builder_->AddOdometerData(time, pose);\n}\n\nconst GlobalTrajectoryBuilder::PoseEstimate&\nGlobalTrajectoryBuilder::pose_estimate() const {\n  return local_trajectory_builder_->pose_estimate();\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/global_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_GLOBAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_3D_GLOBAL_TRAJECTORY_BUILDER_H_\n\n#include <memory>\n\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/local_trajectory_builder.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nclass GlobalTrajectoryBuilder\n    : public mapping::GlobalTrajectoryBuilderInterface {\n public:\n  GlobalTrajectoryBuilder(const proto::LocalTrajectoryBuilderOptions& options,\n                          int trajectory_id,\n                          mapping_3d::SparsePoseGraph* sparse_pose_graph);\n  ~GlobalTrajectoryBuilder() override;\n\n  GlobalTrajectoryBuilder(const GlobalTrajectoryBuilder&) = delete;\n  GlobalTrajectoryBuilder& operator=(const GlobalTrajectoryBuilder&) = delete;\n\n  const mapping_3d::Submaps* submaps() const override;\n  void AddImuData(common::Time time, const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity) override;\n  void AddRangefinderData(common::Time time, const Eigen::Vector3f& origin,\n                          const sensor::PointCloud& ranges) override;\n  void AddOdometerData(common::Time time,\n                       const transform::Rigid3d& pose) override;\n  const PoseEstimate& pose_estimate() const override;\n\n private:\n  const int trajectory_id_;\n  mapping_3d::SparsePoseGraph* const sparse_pose_graph_;\n  std::unique_ptr<LocalTrajectoryBuilderInterface> local_trajectory_builder_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_GLOBAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_3d/hybrid_grid.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_HYBRID_GRID_H_\n#define CARTOGRAPHER_MAPPING_3D_HYBRID_GRID_H_\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <utility>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/probability_values.h\"\n#include \"cartographer/mapping_3d/proto/hybrid_grid.pb.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Converts an 'index' with each dimension from 0 to 2^'bits' - 1 to a flat\n// z-major index.\ninline int ToFlatIndex(const Eigen::Array3i& index, const int bits) {\n  DCHECK((index >= 0).all() && (index < (1 << bits)).all()) << index;\n  return (((index.z() << bits) + index.y()) << bits) + index.x();\n}\n\n// Converts a flat z-major 'index' to a 3-dimensional index with each dimension\n// from 0 to 2^'bits' - 1.\ninline Eigen::Array3i To3DIndex(const int index, const int bits) {\n  DCHECK_LT(index, 1 << (3 * bits));\n  const int mask = (1 << bits) - 1;\n  return Eigen::Array3i(index & mask, (index >> bits) & mask,\n                        (index >> bits) >> bits);\n}\n\n// A function to compare value to the default value. (Allows specializations).\ntemplate <typename TValueType>\nbool IsDefaultValue(const TValueType& v) {\n  return v == TValueType();\n}\n\n// Specialization to compare a std::vector to the default value.\ntemplate <typename TElementType>\nbool IsDefaultValue(const std::vector<TElementType>& v) {\n  return v.empty();\n}\n\n// A flat grid of '2^kBits' x '2^kBits' x '2^kBits' voxels storing values of\n// type 'ValueType' in contiguous memory. Indices in each dimension are 0-based.\ntemplate <typename TValueType, int kBits>\nclass FlatGrid {\n public:\n  using ValueType = TValueType;\n\n  // Creates a new flat grid with all values being default constructed.\n  FlatGrid() {\n    for (ValueType& value : cells_) {\n      value = ValueType();\n    }\n  }\n\n  FlatGrid(const FlatGrid&) = delete;\n  FlatGrid& operator=(const FlatGrid&) = delete;\n\n  // Returns the number of voxels per dimension.\n  static int grid_size() { return 1 << kBits; }\n\n  // Returns the value stored at 'index', each dimension of 'index' being\n  // between 0 and grid_size() - 1.\n  ValueType value(const Eigen::Array3i& index) const {\n    return cells_[ToFlatIndex(index, kBits)];\n  }\n\n  // Returns a pointer to a value to allow changing it.\n  ValueType* mutable_value(const Eigen::Array3i& index) {\n    return &cells_[ToFlatIndex(index, kBits)];\n  }\n\n  // An iterator for iterating over all values not comparing equal to the\n  // default constructed value.\n  class Iterator {\n   public:\n    Iterator() : current_(nullptr), end_(nullptr) {}\n\n    explicit Iterator(const FlatGrid& flat_grid)\n        : current_(flat_grid.cells_.data()),\n          end_(flat_grid.cells_.data() + flat_grid.cells_.size()) {\n      while (!Done() && IsDefaultValue(*current_)) {\n        ++current_;\n      }\n    }\n\n    void Next() {\n      DCHECK(!Done());\n      do {\n        ++current_;\n      } while (!Done() && IsDefaultValue(*current_));\n    }\n\n    bool Done() const { return current_ == end_; }\n\n    Eigen::Array3i GetCellIndex() const {\n      DCHECK(!Done());\n      const int index = (1 << (3 * kBits)) - (end_ - current_);\n      return To3DIndex(index, kBits);\n    }\n\n    const ValueType& GetValue() const {\n      DCHECK(!Done());\n      return *current_;\n    }\n\n   private:\n    const ValueType* current_;\n    const ValueType* end_;\n  };\n\n private:\n  std::array<ValueType, 1 << (3 * kBits)> cells_;\n};\n\n// A grid consisting of '2^kBits' x '2^kBits' x '2^kBits' grids of type\n// 'WrappedGrid'. Wrapped grids are constructed on first access via\n// 'mutable_value()'.\ntemplate <typename WrappedGrid, int kBits>\nclass NestedGrid {\n public:\n  using ValueType = typename WrappedGrid::ValueType;\n\n  // Returns the number of voxels per dimension.\n  static int grid_size() { return WrappedGrid::grid_size() << kBits; }\n\n  // Returns the value stored at 'index', each dimension of 'index' being\n  // between 0 and grid_size() - 1.\n  ValueType value(const Eigen::Array3i& index) const {\n    const Eigen::Array3i meta_index = GetMetaIndex(index);\n    const WrappedGrid* const meta_cell =\n        meta_cells_[ToFlatIndex(meta_index, kBits)].get();\n    if (meta_cell == nullptr) {\n      return ValueType();\n    }\n    const Eigen::Array3i inner_index =\n        index - meta_index * WrappedGrid::grid_size();\n    return meta_cell->value(inner_index);\n  }\n\n  // Returns a pointer to the value at 'index' to allow changing it. If\n  // necessary a new wrapped grid is constructed to contain that value.\n  ValueType* mutable_value(const Eigen::Array3i& index) {\n    const Eigen::Array3i meta_index = GetMetaIndex(index);\n    std::unique_ptr<WrappedGrid>& meta_cell =\n        meta_cells_[ToFlatIndex(meta_index, kBits)];\n    if (meta_cell == nullptr) {\n      meta_cell = common::make_unique<WrappedGrid>();\n    }\n    const Eigen::Array3i inner_index =\n        index - meta_index * WrappedGrid::grid_size();\n    return meta_cell->mutable_value(inner_index);\n  }\n\n  // An iterator for iterating over all values not comparing equal to the\n  // default constructed value.\n  class Iterator {\n   public:\n    Iterator() : current_(nullptr), end_(nullptr), nested_iterator_() {}\n\n    explicit Iterator(const NestedGrid& nested_grid)\n        : current_(nested_grid.meta_cells_.data()),\n          end_(nested_grid.meta_cells_.data() + nested_grid.meta_cells_.size()),\n          nested_iterator_() {\n      AdvanceToValidNestedIterator();\n    }\n\n    void Next() {\n      DCHECK(!Done());\n      nested_iterator_.Next();\n      if (!nested_iterator_.Done()) {\n        return;\n      }\n      ++current_;\n      AdvanceToValidNestedIterator();\n    }\n\n    bool Done() const { return current_ == end_; }\n\n    Eigen::Array3i GetCellIndex() const {\n      DCHECK(!Done());\n      const int index = (1 << (3 * kBits)) - (end_ - current_);\n      return To3DIndex(index, kBits) * WrappedGrid::grid_size() +\n             nested_iterator_.GetCellIndex();\n    }\n\n    const ValueType& GetValue() const {\n      DCHECK(!Done());\n      return nested_iterator_.GetValue();\n    }\n\n   private:\n    void AdvanceToValidNestedIterator() {\n      for (; !Done(); ++current_) {\n        if (*current_ != nullptr) {\n          nested_iterator_ = typename WrappedGrid::Iterator(**current_);\n          if (!nested_iterator_.Done()) {\n            break;\n          }\n        }\n      }\n    }\n\n    const std::unique_ptr<WrappedGrid>* current_;\n    const std::unique_ptr<WrappedGrid>* end_;\n    typename WrappedGrid::Iterator nested_iterator_;\n  };\n\n private:\n  // Returns the Eigen::Array3i (meta) index of the meta cell containing\n  // 'index'.\n  Eigen::Array3i GetMetaIndex(const Eigen::Array3i& index) const {\n    DCHECK((index >= 0).all()) << index;\n    const Eigen::Array3i meta_index = index / WrappedGrid::grid_size();\n    DCHECK((meta_index < (1 << kBits)).all()) << index;\n    return meta_index;\n  }\n\n  std::array<std::unique_ptr<WrappedGrid>, 1 << (3 * kBits)> meta_cells_;\n};\n\n// A grid consisting of 2x2x2 grids of type 'WrappedGrid' initially. Wrapped\n// grids are constructed on first access via 'mutable_value()'. If necessary,\n// the grid grows to twice the size in each dimension. The range of indices is\n// (almost) symmetric around the origin, i.e. negative indices are allowed.\ntemplate <typename WrappedGrid>\nclass DynamicGrid {\n public:\n  using ValueType = typename WrappedGrid::ValueType;\n\n  DynamicGrid() : bits_(1), meta_cells_(8) {}\n  DynamicGrid(DynamicGrid&&) = default;\n  DynamicGrid& operator=(DynamicGrid&&) = default;\n\n  // Returns the current number of voxels per dimension.\n  int grid_size() const { return WrappedGrid::grid_size() << bits_; }\n\n  // Returns the value stored at 'index'.\n  ValueType value(const Eigen::Array3i& index) const {\n    const Eigen::Array3i shifted_index = index + (grid_size() >> 1);\n    // The cast to unsigned is for performance to check with 3 comparisons\n    // shifted_index.[xyz] >= 0 and shifted_index.[xyz] < grid_size.\n    if ((shifted_index.cast<unsigned int>() >= grid_size()).any()) {\n      return ValueType();\n    }\n    const Eigen::Array3i meta_index = GetMetaIndex(shifted_index);\n    const WrappedGrid* const meta_cell =\n        meta_cells_[ToFlatIndex(meta_index, bits_)].get();\n    if (meta_cell == nullptr) {\n      return ValueType();\n    }\n    const Eigen::Array3i inner_index =\n        shifted_index - meta_index * WrappedGrid::grid_size();\n    return meta_cell->value(inner_index);\n  }\n\n  // Returns a pointer to the value at 'index' to allow changing it, dynamically\n  // growing the DynamicGrid and constructing new WrappedGrids as needed.\n  ValueType* mutable_value(const Eigen::Array3i& index) {\n    const Eigen::Array3i shifted_index = index + (grid_size() >> 1);\n    // The cast to unsigned is for performance to check with 3 comparisons\n    // shifted_index.[xyz] >= 0 and shifted_index.[xyz] < grid_size.\n    if ((shifted_index.cast<unsigned int>() >= grid_size()).any()) {\n      Grow();\n      return mutable_value(index);\n    }\n    const Eigen::Array3i meta_index = GetMetaIndex(shifted_index);\n    std::unique_ptr<WrappedGrid>& meta_cell =\n        meta_cells_[ToFlatIndex(meta_index, bits_)];\n    if (meta_cell == nullptr) {\n      meta_cell = common::make_unique<WrappedGrid>();\n    }\n    const Eigen::Array3i inner_index =\n        shifted_index - meta_index * WrappedGrid::grid_size();\n    return meta_cell->mutable_value(inner_index);\n  }\n\n  // An iterator for iterating over all values not comparing equal to the\n  // default constructed value.\n  class Iterator {\n   public:\n    explicit Iterator(const DynamicGrid& dynamic_grid)\n        : bits_(dynamic_grid.bits_),\n          current_(dynamic_grid.meta_cells_.data()),\n          end_(dynamic_grid.meta_cells_.data() +\n               dynamic_grid.meta_cells_.size()),\n          nested_iterator_() {\n      AdvanceToValidNestedIterator();\n    }\n\n    void Next() {\n      DCHECK(!Done());\n      nested_iterator_.Next();\n      if (!nested_iterator_.Done()) {\n        return;\n      }\n      ++current_;\n      AdvanceToValidNestedIterator();\n    }\n\n    bool Done() const { return current_ == end_; }\n\n    Eigen::Array3i GetCellIndex() const {\n      DCHECK(!Done());\n      const int outer_index = (1 << (3 * bits_)) - (end_ - current_);\n      const Eigen::Array3i shifted_index =\n          To3DIndex(outer_index, bits_) * WrappedGrid::grid_size() +\n          nested_iterator_.GetCellIndex();\n      return shifted_index - ((1 << (bits_ - 1)) * WrappedGrid::grid_size());\n    }\n\n    const ValueType& GetValue() const {\n      DCHECK(!Done());\n      return nested_iterator_.GetValue();\n    }\n\n    void AdvanceToEnd() { current_ = end_; }\n\n    const std::pair<Eigen::Array3i, ValueType> operator*() const {\n      return std::pair<Eigen::Array3i, ValueType>(GetCellIndex(), GetValue());\n    }\n\n    Iterator& operator++() {\n      Next();\n      return *this;\n    }\n\n    bool operator!=(const Iterator& it) const {\n      return it.current_ != current_;\n    }\n\n   private:\n    void AdvanceToValidNestedIterator() {\n      for (; !Done(); ++current_) {\n        if (*current_ != nullptr) {\n          nested_iterator_ = typename WrappedGrid::Iterator(**current_);\n          if (!nested_iterator_.Done()) {\n            break;\n          }\n        }\n      }\n    }\n\n    int bits_;\n    const std::unique_ptr<WrappedGrid>* current_;\n    const std::unique_ptr<WrappedGrid>* const end_;\n    typename WrappedGrid::Iterator nested_iterator_;\n  };\n\n private:\n  // Returns the Eigen::Array3i (meta) index of the meta cell containing\n  // 'index'.\n  Eigen::Array3i GetMetaIndex(const Eigen::Array3i& index) const {\n    DCHECK((index >= 0).all()) << index;\n    const Eigen::Array3i meta_index = index / WrappedGrid::grid_size();\n    DCHECK((meta_index < (1 << bits_)).all()) << index;\n    return meta_index;\n  }\n\n  // Grows this grid by a factor of 2 in each of the 3 dimensions.\n  void Grow() {\n    const int new_bits = bits_ + 1;\n    CHECK_LE(new_bits, 8);\n    std::vector<std::unique_ptr<WrappedGrid>> new_meta_cells_(\n        8 * meta_cells_.size());\n    for (int z = 0; z != (1 << bits_); ++z) {\n      for (int y = 0; y != (1 << bits_); ++y) {\n        for (int x = 0; x != (1 << bits_); ++x) {\n          const Eigen::Array3i original_meta_index(x, y, z);\n          const Eigen::Array3i new_meta_index =\n              original_meta_index + (1 << (bits_ - 1));\n          new_meta_cells_[ToFlatIndex(new_meta_index, new_bits)] =\n              std::move(meta_cells_[ToFlatIndex(original_meta_index, bits_)]);\n        }\n      }\n    }\n    meta_cells_ = std::move(new_meta_cells_);\n    bits_ = new_bits;\n  }\n\n  int bits_;\n  std::vector<std::unique_ptr<WrappedGrid>> meta_cells_;\n};\n\ntemplate <typename ValueType>\nusing Grid = DynamicGrid<NestedGrid<FlatGrid<ValueType, 3>, 3>>;\n\n// Represents a 3D grid as a wide, shallow tree.\ntemplate <typename ValueType>\nclass HybridGridBase : public Grid<ValueType> {\n public:\n  using Iterator = typename Grid<ValueType>::Iterator;\n\n  // Creates a new tree-based probability grid with voxels having edge length\n  // 'resolution' around the origin which becomes the center of the cell at\n  // index (0, 0, 0).\n  explicit HybridGridBase(const float resolution) : resolution_(resolution) {}\n\n  float resolution() const { return resolution_; }\n\n  // Returns the index of the cell containing the 'point'. Indices are integer\n  // vectors identifying cells, for this the coordinates are rounded to the next\n  // multiple of the resolution.\n  Eigen::Array3i GetCellIndex(const Eigen::Vector3f& point) const {\n    Eigen::Array3f index = point.array() / resolution_;\n    return Eigen::Array3i(common::RoundToInt(index.x()),\n                          common::RoundToInt(index.y()),\n                          common::RoundToInt(index.z()));\n  }\n\n  // Returns one of the octants, (0, 0, 0), (1, 0, 0), ..., (1, 1, 1).\n  static Eigen::Array3i GetOctant(const int i) {\n    DCHECK_GE(i, 0);\n    DCHECK_LT(i, 8);\n    return Eigen::Array3i(static_cast<bool>(i & 1), static_cast<bool>(i & 2),\n                          static_cast<bool>(i & 4));\n  }\n\n  // Returns the center of the cell at 'index'.\n  Eigen::Vector3f GetCenterOfCell(const Eigen::Array3i& index) const {\n    return index.matrix().cast<float>() * resolution_;\n  }\n\n  // Iterator functions for range-based for loops.\n  Iterator begin() const { return Iterator(*this); }\n\n  Iterator end() const {\n    Iterator it(*this);\n    it.AdvanceToEnd();\n    return it;\n  }\n\n private:\n  // Edge length of each voxel.\n  const float resolution_;\n};\n\n// A grid containing probability values stored using 15 bits, and an update\n// marker per voxel.\nclass HybridGrid : public HybridGridBase<uint16> {\n public:\n  explicit HybridGrid(const float resolution)\n      : HybridGridBase<uint16>(resolution) {}\n\n  explicit HybridGrid(const proto::HybridGrid& proto)\n      : HybridGrid(proto.resolution()) {\n    CHECK_EQ(proto.values_size(), proto.x_indices_size());\n    CHECK_EQ(proto.values_size(), proto.y_indices_size());\n    CHECK_EQ(proto.values_size(), proto.z_indices_size());\n\n    for (int i = 0; i < proto.values_size(); ++i) {\n      // SetProbability does some error checking for us.\n      SetProbability(Eigen::Vector3i(proto.x_indices(i), proto.y_indices(i),\n                                     proto.z_indices(i)),\n                     mapping::ValueToProbability(proto.values(i)));\n    }\n  }\n\n  // Sets the probability of the cell at 'index' to the given 'probability'.\n  void SetProbability(const Eigen::Array3i& index, const float probability) {\n    *mutable_value(index) = mapping::ProbabilityToValue(probability);\n  }\n\n  // Starts the next update sequence.\n  void StartUpdate() {\n    while (!update_indices_.empty()) {\n      DCHECK_GE(*update_indices_.back(), mapping::kUpdateMarker);\n      *update_indices_.back() -= mapping::kUpdateMarker;\n      update_indices_.pop_back();\n    }\n  }\n\n  // Applies the 'odds' specified when calling ComputeLookupTableToApplyOdds()\n  // to the probability of the cell at 'index' if the cell has not already been\n  // updated. Multiple updates of the same cell will be ignored until\n  // StartUpdate() is called. Returns true if the cell was updated.\n  //\n  // If this is the first call to ApplyOdds() for the specified cell, its value\n  // will be set to probability corresponding to 'odds'.\n  bool ApplyLookupTable(const Eigen::Array3i& index,\n                        const std::vector<uint16>& table) {\n    DCHECK_EQ(table.size(), mapping::kUpdateMarker);\n    uint16* const cell = mutable_value(index);\n    if (*cell >= mapping::kUpdateMarker) {\n      return false;\n    }\n    update_indices_.push_back(cell);\n    *cell = table[*cell];\n    DCHECK_GE(*cell, mapping::kUpdateMarker);\n    return true;\n  }\n\n  // Returns the probability of the cell with 'index'.\n  float GetProbability(const Eigen::Array3i& index) const {\n    return mapping::ValueToProbability(value(index));\n  }\n\n  // Returns true if the probability at the specified 'index' is known.\n  bool IsKnown(const Eigen::Array3i& index) const { return value(index) != 0; }\n\n private:\n  // Markers at changed cells.\n  std::vector<ValueType*> update_indices_;\n};\n\ninline proto::HybridGrid ToProto(const HybridGrid& grid) {\n  proto::HybridGrid result;\n  result.set_resolution(grid.resolution());\n  for (const auto it : grid) {\n    result.add_x_indices(it.first.x());\n    result.add_y_indices(it.first.y());\n    result.add_z_indices(it.first.z());\n    result.add_values(it.second);\n  }\n  return result;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_HYBRID_GRID_H_\n"
  },
  {
    "path": "mapping_3d/hybrid_grid_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\n#include <map>\n#include <random>\n#include <tuple>\n\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace {\n\nTEST(HybridGridTest, ApplyOdds) {\n  HybridGrid hybrid_grid(1.f);\n\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(0, 0, 0)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(0, 1, 0)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(1, 0, 0)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(1, 1, 0)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(0, 0, 1)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(0, 1, 1)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(1, 0, 1)));\n  EXPECT_FALSE(hybrid_grid.IsKnown(Eigen::Array3i(1, 1, 1)));\n\n  hybrid_grid.SetProbability(Eigen::Array3i(1, 0, 1), 0.5f);\n\n  hybrid_grid.StartUpdate();\n  hybrid_grid.ApplyLookupTable(\n      Eigen::Array3i(1, 0, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9f)));\n  EXPECT_GT(hybrid_grid.GetProbability(Eigen::Array3i(1, 0, 1)), 0.5f);\n\n  hybrid_grid.SetProbability(Eigen::Array3i(0, 1, 0), 0.5f);\n\n  hybrid_grid.StartUpdate();\n  hybrid_grid.ApplyLookupTable(\n      Eigen::Array3i(0, 1, 0),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.1f)));\n  EXPECT_LT(hybrid_grid.GetProbability(Eigen::Array3i(0, 1, 0)), 0.5f);\n\n  // Tests adding odds to an unknown cell.\n  hybrid_grid.StartUpdate();\n  hybrid_grid.ApplyLookupTable(\n      Eigen::Array3i(1, 1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.42f)));\n  EXPECT_NEAR(hybrid_grid.GetProbability(Eigen::Array3i(1, 1, 1)), 0.42f, 1e-4);\n\n  // Tests that further updates are ignored if StartUpdate() isn't called.\n  hybrid_grid.ApplyLookupTable(\n      Eigen::Array3i(1, 1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9f)));\n  EXPECT_NEAR(hybrid_grid.GetProbability(Eigen::Array3i(1, 1, 1)), 0.42f, 1e-4);\n  hybrid_grid.StartUpdate();\n  hybrid_grid.ApplyLookupTable(\n      Eigen::Array3i(1, 1, 1),\n      mapping::ComputeLookupTableToApplyOdds(mapping::Odds(0.9f)));\n  EXPECT_GT(hybrid_grid.GetProbability(Eigen::Array3i(1, 1, 1)), 0.42f);\n}\n\nTEST(HybridGridTest, GetProbability) {\n  HybridGrid hybrid_grid(1.f);\n\n  hybrid_grid.SetProbability(\n      hybrid_grid.GetCellIndex(Eigen::Vector3f(0.f, 1.f, 1.f)),\n      mapping::kMaxProbability);\n  EXPECT_NEAR(hybrid_grid.GetProbability(\n                  hybrid_grid.GetCellIndex(Eigen::Vector3f(0.f, 1.f, 1.f))),\n              mapping::kMaxProbability, 1e-6);\n  for (const Eigen::Array3i& index :\n       {hybrid_grid.GetCellIndex(Eigen::Vector3f(0.f, 2.f, 1.f)),\n        hybrid_grid.GetCellIndex(Eigen::Vector3f(1.f, 1.f, 1.f)),\n        hybrid_grid.GetCellIndex(Eigen::Vector3f(1.f, 2.f, 1.f))}) {\n    EXPECT_FALSE(hybrid_grid.IsKnown(index));\n  }\n}\n\nMATCHER_P(AllCwiseEqual, index, \"\") { return (arg == index).all(); }\n\nTEST(HybridGridTest, GetCellIndex) {\n  HybridGrid hybrid_grid(2.f);\n\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(0.f, 0.f, 0.f)),\n              AllCwiseEqual(Eigen::Array3i(0, 0, 0)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(0.f, 26.f, 10.f)),\n              AllCwiseEqual(Eigen::Array3i(0, 13, 5)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(14.f, 0.f, 10.f)),\n              AllCwiseEqual(Eigen::Array3i(7, 0, 5)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(14.f, 26.f, 0.f)),\n              AllCwiseEqual(Eigen::Array3i(7, 13, 0)));\n\n  // Check around the origin.\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(8.5f, 11.5f, 0.5f)),\n              AllCwiseEqual(Eigen::Array3i(4, 6, 0)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(7.5f, 12.5f, 1.5f)),\n              AllCwiseEqual(Eigen::Array3i(4, 6, 1)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(6.5f, 14.5f, 2.5f)),\n              AllCwiseEqual(Eigen::Array3i(3, 7, 1)));\n  EXPECT_THAT(hybrid_grid.GetCellIndex(Eigen::Vector3f(5.5f, 13.5f, 3.5f)),\n              AllCwiseEqual(Eigen::Array3i(3, 7, 2)));\n}\n\nTEST(HybridGridTest, GetCenterOfCell) {\n  HybridGrid hybrid_grid(2.f);\n\n  const Eigen::Array3i index(3, 2, 1);\n  const Eigen::Vector3f center = hybrid_grid.GetCenterOfCell(index);\n  EXPECT_NEAR(6.f, center.x(), 1e-6);\n  EXPECT_NEAR(4.f, center.y(), 1e-6);\n  EXPECT_NEAR(2.f, center.z(), 1e-6);\n  EXPECT_THAT(hybrid_grid.GetCellIndex(center), AllCwiseEqual(index));\n}\n\nclass RandomHybridGridTest : public ::testing::Test {\n public:\n  RandomHybridGridTest() : hybrid_grid_(2.f), values_() {\n    std::mt19937 rng(1285120005);\n    std::uniform_real_distribution<float> value_distribution(\n        mapping::kMinProbability, mapping::kMaxProbability);\n    std::uniform_int_distribution<int> xyz_distribution(-3000, 2999);\n    for (int i = 0; i < 10000; ++i) {\n      const auto x = xyz_distribution(rng);\n      const auto y = xyz_distribution(rng);\n      const auto z = xyz_distribution(rng);\n      values_.emplace(std::make_tuple(x, y, z), value_distribution(rng));\n    }\n\n    for (const auto& pair : values_) {\n      const Eigen::Array3i cell_index(std::get<0>(pair.first),\n                                      std::get<1>(pair.first),\n                                      std::get<2>(pair.first));\n      hybrid_grid_.SetProbability(cell_index, pair.second);\n    }\n  }\n\n protected:\n  HybridGrid hybrid_grid_;\n  using ValueMap = std::map<std::tuple<int, int, int>, float>;\n  ValueMap values_;\n};\n\nTEST_F(RandomHybridGridTest, TestIteration) {\n  for (auto it = HybridGrid::Iterator(hybrid_grid_); !it.Done(); it.Next()) {\n    const Eigen::Array3i cell_index = it.GetCellIndex();\n    const float iterator_probability =\n        mapping::ValueToProbability(it.GetValue());\n    EXPECT_EQ(iterator_probability, hybrid_grid_.GetProbability(cell_index));\n    const std::tuple<int, int, int> key =\n        std::make_tuple(cell_index[0], cell_index[1], cell_index[2]);\n    EXPECT_TRUE(values_.count(key));\n    EXPECT_NEAR(values_[key], iterator_probability, 1e-4);\n    values_.erase(key);\n  }\n\n  // Test that range based loop is equivalent to using the iterator.\n  auto it = HybridGrid::Iterator(hybrid_grid_);\n  for (const auto& cell : hybrid_grid_) {\n    ASSERT_FALSE(it.Done());\n    EXPECT_THAT(cell.first, AllCwiseEqual(it.GetCellIndex()));\n    EXPECT_EQ(cell.second, it.GetValue());\n    it.Next();\n  }\n\n  // Now 'values_' must not contain values.\n  for (const auto& pair : values_) {\n    const Eigen::Array3i cell_index(std::get<0>(pair.first),\n                                    std::get<1>(pair.first),\n                                    std::get<2>(pair.first));\n    ADD_FAILURE() << cell_index << \" Probability: \" << pair.second;\n  }\n}\n\nTEST_F(RandomHybridGridTest, ToProto) {\n  const auto proto = ToProto(hybrid_grid_);\n  EXPECT_EQ(hybrid_grid_.resolution(), proto.resolution());\n\n  ASSERT_EQ(proto.x_indices_size(), proto.y_indices_size());\n  ASSERT_EQ(proto.x_indices_size(), proto.z_indices_size());\n  ASSERT_EQ(proto.x_indices_size(), proto.values_size());\n\n  ValueMap proto_map;\n  for (int i = 0; i < proto.x_indices_size(); ++i) {\n    proto_map[std::make_tuple(proto.x_indices(i), proto.y_indices(i),\n                              proto.z_indices(i))] = proto.values(i);\n  }\n\n  // Get hybrid_grid_ into the same format.\n  ValueMap hybrid_grid_map;\n  for (const auto i : hybrid_grid_) {\n    hybrid_grid_map[std::make_tuple(i.first.x(), i.first.y(), i.first.z())] =\n        i.second;\n  }\n\n  EXPECT_EQ(proto_map, hybrid_grid_map);\n}\n\nnamespace {\n\nstruct EigenComparator {\n  bool operator()(const Eigen::Vector3i& lhs, const Eigen::Vector3i& rhs) {\n    return std::forward_as_tuple(lhs.x(), lhs.y(), lhs.z()) <\n           std::forward_as_tuple(rhs.x(), rhs.y(), rhs.z());\n  }\n};\n\n}  // namespace\n\nTEST_F(RandomHybridGridTest, FromProto) {\n  const HybridGrid constructed_grid(ToProto(hybrid_grid_));\n\n  std::map<Eigen::Vector3i, float, EigenComparator> member_map(\n      hybrid_grid_.begin(), hybrid_grid_.end());\n\n  std::map<Eigen::Vector3i, float, EigenComparator> constructed_map(\n      constructed_grid.begin(), constructed_grid.end());\n\n  EXPECT_EQ(member_map, constructed_map);\n}\n\n}  // namespace\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/imu_integration.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/imu_integration.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nIntegrateImuResult<double> IntegrateImu(\n    const std::deque<ImuData>& imu_data, const common::Time start_time,\n    const common::Time end_time, std::deque<ImuData>::const_iterator* it) {\n  return IntegrateImu<double>(imu_data, Eigen::Affine3d::Identity(),\n                              Eigen::Affine3d::Identity(), start_time, end_time,\n                              it);\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/imu_integration.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_IMU_INTEGRATION_H_\n#define CARTOGRAPHER_MAPPING_3D_IMU_INTEGRATION_H_\n\n#include <algorithm>\n#include <deque>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nstruct ImuData {\n  common::Time time;\n  Eigen::Vector3d linear_acceleration;\n  Eigen::Vector3d angular_velocity;\n};\n\ntemplate <typename T>\nstruct IntegrateImuResult {\n  Eigen::Matrix<T, 3, 1> delta_velocity;\n  Eigen::Quaternion<T> delta_rotation;\n};\n\n// Returns velocity delta in map frame.\nIntegrateImuResult<double> IntegrateImu(\n    const std::deque<ImuData>& imu_data, const common::Time start_time,\n    const common::Time end_time, std::deque<ImuData>::const_iterator* it);\n\ntemplate <typename T>\nIntegrateImuResult<T> IntegrateImu(\n    const std::deque<ImuData>& imu_data,\n    const Eigen::Transform<T, 3, Eigen::Affine>&\n        linear_acceleration_calibration,\n    const Eigen::Transform<T, 3, Eigen::Affine>& angular_velocity_calibration,\n    const common::Time start_time, const common::Time end_time,\n    std::deque<ImuData>::const_iterator* it) {\n  CHECK_LE(start_time, end_time);\n  CHECK(*it != imu_data.cend());\n  CHECK_LE((*it)->time, start_time);\n  if ((*it) + 1 != imu_data.cend()) {\n    CHECK_GT(((*it) + 1)->time, start_time);\n  }\n\n  common::Time current_time = start_time;\n\n  IntegrateImuResult<T> result = {Eigen::Matrix<T, 3, 1>::Zero(),\n                                  Eigen::Quaterniond::Identity().cast<T>()};\n  while (current_time < end_time) {\n    common::Time next_imu_data = common::Time::max();\n    if ((*it) + 1 != imu_data.cend()) {\n      next_imu_data = ((*it) + 1)->time;\n    }\n    common::Time next_time = std::min(next_imu_data, end_time);\n    const T delta_t(common::ToSeconds(next_time - current_time));\n\n    const Eigen::Matrix<T, 3, 1> delta_angle =\n        (angular_velocity_calibration * (*it)->angular_velocity.cast<T>()) *\n        delta_t;\n    result.delta_rotation *=\n        transform::AngleAxisVectorToRotationQuaternion(delta_angle);\n    result.delta_velocity +=\n        result.delta_rotation * ((linear_acceleration_calibration *\n                                  (*it)->linear_acceleration.cast<T>()) *\n                                 delta_t);\n    current_time = next_time;\n    if (current_time == next_imu_data) {\n      ++(*it);\n    }\n  }\n  return result;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_IMU_INTEGRATION_H_\n"
  },
  {
    "path": "mapping_3d/kalman_local_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/kalman_local_trajectory_builder.h\"\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/kalman_filter/proto/pose_tracker_options.pb.h\"\n#include \"cartographer/mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_3d/proto/submaps_options.pb.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nKalmanLocalTrajectoryBuilder::KalmanLocalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions& options)\n    : options_(options),\n      submaps_(common::make_unique<Submaps>(options.submaps_options())),\n      scan_matcher_pose_estimate_(transform::Rigid3d::Identity()),\n      motion_filter_(options.motion_filter_options()),\n      real_time_correlative_scan_matcher_(\n          common::make_unique<scan_matching::RealTimeCorrelativeScanMatcher>(\n              options_.kalman_local_trajectory_builder_options()\n                  .real_time_correlative_scan_matcher_options())),\n      ceres_scan_matcher_(common::make_unique<scan_matching::CeresScanMatcher>(\n          options_.ceres_scan_matcher_options())),\n      num_accumulated_(0),\n      first_pose_prediction_(transform::Rigid3f::Identity()),\n      accumulated_range_data_{Eigen::Vector3f::Zero(), {}, {}} {}\n\nKalmanLocalTrajectoryBuilder::~KalmanLocalTrajectoryBuilder() {}\n\nconst mapping_3d::Submaps* KalmanLocalTrajectoryBuilder::submaps() const {\n  return submaps_.get();\n}\n\nvoid KalmanLocalTrajectoryBuilder::AddImuData(\n    const common::Time time, const Eigen::Vector3d& linear_acceleration,\n    const Eigen::Vector3d& angular_velocity) {\n  if (!pose_tracker_) {\n    pose_tracker_ = common::make_unique<kalman_filter::PoseTracker>(\n        options_.kalman_local_trajectory_builder_options()\n            .pose_tracker_options(),\n        time);\n  }\n\n  pose_tracker_->AddImuLinearAccelerationObservation(time, linear_acceleration);\n  pose_tracker_->AddImuAngularVelocityObservation(time, angular_velocity);\n\n  transform::Rigid3d pose_estimate;\n  kalman_filter::PoseCovariance unused_covariance_estimate;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(time, &pose_estimate,\n                                                  &unused_covariance_estimate);\n}\n\nstd::unique_ptr<KalmanLocalTrajectoryBuilder::InsertionResult>\nKalmanLocalTrajectoryBuilder::AddRangefinderData(\n    const common::Time time, const Eigen::Vector3f& origin,\n    const sensor::PointCloud& ranges) {\n  if (!pose_tracker_) {\n    LOG(INFO) << \"PoseTracker not yet initialized.\";\n    return nullptr;\n  }\n\n  transform::Rigid3d pose_prediction;\n  kalman_filter::PoseCovariance unused_covariance_prediction;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(\n      time, &pose_prediction, &unused_covariance_prediction);\n  if (num_accumulated_ == 0) {\n    first_pose_prediction_ = pose_prediction.cast<float>();\n    accumulated_range_data_ =\n        sensor::RangeData{Eigen::Vector3f::Zero(), {}, {}};\n  }\n\n  const transform::Rigid3f tracking_delta =\n      first_pose_prediction_.inverse() * pose_prediction.cast<float>();\n  const sensor::RangeData range_data_in_first_tracking =\n      sensor::TransformRangeData(sensor::RangeData{origin, ranges, {}},\n                                 tracking_delta);\n  for (const Eigen::Vector3f& hit : range_data_in_first_tracking.returns) {\n    const Eigen::Vector3f delta = hit - range_data_in_first_tracking.origin;\n    const float range = delta.norm();\n    if (range >= options_.min_range()) {\n      if (range <= options_.max_range()) {\n        accumulated_range_data_.returns.push_back(hit);\n      } else {\n        // We insert a ray cropped to 'max_range' as a miss for hits beyond the\n        // maximum range. This way the free space up to the maximum range will\n        // be updated.\n        accumulated_range_data_.misses.push_back(\n            range_data_in_first_tracking.origin +\n            options_.max_range() / range * delta);\n      }\n    }\n  }\n  ++num_accumulated_;\n\n  if (num_accumulated_ >= options_.scans_per_accumulation()) {\n    num_accumulated_ = 0;\n    return AddAccumulatedRangeData(\n        time, sensor::TransformRangeData(accumulated_range_data_,\n                                         tracking_delta.inverse()));\n  }\n  return nullptr;\n}\n\nstd::unique_ptr<KalmanLocalTrajectoryBuilder::InsertionResult>\nKalmanLocalTrajectoryBuilder::AddAccumulatedRangeData(\n    const common::Time time, const sensor::RangeData& range_data_in_tracking) {\n  const sensor::RangeData filtered_range_data = {\n      range_data_in_tracking.origin,\n      sensor::VoxelFiltered(range_data_in_tracking.returns,\n                            options_.voxel_filter_size()),\n      sensor::VoxelFiltered(range_data_in_tracking.misses,\n                            options_.voxel_filter_size())};\n\n  if (filtered_range_data.returns.empty()) {\n    LOG(WARNING) << \"Dropped empty range data.\";\n    return nullptr;\n  }\n\n  transform::Rigid3d pose_prediction;\n  kalman_filter::PoseCovariance unused_covariance_prediction;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(\n      time, &pose_prediction, &unused_covariance_prediction);\n\n  const Submap* const matching_submap =\n      submaps_->Get(submaps_->matching_index());\n  transform::Rigid3d initial_ceres_pose =\n      matching_submap->local_pose.inverse() * pose_prediction;\n  sensor::AdaptiveVoxelFilter adaptive_voxel_filter(\n      options_.high_resolution_adaptive_voxel_filter_options());\n  const sensor::PointCloud filtered_point_cloud_in_tracking =\n      adaptive_voxel_filter.Filter(filtered_range_data.returns);\n  if (options_.kalman_local_trajectory_builder_options()\n          .use_online_correlative_scan_matching()) {\n    // We take a copy since we use 'intial_ceres_pose' as an output argument.\n    const transform::Rigid3d initial_pose = initial_ceres_pose;\n    real_time_correlative_scan_matcher_->Match(\n        initial_pose, filtered_point_cloud_in_tracking,\n        matching_submap->high_resolution_hybrid_grid, &initial_ceres_pose);\n  }\n\n  transform::Rigid3d pose_observation_in_submap;\n  ceres::Solver::Summary summary;\n\n  sensor::AdaptiveVoxelFilter low_resolution_adaptive_voxel_filter(\n      options_.low_resolution_adaptive_voxel_filter_options());\n  const sensor::PointCloud low_resolution_point_cloud_in_tracking =\n      low_resolution_adaptive_voxel_filter.Filter(filtered_range_data.returns);\n  ceres_scan_matcher_->Match(scan_matcher_pose_estimate_, initial_ceres_pose,\n                             {{&filtered_point_cloud_in_tracking,\n                               &matching_submap->high_resolution_hybrid_grid},\n                              {&low_resolution_point_cloud_in_tracking,\n                               &matching_submap->low_resolution_hybrid_grid}},\n                             &pose_observation_in_submap, &summary);\n  const transform::Rigid3d pose_observation =\n      matching_submap->local_pose * pose_observation_in_submap;\n  pose_tracker_->AddPoseObservation(\n      time, pose_observation,\n      options_.kalman_local_trajectory_builder_options()\n              .scan_matcher_variance() *\n          kalman_filter::PoseCovariance::Identity());\n\n  kalman_filter::PoseCovariance unused_covariance_estimate;\n  pose_tracker_->GetPoseEstimateMeanAndCovariance(\n      time, &scan_matcher_pose_estimate_, &unused_covariance_estimate);\n\n  last_pose_estimate_ = {\n      time, scan_matcher_pose_estimate_,\n      sensor::TransformPointCloud(filtered_range_data.returns,\n                                  pose_observation.cast<float>())};\n\n  return InsertIntoSubmap(time, filtered_range_data, pose_observation);\n}\n\nvoid KalmanLocalTrajectoryBuilder::AddOdometerData(\n    const common::Time time, const transform::Rigid3d& pose) {\n  if (!pose_tracker_) {\n    pose_tracker_.reset(new kalman_filter::PoseTracker(\n        options_.kalman_local_trajectory_builder_options()\n            .pose_tracker_options(),\n        time));\n  }\n  pose_tracker_->AddOdometerPoseObservation(\n      time, pose,\n      kalman_filter::BuildPoseCovariance(\n          options_.kalman_local_trajectory_builder_options()\n              .odometer_translational_variance(),\n          options_.kalman_local_trajectory_builder_options()\n              .odometer_rotational_variance()));\n}\n\nconst KalmanLocalTrajectoryBuilder::PoseEstimate&\nKalmanLocalTrajectoryBuilder::pose_estimate() const {\n  return last_pose_estimate_;\n}\n\nstd::unique_ptr<KalmanLocalTrajectoryBuilder::InsertionResult>\nKalmanLocalTrajectoryBuilder::InsertIntoSubmap(\n    const common::Time time, const sensor::RangeData& range_data_in_tracking,\n    const transform::Rigid3d& pose_observation) {\n  if (motion_filter_.IsSimilar(time, pose_observation)) {\n    return nullptr;\n  }\n  const Submap* const matching_submap =\n      submaps_->Get(submaps_->matching_index());\n  std::vector<const Submap*> insertion_submaps;\n  for (int insertion_index : submaps_->insertion_indices()) {\n    insertion_submaps.push_back(submaps_->Get(insertion_index));\n  }\n  submaps_->InsertRangeData(\n      sensor::TransformRangeData(range_data_in_tracking,\n                                 pose_observation.cast<float>()),\n      pose_tracker_->gravity_orientation());\n  return std::unique_ptr<InsertionResult>(\n      new InsertionResult{time, range_data_in_tracking, pose_observation,\n                          matching_submap, insertion_submaps});\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/kalman_local_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_H_\n\n#include <memory>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/kalman_filter/pose_tracker.h\"\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/local_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/motion_filter.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_3d/scan_matching/real_time_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Wires up the local SLAM stack (i.e. UKF, scan matching, etc.) without loop\n// closure.\nclass KalmanLocalTrajectoryBuilder : public LocalTrajectoryBuilderInterface {\n public:\n  explicit KalmanLocalTrajectoryBuilder(\n      const proto::LocalTrajectoryBuilderOptions& options);\n  ~KalmanLocalTrajectoryBuilder() override;\n\n  KalmanLocalTrajectoryBuilder(const KalmanLocalTrajectoryBuilder&) = delete;\n  KalmanLocalTrajectoryBuilder& operator=(const KalmanLocalTrajectoryBuilder&) =\n      delete;\n\n  void AddImuData(common::Time time, const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity) override;\n  std::unique_ptr<InsertionResult> AddRangefinderData(\n      common::Time time, const Eigen::Vector3f& origin,\n      const sensor::PointCloud& ranges) override;\n  void AddOdometerData(common::Time time,\n                       const transform::Rigid3d& pose) override;\n  const mapping_3d::Submaps* submaps() const override;\n  const PoseEstimate& pose_estimate() const override;\n\n private:\n  std::unique_ptr<InsertionResult> AddAccumulatedRangeData(\n      common::Time time, const sensor::RangeData& range_data_in_tracking);\n\n  std::unique_ptr<InsertionResult> InsertIntoSubmap(\n      common::Time time, const sensor::RangeData& range_data_in_tracking,\n      const transform::Rigid3d& pose_observation);\n\n  const proto::LocalTrajectoryBuilderOptions options_;\n  std::unique_ptr<mapping_3d::Submaps> submaps_;\n\n  PoseEstimate last_pose_estimate_;\n\n  // Pose of the last computed scan match.\n  transform::Rigid3d scan_matcher_pose_estimate_;\n\n  MotionFilter motion_filter_;\n  std::unique_ptr<scan_matching::RealTimeCorrelativeScanMatcher>\n      real_time_correlative_scan_matcher_;\n  std::unique_ptr<scan_matching::CeresScanMatcher> ceres_scan_matcher_;\n\n  std::unique_ptr<kalman_filter::PoseTracker> pose_tracker_;\n\n  int num_accumulated_;\n  transform::Rigid3f first_pose_prediction_;\n  sensor::RangeData accumulated_range_data_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_3d/kalman_local_trajectory_builder_options.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/kalman_local_trajectory_builder_options.h\"\n\n#include \"cartographer/kalman_filter/pose_tracker.h\"\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::KalmanLocalTrajectoryBuilderOptions\nCreateKalmanLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::KalmanLocalTrajectoryBuilderOptions options;\n  options.set_use_online_correlative_scan_matching(\n      parameter_dictionary->GetBool(\"use_online_correlative_scan_matching\"));\n  *options.mutable_real_time_correlative_scan_matcher_options() =\n      mapping_2d::scan_matching::CreateRealTimeCorrelativeScanMatcherOptions(\n          parameter_dictionary\n              ->GetDictionary(\"real_time_correlative_scan_matcher\")\n              .get());\n  *options.mutable_pose_tracker_options() =\n      kalman_filter::CreatePoseTrackerOptions(\n          parameter_dictionary->GetDictionary(\"pose_tracker\").get());\n  options.set_scan_matcher_variance(\n      parameter_dictionary->GetDouble(\"scan_matcher_variance\"));\n  options.set_odometer_translational_variance(\n      parameter_dictionary->GetDouble(\"odometer_translational_variance\"));\n  options.set_odometer_rotational_variance(\n      parameter_dictionary->GetDouble(\"odometer_rotational_variance\"));\n  return options;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/kalman_local_trajectory_builder_options.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n#define CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_3d/proto/kalman_local_trajectory_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::KalmanLocalTrajectoryBuilderOptions\nCreateKalmanLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_KALMAN_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n"
  },
  {
    "path": "mapping_3d/kalman_local_trajectory_builder_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/kalman_local_trajectory_builder.h\"\n\n#include <memory>\n#include <random>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/local_trajectory_builder_options.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace {\n\nclass KalmanLocalTrajectoryBuilderTest : public ::testing::Test {\n protected:\n  struct TrajectoryNode {\n    common::Time time;\n    transform::Rigid3d pose;\n  };\n\n  void SetUp() override { GenerateBubbles(); }\n\n  proto::LocalTrajectoryBuilderOptions CreateTrajectoryBuilderOptions() {\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n          min_range = 0.5,\n          max_range = 50.,\n          scans_per_accumulation = 1,\n          voxel_filter_size = 0.05,\n\n          high_resolution_adaptive_voxel_filter = {\n            max_length = 0.7,\n            min_num_points = 200,\n            max_range = 50.,\n          },\n\n          low_resolution_adaptive_voxel_filter = {\n            max_length = 0.7,\n            min_num_points = 200,\n            max_range = 50.,\n          },\n\n          ceres_scan_matcher = {\n            occupied_space_weight_0 = 5.,\n            occupied_space_weight_1 = 20.,\n            translation_weight = 0.1,\n            rotation_weight = 0.3,\n            only_optimize_yaw = false,\n            ceres_solver_options = {\n              use_nonmonotonic_steps = true,\n              max_num_iterations = 20,\n              num_threads = 1,\n            },\n          },\n          motion_filter = {\n            max_time_seconds = 0.2,\n            max_distance_meters = 0.02,\n            max_angle_radians = 0.001,\n          },\n          submaps = {\n            high_resolution = 0.2,\n            high_resolution_max_range = 50.,\n            low_resolution = 0.5,\n            num_range_data = 45000,\n            range_data_inserter = {\n              hit_probability = 0.7,\n              miss_probability = 0.4,\n              num_free_space_voxels = 0,\n            },\n          },\n\n          use = \"KALMAN\",\n          kalman_local_trajectory_builder =  {\n            use_online_correlative_scan_matching = false,\n            real_time_correlative_scan_matcher = {\n              linear_search_window = 0.2,\n              angular_search_window = math.rad(1.),\n              translation_delta_cost_weight = 1e-1,\n              rotation_delta_cost_weight = 1.,\n            },\n            pose_tracker = {\n              orientation_model_variance = 5e-4,\n              position_model_variance = 0.000654766,\n              velocity_model_variance = 0.053926,\n              imu_gravity_time_constant = 1.,\n              imu_gravity_variance = 1e-4,\n              num_odometry_states = 1,\n            },\n\n            scan_matcher_variance = 1e-6,\n            odometer_translational_variance = 1e-7,\n            odometer_rotational_variance = 1e-7,\n          },\n          optimizing_local_trajectory_builder = {\n            high_resolution_grid_weight = 5.,\n            low_resolution_grid_weight = 15.,\n            velocity_weight = 4e1,\n            translation_weight = 1e2,\n            rotation_weight = 1e3,\n            odometry_translation_weight = 1e4,\n            odometry_rotation_weight = 1e4,\n          },\n        }\n        )text\");\n    return CreateLocalTrajectoryBuilderOptions(parameter_dictionary.get());\n  }\n\n  void GenerateBubbles() {\n    std::mt19937 prng(42);\n    std::uniform_real_distribution<float> distribution(-1.f, 1.f);\n\n    for (int i = 0; i != 100; ++i) {\n      const float x = distribution(prng);\n      const float y = distribution(prng);\n      const float z = distribution(prng);\n      bubbles_.push_back(10. * Eigen::Vector3f(x, y, z).normalized());\n    }\n  }\n\n  // Computes the earliest intersection of the ray 'from' to 'to' with the\n  // axis-aligned cube with edge length 30 and center at the origin. Assumes\n  // that 'from' is inside the cube.\n  Eigen::Vector3f CollideWithBox(const Eigen::Vector3f& from,\n                                 const Eigen::Vector3f& to) {\n    float first = 100.f;\n    if (to.x() > from.x()) {\n      first = std::min(first, (15.f - from.x()) / (to.x() - from.x()));\n    } else if (to.x() < from.x()) {\n      first = std::min(first, (-15.f - from.x()) / (to.x() - from.x()));\n    }\n    if (to.y() > from.y()) {\n      first = std::min(first, (15.f - from.y()) / (to.y() - from.y()));\n    } else if (to.y() < from.y()) {\n      first = std::min(first, (-15.f - from.y()) / (to.y() - from.y()));\n    }\n    if (to.z() > from.z()) {\n      first = std::min(first, (15.f - from.z()) / (to.z() - from.z()));\n    } else if (to.z() < from.z()) {\n      first = std::min(first, (-15.f - from.z()) / (to.z() - from.z()));\n    }\n    return first * (to - from) + from;\n  }\n\n  // Computes the earliest intersection of the ray 'from' to 'to' with all\n  // bubbles. Returns 'to' if no intersection exists.\n  Eigen::Vector3f CollideWithBubbles(const Eigen::Vector3f& from,\n                                     const Eigen::Vector3f& to) {\n    float first = 1.f;\n    constexpr float kBubbleRadius = 0.5f;\n    for (const Eigen::Vector3f& center : bubbles_) {\n      const float a = (to - from).squaredNorm();\n      const float beta = (to - from).dot(from - center);\n      const float c =\n          (from - center).squaredNorm() - kBubbleRadius * kBubbleRadius;\n      const float discriminant = beta * beta - a * c;\n      if (discriminant < 0.f) {\n        continue;\n      }\n      const float solution = (-beta - std::sqrt(discriminant)) / a;\n      if (solution < 0.f) {\n        continue;\n      }\n      first = std::min(first, solution);\n    }\n    return first * (to - from) + from;\n  }\n\n  sensor::RangeData GenerateRangeData(const transform::Rigid3d& pose) {\n    // 360 degree rays at 16 angles.\n    sensor::PointCloud directions_in_rangefinder_frame;\n    for (int r = -8; r != 8; ++r) {\n      for (int s = -250; s != 250; ++s) {\n        directions_in_rangefinder_frame.push_back(\n            Eigen::AngleAxisf(M_PI * s / 250., Eigen::Vector3f::UnitZ()) *\n            Eigen::AngleAxisf(M_PI / 12. * r / 8., Eigen::Vector3f::UnitY()) *\n            Eigen::Vector3f::UnitX());\n        // Second orthogonal rangefinder.\n        directions_in_rangefinder_frame.push_back(\n            Eigen::AngleAxisf(M_PI / 2., Eigen::Vector3f::UnitX()) *\n            Eigen::AngleAxisf(M_PI * s / 250., Eigen::Vector3f::UnitZ()) *\n            Eigen::AngleAxisf(M_PI / 12. * r / 8., Eigen::Vector3f::UnitY()) *\n            Eigen::Vector3f::UnitX());\n      }\n    }\n    // We simulate a 30 m edge length box around the origin, also containing\n    // 50 cm radius spheres.\n    sensor::PointCloud returns_in_world_frame;\n    for (const Eigen::Vector3f& direction_in_world_frame :\n         sensor::TransformPointCloud(directions_in_rangefinder_frame,\n                                     pose.cast<float>())) {\n      const Eigen::Vector3f origin =\n          pose.cast<float>() * Eigen::Vector3f::Zero();\n      returns_in_world_frame.push_back(CollideWithBubbles(\n          origin, CollideWithBox(origin, direction_in_world_frame)));\n    }\n    return {Eigen::Vector3f::Zero(),\n            sensor::TransformPointCloud(returns_in_world_frame,\n                                        pose.inverse().cast<float>()),\n            {}};\n  }\n\n  void AddLinearOnlyImuObservation(const common::Time time,\n                                   const transform::Rigid3d& expected_pose) {\n    const Eigen::Vector3d gravity =\n        expected_pose.rotation().inverse() * Eigen::Vector3d(0., 0., 9.81);\n    local_trajectory_builder_->AddImuData(time, gravity,\n                                          Eigen::Vector3d::Zero());\n  }\n\n  std::vector<TrajectoryNode> GenerateCorkscrewTrajectory() {\n    std::vector<TrajectoryNode> trajectory;\n    common::Time current_time = common::FromUniversal(12345678);\n    // One second at zero velocity.\n    for (int i = 0; i != 5; ++i) {\n      current_time += common::FromSeconds(0.3);\n      trajectory.push_back(\n          TrajectoryNode{current_time, transform::Rigid3d::Identity()});\n    }\n    // Corkscrew translation and constant velocity rotation.\n    for (double t = 0.; t <= 0.6; t += 0.006) {\n      current_time += common::FromSeconds(0.3);\n      trajectory.push_back(TrajectoryNode{\n          current_time,\n          transform::Rigid3d::Translation(Eigen::Vector3d(\n              std::sin(4. * t), 1. - std::cos(4. * t), 1. * t)) *\n              transform::Rigid3d::Rotation(Eigen::AngleAxisd(\n                  0.3 * t, Eigen::Vector3d(1., -1., 2.).normalized()))});\n    }\n    return trajectory;\n  }\n\n  void VerifyAccuracy(const std::vector<TrajectoryNode>& expected_trajectory,\n                      double expected_accuracy) {\n    int num_poses = 0;\n    for (const TrajectoryNode& node : expected_trajectory) {\n      AddLinearOnlyImuObservation(node.time, node.pose);\n      const auto range_data = GenerateRangeData(node.pose);\n      if (local_trajectory_builder_->AddRangefinderData(\n              node.time, range_data.origin, range_data.returns) != nullptr) {\n        const auto pose_estimate = local_trajectory_builder_->pose_estimate();\n        EXPECT_THAT(pose_estimate.pose, transform::IsNearly(node.pose, 1e-1));\n        ++num_poses;\n        LOG(INFO) << \"num_poses: \" << num_poses;\n      }\n    }\n  }\n\n  std::unique_ptr<KalmanLocalTrajectoryBuilder> local_trajectory_builder_;\n  std::vector<Eigen::Vector3f> bubbles_;\n};\n\nTEST_F(KalmanLocalTrajectoryBuilderTest,\n       MoveInsideCubeUsingOnlyCeresScanMatcher) {\n  local_trajectory_builder_.reset(\n      new KalmanLocalTrajectoryBuilder(CreateTrajectoryBuilderOptions()));\n  VerifyAccuracy(GenerateCorkscrewTrajectory(), 1e-1);\n}\n\n}  // namespace\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/local_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/local_trajectory_builder.h\"\n\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping_3d/kalman_local_trajectory_builder.h\"\n#include \"cartographer/mapping_3d/optimizing_local_trajectory_builder.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nstd::unique_ptr<LocalTrajectoryBuilderInterface> CreateLocalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions&\n        local_trajectory_builder_options) {\n  switch (local_trajectory_builder_options.use()) {\n    case proto::LocalTrajectoryBuilderOptions::KALMAN:\n      return common::make_unique<KalmanLocalTrajectoryBuilder>(\n          local_trajectory_builder_options);\n    case proto::LocalTrajectoryBuilderOptions::OPTIMIZING:\n      return common::make_unique<OptimizingLocalTrajectoryBuilder>(\n          local_trajectory_builder_options);\n  }\n  LOG(FATAL);\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/local_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"cartographer/mapping_3d/local_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nstd::unique_ptr<LocalTrajectoryBuilderInterface> CreateLocalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions&\n        local_trajectory_builder_options);\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_3d/local_trajectory_builder_interface.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_INTERFACE_H_\n#define CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_INTERFACE_H_\n\n#include <memory>\n#include <vector>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nclass LocalTrajectoryBuilderInterface {\n public:\n  using PoseEstimate = mapping::GlobalTrajectoryBuilderInterface::PoseEstimate;\n\n  struct InsertionResult {\n    common::Time time;\n    sensor::RangeData range_data_in_tracking;\n    transform::Rigid3d pose_observation;\n    const Submap* matching_submap;\n    std::vector<const Submap*> insertion_submaps;\n  };\n\n  virtual ~LocalTrajectoryBuilderInterface() {}\n\n  LocalTrajectoryBuilderInterface(const LocalTrajectoryBuilderInterface&) =\n      delete;\n  LocalTrajectoryBuilderInterface& operator=(\n      const LocalTrajectoryBuilderInterface&) = delete;\n\n  virtual void AddImuData(common::Time time,\n                          const Eigen::Vector3d& linear_acceleration,\n                          const Eigen::Vector3d& angular_velocity) = 0;\n  virtual std::unique_ptr<InsertionResult> AddRangefinderData(\n      common::Time time, const Eigen::Vector3f& origin,\n      const sensor::PointCloud& ranges) = 0;\n  virtual void AddOdometerData(common::Time time,\n                               const transform::Rigid3d& pose) = 0;\n\n  virtual const mapping_3d::Submaps* submaps() const = 0;\n  virtual const PoseEstimate& pose_estimate() const = 0;\n\n protected:\n  LocalTrajectoryBuilderInterface() {}\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_INTERFACE_H_\n"
  },
  {
    "path": "mapping_3d/local_trajectory_builder_options.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/local_trajectory_builder_options.h\"\n\n#include \"cartographer/mapping_3d/kalman_local_trajectory_builder_options.h\"\n#include \"cartographer/mapping_3d/motion_filter.h\"\n#include \"cartographer/mapping_3d/optimizing_local_trajectory_builder_options.h\"\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::LocalTrajectoryBuilderOptions CreateLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::LocalTrajectoryBuilderOptions options;\n  options.set_min_range(parameter_dictionary->GetDouble(\"min_range\"));\n  options.set_max_range(parameter_dictionary->GetDouble(\"max_range\"));\n  options.set_scans_per_accumulation(\n      parameter_dictionary->GetInt(\"scans_per_accumulation\"));\n  options.set_voxel_filter_size(\n      parameter_dictionary->GetDouble(\"voxel_filter_size\"));\n  *options.mutable_high_resolution_adaptive_voxel_filter_options() =\n      sensor::CreateAdaptiveVoxelFilterOptions(\n          parameter_dictionary\n              ->GetDictionary(\"high_resolution_adaptive_voxel_filter\")\n              .get());\n  *options.mutable_low_resolution_adaptive_voxel_filter_options() =\n      sensor::CreateAdaptiveVoxelFilterOptions(\n          parameter_dictionary\n              ->GetDictionary(\"low_resolution_adaptive_voxel_filter\")\n              .get());\n  *options.mutable_ceres_scan_matcher_options() =\n      scan_matching::CreateCeresScanMatcherOptions(\n          parameter_dictionary->GetDictionary(\"ceres_scan_matcher\").get());\n  *options.mutable_motion_filter_options() = CreateMotionFilterOptions(\n      parameter_dictionary->GetDictionary(\"motion_filter\").get());\n  *options.mutable_submaps_options() = mapping_3d::CreateSubmapsOptions(\n      parameter_dictionary->GetDictionary(\"submaps\").get());\n  *options.mutable_kalman_local_trajectory_builder_options() =\n      CreateKalmanLocalTrajectoryBuilderOptions(\n          parameter_dictionary->GetDictionary(\"kalman_local_trajectory_builder\")\n              .get());\n  *options.mutable_optimizing_local_trajectory_builder_options() =\n      CreateOptimizingLocalTrajectoryBuilderOptions(\n          parameter_dictionary\n              ->GetDictionary(\"optimizing_local_trajectory_builder\")\n              .get());\n  const string use_string = parameter_dictionary->GetString(\"use\");\n  proto::LocalTrajectoryBuilderOptions::Use use;\n  CHECK(proto::LocalTrajectoryBuilderOptions::Use_Parse(use_string, &use))\n      << \"Unknown local_trajectory_builder kind: \" << use_string;\n  options.set_use(use);\n  return options;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/local_trajectory_builder_options.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n#define CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::LocalTrajectoryBuilderOptions CreateLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n"
  },
  {
    "path": "mapping_3d/motion_filter.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/motion_filter.h\"\n\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::MotionFilterOptions CreateMotionFilterOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::MotionFilterOptions options;\n  options.set_max_time_seconds(\n      parameter_dictionary->GetDouble(\"max_time_seconds\"));\n  options.set_max_distance_meters(\n      parameter_dictionary->GetDouble(\"max_distance_meters\"));\n  options.set_max_angle_radians(\n      parameter_dictionary->GetDouble(\"max_angle_radians\"));\n  return options;\n}\n\nMotionFilter::MotionFilter(const proto::MotionFilterOptions& options)\n    : options_(options) {}\n\nbool MotionFilter::IsSimilar(const common::Time time,\n                             const transform::Rigid3d& pose) {\n  LOG_IF_EVERY_N(INFO, num_total_ >= 500, 500)\n      << \"Motion filter reduced the number of scans to \"\n      << 100. * num_different_ / num_total_ << \"%.\";\n  ++num_total_;\n  if (num_total_ > 1 &&\n      time - last_time_ <= common::FromSeconds(options_.max_time_seconds()) &&\n      (pose.translation() - last_pose_.translation()).norm() <=\n          options_.max_distance_meters() &&\n      transform::GetAngle(pose.inverse() * last_pose_) <=\n          options_.max_angle_radians()) {\n    return true;\n  }\n  last_time_ = time;\n  last_pose_ = pose;\n  ++num_different_;\n  return false;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/motion_filter.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_MOTION_FILTER_H_\n#define CARTOGRAPHER_MAPPING_3D_MOTION_FILTER_H_\n\n#include <limits>\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping_3d/proto/motion_filter_options.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::MotionFilterOptions CreateMotionFilterOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n// Takes poses as input and filters them to get fewer poses.\nclass MotionFilter {\n public:\n  explicit MotionFilter(const proto::MotionFilterOptions& options);\n\n  // If the accumulated motion (linear, rotational, or time) is above the\n  // threshold, returns false. Otherwise the relative motion is accumulated and\n  // true is returned.\n  bool IsSimilar(common::Time time, const transform::Rigid3d& pose);\n\n private:\n  const proto::MotionFilterOptions options_;\n  int num_total_ = 0;\n  int num_different_ = 0;\n  common::Time last_time_;\n  transform::Rigid3d last_pose_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_MOTION_FILTER_H_\n"
  },
  {
    "path": "mapping_3d/motion_filter_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/motion_filter.h\"\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace {\n\nclass MotionFilterTest : public ::testing::Test {\n protected:\n  MotionFilterTest() {\n    auto parameter_dictionary = common::MakeDictionary(\n        \"return {\"\n        \"max_time_seconds = 0.5, \"\n        \"max_distance_meters = 0.2, \"\n        \"max_angle_radians = 2., \"\n        \"}\");\n    options_ = CreateMotionFilterOptions(parameter_dictionary.get());\n  }\n\n  common::Time SecondsSinceEpoch(int seconds) {\n    return common::FromUniversal(seconds * 10000000);\n  }\n\n  proto::MotionFilterOptions options_;\n};\n\nTEST_F(MotionFilterTest, NotInitialized) {\n  MotionFilter motion_filter(options_);\n  EXPECT_FALSE(\n      motion_filter.IsSimilar(common::Time(), transform::Rigid3d::Identity()));\n}\n\nTEST_F(MotionFilterTest, NoChange) {\n  MotionFilter motion_filter(options_);\n  EXPECT_FALSE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                       transform::Rigid3d::Identity()));\n  EXPECT_TRUE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                      transform::Rigid3d::Identity()));\n}\n\nTEST_F(MotionFilterTest, TimeElapsed) {\n  MotionFilter motion_filter(options_);\n  EXPECT_FALSE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                       transform::Rigid3d::Identity()));\n  EXPECT_FALSE(motion_filter.IsSimilar(SecondsSinceEpoch(43),\n                                       transform::Rigid3d::Identity()));\n  EXPECT_TRUE(motion_filter.IsSimilar(SecondsSinceEpoch(43),\n                                      transform::Rigid3d::Identity()));\n}\n\nTEST_F(MotionFilterTest, LinearMotion) {\n  MotionFilter motion_filter(options_);\n  EXPECT_FALSE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                       transform::Rigid3d::Identity()));\n  EXPECT_FALSE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42),\n      transform::Rigid3d::Translation(Eigen::Vector3d(0.3, 0., 0.))));\n  EXPECT_TRUE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42),\n      transform::Rigid3d::Translation(Eigen::Vector3d(0.45, 0., 0.))));\n  EXPECT_FALSE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42),\n      transform::Rigid3d::Translation(Eigen::Vector3d(0.6, 0., 0.))));\n  EXPECT_TRUE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42),\n      transform::Rigid3d::Translation(Eigen::Vector3d(0.6, 0.15, 0.))));\n}\n\nTEST_F(MotionFilterTest, RotationalMotion) {\n  MotionFilter motion_filter(options_);\n  EXPECT_FALSE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                       transform::Rigid3d::Identity()));\n  EXPECT_TRUE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42), transform::Rigid3d::Rotation(Eigen::AngleAxisd(\n                                 1.9, Eigen::Vector3d::UnitY()))));\n  EXPECT_FALSE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42), transform::Rigid3d::Rotation(Eigen::AngleAxisd(\n                                 2.1, Eigen::Vector3d::UnitY()))));\n  EXPECT_TRUE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42), transform::Rigid3d::Rotation(Eigen::AngleAxisd(\n                                 4., Eigen::Vector3d::UnitY()))));\n  EXPECT_FALSE(motion_filter.IsSimilar(\n      SecondsSinceEpoch(42), transform::Rigid3d::Rotation(Eigen::AngleAxisd(\n                                 5.9, Eigen::Vector3d::UnitY()))));\n  EXPECT_TRUE(motion_filter.IsSimilar(SecondsSinceEpoch(42),\n                                      transform::Rigid3d::Identity()));\n}\n\n}  // namespace\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/optimizing_local_trajectory_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/optimizing_local_trajectory_builder.h\"\n\n#include \"cartographer/common/ceres_solver_options.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping_3d/proto/optimizing_local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_3d/rotation_cost_function.h\"\n#include \"cartographer/mapping_3d/scan_matching/occupied_space_cost_functor.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_3d/scan_matching/translation_delta_cost_functor.h\"\n#include \"cartographer/mapping_3d/translation_cost_function.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"cartographer/transform/transform_interpolation_buffer.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nnamespace {\n\n// Computes the cost of differences between two velocities. For the constant\n// velocity model the residuals are just the vector difference.\nclass VelocityDeltaCostFunctor {\n public:\n  explicit VelocityDeltaCostFunctor(const double scaling_factor)\n      : scaling_factor_(scaling_factor) {}\n\n  VelocityDeltaCostFunctor(const VelocityDeltaCostFunctor&) = delete;\n  VelocityDeltaCostFunctor& operator=(const VelocityDeltaCostFunctor&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const old_velocity, const T* const new_velocity,\n                  T* residual) const {\n    residual[0] = scaling_factor_ * (new_velocity[0] - old_velocity[0]);\n    residual[1] = scaling_factor_ * (new_velocity[1] - old_velocity[1]);\n    residual[2] = scaling_factor_ * (new_velocity[2] - old_velocity[2]);\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n};\n\nclass RelativeTranslationAndYawCostFunction {\n public:\n  RelativeTranslationAndYawCostFunction(const double translation_scaling_factor,\n                                        const double rotation_scaling_factor,\n                                        const transform::Rigid3d& delta)\n      : translation_scaling_factor_(translation_scaling_factor),\n        rotation_scaling_factor_(rotation_scaling_factor),\n        delta_(delta) {}\n\n  RelativeTranslationAndYawCostFunction(\n      const RelativeTranslationAndYawCostFunction&) = delete;\n  RelativeTranslationAndYawCostFunction& operator=(\n      const RelativeTranslationAndYawCostFunction&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const start_translation,\n                  const T* const start_rotation, const T* const end_translation,\n                  const T* const end_rotation, T* residual) const {\n    const transform::Rigid3<T> start(\n        Eigen::Map<const Eigen::Matrix<T, 3, 1>>(start_translation),\n        Eigen::Quaternion<T>(start_rotation[0], start_rotation[1],\n                             start_rotation[2], start_rotation[3]));\n    const transform::Rigid3<T> end(\n        Eigen::Map<const Eigen::Matrix<T, 3, 1>>(end_translation),\n        Eigen::Quaternion<T>(end_rotation[0], end_rotation[1], end_rotation[2],\n                             end_rotation[3]));\n\n    const transform::Rigid3<T> delta = end.inverse() * start;\n    const transform::Rigid3<T> error = delta.inverse() * delta_.cast<T>();\n    residual[0] = translation_scaling_factor_ * error.translation().x();\n    residual[1] = translation_scaling_factor_ * error.translation().y();\n    residual[2] = translation_scaling_factor_ * error.translation().z();\n    residual[3] = rotation_scaling_factor_ * transform::GetYaw(error);\n    return true;\n  }\n\n private:\n  const double translation_scaling_factor_;\n  const double rotation_scaling_factor_;\n  const transform::Rigid3d delta_;\n};\n\n}  // namespace\n\nOptimizingLocalTrajectoryBuilder::OptimizingLocalTrajectoryBuilder(\n    const proto::LocalTrajectoryBuilderOptions& options)\n    : options_(options),\n      ceres_solver_options_(common::CreateCeresSolverOptions(\n          options.ceres_scan_matcher_options().ceres_solver_options())),\n      submaps_(common::make_unique<Submaps>(options.submaps_options())),\n      num_accumulated_(0),\n      motion_filter_(options.motion_filter_options()) {}\n\nOptimizingLocalTrajectoryBuilder::~OptimizingLocalTrajectoryBuilder() {}\n\nconst mapping_3d::Submaps* OptimizingLocalTrajectoryBuilder::submaps() const {\n  return submaps_.get();\n}\n\nvoid OptimizingLocalTrajectoryBuilder::AddImuData(\n    const common::Time time, const Eigen::Vector3d& linear_acceleration,\n    const Eigen::Vector3d& angular_velocity) {\n  imu_data_.push_back(ImuData{\n      time, linear_acceleration, angular_velocity,\n  });\n  RemoveObsoleteSensorData();\n}\n\nvoid OptimizingLocalTrajectoryBuilder::AddOdometerData(\n    const common::Time time, const transform::Rigid3d& pose) {\n  odometer_data_.push_back(OdometerData{time, pose});\n  RemoveObsoleteSensorData();\n}\n\nstd::unique_ptr<OptimizingLocalTrajectoryBuilder::InsertionResult>\nOptimizingLocalTrajectoryBuilder::AddRangefinderData(\n    const common::Time time, const Eigen::Vector3f& origin,\n    const sensor::PointCloud& ranges) {\n  CHECK_GT(ranges.size(), 0);\n\n  // TODO(hrapp): Handle misses.\n  // TODO(hrapp): Where are NaNs in range_data_in_tracking coming from?\n  sensor::PointCloud point_cloud;\n  for (const Eigen::Vector3f& hit : ranges) {\n    const Eigen::Vector3f delta = hit - origin;\n    const float range = delta.norm();\n    if (range >= options_.min_range()) {\n      if (range <= options_.max_range()) {\n        point_cloud.push_back(hit);\n      }\n    }\n  }\n\n  auto high_resolution_options =\n      options_.high_resolution_adaptive_voxel_filter_options();\n  high_resolution_options.set_min_num_points(\n      high_resolution_options.min_num_points() /\n      options_.scans_per_accumulation());\n  sensor::AdaptiveVoxelFilter high_resolution_adaptive_voxel_filter(\n      high_resolution_options);\n  const sensor::PointCloud high_resolution_filtered_points =\n      high_resolution_adaptive_voxel_filter.Filter(point_cloud);\n\n  auto low_resolution_options =\n      options_.low_resolution_adaptive_voxel_filter_options();\n  low_resolution_options.set_min_num_points(\n      low_resolution_options.min_num_points() /\n      options_.scans_per_accumulation());\n  sensor::AdaptiveVoxelFilter low_resolution_adaptive_voxel_filter(\n      low_resolution_options);\n  const sensor::PointCloud low_resolution_filtered_points =\n      low_resolution_adaptive_voxel_filter.Filter(point_cloud);\n\n  if (batches_.empty()) {\n    // First rangefinder data ever. Initialize to the origin.\n    batches_.push_back(\n        Batch{time, point_cloud, high_resolution_filtered_points,\n              low_resolution_filtered_points,\n              State(Eigen::Vector3d::Zero(), Eigen::Quaterniond::Identity(),\n                    Eigen::Vector3d::Zero())});\n  } else {\n    const Batch& last_batch = batches_.back();\n    batches_.push_back(Batch{\n        time, point_cloud, high_resolution_filtered_points,\n        low_resolution_filtered_points,\n        PredictState(last_batch.state, last_batch.time, time),\n    });\n  }\n  ++num_accumulated_;\n\n  RemoveObsoleteSensorData();\n  return MaybeOptimize(time);\n}\n\nvoid OptimizingLocalTrajectoryBuilder::RemoveObsoleteSensorData() {\n  if (imu_data_.empty()) {\n    batches_.clear();\n    return;\n  }\n\n  while (batches_.size() >\n         static_cast<size_t>(options_.scans_per_accumulation())) {\n    batches_.pop_front();\n  }\n\n  while (imu_data_.size() > 1 &&\n         (batches_.empty() || imu_data_[1].time <= batches_.front().time)) {\n    imu_data_.pop_front();\n  }\n\n  while (\n      odometer_data_.size() > 1 &&\n      (batches_.empty() || odometer_data_[1].time <= batches_.front().time)) {\n    odometer_data_.pop_front();\n  }\n}\n\nvoid OptimizingLocalTrajectoryBuilder::TransformStates(\n    const transform::Rigid3d& transform) {\n  for (Batch& batch : batches_) {\n    const transform::Rigid3d new_pose = transform * batch.state.ToRigid();\n    const auto& velocity = batch.state.velocity;\n    const Eigen::Vector3d new_velocity =\n        transform.rotation() *\n        Eigen::Vector3d(velocity[0], velocity[1], velocity[2]);\n    batch.state =\n        State(new_pose.translation(), new_pose.rotation(), new_velocity);\n  }\n}\n\nstd::unique_ptr<OptimizingLocalTrajectoryBuilder::InsertionResult>\nOptimizingLocalTrajectoryBuilder::MaybeOptimize(const common::Time time) {\n  // TODO(hrapp): Make the number of optimizations configurable.\n  if (num_accumulated_ < options_.scans_per_accumulation() &&\n      num_accumulated_ % 10 != 0) {\n    return nullptr;\n  }\n\n  ceres::Problem problem;\n  const Submap* const matching_submap =\n      submaps_->Get(submaps_->matching_index());\n  // We transform the states in 'batches_' in place to be in the submap frame as\n  // expected by the OccupiedSpaceCostFunctor. This is reverted after solving\n  // the optimization problem.\n  TransformStates(matching_submap->local_pose.inverse());\n  for (size_t i = 0; i < batches_.size(); ++i) {\n    Batch& batch = batches_[i];\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<scan_matching::OccupiedSpaceCostFunctor,\n                                        ceres::DYNAMIC, 3, 4>(\n            new scan_matching::OccupiedSpaceCostFunctor(\n                options_.optimizing_local_trajectory_builder_options()\n                        .high_resolution_grid_weight() /\n                    std::sqrt(static_cast<double>(\n                        batch.high_resolution_filtered_points.size())),\n                batch.high_resolution_filtered_points,\n                matching_submap->high_resolution_hybrid_grid),\n            batch.high_resolution_filtered_points.size()),\n        nullptr, batch.state.translation.data(), batch.state.rotation.data());\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<scan_matching::OccupiedSpaceCostFunctor,\n                                        ceres::DYNAMIC, 3, 4>(\n            new scan_matching::OccupiedSpaceCostFunctor(\n                options_.optimizing_local_trajectory_builder_options()\n                        .low_resolution_grid_weight() /\n                    std::sqrt(static_cast<double>(\n                        batch.low_resolution_filtered_points.size())),\n                batch.low_resolution_filtered_points,\n                matching_submap->low_resolution_hybrid_grid),\n            batch.low_resolution_filtered_points.size()),\n        nullptr, batch.state.translation.data(), batch.state.rotation.data());\n\n    if (i == 0) {\n      problem.SetParameterBlockConstant(batch.state.translation.data());\n      problem.SetParameterBlockConstant(batch.state.rotation.data());\n      problem.AddParameterBlock(batch.state.velocity.data(), 3);\n      problem.SetParameterBlockConstant(batch.state.velocity.data());\n    } else {\n      problem.SetParameterization(batch.state.rotation.data(),\n                                  new ceres::QuaternionParameterization());\n    }\n  }\n\n  auto it = imu_data_.cbegin();\n  for (size_t i = 1; i < batches_.size(); ++i) {\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<VelocityDeltaCostFunctor, 3, 3, 3>(\n            new VelocityDeltaCostFunctor(\n                options_.optimizing_local_trajectory_builder_options()\n                    .velocity_weight())),\n        nullptr, batches_[i - 1].state.velocity.data(),\n        batches_[i].state.velocity.data());\n\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<TranslationCostFunction, 3, 3, 3, 3>(\n            new TranslationCostFunction(\n                options_.optimizing_local_trajectory_builder_options()\n                    .translation_weight(),\n                common::ToSeconds(batches_[i].time - batches_[i - 1].time))),\n        nullptr, batches_[i - 1].state.translation.data(),\n        batches_[i].state.translation.data(),\n        batches_[i - 1].state.velocity.data());\n\n    const IntegrateImuResult<double> result =\n        IntegrateImu(imu_data_, batches_[i - 1].time, batches_[i].time, &it);\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<RotationCostFunction, 3, 4, 4>(\n            new RotationCostFunction(\n                options_.optimizing_local_trajectory_builder_options()\n                    .rotation_weight(),\n                result.delta_rotation)),\n        nullptr, batches_[i - 1].state.rotation.data(),\n        batches_[i].state.rotation.data());\n  }\n\n  if (odometer_data_.size() > 1) {\n    transform::TransformInterpolationBuffer interpolation_buffer;\n    for (const auto& odometer_data : odometer_data_) {\n      interpolation_buffer.Push(odometer_data.time, odometer_data.pose);\n    }\n    for (size_t i = 1; i < batches_.size(); ++i) {\n      // Only add constraints for this range data if  we have bracketing data\n      // from the odometer.\n      if (!(interpolation_buffer.earliest_time() <= batches_[i - 1].time &&\n            batches_[i].time <= interpolation_buffer.latest_time())) {\n        continue;\n      }\n      const transform::Rigid3d previous_odometer_pose =\n          interpolation_buffer.Lookup(batches_[i - 1].time);\n      const transform::Rigid3d current_odometer_pose =\n          interpolation_buffer.Lookup(batches_[i].time);\n      const transform::Rigid3d delta_pose =\n          current_odometer_pose.inverse() * previous_odometer_pose;\n      problem.AddResidualBlock(\n          new ceres::AutoDiffCostFunction<RelativeTranslationAndYawCostFunction,\n                                          4, 3, 4, 3, 4>(\n              new RelativeTranslationAndYawCostFunction(\n                  options_.optimizing_local_trajectory_builder_options()\n                      .odometry_translation_weight(),\n                  options_.optimizing_local_trajectory_builder_options()\n                      .odometry_rotation_weight(),\n                  delta_pose)),\n          nullptr, batches_[i - 1].state.translation.data(),\n          batches_[i - 1].state.rotation.data(),\n          batches_[i].state.translation.data(),\n          batches_[i].state.rotation.data());\n    }\n  }\n\n  ceres::Solver::Summary summary;\n  ceres::Solve(ceres_solver_options_, &problem, &summary);\n  // The optimized states in 'batches_' are in the submap frame and we transform\n  // them in place to be in the local SLAM frame again.\n  TransformStates(matching_submap->local_pose);\n  if (num_accumulated_ < options_.scans_per_accumulation()) {\n    return nullptr;\n  }\n\n  num_accumulated_ = 0;\n\n  const transform::Rigid3d optimized_pose = batches_.back().state.ToRigid();\n  sensor::RangeData accumulated_range_data_in_tracking = {\n      Eigen::Vector3f::Zero(), {}, {}};\n\n  for (const auto& batch : batches_) {\n    const transform::Rigid3f transform =\n        (optimized_pose.inverse() * batch.state.ToRigid()).cast<float>();\n    for (const Eigen::Vector3f& point : batch.points) {\n      accumulated_range_data_in_tracking.returns.push_back(transform * point);\n    }\n  }\n\n  return AddAccumulatedRangeData(time, optimized_pose,\n                                 accumulated_range_data_in_tracking);\n}\n\nstd::unique_ptr<OptimizingLocalTrajectoryBuilder::InsertionResult>\nOptimizingLocalTrajectoryBuilder::AddAccumulatedRangeData(\n    const common::Time time, const transform::Rigid3d& optimized_pose,\n    const sensor::RangeData& range_data_in_tracking) {\n  const sensor::RangeData filtered_range_data = {\n      range_data_in_tracking.origin,\n      sensor::VoxelFiltered(range_data_in_tracking.returns,\n                            options_.voxel_filter_size()),\n      sensor::VoxelFiltered(range_data_in_tracking.misses,\n                            options_.voxel_filter_size())};\n\n  if (filtered_range_data.returns.empty()) {\n    LOG(WARNING) << \"Dropped empty range data.\";\n    return nullptr;\n  }\n\n  last_pose_estimate_ = {\n      time, optimized_pose,\n      sensor::TransformPointCloud(filtered_range_data.returns,\n                                  optimized_pose.cast<float>())};\n\n  return InsertIntoSubmap(time, filtered_range_data, optimized_pose);\n}\n\nconst OptimizingLocalTrajectoryBuilder::PoseEstimate&\nOptimizingLocalTrajectoryBuilder::pose_estimate() const {\n  return last_pose_estimate_;\n}\n\nstd::unique_ptr<OptimizingLocalTrajectoryBuilder::InsertionResult>\nOptimizingLocalTrajectoryBuilder::InsertIntoSubmap(\n    const common::Time time, const sensor::RangeData& range_data_in_tracking,\n    const transform::Rigid3d& pose_observation) {\n  if (motion_filter_.IsSimilar(time, pose_observation)) {\n    return nullptr;\n  }\n  const Submap* const matching_submap =\n      submaps_->Get(submaps_->matching_index());\n  std::vector<const Submap*> insertion_submaps;\n  for (int insertion_index : submaps_->insertion_indices()) {\n    insertion_submaps.push_back(submaps_->Get(insertion_index));\n  }\n  // TODO(whess): Use an ImuTracker to track the gravity direction.\n  const Eigen::Quaterniond kFakeGravityOrientation =\n      Eigen::Quaterniond::Identity();\n  submaps_->InsertRangeData(\n      sensor::TransformRangeData(range_data_in_tracking,\n                                 pose_observation.cast<float>()),\n      kFakeGravityOrientation);\n\n  return std::unique_ptr<InsertionResult>(\n      new InsertionResult{time, range_data_in_tracking, pose_observation,\n                          matching_submap, insertion_submaps});\n}\n\nOptimizingLocalTrajectoryBuilder::State\nOptimizingLocalTrajectoryBuilder::PredictState(const State& start_state,\n                                               const common::Time start_time,\n                                               const common::Time end_time) {\n  auto it = --imu_data_.cend();\n  while (it->time > start_time) {\n    CHECK(it != imu_data_.cbegin());\n    --it;\n  }\n\n  const IntegrateImuResult<double> result =\n      IntegrateImu(imu_data_, start_time, end_time, &it);\n\n  const Eigen::Quaterniond start_rotation(\n      start_state.rotation[0], start_state.rotation[1], start_state.rotation[2],\n      start_state.rotation[3]);\n  const Eigen::Quaterniond orientation = start_rotation * result.delta_rotation;\n  const double delta_time_seconds = common::ToSeconds(end_time - start_time);\n\n  // TODO(hrapp): IntegrateImu should integration position as well.\n  const Eigen::Vector3d position =\n      Eigen::Map<const Eigen::Vector3d>(start_state.translation.data()) +\n      delta_time_seconds *\n          Eigen::Map<const Eigen::Vector3d>(start_state.velocity.data());\n  const Eigen::Vector3d velocity =\n      Eigen::Map<const Eigen::Vector3d>(start_state.velocity.data()) +\n      start_rotation * result.delta_velocity -\n      gravity_constant_ * delta_time_seconds * Eigen::Vector3d::UnitZ();\n\n  return State(position, orientation, velocity);\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/optimizing_local_trajectory_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_H_\n\n#include <array>\n#include <deque>\n#include <memory>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/global_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/imu_integration.h\"\n#include \"cartographer/mapping_3d/local_trajectory_builder_interface.h\"\n#include \"cartographer/mapping_3d/motion_filter.h\"\n#include \"cartographer/mapping_3d/proto/local_trajectory_builder_options.pb.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Batches up some sensor data and optimizes them in one go to get a locally\n// consistent trajectory.\nclass OptimizingLocalTrajectoryBuilder\n    : public LocalTrajectoryBuilderInterface {\n public:\n  explicit OptimizingLocalTrajectoryBuilder(\n      const proto::LocalTrajectoryBuilderOptions& options);\n  ~OptimizingLocalTrajectoryBuilder() override;\n\n  OptimizingLocalTrajectoryBuilder(const OptimizingLocalTrajectoryBuilder&) =\n      delete;\n  OptimizingLocalTrajectoryBuilder& operator=(\n      const OptimizingLocalTrajectoryBuilder&) = delete;\n\n  void AddImuData(common::Time time, const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity) override;\n  std::unique_ptr<InsertionResult> AddRangefinderData(\n      common::Time time, const Eigen::Vector3f& origin,\n      const sensor::PointCloud& ranges) override;\n  void AddOdometerData(common::Time time,\n                       const transform::Rigid3d& pose) override;\n  const mapping_3d::Submaps* submaps() const override;\n  const PoseEstimate& pose_estimate() const override;\n\n private:\n  struct State {\n    std::array<double, 3> translation;\n    std::array<double, 4> rotation;  // Rotation quaternion as (w, x, y, z).\n    std::array<double, 3> velocity;\n\n    State(const Eigen::Vector3d& translation,\n          const Eigen::Quaterniond& rotation, const Eigen::Vector3d& velocity)\n        : translation{{translation.x(), translation.y(), translation.z()}},\n          rotation{{rotation.w(), rotation.x(), rotation.y(), rotation.z()}},\n          velocity{{velocity.x(), velocity.y(), velocity.z()}} {}\n\n    Eigen::Quaterniond ToQuaternion() const {\n      return Eigen::Quaterniond(rotation[0], rotation[1], rotation[2],\n                                rotation[3]);\n    }\n\n    transform::Rigid3d ToRigid() const {\n      return transform::Rigid3d(\n          Eigen::Vector3d(translation[0], translation[1], translation[2]),\n          ToQuaternion());\n    }\n  };\n\n  struct Batch {\n    common::Time time;\n    sensor::PointCloud points;\n    sensor::PointCloud high_resolution_filtered_points;\n    sensor::PointCloud low_resolution_filtered_points;\n    State state;\n  };\n\n  struct OdometerData {\n    common::Time time;\n\n    // Dead-reckoning pose of the odometry.\n    transform::Rigid3d pose;\n  };\n\n  State PredictState(const State& start_state, const common::Time start_time,\n                     const common::Time end_time);\n\n  void RemoveObsoleteSensorData();\n\n  std::unique_ptr<InsertionResult> AddAccumulatedRangeData(\n      common::Time time, const transform::Rigid3d& pose_observation,\n      const sensor::RangeData& range_data_in_tracking);\n\n  std::unique_ptr<InsertionResult> InsertIntoSubmap(\n      const common::Time time, const sensor::RangeData& range_data_in_tracking,\n      const transform::Rigid3d& pose_observation);\n\n  void TransformStates(const transform::Rigid3d& transform);\n  std::unique_ptr<InsertionResult> MaybeOptimize(common::Time time);\n\n  const proto::LocalTrajectoryBuilderOptions options_;\n  const ceres::Solver::Options ceres_solver_options_;\n  std::unique_ptr<mapping_3d::Submaps> submaps_;\n  int num_accumulated_;\n\n  std::deque<Batch> batches_;\n  double gravity_constant_ = 9.8;\n  std::deque<ImuData> imu_data_;\n  std::deque<OdometerData> odometer_data_;\n\n  PoseEstimate last_pose_estimate_;\n\n  MotionFilter motion_filter_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_H_\n"
  },
  {
    "path": "mapping_3d/optimizing_local_trajectory_builder_options.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/optimizing_local_trajectory_builder_options.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::OptimizingLocalTrajectoryBuilderOptions\nCreateOptimizingLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::OptimizingLocalTrajectoryBuilderOptions options;\n  options.set_high_resolution_grid_weight(\n      parameter_dictionary->GetDouble(\"high_resolution_grid_weight\"));\n  options.set_low_resolution_grid_weight(\n      parameter_dictionary->GetDouble(\"low_resolution_grid_weight\"));\n  options.set_velocity_weight(\n      parameter_dictionary->GetDouble(\"velocity_weight\"));\n  options.set_translation_weight(\n      parameter_dictionary->GetDouble(\"translation_weight\"));\n  options.set_rotation_weight(\n      parameter_dictionary->GetDouble(\"rotation_weight\"));\n  options.set_odometry_translation_weight(\n      parameter_dictionary->GetDouble(\"odometry_translation_weight\"));\n  options.set_odometry_rotation_weight(\n      parameter_dictionary->GetDouble(\"odometry_rotation_weight\"));\n  return options;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/optimizing_local_trajectory_builder_options.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n#define CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_3d/proto/optimizing_local_trajectory_builder_options.pb.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::OptimizingLocalTrajectoryBuilderOptions\nCreateOptimizingLocalTrajectoryBuilderOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_OPTIMIZING_LOCAL_TRAJECTORY_BUILDER_OPTIONS_H_\n"
  },
  {
    "path": "mapping_3d/proto/hybrid_grid.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage HybridGrid {\n  optional float resolution = 1;\n  // '{x, y, z}_indices[i]' is the index of 'values[i]'.\n  repeated sint32 x_indices = 3 [packed = true];\n  repeated sint32 y_indices = 4 [packed = true];\n  repeated sint32 z_indices = 5 [packed = true];\n  // The entries in 'values' should be uint16s, not int32s, but protos don't\n  // have a uint16 type.\n  repeated int32 values = 6 [packed = true];\n}\n"
  },
  {
    "path": "mapping_3d/proto/kalman_local_trajectory_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/kalman_filter/proto/pose_tracker_options.proto\";\nimport \"cartographer/mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.proto\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage KalmanLocalTrajectoryBuilderOptions {\n  // Whether to solve the online scan matching first using the correlative scan\n  // matcher to generate a good starting point for Ceres.\n  optional bool use_online_correlative_scan_matching = 1;\n\n  optional mapping_2d.scan_matching.proto.RealTimeCorrelativeScanMatcherOptions\n      real_time_correlative_scan_matcher_options = 2;\n  optional kalman_filter.proto.PoseTrackerOptions pose_tracker_options = 3;\n\n  optional double scan_matcher_variance = 6;\n  optional double odometer_translational_variance = 4;\n  optional double odometer_rotational_variance = 5;\n}\n"
  },
  {
    "path": "mapping_3d/proto/local_trajectory_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_3d/proto/motion_filter_options.proto\";\nimport \"cartographer/sensor/proto/adaptive_voxel_filter_options.proto\";\nimport \"cartographer/mapping_3d/proto/kalman_local_trajectory_builder_options.proto\";\nimport \"cartographer/mapping_3d/proto/optimizing_local_trajectory_builder_options.proto\";\nimport \"cartographer/mapping_3d/proto/submaps_options.proto\";\nimport \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.proto\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage LocalTrajectoryBuilderOptions {\n  enum Use {\n    KALMAN = 0;\n    OPTIMIZING = 1;\n  }\n\n  // Rangefinder points outside these ranges will be dropped.\n  optional float min_range = 1;\n  optional float max_range = 2;\n\n  // Number of scans to accumulate into one unwarped, combined scan to use for\n  // scan matching.\n  optional int32 scans_per_accumulation = 3;\n\n  // Voxel filter that gets applied to the range data immediately after\n  // cropping.\n  optional float voxel_filter_size = 4;\n\n  // Voxel filter used to compute a sparser point cloud for matching.\n  optional sensor.proto.AdaptiveVoxelFilterOptions\n      high_resolution_adaptive_voxel_filter_options = 5;\n  optional sensor.proto.AdaptiveVoxelFilterOptions\n      low_resolution_adaptive_voxel_filter_options = 12;\n\n  optional scan_matching.proto.CeresScanMatcherOptions\n      ceres_scan_matcher_options = 6;\n  optional MotionFilterOptions motion_filter_options = 7;\n  optional SubmapsOptions submaps_options = 8;\n\n  // Which one of the implementation to instantiate and use.\n  optional Use use = 9;\n  optional KalmanLocalTrajectoryBuilderOptions\n      kalman_local_trajectory_builder_options = 10;\n  optional OptimizingLocalTrajectoryBuilderOptions\n      optimizing_local_trajectory_builder_options = 11;\n}\n"
  },
  {
    "path": "mapping_3d/proto/motion_filter_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage MotionFilterOptions {\n  // Threshold above which a new scan is inserted based on time.\n  optional double max_time_seconds = 1;\n\n  // Threshold above which a new scan is inserted based on linear motion.\n  optional double max_distance_meters = 2;\n\n  // Threshold above which a new scan is inserted based on rotational motion.\n  optional double max_angle_radians = 3;\n}\n"
  },
  {
    "path": "mapping_3d/proto/optimizing_local_trajectory_builder_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage OptimizingLocalTrajectoryBuilderOptions {\n  optional double high_resolution_grid_weight = 6;\n  optional double low_resolution_grid_weight = 7;\n  optional double velocity_weight = 1;\n  optional double translation_weight = 2;\n  optional double rotation_weight = 3;\n  optional double odometry_translation_weight = 4;\n  optional double odometry_rotation_weight = 5;\n}\n"
  },
  {
    "path": "mapping_3d/proto/range_data_inserter_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage RangeDataInserterOptions {\n  // Probability change for a hit (this will be converted to odds and therefore\n  // must be greater than 0.5).\n  optional double hit_probability = 1;\n\n  // Probability change for a miss (this will be converted to odds and therefore\n  // must be less than 0.5).\n  optional double miss_probability = 2;\n\n  // Up to how many free space voxels are updated for scan matching.\n  // 0 disables free space.\n  optional int32 num_free_space_voxels = 3;\n}\n"
  },
  {
    "path": "mapping_3d/proto/submaps_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\nimport \"cartographer/mapping_3d/proto/range_data_inserter_options.proto\";\n\npackage cartographer.mapping_3d.proto;\n\nmessage SubmapsOptions {\n  // Resolution of the 'high_resolution' map in meters used for local SLAM and\n  // loop closure.\n  optional double high_resolution = 1;\n\n  // Maximum range to filter the point cloud to before insertion into the\n  // 'high_resolution' map.\n  optional double high_resolution_max_range = 4;\n\n  // Resolution of the 'low_resolution' version of the map in meters used for\n  // local SLAM only.\n  optional double low_resolution = 5;\n\n  // Number of scans before adding a new submap. Each submap will get twice the\n  // number of scans inserted: First for initialization without being matched\n  // against, then while being matched.\n  optional int32 num_range_data = 2;\n\n  optional RangeDataInserterOptions range_data_inserter_options = 3;\n}\n"
  },
  {
    "path": "mapping_3d/range_data_inserter.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/range_data_inserter.h\"\n\n#include \"Eigen/Core\"\n#include \"cartographer/mapping/probability_values.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nnamespace {\n\nvoid InsertMissesIntoGrid(const std::vector<uint16>& miss_table,\n                          const Eigen::Vector3f& origin,\n                          const sensor::PointCloud& returns,\n                          HybridGrid* hybrid_grid,\n                          const int num_free_space_voxels) {\n  const Eigen::Array3i origin_cell = hybrid_grid->GetCellIndex(origin);\n  for (const Eigen::Vector3f& hit : returns) {\n    const Eigen::Array3i hit_cell = hybrid_grid->GetCellIndex(hit);\n\n    const Eigen::Array3i delta = hit_cell - origin_cell;\n    const int num_samples = delta.cwiseAbs().maxCoeff();\n    CHECK_LT(num_samples, 1 << 15);\n    // 'num_samples' is the number of samples we equi-distantly place on the\n    // line between 'origin' and 'hit'. (including a fractional part for sub-\n    // voxels) It is chosen so that between two samples we change from one voxel\n    // to the next on the fastest changing dimension.\n    //\n    // Only the last 'num_free_space_voxels' are updated for performance.\n    for (int position = std::max(0, num_samples - num_free_space_voxels);\n         position < num_samples; ++position) {\n      const Eigen::Array3i miss_cell =\n          origin_cell + delta * position / num_samples;\n      hybrid_grid->ApplyLookupTable(miss_cell, miss_table);\n    }\n  }\n}\n\n}  // namespace\n\nproto::RangeDataInserterOptions CreateRangeDataInserterOptions(\n    common::LuaParameterDictionary* parameter_dictionary) {\n  proto::RangeDataInserterOptions options;\n  options.set_hit_probability(\n      parameter_dictionary->GetDouble(\"hit_probability\"));\n  options.set_miss_probability(\n      parameter_dictionary->GetDouble(\"miss_probability\"));\n  options.set_num_free_space_voxels(\n      parameter_dictionary->GetInt(\"num_free_space_voxels\"));\n  CHECK_GT(options.hit_probability(), 0.5);\n  CHECK_LT(options.miss_probability(), 0.5);\n  return options;\n}\n\nRangeDataInserter::RangeDataInserter(\n    const proto::RangeDataInserterOptions& options)\n    : options_(options),\n      hit_table_(mapping::ComputeLookupTableToApplyOdds(\n          mapping::Odds(options_.hit_probability()))),\n      miss_table_(mapping::ComputeLookupTableToApplyOdds(\n          mapping::Odds(options_.miss_probability()))) {}\n\nvoid RangeDataInserter::Insert(const sensor::RangeData& range_data,\n                               HybridGrid* hybrid_grid) const {\n  CHECK_NOTNULL(hybrid_grid)->StartUpdate();\n\n  for (const Eigen::Vector3f& hit : range_data.returns) {\n    const Eigen::Array3i hit_cell = hybrid_grid->GetCellIndex(hit);\n    hybrid_grid->ApplyLookupTable(hit_cell, hit_table_);\n  }\n\n  // By not starting a new update after hits are inserted, we give hits priority\n  // (i.e. no hits will be ignored because of a miss in the same cell).\n  InsertMissesIntoGrid(miss_table_, range_data.origin, range_data.returns,\n                       hybrid_grid, options_.num_free_space_voxels());\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/range_data_inserter.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_RANGE_DATA_INSERTER_H_\n#define CARTOGRAPHER_MAPPING_3D_RANGE_DATA_INSERTER_H_\n\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/proto/range_data_inserter_options.pb.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nproto::RangeDataInserterOptions CreateRangeDataInserterOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nclass RangeDataInserter {\n public:\n  explicit RangeDataInserter(const proto::RangeDataInserterOptions& options);\n\n  RangeDataInserter(const RangeDataInserter&) = delete;\n  RangeDataInserter& operator=(const RangeDataInserter&) = delete;\n\n  // Inserts 'range_data' into 'hybrid_grid'.\n  void Insert(const sensor::RangeData& range_data,\n              HybridGrid* hybrid_grid) const;\n\n private:\n  const proto::RangeDataInserterOptions options_;\n  const std::vector<uint16> hit_table_;\n  const std::vector<uint16> miss_table_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_RANGE_DATA_INSERTER_H_\n"
  },
  {
    "path": "mapping_3d/range_data_inserter_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/range_data_inserter.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace {\n\nclass RangeDataInserterTest : public ::testing::Test {\n protected:\n  RangeDataInserterTest() : hybrid_grid_(1.f) {\n    auto parameter_dictionary = common::MakeDictionary(\n        \"return { \"\n        \"hit_probability = 0.7, \"\n        \"miss_probability = 0.4, \"\n        \"num_free_space_voxels = 1000, \"\n        \"}\");\n    options_ = CreateRangeDataInserterOptions(parameter_dictionary.get());\n    range_data_inserter_.reset(new RangeDataInserter(options_));\n  }\n\n  void InsertPointCloud() {\n    const Eigen::Vector3f origin = Eigen::Vector3f(0.f, 0.f, -4.f);\n    sensor::PointCloud returns = {\n        {-3.f, -1.f, 4.f}, {-2.f, 0.f, 4.f}, {-1.f, 1.f, 4.f}, {0.f, 2.f, 4.f}};\n    range_data_inserter_->Insert(sensor::RangeData{origin, returns, {}},\n                                 &hybrid_grid_);\n  }\n\n  float GetProbability(float x, float y, float z) const {\n    return hybrid_grid_.GetProbability(\n        hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));\n  }\n\n  float IsKnown(float x, float y, float z) const {\n    return hybrid_grid_.IsKnown(\n        hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));\n  }\n\n  const proto::RangeDataInserterOptions& options() const { return options_; }\n\n private:\n  HybridGrid hybrid_grid_;\n  std::unique_ptr<RangeDataInserter> range_data_inserter_;\n  proto::RangeDataInserterOptions options_;\n};\n\nTEST_F(RangeDataInserterTest, InsertPointCloud) {\n  InsertPointCloud();\n  EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -4.f),\n              1e-4);\n  EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -3.f),\n              1e-4);\n  EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -2.f),\n              1e-4);\n  for (int x = -4; x <= 4; ++x) {\n    for (int y = -4; y <= 4; ++y) {\n      if (x < -3 || x > 0 || y != x + 2) {\n        EXPECT_FALSE(IsKnown(x, y, 4.f));\n      } else {\n        EXPECT_NEAR(options().hit_probability(), GetProbability(x, y, 4.f),\n                    1e-4);\n      }\n    }\n  }\n}\n\nTEST_F(RangeDataInserterTest, ProbabilityProgression) {\n  InsertPointCloud();\n  EXPECT_NEAR(options().hit_probability(), GetProbability(-2.f, 0.f, 4.f),\n              1e-4);\n  EXPECT_NEAR(options().miss_probability(), GetProbability(-2.f, 0.f, 3.f),\n              1e-4);\n  EXPECT_NEAR(options().miss_probability(), GetProbability(0.f, 0.f, -3.f),\n              1e-4);\n\n  for (int i = 0; i < 1000; ++i) {\n    InsertPointCloud();\n  }\n  EXPECT_NEAR(mapping::kMaxProbability, GetProbability(-2.f, 0.f, 4.f), 1e-3);\n  EXPECT_NEAR(mapping::kMinProbability, GetProbability(-2.f, 0.f, 3.f), 1e-3);\n  EXPECT_NEAR(mapping::kMinProbability, GetProbability(0.f, 0.f, -3.f), 1e-3);\n}\n\n}  // namespace\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/rotation_cost_function.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_ROTATION_COST_FUNCTION_H_\n#define CARTOGRAPHER_MAPPING_3D_ROTATION_COST_FUNCTION_H_\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Penalizes differences between IMU data and optimized orientations.\nclass RotationCostFunction {\n public:\n  RotationCostFunction(const double scaling_factor,\n                       const Eigen::Quaterniond& delta_rotation_imu_frame)\n      : scaling_factor_(scaling_factor),\n        delta_rotation_imu_frame_(delta_rotation_imu_frame) {}\n\n  RotationCostFunction(const RotationCostFunction&) = delete;\n  RotationCostFunction& operator=(const RotationCostFunction&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const start_rotation, const T* const end_rotation,\n                  T* residual) const {\n    const Eigen::Quaternion<T> start(start_rotation[0], start_rotation[1],\n                                     start_rotation[2], start_rotation[3]);\n    const Eigen::Quaternion<T> end(end_rotation[0], end_rotation[1],\n                                   end_rotation[2], end_rotation[3]);\n    const Eigen::Quaternion<T> error =\n        end.conjugate() * start * delta_rotation_imu_frame_.cast<T>();\n    residual[0] = scaling_factor_ * error.x();\n    residual[1] = scaling_factor_ * error.y();\n    residual[2] = scaling_factor_ * error.z();\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const Eigen::Quaterniond delta_rotation_imu_frame_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_ROTATION_COST_FUNCTION_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/ceres_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"cartographer/common/ceres_solver_options.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/mapping_3d/ceres_pose.h\"\n#include \"cartographer/mapping_3d/scan_matching/occupied_space_cost_functor.h\"\n#include \"cartographer/mapping_3d/scan_matching/rotation_delta_cost_functor.h\"\n#include \"cartographer/mapping_3d/scan_matching/translation_delta_cost_functor.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nstruct YawOnlyQuaternionPlus {\n  template <typename T>\n  bool operator()(const T* x, const T* delta, T* x_plus_delta) const {\n    const T clamped_delta = common::Clamp(delta[0], T(-0.5), T(0.5));\n    T q_delta[4];\n    q_delta[0] = ceres::sqrt(1. - clamped_delta * clamped_delta);\n    q_delta[1] = T(0.);\n    q_delta[2] = T(0.);\n    q_delta[3] = clamped_delta;\n    ceres::QuaternionProduct(q_delta, x, x_plus_delta);\n    return true;\n  }\n};\n\n}  // namespace\n\nproto::CeresScanMatcherOptions CreateCeresScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::CeresScanMatcherOptions options;\n  for (int i = 0;; ++i) {\n    const string lua_identifier = \"occupied_space_weight_\" + std::to_string(i);\n    if (!parameter_dictionary->HasKey(lua_identifier)) {\n      break;\n    }\n    options.add_occupied_space_weight(\n        parameter_dictionary->GetDouble(lua_identifier));\n  }\n  options.set_translation_weight(\n      parameter_dictionary->GetDouble(\"translation_weight\"));\n  options.set_rotation_weight(\n      parameter_dictionary->GetDouble(\"rotation_weight\"));\n  options.set_only_optimize_yaw(\n      parameter_dictionary->GetBool(\"only_optimize_yaw\"));\n  *options.mutable_ceres_solver_options() =\n      common::CreateCeresSolverOptionsProto(\n          parameter_dictionary->GetDictionary(\"ceres_solver_options\").get());\n  return options;\n}\n\nCeresScanMatcher::CeresScanMatcher(\n    const proto::CeresScanMatcherOptions& options)\n    : options_(options),\n      ceres_solver_options_(\n          common::CreateCeresSolverOptions(options.ceres_solver_options())) {\n  ceres_solver_options_.linear_solver_type = ceres::DENSE_QR;\n}\n\nvoid CeresScanMatcher::Match(const transform::Rigid3d& previous_pose,\n                             const transform::Rigid3d& initial_pose_estimate,\n                             const std::vector<PointCloudAndHybridGridPointers>&\n                                 point_clouds_and_hybrid_grids,\n                             transform::Rigid3d* const pose_estimate,\n                             ceres::Solver::Summary* const summary) {\n  ceres::Problem problem;\n  CeresPose ceres_pose(\n      initial_pose_estimate, nullptr /* translation_parameterization */,\n      options_.only_optimize_yaw()\n          ? std::unique_ptr<ceres::LocalParameterization>(\n                common::make_unique<ceres::AutoDiffLocalParameterization<\n                    YawOnlyQuaternionPlus, 4, 1>>())\n          : std::unique_ptr<ceres::LocalParameterization>(\n                common::make_unique<ceres::QuaternionParameterization>()),\n      &problem);\n\n  CHECK_EQ(options_.occupied_space_weight_size(),\n           point_clouds_and_hybrid_grids.size());\n  for (size_t i = 0; i != point_clouds_and_hybrid_grids.size(); ++i) {\n    CHECK_GT(options_.occupied_space_weight(i), 0.);\n    const sensor::PointCloud& point_cloud =\n        *point_clouds_and_hybrid_grids[i].first;\n    const HybridGrid& hybrid_grid = *point_clouds_and_hybrid_grids[i].second;\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<OccupiedSpaceCostFunctor,\n                                        ceres::DYNAMIC, 3, 4>(\n            new OccupiedSpaceCostFunctor(\n                options_.occupied_space_weight(i) /\n                    std::sqrt(static_cast<double>(point_cloud.size())),\n                point_cloud, hybrid_grid),\n            point_cloud.size()),\n        nullptr, ceres_pose.translation(), ceres_pose.rotation());\n  }\n  CHECK_GT(options_.translation_weight(), 0.);\n  problem.AddResidualBlock(\n      new ceres::AutoDiffCostFunction<TranslationDeltaCostFunctor, 3, 3>(\n          new TranslationDeltaCostFunctor(options_.translation_weight(),\n                                          previous_pose)),\n      nullptr, ceres_pose.translation());\n  CHECK_GT(options_.rotation_weight(), 0.);\n  problem.AddResidualBlock(\n      new ceres::AutoDiffCostFunction<RotationDeltaCostFunctor, 3, 4>(\n          new RotationDeltaCostFunctor(options_.rotation_weight(),\n                                       initial_pose_estimate.rotation())),\n      nullptr, ceres_pose.rotation());\n\n  ceres::Solve(ceres_solver_options_, &problem, summary);\n\n  *pose_estimate = ceres_pose.ToRigid();\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/ceres_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nproto::CeresScanMatcherOptions CreateCeresScanMatcherOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nusing PointCloudAndHybridGridPointers =\n    std::pair<const sensor::PointCloud*, const HybridGrid*>;\n\n// This scan matcher uses Ceres to align scans with an existing map.\nclass CeresScanMatcher {\n public:\n  explicit CeresScanMatcher(const proto::CeresScanMatcherOptions& options);\n\n  CeresScanMatcher(const CeresScanMatcher&) = delete;\n  CeresScanMatcher& operator=(const CeresScanMatcher&) = delete;\n\n  // Aligns 'point_clouds' within the 'hybrid_grids' given an\n  // 'initial_pose_estimate' and returns a 'pose_estimate' and the solver\n  // 'summary'.\n  void Match(const transform::Rigid3d& previous_pose,\n             const transform::Rigid3d& initial_pose_estimate,\n             const std::vector<PointCloudAndHybridGridPointers>&\n                 point_clouds_and_hybrid_grids,\n             transform::Rigid3d* pose_estimate,\n             ceres::Solver::Summary* summary);\n\n private:\n  const proto::CeresScanMatcherOptions options_;\n  ceres::Solver::Options ceres_solver_options_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_CERES_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/ceres_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n\n#include <memory>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nclass CeresScanMatcherTest : public ::testing::Test {\n protected:\n  CeresScanMatcherTest()\n      : hybrid_grid_(1.f),\n        expected_pose_(\n            transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., 0.))) {\n    for (const auto& point :\n         {Eigen::Vector3f(-3.f, 2.f, 0.f), Eigen::Vector3f(-4.f, 2.f, 0.f),\n          Eigen::Vector3f(-5.f, 2.f, 0.f), Eigen::Vector3f(-6.f, 2.f, 0.f),\n          Eigen::Vector3f(-6.f, 3.f, 1.f), Eigen::Vector3f(-6.f, 4.f, 2.f),\n          Eigen::Vector3f(-7.f, 3.f, 1.f)}) {\n      point_cloud_.push_back(point);\n      hybrid_grid_.SetProbability(\n          hybrid_grid_.GetCellIndex(expected_pose_.cast<float>() * point), 1.);\n    }\n\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n          occupied_space_weight_0 = 1.,\n          translation_weight = 0.01,\n          rotation_weight = 0.1,\n          only_optimize_yaw = false,\n          ceres_solver_options = {\n            use_nonmonotonic_steps = true,\n            max_num_iterations = 10,\n            num_threads = 1,\n          },\n        })text\");\n    options_ = CreateCeresScanMatcherOptions(parameter_dictionary.get());\n    ceres_scan_matcher_.reset(new CeresScanMatcher(options_));\n  }\n\n  void TestFromInitialPose(const transform::Rigid3d& initial_pose) {\n    transform::Rigid3d pose;\n\n    ceres::Solver::Summary summary;\n    ceres_scan_matcher_->Match(initial_pose, initial_pose,\n                               {{&point_cloud_, &hybrid_grid_}}, &pose,\n                               &summary);\n    EXPECT_NEAR(0., summary.final_cost, 1e-2) << summary.FullReport();\n    EXPECT_THAT(pose, transform::IsNearly(expected_pose_, 3e-2));\n  }\n\n  HybridGrid hybrid_grid_;\n  transform::Rigid3d expected_pose_;\n  sensor::PointCloud point_cloud_;\n  proto::CeresScanMatcherOptions options_;\n  std::unique_ptr<CeresScanMatcher> ceres_scan_matcher_;\n};\n\nTEST_F(CeresScanMatcherTest, PerfectEstimate) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., 0.)));\n}\n\nTEST_F(CeresScanMatcherTest, AlongX) {\n  ceres_scan_matcher_.reset(new CeresScanMatcher(options_));\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-0.8, 0., 0.)));\n}\n\nTEST_F(CeresScanMatcherTest, AlongZ) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., -0.2)));\n}\n\nTEST_F(CeresScanMatcherTest, AlongXYZ) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-0.9, -0.2, 0.2)));\n}\n\nTEST_F(CeresScanMatcherTest, FullPoseCorrection) {\n  // We try to find the rotation around z...\n  const auto additional_transform = transform::Rigid3d::Rotation(\n      Eigen::AngleAxisd(0.05, Eigen::Vector3d(0., 0., 1.)));\n  point_cloud_ = sensor::TransformPointCloud(\n      point_cloud_, additional_transform.cast<float>());\n  expected_pose_ = expected_pose_ * additional_transform.inverse();\n  // ...starting initially with rotation around x.\n  TestFromInitialPose(\n      transform::Rigid3d(Eigen::Vector3d(-0.95, -0.05, 0.05),\n                         Eigen::AngleAxisd(0.05, Eigen::Vector3d(1., 0., 0.))));\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/fast_correlative_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/fast_correlative_scan_matcher.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <functional>\n#include <limits>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping_3d/scan_matching/precomputation_grid.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::FastCorrelativeScanMatcherOptions options;\n  options.set_branch_and_bound_depth(\n      parameter_dictionary->GetInt(\"branch_and_bound_depth\"));\n  options.set_full_resolution_depth(\n      parameter_dictionary->GetInt(\"full_resolution_depth\"));\n  options.set_rotational_histogram_size(\n      parameter_dictionary->GetInt(\"rotational_histogram_size\"));\n  options.set_min_rotational_score(\n      parameter_dictionary->GetDouble(\"min_rotational_score\"));\n  options.set_linear_xy_search_window(\n      parameter_dictionary->GetDouble(\"linear_xy_search_window\"));\n  options.set_linear_z_search_window(\n      parameter_dictionary->GetDouble(\"linear_z_search_window\"));\n  options.set_angular_search_window(\n      parameter_dictionary->GetDouble(\"angular_search_window\"));\n  return options;\n}\n\nclass PrecomputationGridStack {\n public:\n  PrecomputationGridStack(\n      const HybridGrid& hybrid_grid,\n      const proto::FastCorrelativeScanMatcherOptions& options) {\n    CHECK_GE(options.branch_and_bound_depth(), 1);\n    CHECK_GE(options.full_resolution_depth(), 1);\n    precomputation_grids_.reserve(options.branch_and_bound_depth());\n    precomputation_grids_.push_back(ConvertToPrecomputationGrid(hybrid_grid));\n    Eigen::Array3i last_width = Eigen::Array3i::Ones();\n    for (int depth = 1; depth != options.branch_and_bound_depth(); ++depth) {\n      const bool half_resolution = depth >= options.full_resolution_depth();\n      const Eigen::Array3i next_width = ((1 << depth) * Eigen::Array3i::Ones());\n      const int full_voxels_per_high_resolution_voxel =\n          1 << std::max(0, depth - options.full_resolution_depth());\n      const Eigen::Array3i shift =\n          (next_width - last_width +\n           (full_voxels_per_high_resolution_voxel - 1)) /\n          full_voxels_per_high_resolution_voxel;\n      precomputation_grids_.push_back(\n          PrecomputeGrid(precomputation_grids_.back(), half_resolution, shift));\n      last_width = next_width;\n    }\n  }\n\n  const PrecomputationGrid& Get(int depth) const {\n    return precomputation_grids_.at(depth);\n  }\n\n  int max_depth() const { return precomputation_grids_.size() - 1; }\n\n private:\n  std::vector<PrecomputationGrid> precomputation_grids_;\n};\n\nFastCorrelativeScanMatcher::FastCorrelativeScanMatcher(\n    const HybridGrid& hybrid_grid,\n    const std::vector<mapping::TrajectoryNode>& nodes,\n    const proto::FastCorrelativeScanMatcherOptions& options)\n    : options_(options),\n      resolution_(hybrid_grid.resolution()),\n      width_in_voxels_(hybrid_grid.grid_size()),\n      precomputation_grid_stack_(\n          common::make_unique<PrecomputationGridStack>(hybrid_grid, options)),\n      rotational_scan_matcher_(nodes, options_.rotational_histogram_size()) {}\n\nFastCorrelativeScanMatcher::~FastCorrelativeScanMatcher() {}\n\nbool FastCorrelativeScanMatcher::Match(\n    const transform::Rigid3d& initial_pose_estimate,\n    const sensor::PointCloud& coarse_point_cloud,\n    const sensor::PointCloud& fine_point_cloud, const float min_score,\n    float* const score, transform::Rigid3d* const pose_estimate) const {\n  const SearchParameters search_parameters{\n      common::RoundToInt(options_.linear_xy_search_window() / resolution_),\n      common::RoundToInt(options_.linear_z_search_window() / resolution_),\n      options_.angular_search_window()};\n  return MatchWithSearchParameters(search_parameters, initial_pose_estimate,\n                                   coarse_point_cloud, fine_point_cloud,\n                                   min_score, score, pose_estimate);\n}\n\nbool FastCorrelativeScanMatcher::MatchFullSubmap(\n    const Eigen::Quaterniond& gravity_alignment,\n    const sensor::PointCloud& coarse_point_cloud,\n    const sensor::PointCloud& fine_point_cloud, const float min_score,\n    float* const score, transform::Rigid3d* const pose_estimate) const {\n  const transform::Rigid3d initial_pose_estimate(Eigen::Vector3d::Zero(),\n                                                 gravity_alignment);\n  float max_point_distance = 0.f;\n  for (const Eigen::Vector3f& point : coarse_point_cloud) {\n    max_point_distance = std::max(max_point_distance, point.norm());\n  }\n  const int linear_window_size =\n      (width_in_voxels_ + 1) / 2 +\n      common::RoundToInt(max_point_distance / resolution_ + 0.5f);\n  const SearchParameters search_parameters{linear_window_size,\n                                           linear_window_size, M_PI};\n  return MatchWithSearchParameters(search_parameters, initial_pose_estimate,\n                                   coarse_point_cloud, fine_point_cloud,\n                                   min_score, score, pose_estimate);\n}\n\nbool FastCorrelativeScanMatcher::MatchWithSearchParameters(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const transform::Rigid3d& initial_pose_estimate,\n    const sensor::PointCloud& coarse_point_cloud,\n    const sensor::PointCloud& fine_point_cloud, const float min_score,\n    float* const score, transform::Rigid3d* const pose_estimate) const {\n  CHECK_NOTNULL(score);\n  CHECK_NOTNULL(pose_estimate);\n\n  const std::vector<DiscreteScan> discrete_scans = GenerateDiscreteScans(\n      search_parameters, coarse_point_cloud, fine_point_cloud,\n      initial_pose_estimate.cast<float>());\n\n  const std::vector<Candidate> lowest_resolution_candidates =\n      ComputeLowestResolutionCandidates(search_parameters, discrete_scans);\n\n  const Candidate best_candidate = BranchAndBound(\n      search_parameters, discrete_scans, lowest_resolution_candidates,\n      precomputation_grid_stack_->max_depth(), min_score);\n  if (best_candidate.score > min_score) {\n    *score = best_candidate.score;\n    *pose_estimate =\n        (transform::Rigid3f(\n             resolution_ * best_candidate.offset.matrix().cast<float>(),\n             Eigen::Quaternionf::Identity()) *\n         discrete_scans[best_candidate.scan_index].pose)\n            .cast<double>();\n    return true;\n  }\n  return false;\n}\n\nDiscreteScan FastCorrelativeScanMatcher::DiscretizeScan(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const sensor::PointCloud& point_cloud,\n    const transform::Rigid3f& pose) const {\n  std::vector<std::vector<Eigen::Array3i>> cell_indices_per_depth;\n  const PrecomputationGrid& original_grid = precomputation_grid_stack_->Get(0);\n  std::vector<Eigen::Array3i> full_resolution_cell_indices;\n  for (const Eigen::Vector3f& point :\n       sensor::TransformPointCloud(point_cloud, pose)) {\n    full_resolution_cell_indices.push_back(original_grid.GetCellIndex(point));\n  }\n  const int full_resolution_depth = std::min(options_.full_resolution_depth(),\n                                             options_.branch_and_bound_depth());\n  CHECK_GE(full_resolution_depth, 1);\n  for (int i = 0; i != full_resolution_depth; ++i) {\n    cell_indices_per_depth.push_back(full_resolution_cell_indices);\n  }\n  const int low_resolution_depth =\n      options_.branch_and_bound_depth() - full_resolution_depth;\n  CHECK_GE(low_resolution_depth, 0);\n  const Eigen::Array3i search_window_start(\n      -search_parameters.linear_xy_window_size,\n      -search_parameters.linear_xy_window_size,\n      -search_parameters.linear_z_window_size);\n  for (int i = 0; i != low_resolution_depth; ++i) {\n    const int reduction_exponent = i + 1;\n    const Eigen::Array3i low_resolution_search_window_start(\n        search_window_start[0] >> reduction_exponent,\n        search_window_start[1] >> reduction_exponent,\n        search_window_start[2] >> reduction_exponent);\n    cell_indices_per_depth.emplace_back();\n    for (const Eigen::Array3i& cell_index : full_resolution_cell_indices) {\n      const Eigen::Array3i cell_at_start = cell_index + search_window_start;\n      const Eigen::Array3i low_resolution_cell_at_start(\n          cell_at_start[0] >> reduction_exponent,\n          cell_at_start[1] >> reduction_exponent,\n          cell_at_start[2] >> reduction_exponent);\n      cell_indices_per_depth.back().push_back(\n          low_resolution_cell_at_start - low_resolution_search_window_start);\n    }\n  }\n  return DiscreteScan{pose, cell_indices_per_depth};\n}\n\nstd::vector<DiscreteScan> FastCorrelativeScanMatcher::GenerateDiscreteScans(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const sensor::PointCloud& coarse_point_cloud,\n    const sensor::PointCloud& fine_point_cloud,\n    const transform::Rigid3f& initial_pose) const {\n  std::vector<DiscreteScan> result;\n  // We set this value to something on the order of resolution to make sure that\n  // the std::acos() below is defined.\n  float max_scan_range = 3.f * resolution_;\n  for (const Eigen::Vector3f& point : coarse_point_cloud) {\n    const float range = point.norm();\n    max_scan_range = std::max(range, max_scan_range);\n  }\n  const float kSafetyMargin = 1.f - 1e-2f;\n  const float angular_step_size =\n      kSafetyMargin * std::acos(1.f - common::Pow2(resolution_) /\n                                          (2.f * common::Pow2(max_scan_range)));\n  const int angular_window_size = common::RoundToInt(\n      search_parameters.angular_search_window / angular_step_size);\n  // TODO(whess): Should there be a small search window for rotations around\n  // x and y?\n  std::vector<float> angles;\n  for (int rz = -angular_window_size; rz <= angular_window_size; ++rz) {\n    angles.push_back(rz * angular_step_size);\n  }\n  const std::vector<float> scores = rotational_scan_matcher_.Match(\n      sensor::TransformPointCloud(fine_point_cloud, initial_pose), angles);\n  for (size_t i = 0; i != angles.size(); ++i) {\n    if (scores[i] < options_.min_rotational_score()) {\n      continue;\n    }\n    const Eigen::Vector3f angle_axis(0.f, 0.f, angles[i]);\n    // It's important to apply the 'angle_axis' rotation between the translation\n    // and rotation of the 'initial_pose', so that the rotation is around the\n    // origin of the range data, and yaw is in map frame.\n    const transform::Rigid3f pose(\n        initial_pose.translation(),\n        transform::AngleAxisVectorToRotationQuaternion(angle_axis) *\n            initial_pose.rotation());\n    result.push_back(\n        DiscretizeScan(search_parameters, coarse_point_cloud, pose));\n  }\n  return result;\n}\n\nstd::vector<Candidate>\nFastCorrelativeScanMatcher::GenerateLowestResolutionCandidates(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const int num_discrete_scans) const {\n  const int linear_step_size = 1 << precomputation_grid_stack_->max_depth();\n  const int num_lowest_resolution_linear_xy_candidates =\n      (2 * search_parameters.linear_xy_window_size + linear_step_size) /\n      linear_step_size;\n  const int num_lowest_resolution_linear_z_candidates =\n      (2 * search_parameters.linear_z_window_size + linear_step_size) /\n      linear_step_size;\n  const int num_candidates =\n      num_discrete_scans *\n      common::Power(num_lowest_resolution_linear_xy_candidates, 2) *\n      num_lowest_resolution_linear_z_candidates;\n  std::vector<Candidate> candidates;\n  candidates.reserve(num_candidates);\n  for (int scan_index = 0; scan_index != num_discrete_scans; ++scan_index) {\n    for (int z = -search_parameters.linear_z_window_size;\n         z <= search_parameters.linear_z_window_size; z += linear_step_size) {\n      for (int y = -search_parameters.linear_xy_window_size;\n           y <= search_parameters.linear_xy_window_size;\n           y += linear_step_size) {\n        for (int x = -search_parameters.linear_xy_window_size;\n             x <= search_parameters.linear_xy_window_size;\n             x += linear_step_size) {\n          candidates.emplace_back(scan_index, Eigen::Array3i(x, y, z));\n        }\n      }\n    }\n  }\n  CHECK_EQ(candidates.size(), num_candidates);\n  return candidates;\n}\n\nvoid FastCorrelativeScanMatcher::ScoreCandidates(\n    const int depth, const std::vector<DiscreteScan>& discrete_scans,\n    std::vector<Candidate>* const candidates) const {\n  const int reduction_exponent =\n      std::max(0, depth - options_.full_resolution_depth() + 1);\n  for (Candidate& candidate : *candidates) {\n    int sum = 0;\n    const DiscreteScan& discrete_scan = discrete_scans[candidate.scan_index];\n    const Eigen::Array3i offset(candidate.offset[0] >> reduction_exponent,\n                                candidate.offset[1] >> reduction_exponent,\n                                candidate.offset[2] >> reduction_exponent);\n    CHECK_LT(depth, discrete_scan.cell_indices_per_depth.size());\n    for (const Eigen::Array3i& cell_index :\n         discrete_scan.cell_indices_per_depth[depth]) {\n      const Eigen::Array3i proposed_cell_index = cell_index + offset;\n      sum += precomputation_grid_stack_->Get(depth).value(proposed_cell_index);\n    }\n    candidate.score = PrecomputationGrid::ToProbability(\n        sum /\n        static_cast<float>(discrete_scan.cell_indices_per_depth[depth].size()));\n  }\n  std::sort(candidates->begin(), candidates->end(), std::greater<Candidate>());\n}\n\nstd::vector<Candidate>\nFastCorrelativeScanMatcher::ComputeLowestResolutionCandidates(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const std::vector<DiscreteScan>& discrete_scans) const {\n  std::vector<Candidate> lowest_resolution_candidates =\n      GenerateLowestResolutionCandidates(search_parameters,\n                                         discrete_scans.size());\n  ScoreCandidates(precomputation_grid_stack_->max_depth(), discrete_scans,\n                  &lowest_resolution_candidates);\n  return lowest_resolution_candidates;\n}\n\nCandidate FastCorrelativeScanMatcher::BranchAndBound(\n    const FastCorrelativeScanMatcher::SearchParameters& search_parameters,\n    const std::vector<DiscreteScan>& discrete_scans,\n    const std::vector<Candidate>& candidates, const int candidate_depth,\n    float min_score) const {\n  if (candidate_depth == 0) {\n    // Return the best candidate.\n    return *candidates.begin();\n  }\n\n  Candidate best_high_resolution_candidate(0, Eigen::Array3i::Zero());\n  best_high_resolution_candidate.score = min_score;\n  for (const Candidate& candidate : candidates) {\n    if (candidate.score <= min_score) {\n      break;\n    }\n    std::vector<Candidate> higher_resolution_candidates;\n    const int half_width = 1 << (candidate_depth - 1);\n    for (int z : {0, half_width}) {\n      if (candidate.offset.z() + z > search_parameters.linear_z_window_size) {\n        break;\n      }\n      for (int y : {0, half_width}) {\n        if (candidate.offset.y() + y >\n            search_parameters.linear_xy_window_size) {\n          break;\n        }\n        for (int x : {0, half_width}) {\n          if (candidate.offset.x() + x >\n              search_parameters.linear_xy_window_size) {\n            break;\n          }\n          higher_resolution_candidates.emplace_back(\n              candidate.scan_index, candidate.offset + Eigen::Array3i(x, y, z));\n        }\n      }\n    }\n    ScoreCandidates(candidate_depth - 1, discrete_scans,\n                    &higher_resolution_candidates);\n    best_high_resolution_candidate = std::max(\n        best_high_resolution_candidate,\n        BranchAndBound(search_parameters, discrete_scans,\n                       higher_resolution_candidates, candidate_depth - 1,\n                       best_high_resolution_candidate.score));\n  }\n  return best_high_resolution_candidate;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/fast_correlative_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an implementation of a 3D branch-and-bound algorithm similar to\n// mapping_2d::FastCorrelativeScanMatcher.\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/mapping_2d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_3d/scan_matching/rotational_scan_matcher.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nclass PrecomputationGridStack;\n\nstruct DiscreteScan {\n  transform::Rigid3f pose;\n  // Contains a vector of discretized scans for each 'depth'.\n  std::vector<std::vector<Eigen::Array3i>> cell_indices_per_depth;\n};\n\nstruct Candidate {\n  Candidate(const int scan_index, const Eigen::Array3i& offset)\n      : scan_index(scan_index), offset(offset) {}\n\n  // Index into the discrete scans vectors.\n  int scan_index;\n\n  // Linear offset from the initial pose in cell indices. For lower resolution\n  // candidates this is the lowest offset of the 2^depth x 2^depth x 2^depth\n  // block of possibilities.\n  Eigen::Array3i offset;\n\n  // Score, higher is better.\n  float score = 0.f;\n\n  bool operator<(const Candidate& other) const { return score < other.score; }\n  bool operator>(const Candidate& other) const { return score > other.score; }\n};\n\nclass FastCorrelativeScanMatcher {\n public:\n  FastCorrelativeScanMatcher(\n      const HybridGrid& hybrid_grid,\n      const std::vector<mapping::TrajectoryNode>& nodes,\n      const proto::FastCorrelativeScanMatcherOptions& options);\n  ~FastCorrelativeScanMatcher();\n\n  FastCorrelativeScanMatcher(const FastCorrelativeScanMatcher&) = delete;\n  FastCorrelativeScanMatcher& operator=(const FastCorrelativeScanMatcher&) =\n      delete;\n\n  // Aligns 'coarse_point_cloud' within the 'hybrid_grid' given an\n  // 'initial_pose_estimate'. If a score above 'min_score' (excluding equality)\n  // is possible, true is returned, and 'score' and 'pose_estimate' are updated\n  // with the result. 'fine_point_cloud' is used to compute the rotational scan\n  // matcher score.\n  bool Match(const transform::Rigid3d& initial_pose_estimate,\n             const sensor::PointCloud& coarse_point_cloud,\n             const sensor::PointCloud& fine_point_cloud, float min_score,\n             float* score, transform::Rigid3d* pose_estimate) const;\n\n  // Aligns 'coarse_point_cloud' within the 'hybrid_grid' given a rotation which\n  // is expected to be approximately gravity aligned. If a score above\n  // 'min_score' (excluding equality) is possible, true is returned, and 'score'\n  // and 'pose_estimate' are updated with the result. 'fine_point_cloud' is used\n  // to compute the rotational scan matcher score.\n  bool MatchFullSubmap(const Eigen::Quaterniond& gravity_alignment,\n                       const sensor::PointCloud& coarse_point_cloud,\n                       const sensor::PointCloud& fine_point_cloud,\n                       float min_score, float* score,\n                       transform::Rigid3d* pose_estimate) const;\n\n private:\n  struct SearchParameters {\n    const int linear_xy_window_size;     // voxels\n    const int linear_z_window_size;      // voxels\n    const double angular_search_window;  // radians\n  };\n\n  bool MatchWithSearchParameters(\n      const SearchParameters& search_parameters,\n      const transform::Rigid3d& initial_pose_estimate,\n      const sensor::PointCloud& coarse_point_cloud,\n      const sensor::PointCloud& fine_point_cloud, float min_score, float* score,\n      transform::Rigid3d* pose_estimate) const;\n  DiscreteScan DiscretizeScan(const SearchParameters& search_parameters,\n                              const sensor::PointCloud& point_cloud,\n                              const transform::Rigid3f& pose) const;\n  std::vector<DiscreteScan> GenerateDiscreteScans(\n      const SearchParameters& search_parameters,\n      const sensor::PointCloud& coarse_point_cloud,\n      const sensor::PointCloud& fine_point_cloud,\n      const transform::Rigid3f& initial_pose) const;\n  std::vector<Candidate> GenerateLowestResolutionCandidates(\n      const SearchParameters& search_parameters, int num_discrete_scans) const;\n  void ScoreCandidates(int depth,\n                       const std::vector<DiscreteScan>& discrete_scans,\n                       std::vector<Candidate>* const candidates) const;\n  std::vector<Candidate> ComputeLowestResolutionCandidates(\n      const SearchParameters& search_parameters,\n      const std::vector<DiscreteScan>& discrete_scans) const;\n  Candidate BranchAndBound(const SearchParameters& search_parameters,\n                           const std::vector<DiscreteScan>& discrete_scans,\n                           const std::vector<Candidate>& candidates,\n                           int candidate_depth, float min_score) const;\n\n  const proto::FastCorrelativeScanMatcherOptions options_;\n  const float resolution_;\n  const int width_in_voxels_;\n  std::unique_ptr<PrecomputationGridStack> precomputation_grid_stack_;\n  RotationalScanMatcher rotational_scan_matcher_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_FAST_CORRELATIVE_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/fast_correlative_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/fast_correlative_scan_matcher.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <random>\n#include <string>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/mapping_3d/range_data_inserter.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nproto::FastCorrelativeScanMatcherOptions\nCreateFastCorrelativeScanMatcherTestOptions(const int branch_and_bound_depth) {\n  auto parameter_dictionary = common::MakeDictionary(\n      \"return {\"\n      \"branch_and_bound_depth = \" +\n      std::to_string(branch_and_bound_depth) +\n      \", \"\n      \"full_resolution_depth = \" +\n      std::to_string(branch_and_bound_depth) +\n      \", \"\n      \"rotational_histogram_size = 30, \"\n      \"min_rotational_score = 0.1, \"\n      \"linear_xy_search_window = 0.8, \"\n      \"linear_z_search_window = 0.8, \"\n      \"angular_search_window = 0.3, \"\n      \"}\");\n  return CreateFastCorrelativeScanMatcherOptions(parameter_dictionary.get());\n}\n\nmapping_3d::proto::RangeDataInserterOptions\nCreateRangeDataInserterTestOptions() {\n  auto parameter_dictionary = common::MakeDictionary(\n      \"return { \"\n      \"hit_probability = 0.7, \"\n      \"miss_probability = 0.4, \"\n      \"num_free_space_voxels = 5, \"\n      \"}\");\n  return CreateRangeDataInserterOptions(parameter_dictionary.get());\n}\n\nTEST(FastCorrelativeScanMatcherTest, CorrectPose) {\n  std::mt19937 prng(42);\n  std::uniform_real_distribution<float> distribution(-1.f, 1.f);\n  RangeDataInserter range_data_inserter(CreateRangeDataInserterTestOptions());\n  constexpr float kMinScore = 0.1f;\n  const auto options = CreateFastCorrelativeScanMatcherTestOptions(5);\n\n  sensor::PointCloud point_cloud{\n      Eigen::Vector3f(4.f, 0.f, 0.f), Eigen::Vector3f(4.5f, 0.f, 0.f),\n      Eigen::Vector3f(5.f, 0.f, 0.f), Eigen::Vector3f(5.5f, 0.f, 0.f),\n      Eigen::Vector3f(0.f, 4.f, 0.f), Eigen::Vector3f(0.f, 4.5f, 0.f),\n      Eigen::Vector3f(0.f, 5.f, 0.f), Eigen::Vector3f(0.f, 5.5f, 0.f),\n      Eigen::Vector3f(0.f, 0.f, 4.f), Eigen::Vector3f(0.f, 0.f, 4.5f),\n      Eigen::Vector3f(0.f, 0.f, 5.f), Eigen::Vector3f(0.f, 0.f, 5.5f)};\n\n  for (int i = 0; i != 20; ++i) {\n    const float x = 0.7f * distribution(prng);\n    const float y = 0.7f * distribution(prng);\n    const float z = 0.7f * distribution(prng);\n    const float theta = 0.2f * distribution(prng);\n    const auto expected_pose =\n        transform::Rigid3f::Translation(Eigen::Vector3f(x, y, z)) *\n        transform::Rigid3f::Rotation(\n            Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ()));\n\n    HybridGrid hybrid_grid(0.05f);\n    hybrid_grid.StartUpdate();\n    range_data_inserter.Insert(\n        sensor::RangeData{\n            expected_pose.translation(),\n            sensor::TransformPointCloud(point_cloud, expected_pose),\n            {}},\n        &hybrid_grid);\n\n    FastCorrelativeScanMatcher fast_correlative_scan_matcher(hybrid_grid, {},\n                                                             options);\n    transform::Rigid3d pose_estimate;\n    float score;\n    EXPECT_TRUE(fast_correlative_scan_matcher.Match(\n        transform::Rigid3d::Identity(), point_cloud, point_cloud, kMinScore,\n        &score, &pose_estimate));\n    EXPECT_LT(kMinScore, score);\n    EXPECT_THAT(expected_pose,\n                transform::IsNearly(pose_estimate.cast<float>(), 0.05f))\n        << \"Actual: \" << transform::ToProto(pose_estimate).DebugString()\n        << \"\\nExpected: \" << transform::ToProto(expected_pose).DebugString();\n  }\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/interpolated_grid.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_INTERPOLATED_GRID_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_INTERPOLATED_GRID_H_\n\n#include <cmath>\n\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\n// Interpolates between HybridGrid probability voxels. We use the tricubic\n// interpolation which interpolates the values and has vanishing derivative at\n// these points.\n//\n// This class is templated to work with the autodiff that Ceres provides.\n// For this reason, it is also important that the interpolation scheme be\n// continuously differentiable.\nclass InterpolatedGrid {\n public:\n  explicit InterpolatedGrid(const HybridGrid& hybrid_grid)\n      : hybrid_grid_(hybrid_grid) {}\n\n  InterpolatedGrid(const InterpolatedGrid&) = delete;\n  InterpolatedGrid& operator=(const InterpolatedGrid&) = delete;\n\n  // Returns the interpolated probability at (x, y, z) of the HybridGrid\n  // used to perform the interpolation.\n  //\n  // This is a piecewise, continuously differentiable function. We use the\n  // scalar part of Jet parameters to select our interval below. It is the\n  // tensor product volume of piecewise cubic polynomials that interpolate\n  // the values, and have vanishing derivative at the interval boundaries.\n  template <typename T>\n  T GetProbability(const T& x, const T& y, const T& z) const {\n    double x1, y1, z1, x2, y2, z2;\n    ComputeInterpolationDataPoints(x, y, z, &x1, &y1, &z1, &x2, &y2, &z2);\n\n    const Eigen::Array3i index1 =\n        hybrid_grid_.GetCellIndex(Eigen::Vector3f(x1, y1, z1));\n    const double q111 = hybrid_grid_.GetProbability(index1);\n    const double q112 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(0, 0, 1));\n    const double q121 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(0, 1, 0));\n    const double q122 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(0, 1, 1));\n    const double q211 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(1, 0, 0));\n    const double q212 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(1, 0, 1));\n    const double q221 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(1, 1, 0));\n    const double q222 =\n        hybrid_grid_.GetProbability(index1 + Eigen::Array3i(1, 1, 1));\n\n    const T normalized_x = (x - x1) / (x2 - x1);\n    const T normalized_y = (y - y1) / (y2 - y1);\n    const T normalized_z = (z - z1) / (z2 - z1);\n\n    // Compute pow(..., 2) and pow(..., 3). Using pow() here is very expensive.\n    const T normalized_xx = normalized_x * normalized_x;\n    const T normalized_xxx = normalized_x * normalized_xx;\n    const T normalized_yy = normalized_y * normalized_y;\n    const T normalized_yyy = normalized_y * normalized_yy;\n    const T normalized_zz = normalized_z * normalized_z;\n    const T normalized_zzz = normalized_z * normalized_zz;\n\n    // We first interpolate in z, then y, then x. All 7 times this uses the same\n    // scheme: A * (2t^3 - 3t^2 + 1) + B * (-2t^3 + 3t^2).\n    // The first polynomial is 1 at t=0, 0 at t=1, the second polynomial is 0\n    // at t=0, 1 at t=1. Both polynomials have derivative zero at t=0 and t=1.\n    const T q11 = (q111 - q112) * normalized_zzz * 2. +\n                  (q112 - q111) * normalized_zz * 3. + q111;\n    const T q12 = (q121 - q122) * normalized_zzz * 2. +\n                  (q122 - q121) * normalized_zz * 3. + q121;\n    const T q21 = (q211 - q212) * normalized_zzz * 2. +\n                  (q212 - q211) * normalized_zz * 3. + q211;\n    const T q22 = (q221 - q222) * normalized_zzz * 2. +\n                  (q222 - q221) * normalized_zz * 3. + q221;\n    const T q1 = (q11 - q12) * normalized_yyy * 2. +\n                 (q12 - q11) * normalized_yy * 3. + q11;\n    const T q2 = (q21 - q22) * normalized_yyy * 2. +\n                 (q22 - q21) * normalized_yy * 3. + q21;\n    return (q1 - q2) * normalized_xxx * 2. + (q2 - q1) * normalized_xx * 3. +\n           q1;\n  }\n\n private:\n  template <typename T>\n  void ComputeInterpolationDataPoints(const T& x, const T& y, const T& z,\n                                      double* x1, double* y1, double* z1,\n                                      double* x2, double* y2,\n                                      double* z2) const {\n    const Eigen::Vector3f lower = CenterOfLowerVoxel(x, y, z);\n    *x1 = lower.x();\n    *y1 = lower.y();\n    *z1 = lower.z();\n    *x2 = lower.x() + hybrid_grid_.resolution();\n    *y2 = lower.y() + hybrid_grid_.resolution();\n    *z2 = lower.z() + hybrid_grid_.resolution();\n  }\n\n  // Center of the next lower voxel, i.e., not necessarily the voxel containing\n  // (x, y, z). For each dimension, the largest voxel index so that the\n  // corresponding center is at most the given coordinate.\n  Eigen::Vector3f CenterOfLowerVoxel(const double x, const double y,\n                                     const double z) const {\n    // Center of the cell containing (x, y, z).\n    Eigen::Vector3f center = hybrid_grid_.GetCenterOfCell(\n        hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));\n    // Move to the next lower voxel center.\n    if (center.x() > x) {\n      center.x() -= hybrid_grid_.resolution();\n    }\n    if (center.y() > y) {\n      center.y() -= hybrid_grid_.resolution();\n    }\n    if (center.z() > z) {\n      center.z() -= hybrid_grid_.resolution();\n    }\n    return center;\n  }\n\n  // Uses the scalar part of a Ceres Jet.\n  template <typename T>\n  Eigen::Vector3f CenterOfLowerVoxel(const T& jet_x, const T& jet_y,\n                                     const T& jet_z) const {\n    return CenterOfLowerVoxel(jet_x.a, jet_y.a, jet_z.a);\n  }\n\n  const HybridGrid& hybrid_grid_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_INTERPOLATED_GRID_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/interpolated_grid_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/interpolated_grid.h\"\n\n#include \"Eigen/Core\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nclass InterpolatedGridTest : public ::testing::Test {\n protected:\n  InterpolatedGridTest()\n      : hybrid_grid_(0.1f), interpolated_grid_(hybrid_grid_) {\n    for (const auto& point :\n         {Eigen::Vector3f(-3.f, 2.f, 0.f), Eigen::Vector3f(-4.f, 2.f, 0.f),\n          Eigen::Vector3f(-5.f, 2.f, 0.f), Eigen::Vector3f(-6.f, 2.f, 0.f),\n          Eigen::Vector3f(-6.f, 3.f, 1.f), Eigen::Vector3f(-6.f, 4.f, 2.f),\n          Eigen::Vector3f(-7.f, 3.f, 1.f)}) {\n      hybrid_grid_.SetProbability(hybrid_grid_.GetCellIndex(point), 1.);\n    }\n  }\n\n  float GetHybridGridProbability(float x, float y, float z) const {\n    return hybrid_grid_.GetProbability(\n        hybrid_grid_.GetCellIndex(Eigen::Vector3f(x, y, z)));\n  }\n\n  HybridGrid hybrid_grid_;\n  InterpolatedGrid interpolated_grid_;\n};\n\nTEST_F(InterpolatedGridTest, InterpolatesGridPoints) {\n  for (double z = -1.; z < 3.; z += hybrid_grid_.resolution()) {\n    for (double y = 1.; y < 5.; y += hybrid_grid_.resolution()) {\n      for (double x = -8.; x < -2.; x += hybrid_grid_.resolution()) {\n        EXPECT_NEAR(GetHybridGridProbability(x, y, z),\n                    interpolated_grid_.GetProbability(x, y, z), 1e-6);\n      }\n    }\n  }\n}\n\nTEST_F(InterpolatedGridTest, MonotonicBehaviorBetweenGridPointsInX) {\n  const double kSampleStep = hybrid_grid_.resolution() / 10.;\n  for (double z = -1.; z < 3.; z += hybrid_grid_.resolution()) {\n    for (double y = 1.; y < 5.; y += hybrid_grid_.resolution()) {\n      for (double x = -8.; x < -2.; x += hybrid_grid_.resolution()) {\n        const float start_probability = GetHybridGridProbability(x, y, z);\n        const float next_probability =\n            GetHybridGridProbability(x + hybrid_grid_.resolution(), y, z);\n        const float grid_difference = next_probability - start_probability;\n        if (std::abs(grid_difference) < 1e-6f) {\n          continue;\n        }\n        for (double sample = kSampleStep;\n             sample < hybrid_grid_.resolution() - 2 * kSampleStep;\n             sample += kSampleStep) {\n          EXPECT_LT(0., grid_difference * (interpolated_grid_.GetProbability(\n                                               x + sample + kSampleStep, y, z) -\n                                           interpolated_grid_.GetProbability(\n                                               x + sample, y, z)));\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/occupied_space_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n\n#include \"Eigen/Core\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/scan_matching/interpolated_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\n// Computes the cost of inserting occupied space described by the point cloud\n// into the map. The cost increases with the amount of free space that would be\n// replaced by occupied space.\nclass OccupiedSpaceCostFunctor {\n public:\n  // Creates an OccupiedSpaceCostFunctor using the specified grid, 'rotation' to\n  // add to all poses, and point cloud.\n  OccupiedSpaceCostFunctor(const double scaling_factor,\n                           const sensor::PointCloud& point_cloud,\n                           const HybridGrid& hybrid_grid)\n      : scaling_factor_(scaling_factor),\n        point_cloud_(point_cloud),\n        interpolated_grid_(hybrid_grid) {}\n\n  OccupiedSpaceCostFunctor(const OccupiedSpaceCostFunctor&) = delete;\n  OccupiedSpaceCostFunctor& operator=(const OccupiedSpaceCostFunctor&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const translation, const T* const rotation,\n                  T* const residual) const {\n    const transform::Rigid3<T> transform(\n        Eigen::Map<const Eigen::Matrix<T, 3, 1>>(translation),\n        Eigen::Quaternion<T>(rotation[0], rotation[1], rotation[2],\n                             rotation[3]));\n    return Evaluate(transform, residual);\n  }\n\n  template <typename T>\n  bool Evaluate(const transform::Rigid3<T>& transform,\n                T* const residual) const {\n    for (size_t i = 0; i < point_cloud_.size(); ++i) {\n      const Eigen::Matrix<T, 3, 1> world =\n          transform * point_cloud_[i].cast<T>();\n      const T probability =\n          interpolated_grid_.GetProbability(world[0], world[1], world[2]);\n      residual[i] = scaling_factor_ * (1. - probability);\n    }\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const sensor::PointCloud& point_cloud_;\n  const InterpolatedGrid interpolated_grid_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_OCCUPIED_SPACE_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/precomputation_grid.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/precomputation_grid.h\"\n\n#include <algorithm>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/probability_values.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nnamespace {\n\n// C++11 defines that integer division rounds towards zero. For index math, we\n// actually need it to round towards negative infinity. Luckily bit shifts have\n// that property.\ninline int DivideByTwoRoundingTowardsNegativeInfinity(const int value) {\n  return value >> 1;\n}\n\n// Computes the half resolution index corresponding to the full resolution\n// 'cell_index'.\nEigen::Array3i CellIndexAtHalfResolution(const Eigen::Array3i& cell_index) {\n  return Eigen::Array3i(\n      DivideByTwoRoundingTowardsNegativeInfinity(cell_index[0]),\n      DivideByTwoRoundingTowardsNegativeInfinity(cell_index[1]),\n      DivideByTwoRoundingTowardsNegativeInfinity(cell_index[2]));\n}\n\n}  // namespace\n\nPrecomputationGrid ConvertToPrecomputationGrid(const HybridGrid& hybrid_grid) {\n  PrecomputationGrid result(hybrid_grid.resolution());\n  for (auto it = HybridGrid::Iterator(hybrid_grid); !it.Done(); it.Next()) {\n    const int cell_value = common::RoundToInt(\n        (mapping::ValueToProbability(it.GetValue()) -\n         mapping::kMinProbability) *\n        (255.f / (mapping::kMaxProbability - mapping::kMinProbability)));\n    CHECK_GE(cell_value, 0);\n    CHECK_LE(cell_value, 255);\n    *result.mutable_value(it.GetCellIndex()) = cell_value;\n  }\n  return result;\n}\n\nPrecomputationGrid PrecomputeGrid(const PrecomputationGrid& grid,\n                                  const bool half_resolution,\n                                  const Eigen::Array3i& shift) {\n  PrecomputationGrid result(grid.resolution());\n  for (auto it = PrecomputationGrid::Iterator(grid); !it.Done(); it.Next()) {\n    for (int i = 0; i != 8; ++i) {\n      // We use this value to update 8 values in the resulting grid, at\n      // position (x - {0, 'shift'}, y - {0, 'shift'}, z - {0, 'shift'}).\n      // If 'shift' is 2 ** (depth - 1), where depth 0 is the original grid,\n      // this results in precomputation grids analogous to the 2D case.\n      const Eigen::Array3i cell_index =\n          it.GetCellIndex() - shift * PrecomputationGrid::GetOctant(i);\n      auto* const cell_value = result.mutable_value(\n          half_resolution ? CellIndexAtHalfResolution(cell_index) : cell_index);\n      *cell_value = std::max(it.GetValue(), *cell_value);\n    }\n  }\n  return result;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/precomputation_grid.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_PRECOMPUTATION_GRID_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_PRECOMPUTATION_GRID_H_\n\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nclass PrecomputationGrid : public HybridGridBase<uint8> {\n public:\n  explicit PrecomputationGrid(const float resolution)\n      : HybridGridBase<uint8>(resolution) {}\n\n  // Maps values from [0, 255] to [kMinProbability, kMaxProbability].\n  static float ToProbability(float value) {\n    return mapping::kMinProbability +\n           value *\n               ((mapping::kMaxProbability - mapping::kMinProbability) / 255.f);\n  }\n};\n\n// Converts a HybridGrid to a PrecomputationGrid representing the same data,\n// but only using 8 bit instead of 2 x 16 bit.\nPrecomputationGrid ConvertToPrecomputationGrid(const HybridGrid& hybrid_grid);\n\n// Returns a grid of the same resolution containing the maximum value of\n// original voxels in 'grid'. This maximum is over the 8 voxels that have\n// any combination of index components optionally increased by 'shift'.\n// If 'shift' is 2 ** (depth - 1), where depth 0 is the original grid, and this\n// is using the precomputed grid of one depth before, this results in\n// precomputation grids analogous to the 2D case.\nPrecomputationGrid PrecomputeGrid(const PrecomputationGrid& grid,\n                                  bool half_resolution,\n                                  const Eigen::Array3i& shift);\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_PRECOMPUTATION_GRID_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/precomputation_grid_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/precomputation_grid.h\"\n\n#include <random>\n#include <tuple>\n#include <vector>\n\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nTEST(PrecomputedGridGeneratorTest, TestAgainstNaiveAlgorithm) {\n  HybridGrid hybrid_grid(2.f);\n\n  std::mt19937 rng(23847);\n  std::uniform_int_distribution<int> coordinate_distribution(-50, 49);\n  std::uniform_real_distribution<float> value_distribution(\n      mapping::kMinProbability, mapping::kMaxProbability);\n  for (int i = 0; i < 1000; ++i) {\n    const auto x = coordinate_distribution(rng);\n    const auto y = coordinate_distribution(rng);\n    const auto z = coordinate_distribution(rng);\n    const Eigen::Array3i cell_index(x, y, z);\n    hybrid_grid.SetProbability(cell_index, value_distribution(rng));\n  }\n\n  std::vector<PrecomputationGrid> precomputed_grids;\n  for (int depth = 0; depth <= 3; ++depth) {\n    if (depth == 0) {\n      precomputed_grids.push_back(ConvertToPrecomputationGrid(hybrid_grid));\n    } else {\n      precomputed_grids.push_back(\n          PrecomputeGrid(precomputed_grids.back(), false,\n                         (1 << (depth - 1)) * Eigen::Array3i::Ones()));\n    }\n    const int width = 1 << depth;\n    for (int i = 0; i < 100; ++i) {\n      const auto x = coordinate_distribution(rng);\n      const auto y = coordinate_distribution(rng);\n      const auto z = coordinate_distribution(rng);\n      float max_probability = 0.;\n      for (int dx = 0; dx < width; ++dx) {\n        for (int dy = 0; dy < width; ++dy) {\n          for (int dz = 0; dz < width; ++dz) {\n            max_probability = std::max(\n                max_probability, hybrid_grid.GetProbability(\n                                     Eigen::Array3i(x + dx, y + dy, z + dz)));\n          }\n        }\n      }\n\n      EXPECT_NEAR(max_probability,\n                  PrecomputationGrid::ToProbability(\n                      precomputed_grids.back().value(Eigen::Array3i(x, y, z))),\n                  1e-2);\n    }\n  }\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/proto/ceres_scan_matcher_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.scan_matching.proto;\n\nimport \"cartographer/common/proto/ceres_solver_options.proto\";\n\n// NEXT ID: 7\nmessage CeresScanMatcherOptions {\n  // Scaling parameters for each cost functor.\n  repeated double occupied_space_weight = 1;\n  optional double translation_weight = 2;\n  optional double rotation_weight = 3;\n\n  // Whether only to allow changes to yaw, keeping roll/pitch constant.\n  optional bool only_optimize_yaw = 5;\n\n  // Configure the Ceres solver. See the Ceres documentation for more\n  // information: https://code.google.com/p/ceres-solver/\n  optional common.proto.CeresSolverOptions ceres_solver_options = 6;\n}\n"
  },
  {
    "path": "mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.mapping_3d.scan_matching.proto;\n\nmessage FastCorrelativeScanMatcherOptions {\n  // Number of precomputed grids to use.\n  optional int32 branch_and_bound_depth = 2;\n\n  // Number of full resolution grids to use, additional grids will reduce the\n  // resolution by half each.\n  optional int32 full_resolution_depth = 8;\n\n  // Number of histogram buckets for the rotational scan matcher.\n  optional int32 rotational_histogram_size = 3;\n\n  // Minimum score for the rotational scan matcher.\n  optional double min_rotational_score = 4;\n\n  // Linear search window in the plane orthogonal to gravity in which the best\n  // possible scan alignment will be found.\n  optional double linear_xy_search_window = 5;\n\n  // Linear search window in the gravity direction in which the best possible\n  // scan alignment will be found.\n  optional double linear_z_search_window = 6;\n\n  // Minimum angular search window in which the best possible scan alignment\n  // will be found.\n  optional double angular_search_window = 7;\n}\n"
  },
  {
    "path": "mapping_3d/scan_matching/real_time_correlative_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/real_time_correlative_scan_matcher.h\"\n\n#include <cmath>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nRealTimeCorrelativeScanMatcher::RealTimeCorrelativeScanMatcher(\n    const mapping_2d::scan_matching::proto::\n        RealTimeCorrelativeScanMatcherOptions& options)\n    : options_(options) {}\n\nfloat RealTimeCorrelativeScanMatcher::Match(\n    const transform::Rigid3d& initial_pose_estimate,\n    const sensor::PointCloud& point_cloud, const HybridGrid& hybrid_grid,\n    transform::Rigid3d* pose_estimate) const {\n  CHECK_NOTNULL(pose_estimate);\n  float best_score = -1.f;\n  for (const transform::Rigid3f& transform : GenerateExhaustiveSearchTransforms(\n           hybrid_grid.resolution(), point_cloud)) {\n    const transform::Rigid3f candidate =\n        initial_pose_estimate.cast<float>() * transform;\n    const float score = ScoreCandidate(\n        hybrid_grid, sensor::TransformPointCloud(point_cloud, candidate),\n        transform);\n    if (score > best_score) {\n      best_score = score;\n      *pose_estimate = candidate.cast<double>();\n    }\n  }\n  return best_score;\n}\n\nstd::vector<transform::Rigid3f>\nRealTimeCorrelativeScanMatcher::GenerateExhaustiveSearchTransforms(\n    const float resolution, const sensor::PointCloud& point_cloud) const {\n  std::vector<transform::Rigid3f> result;\n  const int linear_window_size =\n      common::RoundToInt(options_.linear_search_window() / resolution);\n  // We set this value to something on the order of resolution to make sure that\n  // the std::acos() below is defined.\n  float max_scan_range = 3.f * resolution;\n  for (const Eigen::Vector3f& point : point_cloud) {\n    const float range = point.norm();\n    max_scan_range = std::max(range, max_scan_range);\n  }\n  const float kSafetyMargin = 1.f - 1e-3f;\n  const float angular_step_size =\n      kSafetyMargin * std::acos(1.f - common::Pow2(resolution) /\n                                          (2.f * common::Pow2(max_scan_range)));\n  const int angular_window_size =\n      common::RoundToInt(options_.angular_search_window() / angular_step_size);\n  for (int z = -linear_window_size; z <= linear_window_size; ++z) {\n    for (int y = -linear_window_size; y <= linear_window_size; ++y) {\n      for (int x = -linear_window_size; x <= linear_window_size; ++x) {\n        for (int rz = -angular_window_size; rz <= angular_window_size; ++rz) {\n          for (int ry = -angular_window_size; ry <= angular_window_size; ++ry) {\n            for (int rx = -angular_window_size; rx <= angular_window_size;\n                 ++rx) {\n              const Eigen::Vector3f angle_axis(rx * angular_step_size,\n                                               ry * angular_step_size,\n                                               rz * angular_step_size);\n              result.emplace_back(\n                  Eigen::Vector3f(x * resolution, y * resolution,\n                                  z * resolution),\n                  transform::AngleAxisVectorToRotationQuaternion(angle_axis));\n            }\n          }\n        }\n      }\n    }\n  }\n  return result;\n}\n\nfloat RealTimeCorrelativeScanMatcher::ScoreCandidate(\n    const HybridGrid& hybrid_grid,\n    const sensor::PointCloud& transformed_point_cloud,\n    const transform::Rigid3f& transform) const {\n  float score = 0.f;\n  for (const Eigen::Vector3f& point : transformed_point_cloud) {\n    score += hybrid_grid.GetProbability(hybrid_grid.GetCellIndex(point));\n  }\n  score /= static_cast<float>(transformed_point_cloud.size());\n  const float angle = transform::GetAngle(transform);\n  score *=\n      std::exp(-common::Pow2(transform.translation().norm() *\n                                 options_.translation_delta_cost_weight() +\n                             angle * options_.rotation_delta_cost_weight()));\n  CHECK_GT(score, 0.f);\n  return score;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/real_time_correlative_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// A voxel accurate scan matcher, exhaustively evaluating the scan matching\n// search space.\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/mapping_2d/scan_matching/proto/real_time_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nclass RealTimeCorrelativeScanMatcher {\n public:\n  explicit RealTimeCorrelativeScanMatcher(\n      const mapping_2d::scan_matching::proto::\n          RealTimeCorrelativeScanMatcherOptions& options);\n\n  RealTimeCorrelativeScanMatcher(const RealTimeCorrelativeScanMatcher&) =\n      delete;\n  RealTimeCorrelativeScanMatcher& operator=(\n      const RealTimeCorrelativeScanMatcher&) = delete;\n\n  // Aligns 'point_cloud' within the 'hybrid_grid' given an\n  // 'initial_pose_estimate' then updates 'pose_estimate' with the result and\n  // returns the score.\n  float Match(const transform::Rigid3d& initial_pose_estimate,\n              const sensor::PointCloud& point_cloud,\n              const HybridGrid& hybrid_grid,\n              transform::Rigid3d* pose_estimate) const;\n\n private:\n  std::vector<transform::Rigid3f> GenerateExhaustiveSearchTransforms(\n      float resolution, const sensor::PointCloud& point_cloud) const;\n  float ScoreCandidate(const HybridGrid& hybrid_grid,\n                       const sensor::PointCloud& transformed_point_cloud,\n                       const transform::Rigid3f& transform) const;\n\n  const mapping_2d::scan_matching::proto::RealTimeCorrelativeScanMatcherOptions\n      options_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_REAL_TIME_CORRELATIVE_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/real_time_correlative_scan_matcher_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/real_time_correlative_scan_matcher.h\"\n\n#include <memory>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/mapping_2d/scan_matching/real_time_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\nnamespace {\n\nclass RealTimeCorrelativeScanMatcherTest : public ::testing::Test {\n protected:\n  RealTimeCorrelativeScanMatcherTest()\n      : hybrid_grid_(0.1f),\n        expected_pose_(Eigen::Vector3d(-1., 0., 0.),\n                       Eigen::Quaterniond::Identity()) {\n    for (const auto& point :\n         {Eigen::Vector3f(-3.f, 2.f, 0.f), Eigen::Vector3f(-4.f, 2.f, 0.f),\n          Eigen::Vector3f(-5.f, 2.f, 0.f), Eigen::Vector3f(-6.f, 2.f, 0.f),\n          Eigen::Vector3f(-6.f, 3.f, 1.f), Eigen::Vector3f(-6.f, 4.f, 2.f),\n          Eigen::Vector3f(-7.f, 3.f, 1.f)}) {\n      point_cloud_.push_back(point);\n      hybrid_grid_.SetProbability(\n          hybrid_grid_.GetCellIndex(expected_pose_.cast<float>() * point), 1.);\n    }\n\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n          linear_search_window = 0.3,\n          angular_search_window = math.rad(1.),\n          translation_delta_cost_weight = 1e-1,\n          rotation_delta_cost_weight = 1.,\n        })text\");\n    real_time_correlative_scan_matcher_.reset(\n        new RealTimeCorrelativeScanMatcher(\n            mapping_2d::scan_matching::\n                CreateRealTimeCorrelativeScanMatcherOptions(\n                    parameter_dictionary.get())));\n  }\n\n  void TestFromInitialPose(const transform::Rigid3d& initial_pose) {\n    transform::Rigid3d pose;\n\n    const float score = real_time_correlative_scan_matcher_->Match(\n        initial_pose, point_cloud_, hybrid_grid_, &pose);\n    LOG(INFO) << \"Score: \" << score;\n    EXPECT_THAT(pose, transform::IsNearly(expected_pose_, 1e-3));\n  }\n\n  HybridGrid hybrid_grid_;\n  transform::Rigid3d expected_pose_;\n  sensor::PointCloud point_cloud_;\n  std::unique_ptr<RealTimeCorrelativeScanMatcher>\n      real_time_correlative_scan_matcher_;\n};\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, PerfectEstimate) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., 0.)));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, AlongX) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-0.8, 0., 0.)));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, AlongZ) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-1., 0., -0.2)));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, AlongXYZ) {\n  TestFromInitialPose(\n      transform::Rigid3d::Translation(Eigen::Vector3d(-0.9, -0.2, 0.2)));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, RotationAroundX) {\n  TestFromInitialPose(transform::Rigid3d(\n      Eigen::Vector3d(-1., 0., 0.),\n      Eigen::AngleAxisd(0.8 / 180. * M_PI, Eigen::Vector3d(1., 0., 0.))));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, RotationAroundY) {\n  TestFromInitialPose(transform::Rigid3d(\n      Eigen::Vector3d(-1., 0., 0.),\n      Eigen::AngleAxisd(0.8 / 180. * M_PI, Eigen::Vector3d(0., 1., 0.))));\n}\n\nTEST_F(RealTimeCorrelativeScanMatcherTest, RotationAroundYZ) {\n  TestFromInitialPose(transform::Rigid3d(\n      Eigen::Vector3d(-1., 0., 0.),\n      Eigen::AngleAxisd(0.8 / 180. * M_PI, Eigen::Vector3d(0., 1., 1.))));\n}\n\n}  // namespace\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/rotation_delta_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n\n#include <cmath>\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/rotation.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\n// Computes the cost of rotating the pose estimate. Cost increases with the\n// solution's distance from the rotation estimate.\nclass RotationDeltaCostFunctor {\n public:\n  // Constructs a new RotationDeltaCostFunctor from the given\n  // 'rotation_estimate'.\n  explicit RotationDeltaCostFunctor(const double scaling_factor,\n                                    const Eigen::Quaterniond& initial_rotation)\n      : scaling_factor_(scaling_factor) {\n    initial_rotation_inverse_[0] = initial_rotation.w();\n    initial_rotation_inverse_[1] = -initial_rotation.x();\n    initial_rotation_inverse_[2] = -initial_rotation.y();\n    initial_rotation_inverse_[3] = -initial_rotation.z();\n  }\n\n  RotationDeltaCostFunctor(const RotationDeltaCostFunctor&) = delete;\n  RotationDeltaCostFunctor& operator=(const RotationDeltaCostFunctor&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const rotation_quaternion, T* residual) const {\n    T delta[4];\n    T initial_rotation_inverse[4] = {\n        T(initial_rotation_inverse_[0]), T(initial_rotation_inverse_[1]),\n        T(initial_rotation_inverse_[2]), T(initial_rotation_inverse_[3])};\n    ceres::QuaternionProduct(initial_rotation_inverse, rotation_quaternion,\n                             delta);\n    // Will compute the squared norm of the imaginary component of the delta\n    // quaternion which is sin(phi/2)^2.\n    residual[0] = scaling_factor_ * delta[1];\n    residual[1] = scaling_factor_ * delta[2];\n    residual[2] = scaling_factor_ * delta[3];\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  double initial_rotation_inverse_[4];\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATION_DELTA_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/rotational_scan_matcher.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/scan_matching/rotational_scan_matcher.h\"\n\n#include <map>\n#include <vector>\n\n#include \"cartographer/common/math.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nnamespace {\n\nconstexpr float kMinDistance = 0.2f;\nconstexpr float kMaxDistance = 0.9f;\nconstexpr float kSliceHeight = 0.2f;\n\nvoid AddValueToHistogram(float angle, const float value,\n                         Eigen::VectorXf* histogram) {\n  // Map the angle to [0, pi), i.e. a vector and its inverse are considered to\n  // represent the same angle.\n  while (angle > static_cast<float>(M_PI)) {\n    angle -= static_cast<float>(M_PI);\n  }\n  while (angle < 0.f) {\n    angle += static_cast<float>(M_PI);\n  }\n  const float zero_to_one = angle / static_cast<float>(M_PI);\n  const int bucket = common::Clamp<int>(\n      common::RoundToInt(histogram->size() * zero_to_one - 0.5f), 0,\n      histogram->size() - 1);\n  (*histogram)(bucket) += value;\n}\n\nEigen::Vector3f ComputeCentroid(const sensor::PointCloud& slice) {\n  CHECK(!slice.empty());\n  Eigen::Vector3f sum = Eigen::Vector3f::Zero();\n  for (const Eigen::Vector3f& point : slice) {\n    sum += point;\n  }\n  return sum / static_cast<float>(slice.size());\n}\n\nstruct AngleValuePair {\n  float angle;\n  float value;\n};\n\nvoid AddPointCloudSliceToValueVector(\n    const sensor::PointCloud& slice,\n    std::vector<AngleValuePair>* value_vector) {\n  if (slice.empty()) {\n    return;\n  }\n  // We compute the angle of the ray from a point to the centroid of the whole\n  // point cloud. If it is orthogonal to the angle we compute between points, we\n  // will add the angle between points to the histogram with the maximum weight.\n  // This is to reject, e.g., the angles observed on the ceiling and floor.\n  const Eigen::Vector3f centroid = ComputeCentroid(slice);\n  Eigen::Vector3f last_point = slice.front();\n  for (const Eigen::Vector3f& point : slice) {\n    const Eigen::Vector2f delta = (point - last_point).head<2>();\n    const Eigen::Vector2f direction = (point - centroid).head<2>();\n    const float distance = delta.norm();\n    if (distance < kMinDistance || direction.norm() < kMinDistance) {\n      continue;\n    }\n    if (distance > kMaxDistance) {\n      last_point = point;\n      continue;\n    }\n    const float angle = common::atan2(delta);\n    const float value = std::max(\n        0.f, 1.f - std::abs(delta.normalized().dot(direction.normalized())));\n    value_vector->push_back(AngleValuePair{angle, value});\n  }\n}\n\n// A function to sort the points in each slice by angle around the centroid.\n// This is because the returns from different rangefinders are interleaved in\n// the data.\nsensor::PointCloud SortSlice(const sensor::PointCloud& slice) {\n  struct SortableAnglePointPair {\n    bool operator<(const SortableAnglePointPair& rhs) const {\n      return angle < rhs.angle;\n    }\n\n    float angle;\n    Eigen::Vector3f point;\n  };\n  const Eigen::Vector3f centroid = ComputeCentroid(slice);\n  std::vector<SortableAnglePointPair> by_angle;\n  by_angle.reserve(slice.size());\n  for (const Eigen::Vector3f& point : slice) {\n    const Eigen::Vector2f delta = (point - centroid).head<2>();\n    if (delta.norm() < kMinDistance) {\n      continue;\n    }\n    by_angle.push_back(SortableAnglePointPair{common::atan2(delta), point});\n  }\n  std::sort(by_angle.begin(), by_angle.end());\n  sensor::PointCloud result;\n  for (const auto& pair : by_angle) {\n    result.push_back(pair.point);\n  }\n  return result;\n}\n\nstd::vector<AngleValuePair> GetValuesForHistogram(\n    const sensor::PointCloud& point_cloud) {\n  std::map<int, sensor::PointCloud> slices;\n  for (const Eigen::Vector3f& point : point_cloud) {\n    slices[common::RoundToInt(point.z() / kSliceHeight)].push_back(point);\n  }\n  std::vector<AngleValuePair> result;\n  for (const auto& slice : slices) {\n    AddPointCloudSliceToValueVector(SortSlice(slice.second), &result);\n  }\n  return result;\n}\n\nvoid AddValuesToHistogram(const std::vector<AngleValuePair>& value_vector,\n                          const float rotation, Eigen::VectorXf* histogram) {\n  for (const AngleValuePair& pair : value_vector) {\n    AddValueToHistogram(pair.angle + rotation, pair.value, histogram);\n  }\n}\n\n}  // namespace\n\nRotationalScanMatcher::RotationalScanMatcher(\n    const std::vector<mapping::TrajectoryNode>& nodes, const int histogram_size)\n    : histogram_(Eigen::VectorXf::Zero(histogram_size)) {\n  for (const mapping::TrajectoryNode& node : nodes) {\n    AddValuesToHistogram(\n        GetValuesForHistogram(sensor::TransformPointCloud(\n            node.constant_data->range_data_3d.returns.Decompress(),\n            node.pose.cast<float>())),\n        0.f, &histogram_);\n  }\n}\n\nstd::vector<float> RotationalScanMatcher::Match(\n    const sensor::PointCloud& point_cloud,\n    const std::vector<float>& angles) const {\n  std::vector<float> result;\n  result.reserve(angles.size());\n  const std::vector<AngleValuePair> value_vector =\n      GetValuesForHistogram(point_cloud);\n  for (const float angle : angles) {\n    Eigen::VectorXf scan_histogram = Eigen::VectorXf::Zero(histogram_.size());\n    AddValuesToHistogram(value_vector, angle, &scan_histogram);\n    result.push_back(MatchHistogram(scan_histogram));\n  }\n  return result;\n}\n\nfloat RotationalScanMatcher::MatchHistogram(\n    const Eigen::VectorXf& scan_histogram) const {\n  // We compute the dot product of normalized histograms as a measure of\n  // similarity.\n  const float scan_histogram_norm = scan_histogram.norm();\n  const float histogram_norm = histogram_.norm();\n  const float normalization = scan_histogram_norm * histogram_norm;\n  if (normalization < 1e-3f) {\n    return 1.f;\n  }\n  return histogram_.dot(scan_histogram) / normalization;\n}\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/scan_matching/rotational_scan_matcher.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATIONAL_SCAN_MATCHER_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATIONAL_SCAN_MATCHER_H_\n\n#include <vector>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\nclass RotationalScanMatcher {\n public:\n  explicit RotationalScanMatcher(\n      const std::vector<mapping::TrajectoryNode>& nodes, int histogram_size);\n\n  RotationalScanMatcher(const RotationalScanMatcher&) = delete;\n  RotationalScanMatcher& operator=(const RotationalScanMatcher&) = delete;\n\n  // Scores how well a 'point_cloud' can be understood as rotated by certain\n  // 'angles' relative to the 'nodes'. Each angle results in a score between\n  // 0 (worst) and 1 (best).\n  std::vector<float> Match(const sensor::PointCloud& point_cloud,\n                           const std::vector<float>& angles) const;\n\n private:\n  float MatchHistogram(const Eigen::VectorXf& scan_histogram) const;\n\n  Eigen::VectorXf histogram_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_ROTATIONAL_SCAN_MATCHER_H_\n"
  },
  {
    "path": "mapping_3d/scan_matching/translation_delta_cost_functor.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n#define CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n\n#include \"Eigen/Core\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace scan_matching {\n\n// Computes the cost of translating the initial pose estimate. Cost increases\n// with the solution's distance from the initial estimate.\nclass TranslationDeltaCostFunctor {\n public:\n  // Constructs a new TranslationDeltaCostFunctor from the given\n  // 'initial_pose_estimate'.\n  explicit TranslationDeltaCostFunctor(\n      const double scaling_factor,\n      const transform::Rigid3d& initial_pose_estimate)\n      : scaling_factor_(scaling_factor),\n        x_(initial_pose_estimate.translation().x()),\n        y_(initial_pose_estimate.translation().y()),\n        z_(initial_pose_estimate.translation().z()) {}\n\n  TranslationDeltaCostFunctor(const TranslationDeltaCostFunctor&) = delete;\n  TranslationDeltaCostFunctor& operator=(const TranslationDeltaCostFunctor&) =\n      delete;\n\n  template <typename T>\n  bool operator()(const T* const translation, T* residual) const {\n    residual[0] = scaling_factor_ * (translation[0] - x_);\n    residual[1] = scaling_factor_ * (translation[1] - y_);\n    residual[2] = scaling_factor_ * (translation[2] - z_);\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const double x_;\n  const double y_;\n  const double z_;\n};\n\n}  // namespace scan_matching\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SCAN_MATCHING_TRANSLATION_DELTA_COST_FUNCTOR_H_\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/constraint_builder.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/sparse_pose_graph/constraint_builder.h\"\n\n#include <cmath>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"Eigen/Eigenvalues\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/kalman_filter/pose_tracker.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/ceres_scan_matcher_options.pb.h\"\n#include \"cartographer/mapping_3d/scan_matching/proto/fast_correlative_scan_matcher_options.pb.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\n\nConstraintBuilder::ConstraintBuilder(\n    const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions& options,\n    common::ThreadPool* const thread_pool)\n    : options_(options),\n      thread_pool_(thread_pool),\n      sampler_(options.sampling_ratio()),\n      adaptive_voxel_filter_(options.adaptive_voxel_filter_options()),\n      ceres_scan_matcher_(options.ceres_scan_matcher_options_3d()) {}\n\nConstraintBuilder::~ConstraintBuilder() {\n  common::MutexLocker locker(&mutex_);\n  CHECK_EQ(constraints_.size(), 0) << \"WhenDone() was not called\";\n  CHECK_EQ(pending_computations_.size(), 0);\n  CHECK_EQ(submap_queued_work_items_.size(), 0);\n  CHECK(when_done_ == nullptr);\n}\n\nvoid ConstraintBuilder::MaybeAddConstraint(\n    const mapping::SubmapId& submap_id, const Submap* const submap,\n    const mapping::NodeId& node_id,\n    const sensor::CompressedPointCloud* const compressed_point_cloud,\n    const std::vector<mapping::TrajectoryNode>& submap_nodes,\n    const transform::Rigid3d& initial_pose) {\n  if (initial_pose.translation().norm() > options_.max_constraint_distance()) {\n    return;\n  }\n  if (sampler_.Pulse()) {\n    common::MutexLocker locker(&mutex_);\n    constraints_.emplace_back();\n    auto* const constraint = &constraints_.back();\n    ++pending_computations_[current_computation_];\n    const int current_computation = current_computation_;\n    ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n        submap_id, submap_nodes, &submap->high_resolution_hybrid_grid,\n        [=]() EXCLUDES(mutex_) {\n          ComputeConstraint(submap_id, submap, node_id,\n                            false,   /* match_full_submap */\n                            nullptr, /* trajectory_connectivity */\n                            compressed_point_cloud, initial_pose, constraint);\n          FinishComputation(current_computation);\n        });\n  }\n}\n\nvoid ConstraintBuilder::MaybeAddGlobalConstraint(\n    const mapping::SubmapId& submap_id, const Submap* const submap,\n    const mapping::NodeId& node_id,\n    const sensor::CompressedPointCloud* const compressed_point_cloud,\n    const std::vector<mapping::TrajectoryNode>& submap_nodes,\n    const Eigen::Quaterniond& gravity_alignment,\n    mapping::TrajectoryConnectivity* const trajectory_connectivity) {\n  common::MutexLocker locker(&mutex_);\n  constraints_.emplace_back();\n  auto* const constraint = &constraints_.back();\n  ++pending_computations_[current_computation_];\n  const int current_computation = current_computation_;\n  ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n      submap_id, submap_nodes, &submap->high_resolution_hybrid_grid,\n      [=]() EXCLUDES(mutex_) {\n        ComputeConstraint(\n            submap_id, submap, node_id, true, /* match_full_submap */\n            trajectory_connectivity, compressed_point_cloud,\n            transform::Rigid3d::Rotation(gravity_alignment), constraint);\n        FinishComputation(current_computation);\n      });\n}\n\nvoid ConstraintBuilder::NotifyEndOfScan() {\n  common::MutexLocker locker(&mutex_);\n  ++current_computation_;\n}\n\nvoid ConstraintBuilder::WhenDone(\n    const std::function<void(const ConstraintBuilder::Result&)> callback) {\n  common::MutexLocker locker(&mutex_);\n  CHECK(when_done_ == nullptr);\n  when_done_ =\n      common::make_unique<std::function<void(const Result&)>>(callback);\n  ++pending_computations_[current_computation_];\n  const int current_computation = current_computation_;\n  thread_pool_->Schedule(\n      [this, current_computation] { FinishComputation(current_computation); });\n}\n\nvoid ConstraintBuilder::ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n    const mapping::SubmapId& submap_id,\n    const std::vector<mapping::TrajectoryNode>& submap_nodes,\n    const HybridGrid* const submap, const std::function<void()> work_item) {\n  if (submap_scan_matchers_[submap_id].fast_correlative_scan_matcher !=\n      nullptr) {\n    thread_pool_->Schedule(work_item);\n  } else {\n    submap_queued_work_items_[submap_id].push_back(work_item);\n    if (submap_queued_work_items_[submap_id].size() == 1) {\n      thread_pool_->Schedule([=]() {\n        ConstructSubmapScanMatcher(submap_id, submap_nodes, submap);\n      });\n    }\n  }\n}\n\nvoid ConstraintBuilder::ConstructSubmapScanMatcher(\n    const mapping::SubmapId& submap_id,\n    const std::vector<mapping::TrajectoryNode>& submap_nodes,\n    const HybridGrid* const submap) {\n  auto submap_scan_matcher =\n      common::make_unique<scan_matching::FastCorrelativeScanMatcher>(\n          *submap, submap_nodes,\n          options_.fast_correlative_scan_matcher_options_3d());\n  common::MutexLocker locker(&mutex_);\n  submap_scan_matchers_[submap_id] = {submap, std::move(submap_scan_matcher)};\n  for (const std::function<void()>& work_item :\n       submap_queued_work_items_[submap_id]) {\n    thread_pool_->Schedule(work_item);\n  }\n  submap_queued_work_items_.erase(submap_id);\n}\n\nconst ConstraintBuilder::SubmapScanMatcher*\nConstraintBuilder::GetSubmapScanMatcher(const mapping::SubmapId& submap_id) {\n  common::MutexLocker locker(&mutex_);\n  const SubmapScanMatcher* submap_scan_matcher =\n      &submap_scan_matchers_[submap_id];\n  CHECK(submap_scan_matcher->fast_correlative_scan_matcher != nullptr);\n  return submap_scan_matcher;\n}\n\nvoid ConstraintBuilder::ComputeConstraint(\n    const mapping::SubmapId& submap_id, const Submap* const submap,\n    const mapping::NodeId& node_id, bool match_full_submap,\n    mapping::TrajectoryConnectivity* trajectory_connectivity,\n    const sensor::CompressedPointCloud* const compressed_point_cloud,\n    const transform::Rigid3d& initial_pose,\n    std::unique_ptr<OptimizationProblem::Constraint>* constraint) {\n  const SubmapScanMatcher* const submap_scan_matcher =\n      GetSubmapScanMatcher(submap_id);\n  const sensor::PointCloud point_cloud = compressed_point_cloud->Decompress();\n  const sensor::PointCloud filtered_point_cloud =\n      adaptive_voxel_filter_.Filter(point_cloud);\n\n  // The 'constraint_transform' (submap 'i' <- scan 'j') is computed from the\n  // initial guess 'initial_pose' for (submap 'i' <- scan 'j') and a\n  // 'filtered_point_cloud' in 'j'.\n  float score = 0.;\n  transform::Rigid3d pose_estimate;\n\n  if (match_full_submap) {\n    if (submap_scan_matcher->fast_correlative_scan_matcher->MatchFullSubmap(\n            initial_pose.rotation(), filtered_point_cloud, point_cloud,\n            options_.global_localization_min_score(), &score, &pose_estimate)) {\n      CHECK_GT(score, options_.global_localization_min_score());\n      CHECK_GE(node_id.trajectory_id, 0);\n      CHECK_GE(submap_id.trajectory_id, 0);\n      trajectory_connectivity->Connect(node_id.trajectory_id,\n                                       submap_id.trajectory_id);\n    } else {\n      return;\n    }\n  } else {\n    if (submap_scan_matcher->fast_correlative_scan_matcher->Match(\n            initial_pose, filtered_point_cloud, point_cloud,\n            options_.min_score(), &score, &pose_estimate)) {\n      // We've reported a successful local match.\n      CHECK_GT(score, options_.min_score());\n    } else {\n      return;\n    }\n  }\n  {\n    common::MutexLocker locker(&mutex_);\n    score_histogram_.Add(score);\n  }\n\n  // Use the CSM estimate as both the initial and previous pose. This has the\n  // effect that, in the absence of better information, we prefer the original\n  // CSM estimate.\n  ceres::Solver::Summary unused_summary;\n  transform::Rigid3d constraint_transform;\n  ceres_scan_matcher_.Match(\n      pose_estimate, pose_estimate,\n      {{&filtered_point_cloud, submap_scan_matcher->hybrid_grid}},\n      &constraint_transform, &unused_summary);\n\n  constraint->reset(new OptimizationProblem::Constraint{\n      submap_id,\n      node_id,\n      {constraint_transform, options_.loop_closure_translation_weight(),\n       options_.loop_closure_rotation_weight()},\n      OptimizationProblem::Constraint::INTER_SUBMAP});\n\n  if (options_.log_matches()) {\n    std::ostringstream info;\n    info << \"Node \" << node_id << \" with \" << filtered_point_cloud.size()\n         << \" points on submap \" << submap_id << std::fixed;\n    if (match_full_submap) {\n      info << \" matches\";\n    } else {\n      const transform::Rigid3d difference =\n          initial_pose.inverse() * constraint_transform;\n      info << \" differs by translation \" << std::setprecision(2)\n           << difference.translation().norm() << \" rotation \"\n           << std::setprecision(3) << transform::GetAngle(difference);\n    }\n    info << \" with score \" << std::setprecision(1) << 100. * score << \"%.\";\n    LOG(INFO) << info.str();\n  }\n}\n\nvoid ConstraintBuilder::FinishComputation(const int computation_index) {\n  Result result;\n  std::unique_ptr<std::function<void(const Result&)>> callback;\n  {\n    common::MutexLocker locker(&mutex_);\n    if (--pending_computations_[computation_index] == 0) {\n      pending_computations_.erase(computation_index);\n    }\n    if (pending_computations_.empty()) {\n      CHECK_EQ(submap_queued_work_items_.size(), 0);\n      if (when_done_ != nullptr) {\n        for (const std::unique_ptr<OptimizationProblem::Constraint>&\n                 constraint : constraints_) {\n          if (constraint != nullptr) {\n            result.push_back(*constraint);\n          }\n        }\n        if (options_.log_matches()) {\n          LOG(INFO) << constraints_.size() << \" computations resulted in \"\n                    << result.size() << \" additional constraints.\";\n          LOG(INFO) << \"Score histogram:\\n\" << score_histogram_.ToString(10);\n        }\n        constraints_.clear();\n        callback = std::move(when_done_);\n        when_done_.reset();\n      }\n    }\n  }\n  if (callback != nullptr) {\n    (*callback)(result);\n  }\n}\n\nint ConstraintBuilder::GetNumFinishedScans() {\n  common::MutexLocker locker(&mutex_);\n  if (pending_computations_.empty()) {\n    return current_computation_;\n  }\n  return pending_computations_.begin()->first;\n}\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/constraint_builder.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n#define CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n\n#include <array>\n#include <deque>\n#include <functional>\n#include <limits>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n#include \"cartographer/common/histogram.h\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n#include \"cartographer/mapping/trajectory_node.h\"\n#include \"cartographer/mapping_3d/scan_matching/ceres_scan_matcher.h\"\n#include \"cartographer/mapping_3d/scan_matching/fast_correlative_scan_matcher.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph/optimization_problem.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\n\n// Asynchronously computes constraints.\n//\n// Intermingle an arbitrary number of calls to MaybeAddConstraint() or\n// MaybeAddGlobalConstraint, then call WhenDone(). After all computations are\n// done the 'callback' will be called with the result and another\n// MaybeAdd(Global)Constraint()/WhenDone() cycle can follow.\n//\n// This class is thread-safe.\nclass ConstraintBuilder {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n  using Result = std::vector<Constraint>;\n\n  ConstraintBuilder(\n      const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions&\n          options,\n      common::ThreadPool* thread_pool);\n  ~ConstraintBuilder();\n\n  ConstraintBuilder(const ConstraintBuilder&) = delete;\n  ConstraintBuilder& operator=(const ConstraintBuilder&) = delete;\n\n  // Schedules exploring a new constraint between 'submap' identified by\n  // 'submap_id', and the 'point_cloud' for 'node_id'. The 'intial_pose' is\n  // relative to the 'submap'.\n  //\n  // The pointees of 'submap' and 'compressed_point_cloud' must stay valid until\n  // all computations are finished.\n  void MaybeAddConstraint(\n      const mapping::SubmapId& submap_id, const Submap* submap,\n      const mapping::NodeId& node_id,\n      const sensor::CompressedPointCloud* compressed_point_cloud,\n      const std::vector<mapping::TrajectoryNode>& submap_nodes,\n      const transform::Rigid3d& initial_pose);\n\n  // Schedules exploring a new constraint between 'submap' identified by\n  // 'submap_id' and the 'compressed_point_cloud' for 'node_id'.\n  // This performs full-submap matching.\n  //\n  // The 'gravity_alignment' is the rotation to apply to the point cloud data\n  // to make it approximately gravity aligned. The 'trajectory_connectivity' is\n  // updated if the full-submap match succeeds.\n  //\n  // The pointees of 'submap' and 'compressed_point_cloud' must stay valid until\n  // all computations are finished.\n  void MaybeAddGlobalConstraint(\n      const mapping::SubmapId& submap_id, const Submap* submap,\n      const mapping::NodeId& node_id,\n      const sensor::CompressedPointCloud* compressed_point_cloud,\n      const std::vector<mapping::TrajectoryNode>& submap_nodes,\n      const Eigen::Quaterniond& gravity_alignment,\n      mapping::TrajectoryConnectivity* trajectory_connectivity);\n\n  // Must be called after all computations related to one node have been added.\n  void NotifyEndOfScan();\n\n  // Registers the 'callback' to be called with the results, after all\n  // computations triggered by MaybeAddConstraint() have finished.\n  void WhenDone(std::function<void(const Result&)> callback);\n\n  // Returns the number of consecutive finished scans.\n  int GetNumFinishedScans();\n\n private:\n  struct SubmapScanMatcher {\n    const HybridGrid* hybrid_grid;\n    std::unique_ptr<scan_matching::FastCorrelativeScanMatcher>\n        fast_correlative_scan_matcher;\n  };\n\n  // Either schedules the 'work_item', or if needed, schedules the scan matcher\n  // construction and queues the 'work_item'.\n  void ScheduleSubmapScanMatcherConstructionAndQueueWorkItem(\n      const mapping::SubmapId& submap_id,\n      const std::vector<mapping::TrajectoryNode>& submap_nodes,\n      const HybridGrid* submap, std::function<void()> work_item)\n      REQUIRES(mutex_);\n\n  // Constructs the scan matcher for a 'submap', then schedules its work items.\n  void ConstructSubmapScanMatcher(\n      const mapping::SubmapId& submap_id,\n      const std::vector<mapping::TrajectoryNode>& submap_nodes,\n      const HybridGrid* submap) EXCLUDES(mutex_);\n\n  // Returns the scan matcher for a submap, which has to exist.\n  const SubmapScanMatcher* GetSubmapScanMatcher(\n      const mapping::SubmapId& submap_id) EXCLUDES(mutex_);\n\n  // Runs in a background thread and does computations for an additional\n  // constraint, assuming 'submap' and 'point_cloud' do not change anymore.\n  // If 'match_full_submap' is true, and global localization succeeds, will\n  // connect 'node_id.trajectory_id' and 'submap_id.trajectory_id' in\n  // 'trajectory_connectivity'.\n  // As output, it may create a new Constraint in 'constraint'.\n  void ComputeConstraint(\n      const mapping::SubmapId& submap_id, const Submap* submap,\n      const mapping::NodeId& node_id, bool match_full_submap,\n      mapping::TrajectoryConnectivity* trajectory_connectivity,\n      const sensor::CompressedPointCloud* compressed_point_cloud,\n      const transform::Rigid3d& initial_pose,\n      std::unique_ptr<Constraint>* constraint) EXCLUDES(mutex_);\n\n  // Decrements the 'pending_computations_' count. If all computations are done,\n  // runs the 'when_done_' callback and resets the state.\n  void FinishComputation(int computation_index) EXCLUDES(mutex_);\n\n  const mapping::sparse_pose_graph::proto::ConstraintBuilderOptions options_;\n  common::ThreadPool* thread_pool_;\n  common::Mutex mutex_;\n\n  // 'callback' set by WhenDone().\n  std::unique_ptr<std::function<void(const Result&)>> when_done_\n      GUARDED_BY(mutex_);\n\n  // Index of the scan in reaction to which computations are currently\n  // added. This is always the highest scan index seen so far, even when older\n  // scans are matched against a new submap.\n  int current_computation_ GUARDED_BY(mutex_) = 0;\n\n  // For each added scan, maps to the number of pending computations that were\n  // added for it.\n  std::map<int, int> pending_computations_ GUARDED_BY(mutex_);\n\n  // Constraints currently being computed in the background. A deque is used to\n  // keep pointers valid when adding more entries.\n  std::deque<std::unique_ptr<Constraint>> constraints_ GUARDED_BY(mutex_);\n\n  // Map of already constructed scan matchers by 'submap_id'.\n  std::map<mapping::SubmapId, SubmapScanMatcher> submap_scan_matchers_\n      GUARDED_BY(mutex_);\n\n  // Map by 'submap_id' of scan matchers under construction, and the work\n  // to do once construction is done.\n  std::map<mapping::SubmapId, std::vector<std::function<void()>>>\n      submap_queued_work_items_ GUARDED_BY(mutex_);\n\n  common::FixedRatioSampler sampler_;\n  const sensor::AdaptiveVoxelFilter adaptive_voxel_filter_;\n  scan_matching::CeresScanMatcher ceres_scan_matcher_;\n\n  // Histogram of scan matcher scores.\n  common::Histogram score_histogram_ GUARDED_BY(mutex_);\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_CONSTRAINT_BUILDER_H_\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/optimization_problem.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/sparse_pose_graph/optimization_problem.h\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/ceres_solver_options.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping_3d/acceleration_cost_function.h\"\n#include \"cartographer/mapping_3d/ceres_pose.h\"\n#include \"cartographer/mapping_3d/rotation_cost_function.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph/spa_cost_function.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"ceres/jet.h\"\n#include \"ceres/rotation.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\n\nnamespace {\n\nstruct ConstantYawQuaternionPlus {\n  template <typename T>\n  bool operator()(const T* x, const T* delta, T* x_plus_delta) const {\n    const T delta_norm =\n        ceres::sqrt(common::Pow2(delta[0]) + common::Pow2(delta[1]));\n    const T sin_delta_over_delta =\n        delta_norm < 1e-6 ? T(1.) : ceres::sin(delta_norm) / delta_norm;\n    T q_delta[4];\n    q_delta[0] = delta_norm < 1e-6 ? T(1.) : ceres::cos(delta_norm);\n    q_delta[1] = sin_delta_over_delta * delta[0];\n    q_delta[2] = sin_delta_over_delta * delta[1];\n    q_delta[3] = T(0.);\n    // We apply the 'delta' which is interpreted as an angle-axis rotation\n    // vector in the xy-plane of the submap frame. This way we can align to\n    // gravity because rotations around the z-axis in the submap frame do not\n    // change gravity alignment, while disallowing random rotations of the map\n    // that have nothing to do with gravity alignment (i.e. we disallow steps\n    // just changing \"yaw\" of the complete map).\n    ceres::QuaternionProduct(x, q_delta, x_plus_delta);\n    return true;\n  }\n};\n\n}  // namespace\n\nOptimizationProblem::OptimizationProblem(\n    const mapping::sparse_pose_graph::proto::OptimizationProblemOptions&\n        options,\n    FixZ fix_z)\n    : options_(options), fix_z_(fix_z) {}\n\nOptimizationProblem::~OptimizationProblem() {}\n\nvoid OptimizationProblem::AddImuData(const int trajectory_id,\n                                     const common::Time time,\n                                     const Eigen::Vector3d& linear_acceleration,\n                                     const Eigen::Vector3d& angular_velocity) {\n  CHECK_GE(trajectory_id, 0);\n  imu_data_.resize(\n      std::max(imu_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  imu_data_[trajectory_id].push_back(\n      ImuData{time, linear_acceleration, angular_velocity});\n}\n\nvoid OptimizationProblem::AddTrajectoryNode(\n    const int trajectory_id, const common::Time time,\n    const transform::Rigid3d& point_cloud_pose) {\n  CHECK_GE(trajectory_id, 0);\n  node_data_.resize(\n      std::max(node_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  node_data_[trajectory_id].push_back(NodeData{time, point_cloud_pose});\n}\n\nvoid OptimizationProblem::AddSubmap(const int trajectory_id,\n                                    const transform::Rigid3d& submap_pose) {\n  CHECK_GE(trajectory_id, 0);\n  submap_data_.resize(\n      std::max(submap_data_.size(), static_cast<size_t>(trajectory_id) + 1));\n  submap_data_[trajectory_id].push_back(SubmapData{submap_pose});\n}\n\nvoid OptimizationProblem::SetMaxNumIterations(const int32 max_num_iterations) {\n  options_.mutable_ceres_solver_options()->set_max_num_iterations(\n      max_num_iterations);\n}\n\nvoid OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {\n  if (node_data_.empty()) {\n    // Nothing to optimize.\n    return;\n  }\n\n  ceres::Problem::Options problem_options;\n  ceres::Problem problem(problem_options);\n\n  const auto translation_parameterization =\n      [this]() -> std::unique_ptr<ceres::LocalParameterization> {\n    return fix_z_ == FixZ::kYes\n               ? common::make_unique<ceres::SubsetParameterization>(\n                     3, std::vector<int>{2})\n               : nullptr;\n  };\n\n  // Set the starting point.\n  CHECK(!submap_data_.empty());\n  CHECK(!submap_data_[0].empty());\n  // TODO(hrapp): Move ceres data into SubmapData.\n  std::vector<std::deque<CeresPose>> C_submaps(submap_data_.size());\n  std::vector<std::deque<CeresPose>> C_nodes(node_data_.size());\n  for (size_t trajectory_id = 0; trajectory_id != submap_data_.size();\n       ++trajectory_id) {\n    for (size_t submap_index = 0;\n         submap_index != submap_data_[trajectory_id].size(); ++submap_index) {\n      if (trajectory_id == 0 && submap_index == 0) {\n        // Tie the first submap of the first trajectory to the origin.\n        C_submaps[trajectory_id].emplace_back(\n            transform::Rigid3d::Identity(), translation_parameterization(),\n            common::make_unique<ceres::AutoDiffLocalParameterization<\n                ConstantYawQuaternionPlus, 4, 2>>(),\n            &problem);\n        problem.SetParameterBlockConstant(\n            C_submaps[trajectory_id].back().translation());\n      } else {\n        C_submaps[trajectory_id].emplace_back(\n            submap_data_[trajectory_id][submap_index].pose,\n            translation_parameterization(),\n            common::make_unique<ceres::QuaternionParameterization>(), &problem);\n      }\n    }\n  }\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    for (size_t node_index = 0; node_index != node_data_[trajectory_id].size();\n         ++node_index) {\n      C_nodes[trajectory_id].emplace_back(\n          node_data_[trajectory_id][node_index].point_cloud_pose,\n          translation_parameterization(),\n          common::make_unique<ceres::QuaternionParameterization>(), &problem);\n    }\n  }\n\n  // Add cost functions for intra- and inter-submap constraints.\n  for (const Constraint& constraint : constraints) {\n    problem.AddResidualBlock(\n        new ceres::AutoDiffCostFunction<SpaCostFunction, 6, 4, 3, 4, 3>(\n            new SpaCostFunction(constraint.pose)),\n        // Only loop closure constraints should have a loss function.\n        constraint.tag == Constraint::INTER_SUBMAP\n            ? new ceres::HuberLoss(options_.huber_scale())\n            : nullptr,\n        C_submaps.at(constraint.submap_id.trajectory_id)\n            .at(constraint.submap_id.submap_index)\n            .rotation(),\n        C_submaps.at(constraint.submap_id.trajectory_id)\n            .at(constraint.submap_id.submap_index)\n            .translation(),\n        C_nodes.at(constraint.node_id.trajectory_id)\n            .at(constraint.node_id.node_index)\n            .rotation(),\n        C_nodes.at(constraint.node_id.trajectory_id)\n            .at(constraint.node_id.node_index)\n            .translation());\n  }\n\n  // Add constraints based on IMU observations of angular velocities and\n  // linear acceleration.\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    const std::deque<ImuData>& imu_data = imu_data_.at(trajectory_id);\n    CHECK(!imu_data.empty());\n    // TODO(whess): Add support for empty trajectories.\n    const auto& node_data = node_data_[trajectory_id];\n    CHECK(!node_data.empty());\n\n    // Skip IMU data before the first node of this trajectory.\n    auto it = imu_data.cbegin();\n    while ((it + 1) != imu_data.cend() && (it + 1)->time <= node_data[0].time) {\n      ++it;\n    }\n\n    for (size_t node_index = 1; node_index < node_data.size(); ++node_index) {\n      auto it2 = it;\n      const IntegrateImuResult<double> result =\n          IntegrateImu(imu_data, node_data[node_index - 1].time,\n                       node_data[node_index].time, &it);\n      if (node_index + 1 < node_data.size()) {\n        const common::Time first_time = node_data[node_index - 1].time;\n        const common::Time second_time = node_data[node_index].time;\n        const common::Time third_time = node_data[node_index + 1].time;\n        const common::Duration first_duration = second_time - first_time;\n        const common::Duration second_duration = third_time - second_time;\n        const common::Time first_center = first_time + first_duration / 2;\n        const common::Time second_center = second_time + second_duration / 2;\n        const IntegrateImuResult<double> result_to_first_center =\n            IntegrateImu(imu_data, first_time, first_center, &it2);\n        const IntegrateImuResult<double> result_center_to_center =\n            IntegrateImu(imu_data, first_center, second_center, &it2);\n        // 'delta_velocity' is the change in velocity from the point in time\n        // halfway between the first and second poses to halfway between second\n        // and third pose. It is computed from IMU data and still contains a\n        // delta due to gravity. The orientation of this vector is in the IMU\n        // frame at the second pose.\n        const Eigen::Vector3d delta_velocity =\n            (result.delta_rotation.inverse() *\n             result_to_first_center.delta_rotation) *\n            result_center_to_center.delta_velocity;\n        problem.AddResidualBlock(\n            new ceres::AutoDiffCostFunction<AccelerationCostFunction, 3, 4, 3,\n                                            3, 3, 1>(\n                new AccelerationCostFunction(\n                    options_.acceleration_weight(), delta_velocity,\n                    common::ToSeconds(first_duration),\n                    common::ToSeconds(second_duration))),\n            nullptr, C_nodes[trajectory_id].at(node_index).rotation(),\n            C_nodes[trajectory_id].at(node_index - 1).translation(),\n            C_nodes[trajectory_id].at(node_index).translation(),\n            C_nodes[trajectory_id].at(node_index + 1).translation(),\n            &gravity_constant_);\n      }\n      problem.AddResidualBlock(\n          new ceres::AutoDiffCostFunction<RotationCostFunction, 3, 4, 4>(\n              new RotationCostFunction(options_.rotation_weight(),\n                                       result.delta_rotation)),\n          nullptr, C_nodes[trajectory_id].at(node_index - 1).rotation(),\n          C_nodes[trajectory_id].at(node_index).rotation());\n    }\n  }\n\n  // Solve.\n  ceres::Solver::Summary summary;\n  ceres::Solve(\n      common::CreateCeresSolverOptions(options_.ceres_solver_options()),\n      &problem, &summary);\n\n  if (options_.log_solver_summary()) {\n    LOG(INFO) << summary.FullReport();\n    LOG(INFO) << \"Gravity was: \" << gravity_constant_;\n  }\n\n  // Store the result.\n  for (size_t trajectory_id = 0; trajectory_id != submap_data_.size();\n       ++trajectory_id) {\n    for (size_t submap_index = 0;\n         submap_index != submap_data_[trajectory_id].size(); ++submap_index) {\n      submap_data_[trajectory_id][submap_index].pose =\n          C_submaps[trajectory_id][submap_index].ToRigid();\n    }\n  }\n  for (size_t trajectory_id = 0; trajectory_id != node_data_.size();\n       ++trajectory_id) {\n    for (size_t node_index = 0; node_index != node_data_[trajectory_id].size();\n         ++node_index) {\n      node_data_[trajectory_id][node_index].point_cloud_pose =\n          C_nodes[trajectory_id][node_index].ToRigid();\n    }\n  }\n}\n\nconst std::vector<std::vector<NodeData>>& OptimizationProblem::node_data()\n    const {\n  return node_data_;\n}\n\nconst std::vector<std::vector<SubmapData>>& OptimizationProblem::submap_data()\n    const {\n  return submap_data_;\n}\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/optimization_problem.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n#define CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n\n#include <array>\n#include <deque>\n#include <map>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/optimization_problem_options.pb.h\"\n#include \"cartographer/mapping_3d/imu_integration.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\n\nstruct NodeData {\n  common::Time time;\n  transform::Rigid3d point_cloud_pose;\n};\n\nstruct SubmapData {\n  transform::Rigid3d pose;\n};\n\n// Implements the SPA loop closure method.\nclass OptimizationProblem {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n\n  enum class FixZ { kYes, kNo };\n\n  OptimizationProblem(\n      const mapping::sparse_pose_graph::proto::OptimizationProblemOptions&\n          options,\n      FixZ fix_z);\n  ~OptimizationProblem();\n\n  OptimizationProblem(const OptimizationProblem&) = delete;\n  OptimizationProblem& operator=(const OptimizationProblem&) = delete;\n\n  void AddImuData(int trajectory_id, common::Time time,\n                  const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity);\n  void AddTrajectoryNode(int trajectory_id, common::Time time,\n                         const transform::Rigid3d& point_cloud_pose);\n  void AddSubmap(int trajectory_id, const transform::Rigid3d& submap_pose);\n\n  void SetMaxNumIterations(int32 max_num_iterations);\n\n  // Computes the optimized poses.\n  void Solve(const std::vector<Constraint>& constraints);\n\n  const std::vector<std::vector<NodeData>>& node_data() const;\n  const std::vector<std::vector<SubmapData>>& submap_data() const;\n\n private:\n  mapping::sparse_pose_graph::proto::OptimizationProblemOptions options_;\n  FixZ fix_z_;\n  std::vector<std::deque<ImuData>> imu_data_;\n  std::vector<std::vector<NodeData>> node_data_;\n  std::vector<std::vector<SubmapData>> submap_data_;\n  double gravity_constant_ = 9.8;\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_OPTIMIZATION_PROBLEM_H_\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/optimization_problem_test.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/sparse_pose_graph/optimization_problem.h\"\n\n#include <random>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/sparse_pose_graph/optimization_problem_options.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\nnamespace {\n\nclass OptimizationProblemTest : public ::testing::Test {\n protected:\n  OptimizationProblemTest()\n      : optimization_problem_(CreateOptions(), OptimizationProblem::FixZ::kNo),\n        rng_(45387) {}\n\n  mapping::sparse_pose_graph::proto::OptimizationProblemOptions\n  CreateOptions() {\n    auto parameter_dictionary = common::MakeDictionary(R\"text(\n        return {\n          acceleration_weight = 1e-4,\n          rotation_weight = 1e-2,\n          huber_scale = 1.,\n          consecutive_scan_translation_penalty_factor = 1e-2,\n          consecutive_scan_rotation_penalty_factor = 1e-2,\n          log_solver_summary = true,\n          ceres_solver_options = {\n            use_nonmonotonic_steps = false,\n            max_num_iterations = 200,\n            num_threads = 4,\n          },\n        })text\");\n    return mapping::sparse_pose_graph::CreateOptimizationProblemOptions(\n        parameter_dictionary.get());\n  }\n\n  transform::Rigid3d RandomTransform(double translation_size,\n                                     double rotation_size) {\n    std::uniform_real_distribution<double> translation_distribution(\n        -translation_size, translation_size);\n    const double x = translation_distribution(rng_);\n    const double y = translation_distribution(rng_);\n    const double z = translation_distribution(rng_);\n    std::uniform_real_distribution<double> rotation_distribution(-rotation_size,\n                                                                 rotation_size);\n    const double rx = rotation_distribution(rng_);\n    const double ry = rotation_distribution(rng_);\n    const double rz = rotation_distribution(rng_);\n    return transform::Rigid3d(Eigen::Vector3d(x, y, z),\n                              transform::AngleAxisVectorToRotationQuaternion(\n                                  Eigen::Vector3d(rx, ry, rz)));\n  }\n\n  transform::Rigid3d RandomYawOnlyTransform(double translation_size,\n                                            double rotation_size) {\n    std::uniform_real_distribution<double> translation_distribution(\n        -translation_size, translation_size);\n    const double x = translation_distribution(rng_);\n    const double y = translation_distribution(rng_);\n    const double z = translation_distribution(rng_);\n    std::uniform_real_distribution<double> rotation_distribution(-rotation_size,\n                                                                 rotation_size);\n    const double rz = rotation_distribution(rng_);\n    return transform::Rigid3d(Eigen::Vector3d(x, y, z),\n                              transform::AngleAxisVectorToRotationQuaternion(\n                                  Eigen::Vector3d(0., 0., rz)));\n  }\n\n  OptimizationProblem optimization_problem_;\n  std::mt19937 rng_;\n};\n\ntransform::Rigid3d AddNoise(const transform::Rigid3d& transform,\n                            const transform::Rigid3d& noise) {\n  const Eigen::Quaterniond noisy_rotation(noise.rotation() *\n                                          transform.rotation());\n  return transform::Rigid3d(transform.translation() + noise.translation(),\n                            noisy_rotation);\n}\n\nTEST_F(OptimizationProblemTest, ReducesNoise) {\n  constexpr int kNumNodes = 100;\n  const transform::Rigid3d kSubmap0Transform = transform::Rigid3d::Identity();\n  const transform::Rigid3d kSubmap2Transform = transform::Rigid3d::Rotation(\n      Eigen::AngleAxisd(M_PI, Eigen::Vector3d::UnitZ()));\n  const int kTrajectoryId = 0;\n\n  struct NoisyNode {\n    transform::Rigid3d ground_truth_pose;\n    transform::Rigid3d noise;\n  };\n  std::vector<NoisyNode> test_data;\n  for (int j = 0; j != kNumNodes; ++j) {\n    test_data.push_back(\n        NoisyNode{RandomTransform(10., 3.), RandomYawOnlyTransform(0.2, 0.3)});\n  }\n\n  common::Time now = common::FromUniversal(0);\n  for (const NoisyNode& node : test_data) {\n    const transform::Rigid3d pose =\n        AddNoise(node.ground_truth_pose, node.noise);\n    optimization_problem_.AddImuData(kTrajectoryId, now,\n                                     Eigen::Vector3d::UnitZ() * 9.81,\n                                     Eigen::Vector3d::Zero());\n    optimization_problem_.AddTrajectoryNode(kTrajectoryId, now, pose);\n    now += common::FromSeconds(0.01);\n  }\n\n  std::vector<OptimizationProblem::Constraint> constraints;\n  for (int j = 0; j != kNumNodes; ++j) {\n    constraints.push_back(OptimizationProblem::Constraint{\n        mapping::SubmapId{0, 0}, mapping::NodeId{0, j},\n        OptimizationProblem::Constraint::Pose{\n            AddNoise(test_data[j].ground_truth_pose, test_data[j].noise), 1.,\n            1.}});\n    // We add an additional independent, but equally noisy observation.\n    constraints.push_back(OptimizationProblem::Constraint{\n        mapping::SubmapId{0, 1}, mapping::NodeId{0, j},\n        OptimizationProblem::Constraint::Pose{\n            AddNoise(test_data[j].ground_truth_pose,\n                     RandomYawOnlyTransform(0.2, 0.3)),\n            1., 1.}});\n    // We add very noisy data with a low weight to verify it is mostly ignored.\n    constraints.push_back(OptimizationProblem::Constraint{\n        mapping::SubmapId{0, 2}, mapping::NodeId{0, j},\n        OptimizationProblem::Constraint::Pose{\n            kSubmap2Transform.inverse() * test_data[j].ground_truth_pose *\n                RandomTransform(1e3, 3.),\n            1e-9, 1e-9}});\n  }\n\n  double translation_error_before = 0.;\n  double rotation_error_before = 0.;\n  const auto& node_data = optimization_problem_.node_data().at(0);\n  for (int j = 0; j != kNumNodes; ++j) {\n    translation_error_before += (test_data[j].ground_truth_pose.translation() -\n                                 node_data[j].point_cloud_pose.translation())\n                                    .norm();\n    rotation_error_before +=\n        transform::GetAngle(test_data[j].ground_truth_pose.inverse() *\n                            node_data[j].point_cloud_pose);\n  }\n\n  optimization_problem_.AddSubmap(kTrajectoryId, kSubmap0Transform);\n  optimization_problem_.AddSubmap(kTrajectoryId, kSubmap0Transform);\n  optimization_problem_.AddSubmap(kTrajectoryId, kSubmap2Transform);\n  optimization_problem_.Solve(constraints);\n\n  double translation_error_after = 0.;\n  double rotation_error_after = 0.;\n  for (int j = 0; j != kNumNodes; ++j) {\n    translation_error_after += (test_data[j].ground_truth_pose.translation() -\n                                node_data[j].point_cloud_pose.translation())\n                                   .norm();\n    rotation_error_after +=\n        transform::GetAngle(test_data[j].ground_truth_pose.inverse() *\n                            node_data[j].point_cloud_pose);\n  }\n\n  EXPECT_GT(0.8 * translation_error_before, translation_error_after);\n  EXPECT_GT(0.8 * rotation_error_before, rotation_error_after);\n}\n\n}  // namespace\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph/spa_cost_function.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n#define CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n\n#include <array>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"ceres/ceres.h\"\n#include \"ceres/jet.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\nnamespace sparse_pose_graph {\n\nclass SpaCostFunction {\n public:\n  using Constraint = mapping::SparsePoseGraph::Constraint;\n\n  explicit SpaCostFunction(const Constraint::Pose& pose) : pose_(pose) {}\n\n  // Computes the error between the scan-to-submap alignment 'zbar_ij' and the\n  // difference of submap pose 'c_i' and scan pose 'c_j' which are both in an\n  // arbitrary common frame.\n  template <typename T>\n  static std::array<T, 6> ComputeUnscaledError(\n      const transform::Rigid3d& zbar_ij, const T* const c_i_rotation,\n      const T* const c_i_translation, const T* const c_j_rotation,\n      const T* const c_j_translation) {\n    const Eigen::Quaternion<T> R_i_inverse(c_i_rotation[0], -c_i_rotation[1],\n                                           -c_i_rotation[2], -c_i_rotation[3]);\n\n    const Eigen::Matrix<T, 3, 1> delta(c_j_translation[0] - c_i_translation[0],\n                                       c_j_translation[1] - c_i_translation[1],\n                                       c_j_translation[2] - c_i_translation[2]);\n    const Eigen::Matrix<T, 3, 1> h_translation = R_i_inverse * delta;\n\n    const Eigen::Quaternion<T> h_rotation_inverse =\n        Eigen::Quaternion<T>(c_j_rotation[0], -c_j_rotation[1],\n                             -c_j_rotation[2], -c_j_rotation[3]) *\n        Eigen::Quaternion<T>(c_i_rotation[0], c_i_rotation[1], c_i_rotation[2],\n                             c_i_rotation[3]);\n\n    const Eigen::Matrix<T, 3, 1> angle_axis_difference =\n        transform::RotationQuaternionToAngleAxisVector(\n            h_rotation_inverse * zbar_ij.rotation().cast<T>());\n\n    return {{T(zbar_ij.translation().x()) - h_translation[0],\n             T(zbar_ij.translation().y()) - h_translation[1],\n             T(zbar_ij.translation().z()) - h_translation[2],\n             angle_axis_difference[0], angle_axis_difference[1],\n             angle_axis_difference[2]}};\n  }\n\n  // Computes the error scaled by 'translation_weight' and 'rotation_weight',\n  // storing it in 'e'.\n  template <typename T>\n  static void ComputeScaledError(const Constraint::Pose& pose,\n                                 const T* const c_i_rotation,\n                                 const T* const c_i_translation,\n                                 const T* const c_j_rotation,\n                                 const T* const c_j_translation, T* const e) {\n    const std::array<T, 6> e_ij =\n        ComputeUnscaledError(pose.zbar_ij, c_i_rotation, c_i_translation,\n                             c_j_rotation, c_j_translation);\n    for (int ij : {0, 1, 2}) {\n      e[ij] = e_ij[ij] * T(pose.translation_weight);\n    }\n    for (int ij : {3, 4, 5}) {\n      e[ij] = e_ij[ij] * T(pose.rotation_weight);\n    }\n  }\n\n  template <typename T>\n  bool operator()(const T* const c_i_rotation, const T* const c_i_translation,\n                  const T* const c_j_rotation, const T* const c_j_translation,\n                  T* const e) const {\n    ComputeScaledError(pose_, c_i_rotation, c_i_translation, c_j_rotation,\n                       c_j_translation, e);\n    return true;\n  }\n\n private:\n  const Constraint::Pose pose_;\n};\n\n}  // namespace sparse_pose_graph\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_SPA_COST_FUNCTION_H_\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/sparse_pose_graph.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstdio>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"Eigen/Eigenvalues\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping/sparse_pose_graph/proto/constraint_builder_options.pb.h\"\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n#include \"cartographer/sensor/voxel_filter.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nSparsePoseGraph::SparsePoseGraph(\n    const mapping::proto::SparsePoseGraphOptions& options,\n    common::ThreadPool* thread_pool)\n    : options_(options),\n      optimization_problem_(options_.optimization_problem_options(),\n                            sparse_pose_graph::OptimizationProblem::FixZ::kNo),\n      constraint_builder_(options_.constraint_builder_options(), thread_pool) {}\n\nSparsePoseGraph::~SparsePoseGraph() {\n  WaitForAllComputations();\n  common::MutexLocker locker(&mutex_);\n  CHECK(scan_queue_ == nullptr);\n}\n\nvoid SparsePoseGraph::GrowSubmapTransformsAsNeeded(\n    const std::vector<const Submap*>& insertion_submaps) {\n  CHECK(!insertion_submaps.empty());\n  const mapping::SubmapId first_submap_id = GetSubmapId(insertion_submaps[0]);\n  const int trajectory_id = first_submap_id.trajectory_id;\n  CHECK_GE(trajectory_id, 0);\n  const auto& submap_data = optimization_problem_.submap_data();\n  if (insertion_submaps.size() == 1) {\n    // If we don't already have an entry for the first submap, add one.\n    CHECK_EQ(first_submap_id.submap_index, 0);\n    if (static_cast<size_t>(trajectory_id) >= submap_data.size() ||\n        submap_data[trajectory_id].empty()) {\n      optimization_problem_.AddSubmap(trajectory_id,\n                                      transform::Rigid3d::Identity());\n    }\n    return;\n  }\n  CHECK_EQ(2, insertion_submaps.size());\n  const int next_submap_index = submap_data.at(trajectory_id).size();\n  // CHECK that we have a index for the second submap.\n  const mapping::SubmapId second_submap_id = GetSubmapId(insertion_submaps[1]);\n  CHECK_EQ(second_submap_id.trajectory_id, trajectory_id);\n  CHECK_LE(second_submap_id.submap_index, next_submap_index);\n  // Extrapolate if necessary.\n  if (second_submap_id.submap_index == next_submap_index) {\n    const auto& first_submap_pose =\n        submap_data.at(trajectory_id).at(first_submap_id.submap_index).pose;\n    optimization_problem_.AddSubmap(\n        trajectory_id, first_submap_pose *\n                           insertion_submaps[0]->local_pose.inverse() *\n                           insertion_submaps[1]->local_pose);\n  }\n}\n\nvoid SparsePoseGraph::AddScan(\n    common::Time time, const sensor::RangeData& range_data_in_tracking,\n    const transform::Rigid3d& pose, const int trajectory_id,\n    const Submap* const matching_submap,\n    const std::vector<const Submap*>& insertion_submaps) {\n  const transform::Rigid3d optimized_pose(\n      GetLocalToGlobalTransform(trajectory_id) * pose);\n\n  common::MutexLocker locker(&mutex_);\n  constant_node_data_.push_back(mapping::TrajectoryNode::ConstantData{\n      time, sensor::RangeData{Eigen::Vector3f::Zero(), {}, {}},\n      sensor::Compress(range_data_in_tracking), trajectory_id,\n      transform::Rigid3d::Identity()});\n  trajectory_nodes_.Append(\n      trajectory_id,\n      mapping::TrajectoryNode{&constant_node_data_.back(), optimized_pose});\n  ++num_trajectory_nodes_;\n  trajectory_connectivity_.Add(trajectory_id);\n\n  if (submap_ids_.count(insertion_submaps.back()) == 0) {\n    const mapping::SubmapId submap_id =\n        submap_states_.Append(trajectory_id, SubmapState());\n    submap_ids_.emplace(insertion_submaps.back(), submap_id);\n    submap_states_.at(submap_id).submap = insertion_submaps.back();\n  }\n  const Submap* const finished_submap =\n      insertion_submaps.front()->finished ? insertion_submaps.front() : nullptr;\n\n  // Make sure we have a sampler for this trajectory.\n  if (!global_localization_samplers_[trajectory_id]) {\n    global_localization_samplers_[trajectory_id] =\n        common::make_unique<common::FixedRatioSampler>(\n            options_.global_sampling_ratio());\n  }\n\n  AddWorkItem([=]() REQUIRES(mutex_) {\n    ComputeConstraintsForScan(matching_submap, insertion_submaps,\n                              finished_submap, pose);\n  });\n}\n\nvoid SparsePoseGraph::AddWorkItem(std::function<void()> work_item) {\n  if (scan_queue_ == nullptr) {\n    work_item();\n  } else {\n    scan_queue_->push_back(work_item);\n  }\n}\n\nvoid SparsePoseGraph::AddImuData(const int trajectory_id, common::Time time,\n                                 const Eigen::Vector3d& linear_acceleration,\n                                 const Eigen::Vector3d& angular_velocity) {\n  common::MutexLocker locker(&mutex_);\n  AddWorkItem([=]() REQUIRES(mutex_) {\n    optimization_problem_.AddImuData(trajectory_id, time, linear_acceleration,\n                                     angular_velocity);\n  });\n}\n\nvoid SparsePoseGraph::ComputeConstraint(const mapping::NodeId& node_id,\n                                        const mapping::SubmapId& submap_id) {\n  const transform::Rigid3d inverse_submap_pose =\n      optimization_problem_.submap_data()\n          .at(submap_id.trajectory_id)\n          .at(submap_id.submap_index)\n          .pose.inverse();\n\n  const transform::Rigid3d initial_relative_pose =\n      inverse_submap_pose * optimization_problem_.node_data()\n                                .at(node_id.trajectory_id)\n                                .at(node_id.node_index)\n                                .point_cloud_pose;\n\n  std::vector<mapping::TrajectoryNode> submap_nodes;\n  for (const mapping::NodeId& submap_node_id :\n       submap_states_.at(submap_id).node_ids) {\n    submap_nodes.push_back(mapping::TrajectoryNode{\n        trajectory_nodes_.at(submap_node_id).constant_data,\n        inverse_submap_pose * trajectory_nodes_.at(submap_node_id).pose});\n  }\n\n  // Only globally match against submaps not in this trajectory.\n  if (node_id.trajectory_id != submap_id.trajectory_id &&\n      global_localization_samplers_[node_id.trajectory_id]->Pulse()) {\n    // In this situation, 'initial_relative_pose' is:\n    //\n    // submap <- global map 2 <- global map 1 <- tracking\n    //               (agreeing on gravity)\n    //\n    // Since they possibly came from two disconnected trajectories, the only\n    // guaranteed connection between the tracking and the submap frames is\n    // an agreement on the direction of gravity. Therefore, excluding yaw,\n    // 'initial_relative_pose.rotation()' is a good estimate of the relative\n    // orientation of the point cloud in the submap frame. Finding the correct\n    // yaw component will be handled by the matching procedure in the\n    // FastCorrelativeScanMatcher, and the given yaw is essentially ignored.\n    constraint_builder_.MaybeAddGlobalConstraint(\n        submap_id, submap_states_.at(submap_id).submap, node_id,\n        &trajectory_nodes_.at(node_id).constant_data->range_data_3d.returns,\n        submap_nodes, initial_relative_pose.rotation(),\n        &trajectory_connectivity_);\n  } else {\n    const bool scan_and_submap_trajectories_connected =\n        reverse_connected_components_.count(node_id.trajectory_id) > 0 &&\n        reverse_connected_components_.count(submap_id.trajectory_id) > 0 &&\n        reverse_connected_components_.at(node_id.trajectory_id) ==\n            reverse_connected_components_.at(submap_id.trajectory_id);\n    if (node_id.trajectory_id == submap_id.trajectory_id ||\n        scan_and_submap_trajectories_connected) {\n      constraint_builder_.MaybeAddConstraint(\n          submap_id, submap_states_.at(submap_id).submap, node_id,\n          &trajectory_nodes_.at(node_id).constant_data->range_data_3d.returns,\n          submap_nodes, initial_relative_pose);\n    }\n  }\n}\n\nvoid SparsePoseGraph::ComputeConstraintsForOldScans(const Submap* submap) {\n  const auto submap_id = GetSubmapId(submap);\n  const auto& submap_state = submap_states_.at(submap_id);\n\n  const auto& node_data = optimization_problem_.node_data();\n  for (size_t trajectory_id = 0; trajectory_id != node_data.size();\n       ++trajectory_id) {\n    for (size_t node_index = 0; node_index != node_data[trajectory_id].size();\n         ++node_index) {\n      const mapping::NodeId node_id{static_cast<int>(trajectory_id),\n                                    static_cast<int>(node_index)};\n      if (submap_state.node_ids.count(node_id) == 0) {\n        ComputeConstraint(node_id, submap_id);\n      }\n    }\n  }\n}\n\nvoid SparsePoseGraph::ComputeConstraintsForScan(\n    const Submap* matching_submap, std::vector<const Submap*> insertion_submaps,\n    const Submap* finished_submap, const transform::Rigid3d& pose) {\n  GrowSubmapTransformsAsNeeded(insertion_submaps);\n  const mapping::SubmapId matching_id = GetSubmapId(matching_submap);\n  const transform::Rigid3d optimized_pose =\n      optimization_problem_.submap_data()\n          .at(matching_id.trajectory_id)\n          .at(matching_id.submap_index)\n          .pose *\n      matching_submap->local_pose.inverse() * pose;\n  const mapping::NodeId node_id{\n      matching_id.trajectory_id,\n      static_cast<size_t>(matching_id.trajectory_id) <\n              optimization_problem_.node_data().size()\n          ? static_cast<int>(optimization_problem_.node_data()\n                                 .at(matching_id.trajectory_id)\n                                 .size())\n          : 0};\n  const mapping::TrajectoryNode::ConstantData* const scan_data =\n      trajectory_nodes_.at(node_id).constant_data;\n  CHECK_EQ(scan_data->trajectory_id, matching_id.trajectory_id);\n  optimization_problem_.AddTrajectoryNode(matching_id.trajectory_id,\n                                          scan_data->time, optimized_pose);\n  for (const Submap* submap : insertion_submaps) {\n    const mapping::SubmapId submap_id = GetSubmapId(submap);\n    CHECK(!submap_states_.at(submap_id).finished);\n    submap_states_.at(submap_id).node_ids.emplace(node_id);\n    const transform::Rigid3d constraint_transform =\n        submap->local_pose.inverse() * pose;\n    constraints_.push_back(\n        Constraint{submap_id,\n                   node_id,\n                   {constraint_transform, options_.matcher_translation_weight(),\n                    options_.matcher_rotation_weight()},\n                   Constraint::INTRA_SUBMAP});\n  }\n\n  for (int trajectory_id = 0; trajectory_id < submap_states_.num_trajectories();\n       ++trajectory_id) {\n    for (int submap_index = 0;\n         submap_index < submap_states_.num_indices(trajectory_id);\n         ++submap_index) {\n      const mapping::SubmapId submap_id{trajectory_id, submap_index};\n      if (submap_states_.at(submap_id).finished) {\n        CHECK_EQ(submap_states_.at(submap_id).node_ids.count(node_id), 0);\n        ComputeConstraint(node_id, submap_id);\n      }\n    }\n  }\n\n  if (finished_submap != nullptr) {\n    const mapping::SubmapId finished_submap_id = GetSubmapId(finished_submap);\n    SubmapState& finished_submap_state = submap_states_.at(finished_submap_id);\n    CHECK(!finished_submap_state.finished);\n    // We have a new completed submap, so we look into adding constraints for\n    // old scans.\n    ComputeConstraintsForOldScans(finished_submap);\n    finished_submap_state.finished = true;\n  }\n  constraint_builder_.NotifyEndOfScan();\n  ++num_scans_since_last_loop_closure_;\n  if (options_.optimize_every_n_scans() > 0 &&\n      num_scans_since_last_loop_closure_ > options_.optimize_every_n_scans()) {\n    CHECK(!run_loop_closure_);\n    run_loop_closure_ = true;\n    // If there is a 'scan_queue_' already, some other thread will take care.\n    if (scan_queue_ == nullptr) {\n      scan_queue_ = common::make_unique<std::deque<std::function<void()>>>();\n      HandleScanQueue();\n    }\n  }\n}\n\nvoid SparsePoseGraph::HandleScanQueue() {\n  constraint_builder_.WhenDone(\n      [this](const sparse_pose_graph::ConstraintBuilder::Result& result) {\n        {\n          common::MutexLocker locker(&mutex_);\n          constraints_.insert(constraints_.end(), result.begin(), result.end());\n        }\n        RunOptimization();\n\n        common::MutexLocker locker(&mutex_);\n        num_scans_since_last_loop_closure_ = 0;\n        run_loop_closure_ = false;\n        while (!run_loop_closure_) {\n          if (scan_queue_->empty()) {\n            LOG(INFO) << \"We caught up. Hooray!\";\n            scan_queue_.reset();\n            return;\n          }\n          scan_queue_->front()();\n          scan_queue_->pop_front();\n        }\n        // We have to optimize again.\n        HandleScanQueue();\n      });\n}\n\nvoid SparsePoseGraph::WaitForAllComputations() {\n  bool notification = false;\n  common::MutexLocker locker(&mutex_);\n  const int num_finished_scans_at_start =\n      constraint_builder_.GetNumFinishedScans();\n  while (!locker.AwaitWithTimeout(\n      [this]() REQUIRES(mutex_) {\n        return constraint_builder_.GetNumFinishedScans() ==\n               num_trajectory_nodes_;\n      },\n      common::FromSeconds(1.))) {\n    std::ostringstream progress_info;\n    progress_info << \"Optimizing: \" << std::fixed << std::setprecision(1)\n                  << 100. *\n                         (constraint_builder_.GetNumFinishedScans() -\n                          num_finished_scans_at_start) /\n                         (num_trajectory_nodes_ - num_finished_scans_at_start)\n                  << \"%...\";\n    std::cout << \"\\r\\x1b[K\" << progress_info.str() << std::flush;\n  }\n  std::cout << \"\\r\\x1b[KOptimizing: Done.     \" << std::endl;\n  constraint_builder_.WhenDone(\n      [this, &notification](\n          const sparse_pose_graph::ConstraintBuilder::Result& result) {\n        common::MutexLocker locker(&mutex_);\n        constraints_.insert(constraints_.end(), result.begin(), result.end());\n        notification = true;\n      });\n  locker.Await([&notification]() { return notification; });\n}\n\nvoid SparsePoseGraph::RunFinalOptimization() {\n  WaitForAllComputations();\n  optimization_problem_.SetMaxNumIterations(\n      options_.max_num_final_iterations());\n  RunOptimization();\n  optimization_problem_.SetMaxNumIterations(\n      options_.optimization_problem_options()\n          .ceres_solver_options()\n          .max_num_iterations());\n}\n\nvoid SparsePoseGraph::RunOptimization() {\n  if (optimization_problem_.submap_data().empty()) {\n    return;\n  }\n  optimization_problem_.Solve(constraints_);\n  common::MutexLocker locker(&mutex_);\n\n  const auto& node_data = optimization_problem_.node_data();\n  for (int trajectory_id = 0;\n       trajectory_id != static_cast<int>(node_data.size()); ++trajectory_id) {\n    int node_index = 0;\n    const int num_nodes = trajectory_nodes_.num_indices(trajectory_id);\n    for (; node_index != static_cast<int>(node_data[trajectory_id].size());\n         ++node_index) {\n      const mapping::NodeId node_id{trajectory_id, node_index};\n      trajectory_nodes_.at(node_id).pose =\n          node_data[trajectory_id][node_index].point_cloud_pose;\n    }\n    // Extrapolate all point cloud poses that were added later.\n    const auto new_submap_transforms = ExtrapolateSubmapTransforms(\n        optimization_problem_.submap_data(), trajectory_id);\n    const auto old_submap_transforms = ExtrapolateSubmapTransforms(\n        optimized_submap_transforms_, trajectory_id);\n    CHECK_EQ(new_submap_transforms.size(), old_submap_transforms.size());\n    const transform::Rigid3d extrapolation_transform =\n        new_submap_transforms.back() * old_submap_transforms.back().inverse();\n    for (; node_index < num_nodes; ++node_index) {\n      const mapping::NodeId node_id{trajectory_id, node_index};\n      trajectory_nodes_.at(node_id).pose =\n          extrapolation_transform * trajectory_nodes_.at(node_id).pose;\n    }\n  }\n  optimized_submap_transforms_ = optimization_problem_.submap_data();\n  connected_components_ = trajectory_connectivity_.ConnectedComponents();\n  reverse_connected_components_.clear();\n  for (size_t i = 0; i != connected_components_.size(); ++i) {\n    for (const int trajectory_id : connected_components_[i]) {\n      reverse_connected_components_.emplace(trajectory_id, i);\n    }\n  }\n}\n\nstd::vector<std::vector<mapping::TrajectoryNode>>\nSparsePoseGraph::GetTrajectoryNodes() {\n  common::MutexLocker locker(&mutex_);\n  return trajectory_nodes_.data();\n}\n\nstd::vector<SparsePoseGraph::Constraint> SparsePoseGraph::constraints() {\n  common::MutexLocker locker(&mutex_);\n  return constraints_;\n}\n\ntransform::Rigid3d SparsePoseGraph::GetLocalToGlobalTransform(\n    const int trajectory_id) {\n  common::MutexLocker locker(&mutex_);\n  if (submap_states_.num_trajectories() <= trajectory_id ||\n      submap_states_.num_indices(trajectory_id) == 0) {\n    return transform::Rigid3d::Identity();\n  }\n  const auto extrapolated_submap_transforms =\n      ExtrapolateSubmapTransforms(optimized_submap_transforms_, trajectory_id);\n  return extrapolated_submap_transforms.back() *\n         submap_states_\n             .at(mapping::SubmapId{\n                 trajectory_id,\n                 static_cast<int>(extrapolated_submap_transforms.size()) - 1})\n             .submap->local_pose.inverse();\n}\n\nstd::vector<std::vector<int>> SparsePoseGraph::GetConnectedTrajectories() {\n  common::MutexLocker locker(&mutex_);\n  return connected_components_;\n}\n\nstd::vector<transform::Rigid3d> SparsePoseGraph::GetSubmapTransforms(\n    const int trajectory_id) {\n  common::MutexLocker locker(&mutex_);\n  return ExtrapolateSubmapTransforms(optimized_submap_transforms_,\n                                     trajectory_id);\n}\n\nstd::vector<transform::Rigid3d> SparsePoseGraph::ExtrapolateSubmapTransforms(\n    const std::vector<std::vector<sparse_pose_graph::SubmapData>>&\n        submap_transforms,\n    const int trajectory_id) const {\n  if (trajectory_id >= submap_states_.num_trajectories()) {\n    return {transform::Rigid3d::Identity()};\n  }\n\n  // Submaps for which we have optimized poses.\n  std::vector<transform::Rigid3d> result;\n  for (int submap_index = 0;\n       submap_index != submap_states_.num_indices(trajectory_id);\n       ++submap_index) {\n    const mapping::SubmapId submap_id{trajectory_id, submap_index};\n    const auto& state = submap_states_.at(submap_id);\n    if (static_cast<size_t>(trajectory_id) < submap_transforms.size() &&\n        result.size() < submap_transforms.at(trajectory_id).size()) {\n      // Submaps for which we have optimized poses.\n      result.push_back(\n          submap_transforms.at(trajectory_id).at(result.size()).pose);\n    } else if (result.empty()) {\n      result.push_back(transform::Rigid3d::Identity());\n    } else {\n      // Extrapolate to the remaining submaps. Accessing 'local_pose' in Submaps\n      // is okay, since the member is const.\n      const mapping::SubmapId previous_submap_id{\n          trajectory_id, static_cast<int>(result.size()) - 1};\n      result.push_back(\n          result.back() *\n          submap_states_.at(previous_submap_id).submap->local_pose.inverse() *\n          state.submap->local_pose);\n    }\n  }\n\n  if (result.empty()) {\n    result.push_back(transform::Rigid3d::Identity());\n  }\n  return result;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/sparse_pose_graph.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_H_\n#define CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_H_\n\n#include <deque>\n#include <functional>\n#include <limits>\n#include <map>\n#include <unordered_map>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/fixed_ratio_sampler.h\"\n#include \"cartographer/common/mutex.h\"\n#include \"cartographer/common/thread_pool.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/sparse_pose_graph.h\"\n#include \"cartographer/mapping/trajectory_connectivity.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph/constraint_builder.h\"\n#include \"cartographer/mapping_3d/sparse_pose_graph/optimization_problem.h\"\n#include \"cartographer/mapping_3d/submaps.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Implements the loop closure method called Sparse Pose Adjustment (SPA) from\n// Konolige, Kurt, et al. \"Efficient sparse pose adjustment for 2d mapping.\"\n// Intelligent Robots and Systems (IROS), 2010 IEEE/RSJ International Conference\n// on (pp. 22--29). IEEE, 2010.\n//\n// It is extended for submapping in 3D:\n// Each scan has been matched against one or more submaps (adding a constraint\n// for each match), both poses of scans and of submaps are to be optimized.\n// All constraints are between a submap i and a scan j.\nclass SparsePoseGraph : public mapping::SparsePoseGraph {\n public:\n  SparsePoseGraph(const mapping::proto::SparsePoseGraphOptions& options,\n                  common::ThreadPool* thread_pool);\n  ~SparsePoseGraph() override;\n\n  SparsePoseGraph(const SparsePoseGraph&) = delete;\n  SparsePoseGraph& operator=(const SparsePoseGraph&) = delete;\n\n  // Adds a new 'range_data_in_tracking' observation at 'time', and a 'pose'\n  // that will later be optimized. The 'pose' was determined by scan matching\n  // against the 'matching_submap' and the scan was inserted into the\n  // 'insertion_submaps'.\n  void AddScan(common::Time time,\n               const sensor::RangeData& range_data_in_tracking,\n               const transform::Rigid3d& pose, int trajectory_id,\n               const Submap* matching_submap,\n               const std::vector<const Submap*>& insertion_submaps)\n      EXCLUDES(mutex_);\n\n  // Adds new IMU data to be used in the optimization.\n  void AddImuData(int trajectory_id, common::Time time,\n                  const Eigen::Vector3d& linear_acceleration,\n                  const Eigen::Vector3d& angular_velocity);\n\n  void RunFinalOptimization() override;\n  std::vector<std::vector<int>> GetConnectedTrajectories() override;\n  std::vector<transform::Rigid3d> GetSubmapTransforms(int trajectory_id)\n      EXCLUDES(mutex_) override;\n  transform::Rigid3d GetLocalToGlobalTransform(int trajectory_id)\n      EXCLUDES(mutex_) override;\n  std::vector<std::vector<mapping::TrajectoryNode>> GetTrajectoryNodes()\n      override EXCLUDES(mutex_);\n  std::vector<Constraint> constraints() override EXCLUDES(mutex_);\n\n private:\n  struct SubmapState {\n    const Submap* submap = nullptr;\n\n    // IDs of the scans that were inserted into this map together with\n    // constraints for them. They are not to be matched again when this submap\n    // becomes 'finished'.\n    std::set<mapping::NodeId> node_ids;\n\n    // Whether in the current state of the background thread this submap is\n    // finished. When this transitions to true, all scans are tried to match\n    // against this submap. Likewise, all new scans are matched against submaps\n    // which are finished.\n    bool finished = false;\n  };\n\n  // Handles a new work item.\n  void AddWorkItem(std::function<void()> work_item) REQUIRES(mutex_);\n\n  mapping::SubmapId GetSubmapId(const mapping::Submap* submap) const\n      REQUIRES(mutex_) {\n    const auto iterator = submap_ids_.find(submap);\n    CHECK(iterator != submap_ids_.end());\n    return iterator->second;\n  }\n\n  // Grows the optimization problem to have an entry for every element of\n  // 'insertion_submaps'.\n  void GrowSubmapTransformsAsNeeded(\n      const std::vector<const Submap*>& insertion_submaps) REQUIRES(mutex_);\n\n  // Adds constraints for a scan, and starts scan matching in the background.\n  void ComputeConstraintsForScan(const Submap* matching_submap,\n                                 std::vector<const Submap*> insertion_submaps,\n                                 const Submap* finished_submap,\n                                 const transform::Rigid3d& pose)\n      REQUIRES(mutex_);\n\n  // Computes constraints for a scan and submap pair.\n  void ComputeConstraint(const mapping::NodeId& node_id,\n                         const mapping::SubmapId& submap_id) REQUIRES(mutex_);\n\n  // Adds constraints for older scans whenever a new submap is finished.\n  void ComputeConstraintsForOldScans(const Submap* submap) REQUIRES(mutex_);\n\n  // Registers the callback to run the optimization once all constraints have\n  // been computed, that will also do all work that queue up in 'scan_queue_'.\n  void HandleScanQueue() REQUIRES(mutex_);\n\n  // Waits until we caught up (i.e. nothing is waiting to be scheduled), and\n  // all computations have finished.\n  void WaitForAllComputations() EXCLUDES(mutex_);\n\n  // Runs the optimization. Callers have to make sure, that there is only one\n  // optimization being run at a time.\n  void RunOptimization() EXCLUDES(mutex_);\n\n  // Adds extrapolated transforms, so that there are transforms for all submaps.\n  std::vector<transform::Rigid3d> ExtrapolateSubmapTransforms(\n      const std::vector<std::vector<sparse_pose_graph::SubmapData>>&\n          submap_transforms,\n      int trajectory_id) const REQUIRES(mutex_);\n\n  const mapping::proto::SparsePoseGraphOptions options_;\n  common::Mutex mutex_;\n\n  // If it exists, further scans must be added to this queue, and will be\n  // considered later.\n  std::unique_ptr<std::deque<std::function<void()>>> scan_queue_\n      GUARDED_BY(mutex_);\n\n  // How our various trajectories are related.\n  mapping::TrajectoryConnectivity trajectory_connectivity_ GUARDED_BY(mutex_);\n\n  // We globally localize a fraction of the scans from each trajectory.\n  std::unordered_map<int, std::unique_ptr<common::FixedRatioSampler>>\n      global_localization_samplers_ GUARDED_BY(mutex_);\n\n  // Number of scans added since last loop closure.\n  int num_scans_since_last_loop_closure_ GUARDED_BY(mutex_) = 0;\n\n  // Whether the optimization has to be run before more data is added.\n  bool run_loop_closure_ GUARDED_BY(mutex_) = false;\n\n  // Current optimization problem.\n  sparse_pose_graph::OptimizationProblem optimization_problem_;\n  sparse_pose_graph::ConstraintBuilder constraint_builder_ GUARDED_BY(mutex_);\n  std::vector<Constraint> constraints_ GUARDED_BY(mutex_);\n\n  // Submaps get assigned an ID and state as soon as they are seen, even\n  // before they take part in the background computations.\n  std::map<const mapping::Submap*, mapping::SubmapId> submap_ids_\n      GUARDED_BY(mutex_);\n  mapping::NestedVectorsById<SubmapState, mapping::SubmapId> submap_states_\n      GUARDED_BY(mutex_);\n\n  // Connectivity structure of our trajectories by IDs.\n  std::vector<std::vector<int>> connected_components_;\n  // Trajectory ID to connected component ID.\n  std::map<int, size_t> reverse_connected_components_;\n\n  // Data that are currently being shown.\n  //\n  // Deque to keep references valid for the background computation when adding\n  // new data.\n  std::deque<mapping::TrajectoryNode::ConstantData> constant_node_data_;\n  mapping::NestedVectorsById<mapping::TrajectoryNode, mapping::NodeId>\n      trajectory_nodes_ GUARDED_BY(mutex_);\n  int num_trajectory_nodes_ GUARDED_BY(mutex_) = 0;\n\n  // Current submap transforms used for displaying data.\n  std::vector<std::vector<sparse_pose_graph::SubmapData>>\n      optimized_submap_transforms_ GUARDED_BY(mutex_);\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SPARSE_POSE_GRAPH_H_\n"
  },
  {
    "path": "mapping_3d/submaps.cc",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cartographer/mapping_3d/submaps.h\"\n\n#include <cmath>\n#include <limits>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nnamespace {\n\nconstexpr float kSliceHalfHeight = 0.1f;\n\nstruct RaySegment {\n  Eigen::Vector2f from;\n  Eigen::Vector2f to;\n  bool hit;  // Whether there is a hit at 'to'.\n};\n\n// We compute a slice around the xy-plane. 'transform' is applied to the rays in\n// global map frame to allow choosing an arbitrary slice.\nvoid GenerateSegmentForSlice(const sensor::RangeData& range_data,\n                             const transform::Rigid3f& pose,\n                             const transform::Rigid3f& transform,\n                             std::vector<RaySegment>* segments) {\n  const sensor::RangeData transformed_range_data =\n      sensor::TransformRangeData(range_data, transform * pose);\n  segments->reserve(transformed_range_data.returns.size());\n  for (const Eigen::Vector3f& hit : transformed_range_data.returns) {\n    const Eigen::Vector2f origin_xy = transformed_range_data.origin.head<2>();\n    const float origin_z = transformed_range_data.origin.z();\n    const float delta_z = hit.z() - origin_z;\n    const Eigen::Vector2f delta_xy = hit.head<2>() - origin_xy;\n    if (origin_z < -kSliceHalfHeight) {\n      // Ray originates below the slice.\n      if (hit.z() > kSliceHalfHeight) {\n        // Ray is cutting through the slice.\n        segments->push_back(RaySegment{\n            origin_xy + (-kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            origin_xy + (kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            false});\n      } else if (hit.z() > -kSliceHalfHeight) {\n        // Hit is inside the slice.\n        segments->push_back(RaySegment{\n            origin_xy + (-kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            hit.head<2>(), true});\n      }\n    } else if (origin_z < kSliceHalfHeight) {\n      // Ray originates inside the slice.\n      if (hit.z() < -kSliceHalfHeight) {\n        // Hit is below.\n        segments->push_back(RaySegment{\n            origin_xy,\n            origin_xy + (-kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            false});\n      } else if (hit.z() < kSliceHalfHeight) {\n        // Full ray is inside the slice.\n        segments->push_back(RaySegment{origin_xy, hit.head<2>(), true});\n      } else {\n        // Hit is above.\n        segments->push_back(RaySegment{\n            origin_xy,\n            origin_xy + (kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            false});\n      }\n    } else {\n      // Ray originates above the slice.\n      if (hit.z() < -kSliceHalfHeight) {\n        // Ray is cutting through the slice.\n        segments->push_back(RaySegment{\n            origin_xy + (kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            origin_xy + (-kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            false});\n      } else if (hit.z() < kSliceHalfHeight) {\n        // Hit is inside the slice.\n        segments->push_back(RaySegment{\n            origin_xy + (kSliceHalfHeight - origin_z) / delta_z * delta_xy,\n            hit.head<2>(), true});\n      }\n    }\n  }\n}\n\nvoid UpdateFreeSpaceFromSegment(const RaySegment& segment,\n                                const std::vector<uint16>& miss_table,\n                                mapping_2d::ProbabilityGrid* result) {\n  Eigen::Array2i from = result->limits().GetXYIndexOfCellContainingPoint(\n      segment.from.x(), segment.from.y());\n  Eigen::Array2i to = result->limits().GetXYIndexOfCellContainingPoint(\n      segment.to.x(), segment.to.y());\n  bool large_delta_y =\n      std::abs(to.y() - from.y()) > std::abs(to.x() - from.x());\n  if (large_delta_y) {\n    std::swap(from.x(), from.y());\n    std::swap(to.x(), to.y());\n  }\n  if (from.x() > to.x()) {\n    std::swap(from, to);\n  }\n  const int dx = to.x() - from.x();\n  const int dy = std::abs(to.y() - from.y());\n  int error = dx / 2;\n  const int direction = (from.y() < to.y()) ? 1 : -1;\n\n  for (; from.x() < to.x(); ++from.x()) {\n    if (large_delta_y) {\n      result->ApplyLookupTable(Eigen::Array2i(from.y(), from.x()), miss_table);\n    } else {\n      result->ApplyLookupTable(from, miss_table);\n    }\n    error -= dy;\n    if (error < 0) {\n      from.y() += direction;\n      error += dx;\n    }\n  }\n}\n\nvoid InsertSegmentsIntoProbabilityGrid(const std::vector<RaySegment>& segments,\n                                       const std::vector<uint16>& hit_table,\n                                       const std::vector<uint16>& miss_table,\n                                       mapping_2d::ProbabilityGrid* result) {\n  result->StartUpdate();\n  if (segments.empty()) {\n    return;\n  }\n  Eigen::Vector2f min = segments.front().from;\n  Eigen::Vector2f max = min;\n  for (const RaySegment& segment : segments) {\n    min = min.cwiseMin(segment.from);\n    min = min.cwiseMin(segment.to);\n    max = max.cwiseMax(segment.from);\n    max = max.cwiseMax(segment.to);\n  }\n  const float padding = 10. * result->limits().resolution();\n  max += Eigen::Vector2f(padding, padding);\n  min -= Eigen::Vector2f(padding, padding);\n  result->GrowLimits(min.x(), min.y());\n  result->GrowLimits(max.x(), max.y());\n\n  for (const RaySegment& segment : segments) {\n    if (segment.hit) {\n      result->ApplyLookupTable(result->limits().GetXYIndexOfCellContainingPoint(\n                                   segment.to.x(), segment.to.y()),\n                               hit_table);\n    }\n  }\n  for (const RaySegment& segment : segments) {\n    UpdateFreeSpaceFromSegment(segment, miss_table, result);\n  }\n}\n\n// Filters 'range_data', retaining only the returns that have no more than\n// 'max_range' distance from the origin. Removes misses and reflectivity\n// information.\nsensor::RangeData FilterRangeDataByMaxRange(const sensor::RangeData& range_data,\n                                            const float max_range) {\n  sensor::RangeData result{range_data.origin, {}, {}};\n  for (const Eigen::Vector3f& hit : range_data.returns) {\n    if ((hit - range_data.origin).norm() <= max_range) {\n      result.returns.push_back(hit);\n    }\n  }\n  return result;\n}\n\n}  // namespace\n\nvoid InsertIntoProbabilityGrid(\n    const sensor::RangeData& range_data, const transform::Rigid3f& pose,\n    const float slice_z,\n    const mapping_2d::RangeDataInserter& range_data_inserter,\n    mapping_2d::ProbabilityGrid* result) {\n  std::vector<RaySegment> segments;\n  GenerateSegmentForSlice(\n      range_data, pose,\n      transform::Rigid3f::Translation(-slice_z * Eigen::Vector3f::UnitZ()),\n      &segments);\n  InsertSegmentsIntoProbabilityGrid(segments, range_data_inserter.hit_table(),\n                                    range_data_inserter.miss_table(), result);\n}\n\nproto::SubmapsOptions CreateSubmapsOptions(\n    common::LuaParameterDictionary* parameter_dictionary) {\n  proto::SubmapsOptions options;\n  options.set_high_resolution(\n      parameter_dictionary->GetDouble(\"high_resolution\"));\n  options.set_high_resolution_max_range(\n      parameter_dictionary->GetDouble(\"high_resolution_max_range\"));\n  options.set_low_resolution(parameter_dictionary->GetDouble(\"low_resolution\"));\n  options.set_num_range_data(\n      parameter_dictionary->GetNonNegativeInt(\"num_range_data\"));\n  *options.mutable_range_data_inserter_options() =\n      CreateRangeDataInserterOptions(\n          parameter_dictionary->GetDictionary(\"range_data_inserter\").get());\n  CHECK_GT(options.num_range_data(), 0);\n  return options;\n}\n\nSubmap::Submap(const float high_resolution, const float low_resolution,\n               const transform::Rigid3d& local_pose)\n    : mapping::Submap(local_pose),\n      high_resolution_hybrid_grid(high_resolution),\n      low_resolution_hybrid_grid(low_resolution) {}\n\nSubmaps::Submaps(const proto::SubmapsOptions& options)\n    : options_(options),\n      range_data_inserter_(options.range_data_inserter_options()) {\n  // We always want to have at least one submap which we can return and will\n  // create it at the origin in absence of a better choice.\n  //\n  // TODO(whess): Start with no submaps, so that all of them can be\n  // approximately gravity aligned.\n  AddSubmap(transform::Rigid3d::Identity());\n}\n\nconst Submap* Submaps::Get(int index) const {\n  CHECK_GE(index, 0);\n  CHECK_LT(index, size());\n  return submaps_[index].get();\n}\n\nint Submaps::size() const { return submaps_.size(); }\n\nvoid Submaps::SubmapToProto(\n    int index, const transform::Rigid3d& global_submap_pose,\n    mapping::proto::SubmapQuery::Response* const response) const {\n  // Generate an X-ray view through the 'hybrid_grid', aligned to the xy-plane\n  // in the global map frame.\n  const HybridGrid& hybrid_grid = Get(index)->high_resolution_hybrid_grid;\n  response->set_resolution(hybrid_grid.resolution());\n\n  // Compute a bounding box for the texture.\n  Eigen::Array2i min_index(INT_MAX, INT_MAX);\n  Eigen::Array2i max_index(INT_MIN, INT_MIN);\n  const std::vector<Eigen::Array4i> voxel_indices_and_probabilities =\n      ExtractVoxelData(hybrid_grid, global_submap_pose.cast<float>(),\n                       &min_index, &max_index);\n\n  const int width = max_index.y() - min_index.y() + 1;\n  const int height = max_index.x() - min_index.x() + 1;\n  response->set_width(width);\n  response->set_height(height);\n\n  const std::vector<PixelData> accumulated_pixel_data = AccumulatePixelData(\n      width, height, min_index, max_index, voxel_indices_and_probabilities);\n  const string cell_data = ComputePixelValues(accumulated_pixel_data);\n\n  common::FastGzipString(cell_data, response->mutable_cells());\n  *response->mutable_slice_pose() =\n      transform::ToProto(global_submap_pose.inverse() *\n                         transform::Rigid3d::Translation(Eigen::Vector3d(\n                             max_index.x() * hybrid_grid.resolution(),\n                             max_index.y() * hybrid_grid.resolution(),\n                             global_submap_pose.translation().z())));\n}\n\nvoid Submaps::InsertRangeData(const sensor::RangeData& range_data,\n                              const Eigen::Quaterniond& gravity_alignment) {\n  for (const int index : insertion_indices()) {\n    Submap* submap = submaps_[index].get();\n    const sensor::RangeData transformed_range_data = sensor::TransformRangeData(\n        range_data, submap->local_pose.inverse().cast<float>());\n    range_data_inserter_.Insert(\n        FilterRangeDataByMaxRange(transformed_range_data,\n                                  options_.high_resolution_max_range()),\n        &submap->high_resolution_hybrid_grid);\n    range_data_inserter_.Insert(transformed_range_data,\n                                &submap->low_resolution_hybrid_grid);\n    ++submap->num_range_data;\n  }\n  const Submap* const last_submap = Get(size() - 1);\n  if (last_submap->num_range_data == options_.num_range_data()) {\n    AddSubmap(transform::Rigid3d(range_data.origin.cast<double>(),\n                                 gravity_alignment));\n  }\n}\n\nvoid Submaps::AddSubmap(const transform::Rigid3d& local_pose) {\n  if (size() > 1) {\n    Submap* submap = submaps_[size() - 2].get();\n    CHECK(!submap->finished);\n    submap->finished = true;\n  }\n  submaps_.emplace_back(new Submap(options_.high_resolution(),\n                                   options_.low_resolution(), local_pose));\n  LOG(INFO) << \"Added submap \" << size();\n}\n\nstd::vector<Submaps::PixelData> Submaps::AccumulatePixelData(\n    const int width, const int height, const Eigen::Array2i& min_index,\n    const Eigen::Array2i& max_index,\n    const std::vector<Eigen::Array4i>& voxel_indices_and_probabilities) const {\n  std::vector<PixelData> accumulated_pixel_data(width * height);\n  for (const Eigen::Array4i& voxel_index_and_probability :\n       voxel_indices_and_probabilities) {\n    const Eigen::Array2i pixel_index = voxel_index_and_probability.head<2>();\n    if ((pixel_index < min_index).any() || (pixel_index > max_index).any()) {\n      // Out of bounds. This could happen because of floating point inaccuracy.\n      continue;\n    }\n    const int x = max_index.x() - pixel_index[0];\n    const int y = max_index.y() - pixel_index[1];\n    PixelData& pixel = accumulated_pixel_data[x * width + y];\n    ++pixel.count;\n    pixel.min_z = std::min(pixel.min_z, voxel_index_and_probability[2]);\n    pixel.max_z = std::max(pixel.max_z, voxel_index_and_probability[2]);\n    const float probability =\n        mapping::ValueToProbability(voxel_index_and_probability[3]);\n    pixel.probability_sum += probability;\n    pixel.max_probability = std::max(pixel.max_probability, probability);\n  }\n  return accumulated_pixel_data;\n}\n\nstd::vector<Eigen::Array4i> Submaps::ExtractVoxelData(\n    const HybridGrid& hybrid_grid, const transform::Rigid3f& transform,\n    Eigen::Array2i* min_index, Eigen::Array2i* max_index) const {\n  std::vector<Eigen::Array4i> voxel_indices_and_probabilities;\n  const float resolution_inverse = 1. / hybrid_grid.resolution();\n\n  constexpr double kXrayObstructedCellProbabilityLimit = 0.501;\n  for (auto it = HybridGrid::Iterator(hybrid_grid); !it.Done(); it.Next()) {\n    const uint16 probability_value = it.GetValue();\n    const float probability = mapping::ValueToProbability(probability_value);\n    if (probability < kXrayObstructedCellProbabilityLimit) {\n      // We ignore non-obstructed cells.\n      continue;\n    }\n\n    const Eigen::Vector3f cell_center_submap =\n        hybrid_grid.GetCenterOfCell(it.GetCellIndex());\n    const Eigen::Vector3f cell_center_global = transform * cell_center_submap;\n    const Eigen::Array4i voxel_index_and_probability(\n        common::RoundToInt(cell_center_global.x() * resolution_inverse),\n        common::RoundToInt(cell_center_global.y() * resolution_inverse),\n        common::RoundToInt(cell_center_global.z() * resolution_inverse),\n        probability_value);\n\n    voxel_indices_and_probabilities.push_back(voxel_index_and_probability);\n    const Eigen::Array2i pixel_index = voxel_index_and_probability.head<2>();\n    *min_index = min_index->cwiseMin(pixel_index);\n    *max_index = max_index->cwiseMax(pixel_index);\n  }\n  return voxel_indices_and_probabilities;\n}\n\nstring Submaps::ComputePixelValues(\n    const std::vector<Submaps::PixelData>& accumulated_pixel_data) const {\n  string cell_data;\n  cell_data.reserve(2 * accumulated_pixel_data.size());\n  constexpr float kMinZDifference = 3.f;\n  constexpr float kFreeSpaceWeight = 0.15f;\n  for (const PixelData& pixel : accumulated_pixel_data) {\n    // TODO(whess): Take into account submap rotation.\n    // TODO(whess): Document the approach and make it more independent from the\n    // chosen resolution.\n    const float z_difference = pixel.count > 0 ? pixel.max_z - pixel.min_z : 0;\n    if (z_difference < kMinZDifference) {\n      cell_data.push_back(0);  // value\n      cell_data.push_back(0);  // alpha\n      continue;\n    }\n    const float free_space = std::max(z_difference - pixel.count, 0.f);\n    const float free_space_weight = kFreeSpaceWeight * free_space;\n    const float total_weight = pixel.count + free_space_weight;\n    const float free_space_probability = 1.f - pixel.max_probability;\n    const float average_probability = mapping::ClampProbability(\n        (pixel.probability_sum + free_space_probability * free_space_weight) /\n        total_weight);\n    const int delta =\n        128 - mapping::ProbabilityToLogOddsInteger(average_probability);\n    const uint8 alpha = delta > 0 ? 0 : -delta;\n    const uint8 value = delta > 0 ? delta : 0;\n    cell_data.push_back(value);                         // value\n    cell_data.push_back((value || alpha) ? alpha : 1);  // alpha\n  }\n  return cell_data;\n}\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n"
  },
  {
    "path": "mapping_3d/submaps.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_SUBMAPS_H_\n#define CARTOGRAPHER_MAPPING_3D_SUBMAPS_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/mapping/id.h\"\n#include \"cartographer/mapping/proto/submap_visualization.pb.h\"\n#include \"cartographer/mapping/submaps.h\"\n#include \"cartographer/mapping_2d/probability_grid.h\"\n#include \"cartographer/mapping_2d/range_data_inserter.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/mapping_3d/proto/submaps_options.pb.h\"\n#include \"cartographer/mapping_3d/range_data_inserter.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\nvoid InsertIntoProbabilityGrid(\n    const sensor::RangeData& range_data, const transform::Rigid3f& pose,\n    const float slice_z,\n    const mapping_2d::RangeDataInserter& range_data_inserter,\n    mapping_2d::ProbabilityGrid* result);\n\nproto::SubmapsOptions CreateSubmapsOptions(\n    common::LuaParameterDictionary* parameter_dictionary);\n\nstruct Submap : public mapping::Submap {\n  Submap(float high_resolution, float low_resolution,\n         const transform::Rigid3d& local_pose);\n\n  HybridGrid high_resolution_hybrid_grid;\n  HybridGrid low_resolution_hybrid_grid;\n  bool finished = false;\n};\n\n// A container of Submaps.\nclass Submaps : public mapping::Submaps {\n public:\n  explicit Submaps(const proto::SubmapsOptions& options);\n\n  Submaps(const Submaps&) = delete;\n  Submaps& operator=(const Submaps&) = delete;\n\n  const Submap* Get(int index) const override;\n  int size() const override;\n  void SubmapToProto(\n      int index, const transform::Rigid3d& global_submap_pose,\n      mapping::proto::SubmapQuery::Response* response) const override;\n\n  // Inserts 'range_data' into the Submap collection. 'gravity_alignment' is\n  // used for the orientation of new submaps so that the z axis approximately\n  // aligns with gravity.\n  void InsertRangeData(const sensor::RangeData& range_data,\n                       const Eigen::Quaterniond& gravity_alignment);\n\n private:\n  struct PixelData {\n    int min_z = INT_MAX;\n    int max_z = INT_MIN;\n    int count = 0;\n    float probability_sum = 0.f;\n    float max_probability = 0.5f;\n  };\n\n  void AddSubmap(const transform::Rigid3d& local_pose);\n\n  std::vector<PixelData> AccumulatePixelData(\n      const int width, const int height, const Eigen::Array2i& min_index,\n      const Eigen::Array2i& max_index,\n      const std::vector<Eigen::Array4i>& voxel_indices_and_probabilities) const;\n  // The first three entries of each returned value are a cell_index and the\n  // last is the corresponding probability value. We batch them together like\n  // this to only have one vector and have better cache locality.\n  std::vector<Eigen::Array4i> ExtractVoxelData(\n      const HybridGrid& hybrid_grid, const transform::Rigid3f& transform,\n      Eigen::Array2i* min_index, Eigen::Array2i* max_index) const;\n  // Builds texture data containing interleaved value and alpha for the\n  // visualization from 'accumulated_pixel_data'.\n  string ComputePixelValues(\n      const std::vector<PixelData>& accumulated_pixel_data) const;\n\n  const proto::SubmapsOptions options_;\n\n  // 'submaps_' contains pointers, so that resizing the vector does not\n  // invalidate handed out Submap* pointers.\n  std::vector<std::unique_ptr<Submap>> submaps_;\n  RangeDataInserter range_data_inserter_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_SUBMAPS_H_\n"
  },
  {
    "path": "mapping_3d/translation_cost_function.h",
    "content": "/*\n * Copyright 2016 The Cartographer Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CARTOGRAPHER_MAPPING_3D_TRANSLATION_COST_FUNCTION_H_\n#define CARTOGRAPHER_MAPPING_3D_TRANSLATION_COST_FUNCTION_H_\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n\nnamespace cartographer {\nnamespace mapping_3d {\n\n// Penalizes differences between velocity and change in position.\nclass TranslationCostFunction {\n public:\n  TranslationCostFunction(const double scaling_factor,\n                          const double delta_time_seconds)\n      : scaling_factor_(scaling_factor),\n        delta_time_seconds_(delta_time_seconds) {}\n\n  TranslationCostFunction(const TranslationCostFunction&) = delete;\n  TranslationCostFunction& operator=(const TranslationCostFunction&) = delete;\n\n  template <typename T>\n  bool operator()(const T* const start_translation,\n                  const T* const end_translation, const T* const velocity,\n                  T* residual) const {\n    const T delta_x = end_translation[0] - start_translation[0];\n    const T delta_y = end_translation[1] - start_translation[1];\n    const T delta_z = end_translation[2] - start_translation[2];\n\n    residual[0] =\n        scaling_factor_ * (delta_x - velocity[0] * delta_time_seconds_);\n    residual[1] =\n        scaling_factor_ * (delta_y - velocity[1] * delta_time_seconds_);\n    residual[2] =\n        scaling_factor_ * (delta_z - velocity[2] * delta_time_seconds_);\n    return true;\n  }\n\n private:\n  const double scaling_factor_;\n  const double delta_time_seconds_;\n};\n\n}  // namespace mapping_3d\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_3D_TRANSLATION_COST_FUNCTION_H_\n"
  },
  {
    "path": "sensor/collator.cc",
    "content": "\n\n#include \"cartographer/sensor/collator.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n/*\n一个轨迹线,多个传感器\n\n*/\n\n/*\nstruct QueueKey {\n  int trajectory_id;// 轨线id;\n  string sensor_id; //传感器id\n  }\n\n*/\nvoid Collator::AddTrajectory(\n    const int trajectory_id,\n    const std::unordered_set<string>& expected_sensor_ids,\n    const Callback callback) {\n  for (const auto& sensor_id : expected_sensor_ids) { \n    //对于每一个轨迹线+传感器,设置一个key\n    const auto queue_key = QueueKey{trajectory_id, sensor_id};\n\n    //添加一个名为key的队列,并设置回调函数处理data\n    queue_.AddQueue(queue_key,\n                    [callback, sensor_id](std::unique_ptr<Data> data) {\n                      callback(sensor_id, std::move(data));\n                    });\n\n    //map<int,vector<key>>:添加轨迹线对应的key\n    queue_keys_[trajectory_id].push_back(queue_key);\n  }\n}\n\n\n/*队列不再接收数据*/\nvoid Collator::FinishTrajectory(const int trajectory_id) {\n  for (const auto& queue_key : queue_keys_[trajectory_id]) {\n    queue_.MarkQueueAsFinished(queue_key);\n  }\n}\n\n\n/*\n主要的操作,添加传感器数据,数据形式是:key+data\n*/\nvoid Collator::AddSensorData(const int trajectory_id, const string& sensor_id,\n                             std::unique_ptr<Data> data) {\n  //找到key，再move(data)\n  queue_.Add(QueueKey{trajectory_id, sensor_id}, std::move(data));\n}\n\nvoid Collator::Flush() { queue_.Flush(); }\n\nint Collator::GetBlockingTrajectoryId() const {\n  return queue_.GetBlocker().trajectory_id;\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/collator.h",
    "content": "\n\n#ifndef CARTOGRAPHER_SENSOR_COLLATOR_H_\n#define CARTOGRAPHER_SENSOR_COLLATOR_H_\n\n#include <functional>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"cartographer/sensor/data.h\"\n#include \"cartographer/sensor/ordered_multi_queue.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n/*\nCollator,采集者，校对者，整理者\n将多传感器采集的数据归并到轨迹上。\n\nCollator类:不可拷贝,不可赋值.\n只有一个默认构造函数.\n有2个数据成员\n\n1,OrderedMultiQueue queue_; key是pair<轨迹线id,传感器id>.\n一般情况下，对于已有的bag文件，轨迹id等于0.\n\n2,std::unordered_map<int, std::vector<QueueKey>> queue_keys_\n轨迹线和队列key组成的hash表,1：N模式\n\n\n\nCollator类主要提供三个操作:\n1,AddTrajectory() 添加一个轨迹线,\n2,FinishTrajectory() 标记轨迹线已经采集完成\n3,AddSensorData()接收传感器数据\n4,Flush()刷新\n\n*/\nclass Collator {\n public:\n  using Callback = std::function<void(const string&, std::unique_ptr<Data>)>;\n\n  Collator() {}\n\n  Collator(const Collator&) = delete;\n  Collator& operator=(const Collator&) = delete;\n\n/*添加一个轨迹线,接受有序的传感器数据,并使用callback回调处理data。\n  一个轨迹线对应多个传感器数据:id ->unordered_set\n*/\n  // Adds a trajectory to produce sorted sensor output for. Calls 'callback'\n  // for each collated sensor data.\n  void AddTrajectory(int trajectory_id,\n                     const std::unordered_set<string>& expected_sensor_ids,\n                     Callback callback);\n\n  // Marks 'trajectory_id' as finished.标记轨迹线已经完成采样.\n  void FinishTrajectory(int trajectory_id);\n\n/*添加一个传感器id对应的数据data,数据必须按时间排序，*/\n  // Adds 'data' for 'trajectory_id' to be collated. 'data' must contain valid\n  // sensor data. Sensor packets with matching 'sensor_id' must be added in time\n  // order.\n  void AddSensorData(int trajectory_id, const string& sensor_id,\n                     std::unique_ptr<Data> data);\n\n\n//刷新\n  // Dispatches all queued sensor packets. May only be called once.\n  // AddSensorData may not be called after Flush.\n  void Flush();\n\n\n/*返回阻塞的轨迹id,\n此种情况多见于某一传感器持久未采集data，\n造成ordered_multi_queue阻塞。\n\n  */\n  // Must only be called if at least one unfinished trajectory exists. Returns\n  // the ID of the trajectory that needs more data before the Collator is\n  // unblocked.\n  int GetBlockingTrajectoryId() const;\n\n private:\n  //多个key构成的多队列\n  // Queue keys are a pair of trajectory ID and sensor identifier.\n  OrderedMultiQueue queue_;\n  \n  //int为传感器id,vector是id+sensor组成的QueueKey\n  \n  // Map of trajectory ID to all associated QueueKeys.\n  std::unordered_map<int, std::vector<QueueKey>> queue_keys_;\n};\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_COLLATOR_H_\n"
  },
  {
    "path": "sensor/collator_test.cc",
    "content": "\n#include \"cartographer/sensor/collator.h\"\n\n#include <array>\n#include <memory>\n\n#include \"cartographer/common/lua_parameter_dictionary_test_helpers.h\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n\nTEST(Collator, Ordering) {\n  const std::array<string, 4> kSensorId = {\n      {\"horizontal_rangefinder\", \"vertical_rangefinder\", \"imu\", \"odometry\"}};\n  Data zero(common::FromUniversal(0), Data::Rangefinder{});\n  Data first(common::FromUniversal(100), Data::Rangefinder{});\n  Data second(common::FromUniversal(200), Data::Rangefinder{});\n  Data third(common::FromUniversal(300), Data::Imu{});\n  Data fourth(common::FromUniversal(400), Data::Rangefinder{});\n  Data fifth(common::FromUniversal(500), Data::Rangefinder{});\n  Data sixth(common::FromUniversal(600), transform::Rigid3d::Identity());\n  //构造6个传感器数据\n\n  std::vector<std::pair<string, Data>> received;\n  Collator collator;\n  collator.AddTrajectory(                                              \n   //添加一个轨迹线0,多个传感器,并指定回调函数:所有的接收数据都存储在received中\n      0, \n      std::unordered_set<string>(kSensorId.begin(), kSensorId.end()),\n\n      [&received](const string& sensor_id, std::unique_ptr<Data> data) {\n        received.push_back(std::make_pair(sensor_id, *data));\n        //由传感器id和data组成一个pair\n      });\n\n  constexpr int kTrajectoryId = 0;\n\n  // Establish a common start time.\n  collator.AddSensorData(kTrajectoryId, kSensorId[0],\n                         common::make_unique<Data>(zero)); \n                         //轨迹0,传感器0产生一个数据,产生时间是0us\n\n  collator.AddSensorData(kTrajectoryId, kSensorId[1],\n                         common::make_unique<Data>(zero));\n\n  collator.AddSensorData(kTrajectoryId, kSensorId[2],\n                         common::make_unique<Data>(zero));\n\n  collator.AddSensorData(kTrajectoryId, kSensorId[3],\n                         common::make_unique<Data>(zero)); //同上\n\n  collator.AddSensorData(kTrajectoryId, kSensorId[0],\n                         common::make_unique<Data>(first));//产生数据\n  collator.AddSensorData(kTrajectoryId, kSensorId[3],\n                         common::make_unique<Data>(sixth));\n  collator.AddSensorData(kTrajectoryId, kSensorId[0],\n                         common::make_unique<Data>(fourth));\n  collator.AddSensorData(kTrajectoryId, kSensorId[1],\n                         common::make_unique<Data>(second));\n  collator.AddSensorData(kTrajectoryId, kSensorId[1],\n                         common::make_unique<Data>(fifth));\n  collator.AddSensorData(kTrajectoryId, kSensorId[2],\n                         common::make_unique<Data>(third));\n\n/*\n\nh:{0,100,400}\nv:{0,200,500}\ni:{0,300}\no:{0,600}\n*/\n  ASSERT_EQ(7, received.size()); //{0,0,0,0,100,200,300}\n  EXPECT_EQ(100, common::ToUniversal(received[4].second.time));\n  EXPECT_EQ(kSensorId[0], received[4].first);\n  EXPECT_EQ(200, common::ToUniversal(received[5].second.time));\n  EXPECT_EQ(kSensorId[1], received[5].first);\n  EXPECT_EQ(300, common::ToUniversal(received[6].second.time));\n  EXPECT_EQ(kSensorId[2], received[6].first);\n\n  collator.Flush();  //刷新\n\n  ASSERT_EQ(10, received.size());         //10个数据,所有sensor采集的数据\n  EXPECT_EQ(kSensorId[0], received[7].first);\n  EXPECT_EQ(500, common::ToUniversal(received[8].second.time));\n  EXPECT_EQ(kSensorId[1], received[8].first);\n  EXPECT_EQ(600, common::ToUniversal(received[9].second.time));\n  EXPECT_EQ(kSensorId[3], received[9].first);\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/compressed_point_cloud.cc",
    "content": "\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n\n#include <limits>\n\n#include \"cartographer/common/math.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\nnamespace {\n\n// Points are encoded on a fixed grid with a grid spacing of 'kPrecision' with\n// integers. Points are organized in blocks, where each point is encoded\n// relative to the block's origin in an int32 with 'kBitsPerCoordinate' bits per\n// coordinate.\nconstexpr float kPrecision = 0.001f;  // in meters.\nconstexpr int kBitsPerCoordinate = 10;\nconstexpr int kCoordinateMask = (1 << kBitsPerCoordinate) - 1;\nconstexpr int kMaxBitsPerDirection = 23;\n\n}  // namespace\n\nCompressedPointCloud::ConstIterator::ConstIterator(\n    const CompressedPointCloud* compressed_point_cloud)\n    : compressed_point_cloud_(compressed_point_cloud),\n      remaining_points_(compressed_point_cloud->num_points_),\n      remaining_points_in_current_block_(0),\n      input_(compressed_point_cloud->point_data_.begin()) {\n  if (remaining_points_ > 0) {\n    ReadNextPoint();\n  }\n}\n\nCompressedPointCloud::ConstIterator\nCompressedPointCloud::ConstIterator::EndIterator(\n    const CompressedPointCloud* compressed_point_cloud) {\n  ConstIterator end_iterator(compressed_point_cloud);\n  end_iterator.remaining_points_ = 0;\n  return end_iterator;\n}\n\nEigen::Vector3f CompressedPointCloud::ConstIterator::operator*() const {\n  CHECK_GT(remaining_points_, 0);\n  return current_point_;\n}\n\nCompressedPointCloud::ConstIterator& CompressedPointCloud::ConstIterator::\noperator++() {\n  --remaining_points_;\n  if (remaining_points_ > 0) {\n    ReadNextPoint();\n  }\n  return *this;\n}\n\nbool CompressedPointCloud::ConstIterator::operator!=(\n    const ConstIterator& it) const {\n  CHECK(compressed_point_cloud_ == it.compressed_point_cloud_);\n  return remaining_points_ != it.remaining_points_;\n}\n\nvoid CompressedPointCloud::ConstIterator::ReadNextPoint() {\n  if (remaining_points_in_current_block_ == 0) {\n    remaining_points_in_current_block_ = *input_++;\n    for (int i = 0; i < 3; ++i) {\n      current_block_coordinates_[i] = *input_++ << kBitsPerCoordinate;\n    }\n  }\n  --remaining_points_in_current_block_;\n  const int point = *input_++;\n  constexpr int kMask = (1 << kBitsPerCoordinate) - 1;\n  current_point_[0] =\n      (current_block_coordinates_[0] + (point & kMask)) * kPrecision;\n  current_point_[1] = (current_block_coordinates_[1] +\n                       ((point >> kBitsPerCoordinate) & kMask)) *\n                      kPrecision;\n  current_point_[2] =\n      (current_block_coordinates_[2] + (point >> (2 * kBitsPerCoordinate))) *\n      kPrecision;\n}\n\n/*\n最重要的构造函数\n压缩点云\n*/\nCompressedPointCloud::CompressedPointCloud(const PointCloud& point_cloud)\n//point_cloud是一个3f的vector,压缩到point_data_中储存\n    : num_points_(point_cloud.size()) {\n  // Distribute points into blocks.\n  struct RasterPoint {\n    Eigen::Array3i point; //  Array3i (int d1, int d2, int d3)\n    int index;\n  };\n  using Blocks = mapping_3d::HybridGridBase<std::vector<RasterPoint>>;\n  Blocks blocks(kPrecision);\n  int num_blocks = 0;\n  CHECK_LE(point_cloud.size(), std::numeric_limits<int>::max());\n  for (int point_index = 0; point_index < static_cast<int>(point_cloud.size());\n       ++point_index) {\n    const Eigen::Vector3f& point = point_cloud[point_index];//获取某个point{x,y,z}\n    CHECK_LT(point.cwiseAbs().maxCoeff() / kPrecision,\n             1 << kMaxBitsPerDirection)\n        << \"Point out of bounds: \" << point;\n    Eigen::Array3i raster_point;\n    Eigen::Array3i block_coordinate;\n    for (int i = 0; i < 3; ++i) {\n      raster_point[i] = common::RoundToInt(point[i] / kPrecision);\n      block_coordinate[i] = raster_point[i] >> kBitsPerCoordinate;\n      raster_point[i] &= kCoordinateMask;\n    }\n    auto* const block = blocks.mutable_value(block_coordinate);\n    num_blocks += block->empty();\n    block->push_back({raster_point, point_index});\n  } //end for\n\n  // Encode blocks.\n  point_data_.reserve(4 * num_blocks + point_cloud.size());\n  for (Blocks::Iterator it(blocks); !it.Done(); it.Next(), --num_blocks) {\n    const auto& raster_points = it.GetValue();\n    CHECK_LE(raster_points.size(), std::numeric_limits<int32>::max());\n    point_data_.push_back(raster_points.size());\n    const Eigen::Array3i block_coordinate = it.GetCellIndex();\n    point_data_.push_back(block_coordinate.x());\n    point_data_.push_back(block_coordinate.y());\n    point_data_.push_back(block_coordinate.z());\n    for (const RasterPoint& raster_point : raster_points) {\n      point_data_.push_back((((raster_point.point.z() << kBitsPerCoordinate) +\n                              raster_point.point.y())\n                             << kBitsPerCoordinate) +\n                            raster_point.point.x());\n    }\n  }\n  CHECK_EQ(num_blocks, 0);\n}\n\n/*私有的构造函数,外部不能调用*/\nCompressedPointCloud::CompressedPointCloud(const std::vector<int32>& point_data,\n                                           size_t num_points)\n    : point_data_(point_data), num_points_(num_points) {}\n\nbool CompressedPointCloud::empty() const { return num_points_ == 0; }\n\nsize_t CompressedPointCloud::size() const { return num_points_; }\n\nCompressedPointCloud::ConstIterator CompressedPointCloud::begin() const {//迭代器首,\n  return ConstIterator(this);\n}\n\nCompressedPointCloud::ConstIterator CompressedPointCloud::end() const { //迭代器尾,\n  return ConstIterator::EndIterator(this);\n}\n\nPointCloud CompressedPointCloud::Decompress() const {\n  PointCloud decompressed;                    //Vector3f组成的vector\n  for (const Eigen::Vector3f& point : *this) { //此处调用的是迭代器函数,begin(),end()\n    decompressed.push_back(point);\n  }\n  return decompressed;\n}\n\nproto::CompressedPointCloud CompressedPointCloud::ToProto() const {\n  proto::CompressedPointCloud result;\n  result.set_num_points(num_points_);       //序列化点云的个数\n  for (const int32 data : point_data_) {\n    result.add_point_data(data);            //依次序添加数据 \n  }\n  return result;\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/compressed_point_cloud.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_COMPRESSED_POINT_CLOUD_H_\n#define CARTOGRAPHER_SENSOR_COMPRESSED_POINT_CLOUD_H_\n\n#include <iterator>\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n/*\nCompressedPointCloud是点云压缩类,\n目的：压缩ponits以减少存储空间，压缩后有精度损失。\n方法：按照block分组。\n\n只有一个私有的\n*/\n// A compressed representation of a point cloud consisting of a collection of\n// points (Vector3f).\n// Internally, points are grouped by blocks. Each block encodes a bit of meta\n// data (number of points in block, coordinates of the block) and encodes each\n// point with a fixed bit rate in relation to the block.\nclass CompressedPointCloud {\n public:\n  class ConstIterator; //前置声明\n\n  CompressedPointCloud() : num_points_(0) {}\n  explicit CompressedPointCloud(const PointCloud& point_cloud);\n\n  // Returns decompressed point cloud.\n  PointCloud Decompress() const;\n\n  bool empty() const;                   // num_points_==0\n  size_t size() const;                  // num_points_\n  ConstIterator begin() const;\n  ConstIterator end() const;\n\n  proto::CompressedPointCloud ToProto() const;\n\n private:\n  CompressedPointCloud(const std::vector<int32>& point_data, size_t num_points);\n\n  std::vector<int32> point_data_;\n  size_t num_points_;\n};\n\n/*前行迭代器*/\n// Forward iterator for compressed point clouds.\nclass CompressedPointCloud::ConstIterator\n    : public std::iterator<std::forward_iterator_tag, Eigen::Vector3f> {\n public:\n  // Creates begin iterator.\n  explicit ConstIterator(const CompressedPointCloud* compressed_point_cloud);\n\n  // Creates end iterator.\n  static ConstIterator EndIterator(\n      const CompressedPointCloud* compressed_point_cloud);\n\n  Eigen::Vector3f operator*() const;\n  ConstIterator& operator++();\n  bool operator!=(const ConstIterator& it) const;\n\n private:\n  // Reads next point from buffer. Also handles reading the meta data of the\n  // next block, if the current block is depleted.\n  void ReadNextPoint();\n\n  const CompressedPointCloud* compressed_point_cloud_;\n  size_t remaining_points_;\n  int32 remaining_points_in_current_block_;\n  Eigen::Vector3f current_point_;\n  Eigen::Vector3i current_block_coordinates_;\n  std::vector<int32>::const_iterator input_;\n};\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_COMPRESSED_POINT_CLOUD_H_\n"
  },
  {
    "path": "sensor/compressed_point_cloud_test.cc",
    "content": "\n\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n\n#include \"gmock/gmock.h\"\n\nnamespace Eigen {\n\n// Prints Vector3f in a readable format in matcher ApproximatelyEquals when\n// failing a test. Without this function, the output is formated as hexadecimal\n// 8 bit numbers.\nvoid PrintTo(const Vector3f& x, std::ostream* os) {\n  *os << \"(\" << x[0] << \", \" << x[1] << \", \" << x[2] << \")\";\n}\n\n}  // namespace Eigen\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n\nusing ::testing::Contains;\nusing ::testing::FloatNear;\nusing ::testing::PrintToString;\n\nconstexpr float kPrecision = 0.001f;\n\n// Matcher for 3-d vectors w.r.t. to the target precision.\nMATCHER_P(ApproximatelyEquals, expected,\n          string(\"is equal to \") + PrintToString(expected)) {\n  return (arg - expected).isZero(kPrecision);//压缩后有精度丢失,精确度为0.001\n}\n\n// Helper function to test the mapping of a single point. Includes test for\n// recompressing the same point again.\nvoid TestPoint(const Eigen::Vector3f& p) {\n  CompressedPointCloud compressed({p});\n  EXPECT_EQ(1, compressed.size());\n  EXPECT_THAT(*compressed.begin(), ApproximatelyEquals(p));\n  CompressedPointCloud recompressed({*compressed.begin()});\n  EXPECT_THAT(*recompressed.begin(), ApproximatelyEquals(p));\n}\n\nTEST(CompressPointCloudTest, CompressesPointsCorrectly) {\n  TestPoint(Eigen::Vector3f(8000.f, 7500.f, 5000.f));\n  TestPoint(Eigen::Vector3f(1000.f, 2000.f, 3000.f));\n  TestPoint(Eigen::Vector3f(100.f, 200.f, 300.f));\n  TestPoint(Eigen::Vector3f(10.f, 20.f, 30.f));\n  TestPoint(Eigen::Vector3f(-0.00049f, -0.0005f, -0.0015f));\n  TestPoint(Eigen::Vector3f(0.05119f, 0.0512f, 0.05121));\n  TestPoint(Eigen::Vector3f(-0.05119f, -0.0512f, -0.05121));\n  TestPoint(Eigen::Vector3f(0.8405f, 0.84f, 0.8396f));\n  TestPoint(Eigen::Vector3f(0.8395f, 0.8394f, 0.8393f));\n  TestPoint(Eigen::Vector3f(0.839f, 0.8391f, 0.8392f));\n  TestPoint(Eigen::Vector3f(0.8389f, 0.8388f, 0.83985f));\n}\n\nTEST(CompressPointCloudTest, Compresses) {\n  const CompressedPointCloud compressed({Eigen::Vector3f(0.838f, 0, 0),\n                                         Eigen::Vector3f(0.839f, 0, 0),\n                                         Eigen::Vector3f(0.840f, 0, 0)});\n  EXPECT_FALSE(compressed.empty());\n  EXPECT_EQ(3, compressed.size());\n  const PointCloud decompressed = compressed.Decompress();\n  EXPECT_EQ(3, decompressed.size());\n  EXPECT_THAT(decompressed,\n              Contains(ApproximatelyEquals(Eigen::Vector3f(0.838f, 0, 0))));//压缩解压缩后,前3位小数不变\n  EXPECT_THAT(decompressed,\n              Contains(ApproximatelyEquals(Eigen::Vector3f(0.839f, 0, 0))));\n  EXPECT_THAT(decompressed,\n              Contains(ApproximatelyEquals(Eigen::Vector3f(0.840f, 0, 0))));\n}\n\nTEST(CompressPointCloudTest, CompressesEmptyPointCloud) {\n  CompressedPointCloud compressed;\n  EXPECT_TRUE(compressed.empty());\n  EXPECT_EQ(0, compressed.size());\n}\n\n// Test for gaps.\n// Produces a series of points densly packed along the x axis, compresses these\n// points (twice), and tests, whether there are gaps between two consecutive\n// points.\nTEST(CompressPointCloudTest, CompressesNoGaps) {\n  PointCloud point_cloud;\n  for (int i = 0; i < 3000; ++i) {\n    point_cloud.push_back(Eigen::Vector3f(kPrecision * i - 1.5f, 0, 0));\n  }\n  const CompressedPointCloud compressed(point_cloud);//压缩\n  const PointCloud decompressed = compressed.Decompress();//解压缩\n  const CompressedPointCloud recompressed(decompressed);//再压缩\n  EXPECT_EQ(decompressed.size(), recompressed.size());\n\n  std::vector<float> x_coord;\n  for (const auto& p : compressed) {\n    x_coord.push_back(p[0]);\n  }\n  std::sort(x_coord.begin(), x_coord.end());\n  for (size_t i = 1; i < x_coord.size(); ++i) {\n    EXPECT_THAT(std::abs(x_coord[i] - x_coord[i - 1]),\n                FloatNear(kPrecision, 1e-7f)); //前后相差不大\n  }\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/configuration.cc",
    "content": "\n#include \"cartographer/sensor/configuration.h\"\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/sensor/proto/configuration.pb.h\"\n#include \"cartographer/transform/proto/transform.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace sensor {\n/*\nassets_writer_backpack_2d.lua\n\nSensor定义：\n  message Sensor {\n  string frame_id = 2; //'frame_id' of the sensor.\n  Rigid3d transform = 3;\n  }\n*/\nproto::Configuration::Sensor CreateSensorConfiguration(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::Configuration::Sensor sensor;\n  sensor.set_frame_id(parameter_dictionary->GetString(\"frame_id\"));\n\n  *sensor.mutable_transform() = transform::ToProto(transform::FromDictionary(\n      parameter_dictionary->GetDictionary(\"transform\").get()));\n  return sensor;\n}\n\n/*\nConfiguration定义：\nmessage Configuration {\n    repeated Sensor sensor = 15;\n}\n*/\nproto::Configuration CreateConfiguration(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::Configuration configuration;\n  for (auto& sensor_parameter_dictionary :\n       parameter_dictionary->GetArrayValuesAsDictionaries()) {\n    *configuration.add_sensor() =\n        CreateSensorConfiguration(sensor_parameter_dictionary.get());\n  }\n  return configuration;\n}\n\n/*\n判断系统是否支持某一传感器id。\n*/\nbool IsEnabled(const string& frame_id,\n               const sensor::proto::Configuration& sensor_configuration) {\n  for (const auto& sensor : sensor_configuration.sensor()) {\n    if (sensor.frame_id() == frame_id) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/*\n\n将sensor坐标转换为机器坐标。以便于跟踪计算\n*/\ntransform::Rigid3d GetTransformToTracking(\n    const string& frame_id,\n    const sensor::proto::Configuration& sensor_configuration) {\n  for (const auto& sensor : sensor_configuration.sensor()) {\n    if (sensor.frame_id() == frame_id) {\n      return transform::ToRigid3(sensor.transform());\n    }\n  }\n  LOG(FATAL) << \"No configuration found for sensor with frame ID '\" << frame_id\n             << \"'.\";\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/configuration.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_CONFIGURATION_H_\n#define CARTOGRAPHER_SENSOR_CONFIGURATION_H_\n\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/sensor/proto/configuration.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n//从sensor配置文件解析sensor的数据参数。主要是sensor到机器坐标的转换\n// Creates the configuration for a singular sensor from 'parameter_dictionary'.\nproto::Configuration::Sensor CreateSensorConfiguration(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n//求得多个sensor的配置的集合。\n// Creates the mapping from frame_id to Sensors defined in\n// 'parameter_dictionary'.\nproto::Configuration CreateConfiguration(\n    common::LuaParameterDictionary* parameter_dictionary);\n\n//系统是否支持某一传感器。\n// Returns true if 'frame_id' is mentioned in 'sensor_configuration'.\nbool IsEnabled(const string& frame_id,\n               const sensor::proto::Configuration& sensor_configuration);\n\n//将sensor采集的data经过3d坐标变换为机器坐标。\n// Returns the transform which takes data from the sensor frame to the\n// tracking frame.\ntransform::Rigid3d GetTransformToTracking(\n    const string& frame_id,\n    const sensor::proto::Configuration& sensor_configuration);\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_CONFIGURATION_H_\n"
  },
  {
    "path": "sensor/data.h",
    "content": "\n#ifndef CARTOGRAPHER_MAPPING_DATA_H_\n#define CARTOGRAPHER_MAPPING_DATA_H_\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/range_data.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n\n/*  \nData是针对某一类的传感器的数据的封装,\n数据成员含:\n1,传感器类型,包括imu，雷达，里程计\n2,测量时间,time\n3,Imu测量值,(imu:惯性测量单元是测量物体三轴姿态角(或角速率)以及加速度的装置。)\n4,rangefinder,测距仪,\n5,odometer_pose,里程计,指(如装在汽车上的)测量行程的装置。\n\n构造函数有3个,每一类传感器对应一个构造函数.\n\n\n*/\n// This type is a logical union, i.e. only one type of sensor data is actually\n// filled in. It is only used for time ordering sensor data before passing it\n// on.\nstruct Data {\n  enum class Type { kImu, kRangefinder, kOdometer };\n\n  struct Imu {\n    Eigen::Vector3d linear_acceleration; //线性加速度,m/s2\n    Eigen::Vector3d angular_velocity;    //角速度, rad/s\n  };\n  \n//测距仪.基本原理是向待测距的物体发射激光脉冲并开始计时，接收到反射光时停止计时。这段时间即可以转换为激光器与目标之间的距离。\n  struct Rangefinder {\n    Eigen::Vector3f origin; //sensor的位姿\n    PointCloud ranges;      //测距点云\n  };\n\n  Data(const common::Time time, const Imu& imu) //传感器类型是imu\n      : type(Type::kImu), time(time), imu(imu) {}\n\n  Data(const common::Time time, const Rangefinder& rangefinder)//传感器类型是雷达\n      : type(Type::kRangefinder), time(time), rangefinder(rangefinder) {}\n\n  Data(const common::Time time, const transform::Rigid3d& odometer_pose)\n  //传感器类型是里程计\n      : type(Type::kOdometer), time(time), odometer_pose(odometer_pose) {}\n\n  Type type;\n  common::Time time;\n  Imu imu;\n  Rangefinder rangefinder;\n  transform::Rigid3d odometer_pose;\n};\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_MAPPING_DATA_H_\n\n/*\nrigidbody:刚体 \n*/"
  },
  {
    "path": "sensor/ordered_multi_queue.cc",
    "content": "\n#include \"cartographer/sensor/ordered_multi_queue.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <vector>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\nnamespace {\n\n// Number of items that can be queued up before we log which queues are waiting\n// for data.\nconst int kMaxQueueSize = 500;\n\n}  // namespace\n\n//重载QueueKey的<<输出运算符,友元函数\ninline std::ostream& operator<<(std::ostream& out, const QueueKey& key) {\n  return out << '(' << key.trajectory_id << \", \" << key.sensor_id << ')';\n}\n\nOrderedMultiQueue::OrderedMultiQueue() {}\n\nOrderedMultiQueue::~OrderedMultiQueue() {\n  for (auto& entry : queues_) {\n    CHECK(entry.second.finished);\n  }\n}\n\n//添加一个关键词是key的队列,并用比较函数Callback排序\nvoid OrderedMultiQueue::AddQueue(const QueueKey& queue_key, Callback callback) {\n  CHECK_EQ(queues_.count(queue_key), 0);\n  //map.count()相对于[],调用时，是不添加key的。而[]是带有副作用的。c++pr.386\n  queues_[queue_key].callback = std::move(callback);\n}\n\nvoid OrderedMultiQueue::MarkQueueAsFinished(const QueueKey& queue_key) {\n  auto it = queues_.find(queue_key);\n  CHECK(it != queues_.end()) << \"Did not find '\" << queue_key << \"'.\";\n  auto& queue = it->second;\n  CHECK(!queue.finished);//检查状态\n  queue.finished = true;//标记本队列已完成,别的数据不能再入队.\n  Dispatch();           //调用一次MarkQueueAsFinished()就要调用一次Dispatch()\n}\n\n//根据key找到队列,并添加data元素\nvoid OrderedMultiQueue::Add(const QueueKey& queue_key,\n                            std::unique_ptr<Data> data) {\n  auto it = queues_.find(queue_key);\n  if (it == queues_.end()) {//没有key时，警告\n    LOG_EVERY_N(WARNING, 1000)\n        << \"Ignored data for queue: '\" << queue_key << \"'\";\n    return;\n  }\n  it->second.queue.Push(std::move(data));//Queue的queue.push()\n  Dispatch();//调用一次Add()就要调用一次Dispatch()\n}\n\n//先找到没有finished的队列,然后再对这些队列标记finished.已完成的则不作任何处理\nvoid OrderedMultiQueue::Flush() {\n  std::vector<QueueKey> unfinished_queues;\n  for (auto& entry : queues_) {\n    if (!entry.second.finished) {\n      unfinished_queues.push_back(entry.first);\n    }\n  }\n  for (auto& unfinished_queue : unfinished_queues) {\n    MarkQueueAsFinished(unfinished_queue);//一个一个的处理。\n  }\n}\n\nQueueKey OrderedMultiQueue::GetBlocker() const {\n  CHECK(!queues_.empty());\n  return blocker_;\n}\n\n/*\nDispatch()函数，不断的处理来自sensor的数据。按照data采集的时间顺序处理。\n\nkFirst: {1,2,3} finised\nkSecond:{}      finised\nkThird: {}      finised\n\n*/\nvoid OrderedMultiQueue::Dispatch() {\n  while (true) {\n    //首先处理的数据，也即最早采集的数据\n    const Data* next_data = nullptr;\n    Queue* next_queue = nullptr;\n    QueueKey next_queue_key;\n    //遍历队列中的每一个key：填充上面3个变量值。如果某一key对应的data为空，则直接return\n    for (auto it = queues_.begin(); it != queues_.end();) {\n      const auto* data = it->second.queue.Peek<Data>();//获取某一队的队首data\n      if (data == nullptr) { \n        if (it->second.finished) {//it对应的队列为空且为finished,故删除it对应的key\n          queues_.erase(it++);    //map迭代器没有失效?\n          continue;\n        }\n        CannotMakeProgress(it->first);//此时什么也不做。\n        return;\n      }\n      if (next_data == nullptr || data->time < next_data->time) {\n      //找到next_data数据:即采集时间最早的数据，理论上应该最先处理它。\n        next_data = data;\n        next_queue = &it->second;\n        next_queue_key = it->first;\n      }\n      CHECK_LE(last_dispatched_time_, next_data->time)\n          << \"Non-sorted data added to queue: '\" << it->first << \"'\";\n      ++it;\n    }\n\n\n    if (next_data == nullptr) {\n      CHECK(queues_.empty());//只有多队列为空，才可能next_data==nullptr\n      return;\n    }\n\n\n    // If we haven't dispatched any data for this trajectory yet, fast forward\n    // all queues of this trajectory until a common start time has been reached.\n    const common::Time common_start_time =\n        GetCommonStartTime(next_queue_key.trajectory_id);\n    //common_start_time即所有的sensor都开始有data的时间点。\n\n    if (next_data->time >= common_start_time) { //大多数情况，happy case\n      // Happy case, we are beyond the 'common_start_time' already.\n      last_dispatched_time_ = next_data->time;\n      next_queue->callback(next_queue->queue.Pop()); //调用回调函数处理data。\n    } else if (next_queue->queue.Size() < 2) {// 罕见\n      if (!next_queue->finished) {\n        // We cannot decide whether to drop or dispatch this yet.\n        CannotMakeProgress(next_queue_key);\n        return;\n      }\n      last_dispatched_time_ = next_data->time;\n      next_queue->callback(next_queue->queue.Pop());\n    } else {\n      // We take a peek at the time after next data. If it also is not beyond\n      // 'common_start_time' we drop 'next_data', otherwise we just found the\n      // first packet to dispatch from this queue.\n      std::unique_ptr<Data> next_data_owner = next_queue->queue.Pop();\n      if (next_queue->queue.Peek<Data>()->time > common_start_time) {\n        last_dispatched_time_ = next_data->time;\n        next_queue->callback(std::move(next_data_owner));\n      }\n    }\n  }\n}\n\nvoid OrderedMultiQueue::CannotMakeProgress(const QueueKey& queue_key) {\n  blocker_ = queue_key; //标识该队列Queue已经阻塞\n  for (auto& entry : queues_) {//std::map<QueueKey, Queue> \n    if (entry.second.queue.Size() > kMaxQueueSize) {//？\n      LOG_EVERY_N(WARNING, 60) << \"Queue waiting for data: \" << queue_key;\n      return;\n    }\n  }\n}\n\n/*对同一轨迹id，求得所有sensor的首次采集data的最晚时间maxtime\n\n不同轨迹按不同轨迹算：\nkFirst: {0,1,2,3} finised\nkSecond:{2}       \nkThird: {4}\n{2,2,}\n\n*/\ncommon::Time OrderedMultiQueue::GetCommonStartTime(const int trajectory_id) {\n  //map.emplace():Construct and insert element，根据trajectory_id构造一个map。\n  auto emplace_result = common_start_time_per_trajectory_.emplace(\n      trajectory_id, common::Time::min());\n  common::Time& common_start_time = emplace_result.first->second;\n  if (emplace_result.second) {\n    for (auto& entry : queues_) { \n    //entry是map的pair<,>.本循环求得所有传感器中的maxtime\n      if (entry.first.trajectory_id == trajectory_id) {\n        common_start_time =\n            std::max(common_start_time, entry.second.queue.Peek<Data>()->time);\n      }\n    }\n    LOG(INFO) << \"All sensor data for trajectory \" << trajectory_id\n              << \" is available starting at '\" << common_start_time << \"'.\";\n  }\n  return common_start_time;\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n\n\n/*\nordered_multi_queue.cc:172] All sensor data for trajectory 0 is available starting at '635727208200176089'\n\n*/\n\n/*\nLOG_EVERY_N(INFO, 60) << \"Queue...\";在程序中周期性的记录日志信息，\n在该语句第1、61、121……次被执行的时候，记录日志信息。\n*/"
  },
  {
    "path": "sensor/ordered_multi_queue.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_ORDERED_MULTI_QUEUE_H_\n#define CARTOGRAPHER_SENSOR_ORDERED_MULTI_QUEUE_H_\n\n#include <functional>\n#include <map>\n#include <memory>\n#include <string>\n#include <tuple>\n\n#include \"cartographer/common/blocking_queue.h\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/common/time.h\"\n#include \"cartographer/sensor/data.h\"\n\n \nnamespace cartographer {\nnamespace sensor {\n\nstruct QueueKey {\n  int trajectory_id;// 轨线id;\n  string sensor_id; //传感器id\n\n//重载小于运算符,forward_as_tuple:完美转发. (以tuple规则比较2者),tuple定义了<运算符\n  bool operator<(const QueueKey& other) const {\n    return std::forward_as_tuple(trajectory_id, sensor_id) <\n           std::forward_as_tuple(other.trajectory_id, other.sensor_id);\n  }\n};\n\n\n/*\n\nOrderedMultiQueue，用于管理多个有序的传感器数据，\n是有序的多队列类,每个队列有一个key,并且有一个自定义排序函数\nqueues_的形式为：\nkey1:Queue\nkey2：Queue\nkey3：Queue\n\nQueue的形式为\n  struct Queue {\n    common::BlockingQueue<std::unique_ptr<Data>> queue;\n    Callback callback;\n    bool finished = false;\n  };\n\nOrderedMultiQueue的数据成员有\n1,common_start_time_per_trajectory_:轨迹id及对应创建轨迹时间\n2,last_dispatched_time_\n3,std::map<QueueKey, Queue> queues_;按照key排序的map\n4,QueueKey blocker_;\n\n*/\n// Maintains multiple queues of sorted sensor data and dispatches(迅速办理) it in merge\n// sorted order. It will wait to see at least one value for each unfinished\n// queue before dispatching the next time ordered value across all queues.\n//\n// This class is thread-compatible.\nclass OrderedMultiQueue {\n public:\n  using Callback = std::function<void(std::unique_ptr<Data>)>;//回调函数\n\n  OrderedMultiQueue();\n  ~OrderedMultiQueue();\n\n\n//添加一个【队列】Queue,名称是key,以后入队的data，调用回调函数callback处理\n  // Adds a new queue with key 'queue_key' which must not already exist.\n  // 'callback' will be called whenever data from this queue can be dispatched.\n  void AddQueue(const QueueKey& queue_key, Callback callback);\n\n/*\n某一key标识的【队列】Queue已经完成入队,因此不能再入队列,并在map中移除key.\n*/\n  // Marks a queue as finished, i.e. no further data can be added. The queue\n  // will be removed once the last piece of data from it has been dispatched.\n  void MarkQueueAsFinished(const QueueKey& queue_key);\n\n//对某一key标识的队列Queue,压入data,data按照回调函数处理\n  // Adds 'data' to a queue with the given 'queue_key'. Data must be added\n  // sorted per queue.\n  void Add(const QueueKey& queue_key, std::unique_ptr<Data> data);\n\n/*标记全部队列都已经finished.\n kFirst: {0,4,5,6}\nkSecond:{0,1,3,7}\nkThird: {0,2,8}\n之前只处理到6，调用Flush()则处理剩余的7,8\n\n如果不调用Flush()，则析构时会出错\n */\n\n  // Dispatches all remaining values in sorted order and removes the underlying\n  // queues.\n  void Flush();\n\n/*返回阻塞的队列(意为该队列对应的sensor的data未到达)*/\n  // Must only be called if at least one unfinished queue exists. Returns the\n  // key of a queue that needs more data before the OrderedMultiQueue can\n  // dispatch data.\n  QueueKey GetBlocker() const;\n\n private:\n  struct Queue {\n    common::BlockingQueue<std::unique_ptr<Data>> queue;\n    Callback callback;\n    bool finished = false;\n  };\n\n  void Dispatch();\n  void CannotMakeProgress(const QueueKey& queue_key);\n  common::Time GetCommonStartTime(int trajectory_id);\n\n  // Used to verify that values are dispatched in sorted order.\n  common::Time last_dispatched_time_ = common::Time::min();\n\n  std::map<int, common::Time> common_start_time_per_trajectory_;\n  std::map<QueueKey, Queue> queues_;//多队列主体,本类最大的内存占用量\n  QueueKey blocker_;\n};\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_ORDERED_MULTI_QUEUE_H_\n/*\nqueues_:\nfirst 是QueueKey\nsecond 是 Queue。\n\nQueue的形式为\n  struct Queue {\n    common::BlockingQueue<std::unique_ptr<Data>> queue;\n    Callback callback;\n    bool finished = false;\n  };\n\n*/"
  },
  {
    "path": "sensor/ordered_multi_queue_le_test.cc",
    "content": "\n#define sout(Xit)  {std::cout<<__LINE__<<\" \"<< Xit <<\"\"<<std::endl;}\n\n#include \"cartographer/sensor/ordered_multi_queue.h\"\n\n#include <vector>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n/*测试夹具（Test Fixtures）\nTEST_F提供了一个初始化函数（SetUp）和一个清理函数(TearDown)，\n在TEST_F中使用的变量可以在初始化函数SetUp中初始化，\n在TearDown中销毁，并且所有的TEST_F是互相独立的，都是在初始化以后的状态开始运行，\n一个TEST_F不会影响另一个TEST_F所使用的数据.\n用TEST_F定义测试，写法与TEST相同，但测试用例名必须为上面定义的类名。\n*/\nclass OrderedMultiQueueTest : public ::testing::Test {\n protected:\n  // These are keys are chosen so that they sort first, second, third.\n  const QueueKey kFirst{1, \"a\"};//trajectory_id，sensor_id\n  const QueueKey kSecond{1, \"b\"};\n  const QueueKey kThird{2, \"b\"};\n\n//void AddQueue(const QueueKey& queue_key, Callback callback);\n  void SetUp() {\n  //初始化函数,定义处理data的函数：\n  //(以后用于test比较):检查values_的最大值是否小于欲添加的值.\n    for (const auto& queue_key : {kFirst, kSecond, kThird}) {\n      queue_.AddQueue(queue_key, [this](std::unique_ptr<Data> data) {\n        if (!values_.empty()) {\n          EXPECT_GE(data->time, values_.back().time);\n        }\n        values_.push_back(*data);\n      });\n    }\n  }\n\n//Data的定义在data.h\n  std::unique_ptr<Data> MakeImu(const int ordinal) {//时间,序列\n    return common::make_unique<Data>(\n        common::FromUniversal(ordinal),\n        Data::Imu{Eigen::Vector3d::Zero(), Eigen::Vector3d::Zero()});\n  }\n\n  std::vector<Data> values_;\n  OrderedMultiQueue queue_;\n};\n\n\nTEST_F(OrderedMultiQueueTest, Ordering) {\n  // void Add(const QueueKey& queue_key, std::unique_ptr<Data> data);\n  queue_.Add(kFirst, MakeImu(0));//\n  queue_.Add(kFirst, MakeImu(4));\n  queue_.Add(kFirst, MakeImu(5));\n  queue_.Add(kFirst, MakeImu(6));\n  EXPECT_TRUE(values_.empty());  \n  //why为空?：queue_对应的其他sensor还没有产生data，\n  //故调用Add()时，只在本队列插入data，而Dispatch()直接返回，没有调用Callback函数。\n\n\n  queue_.Add(kSecond, MakeImu(0));\n  queue_.Add(kSecond, MakeImu(1));\n  EXPECT_TRUE(values_.empty()); //同上\n\n\n  queue_.Add(kThird, MakeImu(0));\n  queue_.Add(kThird, MakeImu(2));\n  EXPECT_EQ(values_.size(), 4);\n\n /*\nkFirst: {0,4,5,6}\nkSecond:{0,1}\nkThird: {0,2}\n\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{0,0,0,1}\n  }\n\n\n  queue_.Add(kSecond, MakeImu(3));\n  EXPECT_EQ(values_.size(), 5);\n/*\nkFirst: {0,4,5,6}\nkSecond:{0,1,3}\nkThird: {0,2}\n\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{0,0,0,1,2}\n  }\n\n  queue_.Add(kSecond, MakeImu(7));\n  queue_.Add(kThird, MakeImu(8));\n\n  /*\nkFirst: {0,4,5,6}\nkSecond:{0,1,3,7}\nkThird: {0,2,8}\n\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{0,0,0,1,2,3,4,5,6}\n  }\n\n\n//先找到没有finished的队列,然后再对这些队列标记finished\n  queue_.Flush();//应该调用,否则析构时出错\n  EXPECT_EQ(11, values_.size());// 4+4+3\n\n    /*\nkFirst: {0,4,5,6}\nkSecond:{0,1,3,7}\nkThird: {0,2,8}\n\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{0,0,0,1,2,3,4,5,6,7,8}\n  }\n  for (size_t i = 0; i < values_.size() - 1; ++i) { //检查是否按序\n    EXPECT_LE(values_[i].time, values_[i + 1].time);\n  }\n}\n\nTEST_F(OrderedMultiQueueTest, MarkQueueAsFinished) {\n  queue_.Add(kFirst, MakeImu(1));\n  queue_.Add(kFirst, MakeImu(2));\n  queue_.Add(kFirst, MakeImu(3));\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kFirst);\n\n//调用该函数一次，就调用一次Dispatch(),若key对应的队列为空，则从队列中删除key\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kSecond);\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kThird);\n\n    /*\nkFirst: {1,2,3} finised\nkSecond:{}      finised\nkThird: {}      finised\n\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{1,2,3}\n  }\n\n  EXPECT_EQ(3, values_.size());\n  for (size_t i = 0; i < values_.size(); ++i) {\n    EXPECT_EQ(i + 1, common::ToUniversal(values_[i].time));\n  }\n}\n\nTEST_F(OrderedMultiQueueTest, CommonStartTimePerTrajectory) {\n  queue_.Add(kFirst, MakeImu(0));\n  queue_.Add(kFirst, MakeImu(1));\n  queue_.Add(kFirst, MakeImu(2));\n  queue_.Add(kFirst, MakeImu(3));\n  queue_.Add(kSecond, MakeImu(2));\n  EXPECT_TRUE(values_.empty());\n  queue_.Add(kThird, MakeImu(4));\n  EXPECT_EQ(values_.size(), 2);\n/*\nkFirst: {0,1,2,3}\nkSecond:{2}\nkThird: {4}\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{2,2}\n  }\n\n\n  queue_.MarkQueueAsFinished(kFirst);\n  EXPECT_EQ(values_.size(), 2);//没有0,1，因为起点时间不是0,1，而是2\n/*\nkFirst: {0,1,2,3} finised\nkSecond:{2}       \nkThird: {4}\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{2,2}\n  }\n\n  sout(\".id2.\");//为何与test1不同？起点time不同。test1：起点时间全为0，test3起点时间是2\n  queue_.MarkQueueAsFinished(kSecond);\n  EXPECT_EQ(values_.size(), 4);\n/*\nkFirst: {0,1,2,3} finised\nkSecond:{2}       finised\nkThird: {4}\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{2,2,3,4}\n  }\n\n//第二条轨迹id开始采集数据。\n  queue_.MarkQueueAsFinished(kThird);\n  EXPECT_EQ(values_.size(), 4);\n/*\nkFirst: {0,1,2,3} finised\nkSecond:{2}       finised\nkThird: {4}       finised\n*/\n  for(auto i:values_)\n  {\n      sout(i.time);//{2,2,3,4}\n  }\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/ordered_multi_queue_test.cc",
    "content": "\n#include \"cartographer/sensor/ordered_multi_queue.h\"\n\n#include <vector>\n\n#include \"cartographer/common/make_unique.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n/*测试夹具（Test Fixtures）\nTEST_F提供了一个初始化函数（SetUp）和一个清理函数(TearDown)，\n在TEST_F中使用的变量可以在初始化函数SetUp中初始化，\n在TearDown中销毁，并且所有的TEST_F是互相独立的，都是在初始化以后的状态开始运行，\n一个TEST_F不会影响另一个TEST_F所使用的数据.\n用TEST_F定义测试，写法与TEST相同，但测试用例名必须为上面定义的类名。\n*/\nclass OrderedMultiQueueTest : public ::testing::Test {\n protected:\n  // These are keys are chosen so that they sort first, second, third.\n  const QueueKey kFirst{1, \"a\"};//trajectory_id，sensor_id\n  const QueueKey kSecond{1, \"b\"};\n  const QueueKey kThird{2, \"b\"};\n\n//void AddQueue(const QueueKey& queue_key, Callback callback);\n  void SetUp() {\n  //初始化函数,定义处理data的函数：\n  //(以后用于test比较):检查values_的最大值是否小于欲添加的值.\n    for (const auto& queue_key : {kFirst, kSecond, kThird}) {\n      queue_.AddQueue(queue_key, [this](std::unique_ptr<Data> data) {\n        if (!values_.empty()) {\n          EXPECT_GE(data->time, values_.back().time);\n        }\n        values_.push_back(*data);\n      });\n    }\n  }\n\n//Data的定义在data.h\n  std::unique_ptr<Data> MakeImu(const int ordinal) {//时间,序列\n    return common::make_unique<Data>(\n        common::FromUniversal(ordinal),\n        Data::Imu{Eigen::Vector3d::Zero(), Eigen::Vector3d::Zero()});\n  }\n\n  std::vector<Data> values_;\n  OrderedMultiQueue queue_;\n};\n\n\nTEST_F(OrderedMultiQueueTest, Ordering) {\n  // void Add(const QueueKey& queue_key, std::unique_ptr<Data> data);\n  queue_.Add(kFirst, MakeImu(0));//\n  queue_.Add(kFirst, MakeImu(4));\n  queue_.Add(kFirst, MakeImu(5));\n  queue_.Add(kFirst, MakeImu(6));\n  EXPECT_TRUE(values_.empty());  \n  //why为空?：queue_对应的其他sensor还没有产生data，\n  //故调用Add()时，只在本队列插入data，而Dispatch()直接返回，没有调用Callback函数。\n\n\n  queue_.Add(kSecond, MakeImu(0));\n  queue_.Add(kSecond, MakeImu(1));\n  EXPECT_TRUE(values_.empty()); //同上\n\n\n  queue_.Add(kThird, MakeImu(0));\n  queue_.Add(kThird, MakeImu(2));\n  EXPECT_EQ(values_.size(), 4);\n/*\nkFirst: {0,4,5,6}\nkSecond:{0,1}\nkThird: {0,2}\n\n*/\n\n  queue_.Add(kSecond, MakeImu(3));\n  EXPECT_EQ(values_.size(), 5);\n\n  queue_.Add(kSecond, MakeImu(7));\n  queue_.Add(kThird, MakeImu(8));\n  queue_.Flush();//应该调用,否则出错\n\n  EXPECT_EQ(11, values_.size());// f:4,s:4,t:3\n  for (size_t i = 0; i < values_.size() - 1; ++i) { //检查是否按序\n    EXPECT_LE(values_[i].time, values_[i + 1].time);\n  }\n}\n\nTEST_F(OrderedMultiQueueTest, MarkQueueAsFinished) {\n  queue_.Add(kFirst, MakeImu(1));\n  queue_.Add(kFirst, MakeImu(2));\n  queue_.Add(kFirst, MakeImu(3));\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kFirst);\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kSecond);\n  EXPECT_TRUE(values_.empty());\n  queue_.MarkQueueAsFinished(kThird);\n\n  EXPECT_EQ(3, values_.size());\n  for (size_t i = 0; i < values_.size(); ++i) {\n    EXPECT_EQ(i + 1, common::ToUniversal(values_[i].time));\n  }\n}\n\nTEST_F(OrderedMultiQueueTest, CommonStartTimePerTrajectory) {\n  queue_.Add(kFirst, MakeImu(0));\n  queue_.Add(kFirst, MakeImu(1));\n  queue_.Add(kFirst, MakeImu(2));\n  queue_.Add(kFirst, MakeImu(3));\n  queue_.Add(kSecond, MakeImu(2));\n  EXPECT_TRUE(values_.empty());\n  queue_.Add(kThird, MakeImu(4));\n  EXPECT_EQ(values_.size(), 2);\n  queue_.MarkQueueAsFinished(kFirst);\n  EXPECT_EQ(values_.size(), 2);\n  queue_.MarkQueueAsFinished(kSecond);\n  EXPECT_EQ(values_.size(), 4);\n  queue_.MarkQueueAsFinished(kThird);\n  EXPECT_EQ(values_.size(), 4);\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/point_cloud.cc",
    "content": "\n#include \"cartographer/sensor/point_cloud.h\"\n\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n\n/*. \n功能：根据3D变换，转换点云,\n返回：转换后的新点云结果，原点云未变。\n\n*/\nPointCloud TransformPointCloud(const PointCloud& point_cloud,\n                               const transform::Rigid3f& transform) {\n  PointCloud result;                                //vector,元素是3*1f\n  result.reserve(point_cloud.size());               //分配内存\n  for (const Eigen::Vector3f& point : point_cloud) { \n    result.emplace_back(transform * point);         //C=A*B //A是Rigid3f,B是3*1f\n  }\n  return result;\n}\n\n\n/*\n功能：按范围丢弃点云，去掉z轴区域外的点云\n返回：新的符合要求的点云。\n*/\nPointCloud Crop(const PointCloud& point_cloud, const float min_z,\n                const float max_z) {\n  PointCloud cropped_point_cloud;\n  for (const auto& point : point_cloud) {\n    if (min_z <= point.z() && point.z() <= max_z) {//只保留在minz~maxz之间的点云\n      cropped_point_cloud.push_back(point);\n    }\n  }\n  return cropped_point_cloud;\n}\n\n//序列化\nproto::PointCloud ToProto(const PointCloud& point_cloud) {\n  proto::PointCloud proto;\n  for (const auto& point : point_cloud) {\n    proto.add_x(point.x());\n    proto.add_y(point.y());\n    proto.add_z(point.z());\n  }\n  return proto;\n}\n\n//反序列化\nPointCloud ToPointCloud(const proto::PointCloud& proto) {\n  PointCloud point_cloud;\n  const int size = std::min({proto.x_size(), proto.y_size(), proto.z_size()});\n  point_cloud.reserve(size);//最小，否则以下语句有bug\n  for (int i = 0; i != size; ++i) {\n    point_cloud.emplace_back(proto.x(i), proto.y(i), proto.z(i));\n  }\n  return point_cloud;\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/point_cloud.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_POINT_CLOUD_H_\n#define CARTOGRAPHER_SENSOR_POINT_CLOUD_H_\n\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"glog/logging.h\"\n\n/*\n点云数据是指在一个三维坐标系统中的一组向量的集合。{p1,p2,p3,...}\n这些向量通常以X,Y,Z三维坐标的形式表示p:{x,y,z}，\n而且一般主要用来代表一个物体的外表面形状。\n除{x,y,z}可以代表的几何位置信息之外，\n点云数据还可以表示一个点的RGB颜色，灰度值，深度，分割结果等。\n\nEg..Pi={Xi, Yi, Zi,…….}表示空间中的一个点，\n则Point Cloud={P1, P2, P3,…..Pn}表示一组点云数据。\n\ncartographer的PointCloud是由Vector3f组成的vector。\nPointCloudWithIntensities则是由点云和光线强度组成的struct类。\n\n\n*/\nnamespace cartographer {\nnamespace sensor {\n\ntypedef std::vector<Eigen::Vector3f> PointCloud;//vector，元素是3*1f\n\nstruct PointCloudWithIntensities { //点云+光线强度,{x,y,z}+intensity\n  PointCloud points; //3*1的vector\n  std::vector<float> intensities; \n};\n\n// Transforms 'point_cloud' according to 'transform'. 根据三维网格参数转换点云\nPointCloud TransformPointCloud(const PointCloud& point_cloud,\n                               const transform::Rigid3f& transform);\n\n\n/*去掉z轴区域外的点云,返回一个新的点云*/\n// Returns a new point cloud without points that fall outside the region defined\n// by 'min_z' and 'max_z'.\nPointCloud Crop(const PointCloud& point_cloud, float min_z, float max_z);\n\n//序列化\n// Converts 'point_cloud' to a proto::PointCloud.\nproto::PointCloud ToProto(const PointCloud& point_cloud);\n\n//反序列化\n// Converts 'proto' to a PointCloud.\nPointCloud ToPointCloud(const proto::PointCloud& proto);\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_POINT_CLOUD_H_\n"
  },
  {
    "path": "sensor/point_cloud_test.cc",
    "content": "\n\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/transform/transform.h\"\n\n#include <cmath>\n\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n\nTEST(PointCloudTest, TransformPointCloud) {\n  PointCloud point_cloud;\n  point_cloud.emplace_back(0.5f, 0.5f, 1.f);  //构造一个点云{0.5,0.5,1}\n  point_cloud.emplace_back(3.5f, 0.5f, 42.f); //{3.5,0.5,42}\n\n  //调用static Rigid2 Rotation(const double rotation) \n  const PointCloud transformed_point_cloud = TransformPointCloud(\n      point_cloud, transform::Embed3D(transform::Rigid2f::Rotation(M_PI_2))); \n\n/*绕z轴逆时针旋转 pi/2:\n[x',y',x']=[]*[x,y,z]\n化简后： x=-y，y=x，z=z\n*/\n  EXPECT_NEAR(-0.5f, transformed_point_cloud[0].x(), 1e-6);\n  EXPECT_NEAR(0.5f, transformed_point_cloud[0].y(), 1e-6);\n  EXPECT_NEAR(-0.5f, transformed_point_cloud[1].x(), 1e-6);\n  EXPECT_NEAR(3.5f, transformed_point_cloud[1].y(), 1e-6);\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/proto/adaptive_voxel_filter_options.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.sensor.proto;\n\nmessage AdaptiveVoxelFilterOptions {\n  // 'max_length' of a voxel edge.\n  optional float max_length = 1;\n  \n  //不少于min_num\n  // If there are more points and not at least 'min_num_points' remain, the\n  // voxel length is reduced trying to get this minimum number of points.\n  optional float min_num_points = 2;\n\n  // Points further away from the origin are removed.\n  optional float max_range = 3;\n}\n"
  },
  {
    "path": "sensor/proto/configuration.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.sensor.proto;\n\nimport \"cartographer/transform/proto/transform.proto\";\n\n// NEXT_ID: 16\nmessage Configuration {\n  // NEXT_ID: 4\n  message Sensor {\n    // 'frame_id' of the sensor.\n    optional string frame_id = 2;\n\n    // Transformation from 'frame_id' to the tracking frame.\n    optional transform.proto.Rigid3d transform = 3;\n  }\n\n  repeated Sensor sensor = 15;\n}\n"
  },
  {
    "path": "sensor/proto/sensor.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.sensor.proto;\n\noption java_outer_classname = \"Sensor\";\n\nimport \"cartographer/transform/proto/transform.proto\";\n\n// Collection of 3D 'points'.\nmessage PointCloud {          //点云,zyx\n  // Points as repeated floats for efficiency. All fields have the same size. float占4位,double占8位\n  repeated float x = 3 [packed = true];\n  repeated float y = 4 [packed = true];\n  repeated float z = 5 [packed = true];\n}\n\n// Compressed variant of PointCloud.\nmessage CompressedPointCloud { //压缩:点云数量+点云值\n  optional int32 num_points = 1;\n  repeated int32 point_data = 3 [packed = true];\n}\n\n// Proto representation of ::cartographer::sensor::RangeData\nmessage RangeData {             \n  optional transform.proto.Vector3f origin = 1;//sensor坐标\n  optional PointCloud point_cloud = 2;\n  optional PointCloud missing_echo_point_cloud = 3;\n}\n"
  },
  {
    "path": "sensor/range_data.cc",
    "content": "\n#include \"cartographer/sensor/range_data.h\"\n\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace sensor {\n/*\n\nmessage PointCloud {     \n  repeated float x = 3 [packed = true];\n  repeated float y = 4 [packed = true];\n  repeated float z = 5 [packed = true];\n}\n\nmessage CompressedPointCloud { \n  optional int32 num_points = 1;\n  repeated int32 point_data = 3 [packed = true];\n}\n\nmessage RangeData {             \n  optional transform.proto.Vector3f origin = 1;\n  optional PointCloud point_cloud = 2;\n  optional PointCloud missing_echo_point_cloud = 3;\n}\n\nPointCloud;//vector，元素是3*1f\n\n*/\nproto::RangeData ToProto(const RangeData& range_data) {\n  proto::RangeData proto;\n  *proto.mutable_origin() = transform::ToProto(range_data.origin);\n  *proto.mutable_point_cloud() = ToProto(range_data.returns);\n  *proto.mutable_missing_echo_point_cloud() = ToProto(range_data.misses);\n  return proto;\n}\n\nRangeData FromProto(const proto::RangeData& proto) {\n  auto range_data = RangeData{\n      transform::ToEigen(proto.origin()), ToPointCloud(proto.point_cloud()),\n      ToPointCloud(proto.missing_echo_point_cloud()),\n  };\n  return range_data;\n}\n\n/*\n\n将sensor坐标变换为机器坐标。\n*/\nRangeData TransformRangeData(const RangeData& range_data,\n                             const transform::Rigid3f& transform) {\n  return RangeData{\n      transform * range_data.origin,\n      TransformPointCloud(range_data.returns, transform),\n      TransformPointCloud(range_data.misses, transform),\n  };\n}\n\n/*\n不在给定的z轴范围内的点云删除。\n*/\nRangeData CropRangeData(const RangeData& range_data, const float min_z,\n                        const float max_z) {\n  return RangeData{range_data.origin, Crop(range_data.returns, min_z, max_z),\n                   Crop(range_data.misses, min_z, max_z)};\n}\n\n/*\n压缩，有精度丢失。\n*/\nCompressedRangeData Compress(const RangeData& range_data) {\n  return CompressedRangeData{\n      range_data.origin, CompressedPointCloud(range_data.returns),\n      CompressedPointCloud(range_data.misses),\n  };\n}\n/*\n解压缩，有精度丢失\n*/\nRangeData Decompress(const CompressedRangeData& compressed_range_data) {\n  return RangeData{compressed_range_data.origin,\n                   compressed_range_data.returns.Decompress(),\n                   compressed_range_data.misses.Decompress()};\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/range_data.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_RANGE_DATA_H_\n#define CARTOGRAPHER_SENSOR_RANGE_DATA_H_\n\n#include \"cartographer/common/port.h\"\n#include \"cartographer/sensor/compressed_point_cloud.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/proto/sensor.pb.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\n/*\nRangeData:\n数据成员包括\n1),原始位置,{x0,y0,z0}\n2),返回点云,{x,y,z}\n3),缺失点云,标识free space.\n*/\n// Rays begin at 'origin'. 'returns' are the points where obstructions were\n// detected. 'misses' are points in the direction of rays for which no return\n// was detected, and were inserted at a configured distance. It is assumed that\n// between the 'origin' and 'misses' is free space.\nstruct RangeData {\n  Eigen::Vector3f origin;//{x0,y0,z0},sensor坐标。\n  PointCloud returns;    //反射位置{x,y,z}，表征有物体反射。\n  PointCloud misses;     //无反射,自由空间\n};\n\n// Converts 'range_data' to a proto::RangeData. 序列化\nproto::RangeData ToProto(const RangeData& range_data);\n\n// Converts 'proto' to a RangeData. 反序列化\nRangeData FromProto(const proto::RangeData& proto);\n\n//对数据进行3d变换，转换为机器坐标\nRangeData TransformRangeData(const RangeData& range_data,\n                             const transform::Rigid3f& transform);\n\n//根据min_z和max_z把不在z轴范围内的点云丢弃，剪裁到给定范围\n// Crops 'range_data' according to the region defined by 'min_z' and 'max_z'.\nRangeData CropRangeData(const RangeData& range_data, float min_z, float max_z);\n\n//压缩后的点云数据\n// Like RangeData but with compressed point clouds. The point order changes\n// when converting from RangeData.\nstruct CompressedRangeData {\n  Eigen::Vector3f origin;\n  CompressedPointCloud returns;\n  CompressedPointCloud misses;\n};\n\nCompressedRangeData Compress(const RangeData& range_data);\n\nRangeData Decompress(const CompressedRangeData& compressed_range_Data);\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_RANGE_DATA_H_\n"
  },
  {
    "path": "sensor/range_data_test.cc",
    "content": "\n#include \"cartographer/sensor/range_data.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n\nusing ::testing::Contains;\nusing ::testing::PrintToString;\n\n// Custom matcher for Eigen::Vector3f entries.\nMATCHER_P(ApproximatelyEquals, expected,\n          string(\"is equal to \") + PrintToString(expected)) {\n  return (arg - expected).isZero(0.001f);\n}\n\nTEST(RangeDataTest, Compression) {\n  const std::vector<Eigen::Vector3f> returns = {Eigen::Vector3f(0, 1, 2),\n                                                Eigen::Vector3f(4, 5, 6),\n                                                Eigen::Vector3f(0, 1, 2)};\n   //构造一个测量数据\n  const RangeData range_data = {\n      Eigen::Vector3f(1, 1, 1), returns, {Eigen::Vector3f(7, 8, 9)}};\n  //压缩再解压，减少空间占用。\n  const RangeData actual = Decompress(Compress(range_data));\n\n//isApprox():true if *this is approximately equal to other, within the precision determined by prec.\n //压缩前后，精度比较\n  EXPECT_TRUE(actual.origin.isApprox(Eigen::Vector3f(1, 1, 1), 1e-6));\n\n\n  EXPECT_EQ(3, actual.returns.size());\n\n  EXPECT_EQ(1, actual.misses.size());\n\n  EXPECT_TRUE(actual.misses[0].isApprox(Eigen::Vector3f(7, 8, 9), 0.001f));\n\n  // Returns will be reordered, so we compare in an unordered manner.\n  EXPECT_EQ(3, actual.returns.size());\n\n  //是否包含给定值，returns是乱序的。所以使用Contains()函数。\n  EXPECT_THAT(actual.returns,\n              Contains(ApproximatelyEquals(Eigen::Vector3f(0, 1, 2))));\n  EXPECT_THAT(actual.returns,\n              Contains(ApproximatelyEquals(Eigen::Vector3f(4, 5, 6))));\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n"
  },
  {
    "path": "sensor/voxel_filter.cc",
    "content": "\n#include \"cartographer/sensor/voxel_filter.h\"\n\n#include <cmath>\n\n#include \"cartographer/common/math.h\"\n\nnamespace cartographer {\nnamespace sensor {\n\nnamespace {\n\n/*\n对点云数据，根据指定范围max_range进行l2距离过滤，\n形参：   points，\n返回值： points\n*/\nPointCloud FilterByMaxRange(const PointCloud& point_cloud,\n                            const float max_range) {\n  PointCloud result;\n  for (const Eigen::Vector3f& point : point_cloud) {\n    if (point.norm() <= max_range) { // l2范数,向量各个元素平方和的1/2次方\n      result.push_back(point);\n    }\n  }\n  return result;\n}\n\n/*\n自适应滤波函数。\n形参1：滤波器option，\n形参2：待滤波的points\n返回值：points\n\nvoxels_：表征一系列的Cell，每个Cell有一个size，piont以size为间距分布，实现稀疏表示。\nCell的数量和即为稀疏表达的point的数量和。\n\n滤波实现：\n1，调整edge的大小，以使每个Cell占据的points个数在min_num_points左右,以实现稀疏表达。\n2，按照l2范数测度。\n3，\n\n*/\nPointCloud AdaptivelyVoxelFiltered(\n    const proto::AdaptiveVoxelFilterOptions& options,\n    const PointCloud& point_cloud) {\n  if (point_cloud.size() <= options.min_num_points()) {\n    // 'point_cloud' is already sparse enough.\n    return point_cloud;\n  }\n\n  //max_length即为体素edge的max_size。下面以max_size为间距，稀疏提取point。\n  PointCloud result = VoxelFiltered(point_cloud, options.max_length());\n\n  if (result.size() >= options.min_num_points()) {\n    //即使以max_size为间距也超过min_num.则直接返回。\n    // Filtering with 'max_length' resulted in a sufficiently dense point cloud.\n    return result;\n  }\n  // Search for a 'low_length' that is known to result in a sufficiently\n  // dense point cloud. We give up and use the full 'point_cloud' if reducing\n  // the edge length by a factor of 1e-2 is not enough.\n\n/*\n以max_size为间距没有超过min_num.则继续减小edge大小，使得以low_length为间距。\n\nlow_length是每个Cell的长度。\n每个Cell“占据”的points不能超过min_num.\n二分查找法：\nfor()循环每次将edge减半\n对每一个low_length:\nwhile()循环控制edge不能小于10%\n满足要求则记录，否则将high折半。\n\n结果：\n每个Cell最少“占据”min_num个points。\n*/\n  for (float high_length = options.max_length();\n       high_length > 1e-2f * options.max_length(); high_length /= 2.f) {\n    float low_length = high_length / 2.f;\n    result = VoxelFiltered(point_cloud, low_length);//edge大小为low_length\n\n    if (result.size() >= options.min_num_points()) {\n      // Binary search to find the right amount of filtering. 'low_length' gave\n      // a sufficiently dense 'result', 'high_length' did not. We stop when the\n      // edge length is at most 10% off.\n      while ((high_length - low_length) / low_length > 1e-1f) {\n        const float mid_length = (low_length + high_length) / 2.f;\n        const PointCloud candidate = VoxelFiltered(point_cloud, mid_length);\n        if (candidate.size() >= options.min_num_points()) {\n          low_length = mid_length;\n          result = candidate;\n        } else {\n          high_length = mid_length;\n        }\n      }\n      return result;\n    }\n  }\n  return result;\n}\n\n}  // namespace\n\n/*\n形参1：pionts\n形参2：size，体素大小\n返回值：稀疏点云，points\n*/\nPointCloud VoxelFiltered(const PointCloud& point_cloud, const float size) {\n  VoxelFilter voxel_filter(size);//创建滤波器\n  voxel_filter.InsertPointCloud(point_cloud);\n  return voxel_filter.point_cloud();\n}\n\nVoxelFilter::VoxelFilter(const float size) : voxels_(size) {}\n\n//插入点云\nvoid VoxelFilter::InsertPointCloud(const PointCloud& point_cloud) {\n  for (const Eigen::Vector3f& point : point_cloud) {\n/*对每一个点云point,如果落在Cell中，则插入piont，并且后续不在接收落在此Cell的point。\n\n[point1] ,[point2],[point3],[point4]....\n[point11] ,[point12],[point13],[point14]....\n*/\n    auto* const value = voxels_.mutable_value(voxels_.GetCellIndex(point));\n    if (*value == 0) {\n      point_cloud_.push_back(point);\n      *value = 1;\n    }\n  }\n}\n\n//表征Cell边界的point\nconst PointCloud& VoxelFilter::point_cloud() const { return point_cloud_; }\n\n\n/*\nsparse_pose_graph.lua/trajectory_builder_2d.lua\nadaptive_voxel_filter = {\n      max_length = 0.9,\n      min_num_points = 100,\n      max_range = 50.,\n    },\n\n2d:adaptive_voxel_filter = {\n    max_length = 0.5,\n    min_num_points = 200,\n    max_range = 50.,\n  },\n\n根据.lua文件选项，配置options滤波器参数。\n\n*/\nproto::AdaptiveVoxelFilterOptions CreateAdaptiveVoxelFilterOptions(\n    common::LuaParameterDictionary* const parameter_dictionary) {\n  proto::AdaptiveVoxelFilterOptions options;\n  options.set_max_length(parameter_dictionary->GetDouble(\"max_length\"));\n  options.set_min_num_points(\n      parameter_dictionary->GetNonNegativeInt(\"min_num_points\"));\n  options.set_max_range(parameter_dictionary->GetDouble(\"max_range\"));\n  return options;\n}\n\n//构造函数\nAdaptiveVoxelFilter::AdaptiveVoxelFilter(\n    const proto::AdaptiveVoxelFilterOptions& options)\n    : options_(options) {}\n\n\n//将point进行自适应滤波，返回point。\nPointCloud AdaptiveVoxelFilter::Filter(const PointCloud& point_cloud) const {\n  return AdaptivelyVoxelFiltered(\n      options_, FilterByMaxRange(point_cloud, options_.max_range()));\n}\n\n}  // namespace sensor\n}  // namespace cartographer\n\n\n/*\n  3d：\n  high_resolution_adaptive_voxel_filter = {\n    max_length = 2.,\n    min_num_points = 150,\n    max_range = 15.,\n  },\n\n  low_resolution_adaptive_voxel_filter = {\n    max_length = 4.,\n    min_num_points = 200,\n    max_range = MAX_3D_RANGE,\n  }, \n\n*/"
  },
  {
    "path": "sensor/voxel_filter.h",
    "content": "\n#ifndef CARTOGRAPHER_SENSOR_VOXEL_FILTER_H_\n#define CARTOGRAPHER_SENSOR_VOXEL_FILTER_H_\n\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/mapping_3d/hybrid_grid.h\"\n#include \"cartographer/sensor/point_cloud.h\"\n#include \"cartographer/sensor/proto/adaptive_voxel_filter_options.pb.h\"\n\n/*\n\nmessage AdaptiveVoxelFilterOptions { \n  optional float max_length = 1; \n  optional float min_num_points = 2;\n  optional float max_range = 3;\n}\n\n2d:\nadaptive_voxel_filter = {\n      max_length = 0.9,     //voxel_的大小edge的最大值\n      min_num_points = 100, //voxel_最多“占据”的points数量\n      max_range = 50.,\n    },\n\n\nVoxel, 三维像素,体素\n3D模型体素化：http://blog.csdn.net/bugrunner/article/details/5527332\n\n */\nnamespace cartographer {\nnamespace sensor {\n\n\n//返回过滤后的点云,size是体素的长度。\n// Returns a voxel filtered copy of 'point_cloud' where 'size' is the length\n// a voxel edge.\nPointCloud VoxelFiltered(const PointCloud& point_cloud, float size);\n\n\n/*\n\n体素滤波器,对每一个体素voxel,采用第一个point代替所有的points\n\nVoxelFilter:不可拷贝/不可赋值\n默认构造函数指定体素边界大小\n\n\n\n*/\n// Voxel filter for point clouds. For each voxel, the assembled point cloud\n// contains the first point that fell into it from any of the inserted point\n// clouds.\nclass VoxelFilter {\n public:\n  // 'size' is the length of a voxel edge.\n  explicit VoxelFilter(float size);\n\n  VoxelFilter(const VoxelFilter&) = delete;\n  VoxelFilter& operator=(const VoxelFilter&) = delete;\n\n  //将点云插入体素网格中\n  // Inserts a point cloud into the voxel filter.\n  void InsertPointCloud(const PointCloud& point_cloud);\n\n  //返回表达occupied 体素的点云\n  // Returns the filtered point cloud representing the occupied voxels.\n  const PointCloud& point_cloud() const;\n\n private:\n  mapping_3d::HybridGridBase<uint8> voxels_;//以体素表示的网格，Grid/Pixel\n  PointCloud point_cloud_;  //网格内的点云\n};\n\n/*\n自适应体素滤波,不可拷贝/赋值\n\n*/\nproto::AdaptiveVoxelFilterOptions CreateAdaptiveVoxelFilterOptions(\n    common::LuaParameterDictionary* const parameter_dictionary);\n\nclass AdaptiveVoxelFilter {\n public:\n  //根据配置文件设置自适应体素滤波的options\n  explicit AdaptiveVoxelFilter(\n      const proto::AdaptiveVoxelFilterOptions& options);\n\n  AdaptiveVoxelFilter(const AdaptiveVoxelFilter&) = delete;\n  AdaptiveVoxelFilter& operator=(const AdaptiveVoxelFilter&) = delete;\n\n  //对点云进行体素滤波,返回过滤后的点云\n  PointCloud Filter(const PointCloud& point_cloud) const;\n\n private:\n  const proto::AdaptiveVoxelFilterOptions options_;\n};\n\n}  // namespace sensor\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_SENSOR_VOXEL_FILTER_H_\n"
  },
  {
    "path": "sensor/voxel_filter_test.cc",
    "content": "\n#include \"cartographer/sensor/voxel_filter.h\"\n\n#include <cmath>\n\n#include \"gmock/gmock.h\"\n\nnamespace cartographer {\nnamespace sensor {\nnamespace {\n\nusing ::testing::ContainerEq;\n\nTEST(VoxelFilterTest, ReturnsTheFirstPointInEachVoxel) {\n  PointCloud point_cloud = {{0.f, 0.f, 0.f},\n                            {0.1f, -0.1f, 0.1f},\n                            {0.3f, -0.1f, 0.f},\n                            {0.f, 0.f, 0.1f}};\n  EXPECT_THAT(VoxelFiltered(point_cloud, 0.3f),//按照max_range滤波,\n              ContainerEq(PointCloud{point_cloud[0], point_cloud[2]}));\n}\n\n}  // namespace\n}  // namespace sensor\n}  // namespace cartographer\n\n/*\nContainerEq:\nThe same as Eq(container) except that the failure message also \nincludes which elements are in one container but not the other.\n\n*/"
  },
  {
    "path": "transform/proto/transform.proto",
    "content": "// Copyright 2016 The Cartographer Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage cartographer.transform.proto;\n\n// All coordinates are expressed in the right-handed Cartesian coordinate system\n// used by Cartographer (x forward, y left, z up). Message names are chosen to\n// mirror those used in the Eigen library.\n//所有的坐标都是右手旋转笛卡儿坐标;\n\nmessage Vector2d {\n  optional double x = 1;\n  optional double y = 2;\n}\n\nmessage Vector2f {\n  optional float x = 1;\n  optional float y = 2;\n}\n\nmessage Vector3d {\n  optional double x = 1;\n  optional double y = 2;\n  optional double z = 3;\n}\n\nmessage Vector3f {\n  optional float x = 1;\n  optional float y = 2;\n  optional float z = 3;\n}\n\nmessage Quaterniond {\n  optional double x = 1;\n  optional double y = 2;\n  optional double z = 3;\n  optional double w = 4;\n}\n\nmessage Quaternionf {\n  optional float x = 1;\n  optional float y = 2;\n  optional float z = 3;\n  optional float w = 4;\n}\n\nmessage Rigid2d {\n  optional Vector2d translation = 1;\n  optional double rotation = 2;\n}\n\nmessage Rigid2f {\n  optional Vector2f translation = 1;\n  optional float rotation = 2;\n}\n\nmessage Rigid3d {\n  optional Vector3d translation = 1;\n  optional Quaterniond rotation = 2;\n}\n\nmessage Rigid3f {\n  optional Vector3f translation = 1;\n  optional Quaternionf rotation = 2;\n}\n"
  },
  {
    "path": "transform/rigid_transform.cc",
    "content": "\n\n#include \"cartographer/transform/rigid_transform.h\"\n\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace transform {\n\nnamespace {\n//typedef Matrix< double , 3 , 1> Eigen::Vector3d\n\n//根据lua字典获取vector<double>,再转换成Vector3d\nEigen::Vector3d TranslationFromDictionary(  \n    common::LuaParameterDictionary* dictionary) {\n  const std::vector<double> translation = dictionary->GetArrayValuesAsDoubles();\n  CHECK_EQ(3, translation.size()) << \"Need (x, y, z) for translation.\";\n  return Eigen::Vector3d(translation[0], translation[1], translation[2]);\n}\n\n}  // namespace\n\n/*\n利用AngleAxisd可以根据3个坐标轴旋转.构成四元数\nRepresents a 3D rotation as a rotation angle around an arbitrary 3D axis.\n\nhttps://stackoverflow.com/questions/21412169/creating-a-rotation-matrix-with-pitch-yaw-roll-using-eigen\nhttp://eigen.tuxfamily.org/dox/classEigen_1_1AngleAxis.html\n\n返回根据roll,pathch和yaw构成的4元数\n*/\nEigen::Quaterniond RollPitchYaw(const double roll, const double pitch,\n                                const double yaw) {\n  const Eigen::AngleAxisd roll_angle(roll, Eigen::Vector3d::UnitX());\n  //构造一个AngleAxisd对象\n  const Eigen::AngleAxisd pitch_angle(pitch, Eigen::Vector3d::UnitY());//同上\n  const Eigen::AngleAxisd yaw_angle(yaw, Eigen::Vector3d::UnitZ());//同上\n  return yaw_angle * pitch_angle * roll_angle; //返回4元数\n}\n\n\n\n/*\n从assets_writer_backpack_2d.lua取得如：\n\nXY_TRANSFORM =  {\n  translation = { 0., 0., 0. },\n  rotation = { 0., -math.pi / 2., 0., },\n}\n\ntranslation:平移矩阵\nrotation:旋转矩阵,w,x,y,z\n*/\ntransform::Rigid3d FromDictionary(common::LuaParameterDictionary* dictionary) {\n  const Eigen::Vector3d translation =\n      TranslationFromDictionary(dictionary->GetDictionary(\"translation\").get());\n\n  auto rotation_dictionary = dictionary->GetDictionary(\"rotation\");\n  if (rotation_dictionary->HasKey(\"w\")) {\n    const Eigen::Quaterniond rotation(rotation_dictionary->GetDouble(\"w\"),\n                                      rotation_dictionary->GetDouble(\"x\"),\n                                      rotation_dictionary->GetDouble(\"y\"),\n                                      rotation_dictionary->GetDouble(\"z\"));\n    CHECK_NEAR(rotation.norm(), 1., 1e-9);\n    return transform::Rigid3d(translation, rotation);\n  } else {\n    const std::vector<double> rotation =\n        rotation_dictionary->GetArrayValuesAsDoubles();\n    CHECK_EQ(3, rotation.size()) << \"Need (roll, pitch, yaw) for rotation.\";\n    return transform::Rigid3d(\n        translation, RollPitchYaw(rotation[0], rotation[1], rotation[2]));\n  }\n}\n\n}  // namespace transform\n}  // namespace cartographer\n"
  },
  {
    "path": "transform/rigid_transform.h",
    "content": "\n#ifndef CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_H_\n#define CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_H_\n\n#include <iostream>\n#include <string>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/common/port.h\"\n\nnamespace cartographer {\nnamespace transform {\n\n/*\n几个名词细微的区别 :\n\ntransformation:变换\ntranslation:平移\nrotation:旋转\nscaling:缩放\ncounter clock wise rotation:逆时针旋转，counter：反向\nIdentity():单位矩阵,不同于线性代数的“单位”。在不同的语义下，含义会不同。\n\n\nRigid2是对二维刚性变换【旋转与平移】的封装,SE2.对应论文公式(1):\n\n\nRigid2表现为先将vector[x0,y0]旋转θ角度得到[x',y']，然后再叠加[dx,dy]得到[x1,y1]\n即:\nx1=x'+dx,\ny1=y'+dy,\n\n\n模板参数必须是浮点型。(int类型为错误用法)\n\n提供2个操作：\n1，按照指定角度θ逆时针旋转。此时dx,dy=0\n2，按照指定平移向量dx,dy平移. 此时θ=0\n\n静态成员函数包括：\n1,Rotation()\n2,Translation()\n3,Identity()\n\nTranslation()指定对象的2D translation（2D平移）。\n第一个参数对应X轴，第二个参数对应Y轴。默认是单位变换。　　　\n\n\n[x'       [cosθ , -sinθ           [ x\n y']   =   sinθ ,  cosθ ]   *       y ]\n\n*/\ntemplate <typename FloatType> \n//FloatType不能是int类型，不然Rotation2D的cos0==0,丢失角度信息，无法得到正确结果\nclass Rigid2 {\n public:\n  using Vector = Eigen::Matrix<FloatType, 2, 1>;//2行1列,p142\n  using Rotation2D = Eigen::Rotation2D<FloatType>;\n\n// 无参构造函数,平移向量为单位向量[1,0]^t,旋转角度为0,debugstring()输出:[1,0,0]\n  Rigid2()\n      : translation_(Vector::Identity()), rotation_(Rotation2D::Identity()) {}\n\n \n//Rotation2D(double ): Construct a 2D counter clock wise rotation from the angle a in radian. \n\n//双参构造函数，给定平移向量[dx,dy]和旋转角度0，进行旋转变换:  \n  Rigid2(const Vector& translation, const Rotation2D& rotation)\n      : translation_(translation), rotation_(rotation) {}\n//同上，给定旋转角度θ,double是弧度值。\n  Rigid2(const Vector& translation, const double rotation)\n      : translation_(translation), rotation_(rotation) {}\n\n//类的静态成员函数，返回Rigid2,debugstring()是[0,0,θ ]\n  static Rigid2 Rotation(const double rotation) { //给定旋转角度θ \n    return Rigid2(Vector::Zero(), rotation);\n  }\n//同上\n  static Rigid2 Rotation(const Rotation2D& rotation) {//给定旋转矩阵,角度为θ \n    return Rigid2(Vector::Zero(), rotation);\n  }\n//旋转角度是单位矩阵,即θ为0 ,[dx,dy,0]\n  static Rigid2 Translation(const Vector& vector) { \n\n    return Rigid2(vector, Rotation2D::Identity());//θ为0\n  }\n\n//静态成员函数，全为0：\n  static Rigid2<FloatType> Identity() {    //返回Rigid2,[0,0,0]\n    return Rigid2<FloatType>(Vector::Zero(), Rotation2D::Identity());\n  }\n\n/*为什么要加translation_.template：\n参见成员模板函数的写法：\nhttps://stackoverflow.com/questions/29754251/issue-casting-c-eigenmatrix-types-via-templates\nhttps://stackoverflow.com/questions/12676190/how-to-call-a-template-member-function\nhttps://stackoverflow.com/questions/4942703/why-do-i-get-an-error-trying-to-call-a-template-member-function-with-an-explicit?answertab=votes\n\ncast()按照指定的参数类型将数据成员进行类型转换：\nRigid2数据成员原本是double,对象rigid2转换成float可调用：\nrigid2.cast<float>()\n*/\n  template <typename OtherType>\n  Rigid2<OtherType> cast() const {\n    return Rigid2<OtherType>(translation_.template cast<OtherType>(),\n                             rotation_.template cast<OtherType>());\n  }\n\n//返回平移向量,[dx,dy]\n  const Vector& translation() const { return translation_; }\n/*返回旋转矩阵:\n[cosθ , -sinθ  \n sinθ ,  cosθ ] */\n  Rotation2D rotation() const { return rotation_; }\n\n//归一化角度 ,弧度[-pi;pi]\n  double normalized_angle() const {\n    return common::NormalizeAngleDifference(rotation().angle());\n    //方位角θ.以弧度计。\n  }\n\n/*\n平移变换矩阵的逆矩阵与原来的平移量相同，但是方向相反。\n旋转变换矩阵的逆矩阵与原来的旋转轴相同,但是角度相反。\n\nRotation2D重载了operator*(maxtrix<sclar,2,1>).故可以使用乘法。\n乘以rotation：[dx,dy]是旋转了θ以后的[dx,dy]\n而求逆是反方向旋转θ，故需要求[dx,dy]在旋转θ之前的方向的投影。\ntranslation加负号的原因：x1=x'+dx，所以x'=x1-dx\n\n----公式推导----------\n\nP'=R*P+t;\nP=R^(-1)*(P'-t)=R^(-1)* P'-R^(-1)*t.\n--------------------\n\n*/\n  Rigid2 inverse() const {\n    const Rotation2D rotation = rotation_.inverse(); //取负,得到-θ。即上面公式中的R^(-1)\n    const Vector translation = -(rotation * translation_);//得到[-dx',-dy']。即上面公式中的-R^(-1)*t.\n    return Rigid2(translation, rotation); \n    //返回一个新2维刚性变换矩阵.[-dx’,-dy’,-θ]。即上面公式中的R^(-1)和-R^(-1)*t。\n  }\n\n  string DebugString() const {\n    string out;\n    out.append(\"{ t: [\");\n    out.append(std::to_string(translation().x()));\n    out.append(\", \");\n    out.append(std::to_string(translation().y()));\n    out.append(\"], r: [\");\n    out.append(std::to_string(rotation().angle()));\n    out.append(\"] }\");\n    return out;\n  }\n\n private: \n\n//2行1列 的矩阵.平移向量[dx,dy]\n  Vector translation_;\n\n//旋转角度。\n  Rotation2D rotation_;//Eigen::Rotation2D,方向角,旋转变换 [θ]\n\n/*\nRotation2D介绍：\nRotation2D 是二维旋转里根据逆时针旋转θ角而生成的旋转矩阵:\n[cosθ , -sinθ     \n sinθ ,  cosθ ]\n\n  */\n};\n\n/*\n定义 * 乘法操作符:2个Rigid2相乘,得到第三个Rigid2,\n等效于连续变换2次\n实现细节：\n1，最终的[dx'',dy'']等于lhs在rhs方向上的投影[dx',dy']加上rhs自身的[dx,dy]\n2，角度相加(等效于旋转矩阵相乘)\n\n------公式推导--------\n\nP'=R1*P+t1;\n\nP''=R2*P'+t2,即\n\nP''=R2(R1*P+t1)+t2=R2*R1*P+R2*t1+t2.\n\n--------------------\n\n*/\ntemplate <typename FloatType>\nRigid2<FloatType> operator*(const Rigid2<FloatType>& lhs,\n                            const Rigid2<FloatType>& rhs) {\n  return Rigid2<FloatType>(\n      lhs.rotation() * rhs.translation() + lhs.translation(),//对应R2*t1+t2.\n      lhs.rotation() * rhs.rotation());                      //对应R2*R1\n}\n\n/*\n该函数即为公式(1):\n\n参数1：【旋转+平移】矩阵\n参数2：要进行上述变换的point。\n\n返回值：刚性变换后的point。\n等效于对某个2维point旋转0角，再叠加平移矩阵[dx,dy]。\n\n\n实现：\n1，先对vector[x0,y0]自身旋转0，得到[x',y'].\n2，[x',y']再加上dx,dy.得到vector[x1,y1]\n\n\n--------公式推导---------\n\nP'=R*P+t;\n\n------------------------\n\n*/\ntemplate <typename FloatType>\ntypename Rigid2<FloatType>::Vector operator*(\n    const Rigid2<FloatType>& rigid,\n    const typename Rigid2<FloatType>::Vector& point) {\n  return rigid.rotation() * point + rigid.translation();\n  /*\nRotation2D的乘法原型：\nVector2 Eigen::Rotation2D< Scalar >::operator*  ( const Vector2 &   vec ) const\n:\nApplies the rotation to a 2D vector\n\n  */\n}\n\n//定义 << 输出运算符\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os,\n                         const cartographer::transform::Rigid2<T>& rigid) {\n  os << rigid.DebugString();\n  return os;\n}\n\n//模板特化\nusing Rigid2d = Rigid2<double>;\nusing Rigid2f = Rigid2<float>;\n\n\n\n\n\n\n\n/*\n预备概念：\n\n四元数,通常用quaternion来计算3D物体的旋转角度，与Matrix相比，quaternion更加高效。\n在数学上，quaternion表示复数w+xi+yj+zk，其中i,j,k都是虚数单位。\n四元数也可以表示为[w,iv].其中虚数v=[x,y,z]，用四元数描述旋转需要的存储空间很小。\n更为关键的是可以使用被称为球面线性插值（Slerp Algorithm）的方法对四元数进行插值运算，\n从而解决了平滑旋转的插值问题。\n\n3D旋转可以分解为绕x,y,z三个方向的旋转的叠加。\n而Eigen的AngleAxis表示3D旋转绕任意轴axis的旋转。\n并且与MatrixBase::Unit{X,Y,Z}组合,AngleAxis 能非常容易的得到欧垃角。\n\n*/\n\n\n\n\n/*\nRigid3是三维刚性变换.SE3\n该类含义2个数据成员：\n\nVector translation_;\nQuaternion rotation_;\n\n1),3个构造函数分别初始化平移矩阵和旋转矩阵。\n\n2),3个静态成员函数分别按照给定方式旋转.\n   Rotation(),Translation(),Identity()\n构造函数应为单位四元数\n\n*/\n\ntemplate <typename FloatType>\nclass Rigid3 {\n public:\n  using Vector = Eigen::Matrix<FloatType, 3, 1>; //3行1列的矩阵 \n  using Quaternion = Eigen::Quaternion<FloatType>;//四元数,见游戏引擎架构p156\n  using AngleAxis = Eigen::AngleAxis<FloatType>;  //轴角\n\n//构造函数，默认[1,0,0]和[1,0,0,0]。\n  Rigid3()\n      : translation_(Vector::Identity()), rotation_(Quaternion::Identity()) {}\n//构造函数，提供平移向量[dx,dy,dz]和四元数\n  Rigid3(const Vector& translation, const Quaternion& rotation)\n      : translation_(translation), rotation_(rotation) {}\n//构造函数，提供平移向量[dx,dy,dz]和轴角\n  Rigid3(const Vector& translation, const AngleAxis& rotation)\n      : translation_(translation), rotation_(rotation) {}\n\n//静态成员函数.[dx,dy,dz]为0,只绕轴角旋转。\n  static Rigid3 Rotation(const AngleAxis& angle_axis) {\n    return Rigid3(Vector::Zero(), Quaternion(angle_axis));\n  }\n//静态成员函数, 只旋转，不平移。\n  static Rigid3 Rotation(const Quaternion& rotation) {\n    return Rigid3(Vector::Zero(), rotation);\n  }\n//不旋转,只平移[dx,dy,dz]\n  static Rigid3 Translation(const Vector& vector) {\n    return Rigid3(vector, Quaternion::Identity());//[x,y,z,w]为[0,0,0,1]\n  }\n\n//[0,0,0]和[1,0,0,0]。\n  static Rigid3<FloatType> Identity() {\n    return Rigid3<FloatType>(Vector::Zero(), Quaternion::Identity());\n  }\n\n//类型转换\n  template <typename OtherType>\n  Rigid3<OtherType> cast() const {\n    return Rigid3<OtherType>(translation_.template cast<OtherType>(),\n                             rotation_.template cast<OtherType>());\n  }\n\n//获取数据成员\n  const Vector& translation() const { return translation_; }\n  const Quaternion& rotation() const { return rotation_; }\n\n/*求逆,即逆方向旋转和平移。\n细节：\n1),四元数的逆是共轭。\n2),求[-dx',-dy',-dz']\n\n----公式推导----------\n\nP'=R*P+t;\nP=R^(-1)*(P'-t)=R^(-1)* P'-R^(-1)*t.\n--------------------\n\n*/\n  Rigid3 inverse() const {\n    const Quaternion rotation = rotation_.conjugate();     //R^(-1)\n    const Vector translation = -(rotation * translation_); //-R^(-1)*t.\n    return Rigid3(translation, rotation);\n  }\n\n  string DebugString() const {\n    string out;\n    out.append(\"{ t: [\");\n    out.append(std::to_string(translation().x()));\n    out.append(\", \");\n    out.append(std::to_string(translation().y()));\n    out.append(\", \");\n    out.append(std::to_string(translation().z()));\n    out.append(\"], q: [\");\n    out.append(std::to_string(rotation().w()));\n    out.append(\", \");\n    out.append(std::to_string(rotation().x()));\n    out.append(\", \");\n    out.append(std::to_string(rotation().y()));\n    out.append(\", \");\n    out.append(std::to_string(rotation().z()));\n    out.append(\"] }\");\n    return out;\n  }\n\n private:\n  Vector translation_;//x,y,z方向上的平移向量[dx,dy,dz]\n  Quaternion rotation_;//四元数。旋转表达。\n};\n\n//乘法操作：Rigid3*Rigid3,即连续2次刚性变换，得到Rigid3\n/*\n\n------公式推导--------\n\nP'=R1*P+t1;\n\nP''=R2*P'+t2,即\n\nP''=R2(R1*P+t1)+t2=R2*R1*P+R2*t1+t2.\n\n--------------------\n\n*/\ntemplate <typename FloatType>\nRigid3<FloatType> operator*(const Rigid3<FloatType>& lhs,\n                            const Rigid3<FloatType>& rhs) {\n  return Rigid3<FloatType>(\n      lhs.rotation() * rhs.translation() + lhs.translation(),  //R2*t1+t2\n      (lhs.rotation() * rhs.rotation()).normalized());         //R2*R1\n}\n\n/*\n\n\n对点p(x,y,z)进行刚性变换。得到p'\n\n--------公式推导---------\n\nP'=R*P+t;\n\n------------------------\n*/\ntemplate <typename FloatType>\ntypename Rigid3<FloatType>::Vector operator*(\n    const Rigid3<FloatType>& rigid,\n    const typename Rigid3<FloatType>::Vector& point) {\n  return rigid.rotation() * point + rigid.translation();\n}\n\n//重载<< 输出运算符\n// This is needed for gmock.\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os,\n                         const cartographer::transform::Rigid3<T>& rigid) {\n  os << rigid.DebugString();\n  return os;\n}\n\n//特化\nusing Rigid3d = Rigid3<double>;\nusing Rigid3f = Rigid3<float>;\n\n/*\n-----------左手---------------\n游戏引擎架构,p126,姿态角（Euler角）:\npitch是围绕X轴旋转，也叫做俯仰角\nyaw是围绕Y轴旋转，也叫偏航角\nroll是围绕Z轴旋转，也叫翻滚角\n\n\n-----------右手------------\n右手笛卡尔坐标系绕x,y和z轴的旋转分别叫做roll, pitch 和 yaw 旋转。\ngoogle采用的就是右手:roll, pitch, yaw. \n\n绕x轴:θx 是 roll 角，和右手螺旋的方向相反（在yz平面顺时针）\n绕y轴:θy 是 pitch 角，和右手螺旋的方向相反（在zx平面顺时针）。\n绕z轴:θz 是yaw 角，和右手螺旋的方向相反（在xy平面顺时针）。\n\nref:https://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5\n*/\n\n//返回根据roll,pathch和yaw构成的4元数\n// Converts (roll, pitch, yaw) to a unit length quaternion. Based on the URDF\n// specification http://wiki.ros.org/urdf/XML/joint.\nEigen::Quaterniond RollPitchYaw(double roll, double pitch, double yaw);\n\n/*\n\n从assets_writer_backpack_2d.lua取得如：\n\nXY_TRANSFORM =  {\n  translation = { 0., 0., 0. },\n  rotation = { 0., -math.pi / 2., 0., },\n}\n\n*/\n// Returns an transform::Rigid3d given a 'dictionary' containing 'translation'\n// (x, y, z) and 'rotation' which can either we an array of (roll, pitch, yaw)\n// or a dictionary with (w, x, y, z) values as a quaternion.\nRigid3d FromDictionary(common::LuaParameterDictionary* dictionary);\n\n}  // namespace transform\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_H_\n\n/*\n\ncoefficient-wise operations系数操作\n\nthe Array class provides an easy way to perform coefficient-wise operations,\nwhich might not have a linear algebraic meaning,\nsuch as adding a constant to every coefficient in the array or \nmultiplying two arrays coefficient-wise.\n*/\n\n\n\n/*\n \n3D旋转的简单推导：\n可以把一个3D旋转转换为绕基本坐标轴的旋转，即绕三个坐标值x、y、z的旋转。\n\n一）绕X轴的旋转：x不变。围绕x坐标画圆。yoz平面，方向是顺时针(与右手方向相反)\n点P(x,y,z)绕x轴旋转θ角得到点P’(x’,y’,z’)。由于是绕x轴进行的旋转，\n因此x坐标保持不变，y和z组成的yoz（o是坐标原点）平面上进行的是一个二维的旋转\n\np=[1,0,0],q=[0,1,0],r=[0,0,1]\n三个坐标轴单位向量p,q,r旋转后：\np'=[1, 0,    0]\nq'=[0,cosθ,-sinθ]\nr'=[0,sinθ,cosθ]\n所以:\nx′=x \ny′=ycosθ−zsinθ \nz′=ysinθ+zcosθ \n\n二）绕Y轴旋转：Y坐标保持不变，ZOX平面\np'=[cosθ, 0, sinθ]\nq'=[  0,  1,  0]\nr'=[-sinθ,0,cosθ]\n所以：\nx′=xcosθ+zsinθ \ny′=y \nz′=−xsinθ+zcosθ\n\n三）绕Z轴旋转：Z坐标保持不变，xoy平面\np'=[sinθ,-cosθ,0]\nq'=[sinθ, cosθ,0]\nr'=[0,0,1]\n所以:\nx′=xcosθ-ysinθ \ny′=xsinθ+ycosθ \nz′=z \n\n\nref:\nhttps://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%AC\nhttps://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%89%E7%BE%A4\nhttps://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5\n*/"
  },
  {
    "path": "transform/rigid_transform_le2_grid2_test.cc",
    "content": "\n#include \"cartographer/transform/rigid_transform.h\"\n\n#include <vector>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/lua_parameter_dictionary.h\"\n#include \"glog/logging.h\"\n#include \"gtest/gtest.h\"\n#include <iostream>\nnamespace cartographer {\nnamespace transform {\n#define sout(Xit)  {std::cout<<__LINE__<<\" \"<< Xit <<\"\"<<std::endl;}\nnamespace {\n\n\nTEST(Rigid2s, rigid_1) {\n  Rigid2<double> r1; \n  sout(r1);                 //[1,0,0]\n  sout(r1.Identity());      //[0,0,0]\n  sout(r1.Rotation(3.14/2));//[0,0,1.57]\n\n  sout(r1.Translation({1,2}));//[1,2,0],静态成员函数，与对象无关\n  sout(Rigid2<int>::Translation({1,2}))//同上\n\n  sout(r1.inverse());      //[-1,-0,-0]\n  sout(r1.inverse().inverse());//[1,0,0]\n\n  sout(r1*r1);              //[2,0,0]\n  Eigen::Matrix<double, 2, 1> mat{1,2};\n  sout(r1*mat);             //[2,2] ：[1,0]+[1,2]\n  //sout((r1*mat)(1,0));//Eigen矩阵索引是()而不是[]\n  \n }\n\nTEST(Rigid2s, rigid_2) {\n  Rigid2<double> r1({1,2},M_PI_2); \n  sout(M_PI_2)             //1.57\n  sout(r1);                //[1,2,1.57] ，pi/2对应的弧度是1.57\n  sout(r1.Identity());     //[0,0,0]\n  sout(r1.Rotation(3.14/2));//[0,0,1.57]静态成员函数，与对象无关\n  \n  //sout(Rigid2<int>::Rotation(3.14/2));//同上\n  sout(r1.Translation({1,2}));//[1,2,0]\n\n\n  sout(r1);              //[1,2,1.57]\n  sout(r1.inverse());        //[-2,1,-1.57]\n  sout(r1.inverse().inverse());\n\n  Rigid2<double> r2({2,2},M_PI_2); \n  sout(r2.cast<float>()); //[2.0,2.0,1.0],成员模板函数cast()\n  \n\n  Rigid2<double> r3({3,2},3.2*2);\n  sout(r3);                    //[3,2,6.4]\n  sout(r3.normalized_angle()); //0.117\n\n  \n}\n\nTEST(Rigid2s, rigid_3) {\n\n  Rigid2<double> r1({1,2},M_PI_2); \n  Rigid2<double> r2({2,2},M_PI_2); \n  Rigid2<double> r4({2,2},M_PI_2); \n\n  sout(r1);                     //[1,2,1.57]\n  sout(r1*r1);                 //[-1,3,3.14]\n  sout(r1*r2);                 //[-1,4,3.14]\n\n  Eigen::Matrix<double, 2, 1> mat{1,1};\n  Eigen::Matrix<double, 2, 1> matd{1,1};\n\n\n  sout(r1.rotation().matrix ()); //[0,-1;1,0]\n  sout(r1.rotation() * mat ); //[-1,1]\n  sout(r1.translation());     //[1,2] \n  sout(mat);                  //[1,1]\n  sout(r1*mat); //[0,3].  \n\n\n\n\n  sout(r4.rotation().matrix (   ) );//[0,-1;1,0]\n  sout(r4.rotation() * matd );      //[-1,1] \n  sout(r4.translation());           //[2,2]\n  sout(matd); //[1,1]\n  sout(r4*matd); //[1,3].    \n  }\n}\n\n}  // namespace transform\n}  // namespace cartographer\n"
  },
  {
    "path": "transform/rigid_transform_test_helpers.h",
    "content": "\n#ifndef CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_TEST_HELPERS_H_\n#define CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_TEST_HELPERS_H_\n\n#include <string>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/port.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace transform {\n\n/*\n本文件用于辅助gtest\n\nEigen::Translation:Represents an homogeneous transformation in a N dimensional space.\n*/\n\n//\ntemplate <typename T>\nEigen::Transform<T, 2, Eigen::Affine> ToEigen(const Rigid2<T>& rigid2) {\n  return Eigen::Translation<T, 2>(rigid2.translation()) * rigid2.rotation();\n}\n\ntemplate <typename T>\nEigen::Transform<T, 3, Eigen::Affine> ToEigen(const Rigid3<T>& rigid3) {\n  return Eigen::Translation<T, 3>(rigid3.translation()) * rigid3.rotation();\n}\n/*gmock模拟对象行为*/\nMATCHER_P2(IsNearly, rigid, epsilon,\n           string(string(negation ? \"isn't\" : \"is\", \" nearly \") +\n                  rigid.DebugString())) {\n  return ToEigen(arg).isApprox(ToEigen(rigid), epsilon);\n}\n\n}  // namespace transform\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_TRANSFORM_RIGID_TRANSFORM_TEST_HELPERS_H_\n"
  },
  {
    "path": "transform/transform.cc",
    "content": "\n#include \"cartographer/transform/transform.h\"\n\nnamespace cartographer {\nnamespace transform {\n\n//proto的translation对应被旋转矩阵,rotation对应旋转角度数值\nRigid2d ToRigid2(const proto::Rigid2d& pose) { \n  return Rigid2d({pose.translation().x(), pose.translation().y()},\n                 pose.rotation());\n}\n\nEigen::Vector2d ToEigen(const proto::Vector2d& vector) {\n  return Eigen::Vector2d(vector.x(), vector.y()); //proto的x对应eigen的x,y对应y\n}\n\nEigen::Vector3f ToEigen(const proto::Vector3f& vector) { //同上\n  return Eigen::Vector3f(vector.x(), vector.y(), vector.z());\n}\n\nEigen::Vector3d ToEigen(const proto::Vector3d& vector) { //同上\n  return Eigen::Vector3d(vector.x(), vector.y(), vector.z());\n}\n\n//proto的Quaterniond四元数x,y,x,w对应Eigen的w,x,y,z\nEigen::Quaterniond ToEigen(const proto::Quaterniond& quaternion) {\n  return Eigen::Quaterniond(quaternion.w(), quaternion.x(), quaternion.y(),\n                            quaternion.z());\n}\n\nproto::Rigid2d ToProto(const transform::Rigid2d& transform) {\n//序列化,将二维数转化成proto\n  proto::Rigid2d proto;\n  proto.mutable_translation()->set_x(transform.translation().x());\n  proto.mutable_translation()->set_y(transform.translation().y());\n  proto.set_rotation(transform.rotation().angle());\n  //angle()返回方位角,弧度[-pi;pi]\n  return proto;\n}\n\nproto::Rigid2f ToProto(const transform::Rigid2f& transform) {\n  proto::Rigid2f proto;\n  proto.mutable_translation()->set_x(transform.translation().x());//指针运算\n  proto.mutable_translation()->set_y(transform.translation().y());\n  proto.set_rotation(transform.rotation().angle());\n  return proto;\n}\n\nproto::Rigid3d ToProto(const transform::Rigid3d& rigid) {\n  proto::Rigid3d proto;\n  *proto.mutable_translation() = ToProto(rigid.translation());//加*,赋值运算\n  *proto.mutable_rotation() = ToProto(rigid.rotation());\n  return proto;\n}\n\ntransform::Rigid3d ToRigid3(const proto::Rigid3d& rigid) {\n  //调用的是transform::Rigid3d()的构造函数,\n  //构造函数里又调用的ToEigen(const proto::Vector3d& vector)..\n  return transform::Rigid3d(ToEigen(rigid.translation()),\n                            ToEigen(rigid.rotation()));\n}\n\nproto::Rigid3f ToProto(const transform::Rigid3f& rigid) {\n  proto::Rigid3f proto;\n  *proto.mutable_translation() = ToProto(rigid.translation());\n  *proto.mutable_rotation() = ToProto(rigid.rotation());\n  return proto;\n}\n\nproto::Vector2d ToProto(const Eigen::Vector2d& vector) {\n  proto::Vector2d proto;\n  proto.set_x(vector.x());\n  proto.set_y(vector.y());\n  return proto;\n}\n\nproto::Vector3f ToProto(const Eigen::Vector3f& vector) {\n  proto::Vector3f proto;\n  proto.set_x(vector.x());\n  proto.set_y(vector.y());\n  proto.set_z(vector.z());\n  return proto;\n}\n\nproto::Vector3d ToProto(const Eigen::Vector3d& vector) {\n  proto::Vector3d proto;\n  proto.set_x(vector.x());\n  proto.set_y(vector.y());\n  proto.set_z(vector.z());\n  return proto;\n}\n\nproto::Quaternionf ToProto(const Eigen::Quaternionf& quaternion) {\n  proto::Quaternionf proto;\n  proto.set_w(quaternion.w());\n  proto.set_x(quaternion.x());\n  proto.set_y(quaternion.y());\n  proto.set_z(quaternion.z());\n  return proto;\n}\n\nproto::Quaterniond ToProto(const Eigen::Quaterniond& quaternion) {\n  proto::Quaterniond proto;\n  proto.set_w(quaternion.w());\n  proto.set_x(quaternion.x());\n  proto.set_y(quaternion.y());\n  proto.set_z(quaternion.z());\n  return proto;\n}\n\n}  // namespace transform\n}  // namespace cartographer\n"
  },
  {
    "path": "transform/transform.h",
    "content": "\n#ifndef CARTOGRAPHER_TRANSFORM_TRANSFORM_H_\n#define CARTOGRAPHER_TRANSFORM_TRANSFORM_H_\n\n#include <cmath>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/math.h\"\n#include \"cartographer/transform/proto/transform.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace transform {\n\n//google采用三维 xyz:roll, pitch, yaw\n  \n/*\n atan2:返回一个非负的弧度值,  double atan2(double y,double x) :计算复数 x+yi 的辐角\n vec()：a vector expression of the imaginary part (x,y,z) \n norm()： for vectors, the l2 norm of *this, for matrices the Frobenius norm。\n w()  : the w coefficient\n\n 为何乘以2: 四元数q=[cos(θ/2),sin(θ/2)x,sin(θ/2)y,sin(θ/2)z]\n*/\n\n \n// Returns the non-negative rotation angle in radians of the 3D transformation\n// 'transform'.\ntemplate <typename FloatType>\nFloatType GetAngle(const Rigid3<FloatType>& transform) {\n  return FloatType(2) * std::atan2(transform.rotation().vec().norm(),\n                                   std::abs(transform.rotation().w()));\n}\n/*\n返回3D变换yaw方向的弧度值,也就是z轴方向的弧度。\n\n*/\n// Returns the yaw component in radians of the given 3D 'rotation'. Assuming\n// 'rotation' is composed of three rotations around X, then Y, then Z, returns\n// the angle of the Z rotation.\ntemplate <typename T>\nT GetYaw(const Eigen::Quaternion<T>& rotation) {\n  const Eigen::Matrix<T, 3, 1> direction =\n      rotation * Eigen::Matrix<T, 3, 1>::UnitX();\n  return atan2(direction.y(), direction.x());\n}\n\n/*\n返回3D变换yaw方向(z轴)的弧度值\n*/\n// Returns the yaw component in radians of the given 3D transformation\n// 'transform'.\ntemplate <typename T>\nT GetYaw(const Rigid3<T>& transform) {\n  return GetYaw(transform.rotation());\n}\n\n/*\n根据四元数返回绕任意轴旋转的矩阵。angle-axis\n*/\n// Returns an angle-axis vector (a vector with the length of the rotation angle\n// pointing to the direction of the rotation axis) representing the same\n// rotation as the given 'quaternion'.\ntemplate <typename T>\nEigen::Matrix<T, 3, 1> RotationQuaternionToAngleAxisVector(\n    const Eigen::Quaternion<T>& quaternion) {\n  Eigen::Quaternion<T> normalized_quaternion = quaternion.normalized();\n  // We choose the quaternion with positive 'w', i.e., the one with a smaller\n  // angle that represents this orientation.\n  if (normalized_quaternion.w() < 0.) { //w为负时，全部乘以-1\n    // Multiply by -1. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=560\n    normalized_quaternion.w() *= T(-1.);\n    normalized_quaternion.x() *= T(-1.);\n    normalized_quaternion.y() *= T(-1.);\n    normalized_quaternion.z() *= T(-1.);\n  }\n  // We convert the normalized_quaternion into a vector along the rotation axis\n  // with length of the rotation angle.\n  //得到角度angle\n  const T angle = T(2.) * atan2(normalized_quaternion.vec().norm(),\n                                normalized_quaternion.w());\n  constexpr double kCutoffAngle = 1e-7;  // We linearize below this angle.\n  //scale 放大倍数。\n  const T scale = angle < kCutoffAngle ? T(2.) : angle / sin(angle / T(2.));\n  return Eigen::Matrix<T, 3, 1>(scale * normalized_quaternion.x(),\n                                scale * normalized_quaternion.y(),\n                                scale * normalized_quaternion.z());\n}\n\n/*\n与上面函数相反的操作。\n给定绕angle-axis旋转，返回四元数。\n*/\n// Returns a quaternion representing the same rotation as the given 'angle_axis'\n// vector.\ntemplate <typename T>\nEigen::Quaternion<T> AngleAxisVectorToRotationQuaternion(\n    const Eigen::Matrix<T, 3, 1>& angle_axis) {\n  T scale = T(0.5);\n  T w = T(1.);\n  constexpr double kCutoffAngle = 1e-8;  // We linearize below this angle.\n  if (angle_axis.squaredNorm() > kCutoffAngle) {\n    const T norm = angle_axis.norm();\n    scale = sin(norm / 2.) / norm;\n    w = cos(norm / 2.);\n  }\n  const Eigen::Matrix<T, 3, 1> quaternion_xyz = scale * angle_axis;\n  return Eigen::Quaternion<T>(w, quaternion_xyz.x(), quaternion_xyz.y(),\n                              quaternion_xyz.z());\n}\n\n/*投影到2维平面xy。\n具体：\n1,取平移中的[dx,dy],\n2,xy平面的平移角度是yaw.\n*/\n// Projects 'transform' onto the XY plane.\ntemplate <typename T>\nRigid2<T> Project2D(const Rigid3<T>& transform) {\n  return Rigid2<T>(transform.translation().template head<2>(),\n                   GetYaw(transform));\n}\n\n/*\n将2维变换转换为3维变换。\n具体:\n1,平移矩阵为[dx,dy,0]\n2,z方向单位旋转。\n*/\n// Embeds 'transform' into 3D space in the XY plane.Embeds：嵌入。z轴不变。\ntemplate <typename T>\nRigid3<T> Embed3D(const Rigid2<T>& transform) {\n  return Rigid3<T>(\n      {transform.translation().x(), transform.translation().y(), T(0)},\n      Eigen::AngleAxis<T>(transform.rotation().angle(),\n                          Eigen::Matrix<T, 3, 1>::UnitZ()));\n}\n\n/*\n序列化和反序列化的函数,实现在.cc文件\n\n*/\n// Conversions between Eigen and proto.\nRigid2d ToRigid2(const proto::Rigid2d& transform);\nEigen::Vector2d ToEigen(const proto::Vector2d& vector);\nEigen::Vector3f ToEigen(const proto::Vector3f& vector);\nEigen::Vector3d ToEigen(const proto::Vector3d& vector);\nEigen::Quaterniond ToEigen(const proto::Quaterniond& quaternion);\nproto::Rigid2d ToProto(const Rigid2d& transform);\nproto::Rigid2f ToProto(const Rigid2f& transform);\nproto::Rigid3d ToProto(const Rigid3d& rigid);\nRigid3d ToRigid3(const proto::Rigid3d& rigid);\nproto::Rigid3f ToProto(const Rigid3f& rigid);\nproto::Vector2d ToProto(const Eigen::Vector2d& vector);\nproto::Vector3f ToProto(const Eigen::Vector3f& vector);\nproto::Vector3d ToProto(const Eigen::Vector3d& vector);\nproto::Quaternionf ToProto(const Eigen::Quaternionf& quaternion);\nproto::Quaterniond ToProto(const Eigen::Quaterniond& quaternion);\n\n}  // namespace transform\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_TRANSFORM_TRANSFORM_H_\n"
  },
  {
    "path": "transform/transform_interpolation_buffer.cc",
    "content": "\n#include \"cartographer/transform/transform_interpolation_buffer.h\"\n\n#include <algorithm>\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/common/make_unique.h\"\n#include \"cartographer/transform/transform.h\"\n#include \"glog/logging.h\"\n\nnamespace cartographer {\nnamespace transform {\n\nvoid TransformInterpolationBuffer::Push(const common::Time time,\n                                        const transform::Rigid3d& transform) {\n  if (deque_.size() > 0) {\n    CHECK_GE(time, latest_time()) << \"New transform is older than latest.\";\n  }\n  deque_.push_back(TimestampedTransform{time, transform});//构造临时对象\n}\n\nbool TransformInterpolationBuffer::Has(const common::Time time) const {\n  if (deque_.empty()) {\n    return false;\n  }\n  return earliest_time() <= time && time <= latest_time();//检查时间合理性\n}\n\ntransform::Rigid3d TransformInterpolationBuffer::Lookup( //按照time查找\n    const common::Time time) const {\n  CHECK(Has(time)) << \"Missing transform for: \" << time;\n  //lower_bound,二分查找的函数,查找序列中的第一个出现的值大于等于time的位置。（非小于）\n  auto start =\n      std::lower_bound(deque_.begin(), deque_.end(), time,\n                       [](const TimestampedTransform& timestamped_transform,\n                       //自定义lambda表达式,按照时间戳比较\n                          const common::Time time) {\n                         return timestamped_transform.time < time;\n                       });\n/*由于支持插值，所有当time=30,deque=[10,20,20,30,30,50]时：\n  start=30 ，直接返回30\n*/\n  auto end = start;//迭代器\n  if (end->time == time) {\n    return end->transform;\n  }\n\n/*由于支持插值，所有当time=20,deque=[10,20,20,30,30,50]时：\n  start=30 ，返回20\n*/\n  --start;\n  if (start->time == time) {\n    return start->transform;\n  }\n/*由于支持插值，所有当time=25,deque=[10,20,20,30,30,50]时：\n  start=30 ，end=20，返回通过插值得到的25\n*/\n  const double duration = common::ToSeconds(end->time - start->time);\n  const double factor = common::ToSeconds(time - start->time) / duration;\n  //[dx,dy,dz]按比例缩放。\n  const Eigen::Vector3d origin =\n      start->transform.translation() +\n      (end->transform.translation() - start->transform.translation()) * factor;\n  //roation则使用slerp插值。\n  const Eigen::Quaterniond rotation =\n      Eigen::Quaterniond(start->transform.rotation())\n          .slerp(factor, Eigen::Quaterniond(end->transform.rotation()));\n  //返回插值得到的变换矩阵\n  return transform::Rigid3d(origin, rotation);\n}\n\ncommon::Time TransformInterpolationBuffer::earliest_time() const {\n  CHECK(!empty()) << \"Empty buffer.\";\n  return deque_.front().time;\n}\n\ncommon::Time TransformInterpolationBuffer::latest_time() const {\n  CHECK(!empty()) << \"Empty buffer.\";\n  return deque_.back().time;\n}\n\nbool TransformInterpolationBuffer::empty() const { return deque_.empty(); }\n\nstd::unique_ptr<TransformInterpolationBuffer>\nTransformInterpolationBuffer::FromTrajectory(\n    const mapping::proto::Trajectory& trajectory) {\n  auto interpolation_buffer =\n      common::make_unique<TransformInterpolationBuffer>();//返回智能指针\n  for (const mapping::proto::Trajectory::Node& node : trajectory.node()) {\n    interpolation_buffer->Push(common::FromUniversal(node.timestamp()),\n                               transform::ToRigid3(node.pose()));\n  }\n  return interpolation_buffer;\n}\n\n}  // namespace transform\n}  // namespace cartographer\n/*\n\nForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,\n                               const T& val, Compare comp);\n返回一个非递减序列[first, last)中的第一个大于等于值val的位置。\nupper_bound返回的是最后一个大于val的位置，也是有一个新元素val进来时的插入位置。\n\n            10 10 10 20 20 20 30 30\nlower_bound:          ^\nupper_bound:                   ^\n*/"
  },
  {
    "path": "transform/transform_interpolation_buffer.h",
    "content": "\n#ifndef CARTOGRAPHER_TRANSFORM_TRANSFORM_INTERPOLATION_BUFFER_H_\n#define CARTOGRAPHER_TRANSFORM_TRANSFORM_INTERPOLATION_BUFFER_H_\n\n#include <deque>\n#include <memory>\n\n#include \"cartographer/common/time.h\"\n#include \"cartographer/mapping/proto/trajectory.pb.h\"\n#include \"cartographer/transform/rigid_transform.h\"\n\nnamespace cartographer {\nnamespace transform {\n\n/*\n基于时间有序的变换,支持在队列中按时间顺序查找,即使变换不存在于队列中，任然支持相邻时间内的插值变换进行近似。\n作用与ROS的tf2函数族类似。\n\n数据成员：\n1,deque_;\n成员函数：\n1,Push()\n2,Has()\n3,Lookup()\n4,earliest_time()\n5,latest_time()\n6,empty()\n*/\n// A time-ordered buffer of transforms that supports interpolated lookups.\nclass TransformInterpolationBuffer {\n public:\n \t//函数,返回智能指针\n  static std::unique_ptr<TransformInterpolationBuffer> FromTrajectory(\n      const mapping::proto::Trajectory& trajectory);\n\n/*\n添加变换到队列尾部,当缓冲区已满时,删除队首元素\n*/\n  // Adds a new transform to the buffer and removes the oldest transform if the\n  // buffer size limit is exceeded.\n  void Push(common::Time time, const transform::Rigid3d& transform);\n\n//返回能否在给定时间内计算的插值变换。time应在early-old之间，可以插值。\n  // Returns true if an interpolated transfrom can be computed at 'time'\n  bool Has(common::Time time) const;\n//返回time处的变换,可插值\n  // Returns an interpolated transform at 'time'. CHECK()s that a transform at\n  // 'time' is available.\n  transform::Rigid3d Lookup(common::Time time) const;\n\n/*\n返回队列缓冲区内变换的最早时间，也就是队首元素。\n*/\n  // Returns the timestamp of the earliest transform in the buffer or 0 if the\n  // buffer is empty.\n  common::Time earliest_time() const;\n/*\n最晚时间，也就是队尾元素\n*/\n  // Returns the timestamp of the earliest transform in the buffer or 0 if the\n  // buffer is empty.\n  common::Time latest_time() const;\n\n  // Returns true if the buffer is empty.\n  bool empty() const;\n\n private:\n  struct TimestampedTransform {\n    common::Time time;           //发生时间\n    transform::Rigid3d transform;//变换矩阵\n  };\n\n  std::deque<TimestampedTransform> deque_;\n  //队列，元素是带时间戳的变换,存储了一段时间内的变换矩阵信息\n};\n\n}  // namespace transform\n}  // namespace cartographer\n\n#endif  // CARTOGRAPHER_TRANSFORM_TRANSFORM_INTERPOLATION_BUFFER_H_\n"
  },
  {
    "path": "transform/transform_interpolation_buffer_test.cc",
    "content": "\n#include \"cartographer/transform/transform_interpolation_buffer.h\"\n\n#include \"Eigen/Core\"\n#include \"Eigen/Geometry\"\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace transform {\nnamespace {\n\nTEST(TransformInterpolationBufferTest, testHas) {\n  TransformInterpolationBuffer buffer;\n  EXPECT_FALSE(buffer.Has(common::FromUniversal(50)));//false 50us\n  buffer.Push(common::FromUniversal(50), transform::Rigid3d::Identity());//\n  EXPECT_FALSE(buffer.Has(common::FromUniversal(25)));//false，只有1个元素。\n  EXPECT_TRUE(buffer.Has(common::FromUniversal(50)));//true，1个元素\n  EXPECT_FALSE(buffer.Has(common::FromUniversal(75))); //false\n  buffer.Push(common::FromUniversal(100), transform::Rigid3d::Identity());\n  EXPECT_FALSE(buffer.Has(common::FromUniversal(25)));//false，50-100才是true\n  EXPECT_TRUE(buffer.Has(common::FromUniversal(50)));\n  EXPECT_TRUE(buffer.Has(common::FromUniversal(75)));//在50-100之间\n  EXPECT_TRUE(buffer.Has(common::FromUniversal(100)));\n  EXPECT_FALSE(buffer.Has(common::FromUniversal(125)));//不在\n  EXPECT_EQ(common::FromUniversal(50), buffer.earliest_time());\n  EXPECT_EQ(common::FromUniversal(100), buffer.latest_time());\n}\n\nTEST(TransformInterpolationBufferTest, testLookup) {\n  TransformInterpolationBuffer buffer;\n  buffer.Push(common::FromUniversal(50), transform::Rigid3d::Identity());\n  // The rotation needs to be relatively small in order for the \n  // interpolation to remain a z-axis rotation.\n  buffer.Push(common::FromUniversal(100),\n              transform::Rigid3d::Translation(Eigen::Vector3d(10., 10., 10.)) *\n                  transform::Rigid3d::Rotation(\n                      Eigen::AngleAxisd(2., Eigen::Vector3d::UnitZ())));\n  const common::Time time = common::FromUniversal(75);\n  //75在50-100之间，可以使用插值计算。\n  const transform::Rigid3d interpolated = buffer.Lookup(time);\n  EXPECT_THAT(\n      interpolated,\n      IsNearly(transform::Rigid3d::Translation(Eigen::Vector3d(5., 5., 5.)) *\n                   transform::Rigid3d::Rotation(\n                       Eigen::AngleAxisd(1., Eigen::Vector3d::UnitZ())),\n               1e-6));\n}\n\n}  // namespace\n}  // namespace transform\n}  // namespace cartographer\n"
  },
  {
    "path": "transform/transform_test.cc",
    "content": "\n#include \"cartographer/transform/transform.h\"\n\n#include <random>\n\n#include \"cartographer/transform/rigid_transform.h\"\n#include \"cartographer/transform/rigid_transform_test_helpers.h\"\n#include \"gtest/gtest.h\"\n\nnamespace cartographer {\nnamespace transform {\nnamespace {\n\nTEST(TransformTest, GetAngle) {\n  std::mt19937 rng(42);//随机数种子\n  std::uniform_real_distribution<float> angle_distribution(0.f, M_PI);\n     //[0- pi],概率均匀分布\n  std::uniform_real_distribution<float> position_distribution(-1.f, 1.f);\n     //[-1 ,1]\n\n  for (int i = 0; i != 100; ++i) {\n    const float angle = angle_distribution(rng);\n    const float x = position_distribution(rng);\n    const float y = position_distribution(rng);\n    const float z = position_distribution(rng);\n    const Eigen::Vector3f axis = Eigen::Vector3f(x, y, z).normalized();\n    //, for vectors, the l2 norm of *this, and for matrices the Frobenius norm. \n    \n    //给定角度绕axis旋转，得到Rigid3f，再求Rigid3d的angle。2者理论上应该相同\n    EXPECT_NEAR(angle,\n                GetAngle(Rigid3f::Rotation(AngleAxisVectorToRotationQuaternion(\n                    Eigen::Vector3f(angle * axis)))),\n                1e-6f);\n  }\n}\n\nTEST(TransformTest, GetYaw) {\n  const auto rotation =\n      Rigid3d::Rotation(Eigen::AngleAxisd(1.2345, Eigen::Vector3d::UnitZ()));\n  EXPECT_NEAR(1.2345, GetYaw(rotation), 1e-9);\n  EXPECT_NEAR(-1.2345, GetYaw(rotation.inverse()), 1e-9);//四元数的逆就是共轭。\n}\n\nTEST(TransformTest, GetYawAxisOrdering) {\n  //乘法顺序不能颠倒。zyx\n  const auto rotation =\n      Rigid3d::Rotation(Eigen::AngleAxisd(1.2345, Eigen::Vector3d::UnitZ()) *\n                        Eigen::AngleAxisd(0.4321, Eigen::Vector3d::UnitY()) *\n                        Eigen::AngleAxisd(0.6789, Eigen::Vector3d::UnitX()));\n  EXPECT_NEAR(1.2345, GetYaw(rotation), 1e-9);\n}\n\nTEST(TransformTest, Embed3D) {\n  const Rigid2d rigid2d({1., 2.}, 0.);\n  const Rigid3d rigid3d(\n      Rigid3d::Translation(Eigen::Vector3d(1., 2., 3.)) *\n      Rigid3d::Rotation(Eigen::Quaterniond(1., 2., 3., 4.).normalized()));\n  const Rigid3d expected =\n      rigid3d * Rigid3d::Translation(Eigen::Vector3d(1., 2., 0.));\n  EXPECT_THAT(expected, IsNearly(rigid3d * Embed3D(rigid2d), 1e-9));\n}\n\n}  // namespace\n}  // namespace transform\n}  // namespace cartographer\n"
  }
]