大疆無人機 Mobile SDK Android應用程序搭建

  • 大疆官方開發文檔目前沒有提供中文的api,所以看起來可能會比較麻煩,尤其對于一些專業名詞,基于官方的sdk我們能開發出什么呢?我們基于sdk可以開發出跟DJI go 4一模一樣的功能
  • 目前的項目就是先把大疆官方支持的功能全部實現一遍
  • 然后在此基礎上實現自己的功能,無論是全自動執行任務,還是遠程控制都沒有問題。我會在后面的篇章教大家如何基于大疆的sdk開發出一個完整的應用。
  • 不再做過多的解釋直接上代碼,代碼中有相應的注解

在app下的build.gradle中的dependencies下導包

圖片.png
plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId ""
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        multiDexEnabled true//這一行加上,否則會運行錯誤

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    packagingOptions {
        doNotStrip "*/*/libdjivideo.so"
        doNotStrip "*/*/libSDKRelativeJNI.so"
        doNotStrip "*/*/libFlyForbid.so"
        doNotStrip "*/*/libduml_vision_bokeh.so"
        doNotStrip "*/*/libyuv2.so"
        doNotStrip "*/*/libGroudStation.so"
        doNotStrip "*/*/libFRCorkscrew.so"
        doNotStrip "*/*/libUpgradeVerify.so"
        doNotStrip "*/*/libFR.so"
        doNotStrip "*/*/libDJIFlySafeCore.so"
        doNotStrip "*/*/libdjifs_jni.so"
        doNotStrip "*/*/libsfjni.so"
        doNotStrip "*/*/libDJICommonJNI.so"
        doNotStrip "*/*/libDJICSDKCommon.so"
        doNotStrip "*/*/libDJIUpgradeCore.so"
        doNotStrip "*/*/libDJIUpgradeJNI.so"
        doNotStrip "*/*/libDJIWaypointV2Core.so"
        doNotStrip "*/*/libAMapSDK_MAP_v6_9_2.so"
        doNotStrip "*/*/libDJIMOP.so"
        doNotStrip "*/*/libDJISDKLOGJNI.so"
        exclude 'META-INF/rxjava.properties'
    }

}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation('com.dji:dji-sdk:4.15.1', {
        /**
         * 如果您的應用程序不需要Mavic 2 Pro和Mavic 2 Zoom的反失真功能,請取消對“庫反失真”的注釋。
         * 如果您需要發布數據庫,請取消對“飛行安全數據庫”的注釋,否則我們將在DJISDKManager.getInstance().registerApp時下載它
         * 被稱為。
         * 兩者都將大大減小APK的大小。
         */
        exclude module: 'library-anti-distortion'
        exclude module: 'fly-safe-database'
    })
    compileOnly 'com.dji:dji-sdk-provided:4.15.1'

}

在AndroidManifest.xml文件下添加對應的權限和配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="****">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <uses-feature
        android:name="android.hardware.usb.host"
        android:required="false"/>
    <uses-feature
        android:name="android.hardware.usb.accessory"
        android:required="true"/>


    <application
        android:name=".DroneApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.My_DJ_FlyDemo">

        <uses-library android:name="com.android.future.usb.accessory"/>

        <uses-library
            android:name="org.apache.http.legacy"
            android:required="false" />

        <!-- personal key -->
        <meta-data
            android:name="com.dji.sdk.API_KEY"
            android:value="請填入您在大疆開發平臺申請的KEY"/>

        <activity android:name="dji.sdk.sdkmanager.DJIAoaControllerActivity"
            android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
            </intent-filter>
            <meta-data
                android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter"/>

        </activity>

        <service android:name="dji.sdk.sdkmanager.DJIGlobalService"
            tools:ignore="Instantiatable" />


        <activity android:name=".ui.MainActivity"
            android:configChanges="orientation"
            android:screenOrientation="portrait"
            tools:ignore="LockedOrientationActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


    </application>

</manifest>

在Application中初始化類庫

public class DroneApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Helper.install(DroneApplication.this);
    }
}

MainActivity的代碼如下

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.okq.flydemo.R;

import java.util.ArrayList;
import java.util.List;

import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.common.realname.AircraftBindingState;
import dji.common.realname.AppActivationState;
import dji.common.useraccount.UserAccountState;
import dji.common.util.CommonCallbacks;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.realname.AppActivationManager;
import dji.sdk.sdkmanager.DJISDKInitEvent;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.useraccount.UserAccountManager;
import dji.thirdparty.afinal.core.AsyncTask;

public class MainActivity extends AppCompatActivity {

