激光雷達點雲數據內部空點補全


title: 激光雷達點雲數據內部空點補全
date: 2018-03-28 22:55:54
categories: ROS
tags:
- ROS
- PCL

歡迎訪問我的個人博客:zengzeyu.com

前言


點雲數據區別於圖像數據,不管是二維圖像還是三維圖像,圖像數據都充滿整個區域,二維圖像中每個像素點都有值,灰度值、RGB值等;三維圖像中有體數據(Voxel),根據光線投影算法等,可計算出每個體數據對應值,從而顯示於顯示器中。點雲數據由於其掃描生成數據過程的特性,就決定了其在數據方面與圖像數據不同,以機械式激光雷達爲例,當出現以下情況時,該位置掃描生成的點雲數據不存在(即爲NAN點):

  • 激光發射器發射出去的激光未收到返回光束
  • 激光接收器接收到的返回激光強度超出閾值範圍

另外,在數據處理階段,可根據需要對部分數據進行濾波處理,賦值爲NAN點,也能造成該出點雲缺失情況。

PCL庫中有自帶判斷點雲數據是否含有NAN點的函數: pcl::PointCloud<pcl::PointXYZ>::is_dense(), 以及過濾NAN點函數:pcl::removeNaNFromPointCloud()

本文旨在補全內部空洞點,而不是去掉空洞點。

點雲NAN點補全


本文補全原則基於有序點雲(organised)進行處理,非有序點雲無法進行處理(unorganised)。
以自動駕駛中使用的機械式激光雷達速騰聚創16線激光雷達RS-LiDAR-16爲例,其生成的有序點雲(organised)點雲尺寸爲 16 x 2016: 16爲激光線數,2016爲每一線激光繞中心一週旋轉儲存的點個數,因此有 16 x 2016 = 32256 個點,而實際得到的點數據基本不可能是32256,必有缺失。

補全規則

在每一線激光掃描得到一行點數據中,查找與NAN點最近的點進行補全,如果本行數據全部爲NAN(雖然不可能發生),則此行可刪除,調整點雲尺寸。
該規則基於的原則:在同一線激光掃描得到的點中,由於水平方向數據分辨率很高,所以一行數據中每個點與其鄰域內點相似。

算法設計

算法思路

爲了簡化敘述,本文將一線激光掃描得到數據縮小爲 360 個點,即一幀點雲尺寸變爲 16x360。
以一線激光掃描數據爲例,默認激光旋轉方向爲順時針方向,採用線性差值方法進行補全,由於一線激光掃描一圈得到的數據在360°內任意位置都是對偶的,所以在空點附近查找兩邊非空點,用其值進行補全,具體參考下圖示意。
圖上半部分爲一線激光雷達掃描得到數據鳥瞰圖,其中黑色方塊代表非空點,白色方塊代表空點;下半部分爲點距離圖,根據線性插值方法可以補全非空點。非空點距離計算採用極座標方式,首先得到線性插值得到的range,再使用當前轉角轉換到笛卡爾座標系下,可得到其 x, y 座標值,z 座標值也採用同樣的插值方法計算。

圖

其中一個特殊情況:轉角爲 0° 與轉角爲 360° 是等效的,在查找過程中,當轉角順時針查找到 360 °時則置爲 0°,當轉角逆時針查找到 0° 時則置爲 360°。

算法流程

根據以上思路,設計算法如下:

    def fix_nan_point(self, in_cloud):
        #fix edeg nan point 1st
        in_cloud = self.fix_left_edge_nan_point( in_cloud )
        in_cloud = self.fix_right_edge_nan_point( in_cloud )
        #fix centrol nan point
        for i in range(in_cloud.shape[0]):
            for j in range(1, in_cloud.shape[1]):
                if in_cloud[i, j, -1] == -1:
                    nan_size = 1
                    left = j - 1
                    right = j + 1
                    while in_cloud[i, left, -1] == -1:
                        left -= 1
                        nan_size += 1
                    while in_cloud[i, right, -1] == -1:
                        right += 1
                        nan_size += 1

                    height_diff_cell = (in_cloud[i, right, 2] - in_cloud[i, left, 2]) / nan_size
                    range_diff_cell = (in_cloud[i, right, 3] - in_cloud[i, left, 3]) / nan_size
                    in_cloud[i, j, 2] = in_cloud[i, left, 2] + (j - left) * height_diff_cell
                    in_cloud[i, j, 3] = in_cloud[i, left, 3] + (j - left) * range_diff_cell
                    if abs(j - left) < abs(right-j):
                        in_cloud[i, j, -1] = in_cloud[i, left, -1]
                    else:
                        in_cloud[i, j, -1] = in_cloud[i, right, -1]
        return in_cloud



    def fix_left_edge_nan_point(self, in_cloud):
        for i in range(in_cloud.shape[0]):
            if in_cloud[i, 0, -1] == -1:
                nan_size = 1
                left = 359
                right = 1
                while in_cloud[i,left,-1] == -1:
                    left -= 1
                    nan_size += 1
                while in_cloud[i,right,-1] == -1:
                    right += 1
                    nan_size +=1

                height_diff_cell = (in_cloud[i, right, 2] - in_cloud[i, left, 2]) / nan_size
                range_diff_cell = (in_cloud[i, right, 3] - in_cloud[i, left, 3]) / nan_size
                in_cloud[i, 0, 2] = in_cloud[i, left, 2] + (360 - left) * height_diff_cell
                in_cloud[i, 0, 3] = in_cloud[i, left, 3] + (360 - left) * range_diff_cell
                if abs(360 - left) < right:
                    in_cloud[i, 0, -1] = in_cloud[i, left, -1]
                else:
                    in_cloud[i, 0, -1] = in_cloud[i, right, -1]
        return in_cloud


    def fix_right_edge_nan_point(self, in_cloud):
        for i in range(in_cloud.shape[0]):
            if in_cloud[i, in_cloud.shape[1]-1, -1] == -1:
                nan_size = 1
                left = in_cloud.shape[1]-2
                right = 0
                while in_cloud[i,left,-1] == -1:
                    left -= 1
                    nan_size += 1
                while in_cloud[i,right,-1] == -1:
                    right += 1
                    nan_size +=1

                height_diff_cell = (in_cloud[i, right, 2] - in_cloud[i, left, 2]) / nan_size
                range_diff_cell = (in_cloud[i, right, 3] - in_cloud[i, left, 3]) / nan_size
                in_cloud[i, in_cloud.shape[1]-1, 2] = in_cloud[i, left, 2] + (in_cloud.shape[1]-1 - left) * height_diff_cell
                in_cloud[i, in_cloud.shape[1]-1, 3] = in_cloud[i, left, 3] + (in_cloud.shape[1]-1 - left) * range_diff_cell
                if abs(in_cloud.shape[1]-1 - left) < right + 1:
                    in_cloud[i, in_cloud.shape[1]-1, -1] = in_cloud[i, left, -1]
                else:
                    in_cloud[i, in_cloud.shape[1]-1, -1] = in_cloud[i, right, -1]
        return in_cloud
算法結果

下圖結果爲源數據先經過降採樣之後,再進行補全NAN點操作。源數據中的 label 有三個值 [-1, 0, 1], 經過降採樣然後補全操作只剩下 label 爲[0, 1] 的點。

這裏寫圖片描述

以上。


參考文獻:

  1. PCL點雲變換與移除NaN
  2. 速騰聚創自動駕駛激光雷達
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章