Reena Tsai
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # FAST-LIO2: Fast Direct LiDAR-inertial Odometry - 出版年份:2021 - 作者:Wei Xu, Yixi Cai, Dongjiao He, Jiarong Lin, Fu Zhang - 發表期刊:IEEE - 是否提供源代碼:是 ## Abstracts 現今研究技術中,在 unknown environment 中建立 3D map 的議題越來越普遍,而在當前的方法中, Vision-based SLAM 雖然在定位功能上具有極高準確率,但卻對影像移動所產生的 illumination variation / motion blur 干擾極大,導致它只能得到一個較為稀疏的 feature map;而另外一種主流技術,real-time dense mapping,其準確性主要是建立在一些視覺性的感測器的測量結果,則會受到影像畫質高低與硬體運算能力的限制。 文中介紹了 FAST-LIO2:一種快速、穩健且通用的 LiDAR-IMU 框架,它具有以下兩個關鍵的特性: - *ikd-Tree data structure* 設計一種建立於 incremental k-d tree 資料結構,稱為 *ikd-Tree*,用這種結構來進行 point cloud map 的建立。 **(使用 ikd-Tree 的好處與功能:相鄰點的搜尋 + 點雲的更新 + 動態重新平衡)** - *direct method* 直接將每個點雲加入 global map 中,而不提取相關 feature,並在隨後進行 global map updating 的修正,也就是 mapping 的動作。 由於 ikd-Tree 的計算效率極高,作者利用這種結構直接將原始點加入 global map,使系統在一些劇烈運動或是非常混段的環境中,也具有相當高的定位準確性,**這個特性改善了傳統 Visual-SLAM 缺點**。 結合上述 ikd-Tree 的結構與 direct method 的方法到一個具有緊密耦合特性的 LiDAR-IMU 系統中 (也就是 FAST-LIO),為了加快該系統的計算速度,另外設計一個新的數學等效公式,用以重新計算 Kalman gain,這個公式的應用可成功降低計算複雜度,這樣的新系統稱作 FAST-LIO2。 *FAST-LIO 系統的主要運作模式:* *The system uses an IMU to compensate each point's motion via a rigorous back-propagation step and estimates the system's full state via an on-manifold iterated Kalman filter.* ## Method 為了執行狀態估計,<font color = "red">FAST-LIO2 繼承 FAST-LIO 的 tightly-coupled iterated Kalman Filter,即 LiDAR 新掃描到的點雲會通過 **tightly-coupled iterated Kalman Filter** 框架 (紅色虛線區)</font>,加入到 global map 中,而 <font color = "blue">global map 中的地圖點則由以 incremental k-d Tree 為基礎建立而成的 **ikd-Tree** 來進行 (藍色虛線區)</font>,若當前 LiDAR 的 FoV 範圍超過了 map 邊界,系統會將該點從 ikd-Tree 中刪除。 整個系統主要流程如下圖所示: ![011](https://hackmd.io/_uploads/Bk5LaJa3T.png) 在點雲加入 global map 的過程中, ikd-Tree 會追蹤一個大範圍立方體區域 (map size) 中的所有 map point,並用以計算狀態估計模組中的殘差,而優化後的位姿最後會將 LiDAR 新掃描到的下一組點雲,根據 odometry 的頻率來插入 ikd-Tree 中,以這樣的方式再繼續將這組點雲合併到 global map 中。 ## Architecture ### State Estimation FAST-LIO2 的 state estimation module 是從 FAST-LIO 中繼承的,並進一步融合 online calibration of LiDAR-IMU extrinsic parameters,具體步驟說明如下。 #### I. Propagation (Forward Propagation) 已知上一個 LiDAR scan 中得到的最優狀態估計為 $\bar {\rm x}_{k-1}$,且其共變異數矩陣為 $\bar{\rm P}_{k-1}$,則系統的最開始會進行 forward propagation (前向傳播),利用 **state transition model** 中計算出的狀態轉移公式 ${\rm x}_{i + 1} = {\rm x}_i \boxplus (\Delta t {\rm f}({\rm x}_i, {\rm u}_i, {\rm w}_i))$,可以得到當前第 $k$ 個 scan 中的 state vector 與其共變異數矩陣 (設定 ${\rm w}_i = 0$): $$ \begin{aligned} & \hat{\rm x}_{i + 1} = \hat{\rm x}_i \boxplus (\Delta t {\rm f}(\hat{\rm x}_i, {\rm u}_i, {\rm w}_i)) \\ & \hat{\rm P}_{i + 1} = {\rm F}_{\tilde {\rm x}_i} \hat{\rm P}_i {\rm F}^T_{\tilde{\rm x}_i} + {\rm F}_{{\rm w}_i} {\rm Q}_i {\rm F}_{{\rm w}_i}^T \end{aligned} $$ 其中相關參數意義如下: - ${\rm Q}_i$;表示噪聲 ${\rm w}_i$ 的變異數 - $\hat{\rm x}_0 = \bar{\rm x}_{k-1}$ 且 $\hat{\rm P}_0 = \bar{\rm P}_{k-1}$ Forward Propagation 的動作會不斷進行,直到抵達當前 ($k$-th) scan 掃描結束的瞬間為止,此時的 state vector 為 $\hat{\rm x}_k$,其共變異數矩陣為 $\hat{\rm P}_k$。 #### II. Backward Propagation and Motion Compensation 為了解決 LiDAR 在劇烈運動下產生的 motion distortion problem,作者提出 backward propagation 來解決這個問題,以下將解釋 motion distortion problem 與 backward propagation method。 - motion distortion problem (in-scan motion problem) 這個問題形成主要原因有兩個: * LiDAR 本身產生運動 * 點雲為 LiDAR 在掃描過程中蒐集到的數據點集合 如下圖所示,假設原本實際情況 $p1$、$p2$、$p3$ 三個點是在同一直線上,LiDAR 掃描下來會因為它自身在每個時間點的姿態不同,在不同姿態底下得到的數據點就會是在不同座標系上,最後掃描結束後,蒐集到的數據點就會和真實情況有偏差(會得到"這三個點座標處於不同座標系"的資訊)。 ![image](https://hackmd.io/_uploads/BkN4cN6bR.png) 當這些數據點形成點雲後,最後要投影到 2D 平面上,這樣就會導致和真實情況有偏差;而從直觀上來看,蒐集到的點雲數據會產生一定的變形,這就是 backward propagation method 希望能解的 in-scan motion problem (又稱作 motion distortion problem)。 *實際測試* | With Backward Propagation | Without Backward Propagation | | -------- | -------- | | ![image](https://hackmd.io/_uploads/SJA0c46bR.png)| ![image](https://hackmd.io/_uploads/S1j1oVaZC.png)| 由上方可以看出,這個 motion distortion problem 主要影響的部分除了路徑閉合問題外,路徑計算的過程中也會受到數據變形的影響。 - backward propagation method 由於IMU input 會隨著時間產生漂移誤差,所以必須對應同一時間點 LiDAR 產生的點雲,用已校正 IMU input 的漂移誤差。 但在 LiDAR 蒐集到的 feature point 上面會產生剛剛提到 motion distortion 的問題,所以必須先計算出正確的 LiDAR point,而在計算出正確的 IMU 資訊(在 state vector 裡面)後,才可以結合 LiDAR 的 feature point 進行投影,形成點雲。 ![image](https://hackmd.io/_uploads/S1JN3NpWA.png) 在這個方法中,作者利用後向傳播的方式,以 $t_k$ 時刻的位姿,推算出 $\rho_j$ 時刻的位姿,並且把 $\rho_j$ 時刻的特徵點轉換到當前 $t_k$ 時刻。 透過上述計算,得到 $\rho_j$ 時刻到 $t_k$ 時刻的相對位姿為 $^{I_k}{\check T_{I_j}} = (^{I_k}{\check R_{I_j}}, ^{I_k}{\check p_{I_j}})$ 根據上述結果,我們可以通過下方公式,將 local 座標系的點測量值 $^{L_j}p_{f_j}$,轉換為掃描終點時刻的 $^{L_k}p_{f_j}$。 $$ ^{L_k}p_{f_j} = ^I{\rm T}^{-1}_L {^{I_k}\check{{\rm T}}_{I_j}} {^I{{\rm T}_L}} {^{L_j}{\rm p}_{f_j}} $$ 其中,$^IT_L$ 為 LiDAR 與 IMU 之間的外參,$^{L_k}p_{f_k}$ 則表示投影到掃描終點時刻 $t_k$ 的座標,主要用於接下來的 residual computation。 #### III. Residual Computation 根據剛剛 motion compensation 內,backward propagation 的公式 $^{L_k}{\rm p}_{f_j} = ^I{\rm T}^{-1}_L {^{I_k}\hat{\rm T}_{I_j}} {^I{\rm T}_L} {^{L_j}{\rm p}_{f_j}}$,我們可以將一個 scan 中的 feature points $\lbrace ^{L_k}{\rm p}_{f_j} \rbrace$ 視為在同一個時間點 $t_k$ 得到的點,以此來重建地圖並計算殘差。 假設當前由 forward propagation 公式 $\hat{\rm x}_{i + 1} = \hat{\rm x}_i ⊞ (\Delta t {\rm f}(\hat{\rm x}_i, {\rm u}_i, {\rm w}_i))$ 預測出的 state vector 為 $\hat{\rm x}^{\cal K}_k$,則當 $\cal K = 0$ 時,代表著第一次 iteration 前,此時 $\hat{\rm x}^{\cal K}_k=\hat{\rm x}_k$。 利用 backward propagation 計算出當前的 state vector 後,我們將每個測量到的 LiDAR point $^L{\rm p}_j$ 投影到 global frame 中,得到以下結果: $$ ^G\hat{\rm p}_j^{\cal K} = {^G\hat{\rm T}^{\cal K}_{I_k}} {^I\hat{\rm T}^{\cal K}_{L_k}} {^L{\rm p}_j} $$ 其中,$^G\hat{\rm p}_j^{\cal K}$ 為我們想要求的 $t_k$ 時間點到 global 座標系下的位姿變換。 在這個位姿變換後的特徵點應該要落在它對應的特徵線/面上,但由於存在 LiDAR 的測量誤差或是 forward propagation 的推算誤差,這會導致系統推算後的特徵點無法完全落在它對應的特徵線/面上。 $$ {\rm z}^{\cal K}_j = {\rm G}_j (^G{\hat{\rm p}^{\cal K}_{f_j}} - {^G{\rm q}_j}) $$ 在這個式子中,$G_j$ 為法向量 ${\rm u}_j^T$ (平面特徵) 或是反對稱矩陣 $\lfloor u_j \rfloor$ (邊緣特徵),也就是用來計算點到面或點到線之間的距離。而在這之中,作者只考慮在 idk-Tree 所表示的地圖上搜索距離 $^L{\rm p}_j$ 最近的 5 個點,其餘點會被視為 noise 或新的觀測點。 而若是我們將 LiDAR noise 去除,假設測量出來的點都是真實的座標: $$ ^{L_j}{\rm p}^{gt}_{f_j} = ^{L_j}{\rm p}_{f_j} - ^{L_j}{\rm n}_{f_j} $$ 則當我們將這個 ground truth 代入上述座標轉換公式中轉換出的 global 座標系誤差公式,可以得到以下結果,其中殘差值 ${\rm z}^{\cal K}_j = 0$: $$ \begin{aligned} {\rm z}_j &= {\rm G}_j (^G{\hat{\rm p}_{f_j}} - {^G{\rm q}_j}) \\ &= {\rm G}_j ({^G\hat{\rm T}_{I_k}} {^I\hat{\rm T}_{L_k}} \color{red}{^{L_k}{\rm p}_{f_j}} - {^G{\rm q}_j}) \\ &= {\rm G}_j ({^G\hat{\rm T}_{I_k}} {^I\hat{\rm T}_{L_k}} \color{red}{(^I{\rm T}^{-1}_{L_k} {^{I_k}\check{{\rm T}}_{I_j}} {^I{{\rm T}_{L_k}}} {^{L_j}{\rm p}_{f_j}})} - {^G{\rm q}_j}) \\ &= {\rm G}_j ({^G\hat{\rm T}_{I_k}} {^{I_k}\check{{\rm T}}_{I_j}} {^I{{\rm T}_{L_k}}} \color{red}{^{L_j}{\rm p}_{f_j}} - {^G{\rm q}_j}) \\ &= {\rm G}_j ({^G\hat{\rm T}_{I_k}} {^{I_k}{\check {\rm T}}_{I_j}} {^I\hat{\rm T}_{L_k}} \color{red}{({^{L_j}{\rm p}_{f_j}} - ^{L_j}{\rm n}_{f_j})} - {^G{\rm q}_j}) \end{aligned} $$ > **觀測模型的建立** > > 根據上方的計算結果,我們同時比較在狀態向量 $x$ 的內容 ${\rm x} = [\begin{matrix}{^G{\rm R}^T_I} & {^G{\rm p}^T_I} & {^G{\rm v}^T_I} & {{\rm b}^T_\omega} & {{\rm b}^T_a} & {^G{\rm g}^T} & {^I{\rm R}^T_L} & {^I{\rm p}^T_L}\end{matrix}]^T$ 中,發現 $^GT_{I_k} = (^G{\rm R}_{I_k}, ^G{\rm p}_{I_k})$ [IMU 座標系到 globale 座標系的轉換矩陣] 以及 $^I{\rm T}_L = (^I{\rm R}_L, ^I{\rm p}_L)$ [LiDAR 坐標系到 IMU 座標系] 兩個矩陣都在狀態向量 $x$ 中,因此我們可以直把上方的計算結果簡寫為 ${\rm h}_j = ({\rm x}_k, ^L{\rm p}_j)$ 的一個模型,稱作觀測模型 measurement model。 理想情況下(沒有噪聲與干擾),${\rm h}_j ({\rm x}_k, ^L{\rm p}_j) = 0$,此時殘差為 0,模型預測(觀測)的位置和實際的位置應該是完全一樣的。 然而,實際情況下,LiDAR 會受各種情況影響而導致測量值中有噪聲的存在,因此重新加入測量噪聲,計算 $^L{\rm p}^{gt}_j = ^L{\rm p}_j - ^L{\rm p}_j$,推導出 $^L{\rm p} = ^L{\rm p}^{gt}_j + ^L{\rm p}_j$,並重新針對 ${\rm x}^{\cal K}_k = 0$ 處進行一階泰勒展開 (first order approximation)。 > **一階泰勒展開 (First Order Approximation)** > > 一階泰勒展開的目的主要在於要計算一個非線性函數 $h(x)$ 的近似方程,基本上是在計算該函數於展開點 $\hat{x}$ 處的值,加上從這點到 $x$ 之間的一階導數乘以 $x$ 與 $\hat{x}$ 之間的差異,具體作法如下: > > 1. 選擇展開點 > 我們會選擇 $\hat{x} 作為展開點,代表我們想要進行近似的基點 > > 2. 計算函數 $h(\hat{x})$:該函數在基點的值 > 表示當系統狀態為 $\hat{x}$ 時的 measurement model 輸出 > > 3. 計算 Jacobian Matrix > 計算 $h(x)$ 對 $x = \hat{x}$ 的 Jacobian Matrix $H$,其中 Jacobian Matrix 中包含函數 $h$ 對每個狀態變量的偏導數,是函數於 $\hat{x}$ 點中沿各個方向的斜率,數學上以下列公式表示 > $H = \frac{\partial h}{\partial x}|_{x = \hat{x}}$ > > 4. 計算狀態差異 > 計算狀態差異 $\tilde{x} = x - \hat{x}$,表示實際狀態與估計狀態之間的差異 > > 5. 進行一階泰勒展開 > 將函數 $h(x)$ 在 $\hat{x}$ 附近使用一階泰勒展開來近似,可以寫為 > $$ > h(x) \approx h(\hat{x}) + H \tilde{x} > $$ > 其中,$h(\hat{x})$ 表示在 $\hat{x}$ 的測量值,而 $H\tilde{x}$ 表示 $\hat{x}$ 到 $x$ 的線性近似誤差。 > > > 總結來說,以一階泰勒展開來進行近似方程計算的方式可以**將非線性模型線性化**,使我們可以用線性的 Kalman Filter 來處理。 為了使我們在更新狀態向量可以透過縣性工具去解,我們針對 $x^{\cal K}_k = 0$ 處,使用一階泰勒展開將原先的非線性觀測模型線性化,最終計算觀測模型如下 $$ \begin{aligned} 0 &= {\rm h}_j ({\rm x}_k, ^L{\rm p}_j + ^L{\rm n}_j) \simeq {\rm h}_j (\hat{\rm x}^{\cal K}_k, 0) + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k + {\rm v}_j \\ &= {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k + {\rm v}_j \end{aligned} $$ 其中相關參數意義如下: - ${\rm x}_k = \hat{\rm x}^{\cal K}_k ⊞ \tilde{\rm x}^{\cal K}_k$:代表 state vector - ${\rm H}^{\cal K}_j$:代表 ${\rm h}_j ({\rm x}_k, ^{L_j}{\rm n}_{f_j})$ 相對於 $\tilde{\rm x}^{\cal K}_k$ 在 0 處取值的 Jacobian Matrix - ${\rm v}_j$:該數值是由原始的測量噪聲 $^L{\rm n}_j$ 產生而成 由上方公式,最終可推得殘差值計算如下: $$ {\rm z}^{\cal K}_j = {\rm h}_j (\hat{\rm x}^{\cal K}_k, 0) = {\rm u}^T_j ({^G\hat{\rm T}^{\cal K}_{I_k}} {^I\hat{\rm T}^{\cal K}_{L_k}} {^L{\rm p}_j} - ^G{\rm q}_j) $$ 其中 $u^T_j$ 等同 ${\rm G}_j$,表示最近平面的單位法向量(用來確定最近平面的方向)。 (此時我們可以稱 $z_j^{\cal K}$ 為觀測值) ### IV. Iterated Update (state update) 根據 forward propagation 計算出的狀態轉換方式 ${\rm x}_{i + 1} = {\rm x}_i ⊞ (\Delta t {\rm f}({\rm x}_i, {\rm u}_i, {\rm w}_i))$ 與 透過 backward propagation 產生的 Residual Computation 計算結果 $0 = {\rm h}_j ({\rm x}_k, ^L{\rm p}_j + ^L{\rm n}_j)$,作者使用 iterated Kalman Filter,直接在 manifold 上面進行狀態 $\rm x$ 的更新,這種做法可以避免任何 re-normalization 技術的需求。 因此,接下來要進行的是利用 Kalman Filter 來做狀態估計,所謂的狀態估計問題是指基於初始狀態訊息、一系列的觀測量、一系列的輸入量、以及系統的運動模型與觀測模型,可以計算系統在某個時刻的真實狀態估計值。 1. 利用 IMU 與 LiDAR 的輸入,首先推導出系統模型,該模型由狀態轉移模型 (State Transition Model) 與測量模型 (Measurement Model) 組成。 * State Transition Model (狀態轉移模型) 在 IMU 的 input frame 中,取第一個 IMU frame (記為 $I$) 為 global frame (記為 $G$),並定義 $^I{\rm T}_L = (^I{\rm R}_L, ^I{\rm p}_L)$ 為 LiDAR 和 IMU 之間的外參,則狀態轉移模型建立如下: $$ \begin{aligned} & ^G{\rm R}_I = ^G{\rm R}_I \lfloor \omega_m-{\rm b}_\omega-{\rm n}_\omega \rfloor_\wedge \\ & ^G{\rm p}_I = ^G{\rm v}_I \\ & ^G{\rm v}_I = ^G{\rm R}_I({\rm a}_m-{\rm b_a} - {\rm n_a})+^G{\rm g} \end{aligned} $$ 其中參數意義如下: - $^G{\rm R}_I$、$^G{\rm p}_I$ :表示 IMU 在 global frame 中的姿態與位置,這兩個數值為連續性狀態轉移模型的主體 - $^G{\rm g}$:代表 global frame 中的重力向量 - ${\rm a}_m$、$\omega_m$:代表 IMU 測量值 - ${\rm n_a}$、${\rm n}_\omega$:代表 ${\rm a}_m$ 與 $\omega_m$ 的測量噪聲 - ${\rm b_a}$、${\rm b}_\omega$:代表行走其間噪聲 ${\rm n_a}$、${\rm n}_\omega$ 所造成的 IMU 偏移量 - $\lfloor \omega_m-{\rm b}_\omega-{\rm n}_\omega \rfloor_\wedge$:表示針對三維向量 $\omega_m-{\rm b}_\omega-{\rm n}_\omega$ 所構成的反對稱矩陣 根據 $\boxplus$ 的定義,連續時間下的動態模型可以依據 IMU 的採樣週期 $\Delta t$ 進行離散化,最終得到以下狀態向量: $$ {\rm x}_{i + 1} = {\rm x}_i \boxplus (\Delta t {\rm f}({\rm x}_i, {\rm u}_i, {\rm w}_i)) $$ 其中參數意義如下: - ${\rm x} \triangleq \begin{bmatrix} {^G{\rm R}^T_I} & {^G{\rm p}^T_I} & {^G{\rm v}^T_I} & {{\rm b}^T_\omega} & {\rm b}^T_{\rm a} & {^G{\rm g}^T} & {^I{\rm p}^T_L} \end{bmatrix}^T$:表示 state vector,其中 $i$ 表示 IMU 測量的索引。 - ${\rm u} \triangleq \begin{bmatrix} {\omega^T_m} & {{\rm a}^T_m} \end{bmatrix}^T$:表示輸入向量 (IMU 測量值) - ${\rm w} \triangleq \begin{bmatrix} {{\rm n}^T_\omega} & {{\rm n}^T_{\rm a}} & {{\rm n}^T_{\rm b \omega}} & {{\rm n}^T_{\rm ba}} \end{bmatrix}^T$:表示噪音 - ${\rm f(x, u, w)} = \begin{bmatrix} {\omega_m-{\rm b}_\omega-{\rm n}_\omega} \\ {^G{\rm v}_1 + \frac{1}{2}(^G{\rm R}_I({\rm a}_m - {\rm b_a} - {\rm n_a} ) + ^G{\rm g}) \Delta t} \\ ^G{\rm R}_I ({\rm a}_m - {\rm b_a} - {\rm n_a}) + ^G{\rm g} \\ {\rm n_b \omega} \\ {\rm n_ba} \\ {0_{3 \times 1}} \\ {0_{3 \times 1}} \\ {0_{3 \times 1}} \end{bmatrix} \in \mathbb{R}^24$ * Measurement Model (觀測模型) 由於 LiDAR 在經歷連續運動時,所得到的點通常是在不同姿勢下進行採集,也代表著不同的姿式。為了修正這種 in-scan motion 的問題,作者提出 back propagation 技術,用以**估計在當下 scan 的開始瞬間每個 point 的位姿,相對於該 scan 的結束瞬間這些 point 的位姿**,透過 IMU 測量而得的數據進行建模,最後估計相對姿勢。這個做法讓我們能夠基於 LiDAR 掃描過程中的每個單獨點精確採樣時間來將這些所有的點投影到 global frame 中,用以實現校正工作。 取第 k 個 LiDAR scan,則在該 scan 掃描結束的當下,可以獲得第 k 個 scan 的 local 座標點為 $\lbrace ^L{\rm p}_j, j = 1, ..., m \rbrace$,則**因為 LiDAR 在測量時存在各種 LiDAR measurement noise,因此每個點 ${\rm p}_j$ 都會受到測距和方向所組合而成的噪聲 $^L{\rm n}_j$ 所影響**,所以真正的 LiDAR point (稱為 true point) 與測量值之間具有以下關係: $$ ^L{\rm p}^{\rm gt}_j = ^L{\rm p}_j + ^L{\rm n}_j $$ 針對這個 true point $^L{\rm p}^{gt}_j$,以 State Transition Model 中得到的相對應 LiDAR 位姿值 $^G{\rm T}_{I_k} = (^G{\rm R}_{I_k}, ^G{\rm p}_{I_k})$ 與 LiDAR 和 IMU 之間的外參 $^I{\rm T}_L = (^I{\rm R}_L, ^I{\rm p}_L)$,進行座標轉換後,可將該點準確地映射於地圖中的 local plane 上面,並滿足下列公式: $$ 0 = ^G{\rm u}^T_j (^G{\rm T}_{I_k} ^I{\rm T}_L (^L{\rm p}_j + ^L{\rm n}_j) - ^G{\rm q}_j) $$ 其中參數定義如下: - $^G{\rm u}_j$:表示相對 local plane 的法向量 - $^G{\rm q}_j$:表示平面上的點 <img src="https://hackmd.io/_uploads/HkXm6JTn6.png" /> 如上圖所示,紅色點代表 LiDAR scan 中的點 $^L{\rm p}_j$,利用這個點找到它的 true point $^L{\rm p}^{gt}_j$ 後,將該點映射於 corresponding local plane 上,可以得到最後的深藍色點 $^G{\rm q}_j$,其中紅色虛線指向 ($^G{\rm u}_j$) 的則是該點相對於 local plane 的法向量。 而由於 $^I{\rm T}_L = (^I{\rm R}_L, ^I{\rm p}_L)$、$^G{\rm T}_{I_k} = (^G{\rm R}_{I_k}, ^G{\rm p}_{I_k})$ 均包含在狀態向量 ${\rm x}_k$ 中,因此該測量方程又可以寫為: $$ 0 = {\rm h}_j ({\rm x}_k, ^L{\rm p}_j + ^L{\rm n}_j) $$ 2. 狀態的 Prior Gaussian Distribution 在 Kalman Filter 中,先驗高斯分布的意思在於我們在獲取到新的測量數據之前,會先對系統狀態做一個概率分布,而這個分布是基於之前的所有訊息,包含初始狀態估計和所有過去的測量,因此這個分布是「先驗」的,因為它是在觀察到新數據前就形成的。 在卡爾曼濾波的開始,我們需要初始化狀態估計 $\hat{\rm{x}}_0 = \hat{\rm{x}}_{k - 1}$ 以及它的共變異數矩陣 $P_0 = P_{k - 1}$,這部分的值會是前面 forward propagation 的計算結果。 除此之外,我們定義誤差狀態為真實狀態 $\rm{x}_k$ 與估計狀態 $\hat{\rm{x}}_k$ 之間的差異為 $\rm{x}_k \boxminus \hat{\rm{x}}_k$。 把上述誤差狀態的計算公式進一步展開為 $\rm{x}_k \boxminus \hat{\rm{x}}_k = \hat{\rm{x}}^{\cal K}_k \boxminus \hat{\rm{x}}_k + {\rm J}^{\cal K} \tilde{\rm x}^{\cal K}_k \sim {\cal N}(0, \hat{\rm P}_k)$,其中,${\rm J}^{\cal K}$ 表示誤差狀態的 Jacobian Matrix,用來描述如果誤差狀態有小變化的時候,估計狀態將如何變化。 ${\rm J}^{\cal K} = \begin{bmatrix} {{\rm A}(\delta ^G\theta_{I_k})^{-T}} & {0_{3 \times 15}} & {0_{3 \times 3}} & {0_{3 \times 3}} \\ {0_{15 \times 3}} & {{\rm I}_{15 \times 15}} & {0_{3 \times 3}} & {0_{3 \times 3}} \\ {0_{3 \times 3}} & {0_{3 \times 15}} & {{\rm A}(\delta ^I\theta_{L_k})^{-T}} & {0_{3 \times 3}} \\ {0_{3 \times 3}} & {0_{3 \times 15}} & {0_{3 \times 3}} & {{\rm I}_{3 \times 3}} \end{bmatrix}$ 為 $(\hat{\rm x}^{\cal K}_k \boxplus \tilde{\rm x}^{\cal K}_k) \boxminus \hat{\rm x}_k$ 對 $\tilde{\rm x}^{\cal K}_k = 0$ 的偏微分 在第一次的 iteration 中,由於 $\hat{\rm x}^{\cal K}_k = \hat{\rm x}_k$,因此可得知 ${\rm J}^{\cal K} = {\rm I}$。 最後,由於共變異數矩陣 $\hat{P}_k$ 是用以量化狀態估計的不確定性的矩陣,因此我們會以這個不確定性作為高斯常態分佈的模型,表示為 $\sim {\cal N}(0, \hat{\rm P}_k)$。其中,之所以設定平均值為 0 的原因在於我們假設在最初狀態估計的時候沒有誤差,因此可以將誤差狀態的初始分布視為平均值為 0。而隨著時間推移與系統演化,誤差會根據過程的噪聲和觀測噪聲累積起來,而 $\hat{P}_k$ 則會根據這些噪聲的共變異數矩陣來更新,反映了我們對誤差分布的最新理解。 3. Maximum a-Posteriori Estimate 最大化後驗估計 根據我們在殘差估計階段中得到的 $0 = {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k + {\rm v}_j$ 可以調整為 $-{\rm v}_j = {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k \sim {\cal N} (0, {\rm R}_j)$,其中觀測噪聲 $-v_j$ 也可以假設為高斯分布,稱為觀測概率分布,其均值為 0,共變異數矩陣為 ${\rm R}_j$。 再結合剛剛計算出的先驗概率分布 $\rm{x}_k \boxminus \hat{\rm{x}}_k = \hat{\rm{x}}^{\cal K}_k \boxminus \hat{\rm{x}}_k + {\rm J}^{\cal K} \tilde{\rm x}^{\cal K}_k \sim {\cal N}(0, \hat{\rm P}_k)$ 後,可以得到系統狀態的後概率分布,其中計算過程如下: 結合先驗概率分布下列觀測概率,我們可以得到系統狀態的後驗概率分布。 為了找到給定數據的最可能狀態,我們使用 MAP 估計(一種貝斯估計),透過最大化後驗概率來尋找一組參數或狀態的最優值,而在高斯分布的假設下,**最大化後驗概率等同於下列成本函數(負對數似然函數)的最小化計算結果,因此我們必須最小化成本函數計算結果**。 $$ \mathop{\min}\limits_{\tilde{\rm x}^{\cal K}_k} \left( \Vert {\rm x}_k \boxminus \hat{\rm x}_k \Vert^2_{\hat{\rm P}_k} + \sum^m_{j = 1} \Vert {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k \Vert^2_{{\rm R}_j} \right) $$ 在上式中,成本函數 $\left( \Vert {\rm x}_k \boxminus \hat{\rm x}_k \Vert^2_{\hat{\rm P}_k} + \sum^m_{j = 1} \Vert {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k \Vert^2_{{\rm R}_j} \right)$ 表達了對狀態估計 $\hat{x}_k$ 的後驗概率最大化,其中 $\Vert {\rm x} \Vert^2_{\rm M} = {\rm x}^T {\rm M}^{-1} {\rm x}$。 - $\Vert {\rm x}_k \boxminus \hat{\rm x}_k \Vert^2_{\hat{\rm P}_k}$ 表示先驗誤差,即狀態估計誤差的平方,權重為先驗共變異數矩陣的逆矩陣 $\hat{P}_k^{-1}$ - $\sum^m_{j = 1} \Vert {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k \Vert^2_{{\rm R}_j}$:表示地 $j$ 次觀測的加權平方誤差,權重為觀測噪聲共變異數矩陣的逆矩陣 $\hat{R}_j^{-1}$ 最小化這個成本函數代表著找到一個狀態估計,使得在綜合考慮先驗信息和新的觀測信息之後,這個估計的不確定性為最小,在 iterated Kalman Filter 的過程中,通過重複應用預測和更新步驟可以逐步縮小這個不確定性。 以上這個最大化後驗狀態估計的動作稱為 MAP (Maximum a-Posteriori) Estimate,Kalman Filter 的主要目的就是要解這個問題。 4. 利用 Kalman Filter 求解 MAP Estimate 我們可以透過 iterated Kalman Filter 來求解上述的最小目標函數,其中步驟說明如下: - 計算卡爾曼增益 $K$ 為了權衡觀測數據與當前估計的相對重要性,我們會需要計算當前狀態的卡爾曼增益,其中卡爾曼增益被定義為**使後驗共變異數最小化的值**。 根據卡爾曼濾波的推導過程,可以得到 $K$ 的最優解為: $$ K = PH^T(HPH^T + R)^{-1} $$ 其中,$H$ 為觀測矩陣、$P$ 為先驗共變異數矩陣、$R$ 為觀測噪聲的共變異數矩陣。 以上方公式為基準,可以利用 Woodbury 矩陣恆等式推得 $K = (H^TR^{-1}H + P^{-1})^{-1} H^TR^{-1}$ 為 FAST-LIO2 狀態更新時使用的卡爾曼增益。 - 更新狀態矩陣與對應共變異數矩陣 已知卡爾曼濾波的標準更新方程如下: * state update: $\hat{\rm x}_{t+1} = \hat{\rm x}_t + K_t(z_t - H {\rm x}_t)$ * covatiance matrix update: $P_{t+ 1} = (I - K_t H) P_t$ 然而,在處理非線性系統時,這種直接的線性更新就不再適用,在這種情況下,我們需要採用另一種非線性策略。 在這個非線性環境下,狀態的更新公式變為一個在流形上的加法,以 $\boxplus$ 表示,而差分則以 $\boxminus$ 來表示。 因此我們可以得到以下的 optimal state 與其共變異數的計算結果: * $\bar{\rm x}_k = \hat{\rm x}^{{\cal K} + 1}_k = \hat{\rm x}^{\cal K}_k \boxplus \left( -{\rm K}{\rm z}^{\cal K}_k - {\rm I} - {\rm KH})({\rm J}^{\cal K})^{-1} (\hat{\rm x}^{\cal K}_k \boxminus \hat{\rm x}_k) \right)$ * $\bar{\rm P}_k = ({\rm I-KH}){\rm P}$ 而這個 optimal state 即為更新後的狀態向量,透過此更新的狀態向量 $\bar{\rm x}_k$,我們將第 $k$ 個 scan 中的每個 LiDAR point ($^L{\rm p}_j$) 根據以下公式轉換到 global frame 中: $$ ^G\bar{\rm p}_j = {^G\bar{\rm T}_{I_k}} {^I\bar{\rm T_{L_k}}} {^L{\rm p}_j} $$ 其中 $j = 1, ..., m$。 而最後轉換而得的 LiDAR point $\lbrace ^G\bar{\rm p}_j \rbrace$ 則會被加入到 ikd-Tree 中。 <!-- - 計算 optimal state 並將每個 LiDAR point 轉換到 global frame 中 在 residual computation 中,我們利用 measurement model 中建立的測量方程在 ${\rm x}^{\cal K}_k$ 處進行一次近似得到的結果 $0 = {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k + {\rm v}_j$ 可以調整為 $-{\rm v}_j = {\rm z}^{\cal K}_j + {\rm H}^{\cal K}_j \tilde{\rm x}^{\cal K}_k \sim {\cal N} (0, {\rm R}_j)$。 以上述計算結合 forward propagation 與 measurement model 中的計算結果,可以得到 state vector 的 maximum a-posteriori (MAP) [最大後驗估計,一個目標函數] 如下: --> 5. ikd tree 說明 ![img1](https://hackmd.io/_uploads/rJN_53mxR.png) 上圖表示 FAST-LIO2 框架中對於地圖進行區域管理和為維護的示意圖,地圖點通過一個 ikd-tree 進行維護,通過不斷融合新的掃描點,地圖的區域也會不斷增大。但由於地圖不可無限制地增大,因此會在當前位置設定一個長度為 L 的局部區域,如上圖 (a) 所示。 檢測區域以 LiDAR 的位姿(位置)為中心、$r$ 為半徑的球,其中 $r = \gamma R$ ($R$ 為 LiDAR 的測距範圍,$\gamma$ 為一個大於 1 的鬆弛參數)。 當新的位置 $p'$ 產生的球狀範圍與地圖的邊緣相交集時,地圖會移動距離 $d = (\gamma - 1)R$,如上圖 (b) 所示,而橘色區域則會被通過盒狀式操作刪除。 - ikd tree 基本結構 ![img2](https://hackmd.io/_uploads/S1kPIaXeA.png) k-d tree 是在 tree node 上除存一系列的點,而 ikd tree 則是一個 tree node 對應一個點雲,如上圖所示。 其中,相參數意義如下: * `point`:除存了點雲的訊息,如座標、強度等 * `leftchild` / `rightchild`:指向左、右子樹的指標 * `axis`:用來劃分空間的分割軸 * `treesize`:針對當前 tree root 的子節點數量 * `range`:外接的一個立方體,由兩個對角點所組成,為每個維度的最大與最小座標 - ikd tree 的建構過程 idk tree 會不斷根據最常維度的終點進行空間劃分,直到子空間中只有一個點為止。下圖代表 ikd tree 建構的初始化資訊: ![image](https://hackmd.io/_uploads/Bkj0jEaWC.png) - ikd tree Incremental Updates incremental operation 包含兩種類型,Point Insertion 與 Box-wise Delete。 * point insertion:表示在 ikd tree 中插入、刪除或重新插入一個單獨的點 如下方演算法所示,ikd tree 會同時進行點插入和地圖的 downsampling 操作。 ![image](https://hackmd.io/_uploads/BkfToNpWA.png) 根據上方演算法,首先會給定一個轉換到 global 座標系下的點 $p$,downsample resolution 為 $l$。 1. downsample operation:用算法將空間分隔程長度為 $l$ 的小立方體,找到包含點 $p$ 的小立方體 $C_D$,僅保存 $C_D$ 中最靠近中心的點。 首先在 ikd tree 上搜索所有包含在地方體 $C_D$ 的點,和新插入點一起存在一個數組 $V$ 中,之後比較數組 $V$ 中的所有點與 $C_D$ 中心點 $p_{center}$ 之間的距離,只保留 $V$ 中的最近點 $p_{nearest}$,並把保留點 $p_{nearest}$ 插入到 ikd tree 內。 2. point insertion:在 downsample operation 內,找到距離 $C_D$ 中心點 $p_{center}$ 的最近點 $p_{nearest}$ 後,需要將保留點 $p_{nearest}$ 以 point insertioin 的規則插入到 ikd tree 內。 第 11 ~ 24 行裡面以 recursion 的方式執行 point insertion,演算法中,從 root node 開始進行搜索,直到找到一個 empty node 後,就會新增新的 node。 在每一個 non-empty node 中,新的點和原先存於 tree node 上的點在分割維度上進行比較,以進一步去做 recursion。 插入過程中會訪問過節點的 treesize,並更新 range 屬性(line 21),最後在第 22 行中檢查了插入點之後的 tree balance。 * box-wise delete:表示對一個立方體中的所有點進行插入、刪除或重新插入的操作 針對 ikd tree node 的刪除,作者採 lazy labels 的策略,也就是說點不會立即從 tree 上被移除,而是被標記為 "deleted",而當 tree node `T` 的所有 child nodes 都被標記為 "deleted" 時,tree node `T` 的 `treedeleted` 會被標記為 `true`。 最後,在 reconstruct 過程中,被標記為 "deleted" 的點會從 tree 中被移除掉。 box-wise delete operation 依靠 `range` 屬性中的範圍訊息以及 tree node 中的 lazy labels 策略來實現。假設針對一個節點 `T`,它對應的空間範圍為立方體 $C_T$,需要從以節點 `T` 的子樹中刪除立方體 $C_O$ 中的點,則操作步驟具體如下: ![image](https://hackmd.io/_uploads/ryhjjN6WR.png) 1. 如果立方體 $C_O$ 和立方體 $C_T$ 之間沒有交集,則直接返回,不需要更新 ikd tree 2. 如果立方體 $C_O$ 完全包含立方體 $C_T$,則刪除 tree node `T`,設定 統整以上步驟,可得到以下的狀態更新演算法: ![013](https://hackmd.io/_uploads/BJQrp1T2a.png)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully