【Androidでドローン(マルチコプター)を飛ばそう】 ドローン探索機能を実装する

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

前回はプロジェクトにARDroneSDK3を導入する方法を書きました。
今回はARDroneSDK3を使ってAndroid端末周辺に存在するドローンを探査する機能を実装します。

1-ARDroneSDK3公式ドキュメントとサンプルプロジェクト

下記からARDroneSDK3公式ドキュメントとサンプルプロジェクトができます。
Parrot BEBOP DRONE For Developers
Parrot-Developers/Samples

2-Drone探査機能の実装に使うクラス

まずは、ARDroneSDK3を使ってAndroid端末周辺に存在するドローンを探査する機能を実装します。
下記のクラスを使います。

クラス名 説明
ARDiscoveryService ドローンを探査するためのクラスです。このクラス自体はServiceを継承しています。
起動することで周辺にあるParrot社のドローンを探査し、探査済みドローンリストとして内部に保持します。
また、探査済みドローンリストが更新されるたびにAndroid内にブロードキャストを発行し知らせます。
このブロードキャストはARDroneSDK3内で提供されているARDiscoveryServicesDevicesListUpdatedReceiverで受け取ることになります。
ARDiscoveryServicesDevicesListUpdatedReceiver BroadcastReceiverを継承しています。
ARDiscoveryServiceが発行した探査済みドローンリスト更新ブロードキャストを受け取るブロードキャストレシーバーです。
実装は簡単で、ブロードキャストを受け取ると、事前に設定されているARDiscoveryServicesDevicesListUpdatedReceiverDelegate内のコールバックメソッドを呼び出します。
ARDiscoveryServicesDevicesListUpdatedReceiverDelegate コールバックインターフェースです。
ARDiscoveryServicesDevicesListUpdatedReceiverがブロードキャストを受け取ったときに呼ぶコールバックです。
探査済みドローン一覧が更新されるたびにonServicesDevicesListUpdatedが呼ばれます。
ARDiscoveryDeviceService ドローンの情報を保持するクラスです。
  • ドローンの名前
  • ドローンのID
このクラスを使うことでドローンに接続することができます。
他にも保持している情報はありますが、詳細は後のブログで書きます。
また、Parcelableインターフェースを実装しているため、Intentにセットすることができます。

3-ドローン探索機能クラスの実装

サンプルからの流用(そのままなのは内緒)します。クラス名はDroneDiscoverにします
ドローン探索機能クラスの要件は下記のとおりです。

  • 機能1:ARDiscoveryServiceを起動し、ドローン探索機能を有効にする。
  • 機能2:ARDiscoveryServicesDevicesListUpdatedReceiverに探索済みドローンリストが更新されたときに呼ばれるARDiscoveryServicesDevicesListUpdatedReceiverDelegateを設定すること
  • 機能3:ARDiscoveryServiceが発行するブロードキャストを取得するブロードキャストレシーバーであるARDiscoveryServicesDevicesListUpdatedReceiverを登録すること
  • 機能4:探索済みドローンリストが更新されたときに機能利用クラスに対するコールバックを行うこと
  • 機能5:ARDiscoveryServiceを停止し、ドローン探索機能を無効にする。

4-実装1(初期化部)

まず、コンストラクタで各変数を初期化します。
特に重要な処理はブロードキャストレシーバー経由で探知済みドローンリストが更新されたときに呼ばれるARDiscoveryServicesDevicesListUpdatedReceiverをインスタンス化です。
このクラスは探知済みドローンリストが更新されたときに発行されるブロードキャストを受信するレシーバークラスです。
また、ブロードキャストを受信したときにARDiscoveryServicesDevicesListUpdatedReceiverはARDiscoveryServicesDevicesListUpdatedReceiverDelegate#onServicesDevicesListUpdatedを実行します。
今回、onServicesDevicesListUpdatedの実装ではARDiscoveryService#getDeviceServicesArrayを呼び探知済みドローンリストを取得します。
notifyServiceDiscoveredについて後述します。

    /**
    * コンテキスト
    */
    private final Context mContext;

    /**
    * リスナーリスト
    */
    private final List<Listener> mListeners;

    /**
    * 発見した周囲のドローン一覧
    */
    private final List&lt;ARDiscoveryDeviceService&gt; mMatchingDrones;

    /**
    * ドローン発見時のブロードキャストレシーバー
    */
    private final ARDiscoveryServicesDevicesListUpdatedReceiver mArDiscoveryServicesDevicesListUpdatedReceiver;

    /**
    * ブロードキャストレシーバー更新時のコールバックインターフェース
    */
    private final ARDiscoveryServicesDevicesListUpdatedReceiverDelegate

    mArDiscoveryServicesDevicesListUpdatedReceiverDelegate = new ARDiscoveryServicesDevicesListUpdatedReceiverDelegate() {

        @Override
        public void onServicesDevicesListUpdated() {
            if (mArDiscoveryService != null) {
                // 検索済みのドローン一覧を削除
                mMatchingDrones.clear();
                //Service内で保持している検知したドローン一覧を取得する。
                List<ARDiscoveryDeviceService> deviceList = mArDiscoveryService.getDeviceServicesArray();
                //検知したドローンが存在した場合
                if (deviceList != null) {
                    //検知したドローンを検知したドローン一覧に追加する
                    for (ARDiscoveryDeviceService service : deviceList) {
                        mMatchingDrones.add(service);
                    }
                }
                //検知したドローン一覧をコールバックする
                notifyServiceDiscovered(mMatchingDrones);
            }
        }
    };

    /**
    * 接続用コネクション
    */
    private ServiceConnection mServiceConnection;

    /**
    * 周囲のドローンを探すService
    */
    private ARDiscoveryService mArDiscoveryService;

    /**
    * コンストラクタ
    * @param context コンテキスト
    */
    public DroneDiscover(Context context) {
        mContext = context;
        mListeners = new ArrayList&lt;&gt;();
        mMatchingDrones = new ArrayList&lt;&gt;();
        //レシーバーの作成、コンストラクタの引数にコールバックインターフェースを渡す。
        mArDiscoveryServicesDevicesListUpdatedReceiver =
            new ARDiscoveryServicesDevicesListUpdatedReceiver(mArDiscoveryServicesDevicesListUpdatedReceiverDelegate);
    }

 

