剛剛結束了項目交付,趁熱打鐵分享一下這次遇到的新東西。首先了解一下大疆的無人機,它大致可以分為三級。
入門級:適合新手,沒事干在野外飛一飛拍拍風景啥的。操作也簡單,基本上看飛行教程都能懂,也不需要太高的專業(yè)性,飛機也相對較小安全系數(shù)相對較高。如:御2系列的一些飛機。
消費級:形體上會比入門級的大一點,但是飛機搭載了一些高精度的東西,比如RTK、高精度攝像頭、紅外鏡頭等。這類飛機則需要飛手具備一定的專業(yè)知識,并且需要考取大疆的初級飛手證書才能上手飛行。如:精靈系列、悟系列的一些飛機。
專業(yè)級:形體上更大,操作不當會出現(xiàn)一些嚴重后果。它的優(yōu)勢在于高續(xù)航、高精度定位、熱成像鏡頭、搭載第三方喊話器等。一般用于大型工程作業(yè),使用這個級別的飛機則需要考取大疆專業(yè)飛手證書,一般這個會在你購買專業(yè)級飛機的時候贈送你培訓課程,一般全部聽完就能考過。這類飛機的典型代表有:M30系列、M300系列。
1
SDK開發(fā)包
大疆的SDK開發(fā)包目前分為兩個大版本:
v4版
Mobile SDK是一款軟件開發(fā)套件,旨在讓開發(fā)者能夠訪問DJI無人機和手持相機產(chǎn)品的豐富功能。該SDK通過兼顧更底層的功能,諸如飛行穩(wěn)定,電池管理,信號傳輸和通信等,簡化了應用程序開發(fā)的過程。這樣,開發(fā)者就不需要具備豐富的機器人或嵌入式系統(tǒng)背景知識,而可以專注于DJI產(chǎn)品相關的行業(yè)應用開發(fā)。
該SDK包括:
功能概述
開發(fā)者可以通過SDK訪問許多DJI產(chǎn)品的功能。開發(fā)者可以實現(xiàn)自主飛行,控制相機和云臺,接收實時視頻圖傳和傳感器數(shù)據(jù),下載保存好的媒體文件,以及監(jiān)聽其他組件的狀態(tài)。
飛行控制
Mobile SDK提供三種控制無人機飛行的方式:
手動操控: 用戶使用遙控器操控無人機,而SDK支持監(jiān)控實時視頻流和傳感器數(shù)據(jù)。虛擬搖桿命令: SDK支持產(chǎn)生模擬遙控器搖桿的控制指令。
智能任務: 方便,易于實現(xiàn)無人機的高級控制。例如,可以通過航點任務,讓無人機按預定義的飛行路徑飛行。
虛擬搖桿命令和智能任務允許對DJI無人機進行簡單而功能強大的自主飛行控制。
相機
相機和云臺的功能都支持編程調用, 例如:
相機模式: 視頻和靜態(tài)圖像拍攝。
曝光: 快門,ISO,光圈和曝光補償均支持定制,以實現(xiàn)最大的靈活性。
圖像參數(shù): 屏幕長寬比,對比度,色相,清晰度,飽和度和濾鏡。
視頻參數(shù): 分辨率和幀頻。
方向: 使用云臺時,相機的朝向和運動可以自動控制。
實時視頻流
開發(fā)者可以通過Mobile SDK獲取無人機主攝像頭的實時視頻流。即使攝像頭正在將圖像或視頻捕獲到存儲介質中,也可以獲取實時視頻流。
傳感器數(shù)據(jù)
開發(fā)者可以通過SDK獲得豐富的傳感器數(shù)據(jù)。GPS位置,指南針,氣壓計,飛行速度和海拔高度都是通過Mobile SDK獲取的一些傳感器數(shù)據(jù),頻率最高可達10 Hz。
下載媒體文件
開發(fā)者通過Mobile SDK可以查看和下載保存在相機存儲介質(SD卡或固態(tài)硬盤)中的照片和視頻。預覽圖和完整的圖像數(shù)據(jù)都可以被訪問。
遙控器,電池和無線鏈路
遙控器,電池和無線鏈路都可以通過SDK進行訪問。通常,這些組件會提供相關的狀態(tài)信息,但開發(fā)者也可以對它們進行一些控制。
連接應用程序和產(chǎn)品
下圖說明了Mobile SDK如何與移動應用程序進行融合以及如何與DJI飛行器進行連接。
對于手持攝像機產(chǎn)品,遙控器已替換為手持控制器,并且沒有飛行器或其他無線鏈路。
移動應用程序由Mobile SDK,平臺SDK(iOS或Android)構建而成,并在移動設備(Apple iPhone,iPad,Nexus手機,Nexus平板電腦等)上運行。
移動設備可以通過WiFi無線連接到DJI產(chǎn)品上,也可以通過USB線纜連接到DJI產(chǎn)品上。
v5版
v5版現(xiàn)在只適用于M30、M300系列,目前還在持續(xù)更新,因為是新版的SDK,大疆的工程師也是在不斷地再完善里面的內容,這里就不詳細說了,之后的文章會詳細說這個v5版的SDK。
2
多的不說少的不嘮,上主菜。“工欲善其事,必先利其器”,首先準備好開發(fā)軟件,因為我們使用的是Android版的SDK所以開發(fā)軟件我們使用Android Studio,本人使用的是Android Studio Fox版,版本之間感覺都一樣沒有什么開發(fā)上的區(qū)別。準備好工具之后,先去大疆無人機的開發(fā)者官網(wǎng)注冊一個開發(fā)者賬號,并且注冊好自己的應用,拿到sdk的專用Key值,申請的方法大疆官網(wǎng)有我就不贅述了。給大家個地址【大疆開發(fā)者官網(wǎng)】自己看吧,挺簡單的。
注冊成為DJI開發(fā)者
在注冊過程中,需要您提供電子郵件信息和信用卡或手機號碼用于注冊驗證。您所提供的任何信用卡信息將僅用于驗證,不會收取任何費用。
本指南假定您使用 Xcode 7.3 以及 Android Studio 2.1.1 以上版本。
生成 App Key
每個應用程序都需要一個唯一的應用程序密鑰(App Key)來初始化SDK。
要創(chuàng)建一個應用程序App Key:
請訪問DJI開發(fā)者網(wǎng)站的 開發(fā)者中心
Android 示例代碼配置
下載或者克隆Github上的Android示例代碼工程。
在Android Studio中打開項目工程,將生成的App Key字符串粘貼到 "AndroidManifest.xml" 文件中 "com.dji.sdk.API_KEY" meda-data element下的 android:value。
????android:name="com.dji.sdk.API_KEY"
????android:value=""?/> 3
本節(jié)中的屏幕截圖是使用Android Studio 4.1生成的。
創(chuàng)建一個新的應用
可以使用一個新的應用程序來演示如何將DJI SDK集成到Android Studio項目中。


