本节全篇为大疆 Mobile SDK 安卓教程 部分,ios教程参见 IOS教程 .
模拟器应用程序
在本教程中,你可以学习如何使用DJI Mobile SDK在你的Android Studio项目中使用DJISimulator。通过虚拟摇杆控制,你可以输入虚拟杆飞行控制数据,并且可以实时检查模拟器状态变更。
你可以在这里下载本教程最终示例项目 Github Page.
我们用 Mavic Pro 作为示例进行演示,开始吧
介绍
DJISimulator 用于根据虚拟杆输入在模拟环境中控制飞机。模拟的飞机状态信息也将显示在屏幕上。
您可以用 FlightController
中的 Simulator
类去控制模拟。它允许模拟手动和自动飞行,而无需让飞机实际飞行。
此外,可以通过SDK直接控制模拟器初始化,监视和终止,从而允许在持续集成环境中进行应用程序开发。
在中国进行应用激活和飞机绑定
对于在中国使用的DJI SDK移动应用程序,需要激活应用程序并将飞机绑定到用户的DJI帐户。
如果未激活应用程序,未使用飞机(如果需要)或使用旧版SDK(<4.1),则将禁用所有 相机实时流 ,并且飞行将限制为直径100米和高度为30米的区域,以确保飞机保持在视线范围内。
要了解如何实现此功能,请查看本教程 Application Activation and Aircraft Binding.
实现应用程序UI
导入 Maven 依赖
- 创建名为
DJISimulatorDemo
的新项目 - 包名
com.dji.simulatorDemo
- 最低版本
API 19: Android 4.4 (KitKat)
- 选择 "Empty Activity" 然后其他默认
在我们之前的教程中l Importing and Activating DJI SDK in Android Studio Project,您已经学习了如何导入 Android SDK Maven 依赖项并激活您的应用程序。如果你之前没看过,那就回去看一下相关实现。然后再继续。
构建 Activity 布局
1. 实现操纵杆控制
为了输入一些模拟数据, 像 pitch
, roll
, yaw
and verticalThrottle
, 你可能需要一个操纵杆控制。让我们来实现它吧。
我们根据 Github 开源项目 OnScreenJoystick 实现操纵杆控制。你可以从下载的Github项目中获取 OnScreenJoystick.java and OnScreenJoystickListener.java 文件,或者从本教程的Github示例项目中获取。现在,复制粘贴这两个java文件到 "com.dji.simulatorDemo" 下面:
当你触摸 OnScreenJoystick 视图时,OnScreenJoystickListener 的 onTouch
方法将被调用,你可以从这个方法中获取 "OnScreenJoystick" 操纵杆对象,还有节点参数的 x,y 坐标。如下所示:
/** Called when the joystick is touched.
* @param joystick The joystick which has been touched.
* @param pX The x coordinate of the knob. Values are between -1 (left) and 1 (right).
* @param pY The y coordinate of the knob. Values are between -1 (down) and 1 (up).
*/
public void onTouch(final OnScreenJoystick joystick, final float pX, final float pY);
Note: 节点的 x 坐标值在 -1 (left) 和 1 (right) 之间,y坐标值在 -1 (down) 和 1 (up) 之间。
接下来,从本教程的Github示例项目中复制粘贴 joystick.png 和 joystick_bg.png 文件到 mipmap 文件夹(as3.3.2 是 mipmap-xxxhdpi):
2. 实现 MainActivity 类的UI元素
现在,让我们打开 MainActivity.java 文件,并用以下代码替换:
public class MainActivity extends Activity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getName();
protected TextView mConnectStatusTextView;
private Button mBtnEnableVirtualStick;
private Button mBtnDisableVirtualStick;
private ToggleButton mBtnSimulator;
private Button mBtnTakeOff;
private Button mBtnLand;
private TextView mTextView;
private OnScreenJoystick mScreenJoystickRight;
private OnScreenJoystick mScreenJoystickLeft;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
}
@Override
public void onResume() {
Log.e(TAG, "onResume");
super.onResume();
}
@Override
public void onPause() {
Log.e(TAG, "onPause");
super.onPause();
}
@Override
public void onStop() {
Log.e(TAG, "onStop");
super.onStop();
}
public void onReturn(View view){
Log.e(TAG, "onReturn");
this.finish();
}
@Override
protected void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
}
private void initUI() {
mBtnEnableVirtualStick = (Button) findViewById(R.id.btn_enable_virtual_stick);
mBtnDisableVirtualStick = (Button) findViewById(R.id.btn_disable_virtual_stick);
mBtnTakeOff = (Button) findViewById(R.id.btn_take_off);
mBtnLand = (Button) findViewById(R.id.btn_land);
mBtnSimulator = (ToggleButton) findViewById(R.id.btn_start_simulator);
mTextView = (TextView) findViewById(R.id.textview_simulator);
mConnectStatusTextView = (TextView) findViewById(R.id.ConnectStatusTextView);
mScreenJoystickRight = (OnScreenJoystick)findViewById(R.id.directionJoystickRight);
mScreenJoystickLeft = (OnScreenJoystick)findViewById(R.id.directionJoystickLeft);
mBtnEnableVirtualStick.setOnClickListener(this);
mBtnDisableVirtualStick.setOnClickListener(this);
mBtnTakeOff.setOnClickListener(this);
mBtnLand.setOnClickListener(this);
mBtnSimulator.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
}
});
mScreenJoystickLeft.setJoystickListener(new OnScreenJoystickListener(){
@Override
public void onTouch(OnScreenJoystick joystick, float pX, float pY) {
}
});
mScreenJoystickRight.setJoystickListener(new OnScreenJoystickListener() {
@Override
public void onTouch(OnScreenJoystick joystick, float pX, float pY) {
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_enable_virtual_stick:
break;
case R.id.btn_disable_virtual_stick:
break;
case R.id.btn_take_off:
break;
case R.id.btn_land:
break;
default:
break;
}
}
}
以上代码中,我们实现了以下功能:
1. 创建布局UI元素变量,包括两个TextView,4个Button,1个ToggleButton和2个 OnScreenJoystick 控制。
2. 在 onCreate()
方法中, 我们要请求几个运行时权限确保编译时或SDK版本大于22的时候SDK可以正常工作。 然后调用 initUI()
方法去初始化UI变量和监听事件。.
3. 在 initUI()
方法中, 我们首先初始化UI元素变量, 然后将四个按钮的监听事件设置为this 。此外,实现切换按钮监听事件 mBtnSimulator.setOnCheckedChangeListener
的 onCheckedChanged()
方法。 最后,实现两个 OnScreenJoystick.setJoystickListener
对象的监听事件的 onTouch()
方法。
实现切换按钮“mBtnSimulator”的“setOnCheckedChangeListener”的“onCheckedChanged()”方法。
4. 重写 onClick()
以实现4个按钮的点击操作。
3. 实现 MainActivity 布局
打开 activity_main.xml 布局文件,并且用以下代码替换:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/main_title_rl"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:background="@color/black_overlay" >
<ImageButton
android:id="@+id/ReturnBtn"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:onClick="onReturn"
android:scaleType="centerInside"
android:src="@drawable/selector_back_button" />
<TextView
android:id="@+id/ConnectStatusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="SimulatorDemo"
android:textColor="@android:color/white"
android:textSize="19sp" />
</RelativeLayout>
<TextView
android:layout_marginTop="70dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Simulator is off."
android:id="@+id/textview_simulator"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enable Virtual Stick"
style="@style/common_button"
android:id="@+id/btn_enable_virtual_stick"
android:layout_marginLeft="5dp"
android:layout_alignTop="@+id/btn_start_simulator"
android:layout_alignStart="@+id/directionJoystickRight"
android:layout_marginTop="0dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Disable Virtual Stick"
style="@style/common_button"
android:id="@+id/btn_disable_virtual_stick"
android:layout_below="@+id/btn_enable_virtual_stick"
android:layout_alignStart="@+id/btn_enable_virtual_stick"
android:layout_marginLeft="0dp"
android:layout_alignEnd="@+id/btn_enable_virtual_stick" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Take Off"
style="@style/common_button"
android:id="@+id/btn_take_off"
android:layout_alignTop="@+id/btn_disable_virtual_stick"
android:layout_alignStart="@+id/btn_start_simulator"
android:layout_marginTop="0dp"
android:layout_alignEnd="@+id/btn_start_simulator" />
<ToggleButton
android:id="@+id/btn_start_simulator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Simulator"
android:textOff="Start Simulator"
android:textOn="Stop Simulator"
style="@style/common_button"
android:layout_below="@+id/textview_simulator"
android:layout_toEndOf="@+id/btn_enable_virtual_stick"
android:layout_marginTop="107dp"
android:layout_marginLeft="10dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Land"
style="@style/common_button"
android:id="@+id/btn_land"
android:layout_alignTop="@+id/btn_take_off"
android:layout_marginTop="0dp"
android:layout_alignEnd="@+id/directionJoystickLeft"
android:layout_toEndOf="@+id/btn_take_off"
android:layout_marginLeft="10dp" />
<com.dji.simulatorDemo.OnScreenJoystick
android:id="@+id/directionJoystickRight"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:background="@mipmap/joystick_bg"/>
<com.dji.simulatorDemo.OnScreenJoystick
android:id="@+id/directionJoystickLeft"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:background="@mipmap/joystick_bg"/>
</RelativeLayout>
在这个xml文件中, 首先,我们实现这个RelativeLayout元素。 我们声明一个 ImageButton(id: ReturnBtnCamera) 元素去结束程序, 还有一个 TextView(id: ConnectStatusTextView) 元素用来展示连接状态。
接下来,创建一个 TextureView(id: textview_simulator) 元素来展示模拟器状态信息。此外,创建4个按钮: "Enable Virtual Stick" button(id: btn_enable_virtual_stick), "Disable Virtual Stick" button(id: btn_disable_virtual_stick), "Take Off" button(id: btn_take_off), "Land" button(id: btn_land),然后创建 "Start Simulator" toggle button(id: btn_start_simulator) ,并设置 textOn 和 textOff 参数为 "Start Simulator" 和 "Stop Simulator"。
最后,我们为操作杆控制创建两个 OnScreenJoystick 元素(id: directionJoystickRight) 和 (id:directionJoystickLeft)。
4. 配置资源文件
完成上述步骤后,我们添加一些资源文件到 res 文件夹。
从教程示例程序的 drawable 文件夹下复制下图中的 image 和 xml文件 到你的项目中,他们可用于 button UI:
接下来,打开 "colors.xml" 文件,并在底部添加以下代码来声明 “black_overlay”:
<color name="black_overlay">#66000000</color>
此外, 打开 "strings.xml" 文件并添加以下字符串:
<string name="success">Success</string>
最后,打开 "styles.xml" 文件,并添加以下代码来声明 "common_button" :
<!-- Common button style -->
<style name="common_button">
<item name="android:layout_width">100dp</item>
<item name="android:layout_height">45dp</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:background">@drawable/round_btn</item>
<item name="android:paddingLeft">5dp</item>
<item name="android:paddingRight">5dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">14sp</item>
</style>
在 DJISimulatorApplication 和 MainActivity 中实现注册
当你完成上述步骤后,让我们在大疆开发者网站来注册应用程序,并获取 App Key 。 如果不会,看看这里 Generate an App Key
此外, MApplication
, DJISimulatorApplication
和 MainActivity
的详细实现,可以看看这个教程 Creating an Camera Application ,还有这个 sample project 。
现在让我们构建运行程序,然后安装到你的 android 设备上。如果一切ok,那么当你注册成功的时候应该可以看到 "success" 。
实现 MainActivity 类
更新产品连接状态
更详细的实现看本教程的示例代码: sample project
现在,我们构建并运行项目,然后安装到你的Android设备上。然后把demo连接到你的Mavic Pro上 (不会看看这个 Run Application ),如果一切ok,你应该可以看到 title 内容更新为 "MavicPro Connected" :
实现虚拟杆控制
由于我们实现了虚拟杆控制, 现在我们可以继续发送虚拟杆飞行控制数据到飞机。首先,我们在 onCreate()
方法上面创建一个 FlightController 变量mFlightController
,一个 Timer 变量 mSendVirtualStickDataTimer
,一个 SendVirtualStickDataTask (继承自 TimerTask 类) 变量 mSendVirtualStickDataTask
和4个 float 变量,如下所示:
private FlightController mFlightController;
private Timer mSendVirtualStickDataTimer;
private SendVirtualStickDataTask mSendVirtualStickDataTask;
private float mPitch;
private float mRoll;
private float mYaw;
private float mThrottle;
我们可以用 mPitch
, mRoll
, mYaw
和 mThrottle
变量去存储 pitch, roll, yaw 和 vertical throttle 虚拟杆飞行控制数据。
接下来,创建 initFlightController()
方法,在 onResume()
方法中调用它,并实现 SendVirtualStickDataTask 类:
private void initFlightController() {
Aircraft aircraft = DJISimulatorApplication.getAircraftInstance();
if (aircraft == null || !aircraft.isConnected()) {
showToast("Disconnected");
mFlightController = null;
return;
} else {
mFlightController = aircraft.getFlightController();
}
}
@Override
public void onResume() {
Log.e(TAG, "onResume");
super.onResume();
updateTitleBar();
initFlightController();
}
class SendVirtualStickDataTask extends TimerTask {
@Override
public void run() {
if (mFlightController != null) {
mFlightController.sendVirtualStickFlightControlData(
new FlightControlData(
mPitch, mRoll, mYaw, mThrottle
), new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
}
}
);
}
}
}
以上代码实现了以下功能:
1. 在 initFlightController()
方法中, 我们首先检查飞机是否 not null 并且已连接, 然后调用 Aircraft 的 getFlightController()
方法获取 mFlightController
变量。
2. 接下来,继承 TimerTask 类创建 SendVirtualStickDataTask 类。在这个类中,重写 run()
方法去调用 FlightController 的sendVirtualStickFlightControlData()
方法去发送虚拟杆飞行控制数据。在这里,我们创建 FlightControlData 对象,并在第一个参数 FlightControlData 中传入之前声明的4个 float 变量: mPitch
, mRoll
, mYaw
和 mThrottle
。
完成上述步骤后, 让我们在 initUI()
方法底部实现 mScreenJoystickLeft
和 mScreenJoystickRight
变量的setJoystickListener()
方法:
mScreenJoystickLeft.setJoystickListener(new OnScreenJoystickListener(){
@Override
public void onTouch(OnScreenJoystick joystick, float pX, float pY) {
if(Math.abs(pX) < 0.02 ){
pX = 0;
}
if(Math.abs(pY) < 0.02 ){
pY = 0;
}
float pitchJoyControlMaxSpeed = 10;
float rollJoyControlMaxSpeed = 10;
mPitch = (float)(pitchJoyControlMaxSpeed * pX);
mRoll = (float)(rollJoyControlMaxSpeed * pY);
if (null == mSendVirtualStickDataTimer) {
mSendVirtualStickDataTask = new SendVirtualStickDataTask();
mSendVirtualStickDataTimer = new Timer();
mSendVirtualStickDataTimer.schedule(mSendVirtualStickDataTask, 100, 200);
}
}
});
mScreenJoystickRight.setJoystickListener(new OnScreenJoystickListener() {
@Override
public void onTouch(OnScreenJoystick joystick, float pX, float pY) {
if(Math.abs(pX) < 0.02 ){
pX = 0;
}
if(Math.abs(pY) < 0.02 ){
pY = 0;
}
float verticalJoyControlMaxSpeed = 2;
float yawJoyControlMaxSpeed = 30;
mYaw = (float)(yawJoyControlMaxSpeed * pX);
mThrottle = (float)(verticalJoyControlMaxSpeed * pY);
if (null == mSendVirtualStickDataTimer) {
mSendVirtualStickDataTask = new SendVirtualStickDataTask();
mSendVirtualStickDataTimer = new Timer();
mSendVirtualStickDataTimer.schedule(mSendVirtualStickDataTask, 0, 200);
}
}
});
在这里,我们实现了一下功能:
1. 重写 setJoystickListener 的 onTouch()
方法,并过滤低于 0.02的 pX
和 pY
变量的值。如果值太小,我们应该不会太频繁的发送虚拟杆数据到飞行控制器
2. 获取 pitch 和 roll 控制的最大速度,然后把它们存储到 pitchJoyControlMaxSpeed
和 rollJoyControlMaxSpeed
变量。由于 pX
的值在 -1 (left) 和 1 (right) 之间, pY
的值在 -1 (down) 和 1 (up) 之间,我们使用 pitchJoyControlMaxSpeed
和 rollJoyControlMaxSpeed
值相乘来更新 mPitch
和 mRoll
的数据。这里我们以遥控器的模式2(美国模式)为例。
3. 最后,我们检查 mSendVirtualStickDataTimer
是否为null, 并通过调用 SendVirtualStickDataTask()
方法来创建它。然后创建 mSendVirtualStickDataTimer
并通过传递 mSendVirtualStickDataTask
变量,延迟0毫秒,后续执行间隔200毫秒这3个参数来调用它的 schedule(TimerTask task, long delay, long period)
方法去触发计时器 。
4. 同样的,实现 mScreenJoystickRight
变量的 setJoystickListener()
方法去更新 mYaw
和 mThrottle
的值,并调用计时器去发送虚拟杆数据到飞机的飞行控制器。
现在,当你控制左右操纵杆的时候,将发送模拟虚拟杆数据 (包括 Yaw, Pitch, Roll 和 Vertical Throttle) 到飞机的飞行控制器。
最后,重写 onClick()
方法来实现可用和禁用虚拟杆控制按钮的点击操作:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_enable_virtual_stick:
if (mFlightController != null){
mFlightController.setVirtualStickModeEnabled(true, new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null){
showToast(djiError.getDescription());
}else
{
showToast("Enable Virtual Stick Success");
}
}
});
}
break;
case R.id.btn_disable_virtual_stick:
if (mFlightController != null){
mFlightController.setVirtualStickModeEnabled(false, new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
} else {
showToast("Disable Virtual Stick Success");
}
}
});
}
break;
}
}
这将调用 FlightController 的 enableVirtualStickControlMode()
and disableVirtualStickControlMode()
方法来启用和禁用虚拟杆控制模式。
实现 DJISimulator
现在让我们来实现 DJISimulator 功能,为了更新mTextView
中的模拟器状态数据,我们需要在 initFlightController()
方法中实现 DJISimulator 的 setStateCallback()
方法:
private void initFlightController() {
Aircraft aircraft = DJISimulatorApplication.getAircraftInstance();
if (aircraft == null || !aircraft.isConnected()) {
showToast("Disconnected");
mFlightController = null;
return;
} else {
mFlightController = aircraft.getFlightController();
mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY);
mFlightController.setRollPitchCoordinateSystem(FlightCoordinateSystem.BODY);
mFlightController.getSimulator().setStateCallback(new SimulatorState.Callback() {
@Override
public void onUpdate(final SimulatorState stateData) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
String yaw = String.format("%.2f", stateData.getYaw());
String pitch = String.format("%.2f", stateData.getPitch());
String roll = String.format("%.2f", stateData.getRoll());
String positionX = String.format("%.2f", stateData.getPositionX());
String positionY = String.format("%.2f", stateData.getPositionY());
String positionZ = String.format("%.2f", stateData.getPositionZ());
mTextView.setText("Yaw : " + yaw + ", Pitch : " + pitch + ", Roll : " + roll + "\n" + ", PosX : " + positionX +
", PosY : " + positionY +
", PosZ : " + positionZ);
}
});
}
});
}
}
在以上代码中,我们重写了 onUpdate()
方法来获取最新模拟器状态数据,然后调用 SimulatorState
的 getYaw()
, getPitch()
, getRoll()
, getPositionX()
, getPositionY()
and getPositionZ()
方法来获取更新 yaw, pitch, roll, positionX, positionY 和 positionZ 的值,并在mTextView
中显示。
接下来,重写开关按钮的监听事件 mBtnSimulator.setOnCheckedChangeListener()
的 的 onCheckedChanged()
方法:
mBtnSimulator.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mTextView.setVisibility(View.VISIBLE);
if (mFlightController != null) {
mFlightController.getSimulator()
.start(InitializationData.createInstance(new LocationCoordinate2D(23, 113), 10, 10),
new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
}else
{
showToast("Start Simulator Success");
}
}
});
}
} else {
mTextView.setVisibility(View.INVISIBLE);
if (mFlightController != null) {
mFlightController.getSimulator()
.stop(new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
}else
{
showToast("Stop Simulator Success");
}
}
}
);
}
}
}
});
在以上代码中,我们实现了以下功能:
1. 如果开关按钮 mBtnSimulator
被选中,则显示 mTextView
。接下来,如果 mFlightController
不是 null, 我们就调用 DJISimulator 的 start()
方法, 传一个 InitializationData.createInstance(new LocationCoordinate2D(23, 113), 10, 10)
参数说明LocationCoordinate2D(纬度,经度),更新频率,卫星数量) . 更多 DJISimulatorInitializationData 的详情在这里 Android API Reference.
2. 接下来,重写 start()
的 onResult()
方法, 调用 showToast()
方法向用户显示启动模拟器的结果。
3. 同样的,如果切换按钮 mBtnSimulator
没有被选中,则调用 DJISimulator 的 stop()
方法停止模拟器。此外,重写 onResult()
方法并调用 showToast()
方法向用户显示停止模拟器的结果。
Takeoff 和 AutoLanding 功能(起飞和自动着陆)
最后,让我们在 onClick()
方法底部添加以下代码来实现 Take off 和 Land 按钮的点击操作:
case R.id.btn_take_off:
if (mFlightController != null){
mFlightController.startTakeoff(
new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
} else {
showToast("Take off Success");
}
}
}
);
}
break;
case R.id.btn_land:
if (mFlightController != null){
mFlightController.startLanding(
new CommonCallbacks.CompletionCallback() {
@Override
public void onResult(DJIError djiError) {
if (djiError != null) {
showToast(djiError.getDescription());
} else {
showToast("Start Landing");
}
}
}
);
}
break;
对于 "R.id.btn_take_off",我们调用 FlightController 的 startTakeoff()
方法向飞机发送起飞命令。同样的,对于 "R.id.btn_land" ,我们调用 startLanding()
方法发送自动着陆命令。这可以轻松实现。
在本教程中,我们已经走了很长的路,现在让我们构建并运行项目吧,把你的demo程序连接到 Mavic Pro(更多详情 Run Application ) ,然后检查一下我们目前实现的所有功能。
如果一切ok,你应该可以看到一些和下面的git动画一样的东西:
- 如果demo程序和 Mavic Pro 连接成功,你应该可以看到 title 内容更新为 "MavicPro Connected"。
- 按下 Enable Virtual Stick 按钮可以启动虚拟杆控制,然后按 Start Simulator 就可以开启模拟器了。
- 此外,按 Take Off 按钮可以向飞机发送起飞命令, 如果命令执行成功,你可以看到 PosZ 值开始改变,意味着飞机在上升。
- 现在你可以拖动左右虚拟杆来模拟飞行。
- 最后,按 Land 按钮可以让飞机开始自动着陆,一旦完成,你就会发现 PosZ 的值变成 "0.00" 了。按 Stop Simulator 按钮停止模拟器,按 Disable Virtual Stick 可以禁用虚拟杆控制。
摘要
在本教程中,你已经学会了如何使用 DJISimulator 的特性基于虚拟杆控制输入的模拟环境中去模拟飞机的飞行行为,并实时显示模拟器状态(Yaw,Pitch,Roll,PosX,PosY and PosZ) 的改变。你还学习了如何使用虚拟杆控制向飞机发送虚拟杆飞行控制数据。
这个demo是一个使用 DJISimulator 的一个简单演示,为了获取更好的用户体验,你可以使用3D游戏引擎创建一个3D模拟环境,如 Unity3D ,在你的移动应用程序中展示模拟数据和飞机飞行行为(如DJI Go应用程序中的飞行模拟器)。
此外,DJISimulator 允许在持续集成环境中自动化测试(如 Jenkins ),它将帮助你处理基于 DJI-SDK 的程序测试。good luck!