    private static final String[] PERMISSION_LIST = new String[]{
            Manifest.permission.VIBRATE, // 程序震動
            Manifest.permission.INTERNET, // 訪問互聯網
            Manifest.permission.ACCESS_WIFI_STATE, // 獲取 WIFI 狀態和 wifi 接入點信息
            Manifest.permission.WAKE_LOCK,//關閉屏幕時后臺進程仍然進行
            Manifest.permission.ACCESS_COARSE_LOCATION, // 獲得模糊定位信息
            Manifest.permission.ACCESS_NETWORK_STATE, // 獲取網絡狀態信息
            Manifest.permission.ACCESS_FINE_LOCATION, // 獲取精準定位信息
            Manifest.permission.CHANGE_WIFI_STATE, // 改變wifi連接狀態
            Manifest.permission.WRITE_EXTERNAL_STORAGE, // 寫入外部存儲信息
            Manifest.permission.BLUETOOTH, // 配對藍牙設備
            Manifest.permission.BLUETOOTH_ADMIN, // 配對藍牙設備(不通知用戶)
            Manifest.permission.READ_EXTERNAL_STORAGE, // 讀取外部存儲信息
            Manifest.permission.READ_PHONE_STATE, // 訪問電話狀態
            Manifest.permission.RECORD_AUDIO // 錄音權限
    };

    //缺失的用戶權限
    public static List<String> missingPermission = new ArrayList<>();


    private Button btn_login,btn_logout,btn_status_appactivation,btn_status_aircraftbinding;

    private TextView tv_status_appactivation,tv_status_aircraftbinding;

    //應用程序激活狀態監聽
    private AppActivationState.AppActivationStateListener appActivationStateListener;
    //無人機綁定狀態監聽
    private AircraftBindingState.AircraftBindingStateListener aircraftBindingStateListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //檢查用戶權限
        if (!checkPermissions()) {
            requestPermissions();
        }
        if (checkPermissions()) {
            //注冊應用程序
            registerAppLication();
        }

        initView();