注意:Package name是 生成App Key 所需的標識字符串。在這個工程中Package name為“com.dji.ImportSDKDemo”
在 Target Android Devices 界面:

在 Add an Activity to Mobile 界面選擇 Empty Activity。


配置Gradle 腳本

使用以下內容進行更新:
apply?plugin:?'com.android.application'
android?{
????...
????defaultConfig?{
????????...
????}
????...
????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"
????????exclude?'META-INF/rxjava.properties'
????}
}
dependencies?{
???...
????implementation('com.dji:dji-sdk:4.15',?{
????
????exclude?module:?'library-anti-distortion'
????
????})
??compileOnly?'com.dji:dji-sdk-provided:4.15
}
主要變更為:
添加 packagingOptions以防止應用程序意外崩潰。
添加compile和provided依賴項以導入最新的DJIAndroid SDK Maven依賴項。

再次確認 Maven 依賴

實現(xiàn)應用注冊和SDK回調
右鍵單擊com.dji.importSDKDemo,然后選擇 New->Java Class以創(chuàng)建一個新的Java類,并將其命名為“MApplication”。

打開MApplication.java文件,并將內容替換為以下內容:
package?com.dji.importSDKDemo;
import?android.app.Application;
import?android.content.Context;
import?com.secneo.sdk.Helper;
public?class?MApplication?extends?Application?{
????@Override
????protected?void?attachBaseContext(Context?paramContext)?{
????????super.attachBaseContext(paramContext);
????????Helper.install(MApplication.this);
????}
}
注意:由于某些SDK類現(xiàn)在需要在使用之前進行加載,因此加載過程由Helper.install()完成。開發(fā)人員需要在使用任何SDK功能之前調用此方法, 否則可能會導致意外崩潰。

