ORB-SLAM2代碼閱讀筆記:優(yōu)化

ORBSLAM是一種基于優(yōu)化方法的SLAM方法,工程中引入了第三方庫(kù)g2o墅垮,g2o是基于圖優(yōu)化的優(yōu)化算法庫(kù)筏餐。圖優(yōu)化是將普通的優(yōu)化問題用圖的方式(變量用節(jié)點(diǎn)表示鸿秆,關(guān)系用邊來(lái)表示)來(lái)表示。

  • void Optimizer::BundleAdjustment
    3D-2D BA帚稠,在GlobalBundleAdjustemnt中調(diào)用谣旁,計(jì)算量比較大
    向優(yōu)化器添加關(guān)鍵幀位姿頂點(diǎn)

    g2o::VertexSE3Expmap * vSE3 = new g2o::VertexSE3Expmap();
    vSE3->setEstimate(Converter::toSE3Quat(pKF->GetPose()));
    vSE3->setId(pKF->mnId);
    vSE3->setFixed(pKF->mnId==0);
    optimizer.addVertex(vSE3);
    

    向優(yōu)化器添加MapPoints頂點(diǎn)

    g2o::VertexSBAPointXYZ* vPoint = new g2o::VertexSBAPointXYZ();
    vPoint->setEstimate(Converter::toVector3d(pMP->GetWorldPos()));
    const int id = pMP->mnId+maxKFid+1;//避免和位姿頂點(diǎn)ID重復(fù)
    vPoint->setId(id);
    vPoint->setMarginalized(true);
    optimizer.addVertex(vPoint);
    

    在添加每個(gè)MapPoints頂點(diǎn)時(shí)遍歷觀察到當(dāng)前地圖點(diǎn)的所有關(guān)鍵幀,向優(yōu) 化器中添加邊滋早。這條邊連著3D地圖點(diǎn)和位姿榄审,由于觀測(cè)數(shù)據(jù)格式不同,所 以這里要分成單目和雙目?jī)煞N情況添加邊杆麸。
    創(chuàng)建邊并填充數(shù)據(jù)

    g2o::EdgeSE3ProjectXYZ* e = new g2o::EdgeSE3ProjectXYZ();
    e->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>  (optimizer.vertex(id)));
    e->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>  (optimizer.vertex(pKF->mnId)));
    

    觀測(cè)數(shù)據(jù)需分兩種情況:

    • 單目:投影點(diǎn)的x搁进、y坐標(biāo)
     // 構(gòu)造觀測(cè)
    Eigen::Matrix<double,2,1> obs;
    obs << kpUn.pt.x, kpUn.pt.y;      
    
    • 雙目:投影點(diǎn)的x、y坐標(biāo)昔头,以及投影點(diǎn)在右目中的x坐標(biāo)(默認(rèn)y方向上已經(jīng)對(duì)齊了)
       Eigen::Matrix<double,3,1> obs;
    const float kp_ur = pKF->mvuRight[mit->second];
    obs << kpUn.pt.x, kpUn.pt.y, kp_ur;
    

    頂點(diǎn)和邊設(shè)置完后進(jìn)行優(yōu)化饼问,獲取優(yōu)化后的位姿和地圖點(diǎn)進(jìn)行保存。

  • int Optimizer::OptimizeSim3(KeyFrame *pKF1, KeyFrame *pKF2, vector<MapPoint *> &vpMatches1, g2o::Sim3 &g2oS12, const float th2, const bool bFixScale)
    形成閉環(huán)時(shí)進(jìn)行Sim3優(yōu)化揭斧,優(yōu)化目標(biāo)是是兩關(guān)鍵幀之間的相似變換矩陣

    • 設(shè)置優(yōu)化器算法
    • 將變量(當(dāng)前待優(yōu)化幀的初始位姿)作為非固定節(jié)點(diǎn)添加進(jìn)入圖優(yōu)化
    • 將變量(兩關(guān)鍵幀的地圖點(diǎn))作為固定節(jié)點(diǎn)添加進(jìn)入圖優(yōu)化
    • 添加邊將兩關(guān)鍵幀地圖點(diǎn)和待優(yōu)化幀的位姿連接莱革,每一個(gè)位姿對(duì)應(yīng)兩條邊,從關(guān)鍵幀1像素坐標(biāo)映射到關(guān)鍵幀2像素坐標(biāo)讹开,從關(guān)鍵幀2像素坐標(biāo)映射到關(guān)鍵幀1像素坐標(biāo)盅视。
    // step 2.3 添加兩個(gè)頂點(diǎn)(3D點(diǎn))到相機(jī)投影的邊 -- 投影到當(dāng)前關(guān)鍵幀 -- 正向投影
        g2o::EdgeSim3ProjectXYZ* e12 = new g2o::EdgeSim3ProjectXYZ();
        e12->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(id2)));
        // ? 沒看懂為什么這里添加的節(jié)點(diǎn)的id為0?
        e12->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(0)));
        e12->setMeasurement(obs1);
        // 信息矩陣也和這個(gè)點(diǎn)的可靠程度(在圖像金字塔中的圖層)有關(guān)
        const float &invSigmaSquare1 = pKF1->mvInvLevelSigma2[kpUn1.octave];
        e12->setInformation(Eigen::Matrix2d::Identity()*invSigmaSquare1);
    
        // 使用魯棒核函數(shù)
        g2o::RobustKernelHuber* rk1 = new g2o::RobustKernelHuber;
        e12->setRobustKernel(rk1);
        rk1->setDelta(deltaHuber);
        optimizer.addEdge(e12);
    
        // Set edge x2 = S21*X1
        // 接下來(lái)是添加投影到 閉環(huán)關(guān)鍵幀 -- 反向投影
        Eigen::Matrix<double,2,1> obs2;
        const cv::KeyPoint &kpUn2 = pKF2->mvKeysUn[i2];
        obs2 << kpUn2.pt.x, kpUn2.pt.y;
    
        g2o::EdgeInverseSim3ProjectXYZ* e21 = new g2o::EdgeInverseSim3ProjectXYZ();
    
        e21->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(id1)));
        // ? 這里添加的節(jié)點(diǎn)id也為0
        e21->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(0)));
        e21->setMeasurement(obs2);
        float invSigmaSquare2 = pKF2->mvInvLevelSigma2[kpUn2.octave];
        e21->setInformation(Eigen::Matrix2d::Identity()*invSigmaSquare2);
    
        g2o::RobustKernelHuber* rk2 = new g2o::RobustKernelHuber;
        e21->setRobustKernel(rk2);
        rk2->setDelta(deltaHuber);
        optimizer.addEdge(e21);
    
        vpEdges12.push_back(e12);
        vpEdges21.push_back(e21);
        vnIndexEdge.push_back(i);
    
    • 開始迭代優(yōu)化萧吠,迭代下降5次左冬,然后根據(jù)當(dāng)前優(yōu)化結(jié)果位姿篩選內(nèi)點(diǎn),用內(nèi)點(diǎn)重新進(jìn)行迭代下降優(yōu)化纸型,優(yōu)化結(jié)果為計(jì)算得到的兩關(guān)鍵幀之間的相似變換矩陣
  • void Optimizer::LocalBundleAdjustment(KeyFrame pKF, bool pbStopFlag, Map* pMap)
    局部BA優(yōu)化(tracking線程),得到當(dāng)前幀連接的KF和這些 KF中的地圖點(diǎn)后(lLocalKeyFrames和lLocalMapPoints)拇砰,和能觀測(cè)到這些地圖點(diǎn)但沒有和當(dāng)前幀相連的KF(lFixedCameras)。

    • 將變量(當(dāng)前關(guān)鍵幀及其共視關(guān)鍵幀的初始位姿)作為非固定(初始幀固定)節(jié)點(diǎn)添加進(jìn)入圖優(yōu)化狰腌,這里如果幀的id是0(第一幀)除破,則固定:
          vSE3->setFixed(pKFi->mnId==0);//第一幀位置固定
    

    固定的原因應(yīng)該是,如果第一幀不固定琼腔,優(yōu)化后的結(jié)果乘上一個(gè)變換瑰枫,同樣是最優(yōu)解,也就是說(shuō)如果第一幀不固定,會(huì)有很多很多可能的解

    • 將變量(觀察到當(dāng)前關(guān)鍵幀及其共視關(guān)鍵幀中地圖點(diǎn)的關(guān)鍵幀初始位姿)作為固定節(jié)點(diǎn)添加進(jìn)入圖優(yōu)化
    • 將變量(當(dāng)前關(guān)鍵幀及其共視關(guān)鍵幀的地圖點(diǎn))作為非固定節(jié)點(diǎn)添加進(jìn)入圖優(yōu)化
    • 在添加每個(gè)地圖點(diǎn)時(shí)遍歷關(guān)鍵幀光坝,創(chuàng)建邊并填充數(shù)據(jù)(和BA一樣)
    • 開始迭代優(yōu)化尸诽,迭代下降5次,然后根據(jù)當(dāng)前優(yōu)化結(jié)果位姿篩選內(nèi)點(diǎn)盯另,用內(nèi)點(diǎn)重新進(jìn)行迭代下降優(yōu)化性含。
    • 恢復(fù)優(yōu)化后的各關(guān)鍵幀位姿和關(guān)鍵幀地圖點(diǎn)。
  • int Optimizer::PoseOptimization(Frame *pFrame)
    用于Tracking中勻速運(yùn)動(dòng)模型跟蹤等鸳惯,這個(gè)優(yōu)化中只優(yōu)化Frame的Tcw商蕴,不優(yōu)化MapPoints的坐標(biāo),所以在構(gòu)造圖優(yōu)化的時(shí)候,是構(gòu)造的一元邊芝发。觀測(cè)是2維的Vector2d數(shù)據(jù)绪商,即像素坐標(biāo)。

    誤差函數(shù)為:

    添加當(dāng)前位姿頂點(diǎn)辅鲸,遍歷地圖點(diǎn)添加邊和觀測(cè)格郁,這里也是分成單目和雙目?jī)煞N情況,因?yàn)橛^測(cè)數(shù)據(jù)不同以及定義的邊的類型不同:
    • 單目:
    g2o::EdgeSE3ProjectXYZOnlyPose* e = new     g2o::EdgeSE3ProjectXYZOnlyPose();
    
    • 雙目:
    g2o::EdgeStereoSE3ProjectXYZOnlyPose* e = new g2o::EdgeStereoSE3ProjectXYZOnlyPose();
    
  • Optimizer::OptimizeEssentialGraph
    閉環(huán)檢測(cè)后的優(yōu)化
    首先拿到地圖中所有KF和地圖點(diǎn)瓢湃,聲明vScw和vCorrectedSwc理张,分別代表未sim3優(yōu)化前的sim3位姿和優(yōu)化后的。

    添加頂點(diǎn)時(shí)有兩種情況:如果該關(guān)鍵幀在閉環(huán)時(shí)通過(guò)Sim3傳播調(diào)整過(guò)绵患,用校正后的位姿雾叭;如果該關(guān)鍵幀在閉環(huán)時(shí)沒有通過(guò)Sim3傳播調(diào)整過(guò),用自身的位姿

          if(it!=CorrectedSim3.end())
          {
              vScw[nIDi] = it->second;
              VSim3->setEstimate(it->second);
          }
          else
          {
              Eigen::Matrix<double,3,3> Rcw = Converter::toMatrix3d(pKF->GetRotation());
              Eigen::Matrix<double,3,1> tcw = Converter::toVector3d(pKF->GetTranslation());
              g2o::Sim3 Siw(Rcw,tcw,1.0);
              vScw[nIDi] = Siw;
              VSim3->setEstimate(Siw);
          }
    

    連接兩個(gè)Sim3節(jié)點(diǎn)的二元邊落蝙,頂點(diǎn)0的位姿是Tiw,1的位姿是Tjw织狐,那么邊的測(cè)量是Tji,即from i to j筏勒。

              const g2o::Sim3 Sjw = vScw[nIDj];
              // 得到兩個(gè)pose間的Sim3變換
              const g2o::Sim3 Sji = Sjw * Swi;
    
              g2o::EdgeSim3* e = new g2o::EdgeSim3();
              e->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(nIDj)));
              e->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(nIDi)));
              // 根據(jù)兩個(gè)Pose頂點(diǎn)的位姿算出相對(duì)位姿作為邊移迫,那還存在誤差??jī)?yōu)化有用管行?因?yàn)殚]環(huán)MapPoints調(diào)整新形成的邊不優(yōu)化厨埋?(wubo???)
              // REVIEW 我和師兄有同樣的疑問
              e->setMeasurement(Sji);
    
              // 信息矩陣是單位陣,說(shuō)明sim3中每個(gè)自由度的貢獻(xiàn)都是一樣的,并且所有的這個(gè)邊對(duì)總誤差的貢獻(xiàn)也都是一樣大的
              e->information() = matLambda;
    
              optimizer.addEdge(e);
    

