研究SLAM,對編程的要求有多高?
本文來自知乎上的同名問題,已經(jīng)征得了部分答主的授權(quán),對幾個(gè)優(yōu)秀回答進(jìn)行了整理,如下
題主說MATLAB,主要原因是大多數(shù)人本科階段接觸的都是MATLAB,所以希望之后研究SLAM也用它。
MATLAB確實(shí)有很多優(yōu)點(diǎn):語法簡單,開發(fā)速度快,調(diào)試方便,功能豐富。然而,在SLAM領(lǐng)域,MATLAB缺點(diǎn)也很明顯,主要是這兩個(gè):
需要正版軟件(你不能實(shí)機(jī)上也裝個(gè)盜版MATLAB吧);
運(yùn)行效率不高;
需要一個(gè)巨大的安裝包;
而相對的,C++的優(yōu)勢在于直接使用,有很高的運(yùn)行效率,不過開發(fā)速度和調(diào)試方面慢于MATLAB。不過光運(yùn)行效率這一條,就夠許多SLAM方案選擇C++作為開發(fā)語言了,因?yàn)檫\(yùn)行效率真的很重要。同一個(gè)算法,拿MATLAB寫出來實(shí)現(xiàn)不能實(shí)時(shí),拿C++寫的能實(shí)時(shí),你說用哪個(gè)?
當(dāng)然MATLAB也有一些用武之地。我見過一些SLAM相關(guān)的公開課程,讓學(xué)生用MATLAB做仿真,交作業(yè),這沒有問題,比如SLAM toolbox 。同樣的,比較類似于MATLAB的Python(以及octave)亦常被用于此道。它們在開發(fā)上的快捷帶來了很多便利,當(dāng)你想要驗(yàn)證一些數(shù)學(xué)理論、思想時(shí),這些都是不錯的工具。所謂技多不壓身,題主掌握MATLAB和Python當(dāng)然是很棒的。
但是一牽涉到實(shí)用,你會發(fā)現(xiàn)幾乎所有的方案都在用C++。因?yàn)檫\(yùn)行效率實(shí)在是太重要了。
那既然有心思學(xué)MATLAB,為什么不學(xué)好C++呢?
接下來說說C++大概要學(xué)到什么程度。用程序員的話說,C++語言比較特殊,你可以說自己精通了Java,但千萬不要說自己精通了C++。C++非常之博大精深,有數(shù)不清的特性,而且隨著時(shí)間還會不斷變化更新。不過,大多數(shù)人都用不著學(xué)會所有的C++特性,因?yàn)樵S多東西一輩子都用不到。
作為SLAM研究人員,我們面對的主要是算法層面的開發(fā),所以更關(guān)心如何有效地實(shí)現(xiàn)各種相關(guān)的算法。而相對的,那些復(fù)雜的軟件架構(gòu),設(shè)計(jì)模式,我個(gè)人認(rèn)為在SLAM中倒是占次要地位的。畢竟您用SLAM的目的是計(jì)算一個(gè)位置以及建個(gè)地圖,并不是要去寫一套能夠自動更新的、多人網(wǎng)上對戰(zhàn)功能的機(jī)器人大戰(zhàn)平臺。您的主要精力可能會花在矩陣運(yùn)算、分塊、非線性優(yōu)化的實(shí)現(xiàn)、圖像處理上面;您可能對并發(fā)、指令集加速、GPU加速等話題感興趣,也可以花點(diǎn)時(shí)間學(xué)習(xí);你還可能想用模板來拓展你的算法,也不妨一試。相應(yīng)的,很多功能性的東西,比如說UI、網(wǎng)絡(luò)通信等等,當(dāng)你用到的時(shí)候不妨接觸一下,但專注于SLAM上時(shí)就不必專門去學(xué)習(xí)了。
話雖如此,SLAM所需的C++水平,大抵要高于你在書本上看到的那些個(gè)示例代碼。因?yàn)槟切┐a是作者用來向初學(xué)者介紹語法的,所以會盡量簡單。而實(shí)際見到的代碼往往結(jié)合了各種奇特的技巧,乍看起來會顯得高深莫測。比方說你在教科書里看的大概是這樣:
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++也不過如此,并沒有啥特別難以理解的地方。然而實(shí)際代碼大概是這樣的:
嵌套的模板類(來自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的自動求導(dǎo)):
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); });
}
謎之運(yùn)算(來自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;
}
我不知道你們看到這些代碼是什么心情,總之我當(dāng)時(shí)內(nèi)心的感受是:臥槽這怎么和教科書里的完全不一樣??!而且研究了半天發(fā)現(xiàn)人家居然是對的??!
[我不是很擅長貼表情圖總之你們腦補(bǔ)一下就好]
總而言之,對C++的水平要求應(yīng)該是在教科書之上的。而且這個(gè)水平的提高,多數(shù)時(shí)候建立在你不斷地看別人代碼、碼自己代碼的過程之上。它是反復(fù)練習(xí)出來的,并不是僅僅通過看書就能領(lǐng)會的。特別是對于視覺SLAM問題,很多時(shí)候你沒法照著論文把一套方案實(shí)現(xiàn)出來,這很大程度上取決于你的理論和代碼功底。
所以,請盡早開始學(xué)習(xí)C++,盡早開始使用C++,才是研究SLAM的正確之道。不要長期彷徨在自己的舒適區(qū)里猶豫不決,這樣是沒有進(jìn)步的。(同樣的道理亦適用于想研究SLAM但不愿意學(xué)習(xí)Linux的朋友們)
我覺得對編程要求還是挺高的,先從后端說起吧
g2o, gtsam,ceres知道是肯定需要知道的,三者其實(shí)學(xué)習(xí)曲線都挺難的,你覺得你會用了是一回事,然后用好是一回事,自己寫vertex, edge, factor, cost function又是另外一回事,如果你會寫g2o里面的solver應(yīng)該又是一個(gè)級別了吧。但是這三個(gè)在網(wǎng)上的資源而論g2o > ceres > gtsam。再者,你會調(diào)用api就代表你懂優(yōu)化了嗎,自己動手matlab寫一下gauss-newton應(yīng)該也可以試一試吧。當(dāng)然,遠(yuǎn)古一點(diǎn)了levmar也很不錯哦。直到這里,你確定你明白各種算法底層suitesparse里面的效率問題嗎,如果挖掘到了這一層,還有各種線性代數(shù)庫的比較哦,肯定是又快有慢。所以說,想要學(xué)會一到兩個(gè)優(yōu)化器不難,學(xué)會自定義有一點(diǎn)難,考慮到后端的效率自己寫solver更難,深入到線性代數(shù)庫的最底層,考慮問題結(jié)構(gòu),提升效率就更難了
前端,其實(shí)僅僅考慮點(diǎn)特征,也就那么多東西,V-SLAM用OPENCV也就夠用了,而且opencv我覺得到API調(diào)用那一層也就足夠了。如果考慮上RGBD,再加上一個(gè)PCL。理論知識方面基本的Multiview geometry其實(shí)也就足夠了。但是前端的問題在于很多都是經(jīng)驗(yàn)的東西,參數(shù)的選擇,循環(huán)的次數(shù)等等。說白了我個(gè)人覺得視覺部分調(diào)參的問題更多,理論倒是不容易出問題。
前后段一起的話,什么線程管理就不用說了,該用的庫都得上,例如boost啥的。有時(shí)候做visualization可能還需要一些別的庫。
說完好像要求也就那樣,反正我又不是CS出身活得還好好的
要搞SLAM,編程能力非常非常重要。編程能力就像運(yùn)動員的力量和體能,是一切技巧和戰(zhàn)術(shù)的基礎(chǔ)。雖然研究的是算法,但良好的編程能力能夠使你節(jié)省大量的時(shí)間,并且達(dá)到更好的效果。
根據(jù)我的經(jīng)驗(yàn),算法搞得特別好的人很少有編程不行的,因?yàn)榫幊毯每梢允鼓隳軌蜓杆衮?yàn)證想法。見過不少學(xué)生因?yàn)榫幊袒A(chǔ)不好,在實(shí)現(xiàn)和debug上浪費(fèi)大把大把的時(shí)間。這樣的人當(dāng)然很難把算法搞好。
這里編程能力有很多方面,以下簡單說幾個(gè)。贊多了再補(bǔ)( ̄▽ ̄)/
1. 閱讀代碼的能力
未說寫,先說讀。搞科研和搞工程都大忌閉門造車,除了paper外,開源代碼是最有效的學(xué)習(xí)途徑。一般而言,paper中只是著重描述比較創(chuàng)新的部分,80%以上的實(shí)現(xiàn)細(xì)節(jié)(甚至很多很關(guān)鍵的東西)需要從代碼中了解。編程不好的人,往往也不太會讀代碼。
2. 架構(gòu)軟件,管理復(fù)雜項(xiàng)目的能力
一個(gè)SLAM方案通常由很多模塊組成。我見過不少實(shí)驗(yàn)室的內(nèi)部代碼,一鍋粥,學(xué)生們各種胡亂修補(bǔ)。這樣必然導(dǎo)致效率低下。如果能做到模塊化、低耦合,可以使開發(fā)事半功倍。
3. 高效實(shí)現(xiàn)的能力
過去八年間,我最少的一年也碼了兩萬多行。。。根據(jù)我的體會,同樣的算法,好的實(shí)現(xiàn)可以比不好的實(shí)現(xiàn)快2-10倍。復(fù)雜度越高越如是,例如3D SLAM。要擁有高效實(shí)現(xiàn)的能力,必須熟悉常用的數(shù)據(jù)結(jié)構(gòu),對復(fù)雜度分析形成很好的直覺。
4. 管理多線程、異步程序的能力
SLAM往往涉及到多線程和異步程序,例如用于后端優(yōu)化等。玩兒不轉(zhuǎn)這個(gè)就會出現(xiàn)程序不穩(wěn)定,或效率低。
Matlab不能滿足需要。Matlab對數(shù)據(jù)結(jié)構(gòu)的支持太差,而且速度慢。即使不考慮產(chǎn)品,只是做算法,也很不適合。自己做算法驗(yàn)證基本上就是C++或python,如果你因?yàn)椴皇煜み@些而選其他語言,會因?yàn)閰⒖忌?、輪子少的原因效率很低。?qiáng)烈建議題主學(xué)習(xí)C++。
—版權(quán)聲明—
僅用于學(xué)術(shù)分享,版權(quán)屬于原作者。
若有侵權(quán),請聯(lián)系微信號:yiyang-sy 刪除或修改!