修改完成后需要在AndroidManifest中 配置 Application name。
雙擊app模塊中的MainActivity.java。
MainActivity類需要注冊應用程序以獲得使用Mobile SDK的授權。它還需要實現(xiàn)SDK回調方法。
public?class?MainActivity?extends?AppCompatActivity?{
????private?static?final?String?TAG?=?MainActivity.class.getName();
????public?static?final?String?FLAG_CONNECTION_CHANGE?=?"dji_sdk_connection_change";
????private?static?BaseProduct?mProduct;
????private?Handler?mHandler;
????private?static?final?String[]?REQUIRED_PERMISSION_LIST?=?new?String[]{
????????????Manifest.permission.VIBRATE,
????????????Manifest.permission.INTERNET,
????????????Manifest.permission.ACCESS_WIFI_STATE,
????????????Manifest.permission.WAKE_LOCK,
????????????Manifest.permission.ACCESS_COARSE_LOCATION,
????????????Manifest.permission.ACCESS_NETWORK_STATE,
????????????Manifest.permission.ACCESS_FINE_LOCATION,
????????????Manifest.permission.CHANGE_WIFI_STATE,
????????????Manifest.permission.WRITE_EXTERNAL_STORAGE,
????????????Manifest.permission.BLUETOOTH,
????????????Manifest.permission.BLUETOOTH_ADMIN,
????????????Manifest.permission.READ_EXTERNAL_STORAGE,
????????????Manifest.permission.READ_PHONE_STATE,
????};
????private?List?missingPermission?=?new?ArrayList<>();
????private?AtomicBoolean?isRegistrationInProgress?=?new?AtomicBoolean(false);
????private?static?final?int?REQUEST_PERMISSION_CODE?=?12345;
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????//?When?the?compile?and?target?version?is?higher?than?22,?please?request?the?following?permission?at?runtime?to?ensure?the?SDK?works?well.
????????if?(Build.VERSION.SDK_INT?>=?Build.VERSION_CODES.M)?{
????????????checkAndRequestPermissions();
????????}
????????setContentView(R.layout.activity_main);
????????//Initialize?DJI?SDK?Manager
????????mHandler?=?new?Handler(Looper.getMainLooper());
????}
????/**
?????*?Checks?if?there?is?any?missing?permissions,?and
?????*?requests?runtime?permission?if?needed.
?????*/
????private?void?checkAndRequestPermissions()?{
????????//?Check?for?permissions
????????for?(String?eachPermission?:?REQUIRED_PERMISSION_LIST)?{
????????????if?(ContextCompat.checkSelfPermission(this,?eachPermission)?!=?PackageManager.PERMISSION_GRANTED)?{
????????????????missingPermission.add(eachPermission);
????????????}
????????}
????????//?Request?for?missing?permissions
????????if?(missingPermission.isEmpty())?{
????????????startSDKRegistration();
????????}?else?if?(Build.VERSION.SDK_INT?>=?Build.VERSION_CODES.M)?{
????????????showToast("Need?to?grant?the?permissions!");
????????????ActivityCompat.requestPermissions(this,
????????????????????missingPermission.toArray(new?String[missingPermission.size()]),
????????????????????REQUEST_PERMISSION_CODE);
????????}
????}
????/**
?????*?Result?of?runtime?permission?request
?????*/
????@Override
????public?void?onRequestPermissionsResult(int?requestCode,
???????????????????????????????????????????@NonNull?String[]?permissions,
???????????????????????????????????????????@NonNull?int[]?grantResults)?{
????????super.onRequestPermissionsResult(requestCode,?permissions,?grantResults);
????????//?Check?for?granted?permission?and?remove?from?missing?list
????????if?(requestCode?==?REQUEST_PERMISSION_CODE)?{
????????????for?(int?i?=?grantResults.length?-?1;?i?>=?0;?i--)?{
????????????????if?(grantResults[i]?==?PackageManager.PERMISSION_GRANTED)?{
????????????????????missingPermission.remove(permissions[i]);
????????????????}
????????????}
????????}
????????//?If?there?is?enough?permission,?we?will?start?the?registration
????????if?(missingPermission.isEmpty())?{
????????????startSDKRegistration();
????????}?else?{
????????????showToast("Missing?permissions!!!");
????????}
????}
}
DJISDKManager的registerApp()方法具有一個回調,該回調需要處理兩種方法:一、用于處理應用程序注冊結果。二、用于通知硬件產(chǎn)品和移動設備的連接變更。
繼續(xù)添加如下所示的 startSDKRegistration() 方法,并實現(xiàn) onRegister(), onProductDisconnect(), onProductConnect(), onComponentChange(), onInitProcess() and onDatabaseDownloadProgress() 和SDKManagerCallback方法:
private?void?startSDKRegistration()?{
????if?(isRegistrationInProgress.compareAndSet(false,?true))?{
????????AsyncTask.execute(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????showToast("registering,?pls?wait...");
????????????????DJISDKManager.getInstance().registerApp(MainActivity.this.getApplicationContext(),?new?DJISDKManager.SDKManagerCallback()?{
????????????????????@Override
????????????????????public?void?onRegister(DJIError?djiError)?{
????????????????????????if?(djiError?==?DJISDKError.REGISTRATION_SUCCESS)?{
????????????????????????????showToast("Register?Success");
????????????????????????????DJISDKManager.getInstance().startConnectionToProduct();
????????????????????????}?else?{
????????????????????????????showToast("Register?sdk?fails,?please?check?the?bundle?id?and?network?connection!");
????????????????????????}
????????????????????????Log.v(TAG,?djiError.getDescription());
????????????????????}
????????????????????@Override
????????????????????public?void?onProductDisconnect()?{
????????????????????????Log.d(TAG,?"onProductDisconnect");
????????????????????????showToast("Product?Disconnected");
????????????????????????notifyStatusChange();
????????????????????}
????????????????????@Override
????????????????????public?void?onProductConnect(BaseProduct?baseProduct)?{
????????????????????????Log.d(TAG,?String.format("onProductConnect?newProduct:%s",?baseProduct));
????????????????????????showToast("Product?Connected");
????????????????????????notifyStatusChange();
????????????????????}
????????????????????@Override
????????????????????public?void?onComponentChange(BaseProduct.ComponentKey?componentKey,?BaseComponent?oldComponent,
??????????????????????????????????????????????????BaseComponent?newComponent)?{
????????????????????????if?(newComponent?!=?null)?{
????????????????????????????newComponent.setComponentListener(new?BaseComponent.ComponentListener()?{
????????????????????????????????@Override
????????????????????????????????public?void?onConnectivityChange(boolean?isConnected)?{
????????????????????????????????????Log.d(TAG,?"onComponentConnectivityChanged:?"?+?isConnected);
????????????????????????????????????notifyStatusChange();
????????????????????????????????}
????????????????????????????});
????????????????????????}
????????????????????????Log.d(TAG,
????????????????????????????????String.format("onComponentChange?key:%s,?oldComponent:%s,?newComponent:%s",
????????????????????????????????????????componentKey,
????????????????????????????????????????oldComponent,
????????????????????????????????????????newComponent));
????????????????????}
????????????????????@Override
????????????????????public?void?onInitProcess(DJISDKInitEvent?djisdkInitEvent,?int?i)?{
????????????????????}
????????????????????@Override
????????????????????public?void?onDatabaseDownloadProgress(long?l,?long?l1)?{
????????????????????}
????????????????});
????????????}
????????});
????}
}
最后需要實現(xiàn) notifyStatusChange, Runnable 和 showToast 方法:
private?void?notifyStatusChange()?{
????mHandler.removeCallbacks(updateRunnable);
????mHandler.postDelayed(updateRunnable,?500);
}
private?Runnable?updateRunnable?=?new?Runnable()?{
????@Override
????public?void?run()?{
????????Intent?intent?=?new?Intent(FLAG_CONNECTION_CHANGE);
????????sendBroadcast(intent);
????}
};
private?void?showToast(final?String?toastMsg)?{
????Handler?handler?=?new?Handler(Looper.getMainLooper());
????handler.post(new?Runnable()?{
????????@Override
????????public?void?run()?{
????????????Toast.makeText(getApplicationContext(),?toastMsg,?Toast.LENGTH_LONG).show();
????????}
????});
}
必須授予應用程序權限,DJI SDK才能運行。

