研究SLAM,对编程的要求有多高?

新机器视觉

共 9499字,需浏览 19分钟

 ·

2021-09-18 02:17

来源 | 小白学视觉
研究SLAM的同学平时除了要看很多头疼的公式原理外,对编程也是有不少要求的。在学生阶段,很多同学都是用MATLAB作为主要编程语言,容易上手又简单。但是去看SLAM的开源代码,发现基本清一色都是C++,而且有不少开源代码使用C++到了出神入化的地步了。那么问题来了,学习SLAM是否可以不拘泥于编程语言的选择?到底对编程的要求有多高?

本文来自知乎上的同名问题,已经征得了部分答主的授权,对几个优秀回答进行了整理,如下

高翔

关于MATLAB

题主说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++大概要学到什么程度。用程序员的话说,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 constconst* 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的朋友们)

Kache

我觉得对编程要求还是挺高的,先从后端说起吧
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++。

—版权声明—

仅用于学术分享,版权属于原作者。

若有侵权,请联系微信号:yiyang-sy 删除或修改!


—THE END—
浏览 18
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报