5-実装2(探索機能の開始)

まず、ARDiscoveryServiceを登録します。
登録するときに使うIntentFilterのactionはARDiscoveryService#kARDiscoveryServiceNotificationServicesDevicesListUpdatedを使用します。
ARDiscoveryServiceを起動します。ServiceConnectionを使ったServiceの起動方法については説明を省略します。
ServiceConnection#onServiceConnectedの中でドローン探索機能を有効にするstartDiscoveringメソッドを呼びます。
startDiscoveringはARDiscoveryService#startを呼ぶことでドローン探索機能が有効にするメソッドです。

    /**
    * 探索開始フラグ
    */
    private boolean mStartDiscoveryAfterConnection;

    /**
    * クラスのセットアップをする
    */
    public void setup() {
        // ARDiscoveryService(ブロードキャストレシーバー)を登録する
        LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(mContext);
        localBroadcastMgr.registerReceiver(mArDiscoveryServicesDevicesListUpdatedReceiver,
            new IntentFilter(ARDiscoveryService.kARDiscoveryServiceNotificationServicesDevicesListUpdated));
        // ServiceConnectionを使ってServiceのライフサイクルコールバックを受け取る
        if (mServiceConnection == null) {
            mServiceConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    // 起動したServiceのインスタンスを取得する。
                    mArDiscoveryService = ((ARDiscoveryService.LocalBinder) service).getService();
                    if (mStartDiscoveryAfterConnection) {
                        //検知を開始する。
                        startDiscovering();
                        mStartDiscoveryAfterConnection = false;
                    }
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    mArDiscoveryService = null;
                }
            };
        }

        //Serviceが起動していない場合は起動する
        if (mArDiscoveryService == null) {
            // ARDiscoveryServiceを起動する。
            Intent i = new Intent(mContext, ARDiscoveryService.class);
            mContext.bindService(i, mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    }

    /**
    * ドローン探索機能を有効にする。
    * ドローンが発見されたときは,{@link Listener#onDronesListUpdated(List)}にコールバックする
    */
    public void startDiscovering() {
        if (mArDiscoveryService != null) {
            // ドローン検知Serviceを開始する
            mArDiscoveryServicesDevicesListUpdatedReceiverDelegate.onServicesDevicesListUpdated();
            mArDiscoveryService.start();
            mStartDiscoveryAfterConnection = false;
        } else {
            mStartDiscoveryAfterConnection = true;
        }
    }

 

6-実装3(コールバック機能の実装)

コールバックインターフェースのListenerを定義します。
このインターフェースには探索済みドローンリストが更新されたときにコールバックするためのonDronesListUpdatedメソッドがあります。
notifyServiceDiscoveredは登録されたすべてのコールバックインターフェースのonDronesListUpdatedを実行します。
onDronesListUpdatedは引数に探索済みドローンリストを取ります。

    /**
    * 発見したドローンリストが更新されたときに呼ばれるコールバックリスナー
    */
    public interface Listener {
        /**
        * 周囲に存在するドローンの情報(ARDiscoveryDeviceService)リストを
        * 返却する。
        *
        * @param dronesList 周囲に存在するドローンの情報(ARDiscoveryDeviceService)リストを
        */
        void onDronesListUpdated(List<ARDiscoveryDeviceService> dronesList);
    }

    /**
    * コールバックインターフェースの追加する
    *
    * @param listener コールバックインターフェース
    */
    public void addListener(Listener listener) {
        mListeners.add(listener);
        notifyServiceDiscovered(mMatchingDrones);
    }

    /**
    * コールバックインターフェースの削除する
    *
    * @param listener コールバックインターフェース
    */
    public void removeListener(Listener listener) {
        mListeners.remove(listener);
    }

    /**
    * 検知ドローン一覧をコールバックする。
    * @param dronesList 検知ドローン一覧
    */
    private void notifyServiceDiscovered(List<ARDiscoveryDeviceService> dronesList) {
        List<Listener> listenersCpy = new ArrayList<>(mListeners);
        for (Listener listener : listenersCpy) {
            listener.onDronesListUpdated(dronesList);
        }
    }

 

7-実装4(探索機能の停止)

まずはARDiscoveryServiceの探索機能を停止します。
次にARDiscoveryServiceをアンバインドしServiceを停止します。
最後にArDiscoveryServicesDevicesListUpdatedReceiverをレシーバーを解除します。

    /**
    * 後始末をする
    */
    public void cleanup() {
        //検知を止める
        stopDiscovering();
        if (mArDiscoveryService != null) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //Serviceを止める
                    mArDiscoveryService.stop();
                    mContext.unbindService(mServiceConnection);
                    mArDiscoveryService = null;
                }
            }).start();
        }
        // ARDiscoveryService(ブロードキャストレシーバー)を解除する
        LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(mContext);
        localBroadcastMgr.unregisterReceiver(mArDiscoveryServicesDevicesListUpdatedReceiver);
    }

    /**
    * ドローン検知機能を無効にする。
    */
    public void stopDiscovering() {
        if (mArDiscoveryService != null) {
            Log.i(TAG, "Stop discovering");
            mArDiscoveryService.stop();
        }
        mStartDiscoveryAfterConnection = false;
    }

 

