PCL1.8.1+Visual Studio 2015+CUDA 9.0 實現PCL-GPU計算

使用PCL處理LIDAR點雲數據時,感覺計算效率不太讓人滿意,在不明就裏的情況下想到用GPU來做點雲分割、聚類的相關計算。所以自行編譯了PCL以支持GPU計算。最後測試的結果不很理想,真實原因還有待探索。

關於GPU計算比CPU還慢

首先說一下GPU計算的結果,我處理的點雲大概也就一萬來個點,用GPU做聚類居然比CPU還要慢,查了一圈也沒真正搞明白背後的原因,看了這個網頁上的討論,貌似這居然是因爲我的點數太少,無法顯示出GPU計算的優越性,因爲時間都浪費在數據向顯存中傳遞的過程了。我只能理解爲這是PCL-GPU相關算法本身的問題,如有大神對此有更深的見解請在評論區批評指正。

其實看到這種結果時,我內心是崩潰的:這麼大費周章地一次重新編譯了PCL,結果居然是這樣的T_T。也許最新版本的PCL和更高版本的CUDA結合會碰撞出不一樣的火花?暫時沒時間搞清楚這件事情了,不妨先記在這裏,日後有緣再做探討吧。看到這裏如果還想在pcl裏支持GPU計算的,希望後面的內容能幫到您。

爲什麼選擇這些版本號

其實關於這點沒什麼好說的。我寫這段就是想提醒大家,能用最新的就用最新的吧,畢竟新版會修正老版中的各種莫名其妙的問題(這個大家後面就可以看到)。
很早以前就安裝過CUDA,我的顯卡是GeForce 940M,貌似最高也就支持到v9.0了,而且我當然不可能卸載掉它再去嘗試更高的版本。Visual Studio也是以前就安裝了的,不想裝最新的2017,2019什麼的,就想着湊合用了。至於PCL1.8.1,一開始下All-in-one installer時看見也就它後面會跟着MSV2015這個後綴。我想這其實是沒什麼關係的,但保險起見也就用它了。

關於GPU的架構(CUDA版本)

對於NVIDA顯卡的架構,我其實是一竅不通的,只是使用CMake時遇到了不少error,貌似根源就在硬件架構(的代別)上,這裏統一羅列一下。取這個小標題只是爲了省事,顯然本文不可能去討論硬件的問題的。
當然用CMake處理PCL源碼時,首先出現的是一些第三方支持的相關目錄找不到的問題,類似這種的:

CMake Error at D:/Program Files/CMake/share/cmake-3.15/Modules/FindPackageHandleStandardArgs.cmake:137 (message):
Could NOT find Eigen (missing: EIGEN_INCLUDE_DIR)

這個還是好辦的,手動把這些目錄找到並添加上就可以了。
第一次Configure不再報錯以後,毫無疑問,要把下面這兩項勾上:
在這裏插入圖片描述
然後這裏就出現了一系列gpu計算的相關模塊,大家根據需要勾選就可以了。
在這裏插入圖片描述
當然這裏就會開始出問題了。如果你不幸地勾選了BUILD_gpu_people這個選項,那麼CMake會報錯,告訴你找不到CUDA_nppi_LIBRARY。好嘛,我們就打開CUDA的鏈接庫目錄看看到底是什麼情況。
在這裏插入圖片描述
這裏面有一堆nppixx.lib,就是沒有nppi.lib。查了一圈,發現居然是因爲這是個歷史遺留問題,老版本的CUDA貌似確實是有nppi.lib的,但在9.0裏(具體從哪個版本開始的沒有調研),這個nppi.lib就被一分爲N,成了上面這些nppixx.lib。然而pcl 1.8.1的CMake File似乎無視了這一點,還要強行找到那個已經不存在的nppi.lib。於是你只能強行讓他別這麼做了。重申一下,這個問題只有在需要用pcl_gpu_people模塊時纔會遇到,不選這個模塊應該會啥事沒有吧。
找到pcl源碼的gpu_people所在的目錄(比如我的目錄是F:\pcl-pcl-1.9.1\gpu\people),打開CMakeList.txt,把

find_cuda_helper_libs(nppi)

改成

find_cuda_helper_libs(nppial)
find_cuda_helper_libs(nppicc)
find_cuda_helper_libs(nppicom)
find_cuda_helper_libs(nppidei)
find_cuda_helper_libs(nppif)
find_cuda_helper_libs(nppig)
find_cuda_helper_libs(nppim)
find_cuda_helper_libs(nppist)
find_cuda_helper_libs(nppisu)
find_cuda_helper_libs(nppitc)

同時把

set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppi_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library")

改成

set(CUDA_npp_LIBRARY "${CUDA_nppc_LIBRARY};${CUDA_nppial_LIBRARY};${CUDA_nppicc_LIBRARY};${CUDA_nppicom_LIBRARY};${CUDA_nppidei_LIBRARY};${CUDA_nppif_LIBRARY};${CUDA_nppig_LIBRARY};${CUDA_nppim_LIBRARY};${CUDA_nppist_LIBRARY};${CUDA_nppisu_LIBRARY};${CUDA_nppitc_LIBRARY};${CUDA_npps_LIBRARY}")

