Reference:
激光雷达是利用激光束来感知三维世界,通过测量激光返回所需的时间输出为点云。它集成在自动驾驶、无人机、机器人、卫星、火箭等许多领域。
激光雷达传感器利用光原理进行工作,激光雷达代表光探测和测距。它们可以探测到 300m 以内的障碍物,并准确估计它们的位置。在自动驾驶汽车中,这是用于位置估计的最精确的传感器。
激光雷达传感器由两部分组成:激光发射(顶部)和激光接收(底部)。发射系统的工作原理是利用多层激光束,层数越多,激光雷达就越精确。层数越多,传感器就越大。激光被发射到障碍物并反射,当这些激光击中障碍物时,它们会产生一组点云,传感器与飞行时间(TOF)进行工作,从本质上说,它测量的是每束激光反射回来所需的时间。如下图:
当激光雷达的质量和价格非常高时,激光雷达是可以创建丰富的三维环境,并且每秒最多可以发射200万个点。点云表示三维世界激光雷达传感器获得每个撞击点的精确 ( X , Y , Z ) (X, Y, Z) (X,Y,Z)位置。
激光雷达传感器可以是固态的,也可以是旋转的,固态激光雷达将把检测的重点放在一个位置上,并提供一个覆盖范围(比如FOV为90°)。在后一种情况下,它将围绕自身旋转,并提供360°旋转。在这种情况下,一般把它放在设备顶上,以提高能见度。
激光雷达很少用作独立传感器。它们通常与相机或雷达结合在一起,这一过程称为传感器融合。融合过程可分为早期融合和后期融合。早期融合是指点云与图像像素融合,后期融合是指单个检测物的融合。
缺点:
优点:
激光雷达进行障碍物的步骤通常分为 4 个步骤:
稀疏的;
不规则----搜索邻居比较困难;
缺乏纹理信息;
无序的----深度学习很困难;
[ x 1 y 1 z 1 x 2 y 2 z 2 ⋮ ⋮ ⋮ x N y N z N ] = [ x 2 y 2 z 2 x 1 y 1 z 1 ⋮ ⋮ ⋮ x k y k z k ] \left[\begin{array}{ccc}x_{1} & y_{1} & z_{1} \\ x_{2} & y_{2} & z_{2} \\ \vdots & \vdots & \vdots \\ x_{N} & y_{N} & z_{N}\end{array}\right]=\left[\begin{array}{ccc}x_{2} & y_{2} & z_{2} \\ x_{1} & y_{1} & z_{1} \\ \vdots & \vdots & \vdots \\ x_{k} & y_{k} & z_{k}\end{array}\right] ⎣⎢⎢⎢⎡x1x2⋮xNy1y2⋮yNz1z2⋮zN⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡x2x1⋮xky2y1⋮ykz2z1⋮zk⎦⎥⎥⎥⎤
如上面的行换了,表达的还是同一个物体。对于深度学习而言,输入进去的矩阵是不一样的就会产生不同的输出,但是我们希望输出是一样的。
旋转同变性/不变性:我们旋转一些点,它的坐标是不一样的,但是它还是同一个物体。
为了处理点云,我们可以使用最流行的库 PCL(point cloud library)。它在 Python 中可用,但是在 C++ 中使用它更为合理,因为语言更适合机器人学。它也符合 ROS(机器人操作系统)。PCL 库可以完成探测障碍物所需的大部分计算,从加载点到执行算法。这个库相当于 OpenCV 的计算机视觉。因为激光雷达的输出很容易达到每秒 100000 个点,所以我们需要使用一种称为体素网格的方法来对点云进行下采样。
体素网格 是一个三维立方体,通过每个立方体只留下一个点来过滤点云。立方体越大,点云的最终分辨率越低。最终,我们可以将点云的采样从几万点减少到几千点。
滤波完成后我们可以进行的第二个操作是 ROI(感兴趣区域) 的提取,我们只需删除不属于特定区域的每一些点云数据,例如左右距离 10m 以上的点云,前后超过 100m 的点云都通过滤波器滤除。现在我们有了降采样并滤波后的点云了,此时可以继续进行点云的分割、聚类和边界框实现。
点云分割任务是将场景与其中的障碍物分离开来,其实就是地面的分割。一种非常流行的分割方法称为 RANSAC(Random Sample consenses)。该算法的目标是识别一组点中的异常值。点云的输出通常表示一些形状。有些形状表示障碍物,有些只是表示地面上的反射。RANSAC 的目标是识别这些点,并通过拟合平面或直线(拟合的是地面)将它们与其他点分开。
为了拟合直线,我们可以考虑线性回归。但是有这么多的异常值,线性回归会试图平均结果,而得出错误的拟合结果,与线性回归相反,这里的 RANSAC 算法将识别这些异常值,且不会拟合它们。
如上图所示我们可以将这条线视为场景的目标路径(即道路),而孤立点则是障碍物。
过程如下:
因此需要算法一个参数:距离阈值。
最后选择内点最多的迭代作为模型;其余的都是离群值。这样,我们就可以把每一个内点视为道路的一部分,把每一个外点视为障碍的一部分。RANSAC应用在3D点云中。在这种情况下,3个点之间的构成的平面是算法的基础。然后计算点到平面的距离。
下面点云为 RANSAC 算法的结果,紫色区域代表车辆(RANSAC 在这里应该只用来区分地面了):
RANSAC 是一个非常强大和简单的点云分割算法。它试图找到属于同一形状的点云和不属于同一形状的点云,然后将其分开。
RANSAC 的输出是障碍点云和地面。由此,可以为每个障碍定义独立的簇。它是如何工作的?
聚类是一系列机器学习算法,包括:k-means(最流行)、DBScan、HDBScan 等。这里可以简单地使用欧几里德聚类,计算点之间的欧几里德距离。
在进行点云聚类问题时,由于一个激光雷达传感器可以输出几万个点云,这将意味有上万次的欧几里德距离计算。为了避免计算每个点的距离,这里使用 KD-Tree 进行加速。
KD-Tree 是一种搜索算法,它将根据点在树中的XY位置对点进行排序,一般的想法-如果一个点不在定义的距离阈值内,那么x或y更大的点肯定不会在这个距离内。这样,我们就不必计算每一个点云。
过程如下:
点云欧式聚类算法就是将一组点云按其距离进行分割。聚类算法以距离阈值、最小聚类数目和最大聚类数目作为输入。通过这种方式,可以过滤“幽灵障碍物”(一个单点云在空间中是没有理由存在的),并定义一个封闭的障碍物距离。如下图这里用不同颜色来代表聚类后的障碍物点云簇。
K-NN
在空间 M M M 中给定点集 S S S,一个查询点 q ∈ M q\in M q∈M,在 S S S 中查找 k k k 个最近点。
Fixed Radius-NN
在空间 M M M 中给定点集 S S S,一个查询点 q ∈ M q\in M q∈M,在 S S S 中查找所有符合 ∣ ∣ s − q ∣ ∣ < r ||s-q||
图像:
一个邻居简单的表示为 x + Δ x , y + Δ y x+\Delta x,y+\Delta y x+Δx,y+Δy.
点云:
一个节点包含:
比较小放左边,比较大放右边。如果左右边被占据了,就跟左右边被占据的继续对比。
给定一个一维点集 { x 1 , x 2 , . . . , x n } , x i ∈ R \{x_1, x_2, ...,x_n\},x_i\in\R {x1,x2,...,xn},xi∈R,如一个数组: [ 100 , 20 , 500 , 10 , 30 , 40 ] [100,20,500,10,30,40] [100,20,500,10,30,40]
最坏的情况是 O ( h ) O(h) O(h),在这里 h h h 为在 BST 中点的个数。如将数组 [9,8,7,6,5,4,3] 按顺序插入进一个空的 BST 中:
这是一个合法的二叉树但它一无是处。平衡二叉树是另一个话题了。最佳情况: h = l o g 2 n h=log_2n h=log2n。
给定一个 BST 和一个待查找 key,决定哪一个 node 等于这个 key,如果没有,则返回 NULL。
比如说在下图中查找点 11:
1. 根节点为 8,这时最坏距离为 11-8=3。11比8大,这时知道要查找的范围在(8,14)之间,右边子树在范围(8,+inf),往右边看;
2. 到节点 10,这时最坏距离为 11-10=1,这时知道要查找的范围在(10,12)之间,因此不用往右边看,这时右边是比 10 大的,往右边看;
3. 到节点 14,最坏距离依旧为 1。这时要不要找 14 左边子节点?是需要的,左边的子节点比 14 小,要找的仍是(10,12)之间;
4. 找到 13,发现没什么变化,这时子节点已经遍历完了,退回 14;
5. 同理退回 10;
6. 同理退回 8
这样子就省去了五个比较的操作。
原始的二叉树比较难用于高维的最邻近搜索,因为二叉树搜索方法依赖一个特性:左边小,右边大。对于一维数据来说,可以比较距离有多少,但如果这是个高维数据就不行了。
Kd-tree 就是在每个维度上做一个二叉树。但是它有一个更为复杂的地方是,每一个节点包含很多内容。
左边的情况是找到一个超平面,将超平面放在数据点上;
右边的超平面不属于任何点。
下面是树的两种切法,这里的波浪线是个二位数据。第一步在中间切一刀,第二步:
这两种方式结果上是一样的,只是第二种可能速度会更快一点,使树更加平衡。
最终的目标是围绕每个点云簇创建一个三维边界框。因为我们没有对点云簇进行任何分类,所以我们必须将边界框与点云相匹配。主成分分析(PCA)是一种有助于拟合边界框的算法。
PCA 用来找到点云的主方向。物理意义:将数据点都投影到一个非常有特征性的方向上 ,每一个点在这个方向上的投影就是主成分。如下图所示,在三维情况下,这个椭圆的主成分就是它的三个轴。
比如有一个矩阵 M M M,它可以被分解为 U U U, Σ \Sigma Σ, V ∗ V^* V∗,其中 U U U 和 V ∗ V^* V∗ 都是正交矩阵; Σ \Sigma Σ 是一个对角阵,它在对角线上组成了 M M M 的特征值。
假设我们把 M M M 乘上一个向量:
1. 什么叫做最主要的成分?
如果把这堆高维点投影到某一个方向上,这些投影后的点的方差是最大的。也就是说,这些点在这个方向上分布的非常散。
2. 如何得到第二主要的成分?
把这一组输入 x i x_i xi 里面的、属于 z 1 z_1 z1 的成分都去掉,然后再找剩下的东西里面的最主要成分。
3. 如何得到第三主要的成分?
同上,去掉第一、二主要的成分。
总结:
输入: x i ∈ R n , i = 1 , 2 , . . . , m x_i\in \mathbb{R}^n,i=1,2,...,m xi∈Rn,i=1,2,...,m,怎么做 PCA?
标准化数据,即减去数据的中心:
X ~ = [ x ~ 1 , ⋯ , x ~ m ] , x ~ i = x i − x ˉ , i = 1 , ⋯ , m x ˉ = 1 m ∑ i = 1 m x i \tilde{X}=\left[\tilde{x}_{1}, \cdots, \tilde{x}_{m}\right], \tilde{x}_{i}=x_{i}-\bar{x}, i=1, \cdots, m \quad \bar{x}=\frac{1}{m} \sum_{i=1}^{m} x_{i} X~=[x~1,⋯,x~m],x~i=xi−xˉ,i=1,⋯,mxˉ=m1i=1∑mxi
计算 SVD: H = X ~ X ~ T = U r Σ 2 U r T H=\tilde{X} \tilde{X}^{T}=U_{r} \Sigma^{2} U_{r}^{T} H=X~X~T=UrΣ2UrT
主向量为 U r U_r Ur 的列,第一个主向量就是 U r U_r Ur 的第一列,第二个就是第二列,以此类推。
将高维数据点投影到低维去,尽可能保留他们的特征。
给定 x i ∈ R n , i = 1 , 2 , . . . , m x_i\in \mathbb{R}^n,i=1,2,...,m xi∈Rn,i=1,2,...,m,使用 PCA 找到它的 l l l 个主向量 z 1 , z 2 , . . . , z l , z j ∈ R n {z_1,z_2,...,z_l},z_j\in \mathbb{R}^n z1,z2,...,zl,zj∈Rn:
经过降维再升维肯定有数据上的损失,只是损失的比较少 。
从图中可以看到,在主向量上投影下来,仍可以看到有两坨点;而在第二个主向量上就只有一个波峰了。也可以再次说明主向量上保留了更多的信息。
在 5.1.4 中提到的都是普通的 PCA,普通 PCA 是线性的,因为矩阵乘法其实也就是一个线性操作(矩阵乘上一个向量其实是对一个矩阵的线性组合)。在遇到的数据不是一个线性的情况下,该怎么办?如左图的同心圆,做线性PCA很难区分开来,如右图所示,会得到两条线,没给出特别有意义的信息,比如将他们区分开。这时可以考虑在更高维度下进行操作。
刚刚所做的 [ x i 1 , x i 2 , x i 1 2 + x i 2 2 ] [x_{i1}, x_{i2},x_{i1}^2+x_{i2}^2] [xi1,xi2,xi12+xi22] 操作就是 kernel PCA。
Kernel PCA 步骤:
现仍存在的问题:
特征向量可以表达为数据点的线性组合 z ~ = ∑ j = 1 N α j ϕ ( x j ) \tilde{z}=\sum_{j=1}^{N} \alpha_{j} \phi\left(x_{j}\right) z~=∑j=1Nαjϕ(xj)-----找到了系数 α j \alpha_j αj 即找到了特征向量 z ~ \tilde{z} z~。
证明:
H ~ z ~ = λ ~ z ~ 1 N ∑ i = 1 N ϕ ( x i ) ϕ T ( x i ) z ~ = λ ~ z ~ \tilde{H} \tilde{z}=\tilde{\lambda} \tilde{z} \\\frac{1}{N} \sum_{i=1}^{N} \phi\left(x_{i}\right) \phi^{T}\left(x_{i}\right) \tilde{z}=\tilde{\lambda} \tilde{z} H~z~=λ~z~N1i=1∑Nϕ(xi)ϕT(xi)z~=λ~z~其中 ϕ T ( x i ) z ~ \phi^{T}\left(x_{i}\right) \tilde{z} ϕT(xi)z~ 是一个标量,所以最后的特征向量 z ~ \tilde{z} z~ 就是 ϕ ( x i ) \phi(x_i) ϕ(xi) 的线性组合。
常用核函数的选择:
通常来说只能通过实验来判断哪个的效果好,除非对数据非常了解,知道该使用什么样子的核函数。
真 ⋅ \cdot ⋅步骤:
例子:
如下图所示,输入数据在线性 PCA 下不可分:
当 kernel PCA 使用二次多项式核函数 k ( x i , x j ) = ( 1 + x i T x j ) 2 k\left(x_{i}, x_{j}\right)=\left(1+x_{i}^{T} x_{j}\right)^{2} k(xi,xj)=(1+xiTxj)2 时,如左图所示,点通过第一映射 y 0 y_0 y0 就很明显的区分开了:
也可以使用高斯核函数 k ( x i , x j ) = e − β ∥ x i − x j ∥ 2 k\left(x_{i}, x_{j}\right)=e^{-\beta\left\|x_{i}-x_{j}\right\|_{2}} k(xi,xj)=e−β∥xi−xj∥2,如上右图所示,区分的也很好。
/*************************** 额外的 *********************************/
噪声去除
1.1 离群值清除(Raduius Outlier Removal)
1.2 统计离群值清除(Statistical Outlier Removal)
降采样:保存特征的同时,降低运算量
2.1 体素栅格降采样(Voxel Grid Downsampling)
2.2 最远点采样(Farthest Point Sampling)
2.3 法向量空间采样(Normal Space Sampling)
上采样/平滑
3.1 双边滤波(Bilateral Filter)