从零开始手写Cartographer(1): 开端
写在前面的话
我做SLAM已经三年了。读书时初学SLAM,一开始无从下手,直到读了高博士的博客,茅塞顿开,渐入佳境。后来又买了他的《视觉SLAM十四讲》,常伴手边,直至毕业。几个月前找工作的时候,他是我的面试官,与之交谈,如沐春风。那时候我就萌生出一个念头,希望追随博士的脚步,把这些年的经验和心得分享给后来人,所谓“人人为我、我为人人”,应该就是如此吧。 -- 2022.04.16
激光SLAM与Cartographer
我的水平远不及博士,只好挑自己擅长的东西说起。Cartographer是谷歌2015年发布的激光SLAM开源项目,功能完善,逻辑清晰,在学术和工程上都很具代表性。我做相关的工作已经两年了,论文和代码都读过一遍,也尝试做了一些优化和扩展,所以就拿它来做分享。
Cartographer本身是一个浩大的工程,并不利于初学者学习。本文希望根据它的设计思路,从零开始构建一个简易的2D激光SLAM工程。参考资料:
- 官网: https://google-cartographer.readthedocs.io/en/latest/
- github: https://github.com/cartographer-project/cartographer
- 论文:Real-Time Loop Closure in 2D LIDAR SLAM
这里放出一张官方的动图:

SLAM之我见
这里先说说什么是SLAM:SLAM译作同时定位与地图构建,被认为是实现真正全自主移动机器人的关键。我记得这是我硕士论文的第一句,听着确实有掉书袋的嫌疑,也确实费耳朵。从我工作经验来讲,SLAM问题姑且可以等同于:如何通过整合局部观测来建立全局地图。
激光SLAM的观测是点云,每帧点云都是对环境的局部观测;而激光SLAM的目的就是用各时刻的点云拼成全局地图。这个过程如同拼图一样:
当然,以上是我对于“基于图优化的SLAM方法”做的理解,自然有偏颇之处。这里建议初学者不必拘泥于具体概念,相信有一定项目基础后会有更深的体会。
让我们开始吧!
开发环境:Linux,推荐Ubuntu。
其实如果我们的代码是纯C++且不依赖于第三方库,那么无论何种平台(Windows/Linux/MacOS)都可以编译运行。但实际上我们经常会用到仅支持Linux的第三方库,所以我们不得不用Linux。
实际上,Linux也是非常基本的开发环境,而ubuntu是最知名的Linux发行版,有了问题也容易找到资料。如果手头没有Linux环境,可以用WSL或云服务器,这里不再详述。
对于一个崭新的ubuntu环境来说,我们通常需要安装一些基本的工具来编译:
# gcc等基本编译套件
sudo apt install build-essential
# cmake
sudo apt install cmake
然后我们创建工程目录,姑且称之为slam。然后创建src目录存放代码,build目录存放编译产物:
sudo mkdir slam slam/src slam/build
我们在src目录中写一个最简单的C++程序,保存为hello.cc:
#include <iostream>
int main() {
std::cout << "hello, slam!" << std::endl;
return 0;
}
这里我们用了Google Style作为工程规范。
我们使用cmake来编译我们的工程。需要在slam下写一个CMakeLists.txt:
# 指定最低版本
cmake_minimum_required(VERSION 2.8)
# 指定项目名
project(slam)
# 用src/hello.cc产生一个名为hello的可执行项目
add_executable(hello src/hello.cc)
此时路径树应该是这样的:
slam\
|---src\
| |--- hello.cc
|
|---build\
|
|---CMakeLists.txt
我们进入build目录,执行编译:
cd build
cmake ..
make
此时会在build下产生hello可执行文件,可以执行它:
./hello
此时如果输出"hello, slam!"字样,说明成功了。
到此为止,我们有了一个好的开始。下节我们讲述一些激光SLAM的基础概念,并做工程实现。
以上工程可以参考我的github(https://github.com/ysklab/cartographer-from-scratch)仓库,需要手动checkout到指定commit。本节的commit信息为:
start with "hello slam"
这里假设读者具备基本的git操作知识,包括clone、log、checkout等。
从零开始手写Cartographer(1): 开端的更多相关文章
- java 从零开始手写 RPC (03) 如何实现客户端调用服务端?
说明 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 写完了客户端和服务端,那么如何实现客户端和服务端的 ...
- java 从零开始手写 RPC (04) -序列化
序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...
- java 从零开始手写 RPC (05) reflect 反射实现通用调用之服务端
通用调用 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何 ...
- java 从零开始手写 RPC (07)-timeout 超时处理
<过时不候> 最漫长的莫过于等待 我们不可能永远等一个人 就像请求 永远等待响应 超时处理 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RP ...
- 从零开始手写 dubbo rpc 框架
rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. ...
- 从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...
- java 从零开始手写 RPC (01) 基于 websocket 实现
RPC 解决的问题 RPC 主要是为了解决的两个问题: 解决分布式系统中,服务之间的调用问题. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑. 这一节我们来学习下如何基于 we ...
- AI应用开发实战 - 手写识别应用入门
AI应用开发实战 - 手写识别应用入门 手写体识别的应用已经非常流行了,如输入法,图片中的文字识别等.但对于大多数开发人员来说,如何实现这样的一个应用,还是会感觉无从下手.本文从简单的MNIST训练出 ...
- 放弃antd table,基于React手写一个虚拟滚动的表格
缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...
随机推荐
- python3 常见错误(一)
以下全部是在python3中适用. 错误一: 函数默认参数 Python允许为函数的参数提供默认的可选值.但是它可能会导致一些易变默认值的混乱.例子: 我们希望每次调用myFun函数,list1都为默 ...
- css实现气泡提示框三角及css中drop-shadow的使用
css 做一个弹出气泡,样式怎么设计? 难点: 要实现白色三角型,可以在伪元素before和after上设置一个黑.一个白三角形,白三角形会挡住黑的,从而实现. &::before, & ...
- Focal and Global Knowledge Distillation for Detectors
一. 概述 论文地址:链接 代码地址:链接 论文简介: 此篇论文是在CGNet上增加部分限制loss而来 核心部分是将gt框变为mask进行蒸馏 注释:仅为阅读论文和代码,未进行试验,如有漏错请不吝指 ...
- 探索ABP基础架构
为了了解应用程序是如何配置和初始化,本文将探讨ASP.NET Core和ABP框架最基本的构建模块.我们将从 ASP.NET Core 的 Startup类开始了解为什么我们需要模块化系统,以及 AB ...
- redis 基础1
1.redis是什么? redis是非关系型数据库key-value数据库,开源免费.是当下NoSQL技术之一 2.redis能干吗? (1)内存存储,可以持久化,redis存储在内存中,内存的话是断 ...
- 『忘了再学』Shell基础 — 19、使用declare命令声明变量类型
目录 1.declare命令介绍 2.声明数组变量类型 3.声明变量为环境变量 4.声明只读属性 5.补充: 1.declare命令介绍 Shell中所有变量的默认类型是字符串类型,如果你需要进行特殊 ...
- Vue 基础篇---computed 和 watch
最近在看前端 Vue方面的基础知识,虽然前段时间也做了一些vue方面的小项目,但总觉得对vue掌握的不够 所以对vue基础知识需要注意的地方重新撸一遍,可能比较零碎,看到那块就写哪块吧 1.vue中的 ...
- CF1682C. LIS or Reverse LIS?
题意:给\(n\)个数,问你能构出严格上升子序列长度和下降子序列长度最小值的最大值. 思路: 如果一个数出现至少两次,ans++. 统计出现一次的个数,因为再最长上升子序列中,只能有一个值能贡献到下降 ...
- IDEA找不到类但实际存在的问题解决
不知道某天开始Idea就开始抽风了. 现象: 一个service的接口类,就在同一个包下,但总是找不到,编辑器一直标红 编译可以通过 说明类本身应该是没什么问题的.问题是怎么重新编译重新reload ...
- Kubernetes client-go workqueue 源码分析
概述Queue接口和结构体setAdd()Get()Done()DelayingQueue接口和结构体waitForNewDelayingQueuewaitingLoop()AddAfter()Rat ...