再Configure,就不再報錯了。這也就是我爲什麼勸大家用最新版的PCL,因爲如果你打開1.9.1版本的同樣位置的這個CMakeList.txt,就會發現相應的地方被換成了如下的內容:

if(build)
  REMOVE_VTK_DEFINITIONS()
  #find_package(OpenCV QUIET)
  
  #find NPP
  unset(CUDA_npp_LIBRARY CACHE)
  if(${CUDA_VERSION} VERSION_LESS "5.5")
	find_cuda_helper_libs(npp)
  else()
    find_cuda_helper_libs(nppc)
    find_cuda_helper_libs(npps)
    if(${CUDA_VERSION} VERSION_GREATER_EQUAL "9.0")
      find_cuda_helper_libs(nppim)
      find_cuda_helper_libs(nppidei)
    else()
      find_cuda_helper_libs(nppi)
    endif()

    if(${CUDA_VERSION} VERSION_GREATER_EQUAL "9.0")
      set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppim_LIBRARY} ${CUDA_nppidei_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library")
    else()
      set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppi_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library")
    endif()
  endif()

這果然是CUDA版本(或者應該是PCL版本?)造成的問題。老版PCL的其他源碼、相關文件,誰知道有沒有更多這種陷阱呢?
然後,雖然現在CMake已經不報錯了,但請再注意這個地方:
在這裏插入圖片描述
請一定別忘了把這裏所有和 2 有關的全部幹掉,不然後續編譯代碼的時候還是會報錯,錯誤信息忘記記錄了,隱約記得裏面貌似是有 xx_20 的字樣。有可能(真的只是有可能,我怎麼還記得是sm_20…)是下面這個:

nvcc fatal : Unsupported gpu architecture 'compute_20

查了一下,這大概就是GPU架構造成的問題。實際上我後來只留下了 5.0 這一項(具體數字應該和GPU型號有關),但只要沒有 2,似乎都不是問題,此處暫且不表。

編譯過程中的其他問題

編譯過程及其緩慢,其中我一共遇到了三個問題,其中一個上一部分已經提到了,接下來兩個注意事項請在編譯之前,甚至用CMake處理源碼之前,就先處理一下,免得等了半天發現編譯有誤,那種感覺真的是很崩潰。
首先,打開PCL第三方支持Boost的include目錄(我的情況是D:\PCL1.8.1\3rdParty\Boost\include\boost-1_64\boost\config\compiler)下的nvcc.hpp文件,把下面的幾行註釋掉:

// This is fixed in 7.5. As the following version macro was introduced in 7.5 an existance
// check is enough to detect versions < 7.5
#if !defined(__CUDACC_VER__) || (__CUDACC_VER__ < 70500)
#   define BOOST_NO_CXX11_VARIADIC_TEMPLATES
#endif
// The same bug is back again in 8.0:
#if (__CUDACC_VER__ > 80000) && (__CUDACC_VER__ < 80100)
#   define BOOST_NO_CXX11_VARIADIC_TEMPLATES
#endif

然後還會有一個 thrust::device_reference has no member “x” (“y”;“z”)的錯誤,完整的錯誤信息大概是這個樣子的:

F:/pcl-1.8.1/cuda/sample_consensus/src/sac_model_plane.cu(195): error: class “thrust::device_reference” has no member “x”
detected during:
instantiation of “__nv_bool pcl::cuda::CountPlanarInlier::operator()(const Tuple &) [with Tuple=thrust::detail::tuple_of_iterator_references<thrust::device_reference, thrust::device_reference, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type>]”
…(以下省略一萬字)

關於這個問題,這個網頁做了一些討論。這似乎是個只會在Window平臺(坑爹啊)上出現的bug,顯然,是源碼裏的 sac_model_plane.cu 這個源文件出了點狀況。按照網頁的說法,只要在出問題的行號對應的代碼處使用thrust::raw_reference_cast即可解決問題。但鑑於出問題的地方實在太多,我直接下載了pcl-1.9.1的源碼,並把對應文件夾下的所有源碼文件都替換掉了。這裏例舉一下相關的行,大家可以自行體會出問題的原因。
1.8.1-sac_model_plane.cu (line192-201):

template <typename Tuple> bool
CountPlanarInlier::operator () (const Tuple &t)
{
      if (!isfinite (thrust::get<0>(t).x))
        return (false);

      return (fabs (thrust::get<0>(t).x * coefficients.x +
                    thrust::get<0>(t).y * coefficients.y +
                    thrust::get<0>(t).z * coefficients.z + coefficients.w) < threshold);
}

1.9.1-sac_model_plane.cu (line192-201):

template <typename Tuple> bool
CountPlanarInlier::operator () (const Tuple &t)
{
      if (!isfinite (thrust::raw_reference_cast(thrust::get<0>(t)).x))
        return (false);

      return (fabs (thrust::raw_reference_cast(thrust::get<0>(t)).x * coefficients.x +
                    thrust::raw_reference_cast(thrust::get<0>(t)).y * coefficients.y +
                    thrust::raw_reference_cast(thrust::get<0>(t)).z * coefficients.z + coefficients.w) < threshold);
}

其他的問題我就沒再遇到過了。編譯時成功了,gpu相關的模塊貌似也可以用(當然目前我只測試過聚類相關的)。但計算速度真的是…一言難盡啊T_T。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章