RANSAC
(很早之前的筆記,傳上來記錄一下)
Random sample consensus (RANSAC) is an iterative method to estimate parameters of a mathematical model from a set of observed data that contains outliers, when outliers are to be accorded no influence on the values of the estimates.
– wiki
random sample consensus,這個算法很早就聽說過了,卻一直沒有去了解,這兩天學習了一下,發現其實很簡單。
因爲我們最近需要對點雲分類爲地面和非地面兩種,之前在地下車庫所採用的方法就是 autoware 的 lidar_euclidean_cluster,先進行 ransac 提取地面,然後用 difference of normals(根據每個點的法線進行聚類) 去除噪聲點,最後使用 euclidean distance based segmentation(一種基於歐式距離的泛洪方法) 對點雲進行擴充。在車庫裏這套方法本來還是效果還可以的,雖然也存在當車存在傾斜時不魯棒的問題,但是在室外的話還是不太 ok 啊。當然,這些問題之後會想辦法解決,這篇文章首先就先來對 ransac 方法進行一個介紹。
首先自然是先看下 wiki 了,上面說的還是很全的。ransac 方法的輸入是一組包含了離羣點的觀測數據,其輸出就是根據這些觀測數據迭代找到一個能夠最好地擬合目標數學模型的模型參數。
以剛纔提到的點雲分離地面爲例,這裏我們的輸入就是一幀實時的點雲,點雲中既包含了地面點雲(inliers)也包含了非地面點雲(如牆壁,樹木等,outliers),我們希望的輸出就是地面的那些點雲。ransac 的方法就是分析地面點雲的分佈,找到一個能夠最好的擬合這個分佈的數學模型,然後將一幀點雲中所有符合(可以自己設定 offset)這個數學模型的點輸出。在這裏,我們可以假設地面點雲符合的數學模型是一個平面模型,因爲大部分地面還是比較平坦的,可以當做一個平面看待。平面的數學模型就是 ,而確定一個平面一般三個點就可以了,根據 ransac 的方法,我們可以先從一幀點雲中隨機採樣三個點,然後確定這個平面的模型參數(也就是 ),將所有到這個平面的距離 的點視爲 inliers,其餘點都記爲 outliers,如果 滿足一定條件,我們就直接輸出 inliers,否則繼續隨機採樣重複,一直到達到一定的迭代次數。
當然,實際的算法並沒有這麼簡單,因爲我們無法保證算法一定能隨機採樣到我們希望的平面模型,這往往還跟算法的參數、inliers 在整個輸入中佔據的比例等等,不過算法的大概思想也就是這樣了。
算法僞代碼如下:
Given:
data – a set of observations
model – a model to explain observed data points
n – minimum number of data points required to estimate model parameters
k – maximum number of iterations allowed in the algorithm
t – threshold value to determine data points that are fit well by model
d – number of close data points required to assert that a model fits well to data
Return:
bestFit – model parameters which best fit the data (or nul if no good model is found)
iterations = 0
bestFit = nul
bestErr = something really large
while iterations < k {
maybeInliers = n randomly selected values from data
maybeModel = model parameters fitted to maybeInliers
alsoInliers = empty set
for every point in data not in maybeInliers {
if point fits maybeModel with an error smaller than t
add point to alsoInliers
}
if the number of elements in alsoInliers is > d {
% this implies that we may have found a good model
% now test how good it is
betterModel = model parameters fitted to all points in maybeInliers and alsoInliers
thisErr = a measure of how well betterModel fits these points
if thisErr < bestErr {
bestFit = betterModel
bestErr = thisErr
}
}
increment iterations
}
return bestFit
Parameters
算法的參數包括:t(數據擬合模型的閾值),d(算法需要輸出的最少 inliers 數量),k(迭代次數),其中 k 是可以根據期望算法成功(即算法能夠輸出至少 d 個 inliers)概率 p 以及計算模型參數所需的最少輸入數據 n 得到的。假設 爲從全部數據中進行一次採樣選取到 inliers 的概率,那麼選取到 n 個 inliers 的概率就是 (實際應該是 ,wiki 在後面也說到了,算出來的 k 可以作爲上限),這樣 就是沒有選取到 n 個 inliers 的概率,迭代 k 次都沒有選取到 n 個 inliers 的概率也就是 ,所以
Advantages and disadvantages
RANSAC 的好處是能夠比較魯棒地估計出模型參數,即使在有很多離羣點的輸入中也能得到一個高準確率的參數。其缺點是在計算模型參數時沒有一個時間上限(對於一個複雜模型來說);當迭代次數太少時很可能無法得到最優解(這就是一個 trade-off 了);當 inliers 佔比小於 50% 時經常難以找到最優解(當然,這也有其他的 Optimal RANSAC 被提出來解決這個問題,可以在 inlier ratio 小於 5% 時找到最優解);需要手動指定參數,並且根據問題的不同,參數也大不相同。
RANSAC 還有一個無法解決的問題是:當輸入數據中包含多個 model instance 時,RANSAC 可能一個都找不到。也就是說,RANSAC 只適合於找出一個 model instance,對於多個 model instance 問題可以採用 Hough transform 或 PEARL 等其他方法。
pcl/ransac.hpp
在 pcl 中,RANSAC 有好幾種實現,其中 ransac.hpp 實現的就是最原始的 RANSAC,跟上述僞代碼基本差不多。實際實現中有幾個點可以說一下
- 在進行隨機採樣時,是用的 mt19937 進行 shuffle 採樣的
- 採樣之後還要判斷採樣的好壞,代碼裏是調用 pcl::SampleConsensusModelPlane::isSampleGood 來判斷的,在計算平面模型時主要就是看這三點是否共線了
- 在判斷是否點是否在符合平面模型時,不僅考慮了點到平面的距離,還考慮了點的法線與平面法線間的夾角,權重是根據點的曲率來調整的,對於平面點,夾角的權重更大(防止其他平面點擬合到該平面上)
DoN
道格拉斯-普克算法:將曲線近似表示爲一系列點,並減少點的數量
difference of normals:就是給定一個半徑 r1,r2(r1 < r2),然後分別計算每個點 p 在該半徑下的單位法線方向,,如果這個差值不大的話,可以說明點 p 位於一個平坦的位置,如果差值比較大,則不平坦,此時一個 的半徑計算出來的法線可能可以較好地代表 p。
需要注意的是,DoN 本身只是一個算子,它並沒有規定法線是如何計算的,但在論文中有所討論,一般來說在非結構化數據中使用 fixed support radius 計算法線比 fixed number of neighbors 要好,尤其是在點雲密度在不同位置差異較大的情況下。如果是在室外環境中使用 fixed number of neighbors,每個點的 support radius 將會有很大區別,這也就導致各個點在不同的尺度上計算法線,這對 DoN 來說是不適合的。
PCL 的 DoN 源碼裏計算 normal 是通過 PCA 來的,計算多個點的最大特徵向量(最大特徵值對應的特徵向量)