最近碰到一個需求,在app上,引用高德地圖,在地圖上選點。連線,然後上傳航點數據,讓大疆的無人機,按照我們設置的航線飛行。
1.效果圖:
1.1.下載模擬軟件,模擬無人機的當前位置,在軟件上查看軟件的飛行 DJI Assistant 2 For Phantom,運行的效果圖如下,這個軟件可以在大疆api官網下載
1.2.地圖上用紅色的小飛機,表示當前模擬的無人機的位置,點擊按鈕add,開始添加航點,我的程序,自動把航點練成了線,因爲我們是無人機的航線,所以直接連接,不考慮道路交通的情況
1.3.設置最大的航行高度,飛行速度,完成航點任務後,無人機的執行動作等等,設置完後,點擊finish,上傳航線數據,然後點擊upload按鈕,加載航線,
1.4.點擊start按鈕,無人機開始執行任務,任務執行完後,飛機會爬升高度到120M,然後返航,到達起始模擬點,開始降落
2.在app下面的builder.gradle引入大疆dji和高德的框架,這裏只是貼出部分,如果是零基礎開發者,建議查看我上篇博客,裏面詳細講解了如何引用大疆sdk和高德sdk
implementation('com.dji:dji-sdk:4.11', {
/**
* Uncomment the "library-anti-distortion" if your app does not need Anti Distortion for Mavic 2 Pro and Mavic 2 Zoom.
* Uncomment the "fly-safe-database" if you need database for release, or we will download it when DJISDKManager.getInstance().registerApp
* is called.
* Both will greatly reducing the size of the APK.
*/
exclude module: 'library-anti-distortion'
exclude module: 'fly-safe-database'
})
compileOnly 'com.dji:dji-sdk-provided:4.11'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.core:core:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0-rc01'
implementation 'androidx.annotation:annotation:1.0.0'
implementation 'io.netty:netty-all:4.1.38.Final'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.github.zcweng:switch-button:0.0.3@aar'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'io.netty:netty-all:4.1.38.Final'
implementation files('libs/AMap_Location_V4.7.0_20190708.jar')
implementation files('libs/AMap_Search_V6.9.2_20190709.jar')
implementation files('libs/Android_Map3D_SDK_V6.9.2_20190709.jar')
implementation files('libs/Amap_2DMap_V5.2.0_20170627.jar')
}
3.清單文件
3.1 添加權限
<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.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.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
3.2高德和大疆的appkey
<meta-data android:name="com.amap.api.v2.apikey"
android:value="f0cd7c34a768eb0d2">
</meta-data>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<meta-data
android:name="com.dji.sdk.API_KEY"
android:value="8fd3d9" />
4.源碼下載地址:https://github.com/wrs13634194612/DjiWayPoint
5.獲取當前無人機的模擬位置,經緯度和高度,用於顯示紅色的飛機
private void initFlightController() {
BaseProduct product = DJISampleApplication.getProductInstance();
if (product != null && product.isConnected()) {
if (product instanceof Aircraft) {
mFlightController = ((Aircraft) product).getFlightController();
}
}
if (mFlightController != null) {
mFlightController.setStateCallback(
new FlightControllerState.Callback() {
@Override
public void onUpdate(FlightControllerState
djiFlightControllerCurrentState) {
droneLocationLat = djiFlightControllerCurrentState.getAircraftLocation().getLatitude();
droneLocationLng = djiFlightControllerCurrentState.getAircraftLocation().getLongitude();
droneAltitude = djiFlightControllerCurrentState.getAircraftLocation().getAltitude();
updateDroneLocation();
}
});
}
}
6.把無人機當前位置的經緯度,設置顯示紅色小飛機
addMarker 添加圖片
private void updateDroneLocation() {
LatLng pos = new LatLng(droneLocationLat, droneLocationLng);
//Create MarkerOptions object
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
runOnUiThread(new Runnable() {
@Override
public void run() {
if (droneMarker != null) {
droneMarker.remove();
}
if (checkGpsCoordination(droneLocationLat, droneLocationLng)) {
droneMarker = aMap.addMarker(markerOptions);
}
}
});
}
7.獲取地圖上點擊選取的點,把點的經緯度並且存到航線的list集合中
@Override
public void onMapClick(LatLng point) {
if (isAdd == true) {
markWaypoint(point);
latLngList.add(point);
Log.e("TAG", "start_thread: " + latLngList.toString());
aMap.addPolyline((new PolylineOptions())
.addAll(latLngList)
.width(10)
.setDottedLine(false)
.color(Color.RED));
Waypoint mWaypoint = new Waypoint(point.latitude, point.longitude, altitude);
//Add Waypoints to Waypoint arraylist;
if (waypointMissionBuilder != null) {
waypointList.add(mWaypoint);
waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
} else {
waypointMissionBuilder = new WaypointMission.Builder();
waypointList.add(mWaypoint);
waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
}
} else {
setResultToToast("Cannot Add Waypoint");
}
}
8.設置飛機的飛行速度,高度和完成任務的返航動作
private void showSettingDialog() {
LinearLayout wayPointSettings = (LinearLayout) getLayoutInflater().inflate(R.layout.dialog_waypointsetting, null);
final TextView wpAltitude_TV = (TextView) wayPointSettings.findViewById(R.id.altitude);
RadioGroup speed_RG = (RadioGroup) wayPointSettings.findViewById(R.id.speed);
RadioGroup actionAfterFinished_RG = (RadioGroup) wayPointSettings.findViewById(R.id.actionAfterFinished);
RadioGroup heading_RG = (RadioGroup) wayPointSettings.findViewById(R.id.heading);
speed_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.lowSpeed) {
mSpeed = 3.0f;
} else if (checkedId == R.id.MidSpeed) {
mSpeed = 5.0f;
} else if (checkedId == R.id.HighSpeed) {
mSpeed = 10.0f;
}
}
});
actionAfterFinished_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Log.d(TAG, "Select finish action");
if (checkedId == R.id.finishNone) {
mFinishedAction = WaypointMissionFinishedAction.NO_ACTION;
} else if (checkedId == R.id.finishGoHome) {
mFinishedAction = WaypointMissionFinishedAction.GO_HOME;
} else if (checkedId == R.id.finishAutoLanding) {
mFinishedAction = WaypointMissionFinishedAction.AUTO_LAND;
} else if (checkedId == R.id.finishToFirst) {
mFinishedAction = WaypointMissionFinishedAction.GO_FIRST_WAYPOINT;
}
}
});
heading_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Log.d(TAG, "Select heading");
if (checkedId == R.id.headingNext) {
mHeadingMode = WaypointMissionHeadingMode.AUTO;
} else if (checkedId == R.id.headingInitDirec) {
mHeadingMode = WaypointMissionHeadingMode.USING_INITIAL_DIRECTION;
} else if (checkedId == R.id.headingRC) {
mHeadingMode = WaypointMissionHeadingMode.CONTROL_BY_REMOTE_CONTROLLER;
} else if (checkedId == R.id.headingWP) {
mHeadingMode = WaypointMissionHeadingMode.USING_WAYPOINT_HEADING;
}
}
});
new AlertDialog.Builder(this)
.setTitle("")
.setView(wayPointSettings)
.setPositiveButton("Finish", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String altitudeString = wpAltitude_TV.getText().toString();
altitude = Integer.parseInt(nulltoIntegerDefalt(altitudeString));
Log.e(TAG, "altitude " + altitude);
Log.e(TAG, "speed " + mSpeed);
Log.e(TAG, "mFinishedAction " + mFinishedAction);
Log.e(TAG, "mHeadingMode " + mHeadingMode);
configWayPointMission();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
})
.create()
.show();
}
9.上傳航線航點,飛行參數
loadMission 這個方法是上傳的方法
private void configWayPointMission() {
if (waypointMissionBuilder == null) {
waypointMissionBuilder = new WaypointMission.Builder().finishedAction(mFinishedAction)
.headingMode(mHeadingMode)
.autoFlightSpeed(mSpeed)
.maxFlightSpeed(mSpeed)
.flightPathMode(WaypointMissionFlightPathMode.NORMAL);
} else {
waypointMissionBuilder.finishedAction(mFinishedAction)
.headingMode(mHeadingMode)
.autoFlightSpeed(mSpeed)
.maxFlightSpeed(mSpeed)
.flightPathMode(WaypointMissionFlightPathMode.NORMAL);
}
NettyClient.getInstance().sendHeartBeatData("map"+waypointList.toString());
if (waypointMissionBuilder.getWaypointList().size() > 0) {
for (int i = 0; i < waypointMissionBuilder.getWaypointList().size(); i++) {
waypointMissionBuilder.getWaypointList().get(i).altitude = altitude;
}
setResultToToast("Set Waypoint attitude successfully");
}
//waypointMissionBuilder.build().getWaypointList().toString() //上傳的list
NettyClient.getInstance().sendHeartBeatData("way"+ waypointMissionBuilder.build().getWaypointList().toString());
// loadMission 特別注意這個方法 這是加載航線數據的方法
DJIError error = getWaypointMissionOperator().loadMission(waypointMissionBuilder.build());
if (error == null) {
setResultToToast("loadWaypoint succeeded");
} else {
setResultToToast("loadWaypoint failed " + error.getDescription());
}
}
10.加載航線、航點、飛行參數等數據
private void uploadWayPointMission() {
getWaypointMissionOperator().uploadMission(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError error) {
if (error == null) {
setResultToToast("Mission upload successfully!");
} else {
setResultToToast("Mission upload failed, error: " + error.getDescription() + " retrying...");
getWaypointMissionOperator().retryUploadMission(null);
}
}
});
}
11.開始飛行
private void startWaypointMission() {
getWaypointMissionOperator().startMission(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError error) {
setResultToToast("Mission Start: " + (error == null ? "Successfully" : error.getDescription()));
}
});
}
12.停止飛行
private void stopWaypointMission() {
getWaypointMissionOperator().stopMission(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError error) {
setResultToToast("Mission Stop: " + (error == null ? "Successfully" : error.getDescription()));
}
});
}
end ,本程序,親測可用正常使用