        initListener();
    }

    private void initListener() {

        appActivationStateListener = new AppActivationState.AppActivationStateListener() {
            @Override
            public void onUpdate(AppActivationState appActivationState) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv_status_appactivation.setText("當前應用激活狀態:"+appActivationState.name());
                    }
                });
            }
        };

        aircraftBindingStateListener = new AircraftBindingState.AircraftBindingStateListener() {
            @Override
            public void onUpdate(AircraftBindingState aircraftBindingState) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv_status_aircraftbinding.setText("當前無人機綁定狀態:"+aircraftBindingState.name());
                    }
                });
            }
        };

        //添加應用程序激活監聽
        AppActivationManager.getInstance().addAppActivationStateListener(appActivationStateListener);
        //添加無人機綁定監聽
        AppActivationManager.getInstance().addAircraftBindingStateListener(aircraftBindingStateListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //移除應用程序激活監聽
        AppActivationManager.getInstance().removeAppActivationStateListener(appActivationStateListener);
        //移除無人機綁定監聽
        AppActivationManager.getInstance().removeAircraftBindingStateListener(aircraftBindingStateListener);
    }

    private void initView() {
        tv_status_appactivation = (TextView) findViewById(R.id.tv_status_appactivation);
        tv_status_aircraftbinding = (TextView) findViewById(R.id.tv_status_aircraftbinding);

        //登錄dji賬號
        btn_login = (Button)findViewById(R.id.btn_login);
        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                UserAccountManager.getInstance().logIntoDJIUserAccount(MainActivity.this, new CommonCallbacks.CompletionCallbackWith<UserAccountState>() {
                    @Override
                    public void onSuccess(UserAccountState userAccountState) {
                        ShowToas("登錄成功");
                    }

                    @Override
                    public void onFailure(DJIError djiError) {
                        ShowToas("登錄失?。?+djiError.getDescription());
                    }
                });
            }
        });

        //退出賬號
        btn_logout = (Button)findViewById(R.id.btn_logout);
        btn_logout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                UserAccountManager.getInstance().logoutOfDJIUserAccount(new CommonCallbacks.CompletionCallback() {
                    @Override
                    public void onResult(DJIError djiError) {
                        if (djiError == null){
                            ShowToas("退出成功");
                        }else {
                            ShowToas("退出失?。?+djiError.getDescription());
                        }
                    }
                });
            }
        });

        //獲取應用激活狀態
        btn_status_appactivation = (Button)findViewById(R.id.btn_status_appactivation);
        btn_status_appactivation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AppActivationManager appActivationManager = DJISDKManager.getInstance().getAppActivationManager();
                AppActivationState appActivationState = appActivationManager.getAppActivationState();
                ShowToas("激活狀態:"+appActivationState);
                if (!(""+appActivationState).equals("ACTIVATED")){
                    Intent launchIntentForPackage = getPackageManager().getLaunchIntentForPackage("dji.go.v4");
                    if (launchIntentForPackage != null){
                        startActivity(launchIntentForPackage);
                    }else {
                        ShowToas("未安裝 DJI Go");
                    }
                }

            }
        });

        //獲取無人機綁定狀態
        btn_status_aircraftbinding = (Button)findViewById(R.id.btn_status_aircraftbinding);
        btn_status_aircraftbinding.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AppActivationManager appActivationManager = DJISDKManager.getInstance().getAppActivationManager();
                AircraftBindingState aircraftBindingState = appActivationManager.getAircraftBindingState();
                ShowToas("綁定狀態:"+aircraftBindingState);
                if (!(""+aircraftBindingState).equals("BOUND")){
                    Intent launchIntentForPackage = getPackageManager().getLaunchIntentForPackage("dji.go.v4");
                    if (launchIntentForPackage != null){
                        startActivity(launchIntentForPackage);
                    }else {
                        ShowToas("未安裝 DJI Go 4");
                    }
                }
            }
        });



    }


    //注冊程序
    private void registerAppLication() {

        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                DJISDKManager.getInstance().registerApp(MainActivity.this.getApplicationContext(), new DJISDKManager.SDKManagerCallback() {
                    @Override
                    public void onRegister(DJIError djiError) {//注冊應用程序回調方法
                        if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
                            ShowToas("onRegister--->應用注冊成功:" + djiError.getDescription());
                            DJISDKManager.getInstance().enableBridgeModeWithBridgeAppIP("192.168.1.189");
                        } else {
                            ShowToas("onRegister--->應用注冊失敗:" + djiError.getDescription());
                        }
                    }

                    @Override
                    public void onProductDisconnect() {//無人機失去連接回調
                        ShowToas("onProductDisconnect--->設備斷開連接");
                    }

                    @Override
                    public void onProductConnect(BaseProduct baseProduct) {//無人機連接回調
                        if (baseProduct.getModel() == null){
                            return;
                        }
                        ShowToas("onProductConnect--->設備連接"+baseProduct.getModel().getDisplayName());
                    }

                    @Override
                    public void onProductChanged(BaseProduct baseProduct) {//無人機連接變化回調
                        if (baseProduct == null){
                            return;
                        }
                        ShowToas("onProductChanged--->設備連接"+baseProduct.getModel().getDisplayName());
                    }

                    //無人機組件 變化變化回調
                    @Override
                    public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent baseComponent, BaseComponent baseComponent1) {
                        ShowToas("onComponentChange--->"+String.format("組件變化 鍵:%s,舊組件:%s,新組件:%s",componentKey,baseComponent,baseComponent1));
                    }

                    @Override
                    public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) {//初始化進程回調
                        ShowToas("onInitProcess--->"+djisdkInitEvent.getInitializationState().toString() + "進度:" + i + "%");
                    }

                    @Override
                    public void onDatabaseDownloadProgress(long l, long l1) {//限飛數據庫下載進度回調
//                        ShowToas("onDatabaseDownloadProgress--->已下載: "+l+"字節,總共:"+l1+"字節");
                        Log.i("TAG", "onDatabaseDownloadProgress--->已下載: "+l+"字節,總共:"+l1+"字節");
                    }
                });
            }
        });

    }

    private void ShowToas(String msg){

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

    }


    public boolean checkPermissions() {
        //判斷應用程序編輯的SDK版本是否大于22
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for (String string : PERMISSION_LIST) {
                if (ContextCompat.checkSelfPermission(MainActivity.this, string) != PackageManager.PERMISSION_GRANTED) {
                    //沒有賦予的權限添加到集合中
                    missingPermission.add(string);
                }
            }
            return missingPermission.isEmpty();
        } else {
            return false;
        }
    }

    public void requestPermissions() {
        //申請所有沒有被賦予的權限
        ActivityCompat.requestPermissions(MainActivity.this, missingPermission.toArray(new String[missingPermission.size()]), 1009);
    }

}

activity_main.xml布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="應用激活與無人機綁定"/>

    <TextView
        android:id="@+id/tv_status_appactivation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="35dp"
        android:text="當前應用程序激活狀態:UNKNOWN"
        android:gravity="center"/>

    <TextView
        android:id="@+id/tv_status_aircraftbinding"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="35dp"
        android:gravity="center"
        android:text="當前無人機綁定狀態:UNKNOWN"/>

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="70dp"
        android:layout_marginRight="70dp"
        android:layout_marginTop="35dp"
        android:text="登錄DJI賬號"/>

    <Button
        android:id="@+id/btn_logout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="70dp"
        android:layout_marginRight="70dp"
        android:layout_marginTop="35dp"
        android:text="退出DJI賬號"/>

    <Button
        android:id="@+id/btn_status_appactivation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="70dp"
        android:layout_marginRight="70dp"
        android:layout_marginTop="35dp"
        android:text="獲取應用激活狀態"/>

    <Button
        android:id="@+id/btn_status_aircraftbinding"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="70dp"
        android:layout_marginRight="70dp"
        android:layout_marginTop="35dp"
        android:text="獲取無人機綁定狀態"/>

</LinearLayout>
  • 至此就能獲取對應的設備狀態和賬號登錄等狀態,我使用的御2無人機


    圖片.png

DJI Go 4下載地址(https://www.dji.com/cn/downloads/djiapp/dji-go-4)

  • 參考資料《大疆無人機二次開發教程》
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容