Udacity机器人软件工程师课程笔记(二十) - 感知 - 校准,过滤, 分段, RANSAC
校準,過濾, 分段, RANSAC
首先,我們將討論傳感器校準,也就是說,從幾何形狀,失真和噪聲方面校準相機如何看待周圍的世界。了解相機的校準對于了解測量數據如何代表實際物理環境至關重要。
之后,我們將討論過濾數據。
對于3D點云,大多數數據實際上對應于不需要的背景對象或其他必須過濾掉的對象,然后才能執行對象檢測或識別。
最后,我們將介紹分段,這是將經過過濾的點云數據分解為有意義的片段的過程,每個片段都對應于數據中的一個對象或一組對象。
我們將使用“隨機樣本共識”或RANSAC算法,這是在存在噪聲或異常高級數據的情況下執行細分的強大方法。
1.校準
要校準相機,我們需要測量世界上的3D點如何向下映射到相機的2D圖像平面上。實際上,這是一個相當復雜的過程,但是在接下來的這些練習中,我們將使用一些的OpenCV工具使其變得簡單。
(1) 校準圖案
第一步是選擇校準圖案。我們使用以下棋盤樣式:
棋盤并不是測試圖案的唯一選擇,它可以是由其他一些簡單形狀(例如圓形)組成的圖案。無論使用哪種形狀,測試圖案的目的都是提供一種已知的幾何形狀,您可以使用相機拍攝該圖像,并在將3D世界點映射到2D圖像點時用作參考。
(2) 拍攝測試圖案
下一步是打印出測試圖案并為其拍照!您可以固定照相機的位置并在場景中四處移動測試圖案,也可以固定照相機的位置并在場景中四處移動相機以從不同角度和距離拍攝照片。
在這里,測試圖案每個正方形為10cm x 10cm,并將其粘貼到墻上,從各個方面拍攝了如下照片:
(3) 尋找內角
一旦獲得測試圖案的圖像,首先需要在每個圖像中找到測試圖案。
使用OpenCV功能findChessboardCorners()和drawChessboardCorners()在棋盤圖案的圖像上自動找到并繪制“內角”。
要了解有關這兩個功能的更多信息,可以在此處查看OpenCV文檔:cv2.findChessboardCorners()和cv2.drawChessboardCorners()。
將這兩個功能應用于樣本圖像,將獲得如下結果:
計算任何給定行中的內角數,然后在變量中輸入該值nx。同樣,計算給定列中的角數并將其存儲在中ny。請記住,內角僅是兩個黑色和兩個白色正方形相交的點,換句話說,僅計算內角,而不計算外角。
(4) 程序示例
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import glob# #準備對象點
nx = 8 # TODO: 在x中輸入內邊角的數量
ny = 6 # TODO: 在y中輸入內邊角的數量img = cv2.imread('chessboardpattern.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 轉換為灰度
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
cv2.imshow('iamge', img)
# 找到棋盤的角
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)# 如果找到,畫角
if ret == True:# 繪制和顯示角cv2.drawChessboardCorners(img, (nx, ny), corners, ret)# 使用cv2進行輸出cv2.imshow('coners', img)# 使用plt進行輸出plt.imshow(img)plt.show()cv2.waitKey()
輸出如下:
在示例程序中,我們使用了Opencv進行校準,所用到的函數為:cv2.findChessboardCorners,這個函數可以用來尋找棋盤圖的內角點位置。通常作為我們使用Opencv校準的第一步。
然后我們可以使用cv2.calibrateCamera()計算校準矩陣和失真系數。然后使用cv2.undistort()函數使測試圖像不失真。
關于cv2.calibrateCamera()的使用方法:
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, (img.shape[1], img.shape[0]), None, None)
我們需要關注的輸出為mtx和dist,分別包含攝像機本征矩陣和失真系數。
固有攝像機矩陣的形式為:
camaeramatrix=[fx0cx0fycy001]camaera \ matrix = \begin{bmatrix}f_x &0& c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1\end{bmatrix}camaera?matrix=???fx?00?0fy?0?cx?cy?1????
上面矩陣中fxf_xfx?和fyf_yfy?焦距(當像素為正方形時它們相等),而 cxc_xcx? 和 cyc_ycy?
對應于相機中的(光學)中心 (x,y)(x ,y )(x,y) 平面。
共有五個失真系數,dist中按以下順序給出:
DistortionCoefficients=(k1,k2,p1,p2,k3)Distortion \ Coefficients = (k_1, k_2, p_1, p_2, k_3)Distortion?Coefficients=(k1?,k2?,p1?,p2?,k3?)
其中,k是徑向失真系數,p是切向失真系數。要校正圖像的徑向和切向畸變,將使用cv2.undistort()函數應用以下方程式:
使用cv2.undistort()函數來使圖像不失真:
undist = cv2.undistort(img, mtx, dist, None, mtx)
import numpy as np
import cv2
import glob
import matplotlib.pyplot as plt
import matplotlib.image as mpimg# 規定內角數量
nx = 8
ny = 6
# 準備目標點, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((ny * nx, 3), np.float32)
# 關于np.mgrid的用法:https://www.cnblogs.com/wanghui-garcia/p/10763103.html
# .T是矩陣的轉置
# reshape是轉換成兩列
objp[:, :2] = np.mgrid[0:nx, 0:ny].T.reshape(-1, 2)# 用于存儲來自所有圖像的對象點和圖像點的數組。
objpoints = [] # 在真實空間中的3D點
imgpoints = [] # 在圖像平面的2D點'''
# 制作校準圖像列表
images = glob.glob('./ Cal * .jpg')
# 單步通過名單和搜索棋盤內角
for idx, fname in enumerate(images):img = mpimg.imread(fname)gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)# 找到棋盤的內角ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)# 如果找到,添加目標點,圖像點if ret == True:objpoints.append(objp)imgpoints.append(corners)
'''
# 代替上面的校準圖像列表
img = cv2.imread('test_image.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)# 找到棋盤的內角
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)# 如果找到,添加目標點,圖像點
if ret == True:objpoints.append(objp)imgpoints.append(corners)img = cv2.imread('test_image.png')
img_size = (img.shape[1], img.shape[0])# 攝像機標定給定的目標點和圖像點
# calivrateCamera函數參考博客:https://blog.csdn.net/u011574296/article/details/73823569
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None, None)# 執行undistortion函數使測試圖像不失真
undist = cv2.undistort(img, mtx, dist, None, mtx)# 展示 undistortion
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 9))
f.tight_layout()
ax1.imshow(img)
ax1.set_title('Original Image', fontsize=50)
ax2.imshow(undist)
ax2.set_title('Undistorted Image', fontsize=50)
plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
plt.show()
輸出結果如下:
部分程序注釋
images = glob.glob('./ Cal * .jpg')
glob.glob : 返回所有匹配的文件路徑列表。它只有一個參數pathname,定義了文件路徑匹配規則,這里可以是絕對路徑,也可以是相對路徑。注釋掉的部分是因為我只有一張圖片,所以我選擇了按照上一個程序的的讀入方法。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None, None)
mtx :內參數矩陣。
dist :畸變矩陣。
rvecs :旋轉向量。
tvecs :位移向量。
objectPoints :世界坐標系中的點。
imagePoints :其對應的圖像點。
imageSize :圖像的大小,在計算相機的內參數和畸變矩陣需要用到;
(5) 外在校準
2D像素坐標中的點的位置與3D世界坐標中的對應點的位置之間的關系由針孔相機模型定義:
zc[uv1]=K[RT][xwywzw1]z_c\begin{bmatrix}u\\v\\1\end{bmatrix} = K\begin{bmatrix}R& T\end{bmatrix}\begin{bmatrix}x_w\\y_w\\z_w\\1\end{bmatrix}zc????uv1????=K[R?T?]?????xw?yw?zw?1??????
其中KKK代表在上一節中得出的本征矩陣。
RRR和TTT是外部參數,表示從3D世界坐標到3D相機坐標的坐標系轉換。等效地,外部參數定義攝像機中心的位置和攝像機在世界坐標中的航向。T是以相機為中心的坐標系的坐標表示的世界坐標系原點的位置。而攝像機的位置C=?R′TC = -R'TC=?R′T。
由于外部參數確定世界坐標中各個攝像機的3D姿態,因此它們也可以用于圖像配準過程。
圖像配準是指將一個攝像機的數據幀轉換為另一個像素的數據幀匹配。
這對于創建精確的點云絕對必不可少。
(6) ROS中的RGBD校準
我們將使用ROS的image_pipeline來校準RGB-D相機,該相機包含一套套件,可提供多種圖像處理工具。這些工具之一是camera_calibration的軟件包,其可輕松校準單眼,立體相機和RGB-D相機。
該camera_calibration軟件包使用棋盤格作為校準的目標對象,其工作原理與之前所講過的相同。
了解有關ROS提供的用于校準的工具的更多信息。
校準過程將可以將RGB相機數據與隨附的深度相機數據結合起來以生成3D點云。目前,我們將繼續直接處理點云數據,但是在項目中,我們從原始RGB-D數據開始,并通過相機校準過程生成自己的點云。
2.過濾和細分
(1)點云過濾
以下是點云庫中一些最常用的過濾器。
- VoxelGrid下采樣濾波器
- ExtractIndices篩選器
- 直通過濾器
- RANSAC Plane Fitting
- 離群值去除過濾器
盡管其中一些是使用底層算法來提高點云數據質量的實際過濾器,但有些是在感知管道中某些階段所需的簡單工具,以幫助提取輸入點云的子集。
(2)桌面細分練習
我們將使用點云庫來進行這個練習。
- Step-1. 在提供的課程虛擬機中下載或復制此存儲庫,并遵循安裝說明。
- Step-2. 在接下來的部分中,嘗試使用各種過濾器。
- Step-3. 在場景中進行RANSAC平面擬合對表格進行分段。
- Step-4. 分別保存兩個只包含表和對象的新點云文件。
在存儲庫中,以單個文件的形式提供了模擬桌面場景的點云數據:tabletop.pcd。最后的目標是創建兩個單獨的點云文件:
- Table.pcd - 只包含屬于桌子的點
- Objects.pcd - 包含所有桌上的對象
要完成這些任務,需要修改RANSAC.py腳本中的代碼,該腳本在Exercise-1文件夾的存儲庫中提供。該腳本如下所示:
# Import PCL module
import pcl# Load point cloud file
cloud = pcl.load_XYZRGB('tabletop.pcd')# Voxel Grid filter
# Implement Voxel Grid Downsampling here# Pass Through filter
# Implement a Pass Through Filter here# RANSAC plane segmentation
# Implement plane segmentation here# Extract inliers
# Implement inlier extraction here# Save output point cloud data
# example: filename = 'table.pcd' for table
# pcl.save(cloud, filename)# Extract outliers# Save pcd for tabletop objects
我們將在下文對腳本內容進行填充。由于虛擬機的python不支持中文,代碼注釋將使用英文注釋。
pcl_helper.py 文件[鏈接]
pcl_helper.py包含用于使用RSO和PCL處理點云數據的功能。其相關的github鏈接見上。下面給出其相關功能的翻譯。
random_color_gen()
生成rgb值的隨機集合
Return: a 3-tuple with r,g,b values (range 0-255)
ros_to_pcl(sensor_msgs/PointCloud2)
Converts sensor_msgs/PointCloud2 to XYZRGB Point Cloud
Return: pcl.PointCloud_PointXYZRGB
pcl_to_ros(pcl.PointCloud_PointXYZRGB0
將sensor_msgs/PointCloud2轉換為XYZRGB點云
Return: sensor_msgs/PointCloud2
XYZRGB_to_XYZ(XYZRGB_cloud)
將XYZRGB點云轉換為XYZ點云
Return: pcl.PointCloud
XYZ_to_XYZRGB(XYZ_cloud, color)
將一個3元組作為顏色并將其添加到XYZ點云
Return: pcl.PointCloud_PointXYZRGB
rgb_to_float(color)
將3元組顏色轉換為單個浮點數32
Return: rgb packed as a single float32
get_color_list(cluster_count)
創建一個3元組(rgb)列表,該列表的長度為cluster_count
Return: get_color_list.color_list
(3)體素網格降采樣
RGB-D攝像機提供了豐富而特別密集的點云,這意味著單位體積內的點云比例如激光雷達點云要多。在全分辨率點云上運行計算可能很慢,而且可能不會對使用更稀疏采樣點云獲得的結果產生任何改進。
因此,在許多情況下,向下采樣數據是有利的。我們將使用一個VoxelGrid下采樣過濾器來派生一個點云,該點云具有較少的點,但是仍然可以很好地表示輸入點云的整體。
“像素”是“圖片元素”的縮寫。同樣,“體素”是“體積元素”的縮寫。正如我們可以將2D圖像分割為常規的區域元素網格(如上圖左所示),我們也可以將3D點云分割為常規的三維體元素網格(如上圖右所示)。網格中的每個單獨的細胞現在都是一個體素,而3D網格被稱為“體素網格”。
PCL提供了一個方便的函數來執行VoxelGrid下采樣。
將代碼更改到、到RANSAC.py的立體網格下采樣部分。
import pcl# 加載點云文件
cloud = pcl.load_XYZRGB('tabletop.pcd')# 為我們的輸入點云創建一個VoxelGrid過濾器對象
vox = cloud.make_voxel_grid_filter()# 選擇一個體素大小
LEAF_SIZE = 0.01# 設置體素大小
vox.set_leaf_size(LEAF_SIZE, LEAF_SIZE, LEAF_SIZE)# 調用過濾函數以獲得最終的下采樣點云
cloud_filtered = vox.filter()
filename = 'voxel_downsampled.pcd'
pcl.save(cloud_filtered, filename)
然后使用python運行文件,使用pcl_viewer
python RANSAC.py
pcl_viewer voxel_downsampled.pcd
注:
1)按下’r’鍵使整個點云居中
2)不要復制中文
體素大小的單位為米。將其設置為1意味著體素的體積為1立方米,這可能不會產生最佳效果,因為它可能會忽略重要的特征。因此,在實踐中,我們應該以較小的體素尺寸開始,然后再增大為較大的尺寸,直到達到一個最合適的效果??梢酝ㄟ^具有一些有關場景的先驗信息(如最小對象的大小,目標對象的大小或場景的總視野)來獲得對體素大小的良好估計。
(4)通過過濾
如果我們知道一些有關目標在場景中位置的某些先驗信息,則可以應用“通過過濾器”從點云中刪除無用的數據。
直通濾鏡的工作方式與裁剪工具非常相似,它可以通過指定沿該軸具有截止值的軸來裁剪任何給定的3D點云。允許通過的區域通常稱為關注區域。
例如,在我們的桌面場景中,我們知道桌子大約在機器人視野的中心。因此,通過使用“通過過濾器”,我們可以選擇一個感興趣的區域來刪除一些多余的數據。
要完成此任務,請RANSAC.py在“通過過濾器”部分的文件中添加以下代碼,以從Voxel下采樣過濾后的點云中選擇感興趣的區域。
為了方便編輯,可以使用以下命令:
sudo chmod 777 RANSAC.py
然后我們就可以使用編輯器來直接編寫程序,而不用nano。
# Import PCL module
import pcl# Load Point Cloud file
cloud = pcl.load_XYZRGB('tabletop.pcd')# Voxel Grid filter
# Create a VoxelGrid filter object for our input point cloud
vox = cloud.make_voxel_grid_filter()# Experiment and find the appropriate size!
LEAF_SIZE = 0.01 # Set the voxel (or leaf) size
vox.set_leaf_size(LEAF_SIZE, LEAF_SIZE, LEAF_SIZE)
cloud_filtered = vox.filter()# PassThrough filter
# Create a PassThrough filter object.
passthrough = cloud_filtered.make_passthrough_filter()# Assign axis and range to the passthrough filter object.
filter_axis = 'z'
passthrough.set_filter_field_name(filter_axis)
axis_min = 0.78
axis_max = 1.2
passthrough.set_filter_limits(axis_min, axis_max)# Finally use the filter function to obtain the resultant point cloud.
cloud_filtered = passthrough.filter()# RANSAC plane segmentation# Extract inliers# Save pcd for table
# pcl.save(cloud, filename)# Extract outliers# Save pcd for tabletop objects
filename = 'voxel_downsampled.pcd'
pcl.save(cloud_filtered, filename)
其輸出結果如下
3.RANSAC
(1)RANSAC概述
在我們的項目中,結合了有關場景的一些先驗知識和一些點云過濾器,我們可以將點云縮小為僅包含表和表頂部的對象。
接下來,在感知中,需要將桌子本身從場景中刪除。為此,我們使用一種稱為隨機樣本共識或“ RANSAC” 的流行技術。RANSAC是一種算法,可用于識別數據集中屬于特定模型的點。對于正在使用的3D場景,我們選擇的模型可以是平面,圓柱體,盒子或任何其他常用形狀。
RANSAC算法假定數據集中的所有數據均由離群值和離群值組成,其中離群值可以由具有特定參數集的特定模型定義,而離群值不適合該模型,因此可以丟棄。像下面的示例一樣,我們可以提取出不太適合模型的輪廓繪制器。
如果對給定數據集中存在的某個形狀有一定的先驗知識,可以使用RANSAC通過假設一個特定的模型來估計點云集的哪些部分屬于該形狀。
將桌子建模為平面,將其從點云中移除,得到如下結果:
另一方面,RANSAC的缺點是沒有計算模型參數所需時間的上限。通過選擇固定數量的迭代,這在一定程度上得到了緩解,但是這也有它自己的缺點。如果選擇較低的迭代次數,得到的解決方案可能不是最優的。通過這種方式,RANSAC在計算時間和模型檢測精度之間進行了權衡。由于場景中桌面是最突出的平面,地面移除后,可以有效地利用RANSAC來識別屬于桌面和discar的點。
(2)RANSAC平面擬合
RANSAC算法主要涉及對給定的數據集執行兩個迭代重復的步驟:假設和驗證。首先,通過隨機選擇n個點的最小子集并估計相應的形狀模型參數來生成期望模型的假設形狀。
最小子集包含唯一估計模型所需的最小點數。例如,確定一條直線需要2個點,而確定一個平面需要3個非共線點,如下所述。
使p1p_1p1?,p2p_2p2?, 和p3p_3p3?為隨機選取的三個非共線點,使得:
p1=(x1,y1,z1)p_1=(x_1, y_1, z_1)p1?=(x1?,y1?,z1?)
p2=(x2,y2,z2)p_2=(x_2, y_2, z_2)p2?=(x2?,y2?,z2?)
p3=(x3,y3,z3)p_3=(x_3, y_3, z_3)p3?=(x3?,y3?,z3?)
平面可以用如下的方程來描述:
ax+by+cz+d=0a x + b y + c z + d = 0ax+by+cz+d=0
系數aaa,bbb,ccc,ddd可以通過求解以下方程組得到:
ax1+by1+cz1+d=0ax_1 + by_1 + cz_1 + d = 0ax1?+by1?+cz1?+d=0
ax2+by2+cz2+d=0ax_2 + by_2 + cz_2 + d = 0ax2?+by2?+cz2?+d=0
ax3+by3+cz3+d=0ax_3 + by_3 + cz_3 + d = 0ax3?+by3?+cz3?+d=0
這個系統可以用Cramer’s rule和一些基本的矩陣運算來解決,具體如下:
D=[x1y1z1x2y2z2x3y3z3]D = \begin{bmatrix}x_1&y_1&z_1\\x_2&y_2&z_2\\x_3&y_3&z_3\end{bmatrix}D=???x1?x2?x3??y1?y2?y3??z1?z2?z3?????
如果D是非零的(對于沒有經過原點的平面),aaa, bbb, ccc的值可以計算如下:
a=?dD[1x1z11y2z21y3z3]a = \frac{-d}{D} \begin{bmatrix}1&x_1&z_1\\1&y_2&z_2\\1&y_3&z_3\end{bmatrix}a=D?d????111?x1?y2?y3??z1?z2?z3?????
b=?dD[x11z1x21z2x31z3]b = \frac{-d}{D} \begin{bmatrix}x_1&1&z_1\\x_2&1&z_2\\x_3&1&z_3\end{bmatrix}b=D?d????x1?x2?x3??111?z1?z2?z3?????
c=?dD[x1x11x2y21x3y31]c = \frac{-d}{D} \begin{bmatrix}x_1&x_1&1\\x_2&y_2&1\\x_3&y_3&1\end{bmatrix}c=D?d????x1?x2?x3??x1?y2?y3??111????
一旦建立了模型,就針對所得的候選形狀測試點云中的其余點,以確定該模型很好地近似了多少個點。
經過一定數量的迭代后,將提取出擁有最大百分比的線性像素的形狀,并且算法將繼續處理剩余數據。
將RANSAC與PCL結合使用:
RANSAC平面擬合算法已存在于PCL庫中。要在本練習的代碼中實現RANSAC平面擬合,請將以下代碼添加到文件的RANSAC平面分段部分RANSAC.py:
# Create the segmentation object
seg = cloud_filtered.make_segmenter()# Set the model you wish to fit
seg.set_model_type(pcl.SACMODEL_PLANE)
seg.set_method_type(pcl.SAC_RANSAC)# Max distance for a point to be considered fitting the model
# Experiment with different values for max_distance
# for segmenting the table
max_distance = 1
seg.set_distance_threshold(max_distance)# Call the segment function to obtain set of inlier indices and model coefficients
inliers, coefficients = seg.segment()
(3)提取指標
使用RANSAC,我們可以確定點云中的哪些索引與桌子相對應。如果正確應用了“通過過濾器”,那么與表不對應的索引就是代表桌子上的對象的索引。
顧名思義,ExtractIndices篩選器通過提供索引列表從點云中提取點。使用剛剛執行的RANSAC擬合,輸出將inliers對應于max_distance最佳擬合模型中的點云索引。
盡管此過濾器不執行任何高級過濾操作,但它經常與其他技術一起用于從輸入點云中獲取點的子集。大多數對象識別算法會返回一組與形成已識別目標對象的點相關的索引。
因此,使用ExtractIndices過濾器提取與已識別對象關聯的點云非常方便。為此,將以下代碼添加到RANSAC.py腳本的“提取內部”部分:
# Extract inliers
extracted_inliers = cloud_filtered.extract(inliers, negative=False)
filename = 'extracted_inliers.pcd'
pcl.save(extracted_inliers, filename)
對象提取
實際上,我們只是想去掉桌子并專注于桌子上的對象。
只需將extract方法上的negativ=標志更改為True,就可以輕松使用已經完成的操作從點云中提取所有感興趣的對象。將以下代碼添加到RANSAC.py腳本的“提取異常值”部分,以檢索所有不符合RANSAC模型的點的子集:
# Extract outliers
extracted_outliers = cloud_filtered.extract(inliers, negative=True)
filename = 'extracted_outliers.pcd'
pcl.save(extracted_outliers, filename)
總程序如下:
# Import PCL module
import pcl# Load Point Cloud file
cloud = pcl.load_XYZRGB('tabletop.pcd')# Voxel Grid filter
# Create a VoxelGrid filter object for our input point cloud
vox = cloud.make_voxel_grid_filter()# Experiment and find the appropriate size!
LEAF_SIZE = 0.01 # Set the voxel (or leaf) size
vox.set_leaf_size(LEAF_SIZE, LEAF_SIZE, LEAF_SIZE)
cloud_filtered = vox.filter()# PassThrough filter
# Create a PassThrough filter object.
passthrough = cloud_filtered.make_passthrough_filter()# Assign axis and range to the passthrough filter object.
filter_axis = 'z'
passthrough.set_filter_field_name(filter_axis)
axis_min = 0.75
axis_max = 1.2
passthrough.set_filter_limits(axis_min, axis_max)# Finally use the filter function to obtain the resultant point cloud.
cloud_filtered = passthrough.filter()# RANSAC plane segmentation# Create the segmentation object
seg = cloud_filtered.make_segmenter()# Set the model you wish to fit
seg.set_model_type(pcl.SACMODEL_PLANE)
seg.set_method_type(pcl.SAC_RANSAC)# Max distance for a point to be considered fitting the model
# Experiment with different values for max_distance
# for segmenting the table
max_distance = 0.01
seg.set_distance_threshold(max_distance)# Call the segment function to obtain set of inlier indices and model coefficients
inliers, coefficients = seg.segment()
# Extract inliers
extracted_inliers = cloud_filtered.extract(inliers, negative=False)
filename = 'extracted_inliers.pcd'
pcl.save(extracted_inliers, filename)
# Save pcd for table
# pcl.save(cloud, filename)
filename = 'voxel_downsampled.pcd'
pcl.save(cloud_filtered, filename)# Extract outliers
try:extracted_outliers = cloud_filtered.extract(inliers, negative=True)
except RuntimeError:print("runtimeError!Plese check max_distance in your code.")
else:filename = 'extracted_outliers.pcd'pcl.save(extracted_outliers, filename)
# Save pcd for tabletop objects
其中extracted_outliers.pcd的輸出如下:
extracted_inliers.pcd的輸出如下:
(4)離群值去除過濾器
雖然校準可以解決失真問題,但由于外部因素(例如環境中的灰塵,空氣中的濕度或各種光源的存在)導致的噪聲會導致稀疏的異常值,從而進一步破壞結果。
這些離群值會導致點云特征(例如曲率,梯度等)的估計復雜化,從而導致錯誤的值,進而可能導致我們感知流程中各個階段的失敗。
用于刪除此類離群值的一種過濾技術是在每個點附近執行統計分析,并刪除不符合特定條件的那些點。PCL的Statistics Outlier Removal過濾器就是其中一種過濾技術的示例。對于點云中的每個點,它計算到其所有鄰居的距離,然后計算平均距離。
通過假設為高斯分布,所有平均距離均在由全局距離均值+標準差定義的區間之外的點都被視為離群值,并從點云中刪除。
下圖顯示了將Statistics Outlier Removal過濾器應用于噪聲點云數據的結果:
可以這樣實現:
# Much like the previous filters, we start by creating a filter object:
outlier_filter = cloud_filtered.make_statistical_outlier_filter()# Set the number of neighboring points to analyze for any given point
outlier_filter.set_mean_k(50)# Set threshold scale factor
x = 1.0# Any point with a mean distance larger than global (mean distance+x*std_dev) will be considered outlier
outlier_filter.set_std_dev_mul_thresh(x)# Finally call the filter function for magic
cloud_filtered = outlier_filter.filter()
可以在此處找到統計異常值過濾器的可測試代碼段,并在此處找到具有異常值的表點云的鏈接。
總結
以上是生活随笔為你收集整理的Udacity机器人软件工程师课程笔记(二十) - 感知 - 校准,过滤, 分段, RANSAC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Udacity机器人软件工程师课程笔记(
- 下一篇: Udacity机器人软件工程师课程笔记(