点击上方蓝字关注 计算机视觉life,置顶星标及时接收消息!
本文对 “研究SLAM,但编程又不好的人” 会有很大启发
研究SLAM的同学平时除了要看很多头疼的公式原理外,对编程也是有不少要求的。在学生阶段,很多同学都是用MATLAB作为主要编程语言,容易上手又简单。但是去看SLAM的开源代码,发现基本清一色都是C++,而且有不少开源代码使用C++到了出神入化的地步了。那么问题来了,学习SLAM是否可以不拘泥于编程语言的选择?到底对编程的要求有多高?
本文来自知乎上的同名问题,已经征得了部分答主的授权,对几个优秀回答进行了整理,如下
题主说MATLAB,主要原因是大多数人本科阶段接触的都是MATLAB,所以希望之后研究SLAM也用它。
MATLAB确实有很多优点:语法简单,开发速度快,调试方便,功能丰富。然而,在SLAM领域,MATLAB缺点也很明显,主要是这两个:
需要正版软件(你不能实机上也装个盗版MATLAB吧);
运行效率不高;
需要一个巨大的安装包;
而相对的,C++的优势在于直接使用,有很高的运行效率,不过开发速度和调试方面慢于MATLAB。不过光运行效率这一条,就够许多SLAM方案选择C++作为开发语言了,因为运行效率真的很重要。同一个算法,拿MATLAB写出来实现不能实时,拿C++写的能实时,你说用哪个?
当然MATLAB也有一些用武之地。我见过一些SLAM相关的公开课程,让学生用MATLAB做仿真,交作业,这没有问题,比如SLAM toolbox 。同样的,比较类似于MATLAB的Python(以及octave)亦常被用于此道。它们在开发上的快捷带来了很多便利,当你想要验证一些数学理论、思想时,这些都是不错的工具。所谓技多不压身,题主掌握MATLAB和Python当然是很棒的。
但是一牵涉到实用,你会发现几乎所有的方案都在用C++。 因为运行效率实在是太重要了。
那既然有心思学MATLAB,为什么不学好C++呢?
接下来说说C++大概要学到什么程度。用程序员的话说,C++语言比较特殊,你可以说自己精通了Java,但千万不要说自己精通了C++。C++非常之博大精深,有数不清的特性,而且随着时间还会不断变化更新。不过,大多数人都用不着学会所有的C++特性,因为许多东西一辈子都用不到。
作为SLAM研究人员,我们面对的主要是算法层面的开发,所以更关心如何有效地实现各种相关的算法。而相对的,那些复杂的软件架构,设计模式,我个人认为在SLAM中倒是占次要地位的。毕竟您用SLAM的目的是计算一个位置以及建个地图,并不是要去写一套能够自动更新的、多人网上对战功能的机器人大战平台。您的主要精力可能会花在矩阵运算、分块、非线性优化的实现、图像处理上面;您可能对并发、指令集加速、GPU加速等话题感兴趣,也可以花点时间学习;你还可能想用模板来拓展你的算法,也不妨一试。相应的,很多功能性的东西,比如说UI、网络通信等等,当你用到的时候不妨接触一下,但专注于SLAM上时就不必专门去学习了。
话虽如此,SLAM所需的C++水平,大抵要高于你在书本上看到的那些个示例代码。因为那些代码是作者用来向初学者介绍语法的,所以会尽量简单。而实际见到的代码往往结合了各种奇特的技巧,乍看起来会显得高深莫测。比方说你在教科书里看的大概是这样:
int main ( int argc, char** argv )
{
vector<string> vec;
vec.push_back("abc");
for ( int i=0; i<vec.size(); i++ )
{
// ...
}
return 0;
}
你看了C++ Primer Plus,觉得C++也不过如此,并没有啥特别难以理解的地方。然而实际代码大概是这样的:
嵌套的模板类(来自g2o的块求解器):
g2o::BlockSolver< g2o::BlockSolverTraits<3,1> >::LinearSolverType* linearSolver = new g2o::LinearSolverDense<g2o::BlockSolver< g2o::BlockSolverTraits<3,1> >::PoseMatrixType>();
g2o::BlockSolver< g2o::BlockSolverTraits<3,1> >* solver_ptr = new g2o::BlockSolver< g2o::BlockSolverTraits<3,1> >( linearSolver );
g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg( solver_ptr );
g2o::SparseOptimizer optimizer;
optimizer.setAlgorithm( solver );
模板元(来自ceres的自动求导):
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
if (!jacobians) {
return internal::VariadicEvaluate<
CostFunctor, double, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>
::Call(*functor_, parameters, residuals);
}
return internal::AutoDiff<CostFunctor, double,
N0, N1, N2, N3, N4, N5, N6, N7, N8, N9>::Differentiate(
*functor_,
parameters,
SizedCostFunction<kNumResiduals,
N0, N1, N2, N3, N4,
N5, N6, N7, N8, N9>::num_residuals(),
residuals,
jacobians);
}
C11新特性(来自SVO特征提取部分)
void Frame::setKeyPoints()
{
for(size_t i = 0; i < 5; ++i)
if(key_pts_[i] != NULL)
if(key_pts_[i]->point == NULL)
key_pts_[i] = NULL;
std::for_each(fts_.begin(), fts_.end(), [&](Feature* ftr){ if(ftr->point != NULL) checkKeyPoints(ftr); });
}
谜之运算(来自SVO的深度滤波器):
void DepthFilter::updateSeed(const float x, const float tau2, Seed* seed)
{
float norm_scale = sqrt(seed->sigma2 + tau2);
if(std::isnan(norm_scale))
return;
boost::math::normal_distribution<float> nd(seed->mu, norm_scale);
float s2 = 1./(1./seed->sigma2 + 1./tau2);
float m = s2*(seed->mu/seed->sigma2 + x/tau2);
float C1 = seed->a/(seed->a+seed->b) * boost::math::pdf(nd, x);
float C2 = seed->b/(seed->a+seed->b) * 1./seed->z_range;
float normalization_constant = C1 + C2;
C1 /= normalization_constant;
C2 /= normalization_constant;
float f = C1*(seed->a+1.)/(seed->a+seed->b+1.) + C2*seed->a/(seed->a+seed->b+1.);
float e = C1*(seed->a+1.)*(seed->a+2.)/((seed->a+seed->b+1.)*(seed->a+seed->b+2.))
+ C2*seed->a*(seed->a+1.0f)/((seed->a+seed->b+1.0f)*(seed->a+seed->b+2.0f));
// update parameters
float mu_new = C1*m+C2*seed->mu;
seed->sigma2 = C1*(s2 + m*m) + C2*(seed->sigma2 + seed->mu*seed->mu) - mu_new*mu_new;
seed->mu = mu_new;
seed->a = (e-f)/(f-e/f);
seed->b = seed->a*(1.0f-f)/f;
}
我不知道你们看到这些代码是什么心情,总之我当时内心的感受是:卧槽这怎么和教科书里的完全不一样啊!而且研究了半天发现人家居然是对的啊!
[我不是很擅长贴表情图总之你们脑补一下就好]
总而言之,对C++的水平要求应该是在教科书之上的。而且这个水平的提高,多数时候建立在你不断地看别人代码、码自己代码的过程之上。它是反复练习出来的,并不是仅仅通过看书就能领会的。特别是对于视觉SLAM问题,很多时候你没法照着论文把一套方案实现出来,这很大程度上取决于你的理论和代码功底。
所以,请尽早开始学习C++,尽早开始使用C++,才是研究SLAM的正确之道。不要长期彷徨在自己的舒适区里犹豫不决,这样是没有进步的。(同样的道理亦适用于想研究SLAM但不愿意学习Linux的朋友们)
我觉得对编程要求还是挺高的,先从后端说起吧
g2o, gtsam,ceres知道是肯定需要知道的,三者其实学习曲线都挺难的,你觉得你会用了是一回事,然后用好是一回事,自己写vertex, edge, factor, cost function又是另外一回事,如果你会写g2o里面的solver应该又是一个级别了吧。但是这三个在网上的资源而论g2o > ceres > gtsam。再者,你会调用api就代表你懂优化了吗,自己动手matlab写一下gauss-newton应该也可以试一试吧。当然,远古一点了levmar也很不错哦。直到这里,你确定你明白各种算法底层suitesparse里面的效率问题吗,如果挖掘到了这一层,还有各种线性代数库的比较哦,肯定是又快有慢。所以说,想要学会一到两个优化器不难,学会自定义有一点难,考虑到后端的效率自己写solver更难,深入到线性代数库的最底层,考虑问题结构,提升效率就更难了
前端,其实仅仅考虑点特征,也就那么多东西,V-SLAM用OPENCV也就够用了,而且opencv我觉得到API调用那一层也就足够了。如果考虑上RGBD,再加上一个PCL。理论知识方面基本的Multiview geometry其实也就足够了。但是前端的问题在于很多都是经验的东西,参数的选择,循环的次数等等。说白了我个人觉得视觉部分调参的问题更多,理论倒是不容易出问题。
前后段一起的话,什么线程管理就不用说了,该用的库都得上,例如boost啥的。有时候做visualization可能还需要一些别的库。
说完好像要求也就那样,反正我又不是CS出身活得还好好的
要搞SLAM,编程能力非常非常重要。编程能力就像运动员的力量和体能,是一切技巧和战术的基础。虽然研究的是算法,但良好的编程能力能够使你节省大量的时间,并且达到更好的效果。
根据我的经验,算法搞得特别好的人很少有编程不行的,因为编程好可以使你能够迅速验证想法。见过不少学生因为编程基础不好,在实现和debug上浪费大把大把的时间。这样的人当然很难把算法搞好。
这里编程能力有很多方面,以下简单说几个。赞多了再补( ̄▽ ̄)/
1. 阅读代码的能力
未说写,先说读。搞科研和搞工程都大忌闭门造车,除了paper外,开源代码是最有效的学习途径。一般而言,paper中只是着重描述比较创新的部分,80%以上的实现细节(甚至很多很关键的东西)需要从代码中了解。编程不好的人,往往也不太会读代码。
2. 架构软件,管理复杂项目的能力
一个SLAM方案通常由很多模块组成。我见过不少实验室的内部代码,一锅粥,学生们各种胡乱修补。这样必然导致效率低下。如果能做到模块化、低耦合,可以使开发事半功倍。
3. 高效实现的能力
过去八年间,我最少的一年也码了两万多行。。。根据我的体会,同样的算法,好的实现可以比不好的实现快2-10倍。复杂度越高越如是,例如3D SLAM。要拥有高效实现的能力,必须熟悉常用的数据结构,对复杂度分析形成很好的直觉。
4. 管理多线程、异步程序的能力
SLAM往往涉及到多线程和异步程序,例如用于后端优化等。玩儿不转这个就会出现程序不稳定,或效率低。
Matlab不能满足需要。Matlab对数据结构的支持太差,而且速度慢。即使不考虑产品,只是做算法,也很不适合。自己做算法验证基本上就是C++或python,如果你因为不熟悉这些而选其他语言,会因为参考少、轮子少的原因效率很低。强烈建议题主学习C++。
推荐文章
从零开始一起学习SLAM | 为什么要学SLAM?
从零开始一起学习SLAM | 学习SLAM到底需要学什么?
从零开始一起学习SLAM | 不推公式,如何真正理解对极约束?
从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码
欢迎关注公众号:计算机视觉life,一起探索计算机视觉新世界~
点击阅读原文可以查看原文链接。