合優(yōu)化的工程實踐)
做視覺SLAM的各位應(yīng)該都有過這種體驗明明前端跟蹤沒毛病后端優(yōu)化看起來也對但地圖點就是飄、姿態(tài)就是抖。行內(nèi)人都知道問題大概率出在滑窗里的SFM環(huán)節(jié)——相機(jī)位姿沒鎖準(zhǔn)3D點自然白扯。這篇接著系列往下聊專門說說Vins-Fusion處理滑窗內(nèi)SFM的關(guān)鍵動作從兩兩相對位姿估計到三角化再到位姿和3D點的聯(lián)合優(yōu)化同時把我調(diào)試過程中踩過的坑一并曬出來。這套東西適合誰看正準(zhǔn)備接手VINS系代碼、被初始化弄得頭大的人或者已經(jīng)在跑自己數(shù)據(jù)但是效果不穩(wěn)、想搞明白“到底該查前端還是后端”的人。我把原理邏輯和實操經(jīng)驗揉在一起講盡量讓沒有系統(tǒng)學(xué)過視覺幾何的讀者也能跟上。1. SFM在Vins-Fusion滑窗里到底扮演什么角色1.1 滑窗不是靜態(tài)快照而是動態(tài)測量系統(tǒng)很多新手看Vins-Fusion的代碼第一反應(yīng)是找“建圖模塊”結(jié)果翻半天發(fā)現(xiàn)根本沒什么顯式的重建流程只有一堆位姿、狀態(tài)量、殘差。實際上整條pipeline的核心就是“邊估位姿邊估點”只不過它把這個問題限制在一個很小的滑窗里反復(fù)做?;耙话憔S持10到13幀關(guān)鍵幀不同版本和配置略有差異窗口每往前滑一步就丟掉最老的幀引入新幀。這個窗口本質(zhì)上是一個動態(tài)的局部測量系統(tǒng)新關(guān)鍵幀帶來新的觀測老關(guān)鍵幀要被邊緣化中間的所有幀和特征點需要持續(xù)更新。SFM就是在這個窗口里完成的“視覺結(jié)構(gòu)恢復(fù)”——它既不是離線跑一次就完事也不是純前端的跟蹤而是整個狀態(tài)估計器的初始化器和校正器。我習(xí)慣打一個比方全局BA是修一棟大樓滑窗SFM是每次只修你所在的那一層但是每修一層都會把承重墻澆好等修到頂樓時整棟樓不會塌。這個“每一層的承重墻”就是滑窗里被反復(fù)驗證過的相機(jī)位姿和3D點。1.2 為什么必須用滑窗而不是全局BA理論上SFM最“干凈”的做法是把所有幀和所有點一次性拿來做光束平差得到全局最優(yōu)解。但現(xiàn)實里這根本跑不動——移動機(jī)器人連續(xù)跑一個小時可能積累上萬幀、幾十萬個特征點全局BA的計算量爆炸。更麻煩的是環(huán)境是會變的光照會變、動態(tài)物體會進(jìn)出視野你辛辛苦苦把所有歷史幀鎖在一起一旦某段軌跡漂了后期花多大力氣也拉不回來。所以Vins-Fusion采用滑窗策略本質(zhì)上是在精度和實時性之間找一個工程上可接受的平衡點?;暗某叽鐩Q定了局部結(jié)構(gòu)的記憶長度窗口太小位姿之間約束太少視覺退化和尺度漂移很容易爆發(fā)窗口太大后端優(yōu)化的實時性就保不住尤其還要和IMU做緊耦合的時候。我實測下來10到13幀是一個很穩(wěn)定的區(qū)間既能讓視差足夠覆蓋同一個特征點的多次觀測又不至于讓后端優(yōu)化單次迭代耗時超過幾十毫秒。另一個經(jīng)常被忽略的理由是“不確定性管理”。全局BA總假設(shè)所有誤差是高斯、獨立的但實際跑數(shù)據(jù)時早期幀的誤差結(jié)構(gòu)早就不準(zhǔn)了硬拉進(jìn)去參與優(yōu)化反而會污染后面的估計。滑窗的意義在于老幀的占用信息被壓縮成先驗項而不是讓原始觀測“陰魂不散”地一直干擾新估計。1.3 數(shù)據(jù)流動從圖像到位姿再到3D點的管線在滑窗SFM里數(shù)據(jù)流向是嚴(yán)格分層的。第一步前端拿到圖像序列通過特征提取和光流跟蹤得到每一幀上特征點的2D像素坐標(biāo)并且把不同幀之間同一物理點的對應(yīng)關(guān)系維護(hù)起來。第二步利用這些2D對應(yīng)關(guān)系估計相鄰關(guān)鍵幀之間的相對位姿。第三步有了位姿用多視圖三角化恢復(fù)特征點的3D坐標(biāo)。第四步把相機(jī)位姿和3D特征點都放進(jìn)同一個優(yōu)化目標(biāo)里做局部BA讓兩者互相糾正。注意第四步不是說“第三步做完再優(yōu)化一次”而是在滑窗內(nèi)反復(fù)進(jìn)行的。我把SFM和BA理解成一個連續(xù)交互的過程SFM提供初始幾何BA把它打磨結(jié)實BA打磨完的位姿又會讓下一輪SFM三角化更準(zhǔn)確。這個循環(huán)才叫“聯(lián)合估計”很多剛起步的人把SFM當(dāng)作離線預(yù)處理概念上就跑偏了。2. 相機(jī)位姿估計從匹配點到相對運動2.1 特征跟蹤產(chǎn)出的“測量值”長什么樣進(jìn)入位姿估計前得先搞清楚輸入到底是什么。Vins-Fusion前端的特征點庫里每個特征點記錄了一串觀測在哪些幀出現(xiàn)、每次出現(xiàn)時的像素坐標(biāo)、歸一化坐標(biāo)、特征描述子或者光流跟蹤時的patch信息。我們要用的是歸一化坐標(biāo)也就是把像素坐標(biāo)經(jīng)過內(nèi)參變換到相機(jī)坐標(biāo)系下的一個方向向量。為什么用歸一化坐標(biāo)因為本質(zhì)矩陣和單應(yīng)矩陣模型都建立在對極約束的幾何關(guān)系上本身不關(guān)心內(nèi)參提前歸一化可以讓問題更干凈。實際代碼里這一步通常是先將像素坐標(biāo)減掉主點再除以焦距得到camera frame下的坐標(biāo)即x(u-cx)/fx。這里有個常見坑如果你手里的內(nèi)參是標(biāo)定好的歸一化只按原圖尺寸做一次即可。如果用了圖像縮放必須把內(nèi)參同步縮放否則后面所有的閾值判斷都會失效。跟蹤質(zhì)量直接決定位姿估計的上限。我通常要求一個關(guān)鍵幀被至少跟蹤到15到20個特征點光流成功且通過雙向一致性檢查少于這個數(shù)量我會直接拒絕把該幀設(shè)為關(guān)鍵幀繼續(xù)等待新幀積累視差。這看起來保守實際上能省掉大量RANSAC拉鋸時間。2.2 本質(zhì)矩陣與單應(yīng)矩陣的選擇邏輯兩幀之間的相對位姿估計核心問題是從2D對應(yīng)關(guān)系中恢復(fù)R和t。根據(jù)場景不同同一個問題可以用兩種模型本質(zhì)矩陣E適合大部分普通三維場景自由度為5最少5個點對可解即五點法。E t^× R分解后直接得到相對旋轉(zhuǎn)和平移方向。單應(yīng)矩陣H當(dāng)場景近似平面或者相機(jī)發(fā)生純旋轉(zhuǎn)時本質(zhì)矩陣模型會退化此時用H二維單應(yīng)更穩(wěn)定。H的自由度為8最少4個點對。VINs-Fusion的初始化模塊里并不會徹底放棄任何一個模型而是把H和E都估出來用統(tǒng)一的評分機(jī)制比較RANSAC內(nèi)點數(shù)和重投影殘差誰更優(yōu)就選誰。這個“雙模型競爭”的做法我認(rèn)為極其重要。如果只跑E模型你在走廊墻面前跑直線特征點幾乎都落在一個平面上本質(zhì)矩陣會非常病態(tài)初始化常常炸掉如果只跑H模型遇到真正的非平面場景單應(yīng)矩陣擬合出的模型同樣不靠譜。實操中的經(jīng)驗是模型選擇不要只看內(nèi)點數(shù)還要看殘差的分布。如果H模型內(nèi)點殘差普遍很大而E模型內(nèi)點殘差小而集中哪怕H內(nèi)點數(shù)更高也優(yōu)先選擇E。因為平面場景下H的內(nèi)點可能很多但那些內(nèi)點的幾何約束本身就很弱用于位姿解算反而會把噪聲放大。2.3 RANSAC內(nèi)點閾值到底怎么給RANSAC閾值的選擇經(jīng)常被隨便填實際上它直接關(guān)系到位姿的精度和魯棒性。閾值過小真實匹配都被當(dāng)成外點丟棄內(nèi)點率難看閾值過大大量錯匹配混進(jìn)來E矩陣被帶偏。在歸一化平面坐標(biāo)的情況下RANSAC的距離度量通常是對極線距離。我把閾值設(shè)在0.002到0.01之間歸一化單位對應(yīng)像素大約0.2到0.8個像素在640x480的圖像上歸一化1.0大約對應(yīng)幾百像素所以這個區(qū)間很敏感。具體值需要根據(jù)你圖像噪聲和標(biāo)定精度來定。平時跑車載數(shù)據(jù)圖像噪聲大一點我會放到0.01如果是實驗室高分辨率靜物場景0.002就能跑得很好。還有一點容易被忽略RANSAC的迭代次數(shù)和置信度要配合。一般設(shè)置信度0.99初始內(nèi)外點比例未知時迭代次數(shù)從幾十到幾百動態(tài)決定。我見過有人為了“省時間”把最大迭代次數(shù)鎖死在50結(jié)果在內(nèi)點率低的場景里解出來的模型完全是噪聲擬合。RANSAC本身不貴貴的是你對它過度信任。2.4 分解出R和t之后不能直接用于建圖從E分解出R和t是個數(shù)學(xué)上很優(yōu)雅、工程上很麻煩的過程。E矩陣本質(zhì)上是3x3矩陣但它必須滿足兩個奇異值相等、第三個為0的約束。實際計算出來的E由于噪聲不會完美滿足這個結(jié)構(gòu)所以通常先做一次SVD然后把奇異值強(qiáng)制設(shè)為(1, 1, 0)附近再重新合成。分解時R存在兩個候選解加上t的方向正負(fù)一共4種組合。正確的組合需要靠三角化后的“正深度檢驗”來挑選把特征點三角化看它是否同時在兩個相機(jī)的正前方深度大于零一致的那個組合才被認(rèn)為是正確相對位姿。這一步是失敗高發(fā)區(qū)。我見過很多次初始化崩潰不是因為E分解算錯了而是因為深度檢驗只做了單幀檢查或者只檢查了一小部分點。正確做法是對所有被模型覆蓋的內(nèi)點做三角化統(tǒng)計深度大于零的比例同時檢查某幾個點的重投影誤差綜合投票。只檢查少數(shù)幾個點一旦這幾點恰好是外點你就選了一個完全錯誤的位姿組合后面整個滑窗都救不回來。3. 特征點3D位置估計三角化的工程實現(xiàn)3.1 三角化本質(zhì)上是解超定方程有了相機(jī)位姿下一步是把特征點從2D映射成3D。這個動作看起來像是“兩束射線求交點”但實際上因為觀測噪聲兩條射線在空間里根本不交而是錯開的。所以三角化本質(zhì)是在解一個最小二乘問題找一個3D點讓它在所有觀測相機(jī)上的重投影誤差盡可能小。工程上最通用的是線性三角化DLT一類的方法每個觀測都提供兩個方程x坐標(biāo)和y坐標(biāo)各一個把X的三個未知數(shù)或齊次形式的四個未知數(shù)代入最后得到一個超定線性方程組AX 0。解法是SVD分解取最小奇異值對應(yīng)的右奇異向量歸一化后得到3D坐標(biāo)。這里我要特別提醒一個理論之外的問題輸入點位必須先做歸一化。直接在像素坐標(biāo)上做DLT數(shù)值條件會很差誤差大的離譜。正確的做法是先在每幅圖像上做坐標(biāo)歸一化這不同于前面提到的相機(jī)內(nèi)參歸一化將所有2D坐標(biāo)縮放和中心化解完再把結(jié)果變換回世界坐標(biāo)。這一步在很多簡化的教學(xué)代碼里被省掉我教你一個判斷方法如果三角化出的點深度經(jīng)常是負(fù)的或者重投影誤差不穩(wěn)定優(yōu)先檢查有沒有做歸一化。3.2 微小視差會讓解病態(tài)三角化的精度對“視差角”極其敏感。視差角就是相機(jī)在觀測同一個點時兩條光線的夾角。夾角太小意味著兩幀相機(jī)位置幾乎沒變化3D點的深度就從噪聲中徹底不可觀了。我在實際數(shù)據(jù)里驗證過對一個距離10米的點如果基線只有0.1米視差角不到0.6度三角化出的深度方差會大到一個離譜的程度——看起來重投影誤差很小但3D點位置完全不可信。所以滑窗SFM里一定要加視差檢驗要么算兩個相機(jī)光心到特征點的光線夾角要么更直觀地在圖像上數(shù)特征點位移像素數(shù)。VINS這類系統(tǒng)一般會判斷“足夠的視差才把當(dāng)前幀設(shè)為關(guān)鍵幀”目的就是為了保證后續(xù)三角化和優(yōu)化的幾何條件不至于病態(tài)。我在自己的參數(shù)表里設(shè)了一個最小視差角閾值大約0.5度到1度具體取決于特征點噪聲水平。噪聲越大閾值越要往高調(diào)。如果你發(fā)現(xiàn)三角化出的點跟著噪聲抖動先把視差閾值往上拉比你去調(diào)RANSAC閾值更管用。另一個經(jīng)驗是不要在只有2幀觀測時就把3D點鎖死?;袄镆粋€特征點至少要被3到5幀連續(xù)觀測到才值得把它納入優(yōu)化狀態(tài)。觀測次數(shù)太少三角化的不確定度沒有充分約束加入BA只會增加計算量不會帶來精度收益。3.3 給3D點做體檢重投影、深度、共線三角化完畢不能直接“上戶口”。我習(xí)慣對每個3D點做三項體檢至少兩項通過才留用重投影誤差把3D點重新投影到每一幀圖像上計算與原始觀測的像素距離。一般要求小于2像素。這個指標(biāo)很直觀但只能反映“是否與觀測一致”不能說明“是否真的準(zhǔn)”。深度正負(fù)檢查點在各個相機(jī)坐標(biāo)系下的深度是否都大于零。這個必須嚴(yán)格通過允許少數(shù)異常點被剔除。共線檢查檢查3D點到相機(jī)光心的連線方向與2D觀測方向的一致性尤其在多幀觀測時如果觀測之間的方向變化很亂說明跟蹤大概率已經(jīng)漂了。還有一個工程細(xì)節(jié)當(dāng)一個3D點在后續(xù)滑窗中不斷被新觀測更新時我建議定期用滑窗內(nèi)所有觀測重新做一次帶約束的三角化而不是只保留第一次三角化結(jié)果。因為位姿在BA中會更新初始三角化的點不再嚴(yán)格滿足新位姿下的幾何關(guān)系定期“換一次底”能顯著減少累計誤差。4. 滑窗內(nèi)聯(lián)合優(yōu)化SFM和BA的銜接4.1 怎么把兩兩位姿拼成一致軌跡SFM輸出的本質(zhì)是相對位姿和3D點但要真正發(fā)揮價值必須把散落的相對位姿統(tǒng)一到滑窗的一個坐標(biāo)系下。這個統(tǒng)一過程通常以滑窗里較老的一幀為參考系通過位姿乘法把所有幀的絕對位姿算出來。第一次初始化時可能用第一幀作為世界坐標(biāo)系原點后續(xù)滑窗滑動時需要保證新老坐標(biāo)系之間的一致性。這一步的坑在于你從E分解出的t是單位尺度它沒有任何物理尺度信息。在純視覺滑窗里尺度本來就是自由漂移的。Vins-Fusion之所以引入IMU預(yù)積分就是要把重力方向和尺度拉回真實的物理量綱。如果你的場景里只有純視覺沒有IMU那你必須接受sfm結(jié)果在無尺度空間內(nèi)更新不能直接拿去當(dāng)真實軌跡。拼接軌跡時我強(qiáng)烈建議用李群SE(3)上做插值和平均而不是把旋轉(zhuǎn)矩陣拆成歐拉角來融合。歐拉角在接近90度時會奇異性爆表哪怕你數(shù)據(jù)里根本轉(zhuǎn)不到那個角度數(shù)值上也會出現(xiàn)奇怪的跳動。代碼里直接用四元數(shù)或旋轉(zhuǎn)矩陣的log/exp映射至少能保證軌跡的連續(xù)性。4.2 滑窗BA為什么要走稀疏求解到位姿和3D點都有了初值接下來就是局部BA的活。BA的數(shù)學(xué)模型并不復(fù)雜最小化所有殘差的平方和其中殘差是3D點在某一幀的重投影誤差。復(fù)雜的是怎么快速求解。假設(shè)滑窗里有11幀、200個特征點那么未知量就是11個位姿每個6自由度加尺度信息和200個3D點每個3自由度。這個規(guī)??粗淮蟮苯訉artesian坐標(biāo)求導(dǎo)并填充Hessian矩陣矩陣會非常龐大且稠密。幸好BA的Jacobian天然帶有稀疏結(jié)構(gòu)某個殘差只連接一個3D點和一幀位姿于是整個Hessian矩陣呈塊狀稀疏布局絕大部分元素是零。利用這個稀疏性標(biāo)準(zhǔn)做法是先對特征點塊做Schur消元也就是先在數(shù)學(xué)上把3D點的量“消”掉只剩下位姿相關(guān)的量最后組成一個維度小很多的位姿增量線性系統(tǒng)。我見過有人為了省事直接調(diào)用通用非線性優(yōu)化庫卻不開稀疏求解器結(jié)果每輪迭代耗時幾十毫秒滑窗變成幻燈片。正確姿勢一定是用精求解器開啟SCHUR_STRATEGY和稀疏線性代數(shù)把邊緣化掉的路標(biāo)點塊提前消元速度可以提升一個數(shù)量級。這里再提一個能提升穩(wěn)定性的技巧在BA迭代前把圖像坐標(biāo)投影誤差乘以一個huber核函數(shù)魯棒核從而抑制大誤差項對解的牽引。Vins-Fusion的殘差實現(xiàn)里也引入了這類魯棒化處理我的經(jīng)驗是誤匹配率在1%3%時加了huber核能明顯改善優(yōu)化收斂性。如果你發(fā)現(xiàn)BA結(jié)果在一次誤匹配下就大跳八成是核函數(shù)沒加或者閾值設(shè)置太寬松。4.3 邊緣化把老幀的貢獻(xiàn)折算成先驗滑窗每前進(jìn)一步最老的關(guān)鍵幀就要被移出窗口。直接把它丟掉會損失掉它與其他幀之間的約束信息系統(tǒng)會在某一刻“忘掉”過去導(dǎo)致漂移。所以這里要用邊緣化Marginalization把被移除幀的所有約束信息壓縮成一個先驗概率項附加到后續(xù)的優(yōu)化問題中。邊緣化的數(shù)學(xué)工具是Schur補(bǔ)。把要刪除的變量從信息矩陣中消掉剩下的信息矩陣加上測量貢獻(xiàn)就形成新的先驗。我聽得懂但真正寫代碼時比較容易忘記的是邊緣化的前提是線性化點固定。一旦先驗項在你當(dāng)前的雅可比線性化點生成之后不要再用新雅可比反復(fù)線性化它否則信息會反復(fù)放大導(dǎo)致所謂的“信息矩陣重復(fù)注入”系統(tǒng)會假性樂觀——協(xié)方差變小、誤差卻不減。這個坑我在調(diào)參時撞過一次因為在邊緣化之后沒有凍結(jié)線性化點程序的RMSE看似越來越小但實際軌跡和真值越拉越遠(yuǎn)。排查了半天最后發(fā)現(xiàn)是先驗項被重復(fù)線性化了幾次信息矩陣?yán)锏闹祽{空大了好幾倍。修復(fù)之后系統(tǒng)才恢復(fù)正常。所以你看邊緣化這一環(huán)節(jié)正確性比代碼性能更重要。5. 常見問題與排查記錄5.1 初始化總是失敗的常見原因如果你發(fā)現(xiàn)Vins-Fusion在第一次初始化時反復(fù)失敗先別懷疑調(diào)參多數(shù)是數(shù)據(jù)本身不夠格。我看過很多數(shù)據(jù)集特征是夠多但相機(jī)幾乎在原地轉(zhuǎn)圈純旋轉(zhuǎn)基礎(chǔ)上平移量很小這時候本質(zhì)矩陣模型退化。你可以打印出H和E模型各自的內(nèi)點數(shù)來確認(rèn)如果H模型內(nèi)點數(shù)遠(yuǎn)超E模型且場景看起來有大塊平面那基本就是低視差或旋轉(zhuǎn)退化問題。解決方法是等相機(jī)平移量積累起來再觸發(fā)初始化。你可以手動控制機(jī)器人在初始化階段做一個帶明顯平移的八字繞行而不是原地轉(zhuǎn)圈。另外初始化時不要用模糊幀運動模糊會讓光流跟蹤的像素位置整體偏移RANSAC再怎么聰明也救不回系統(tǒng)性偏差。有個更隱蔽的原因內(nèi)參沒對準(zhǔn)輸出圖像的尺寸。我遇到過一次圖像被resize到一半分辨率標(biāo)定內(nèi)參卻沒跟著縮放出新主點和焦距初始化階段特征點歸一化之后全部偏了結(jié)果H/E模型內(nèi)點數(shù)慘不忍睹。這個問題只靠肉眼看不出但我建議你在代碼里打印幾個點的歸一化坐標(biāo)跟真實物理方向?qū)σ粚δ芸焖俣ㄎ皇遣皇莾?nèi)參坐標(biāo)系搞錯了。5.2 3D點在兩次三角化后跳變同一個特征點前幾幀三角化出來的3D坐標(biāo)在一個位置滑窗滾過幾幀之后再做一次三角化3D點位置突然跳了幾米。這是非常典型的“三角化時視差角不夠”擲出來的病態(tài)解。我第一次遇到時以為是前端跟蹤崩了但其實前端很穩(wěn)問題就是當(dāng)幀的基線和深度比太低3D點深度方向上的不確定性高達(dá)數(shù)米重投影誤差卻很小看起來很正常。排查方法是把“該特征點所有觀測幀”標(biāo)出來逐幀打印出三角化時的視差角。視差角小于閾值時哪怕重投影誤差再小也不要更新這個3D點。另一個經(jīng)驗是使用多幀觀測做三角化比只用兩幀要穩(wěn)得多。多出來的幀可以讓最小二乘殘差在深度方向上形成更好的約束。如果你只有一個特征庫但不記得它被多少幀觀測過可以統(tǒng)計一下“每個點在滑窗內(nèi)的觀測總數(shù)”低于3幀的一律不用它做定位。5.3 先驗一致性破壞后的漂移特征邊緣化先驗出問題的時候系統(tǒng)的癥狀很有迷惑性姿態(tài)看起來很平滑但軌跡會隨時間緩慢往一個方向偏越拉越遠(yuǎn)。這種漂移和前端特征跟蹤錯誤的“突變性抖動”不一樣是慢性病。你檢查IMU看看沒異常檢查3D點重投影誤差也不大但全世界點云和軌跡就像被一股無形的力緩慢拖拽。大概率出在先驗項的線性化點被重復(fù)更新或者先驗項殘差的雅可比計算中使用的新位姿和生成先驗時不一致。我自己的排錯流程是把滑窗邊緣化前后的信息矩陣導(dǎo)出看對角元素是否異常增大如果增大超過一個數(shù)量級就基本坐實了重復(fù)注入。再檢查先驗殘差模塊中“固定線性化點”那個開關(guān)有沒有在代碼里丟失。這種問題在純視覺定位場景里更明顯因為IMU還能提供一部分參考多少能掩蓋先驗錯誤。一旦你的部署環(huán)境里IMU噪聲很大視覺邊緣化先驗的問題就會被放大你就更難懷疑到先驗頭上了。5.4 參數(shù)調(diào)優(yōu)的心得控制不住的掉幀和漂移往往不是某一個參數(shù)的問題而是參數(shù)之間不匹配。我的經(jīng)驗是按下圖這個順序來調(diào)不要跳著調(diào)先確認(rèn)滑窗大小1013不被拍腦袋改再確認(rèn)關(guān)鍵幀的最小視差閾值0.5度到1度只做微調(diào)接著看RANSAC閾值和最大迭代次數(shù)歸一化閾值0.0020.01最后才動特征點跟蹤窗口尺寸和金字塔層數(shù)。亂調(diào)參數(shù)的后果是你換了閾值A(chǔ)B的問題被暴露你換B又讓A的問題回來陷入死循環(huán)。另外一個心得是寫一個小的“診斷日志”功能每隔幾幀輸出滑窗內(nèi)平均重投影誤差、內(nèi)點數(shù)、最老幀到當(dāng)前幀的平移量。調(diào)試時開著它跑一小段就能快速判斷系統(tǒng)狀態(tài)。我每接手一個新場景數(shù)據(jù)第一件事就是開著診斷日志走一遍看看初始化和滑窗內(nèi)的殘差變化趨勢比盯著一堆軌跡圖發(fā)呆有效太多。滑窗SFM這部分做到位最大的感受就是后端的BA優(yōu)化變“聰明”了它不再會因為一次誤匹配而翻車也不會因為幾幀的低視差而產(chǎn)出飄移點。前期的特征跟蹤、位姿估計、三角化這些看似基礎(chǔ)的操作直接決定了全系統(tǒng)最終能跑多穩(wěn)這不是公式推導(dǎo)能帶給你的經(jīng)驗而是反復(fù)調(diào)數(shù)據(jù)積累出來的肌肉記憶。我個人到現(xiàn)在還保留著一個習(xí)慣每次改動三角化模塊的參數(shù)我都會用一組帶真值的仿真數(shù)據(jù)做回歸測試看看深度誤差隨視差角變化的曲線。這能最快暴露你把閾值調(diào)過頭了還是調(diào)不到位了。以后哪位朋友要是被困在“初始化失敗”“點云漂移”“軌跡慢性漂”這三個坑里希望這篇文章能幫你繞開大部分雷區(qū)。