(很早之前的笔记,传上来记录一下)
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)这个数学模型的点输出。在这里,我们可以假设地面点云符合的数学模型是一个平面模型,因为大部分地面还是比较平坦的,可以当做一个平面看待。平面的数学模型就是 a x + b y + c z + d = 0 ax + by + cz + d = 0 ax+by+cz+d=0,而确定一个平面一般三个点就可以了,根据 ransac 的方法,我们可以先从一帧点云中随机采样三个点,然后确定这个平面的模型参数(也就是 a , b , c , d a,b,c,d a,b,c,d),将所有到这个平面的距离 d i s t a n c e ( p o i n t , p l a n e ) < δ distance(point, plane) < \delta distance(point,plane)<δ 的点视为 inliers,其余点都记为 outliers,如果 N i n l i e r s N a l l \frac{N_{inliers}}{N_{all}} NallNinliers 满足一定条件,我们就直接输出 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
算法的参数包括:t(数据拟合模型的阈值),d(算法需要输出的最少 inliers 数量),k(迭代次数),其中 k 是可以根据期望算法成功(即算法能够输出至少 d 个 inliers)概率 p 以及计算模型参数所需的最少输入数据 n 得到的。假设 w = N i n l i e r s N a l l w = \frac{N_{inliers}}{N_{all}} w=NallNinliers 为从全部数据中进行一次采样选取到 inliers 的概率,那么选取到 n 个 inliers 的概率就是 w n w^n wn(实际应该是 C N i n l i e r s n C N a l l n \frac{C_{N_{inliers}}^n}{C_{N_{all}}^n} CNallnCNinliersn,wiki 在后面也说到了,算出来的 k 可以作为上限),这样 1 − w n 1 - w^n 1−wn 就是没有选取到 n 个 inliers 的概率,迭代 k 次都没有选取到 n 个 inliers 的概率也就是 1 − p = ( 1 − w n ) k 1-p = (1-w^n)^k 1−p=(1−wn)k,所以 k = l o g ( 1 − p ) l o g ( 1 − w n ) k = \frac{log(1-p)}{log(1-w^n)} k=log(1−wn)log(1−p)
RANSAC 的好处是能够比较鲁棒地估计出模型参数,即使在有很多离群点的输入中也能得到一个高准确率的参数。其缺点是在计算模型参数时没有一个时间上限(对于一个复杂模型来说);当迭代次数太少时很可能无法得到最优解(这就是一个 trade-off 了);当 inliers 占比小于 50% 时经常难以找到最优解(当然,这也有其他的 Optimal RANSAC 被提出来解决这个问题,可以在 inlier ratio 小于 5% 时找到最优解);需要手动指定参数,并且根据问题的不同,参数也大不相同。
RANSAC 还有一个无法解决的问题是:当输入数据中包含多个 model instance 时,RANSAC 可能一个都找不到。也就是说,RANSAC 只适合于找出一个 model instance,对于多个 model instance 问题可以采用 Hough transform 或 PEARL 等其他方法。
在 pcl 中,RANSAC 有好几种实现,其中 ransac.hpp 实现的就是最原始的 RANSAC,跟上述伪代码基本差不多。实际实现中有几个点可以说一下
道格拉斯-普克算法:将曲线近似表示为一系列点,并减少点的数量
difference of normals:就是给定一个半径 r1,r2(r1 < r2),然后分别计算每个点 p 在该半径下的单位法线方向, D o N = n ^ ( p , r 1 ) − n ^ ( p , r 2 ) 2 DoN = \frac{\hat{n}(p, r1) - \hat{n}(p, r2)}{2} DoN=2n^(p,r1)−n^(p,r2),如果这个差值不大的话,可以说明点 p 位于一个平坦的位置,如果差值比较大,则不平坦,此时一个 r 1 < r < r 2 r1 < r < r2 r1<r<r2 的半径计算出来的法线可能可以较好地代表 p。
需要注意的是,DoN 本身只是一个算子,它并没有规定法线是如何计算的,但在论文中有所讨论,一般来说在非结构化数据中使用 fixed support radius 计算法线比 fixed number of neighbors 要好,尤其是在点云密度在不同位置差异较大的情况下。如果是在室外环境中使用 fixed number of neighbors,每个点的 support radius 将会有很大区别,这也就导致各个点在不同的尺度上计算法线,这对 DoN 来说是不适合的。
PCL 的 DoN 源码里计算 normal 是通过 PCA 来的,计算多个点的最大特征向量(最大特征值对应的特征向量)