大疆文档(8)-Android教程-模拟器App

本节全篇为大疆 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

当你触摸 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.pngjoystick_bg.png 文件到 mipmap 文件夹(as3.3.2 是 mipmap-xxxhdpi):

joystickImages

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.setOnCheckedChangeListeneronCheckedChanged() 方法。 最后,实现两个 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) ,并设置 textOntextOff 参数为 "Start Simulator" 和 "Stop Simulator"。

最后,我们为操作杆控制创建两个 OnScreenJoystick 元素(id: directionJoystickRight) 和 (id:directionJoystickLeft)。

4. 配置资源文件

完成上述步骤后,我们添加一些资源文件到 res 文件夹。

从教程示例程序的 drawable 文件夹下复制下图中的 image 和 xml文件 到你的项目中,他们可用于 button UI:

drawable

接下来,打开 "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, DJISimulatorApplicationMainActivity 的详细实现,可以看看这个教程 Creating an Camera Application ,还有这个 sample project

现在让我们构建运行程序,然后安装到你的 android 设备上。如果一切ok,那么当你注册成功的时候应该可以看到 "success" 。

registerSuccess

实现 MainActivity 类

更新产品连接状态

更详细的实现看本教程的示例代码: sample project

现在,我们构建并运行项目,然后安装到你的Android设备上。然后把demo连接到你的Mavic Pro上 (不会看看这个 Run Application ),如果一切ok,你应该可以看到 title 内容更新为 "MavicPro Connected" :

registerSuccess

实现虚拟杆控制

由于我们实现了虚拟杆控制, 现在我们可以继续发送虚拟杆飞行控制数据到飞机。首先,我们在 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, mYawmThrottle 变量去存储 pitch, roll, yawvertical 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, mYawmThrottle

完成上述步骤后, 让我们在 initUI() 方法底部实现 mScreenJoystickLeftmScreenJoystickRight 变量的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. 重写 setJoystickListeneronTouch() 方法,并过滤低于 0.02的 pXpY 变量的值。如果值太小,我们应该不会太频繁的发送虚拟杆数据到飞行控制器

2. 获取 pitch 和 roll 控制的最大速度,然后把它们存储到 pitchJoyControlMaxSpeedrollJoyControlMaxSpeed 变量。由于 pX 的值在 -1 (left) 和 1 (right) 之间, pY 的值在 -1 (down) 和 1 (up) 之间,我们使用 pitchJoyControlMaxSpeedrollJoyControlMaxSpeed 值相乘来更新 mPitchmRoll 的数据。这里我们以遥控器的模式2(美国模式)为例。

3. 最后,我们检查 mSendVirtualStickDataTimer 是否为null, 并通过调用 SendVirtualStickDataTask() 方法来创建它。然后创建 mSendVirtualStickDataTimer 并通过传递 mSendVirtualStickDataTask 变量,延迟0毫秒,后续执行间隔200毫秒这3个参数来调用它的 schedule(TimerTask task, long delay, long period) 方法去触发计时器 。

4. 同样的,实现 mScreenJoystickRight 变量的 setJoystickListener() 方法去更新 mYawmThrottle 的值,并调用计时器去发送虚拟杆数据到飞机的飞行控制器。

现在,当你控制左右操纵杆的时候,将发送模拟虚拟杆数据 (包括 Yaw, Pitch, RollVertical 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() 方法来获取最新模拟器状态数据,然后调用 SimulatorStategetYaw(), 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 offLand 按钮的点击操作:

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动画一样的东西:

img
  • 如果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!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容