Flash で AR.Drone を飛ばしてみたよ


120509

AR.Drone を Processing で操作できるようなるフレームワーク ARDroneForP5 を使って、AR.Drone を飛ばす実験に成功したため、次のステップとして Flash で作った UI から操作できないかを試してみました。( ARDroneForP5 の詳しい説明はこちら

ちなみに、こんなことやる意味があるかということについてのツッ込みはナシの方向でおねがいします…

下の動画が飛ぶ様子なのですが…
またしても狭い室内で試したのであまり飛ばせませんでしたが、一応こんな感じで飛びました。

で、実装の概要ですが、Flash と Processing 間の通信はこの記事の時に検証済みでしたので、今回も Flash と Processing をソケット(バイナリ)で接続して、Flash 側の UI 操作の結果をソケット経由で ARDroneForP5 に送るようにしました。

以下が今回使用した Processing のコードです。
(ライブラリとして ARDroneForP5 を AddFile しています)

import com.shigeodayo.ardrone.manager.*;
import com.shigeodayo.ardrone.navdata.*;
import com.shigeodayo.ardrone.utils.*;
import com.shigeodayo.ardrone.processing.*;
import com.shigeodayo.ardrone.command.*;
import com.shigeodayo.ardrone.*;

import processing.net.*;


ARDroneForP5 ardrone;

//
Server server ;
Client client ;
boolean connected ;
String rcvString;


void setup(){
  size(320, 240);
  
  ardrone=new ARDroneForP5("192.168.1.1");
  //AR.Droneに接続,操縦するために必要
  ardrone.connect();
  //AR.Droneからのセンサ情報を取得するために必要
  ardrone.connectNav();
  //AR.Droneからの画像情報を取得するために必要
  ardrone.connectVideo();
  //これを宣言すると上でconnectした3つが使えるようになる.
  ardrone.start();

  // Add Socket
  server = new Server(this, 5204) ;
}

void draw(){
  background(204);  

  //AR.Droneからの画像を取得
  PImage img=ardrone.getVideoImage(false);
  if(img==null)
    return;
  image(img, 0, 0);

  //AR.Droneからのセンサ情報を標準出力する.
  //ardrone.printARDroneInfo();
  //各種センサ情報を取得する
  float pitch=ardrone.getPitch();
  float roll=ardrone.getRoll();
  float yaw=ardrone.getYaw();
  float altitude=ardrone.getAltitude();
  float[] velocity=ardrone.getVelocity();
  int battery=ardrone.getBatteryPercentage();
  
  String attitude="pitch:"+pitch+"\nroll:"+roll+"\nyaw:"+yaw+"\naltitude:"+altitude;
  text(attitude, 20, 85);
  String vel="vx:"+velocity[0]+"\nvy:"+velocity[1];
  text(vel, 20, 140);
  String bat="battery:"+battery+" %";
  text(bat, 20, 170);
  
  //
  receivedSocket();
}


// ----- Socket Receiver ------
void receivedSocket()
{
  if (client != null) 
  {
    String msg = client.readString();
    
    if (msg != null) 
    { 
        if(msg.equals("ff"))
        {
          ardrone.forward(); // Fwd
        }
        else if(msg.equals("rew"))
        {
          ardrone.backward(); // Back
        }
        else if(msg.equals("left"))
        {
          ardrone.goLeft(); // Left
        }
        else if(msg.equals("right"))
        {
          ardrone.goRight(); // Right
        }
        else if(msg.equals("takeoff"))
        {
          ardrone.takeOff(); // Takeoff
        }
        else if(msg.equals("landing"))
        {
          ardrone.landing(); // Landing
        }
        else if(msg.equals("stop"))
        {
          ardrone.stop(); // Stop
        }
        else if(msg.equals("rot_right"))
        {
         ardrone.spinRight(); // Rot Right
        }
        else if(msg.equals("rot_left"))
        {
          ardrone.spinLeft(); // Rot Left
        }
        else if(msg.equals("up"))
        {
          ardrone.up(); // Up
        }
        else if(msg.equals("down"))
        {
          ardrone.down(); // Down
        }
        else if(msg.equals("camF"))
        {
          ardrone.setHorizontalCamera(); // Front Camera
        }
        else if(msg.equals("camFB"))
        {
          ardrone.setHorizontalCameraWithVertical(); // Front and Bottom Camera
        }
        else if(msg.equals("camB"))
        {
          ardrone.setVerticalCamera(); // Bottom Camera
        }
        else if(msg.equals("camBF"))
        {
          ardrone.setVerticalCameraWithHorizontal(); // Bottom and Front Camera
        }
     }
  }
}

void serverEvent(Server srv, Client clt) 
{
  println("connected") ;
  if (client != null) server.disconnect(client) ;
  client = clt ;
}

こんな感じで、ソケット経由でメッセージというかパラメータを取得、パラメータの値によってそれぞれ ARDroneForP5 のコマンドを叩くようにしてみました。

次に Flash 側のコードです。
( UI の部品としてMinimalComps 0.9.8 を使用しています)

package
{
	import com.bit101.components.*;
	
	import flash.display.Sprite;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.net.Socket;
	
	[SWF(width = "465", height = "465", backgroundColor = "0xCFCFCF", frameRate = "30")]
	
	public class ARDroneTest extends Sprite
	{
		
		public var bt_takeoff:PushButton;
		public var bt_landing:PushButton;
		public var bt_rotateR:PushButton;
		public var bt_rotateL:PushButton;
		
		public var bt_moveRight:PushButton;
		public var bt_moveLeft:PushButton;
		public var bt_moveFront:PushButton;
		public var bt_moveBack:PushButton;
		public var bt_moveStop:PushButton;
		
		public var bt_cameraChange:PushButton;
		
		private var _camMode:int;
		
		private var _sock:Socket;
		
		
		public function ARDroneTest()
		{
			setup();
		}
		
		// -------------- Setup ----------------------------------------------- /
		private function setup():void
		{
			// add Button
			bt_takeoff = new PushButton( this, stage.stageWidth * .5 - 50, 20, "TAKEOFF", onClick );
			bt_landing = new PushButton( this, stage.stageWidth * .5 - 50, stage.stageHeight - 40, "LANDING", onClick );
			
			bt_rotateR = new PushButton( this, bt_landing.x + 120, bt_landing.y, "ROTATION RIGHT", onClick );
			bt_rotateL = new PushButton( this, bt_landing.x - 120, bt_landing.y, "ROTATION LEFT", onClick );
			
			bt_moveStop = new PushButton( this, bt_takeoff.x, stage.stageHeight * .5 - 10, "MOVE STOP", onClick );
			bt_moveFront = new PushButton( this, bt_takeoff.x, bt_moveStop.y - 30, "MOVE FRONT", onClick );
			bt_moveBack = new PushButton( this, bt_takeoff.x, bt_moveStop.y + 30, "MOVE BACK", onClick );
			bt_moveRight = new PushButton( this, bt_takeoff.x + 110, bt_moveStop.y, "MOVE RIGHT", onClick );
			bt_moveLeft = new PushButton( this, bt_takeoff.x - 110, bt_moveStop.y, "MOVE LEFT", onClick );
			
			bt_cameraChange = new PushButton( this, stage.stageWidth * .5 - 50, bt_landing.y - 30, "CAMERA CHANGE", onClick );
			
			// init CamMode
			_camMode = 0;
			
			// Socket
			_sock = new Socket();
			_sock.connect("localhost", 5204);
			_sock.addEventListener( IOErrorEvent.IO_ERROR, onConnectError ); // Error Handling.
			
			function onConnectError( e:IOErrorEvent ):void
			{
				trace( e );
			}
			
		}
		
		
		// -------------- MouseHandler ----------------------------------------------- /
		private function onClick( e:MouseEvent ):void
		{
			var bt:PushButton = e.currentTarget as PushButton;
			
			//sendData( "test" );
			
			trace(bt);
			
			switch( bt )
			{
				case bt_takeoff:
					sendData( "takeoff" );
					break;
				case bt_landing:
					sendData( "landing" );
					break;
				case bt_rotateR:
					break;
				case bt_rotateL:
					break;
				case bt_moveStop:
					break;
				case bt_moveFront:
					break;
				case bt_moveBack:
					break;
				case bt_moveRight:
					break;
				case bt_moveLeft:
					break;
				case bt_cameraChange:
					sendData( cameraMode() );
					break;
				default :
					break;
			}
		}
		
		
		// ------------- camara change ---------------------------------------------------- /
		private function cameraMode():String
		{
			var str:String;
			
			if(_camMode > 3)
			{
				_camMode = 0;
			}
			else
			{
				_camMode ++;
			}
			
			switch( _camMode )
			{
				case 0:
					str = "camF";
					break;
				case 1:
					str = "camFB";
					break;
				case 2:
					str = "camB";
					break;
				case 3:
					str = "camBF";
					break;
				default:
					break;
			}
			
			return str;
		}
		
		
		// ------------- Socket Sender ---------------------------------------------------- /
		private function sendData( data:String ):void
		{
			try
			{
				// Wrighte
				_sock.writeUTFBytes( data );
				// Send
				_sock.flush();
			} 
			catch(e:Error) 
			{
				trace( e );	
			}
		}
	}
}

Flash 側は、ボタンがクリックされると、クリックされたボタンに割り当てたコマンドをソケットで送信するという単純なものですが、これでも一応飛ばすことに成功しました。(上記コードでは TakeOff と Landing と カメラ切り替えくらいしかコマンドを実装してませんが…w)

これでひとまず飛ばすことには成功したので、次の課題としては…

・Kinect と連動させてジェスチャーで操作できるようにしてみる
・AR.Drone のカメラから取り込んだ映像(画像)を処理する
・傾きや高度(取れるのか?)など AR.Drone の状態を取得してその値で何かを行う

などなど、Drone から取得できる情報を利用して面白いことができればと思っています。