鍵幀與 BA 前后端架構(gòu))
SLAM 算法相關(guān)基礎(chǔ)知識(shí)概念本筆記圍繞DROID-SLAM梳理 SLAM 的三個(gè)核心基礎(chǔ)概念光流Optical Flow、關(guān)鍵幀Keyframe、BABundle Adjustment與前后端架構(gòu)。三者構(gòu)成一條因果鏈光流DroidNet 預(yù)測(cè)的稠密像素對(duì)應(yīng) │ ├──→ 運(yùn)動(dòng)量夠大→ 選為「關(guān)鍵幀」MotionFilter 閾值過(guò)濾 │ └──→ 作為「觀測(cè) / 約束」喂給因子圖 → BA 優(yōu)化位姿 深度 ↑ 前端局部 BA實(shí)時(shí) 后端全局 BA 回環(huán)閱讀順序建議先讀 一、BA 與前后端 建立整體架構(gòu)觀再讀 二、關(guān)鍵幀 和 三、光流 理解 BA 的輸入從哪來(lái)。一、BA 與前后端這些都是 SLAM 領(lǐng)域的標(biāo)準(zhǔn)術(shù)語(yǔ)。下面分層次講清楚。BA 是什么 —— Bundle Adjustment光束法平差 / 集束優(yōu)化名字來(lái)源“Bundle”(光束)指相機(jī)發(fā)射出的光線;每條光線經(jīng)過(guò)一個(gè) 3D 點(diǎn),投影到圖像上的一個(gè)像素。Bundle Adjustment 的本質(zhì):調(diào)整光束——也就是同時(shí)調(diào)整相機(jī)位姿和 3D 點(diǎn)位置——使得3D 點(diǎn)重投影回圖像的位置與實(shí)際觀測(cè)到的像素位置盡可能吻合。數(shù)學(xué)表達(dá)假設(shè)有相機(jī)位姿T i T_iTi?、3D 點(diǎn)X j X_jXj?,它的真實(shí)觀測(cè)像素是u i j u_{ij}uij?。BA 要最小化重投影誤差:min ? T i , X j ∑ i , j ρ ( ∥ π ( T i , X j ) ? u i j ∥ 2 ) \min_{T_i, X_j} \sum_{i,j} \rho\Big( \big\| \, \pi(T_i, X_j) - u_{ij} \,\big\|^2 \Big)Ti?,Xj?min?i,j∑?ρ(?π(Ti?,Xj?)?uij??2)其中π ( T i , X j ) \pi(T_i, X_j)π(Ti?,Xj?)是把 3D 點(diǎn)X j X_jXj?用位姿T i T_iTi?投影到像素坐標(biāo)。通俗說(shuō):“調(diào)整相機(jī)和地圖,讓預(yù)測(cè)的像素位置和真實(shí)像素對(duì)齊。”為什么 SLAM 都要用 BA光靠幀間追蹤,誤差會(huì)不斷累積(走 100 步,每步差一點(diǎn),最后位置完全漂了)。BA 把很多幀、很多點(diǎn)放在一起聯(lián)合優(yōu)化,誤差被全局?jǐn)偙?這正是 SLAM 保持精度的關(guān)鍵。在 DROID-SLAM 里 BA 的特殊之處傳統(tǒng) BA 用的是手工特征點(diǎn)(SIFT/ORB)的匹配關(guān)系。DROID-SLAM 用DroidNet 神經(jīng)網(wǎng)絡(luò)直接預(yù)測(cè)兩幀之間的稠密光流(就是成對(duì)的像素對(duì)應(yīng)關(guān)系),然后用這些光流作為 BA 的觀測(cè),在因子圖(factor graph)上迭代求解。前端 / 后端 —— SLAM 的標(biāo)準(zhǔn)架構(gòu)劃分這是 SLAM 系統(tǒng)設(shè)計(jì)中的兩大模塊分工,幾乎所有現(xiàn)代 SLAM 都這么分。類(lèi)比:自動(dòng)駕駛汽車(chē)可以這樣形象地理解:前端 (Frontend)后端 (Backend)角色“駕駛員的眼睛”——快速感知當(dāng)下“導(dǎo)航員的大腦”——全局統(tǒng)籌修正任務(wù)實(shí)時(shí)、局部地處理每一幀離線、全局地優(yōu)化整個(gè)軌跡速度必須快(跟得上視頻幀率)可以慢(攢夠數(shù)據(jù)后批量做)范圍只看最近幾個(gè)關(guān)鍵幀看所有關(guān)鍵幀 回環(huán)前端 (DroidFrontend) 詳解“局部 BA”—— 在一個(gè)滑動(dòng)窗口(sliding window)里做。所有幀: K1 K2 K3 ... [K20 K21 K22 ... K45] ← 窗口(最近 25 個(gè)關(guān)鍵幀) ↑ 前端只優(yōu)化這一段窗口大小frontend_window25:只關(guān)心最近的 25 個(gè)關(guān)鍵幀。目的:追得上、追得穩(wěn)。視頻一直在流式輸入,前端要實(shí)時(shí)估計(jì)當(dāng)前位姿,不能等。每來(lái)一個(gè)新關(guān)鍵幀,窗口往前滑動(dòng),丟掉最老的,加進(jìn)新的,做一次局部 BA。后端 (DroidBackend) 詳解“全局 BA”—— 在所有關(guān)鍵幀構(gòu)成的大圖上做,并且重點(diǎn)處理回環(huán)(loop closure)。全部關(guān)鍵幀: K1 --- K2 --- ... --- K100 --- ... --- K1(回到起點(diǎn)回環(huán)!) ↑ ↑ └────── 發(fā)現(xiàn)回環(huán),全局 BA 把誤差攤平 ──┘backend_thresh22.0:當(dāng)一個(gè)新關(guān)鍵幀與歷史上某個(gè)老關(guān)鍵幀的相似度/距離低于閾值時(shí),認(rèn)為檢測(cè)到回環(huán)(“我又走回原來(lái)這個(gè)地方了”)。回環(huán)的作用極其重要:它給系統(tǒng)一個(gè)強(qiáng)約束——“第 1 幀和第 100 幀其實(shí)是同一個(gè)位置”,于是可以把中間累積的漂移誤差整體拉回來(lái)。后端不追求實(shí)時(shí),它周期性地跑,把前端積累的所有關(guān)鍵幀、所有約束(包括回環(huán))放在一張大因子圖上,做一次徹底的全局優(yōu)化。為什么要分前后端?關(guān)鍵在于實(shí)時(shí)性和精度的矛盾:全局 BA 精度高,但太慢,做不到每幀都跑 → 不能讓前端等它。局部 BA 速度快,但只顧眼前,時(shí)間長(zhǎng)了會(huì)漂移 → 需要后端兜底修正。所以分工是:前端保實(shí)時(shí)、后端保精度,兩者各司其職,數(shù)據(jù)通過(guò)DepthVideo(共享緩沖)傳遞。對(duì)應(yīng)到 DROID-SLAM 的代碼droid_slam/droid.pyclassDroid:def__init__(self):self.videoDepthVideo()# 共享數(shù)據(jù)池(圖像位姿深度置信度)self.filterMotionFilter(...)# 非關(guān)鍵幀的快速初始化(比前端還輕)self.frontendDroidFrontend(...)# 【前端】窗口25 的局部 BAself.backendDroidBackend(...)# 【后端】全局 BA 回環(huán)deftrack(self,tstamp,image,depth,intrinsics):# 每幀都調(diào)用:喂給 motion filter 觸發(fā)前端...defterminate(self):# 視頻結(jié)束:跑最后一次后端全局 BA,返回完整軌跡...調(diào)用流程:每一幀 → MotionFilter(快速估位姿) → 判定是否關(guān)鍵幀 ↓ 是關(guān)鍵幀 DroidFrontend(局部 BA,窗口內(nèi)迭代) ← 實(shí)時(shí)進(jìn)行 ↓ 攢夠一定數(shù)量 DroidBackend(全局 BA 回環(huán)檢測(cè)) ← 周期性進(jìn)行 ↓ 視頻結(jié)束 terminate() → 最終全局 BA → 輸出軌跡一張圖總結(jié)原始圖像流 │ ▼ ┌─────────────┐ 快速、實(shí)時(shí) │ MotionFilter │ ── 非關(guān)鍵幀的位姿粗估 └──────┬──────┘ │ 關(guān)鍵幀(運(yùn)動(dòng)閾值) ▼ ┌──────────────┐ BA Bundle Adjustment(光束法平差) │ 前端 Frontend│ ── 局部 BA:滑窗 25 幀,實(shí)時(shí)追得穩(wěn) │ (局部 BA) │ └──────┬──────┘ │ 周期性觸發(fā) ▼ ┌──────────────┐ loop closure(回環(huán)) │ 后端 Backend │ ── 全局 BA:全部關(guān)鍵幀 回環(huán),消除漂移 │ (全局 BA) │ └──────────────┘總結(jié):BA(Bundle Adjustment,光束法平差) 同時(shí)調(diào)整相機(jī)位姿和 3D 點(diǎn),讓重投影像素與真實(shí)觀測(cè)像素對(duì)齊的優(yōu)化過(guò)程,是 SLAM 消除誤差的核心手段。前端 跑在最近幾個(gè)關(guān)鍵幀上的局部、實(shí)時(shí)BA,保證系統(tǒng)跟得上視頻流。后端 跑在所有關(guān)鍵幀上的全局、周期性BA,重點(diǎn)處理回環(huán),消除長(zhǎng)時(shí)間累積的漂移。兩者通過(guò)共享的DepthVideo緩沖協(xié)作。二、關(guān)鍵幀關(guān)鍵幀 (Keyframe)是 SLAM 里最核心的概念之一,直接決定了系統(tǒng)的精度、速度和內(nèi)存占用。為什么需要關(guān)鍵幀 —— 核心動(dòng)機(jī)先理解一個(gè)矛盾:不是每一幀都值得做 BA。視頻流: F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 ... ↑ ↑ 如果每幀都做 BA: ? 太慢(算力爆炸) 如果只拿一幀做 BA: ? 太少(信息不夠) 需要挑有代表性的幀 → 關(guān)鍵幀 ? 平衡想象你在拍視頻繞一個(gè)物體一圈(360°):如果物體沒(méi)什么變化,連續(xù) 100 幀幾乎一模一樣→ 這些幀信息冗余,沒(méi)必要全留著做優(yōu)化。如果相機(jī)轉(zhuǎn)了很大角度,看到了新的視角→ 這一幀信息量大,值得標(biāo)記為關(guān)鍵幀,納入 BA 優(yōu)化。關(guān)鍵幀的本質(zhì)關(guān)鍵幀 信息量足夠大、值得花算力做 BA 優(yōu)化的代表幀非關(guān)鍵幀(普通的幀)只是用來(lái)跟蹤當(dāng)前位姿,不參與 BA,也不維護(hù)深度。什么算信息量足夠大 —— 兩種主流判據(jù)主流 SLAM 系統(tǒng)判斷關(guān)鍵幀的方法有兩種:方法 1:運(yùn)動(dòng)量判據(jù)Translation Rotation最經(jīng)典、最常用,ORB-SLAM、DROID-SLAM 都用類(lèi)似思路。核心思想:當(dāng)相機(jī)移動(dòng)/旋轉(zhuǎn)超過(guò)一定量,就選一個(gè)新關(guān)鍵幀。F1(關(guān)鍵幀) → F2 → F3 → F4 → F5 ↑ 相機(jī)移動(dòng)了 0.3 米 旋轉(zhuǎn)了 15° 超過(guò)閾值 → F5 成為新關(guān)鍵幀方法 2:視差/重疊率判據(jù)核心思想:當(dāng)當(dāng)前畫(huà)面與上一個(gè)關(guān)鍵幀的差異超過(guò)閾值時(shí),選新關(guān)鍵幀??梢杂脦追N方式衡量:平均光流大小:兩幀之間所有像素的平均位移視差 (parallax):同一個(gè) 3D 點(diǎn)在兩幀間的像素位移圖像相似度:SSIM、CNN 特征距離等DROID-SLAM 用的是一種綜合判據(jù)(下面詳述)。DROID-SLAM 中關(guān)鍵幀的具體判定DROID-SLAM 的判斷邏輯在motion_filter.py里,核心參數(shù)是filter_thresh(默認(rèn)1.0)。判定流程MotionFilter類(lèi)# 偽代碼,源自 droid_slam/motion_filter.pyclassMotionFilter:def__init__(self,net,video,thresh1.0,devicecuda):self.threshthresh# 閾值:1.0self.netnet# DroidNetself.videovideo# 共享的 DepthVideoself.count0# 當(dāng)前相對(duì)上一個(gè)關(guān)鍵幀累積了多少幀def__call__(self,tstamp,image,intrinsics):# 1. 第一幀永遠(yuǎn)是關(guān)鍵幀ifself.video.counter.value0:self.add_keyframe(tstamp,image,intrinsics)return# 2. 計(jì)算當(dāng)前幀與最近關(guān)鍵幀的距離# 用 DroidNet 預(yù)測(cè)當(dāng)前幀相對(duì)上一個(gè)關(guān)鍵幀的光流# 流的大小直接反映了兩幀之間的運(yùn)動(dòng)量withautocast(enabledTrue):mapself.net.predict_flow(...)# 網(wǎng)絡(luò)預(yù)測(cè)的光流# 3. 歐氏距離:衡量運(yùn)動(dòng)量dcompute_distance(map)# 計(jì)算綜合位移# 4. 閾值判斷ifdself.thresh:# 默認(rèn) 1.0# 運(yùn)動(dòng)量不足 → 當(dāng)作普通幀,只快速更新位姿self.video.tracked_poses[tstamp]estimated_poseelse:# 運(yùn)動(dòng)量足夠 → 選為新關(guān)鍵幀!self.add_keyframe(tstamp,image,image,intrinsics)DROID-SLAM 的綜合距離度量DROID-SLAM 用的是光流幅值 平移 旋轉(zhuǎn)的綜合度量。具體來(lái)說(shuō),compute_distance類(lèi)似:distance ||displacement_vector|| sqrt( (平移量)^2 (旋轉(zhuǎn)量)^2 (光流均值)^2 )更精確地說(shuō),DROID-SLAM 通過(guò)DroidNet預(yù)測(cè)兩幀之間的光流,然后計(jì)算光流的平均幅值,再結(jié)合平移、旋轉(zhuǎn)參數(shù)算出一個(gè)標(biāo)量d。參數(shù)含義參數(shù)默認(rèn)值作用filter_thresh1.0MotionFilter 選關(guān)鍵幀的閾值。值越小,關(guān)鍵幀越密(精度↑、速度↓);值越大,關(guān)鍵幀越稀疏keyframe_thresh4.0前端(滑窗 BA)插入關(guān)鍵幀的閾值(frontend_thresh16.0是前端成對(duì)因子添加閾值,不同含義,見(jiàn)下文)warmup8系統(tǒng)啟動(dòng)的前 N 幀強(qiáng)制全部作為關(guān)鍵幀,讓系統(tǒng)快速初始化各個(gè)閾值的作用層級(jí)這幾個(gè)參數(shù)很容易混淆,它們作用在不同的階段:所有幀 F1, F2, F3, ... │ ├─ warmup8:前 8 幀強(qiáng)制全選為關(guān)鍵幀(初始化) │ ▼ [MotionFilter] ─── filter_thresh1.0 ─── 判斷當(dāng)前幀 vs 最近關(guān)鍵幀 │ 運(yùn)動(dòng)量是否夠大? ├─ 不夠 → 非關(guān)鍵幀(只更新位姿,不參與 BA) │ └─ 夠 → 關(guān)鍵幀 │ ▼ [DroidFrontend] ── frontend_window25 ── 只在最近 25 個(gè)關(guān)鍵幀上做 BA frontend_thresh16.0 ── 前端在關(guān)鍵幀對(duì)之間添加因子(約束) 的距離閾值 │ ▼ [DroidBackend] ── backend_thresh22.0 ── 后端全局 BA 回環(huán)檢測(cè)關(guān)鍵幀與非關(guān)鍵幀的不同待遇這是理解為什么要分關(guān)鍵幀的關(guān)鍵:待遇完全不同。關(guān)鍵幀 (Keyframe)非關(guān)鍵幀存儲(chǔ)存入DepthVideo緩沖(保留圖像、位姿、深度、置信度)只臨時(shí)存位姿,用完丟棄深度維護(hù)并優(yōu)化稠密深度圖無(wú)獨(dú)立深度BA 優(yōu)化? 參與前端局部 BA 后端全局 BA? 不參與 BA因子圖作為圖節(jié)點(diǎn),被因子約束不進(jìn)圖用于回環(huán)? 可作為回環(huán)匹配候選? 不參與計(jì)算開(kāi)銷(xiāo)高(存圖像 維護(hù)深度 多次 BA)低(只做一次快速位姿估計(jì))待遇的差異本質(zhì)上就是算力分配策略:關(guān)鍵幀 高價(jià)值,值得投入;非關(guān)鍵幀 低價(jià)值,快速過(guò)一遍即可。參數(shù)調(diào)節(jié)的實(shí)際影響調(diào)節(jié)filter_thresh直接影響系統(tǒng)行為:filter_thresh 0.5 (小閾值 → 嚴(yán)格篩選 → 關(guān)鍵幀密集) ├── 優(yōu)點(diǎn):精度高(更多幀參與 BA,約束更充分) ├── 缺點(diǎn):速度慢、內(nèi)存占用大 └── 適用:高精度重建、慢速運(yùn)動(dòng)場(chǎng)景 filter_thresh 1.0 (默認(rèn),平衡) filter_thresh 2.0 (大閾值 → 寬松篩選 → 關(guān)鍵幀稀疏) ├── 優(yōu)點(diǎn):速度快、省內(nèi)存 ├── 缺點(diǎn):可能漏掉關(guān)鍵信息,精度下降 └── 適用:快速運(yùn)動(dòng)場(chǎng)景、實(shí)時(shí)性要求高數(shù)量對(duì)比直覺(jué)個(gè)人推斷?? 下面數(shù)量級(jí)是直覺(jué)估計(jì)個(gè)人推斷用于建立量感非實(shí)測(cè)實(shí)際取決于場(chǎng)景紋理、運(yùn)動(dòng)速度等。假設(shè) 300 幀視頻,相機(jī)勻速繞物體:filter_thresh1.0→ 可能選出 ~30-50 個(gè)關(guān)鍵幀(每 ~7-10 幀一個(gè))filter_thresh0.5→ 可能選出 ~80-120 個(gè)關(guān)鍵幀(每 ~3-4 幀一個(gè))filter_thresh2.0→ 可能選出 ~15-20 個(gè)關(guān)鍵幀而前端窗口固定 25 幀,所以關(guān)鍵幀越密,前端窗口覆蓋的實(shí)際時(shí)間范圍越短。小結(jié)概念定義關(guān)鍵幀信息量足夠大、值得花算力做 BA 優(yōu)化的代表幀。存入DepthVideo,參與 BA,維護(hù)深度。非關(guān)鍵幀信息量小(與上一個(gè)關(guān)鍵幀相比運(yùn)動(dòng)不足)。只快速估計(jì)位姿,不參與 BA。判定方法DROID-SLAM 用MotionFilter計(jì)算當(dāng)前幀與最近關(guān)鍵幀的光流綜合距離,與閾值filter_thresh1.0比較。核心公式距離 sqrt(平移2 旋轉(zhuǎn)2 光流均值2),超過(guò)1.0→ 關(guān)鍵幀三個(gè)閾值filter_thresh1.0(選關(guān)鍵幀) /frontend_window25(前端滑窗) /backend_thresh22.0(后端回環(huán))一句話:關(guān)鍵幀就是 SLAM 系統(tǒng)精挑細(xì)選出來(lái)、真正值得投入算力做優(yōu)化的幀。通過(guò)MotionFilter用光流運(yùn)動(dòng)量閾值過(guò)濾,實(shí)現(xiàn)精度 vs 速度的平衡。三、光流 (Optical Flow)好問(wèn)題。光流 (Optical Flow)是計(jì)算機(jī)視覺(jué)里最基礎(chǔ)、最重要的概念之一。在 DROID-SLAM 里,它是整個(gè)系統(tǒng)的眼睛——BA 優(yōu)化的所有約束本質(zhì)上都來(lái)自光流。光流的定義光流 連續(xù)兩幀圖像之間,每個(gè)像素的運(yùn)動(dòng)矢量(往哪個(gè)方向、移動(dòng)了多少)。簡(jiǎn)單說(shuō),光流描述的是:視頻里同一個(gè)物體上的同一個(gè)點(diǎn),在相鄰兩幀之間,從圖像的哪個(gè)位置移動(dòng)到了哪個(gè)位置。直觀例子想象你拍一段視頻,一個(gè)人從畫(huà)面左邊走到右邊:第 1 幀: 第 2 幀: ●←─(人) (人)→● 像素在 (100, 200) 像素移動(dòng)到了 (130, 200)這個(gè)人的鼻尖像素,在第 1 幀位于(100, 200),在第 2 幀跑到了(130, 200)。那么這個(gè)像素的光流就是:光流 (130-100, 200-200) (30, 0) └────────┬───────┘ 水平30,垂直0光流就是一個(gè)二維位移向量( d x , d y ) (dx, dy)(dx,dy),描述每個(gè)像素走了多遠(yuǎn)、往哪走。稠密光流 vs 稀疏光流這是理解 DROID-SLAM 的關(guān)鍵。稀疏光流傳統(tǒng) SLAM 用的只追蹤少數(shù)特征點(diǎn)(比如 1000 個(gè)角點(diǎn)):第 1 幀: 第 2 幀: ┌────────────┐ ┌────────────┐ │ ? ? │ │ ? ? │ ← 只追蹤這些點(diǎn) │ ? ? │ │ ? ? │ │ ? ? │ │ ? ? │ └────────────┘ └────────────┘ (如 ORB-SLAM、LK 光流)優(yōu)點(diǎn):快缺點(diǎn):信息少,只能用特征明顯的點(diǎn),紋理弱的區(qū)域(白墻、地板)就抓瞎稠密光流DROID-SLAM 用的追蹤每一個(gè)像素:第 1 幀: 第 2 幀: ┌────────────┐ ┌────────────┐ │ ?????????? │ │ ?????????? │ ← 每個(gè)像素都有 │ ?????????? │ │ ?????????? │ 一個(gè)運(yùn)動(dòng)向量 │ ?????????? │ │ ?????????? │ └────────────┘ └────────────┘每個(gè)像素都有一個(gè)( d x , d y ) (dx, dy)(dx,dy)向量整張圖的光流,大小是H × W × 2(寬×高×兩個(gè)分量)DROID-SLAM 用DroidNet神經(jīng)網(wǎng)絡(luò)直接預(yù)測(cè)這個(gè)稠密光流這正是 DROID-SLAM 比傳統(tǒng) SLAM 強(qiáng)的地方:它不挑特征點(diǎn),全圖所有像素都參與,所以即使是無(wú)紋理區(qū)域也能建立約束。光流的數(shù)學(xué)形式對(duì)圖像I 1 I_1I1?中的每個(gè)像素( x , y ) (x, y)(x,y),光流給出它在I 2 I_2I2?中的對(duì)應(yīng)位置:I 1 ( x , y ) ≈ I 2 ( x d x , y d y ) I_1(x, y) \approx I_2(x dx, y dy)I1?(x,y)≈I2?(xdx,ydy)這就是亮度恒定假設(shè):同一個(gè)點(diǎn)在兩幀里亮度/顏色不變,只是位置變了。整個(gè)光流場(chǎng)可以可視化為一張圖——每個(gè)箭頭表示一個(gè)像素的運(yùn)動(dòng)方向和大小:光流可視化(箭頭表示運(yùn)動(dòng)方向): ────→──── ────→──── 相機(jī)向左平移 → 所有像素向右流動(dòng) ────→────為什么光流對(duì) SLAM / BA 這么重要這是理解前面所有討論的關(guān)鍵?;仡櫼幌虑懊娴膯?wèn)題鏈:Q: BA 是什么? A: 調(diào)整相機(jī)位姿和 3D 點(diǎn),讓重投影誤差最小。 Q: 重投影誤差怎么算?需要知道哪個(gè)像素對(duì)應(yīng)哪個(gè) 3D 點(diǎn)。 A: ← 這正是光流提供的!光流 BA 的觀測(cè)數(shù)據(jù)傳統(tǒng) BA 的流程:手工特征點(diǎn) → 特征匹配 → 匹配關(guān)系喂給 BA → 優(yōu)化DROID-SLAM 的流程:兩幀圖像 → DroidNet 預(yù)測(cè)稠密光流 → 光流作為匹配關(guān)系喂給因子圖 → BA 優(yōu)化光流告訴系統(tǒng):“第 1 幀的像素( x 1 , y 1 ) (x_1, y_1)(x1?,y1?)和第 2 幀的像素( x 2 , y 2 ) (x_2, y_2)(x2?,y2?)是同一個(gè) 3D 點(diǎn)的投影。”有了這個(gè)對(duì)應(yīng)關(guān)系,BA 才能建立約束方程去優(yōu)化位姿和深度。具體到 DROID-SLAM 的因子圖關(guān)鍵幀 K1 的像素 (x1,y1) ──光流──→ 關(guān)鍵幀 K2 的像素 (x2,y2) │ │ │ 光流說(shuō):這倆是同一個(gè) 3D 點(diǎn) X │ │ │ ▼ ▼ X 投影到 K1 (x1,y1) X 投影到 K2 (x2,y2) │ │ └────── 這構(gòu)成一個(gè)因子(約束) ──┘ │ ▼ BA 優(yōu)化:調(diào)整 K1位姿、K2位姿、X 的位置 使兩邊的投影都對(duì)齊光流在本筆記三個(gè)概念中的三個(gè)關(guān)鍵角色現(xiàn)在你可以把前面的知識(shí)點(diǎn)串起來(lái)了:角色 1:判斷關(guān)鍵幀MotionFilter當(dāng)前幀 vs 最近關(guān)鍵幀 → DroidNet 預(yù)測(cè)光流 → 算光流平均幅值 │ 幅值 filter_thresh(1.0)? → 選為關(guān)鍵幀光流大 相機(jī)動(dòng)了 該選關(guān)鍵幀了。如果光流很小(兩幀幾乎一樣),說(shuō)明沒(méi)動(dòng),不選。角色 2:前端 BA 的約束來(lái)源DroidFrontend滑窗內(nèi)每對(duì)關(guān)鍵幀之間 → DroidNet 預(yù)測(cè)光流 → 作為因子加入因子圖 → 局部 BA角色 3:后端回環(huán)檢測(cè)后的約束發(fā)現(xiàn)回環(huán)(當(dāng)前幀 vs 老關(guān)鍵幀)→ 預(yù)測(cè)光流 → 加入因子圖 → 全局 BA 拉回漂移DROID-SLAM 用神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)光流的革命性傳統(tǒng)方法(Lucas-Kanade、Farneb?ck)算光流:基于亮度恒定 局部梯度大位移、遮擋、弱紋理時(shí)會(huì)失敗只能處理小運(yùn)動(dòng)DROID-SLAM 用DroidNet預(yù)測(cè)光流:端到端學(xué)習(xí),直接從兩幀圖像輸出稠密光流能處理大運(yùn)動(dòng)、遮擋、弱紋理還附帶輸出置信度(哪些像素的光流可信,哪些不可信——比如動(dòng)態(tài)物體、遮擋區(qū)域可信度低)關(guān)于動(dòng)態(tài)物體光流的待核實(shí)點(diǎn)本筆記原文提到VIPE 要加 Segment-and-Track-Anything 的原因動(dòng)態(tài)物體上的光流是錯(cuò)誤的需要用分割掩碼屏蔽掉這些區(qū)域的稠密對(duì)應(yīng)否則會(huì)污染 BA。這部分來(lái)源未確認(rèn)VIPE 具體指哪個(gè)系統(tǒng)、其與 Segment-and-Track-Anything 的集成細(xì)節(jié)標(biāo)注為待核實(shí)建議后續(xù)補(bǔ)充原始鏈接/論文。一張圖總結(jié)光流在 DROID-SLAM 中的地位關(guān)鍵幀 K1 關(guān)鍵幀 K2 (圖像) (圖像) │ │ └──────┬─────────────────┘ ▼ ┌────────────┐ │ DroidNet │ ← 神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)兩幀之間的稠密光流 └──────┬─────┘ │ ▼ 稠密光流 (H×W×2) ← 每個(gè)像素的運(yùn)動(dòng)向量 (dx, dy) 置信度圖 ← 哪些像素可信 │ ▼ ┌─────────────────┐ │ 因子圖 BA 優(yōu)化 │ ← 光流 像素對(duì)應(yīng)關(guān)系 BA 的約束 │ 位姿 深度 │ └─────────────────┘ │ ▼ 優(yōu)化后的相機(jī)軌跡 稠密深度一句話總結(jié):光流 (Optical Flow)就是連續(xù)兩幀圖像之間,每個(gè)像素的運(yùn)動(dòng)向量( d x , d y ) (dx, dy)(dx,dy)。它回答的是這個(gè)像素在下一幀跑到哪兒去了。在 DROID-SLAM 里,神經(jīng)網(wǎng)絡(luò)DroidNet直接預(yù)測(cè)稠密光流(全圖所有像素),這些光流提供了哪個(gè)像素對(duì)應(yīng)哪個(gè)像素的匹配關(guān)系——而這正是 BA 建立約束、優(yōu)化位姿和深度的核心輸入數(shù)據(jù)。光流大說(shuō)明相機(jī)動(dòng)了(選關(guān)鍵幀),光流還直接喂給因子圖做 BA(優(yōu)化位姿)。四、三者的串聯(lián)總覽把三個(gè)概念按數(shù)據(jù)如何流動(dòng)串起來(lái),就是 DROID-SLAM 的完整工作鏈路:┌─────────────┐ │ 視頻幀流 │ └──────┬──────┘ ▼ ┌───────────────────────────┐ │ ① DroidNet 預(yù)測(cè)稠密光流 │ ← 概念三:光流 │ (當(dāng)前幀 vs 最近關(guān)鍵幀) │ └──────┬────────────────────┘ │ 光流幅值 運(yùn)動(dòng)量 ▼ ┌───────────────────────────┐ │ ② MotionFilter 判定關(guān)鍵幀 │ ← 概念二:關(guān)鍵幀 │ 光流綜合距離 1.0 ? │ │ ├ 否 → 非關(guān)鍵幀(只更新位姿)│ │ └ 是 → 關(guān)鍵幀,入 DepthVideo│ └──────┬────────────────────┘ │ 關(guān)鍵幀對(duì)之間的稠密光流 觀測(cè) ▼ ┌───────────────────────────┐ │ ③ 因子圖 BA │ ← 概念一:BA 前后端 │ 前端:滑窗 25 幀局部 BA(實(shí)時(shí))│ │ 后端:全部關(guān)鍵幀全局 BA 回環(huán)│ └──────┬────────────────────┘ ▼ 優(yōu)化后的相機(jī)軌跡 稠密深度一句話:光流提供像素對(duì)應(yīng) → 運(yùn)動(dòng)量決定哪些幀值得優(yōu)化(關(guān)鍵幀) → BA 用這些對(duì)應(yīng)關(guān)系在因子圖上聯(lián)合優(yōu)化位姿與深度。