圖優(yōu)化中的魯棒核函數(shù)

SLAM中可能給出錯(cuò)誤的邊。SLAM中的數(shù)據(jù)關(guān)聯(lián)讓科學(xué)家頭疼了很長(zhǎng)時(shí)間捐顷。出于變化荡陷、噪聲等原因,機(jī)器人并不能確定它看到的某個(gè)路標(biāo)迅涮,如果出現(xiàn)錯(cuò)誤废赞,就會(huì)出現(xiàn)一條誤差很大的邊,然后試圖調(diào)整這條邊所連接的節(jié)點(diǎn)的估計(jì)值叮姑,使它們順應(yīng)這條邊的無(wú)理要求唉地。由于這個(gè)邊的誤差真的很大,往往會(huì)抹平了其他正確邊的影響,使優(yōu)化算法專注于調(diào)整一個(gè)錯(cuò)誤的值耘沼。
核函數(shù)作用就是保證每條邊的誤差不會(huì)大的沒邊极颓,掩蓋掉其他的邊。具體的方式是群嗤,把原先誤差的二范數(shù)度量讼昆,替換成一個(gè)增長(zhǎng)沒有那么快的函數(shù),同時(shí)保證自己的光滑性質(zhì)(不然沒法求導(dǎo)吧铡!)闰围。因?yàn)樗鼈兪沟谜麄€(gè)優(yōu)化結(jié)果更為魯棒赃绊,所以又叫它們?yōu)閞obust kernel(魯棒核函數(shù))。很多魯棒核函數(shù)都是分段函數(shù)羡榴,在輸入較大時(shí)給出線性的增長(zhǎng)速率碧查,例如cauchy核,huber核等等校仑。當(dāng)然具體的我們也不展開細(xì)說(shuō)了忠售。