在 package=com.dji.ImportSDKDemo 后,
"android.permission.BLUETOOTH"?/>
"android.permission.BLUETOOTH_ADMIN"?/>
"android.permission.VIBRATE"?/>
"android.permission.INTERNET"?/>
"android.permission.ACCESS_WIFI_STATE"?/>
"android.permission.WAKE_LOCK"?/>
"android.permission.ACCESS_COARSE_LOCATION"?/>
"android.permission.ACCESS_NETWORK_STATE"?/>
"android.permission.ACCESS_FINE_LOCATION"?/>
"android.permission.CHANGE_WIFI_STATE"?/>
"android.permission.MOUNT_UNMOUNT_FILESYSTEMS"?/>
"android.permission.WRITE_EXTERNAL_STORAGE"?/>
"android.permission.READ_EXTERNAL_STORAGE"?/>
"android.permission.SYSTEM_ALERT_WINDOW"?/>
"android.permission.READ_PHONE_STATE"?/>
"android.hardware.camera"?/>
"android.hardware.camera.autofocus"?/>
????android:name="android.hardware.usb.host"
????android:required="false"?/>
????android:name="android.hardware.usb.accessory"
????android:required="true"?/>
????android:name=".MApplication"
????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/AppTheme"?>
"com.android.future.usb.accessory"?/>
????android:name="com.dji.sdk.API_KEY"
????android:value="Please?enter?your?App?Key?here."?/>
????android:name="dji.sdk.sdkmanager.DJIAoaControllerActivity"
????android:theme="@android:style/Theme.Translucent"?>
????
????????"android.hardware.usb.action.USB_ACCESSORY_ATTACHED"?/>
????
????????????android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
????????android:resource="@xml/accessory_filter"?/>
"dji.sdk.sdkmanager.DJIGlobalService"?>
".MainActivity"
??????????android:configChanges="orientation"
??????????android:screenOrientation="portrait">
????
????????"android.intent.action.MAIN"?/>
????????"android.intent.category.LAUNCHER"?/>
????
生成App Key, 然后用App Key 字符串替換AndroidManifest.xml文件內Please enter your App Key here. 字段。
往期推薦詳解FreeRTOS:嵌入式軟件系統(tǒng)架構
汽車軟件開發(fā)V模型過程中AUTOSAR工具鏈的應用
CAN總線網(wǎng)絡中為什么需要安裝終端電阻?
瑞薩RH850 FCL、FDL和EEL庫的配置和使用
LabVIEW智能溫室控制系統(tǒng)
萬萬沒想到,低功耗也會燒毀元器件?
