您的位置:首页 > 移动开发 > Android开发

基于javadrone api实现ARDrone Android控制

2014-07-30 21:42 316 查看
需要的资料

javadrone api: http://code.google.com/p/javadrone

使用javadrone api的一个Android demo(ARDrone-on-Android-master): https://github.com/UnknownGuardian/ARDrone-on-Android

修改demo

Demo使用了javadrone
api中的ardrone、ardrone/comands、ardrone/video以及ardrone/util的一部分api,并使用了http://code.google.com/p/javadrone中javadrone-93e27c36a077.zip的中的3个.jar包(具体哪3个包见demo的.classpath文件)。我的目的是修改该demo从而做到Android对ARDrone的基本控制。

Demo中,其实不需要使用javadrone-93e27c36a077.zip的中的3个.jar包也能顺利build通过,所以直接在demo的.classpath文件中删除这3个包的路径,这样demo和javadrone-93e27c36a077.zip就没有依赖关系了;其次,ardrone/util中BufferedImageVideoListener.java引用了Android
SDK的一个早期的包java.awt.image.BufferedImage,所以该文件始终编译不过,由于目标app暂时不需要处理ardrone的video,所直接把ardrone/util从demo从工程中删除。做完这些修改后build通过,手机上测试该demo,发现按下连接ARDrone的按钮AnDrone有相应的反应(LED如代码中的闪烁),连接上后电池状态能获取到(说明Navadata读取正常),但视频显示是没有的,起飞按钮也很少有效果(好像有效过1次)。

Demo中考虑了视频接收,并增加了一些进度条、图像等控件,这在我的目标里是不需要的,而这些代码可能会影响到控制命令的发送,所以我将demo作一些修改以去掉这些内容。首先修改app界面main.xml,



<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="@string/hello"

android:id="@+id/statusBar"

android:gravity="center_horizontal"

android:background="#B1DCFE"

android:textColor="#000000">

</TextView>

<Button

android:id="@+id/connectButton"

android:text="Connect..."

android:layout_width="match_parent"

android:layout_height="wrap_content"></Button>

<Button

android:id="@+id/launchButton"

android:text="Take off"

android:layout_width="match_parent"

android:layout_height="wrap_content"></Button>

<Button

android:id="@+id/hoverButton"

android:text="Hover"

android:layout_width="match_parent"

android:layout_height="wrap_content"></Button>



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal" >

<Button

android:id="@+id/frontButton"

android:text="Front"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

<Button

android:id="@+id/backButton"

android:text="Back"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

</LinearLayout>



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal" >

<Button

android:id="@+id/leftButton"

android:text="Left"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

<Button

android:id="@+id/rightButton"

android:text="Right"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

</LinearLayout>



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal" >

<Button

android:id="@+id/upButton"

android:text="Up"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

<Button

android:id="@+id/downButton"

android:text="Down"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

</LinearLayout>



<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal" >

<Button

android:id="@+id/spin_leftButton"

android:text="Spin Left"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

<Button

android:id="@+id/spin_rightButton"

android:text="Spin Right"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_weight="1"></Button>

</LinearLayout>



<TextView

android:id="@+id/batteryStatusText"

android:gravity="center_horizontal"

android:text="Battery Status"

android:textAppearance="?android:attr/textAppearanceSmall"

android:layout_width="match_parent"

android:layout_height="wrap_content"></TextView>

</LinearLayout>

修改后的界面比较简洁,除了一系列按钮就是2个文本框。接下来修改AndroidManifest.xml,



<?xml version="1.0" encoding="utf-8"?>

<!-- package="com.codeminders.ardrone.examples" android:versionCode="1" -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.profusiongames" android:versionCode="1"

android:versionName="1.0">

<uses-sdk android:minSdkVersion="8"/>

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".FusionDrone"

android:label="@string/app_name"

android:configChanges="orientation|keyboard"

android:screenOrientation="portrait"

android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen">



<intent-filter>

<action android:name="android.intent.action.MAIN" />

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

</intent-filter>

</activity>

</application>

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />

<uses-permission android:name="android.permission.BATTERY_STATS" />

<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

<uses-permission android:name="android.permission.CALL_PHONE" />

<uses-permission android:name="android.permission.CAMERA" />

<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />

<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />

<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />

<uses-permission android:name="android.permission.DUMP" />

<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />

<uses-permission android:name="android.permission.FLASHLIGHT" />

<uses-permission android:name="android.permission.GET_ACCOUNTS" />

<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />

<uses-permission android:name="android.permission.GET_TASKS" />

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />

<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

<uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

<uses-permission android:name="android.permission.READ_CALENDAR" />

<uses-permission android:name="android.permission.READ_CONTACTS" />

<uses-permission android:name="android.permission.READ_LOGS" />

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<uses-permission android:name="android.permission.READ_SMS" />

<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />

<uses-permission android:name="android.permission.READ_SYNC_STATS" />

<uses-permission android:name="android.permission.RECEIVE_MMS" />

<uses-permission android:name="android.permission.RECEIVE_SMS" />

<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />

<uses-permission android:name="android.permission.RECORD_AUDIO" />

<uses-permission android:name="android.permission.REORDER_TASKS" />

<uses-permission android:name="android.permission.SEND_SMS" />

<uses-permission android:name="android.permission.SET_ALWAYS_FINISH" />

<uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />

<uses-permission android:name="android.permission.SET_DEBUG_APP" />

<uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />

<uses-permission android:name="android.permission.SET_TIME_ZONE" />

<uses-permission android:name="android.permission.SET_WALLPAPER" />

<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />

<uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" />

<uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" />

<uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" />

<uses-permission android:name="android.permission.USE_CREDENTIALS" />

<uses-permission android:name="android.permission.VIBRATE" />

<uses-permission android:name="android.permission.WAKE_LOCK" />

<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />

<uses-permission android:name="android.permission.WRITE_CALENDAR" />

<uses-permission android:name="android.permission.WRITE_CONTACTS" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

<uses-permission android:name="android.permission.WRITE_SMS" />

<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

</manifest>

主要修改了android:screenOrientation项(landscape->portrait),使app界面从横屏变为竖屏。最后,修改的是响应控件的java代码FusionDrone.java,



package com.profusiongames;

import java.io.IOException;

import android.app.Activity;

import android.hardware.Sensor;

import android.hardware.SensorEvent;

import android.hardware.SensorEventListener;

import android.hardware.SensorManager;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.Window;

import android.widget.Button;

//import android.widget.ProgressBar;

import android.widget.TextView;

import com.codeminders.ardrone.ARDrone;

import com.codeminders.ardrone.NavData;

import com.codeminders.ardrone.NavDataListener;

public class FusionDrone extends Activity implements NavDataListener,SensorEventListener {

private static FusionDrone fDrone;

private static ARDrone drone;

private static SensorManager sensorManager;

private static boolean isConnected = false;

private static boolean isFlying = false;

private int batteryLife = 0;

public static int queueToShow = 0;

/* Components */

@SuppressWarnings("unused")

private TextView statusBar;

private Button connectionButton;

private Button launchButton;

private Button hoverButton;

private Button frontButton;

private Button backButton;

private Button leftButton;

private Button rightButton;

private Button upButton;

private Button downButton;

private Button spin_leftButton;

private Button spin_rightButton;

private TextView batteryText;

/* Components */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.main);

fDrone = this;

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

getUIComponents();

}



@Override

protected void onResume() {

super.onResume();

sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), 3);

}



@Override

protected void onPause() {

super.onPause();

sensorManager.unregisterListener(this);

}



@Override

protected void onStop() {

super.onStop();

sensorManager.unregisterListener(this);

try {

if(drone != null)

{

drone.clearImageListeners();

drone.clearNavDataListeners();

drone.clearStatusChangeListeners();

drone.clearStatusChangeListeners();

drone.disconnect();

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}



private void getUIComponents() {

statusBar = (TextView) findViewById(R.id.statusBar);

connectionButton = (Button) findViewById(R.id.connectButton);

connectionButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Connection button");

if (!isConnected) {

connectionButton.setEnabled(false);

connectionButton.setText("Connecting...");

(new DroneStarter()).execute(FusionDrone.drone);

} else {

if(isFlying) { try { drone.land(); Thread.sleep(400);}
catch (Exception e) {e.printStackTrace();}
} //if going to disconnect, but still flying, attempt to tell drone to land
connectionButton.setEnabled(false);

connectionButton.setText("Disconnecting...");

(new DroneEnder()).execute(FusionDrone.drone);

}

}

});



launchButton = (Button)findViewById(R.id.launchButton);

launchButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Launch button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.land(); launchButton.setText("Takeoff"); isFlying = false;}

catch (IOException e) {e.printStackTrace();}

} else {

try { drone.takeOff(); launchButton.setText("Land"); isFlying = true;}

catch (IOException e) {e.printStackTrace();}

}

}

});



hoverButton = (Button)findViewById(R.id.hoverButton);

hoverButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Hover button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.hover();}

catch (IOException e) {e.printStackTrace();}

}

}

});



frontButton = (Button)findViewById(R.id.frontButton);

frontButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Front button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)-0.05,(float)0.0,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



backButton = (Button)findViewById(R.id.backButton);

backButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Back button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)0.05,(float)0.0,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



leftButton = (Button)findViewById(R.id.leftButton);

leftButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Left button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)-0.05,(float)0.0,(float)0.0,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



rightButton = (Button)findViewById(R.id.rightButton);

rightButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Right button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.05,(float)0.0,(float)0.0,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



upButton = (Button)findViewById(R.id.upButton);

upButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Up button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)0.0,(float)0.05,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



downButton = (Button)findViewById(R.id.downButton);

downButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Down button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)0.0,(float)-0.05,(float)0.0);}

catch (IOException e) {e.printStackTrace();}

}

}

});



spin_leftButton = (Button)findViewById(R.id.spin_leftButton);

spin_leftButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Spin Left button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)0.0,(float)0.0,(float)-0.05);}

catch (IOException e) {e.printStackTrace();}

}

}

});



spin_rightButton = (Button)findViewById(R.id.spin_rightButton);

spin_rightButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

Log.v("DRONE", "Clicked Spin Right button");

if (!isConnected) {

//do nothing

} else if(isFlying) {

try { drone.move((float)0.0,(float)0.0,(float)0.0,(float)0.05);}

catch (IOException e) {e.printStackTrace();}

}

}

});



batteryText = (TextView) findViewById(R.id.batteryStatusText);

}

/*public static ARDrone getARDrone() {

return drone;

}

public static FusionDrone getFusionDrone() {

return fDrone;

}*/



@Override

public void navDataReceived(NavData nd) {

//NavData.printState(nd);

//Log.v("DRONE", nd.getVisionTags().toString());

if(nd.getVisionTags() != null)

{

Log.v("DRONE", nd.getVisionTags().toString());

}

batteryLife = nd.getBattery();

runOnUiThread(new Runnable() {

public void run() {

batteryText.setText("Battery Life: " + batteryLife + "%");

}

});

}



/********************* The following is a must***********************/

private float sensorThreshold = 3;

private double startX = -1f;

private double startY = -1f;

private double startZ = -1f;

@Override

public void onSensorChanged(SensorEvent e) {

if(Math.random() < 1) return;

Log.v("DRONE", "sensor: " + e.sensor + ", x: " + MathUtil.trunk(e.values[0]) + ", y: " + MathUtil.trunk(e.values[1]) + ", z: " + MathUtil.trunk(e.values[2]));

if(startX == -1f)

{

startX = e.values[0];

startY = e.values[1];

startZ = e.values[2];

}

float shortX = MathUtil.trunk(MathUtil.getShortestAngle(e.values[0],(float) startX));

float shortY = MathUtil.trunk(MathUtil.getShortestAngle(e.values[1],(float) startY));

float shortZ = MathUtil.trunk(MathUtil.getShortestAngle(e.values[2],(float) startZ));

if( MathUtil.abs(shortX) < sensorThreshold) shortX = 0;// do nothing

if( MathUtil.abs(shortY) < sensorThreshold) shortY = 0;// do nothing

if( MathUtil.abs(shortZ) < sensorThreshold) shortZ = 0;// do nothing

Log.v("DRONE", "sensor difference: x: " + shortX + ", y: " + shortY + ", z: " + shortZ);

}



