之所以记录下自己编译android版的pcl过程,主要原因是:
故此,通过交叉编译,编译出基于r21版本NDK的1.9.1版本的pcl库,并在windows下使用Android studio成功编译使用pcl库的功能。
操作系统:Ubantu18.04.6 LTS
Python:3.6.9
cmake:3.10.2
conan:1.44.1
GCC:8.4.0
git:2.17.1
如果不想破坏当前的编译环境,可以使用venv创建一个虚拟环境:
mkdir pcl-1.9.1
cd pcl-1.9.1
python -m venv venv
source venv/bin/acticate
当然,也可以不创建虚拟环境,直接在当前环境下进行。虚拟环境的好处就是不会破坏当前的各项配置。
sudo apt install cmake git make ninja python3-pip
sudo pip3 install conan
由于conan默认的仓库https://conan.bintray.com实效,因此,要确保conan使用的版本较新,并导入新的根证书,才可以正常使用conan仓库。
conan config install https://github.com/conan-io/conanclientcert.git
conan remote add conan-center https://center.conan.io
conan remote list #查看设置是否成功
conan-center: https://center.conan.io [Verify SSL: True]#查询到的仓库地址
git clone https://github.com/bashbug/pcl-for-android.git
修改conan-profile下面的文件,把[build-requires]下面的android-toolchain/r20@bashbug/stable修改为android-toolchain/r21@bashbug/stable即可。
打开conanfiles/android-toolchain/conanfile.py,修改version = "r20"为version = “r21”
打开conanfiles/boost/conanfile.py,修改source(self)方法的内容如下:
tools.get("https://boostorg.jfrog.io/artifactory/main/release/{}/source/{}.tar.gz".format(self.version, self.folder_name))
原本的下载地址已经失效,无法下载。原下载地址为
tools.get("https://dl.bintray.com/boostorg/release/{}/source/{}.tar.gz".format(self.version, self.folder_name))
原本的lz4版本是1.9.1,在conan仓库中已经不支持该版本了,因此需要修改为1.9.2版本。需要修改2个文件,一个是lz4的conan文件,一个是flann的文件,因为flann中依赖lz4。
打开conanfiles/flann/conanfile.py,修改requirements部分的lz4版本号,修改后的结果如下:
self.requires("lz4/1.9.2@bashbug/stable")
打开conanfiles/lz4/conanfile.py,修改其中的version值为1.9.2,修改后的结果如下:
version = "1.9.2"
clone出来的pcl配置文件中,所依赖的eigen版本为"eigen/3.3.7@conan/stable",而该版本在conan仓库中不存在;通过查询conan仓库:
conan search eigen* -r=conan-center
Existing package recipes:
eigen/3.3.7
eigen/3.3.8
eigen/3.3.9
eigen/3.4.0
可以看到,仓库中的eigen版本后面没有conan/stable用户通道属性,因此,去掉用户通道参数即可,修改后的结果如下:
self.requires("eigen/3.3.7")
根据需要,修改PCL功能模块开关,默认以下模块被关闭,也就是不会被编译到pcl库中:
cmake.definitions["PCL_SHARED_LIBS"] = "OFF"
cmake.definitions["PCL_BINARIES"] = "OFF"
cmake.definitions["WITH_CUDA"] = "OFF"
cmake.definitions["WITH_OPENGL"] = "OFF"
cmake.definitions["WITH_PCAP"] = "OFF"
cmake.definitions["WITH_PNG"] = "OFF"
cmake.definitions["WITH_QHULL"] = "OFF"
cmake.definitions["WITH_VTK"] = "OFF"
PCL_SHARED_LIBS是控制编译共享库,我们要交叉编译,因此不打开该项;
PCL_BINARIES控制是否编译可执行文件,也就是可以命令行运行的pcl命令。pcl_binaries.patch补丁实现了该配置打开情况下编译的支持。
WITH_VTK模块不适用于Android平台,Android平台使用VES,在PCL的源码CMakeLists.txt中有这样的表述:
if(ANDROID)
set(build FALSE)
message("VTK was found, but cannot be compiled for Android. Please use VES instead.")
endif()
其他模块打开,需要增加对应的配置参数,而且模块间有依赖关系,如VTK依赖OPENG;具体如何配置,还不熟悉,有空再研究。
./pcl-build-for-android.sh armeabi-v7a
./pcl-build-for-android.sh arm64-v8a
如果编译有错,仔细看错误信息,对应修改即可。
编译出来的pcl库和头文件以及相关依赖,按照模块分别存放,主要输出在./conan/data下面,类似以下结构:
├── android-toolchain
├── boost
├── eigen
├── flann
├── lz4
├── pcl
android-toolchain是自动下载的r21版本的NDK,适用于Linux平台,没有包含windows平台的有关文件,因此不能直接拉到windows下使用。
lz4是flann依赖的模块,编译后,已经包含在flann中,因此不需要单独拉取;
boost、eigen、flann、pcl这4个模块是需要拉取出来的,形成一个pcl库和头文件的集合,供其他Android项目使用。要注意:我们所需要拉取的不是./conan/data下的内容全部,而是每个模块对应版本下面的bashbug/stable/××××下面的lib和include2个文件夹,具体各个模块要拉取的目录如下:
.conan/data/boost/1.70.0/bashbug/stable/package/4e65a98fd5c197b6539ce5c3062c84123c059f0c/lib
.conan/data/boost/1.70.0/bashbug/stable/package/4e65a98fd5c197b6539ce5c3062c84123c059f0c/include
.conan/data/eigen/3.3.7/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/eigen3/
.conan/data/flann/1.9.1/bashbug/stable/package/42aed23ccd4c3006a79a880ba523d6d45486ac63/lib
.conan/data/flann/1.9.1/bashbug/stable/package/42aed23ccd4c3006a79a880ba523d6d45486ac63/include
.conan/data/pcl/1.9.1/bashbug/stable/package/edf8fc3f2f92012e3ef85012b21cfa3182a52d47/lib
.conan/data/pcl/1.9.1/bashbug/stable/package/edf8fc3f2f92012e3ef85012b21cfa3182a52d47/include
package后面的文件夹名称时编译时自动生成的,因此,每次编译都不相同。
在windows下创建pcl文件夹,比如:pcl-android-191-build/build,在build下面分别创建boost-android、flann-android、pcl-android,把上述对应的内容拉取到对应模块的文件夹下面;eigen3拉取下来后,修改eigen3为eigen即可;可能需要qhull模块,我是直接复制了原来1.8.0版qhull,目前暂时没有使用到,所以在配置pcl功能模块时没有开打qhull,因此,编译结果也就没有qhull的库和头文件。如果编译android应用需要qhull头文件和库,可以试试直接使用1.8.0版qhull。后面有时间再研究如何编译出qhull。
我拉取后的结构如下:
├── pcl-android-191-build
│ └── build
│ └── boost-android
│ └── include
│ └── lib
│ └── eigen
│ └── Eigen
│ └── unsupported
│ └── flann-android
│ └── include
│ └── lib
│ └── pcl-android
│ └── include
│ └── lib
│ └── qhull-android
│ └── include
│ └── lib
build.gradle中,做对应的配置:
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions -fopenmp -DANDROID_STL=gnustl_static"
}
}
ndk {
abiFilters "armeabi-v7a"
}
编写cpp文件,使用pcl功能。pcl-180库cpp对应的CMakeLists.txt类似如下:
cmake_minimum_required(VERSION 3.4.1)
set(PCL_BUILD "F:/pcl-android-180-build/build")
set(PCL_INCLUDE "${PCL_BUILD}/pcl-android")
set(BOOST_INCLUDE "${PCL_BUILD}/boost-android")
set(FLANN_INCLUDE "${PCL_BUILD}/flann-android")
set(EIGEN_INCLUDE "${PCL_BUILD}/eigen")
set(QHULL_INCLUDE "${PCL_BUILD}/qhull-android")
set(PCL_STATIC_LIB_DIR "${PCL_INCLUDE}/lib")
set(BOOST_STATIC_LIB_DIR "${BOOST_INCLUDE}/lib")
set(FLANN_STATIC_LIB_DIR "${FLANN_INCLUDE}/lib")
set(QHULL_STATIC_LIB_DIR "${QHULL_INCLUDE}/lib")
include_directories(${PCL_INCLUDE}/include)
include_directories(${BOOST_INCLUDE}/include)
include_directories(${FLANN_INCLUDE}/include)
include_directories(${QHULL_INCLUDE}/include)
include_directories(${EIGEN_INCLUDE})
function(importLib dirName libName)
add_library(${libName} STATIC IMPORTED)
set_target_properties(${libName}
PROPERTIES
IMPORTED_LOCATION
"${dirName}/${libName}.a")
endfunction(importLib)
set(libFlannNames
libflann_cpp_s
libflann_s
)
set(libBoostNames
libboost_thread
libboost_signals
libboost_program_options
libboost_filesystem
libboost_system
libboost_regex
libboost_iostreams
libboost_date_time
)
set(libQhullNames
libqhullstatic
)
set(libPclNames
libpcl_stereo
libpcl_2d
libpcl_segmentation
libpcl_recognition
libpcl_ml
libpcl_io
libpcl_io_ply
libpcl_filters
libpcl_tracking
libpcl_search
libpcl_registration
libpcl_keypoints
libpcl_features
libpcl_surface
libpcl_sample_consensus
libpcl_octree
libpcl_kdtree
libpcl_common
)
foreach(lib ${libPclNames})
importLib(${PCL_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libFlannNames})
importLib(${FLANN_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libBoostNames})
importLib(${BOOST_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libQhullNames})
importLib(${QHULL_STATIC_LIB_DIR} ${lib})
endforeach(lib)
add_library(native-jni SHARED
native-jni.cpp
)
target_link_libraries(
native-jni
${libPclNames}
${libFlannNames}
${libBoostNames}
${libQhullNames}
android
log)
native-jni.cpp为使用PCL功能库JNI本地化文件,可以根据自己的实际情况修改为自己的实现文件名。
191版本默认编译出来的pcl库中是没有libpcl_2d库的,可以复制180版本的库文件,也可以修改上面的txt文件,去掉libpcl_2d即可;同样,该本本没有编译出libboost_signals库,去掉即可,也可以复制180版本的同名库文件即可;flann模块也也没有编译出libflann_s.a文件,同样可以去掉,也可以复制180版本的文件过来。
去掉上面3个库文件后,191版的PCL库对应CMakeLists.txt类似如下:
cmake_minimum_required(VERSION 3.4.1)
set(PCL_BUILD "F:/pcl-android-180-build/build")
set(PCL_INCLUDE "${PCL_BUILD}/pcl-android")
set(BOOST_INCLUDE "${PCL_BUILD}/boost-android")
set(FLANN_INCLUDE "${PCL_BUILD}/flann-android")
set(EIGEN_INCLUDE "${PCL_BUILD}/eigen")
set(QHULL_INCLUDE "${PCL_BUILD}/qhull-android")
set(PCL_STATIC_LIB_DIR "${PCL_INCLUDE}/lib")
set(BOOST_STATIC_LIB_DIR "${BOOST_INCLUDE}/lib")
set(FLANN_STATIC_LIB_DIR "${FLANN_INCLUDE}/lib")
set(QHULL_STATIC_LIB_DIR "${QHULL_INCLUDE}/lib")
include_directories(${PCL_INCLUDE}/include)
include_directories(${BOOST_INCLUDE}/include)
include_directories(${FLANN_INCLUDE}/include)
include_directories(${QHULL_INCLUDE}/include)
include_directories(${EIGEN_INCLUDE})
function(importLib dirName libName)
add_library(${libName} STATIC IMPORTED)
set_target_properties(${libName}
PROPERTIES
IMPORTED_LOCATION
"${dirName}/${libName}.a")
endfunction(importLib)
set(libFlannNames
libflann_cpp_s
)
set(libBoostNames
libboost_thread
libboost_program_options
libboost_filesystem
libboost_system
libboost_regex
libboost_iostreams
libboost_date_time
)
set(libQhullNames
libqhullstatic
)
set(libPclNames
libpcl_stereo
libpcl_segmentation
libpcl_recognition
libpcl_ml
libpcl_io
libpcl_io_ply
libpcl_filters
libpcl_tracking
libpcl_search
libpcl_registration
libpcl_keypoints
libpcl_features
libpcl_surface
libpcl_sample_consensus
libpcl_octree
libpcl_kdtree
libpcl_common
)
foreach(lib ${libPclNames})
importLib(${PCL_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libFlannNames})
importLib(${FLANN_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libBoostNames})
importLib(${BOOST_STATIC_LIB_DIR} ${lib})
endforeach(lib)
foreach(lib ${libQhullNames})
importLib(${QHULL_STATIC_LIB_DIR} ${lib})
endforeach(lib)
add_library(native-jni SHARED
native-jni.cpp
)
target_link_libraries(
native-jni
${libPclNames}
${libFlannNames}
${libBoostNames}
${libQhullNames}
android
log)
Android studio会自动识别CMakeList.txt。如果项目中原本有CMakeList.txt,可以把上面的CMakeList.txt当作已有项目的子模块,在项目根目录下的CMakeList.txt中通过以下语句添加子模块即可:
add_subdirectory(src/main/cpp/xxx/)
xxx为存放cpp文件和对应CMakeList.txt文件的文件夹。