参考资料:ORB_SLAM2在Android上的移植
ORB_SLAM2_Android—github
OpenCV4Android
本来想直接下载github上的工程import,但是不知道为什么直接导入后eclipse直接卡死,不管怎么配置SDK、NDK、ADT各种都没用,所以只能自己移植,折腾了好久还顺便学了下Android,终于搞定了。
关于Android的移植网上的资料非常多也非常杂。虽然很多博客写的很好,但是还是推荐直接看官方文档。
Android环境配置
首先是JDK的配置,很简单百度一下,最后做个测试:
在命令行输入Java -version,如果出现以下类似内容则配置成功:
然后下载完Eclipse后,Java的环境配置好了,但是Android的开发,还需要以后步骤:
1.ADT,有在线安装和离线包两种方式,在线方法为:
在Eclipse-> “Help” -> “Install New Software” ->Add ,出现下图所示后Name随便取,Location输入http://dl-ssl.google.com/android/eclipse 确认后按提示一步一步next就OK了。
离线方式前面也一样,只是不是直接填Location,而是先下载离线包,然后点击Archive选择压缩包
2.Android SDK,建议从官网下载(需要科学上网),SDK官网下载 网上下载说不定就会掉坑,不要问我为什么知道。。下载并解压后(路径不要有中午),在Eclipse中选择Window–>preferences ->Android 在Android Location里填入解压的根目录,则Android环境配置基本完成。如下图所示:
这时候你的菜单栏应该有了这两项 :
分别是SDK和AVD(模拟器)
PS:如果配置完后运气不好没有出现也没事(没错就是我),先在Eclipse点Window,如果有下图这样的SDK和AVD,那么就证明并不是配置失败了,而是设置问题。
设置方法:Windows–>Customize Perspective,选择Command Groups Availability选项,确保Android SDK and AVD Manager是勾选上的,然后选择Tool Bar Visibility选项—>勾选Android SDK and AVD Manager,这两个顺序不能反,OK后好了~
3.更新SDK,在更新之前,建议先科学上网,这个需要连接外网。当然网上有更改host文件的方法,可以自行搜索。点刚刚那个左边的图标,界面如下:
其中有许多文件,但是不需要全下!不需要全下!不需要全下!
Tools全下,然后每个版本的SDK Platform、Sources for Android SDK全下,Googel APIs在天朝一般用不到可以不下,主要要提System Image,镜像是模拟器,只用下自己需要模拟器的版本就可以了,都下的话超大超久,血泪教训,Extras建议下载。
具体内容可以参考知乎:Android SDk里面到底哪些东西是必须下载的
———至此Android开发环境就配置好了—-
4.NDK配置。下载官网最新版本的NDK NDK官网下载(需要科学上网)。网上直接下说不定就有坑。。而且这个坑还根本看不出来,还是官网安全。而且低版本r7以下还需要cygwin,7以上的新版就不用了。
先在环境变量里配置NDK的根目录(这一步在命令行操作的时候挺重要的),解压后(规则同sdk),打开Eclipse,点击Window–>Preferences,选择Android–>NDK,在右边界面中填入NDK Location即可。
先测试一下NDK是否配置好(翻译官网提供的教程):
打开Eclipse,File->import->Android->Existing Android Code Into Workspace(这里不要直接在General里选择Existing Projects Into Workspace,是找不到例程的,我刚开始这样还以为是NDK配置问题,还蠢蠢的重新弄,走了好多弯路)
->Browse->/samples/,加入HelloJni工程,(最新版的NDK不知道为什么不提供例程了,可以下r10b版本然后导入)。
导入后,右键select Android Tools > Add Native Support,Accept the default library name (“hello-jni”), and click Finish.
这时候菜单栏应该会出现一个小锤子图标。点击Build,在模拟器或者真机运行后如下图所示则配置成功:
Android移植基础
NDK是集成的Android中调用C++代码的工具包,核心是JNI(Java Native Interface)技术,具体这里略过不表。只说说NDK开发的基本步骤:
编写Java代码:在Java中定义一个类,比如说叫NDKHelper吧,里面定义几个java的方法,只需要声明,不需要实现,如下所示:
public class NDKHelper {
//NDK示例方法1
public static native void ndkOne(int a,long b);
//NDK示例方法2
public static native int ndkTwo(String a,String b);
}
native标识符表示该函数将会利用C++代码完成实现。
接下来在工程上右键,Android Tools–>Add native support
名字就是最后我们要生成的库的名字,随便填,可修改。点击确定就会给你的工程添加C++编译支持,菜单栏会多了个小锤子,这个是用来编译C++的快捷键。在你的工程目录下会新建jni目录和obj目录,其中jni目录用来存放和C++代码有关的东西,obj则存放C++进行编译时产生的中间件,最后生成的library会写入到libs文件夹下。
在jni文件夹中生成了如下文件,一个.cpp,一个Android.mk,其中.cpp是自动生成的,是用来编写C++部分的,而Android.mk类似C++里面的CMakeList,用来指定需要编译的文件和编译生成的模块名,一个最简单的Android.mk文件如下所示:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NDKTest
LOCAL_SRC_FILES := NDKTest.cpp
include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH :=
(callmy−dir)表示包含当前目录。include
(CLEAR_VARS)表示清除全部非系统变量和部分系统变量;
LOCAL_MODULE := NDKTest 表示当前生成的模块名,最终会生成libNDKTest.so文件
LOCAL_SRC_FILES := NDKTest.cpp 表示当前需要编译的cpp文件;
include $(BUILD_SHARED_LIBRARY) 表示生成共享库,需要生成静态库请修改成BUILD_STATIC_LIBRARY。
其他基础命令:
LOCAL_C_INCLUDES:= 表示添加头文件进入编译环境
LOCAL_LDLIBS:= 表示添加系统静态库
LOCAL_SHARED_LIBRARIES:= 表示添加共享库
其他命令请自行查看API文档。
这里指定了进行编译时的各项条件,如果需要指定编译器版本和编译目标平台等信息,则需要在jni目录下新建Application.mk文件。
接下来编写对应的C++文件。
打开eclipse,点击Project–>build Project(若build automatically已勾选则会自动编译)打开命令行,cd到你的工程文件夹下的bin–>classes文件夹下,输入如下命令:
javah com.example.ndktest.NDKHelper
回车,则在你的classes文件夹下会生成对应的头文件。这里com.example.ndktest是你的package名字,NDKHelper是你的NDK函数的类名。
就会生成头文件。其他不用管,我们关注中间的两个函数声明:
JNIEXPORT void JNICALL Java_com_example_ndktest_NDKHelper_ndkOne(JNIEnv *, jclass, jint, jlong);
1
这个函数就是NDKHelper类中ndkOne函数对应的C++版本,其中JNIEXPORT和JNICALL是固定字段,void是函数返回值,函数名由Java字段+包名+类名+函数名组成,参数则多了几个JNI的系统参数JNIEnv 和jclass,其他的就是NDKHelper类中的对应参数,ndk会对该函数进行解析和链接,实现java和C++的对接。
将生成的.h头文件复制到jni目录下,新建对应的cpp文件,将该头文件include进来并对对应函数进行实现,实现过程就视函数功能而定。
这些工作完成后需要修改你的Android.mk文件,将刚刚新建的cpp和h文件包括进来。
然后点击开始那个小锤子或者直接项目右键RunAs–>Android Application,则C++部分会开始编译,编译具体过程可以在Eclipse下方Console窗口看到(如果没有Console窗口则点击Window–>Show Views,选择Console确定即可)。
编译完成后会生成对应的库存放在libs目录下,则你可以开始在Java里面调用刚才定义的ndkOne和ndkTwo函数实现具体的功能。
ORB_SLAM2移植
先新建一个Android工程,建议不要把版本兼容到4.0以下,会出现一些奇奇怪怪的事情,这里我选择新建4.4.2的工程。
然后复制原工程的res文件夹替换掉自己工程下的文件夹,这里包括了所需的图片、存放在res/values/strings.xml的字符、res/layout下的各种布局;复制原工程的AndroidManifest.xml替换新建工程下的xml,复制完之后,意料之中报错啦,这是因为还没有链接OpenCV库,而在布局里有用到。
OpenCV库配置(参考官方文档)
先去官网下载opencv4android:
OpenCV官网下载
解压后,可以参考/docs/opencv_tutorials.pdf 感觉比很多博客讲的都更清楚
根据例程测试过问问题后,导入workspace,然后右键我们的ORB_SLAM2_Android工程->properties->Android,在右下Library里Add OpenCV库,如下图所示:
这样之后就暂时没有报错了
按照之前Android移植基础里讲的办法生成小锤子、jni、libs文件夹,然后复制原工程的.java文件到src,如下图所示:
其中OrbNdkHelper.java文件是关键的native标识符所在的类,代码如下:
public class OrbNdkHelper {
/**
* jni
* @param VOCPath
* @param calibrationPath
*/
public static native void initSystemWithParameters(String VOCPath,String calibrationPath);
public static native int[] startCurrentORB(double curTimeStamp,int[] data,int w,int h);
public native static int[] startCurrentORBForCamera(double curTimeStamp,long addr,int w,int h);
public native static void glesInit();
public native static void glesRender();
public native static void glesResize(int width, int height);
}
然后在命令行cd到工程根目录/bin/classes,输入javah orb.slam2.android.nativefunc.OrbNdkHelper生成.h文件,并复制到jni文件夹,然后复制原工程jni目录下的其他文件到本工程的jni,如下图所示:
这时候大概的文件都已经导入完毕了,但是我出现了许多的报错,以下是填坑过程。
1.orb_slam2_android_nativefunc_OrbNdkHelper.cpp里有几个报错:
JNIEXPORT void JNICALL Java_orb_slam2_android_nativefunc_OrbNdkHelper_initSystemWithParameters
(JNIEnv * env, jclass cls, jstring VOCPath, jstring calibrationPath) {
const char *calChar = env->GetStringUTFChars(calibrationPath, JNI_FALSE);
const char *vocChar = env->GetStringUTFChars(VOCPath, JNI_FALSE);
// use your string
std::string voc_string(vocChar);
std::string cal_string(calChar);
JavaVM *jvm=NULL; //增加的一句
env->GetJavaVM(&jvm);
jvm->AttachCurrentThread(&env, NULL);
s=new ORB_SLAM2::System(voc_string,cal_string,ORB_SLAM2::System::MONOCULAR,true);
env->ReleaseStringUTFChars(calibrationPath, calChar);
env->ReleaseStringUTFChars(VOCPath, vocChar);
init_end=true;
}
这段代码里面的:
JavaVM *jvm=NULL; //增加的一句
是我增加的,如果不增加的话,后两句会报错无法识别jvm这个变量,我搜索了全部工程也找不到jvm,所以只能自己先增加一个;
还有一个地方是:
resultPtr = env->GetIntArrayElements(resultArray, 0);
有3个地方用到GetIntArrayElements这个函数,全工程的第二个参数是false,这个在我这里是报错的,然后我百度了挺久,发现这个函数的声明是:
jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
原工程的flase不能被程序认定为jboolean格式,然后在jni.h顺藤摸瓜找到了以下定义:
#define JNI_FALSE 0
#define JNI_TRUE 1
于是把原来的“false”改成“JNI_FALSE”或者0,就可以通过了!
2.在用NDK-r10b版本的时候ORB_SLAM2文件夹和第三方文件夹出现大量报错,很多变量无法找到,这里的解决办法是:
改到r13b最新版本,然后在Application.mk文件里,NDK_TOOLCHAIN_VERSION := 4.8–>改到4.9
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions -mfloat-abi=softfp -mfpu=neon -std=gnu++0x -Wno-deprecated \
-ftree-vectorize -ffast-math -fsingle-precision-constant
NDK_TOOLCHAIN_VERSION := 4.9
APP_CFLAGS := --std=c++11
APP_ABI :=armeabi-v7a
APP_OPTIM := release
APP_SHORT_COMMANDS := true