8-まとめ

今回はDrone探査機能を実現するためにDroneDiscoverを実装しました。
次回はこのクラスを使ってAndroid端末の周辺に存在するドローンを探索します。

    /**
    * ドローン探索機能
    */
    public class DroneDiscover {
        private static final String TAG = "DroneDiscoverer";

        /**
        * 発見したドローンリストが更新されたときに呼ばれるコールバックリスナー
        */
        public interface Listener {

            /**
            * 周囲に存在するドローンの情報(ARDiscoveryDeviceService)リストを
            * 返却する。
            *
            * @param dronesList 周囲に存在するドローンの情報(ARDiscoveryDeviceService)リストを
            */
            void onDronesListUpdated(List<ARDiscoveryDeviceService> dronesList);
        }

        /**
        * コンテキスト
        */
        private final Context mContext;

        /**
        * リスナーリスト
        */
        private final List&lt;Listener&gt; mListeners;

        /**
        * 発見した周囲のドローン一覧
        */
        private final List&lt;ARDiscoveryDeviceService&gt; mMatchingDrones;

        /**
        * ドローン発見時のブロードキャストレシーバー
        */
        private final ARDiscoveryServicesDevicesListUpdatedReceiver mArDiscoveryServicesDevicesListUpdatedReceiver;

        /**
        * ブロードキャストレシーバー更新時のコールバックインターフェース
        */
        private final ARDiscoveryServicesDevicesListUpdatedReceiverDelegate
            mArDiscoveryServicesDevicesListUpdatedReceiverDelegate = new ARDiscoveryServicesDevicesListUpdatedReceiverDelegate() {

            @Override
            public void onServicesDevicesListUpdated() {
                if (mArDiscoveryService != null) {
                    // 検索済みのドローン一覧を削除
                    mMatchingDrones.clear();
                    //Service内で保持している検知したドローン一覧を取得する。
                    List<ARDiscoveryDeviceService> deviceList = mArDiscoveryService.getDeviceServicesArray();
                    //検知したドローンが存在した場合
                    if (deviceList != null) {
                        //検知したドローンを検知したドローン一覧に追加する
                        for (ARDiscoveryDeviceService service : deviceList) {
                            mMatchingDrones.add(service);
                            Log.d("you",service.toString());
                        }
                    }
                    //検知したドローン一覧をコールバックする
                    notifyServiceDiscovered(mMatchingDrones);
                }
            }
        };

        /**
        * 接続用コネクション
        */
        private ServiceConnection mServiceConnection;

        /**
        * 周囲のドローンを探すService
        */
        private ARDiscoveryService mArDiscoveryService;

        /**
        * コンストラクタ
        * @param context コンテキスト
        */
        public DroneDiscover(Context context) {
            mContext = context;
            mListeners = new ArrayList<>();
            mMatchingDrones = new ArrayList<>();
            //レシーバーの作成
            mArDiscoveryServicesDevicesListUpdatedReceiver =
                new ARDiscoveryServicesDevicesListUpdatedReceiver(mArDiscoveryServicesDevicesListUpdatedReceiverDelegate);
        }

        private boolean mStartDiscoveryAfterConnection;

        /**
        * クラスのセットアップをする
        */
        public void setup() {
            // ARDiscoveryService(ブロードキャストレシーバー)を登録する
            LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(mContext);
            localBroadcastMgr.registerReceiver(mArDiscoveryServicesDevicesListUpdatedReceiver,
                new IntentFilter(ARDiscoveryService.kARDiscoveryServiceNotificationServicesDevicesListUpdated));
            // ServiceConnectionを使ってServiceのライフサイクルコールバックを受け取る
            if (mServiceConnection == null) {
                mServiceConnection = new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        // 起動したServiceのインスタンスを取得する。
                        mArDiscoveryService = ((ARDiscoveryService.LocalBinder) service).getService();
                        if (mStartDiscoveryAfterConnection) {
                            //検知を開始する。
                            startDiscovering();
                            mStartDiscoveryAfterConnection = false;
                        }
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        mArDiscoveryService = null;
                    }
                };
            }
            //Serviceが起動していない場合は起動する
            if (mArDiscoveryService == null) {
                // ARDiscoveryServiceを起動する。
                Intent i = new Intent(mContext, ARDiscoveryService.class);
                mContext.bindService(i, mServiceConnection, Context.BIND_AUTO_CREATE);
            }
        }

        /**
        * ドローン検知機能を有効にする。
        * ドローンが発見されたときは,{@link Listener#onDronesListUpdated(List)}にコールバックする
        */
        public void startDiscovering() {
            if (mArDiscoveryService != null) {
                // ドローン検知Serviceを開始する
                mArDiscoveryServicesDevicesListUpdatedReceiverDelegate.onServicesDevicesListUpdated();
                mArDiscoveryService.start();
                mStartDiscoveryAfterConnection = false;
            } else {
                mStartDiscoveryAfterConnection = true;
            }
        }

        /**
        * 後始末をする
        */
        public void cleanup() {
            //検知を止める
            stopDiscovering();
            if (mArDiscoveryService != null) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //Serviceを止める
                        mArDiscoveryService.stop();
                        mContext.unbindService(mServiceConnection);
                        mArDiscoveryService = null;
                    }
                }).start();
            }
            // ARDiscoveryService(ブロードキャストレシーバー)を解除する
            LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(mContext);
            localBroadcastMgr.unregisterReceiver(mArDiscoveryServicesDevicesListUpdatedReceiver);
        }

        /**
        * ドローン検知機能を無効にする。
        */
        public void stopDiscovering() {
            if (mArDiscoveryService != null) {
                Log.i(TAG, "Stop discovering");
                mArDiscoveryService.stop();
            }
            mStartDiscoveryAfterConnection = false;
        }

        /**
        * コールバックインターフェースの追加する
        *
        * @param listener コールバックインターフェース
        */
        public void addListener(Listener listener) {
            mListeners.add(listener);
            notifyServiceDiscovered(mMatchingDrones);
        }

        /**
        * コールバックインターフェースの削除する
        *
        * @param listener コールバックインターフェース
        */
        public void removeListener(Listener listener) {
            mListeners.remove(listener);
        }

        /**
        * 検知ドローン一覧をコールバックする。
        * @param dronesList 検知ドローン一覧
        */
        private void notifyServiceDiscovered(List<ARDiscoveryDeviceService> dronesList) {
            List<Listener> listenersCpy = new ArrayList<>(mListeners);
            for (Listener listener : listenersCpy) {
                listener.onDronesListUpdated(dronesList);
            }
        }
    }

 

【国内正規品】Parrot Minidrones Airborne Night Blaze(レッド)ドローン規制対象外200g 未満・パリデザイン・LED 付き自動安定ホバーリングクアッドコプター ・30万画素カメラ・簡単にアクロバット・スマホ・タブレットで操作 PF723132

新品価格
¥17,150から
(2017/2/28 00:50時点)


Parrot Airborne Cargo MiniDrone - Mars (White) by Parrot

新品価格
¥22,692から
(2017/2/28 00:53時点)


Parrot MiniDrones Airborne Night Drone Maclane (Blue) [並行輸入品]

新品価格
¥12,000から
(2017/2/28 00:54時点)


【国内正規品】Parrot Bebop 2 ホワイト Full HD/30fps 魚眼レンズ カメラ・25分飛行時間・8GB 内部フラッシュメモリー・自動安定ホバーリング・最高水平速度:60KM/H・パリデザイン・GNSS (GPS GLONASS)付・クワッドコプター PF726073

新品価格
¥65,701から
(2017/2/28 00:57時点)


 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)