這段摘自 深入理解圖優(yōu)化與g2o:圖優(yōu)化篇

高斯牛頓法

  • 牛頓法

    牛頓法是從泰勒公式展開得到的
    迭代式為:

上式為一維的情況,擴(kuò)展到多維時(shí)牛頓法的迭代式為:

其中H為海塞矩陣迄沫,
為梯度
  • 高斯牛頓法

    高斯牛頓法通過(guò)下面方法替代海塞矩陣:
  • LM法
    高斯-牛頓法中為了避免發(fā)散稻扬,有兩種解決方法
    1.調(diào)整下降步伐:β^{s+1}=βs+α Δ.0<α<1
    2.調(diào)整下降方向:4(JTJ+λD)Δ=JTrλ→+∞時(shí):Δ/λ→J^Tr$,即方向和梯度方向一樣羊瘩,變成了梯度下降法泰佳。

相反,如果λ為0尘吗,就變成了高斯牛頓法逝她。
Levenberg-Marquardt方法的好處在于可以調(diào)節(jié):
如果下降太快,使用較小的λ睬捶,使之更接近高斯牛頓法
如果下降太慢黔宛,使用較大的λ,使之更接近梯度下降法

此外擒贸,高斯牛頓法中涉及求逆矩陣的操作臀晃,(J^TJ) 加入λ 也可以保證該矩陣為一個(gè)正定矩陣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酗宋,一起剝皮案震驚了整個(gè)濱河市积仗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜕猫,老刑警劉巖寂曹,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡隆圆,警方通過(guò)查閱死者的電腦和手機(jī)漱挚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)渺氧,“玉大人旨涝,你說(shuō)我怎么就攤上這事÷卤常” “怎么了白华?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贩耐。 經(jīng)常有香客問我弧腥,道長(zhǎng),這世上最難降的妖魔是什么潮太? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任管搪,我火速辦了婚禮,結(jié)果婚禮上铡买,老公的妹妹穿的比我還像新娘更鲁。我一直安慰自己,他們只是感情好奇钞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布澡为。 她就那樣靜靜地躺著,像睡著了一般景埃。 火紅的嫁衣襯著肌膚如雪缀壤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天纠亚,我揣著相機(jī)與錄音塘慕,去河邊找鬼。 笑死蒂胞,一個(gè)胖子當(dāng)著我的面吹牛图呢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骗随,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛤织,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鸿染?” 一聲冷哼從身側(cè)響起指蚜,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涨椒,沒想到半個(gè)月后摊鸡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绽媒,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年免猾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了是辕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猎提,死狀恐怖获三,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锨苏,我是刑警寧澤疙教,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站伞租,受9級(jí)特大地震影響松逊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肯夏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犀暑。 院中可真熱鬧驯击,春花似錦、人聲如沸耐亏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)广辰。三九已至暇矫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間择吊,已是汗流浹背李根。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留几睛,地道東北人房轿。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像所森,于是被迫代替她去往敵國(guó)和親囱持。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容