@Override

public void onAccuracyChanged(Sensor sensor, int accuracy) {

// TODO Auto-generated method stub

//Log.v("DRONE", "Accuracy changed: " + accuracy);



}

/*********************************************************************/



private class DroneStarter extends AsyncTask<ARDrone, Integer, Boolean> {

private static final int CONNECTION_TIMEOUT = 3000;

@Override

protected Boolean doInBackground(ARDrone... drones) {

ARDrone drone = drones[0];

try {

drone = new ARDrone();

FusionDrone.drone = drone; // passing in null objects will not pass object refs

drone.connect();

drone.clearEmergencySignal();

drone.waitForReady(CONNECTION_TIMEOUT);

drone.playLED(1, 10, 4);

drone.addNavDataListener(FusionDrone.fDrone);

//drone.selectVideoChannel(ARDrone.VideoChannel.HORIZONTAL_ONLY);

//try {

//drone.sendTagDetectionOnData();

//drone.sendVideoOnData();

//drone.enableAutomaticVideoBitrate();

//}

//catch(Exception e) { e.printStackTrace();}

Log.v("DRONE", "Connected to ARDrone" + FusionDrone.drone);

return true;

} catch (Exception e) {

e.printStackTrace();

try {

drone.clearEmergencySignal();

//drone.clearImageListeners();

drone.clearNavDataListeners();

drone.clearStatusChangeListeners();

drone.disconnect();

} catch (Exception e1) {

e1.printStackTrace();

}

Log.v("DRONE", "Caught exception. Connection time out." + FusionDrone.drone);

}

return false;

}

protected void onPostExecute(Boolean success) {

if (success.booleanValue()) {

connectionButton.setText("Disconnect...");

} else {

connectionButton.setText("Error 1. Retry?");

}

isConnected = success.booleanValue();

connectionButton.setEnabled(true);

//connectionWhirlProgress.setVisibility(Button.INVISIBLE);

}

}





private class DroneEnder extends AsyncTask<ARDrone, Integer, Boolean> {

@Override

protected Boolean doInBackground(ARDrone... drones) {

ARDrone drone = drones[0];

try {

drone.playLED(2, 10, 4);

drone.clearEmergencySignal();

//drone.clearImageListeners();

drone.clearNavDataListeners();

drone.clearStatusChangeListeners();

drone.disconnect();

Log.v("DRONE", "Disconnected to ARDrone" + FusionDrone.drone);

return true;

} catch (Exception e) {

e.printStackTrace();

try {

drone.clearEmergencySignal();

//drone.clearImageListeners();

drone.clearNavDataListeners();

drone.clearStatusChangeListeners();

drone.disconnect();

} catch (Exception e1) {

e1.printStackTrace();

}

Log.v("DRONE", "Caught exception. Disconnection error.");

}

return false;

}

protected void onPostExecute(Boolean success) {

if (success.booleanValue()) {

connectionButton.setText("Connect...");

connectionButton.setEnabled(true);

batteryText.setText("Battery Status");

} else {

connectionButton.setText("Error 2. Retry?");

}

isConnected = !success.booleanValue();

}

}



}

代码中,按键响应和Navadata接受保留下来并做扩充,视频和其他控件的内容一律删除掉。

这下,整个代码就整洁很多了,build通过后,在真机上测试,效果比原来demo好多了(demo只有连接ardrone按键比较灵敏,其他都基本没效果;现在按键基本都有相应的效果了)。

参考:

http://code.google.com/p/javadrone/source/browse/src/com/codeminders/ardrone/examples/RotationSnapshotTaker.java?r=faeae1f020fa6399b5261e8d3fd6e1a44be9f834(一个javadrone控制api的使用例子)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: