ARスイッチで家電操作!

NTTドコモ移動機開発部の田中です。普段の業務ではXR（AR,VR,MR）関連の研究開発を行っています。
[AdventCalendar2018](https://qiita.com/advent-calendar/2018/nttdocomo_service_innovation)では、[IoT関連の記事](https://qiita.com/dcm_tanaka/items/02f44af8a2ac7e2e041a)を書かせていただきました。
今回はリアルとバーチャルを絡めた記事を書きたいと思い、ARスイッチを考えついたので、ハンズオン形式で作成する内容とさせていただきました。

# はじめに
- 本記事はプログラミング初級者向けです。ハンズオン形式で実際にARリモコンを作っていきます。
- 本アプリはUnityで作ります。AR描画機能は簡単化のためVuforia[[1]](https://developer.vuforia.com/)を使い、家電操作にはOSSのDeviceConnect[[2]](https://github.com/DeviceConnect/DeviceConnect-Android)を用います。
- 家電ですが、何がいいかなと悩みました。皆さんお持ちのスマートフォンを活用できないかなと思い、今回、スマホのライトを照明器具と見立て、ライトのON/OFFを操作するARリモコン（検証アプリ）を作っていきたいと思います。

- イメージ
![intro.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/15dc30b9-8baa-b829-af73-7332ae7ac36a.jpeg)

### 補足
- 本記事を参考にした開発、SDK利用、プログラムの実行については自己責任でお願いいたします。
- Vuforiaに関しては、2020年12月現在、非商用・開発検証目的であれば無償ライセンスで利用できます。本記事では検証目的でVuforiaを扱います。

# ハンズオン（ARリモコンの作成）
- 作成手順は大きく3ステップです。AR機能実装、デバイス制御実装、AR機能とデバイス制御の結合、の順に作っていきます。
- 今回は家電＝スマホライト、と見立ておりますが、DeviceConnectに対応した家電であれば本当の意味での家電（照明器具など）も操作可能です。

## STEP0.　必要なもの
- 【開発環境】　
    - Unity＠開発用PC　　（筆者環境は、Unity2019.4.10f1）　
- 【実機、その他必要なもの】
    - Androidスマートフォン（家電のポジションです）
    - PC用のWebカメラ（もしくは2台目のスマホを用意し、アプリをスマホで動かすでもOKです。）
    - ARマーカ（プリンタで印刷。後ほどサンプルのマーカを記載します。）

## STEP1.　Vuforiaの設定・実装
- ※Unity開発環境がある前提で進めます。
- Unity上で新規シーンを作成いただき、Vuforiaの設定を行います。
- 以下の手順でVuforiaをUnityにインストールしてください。
    - ①「Window→PackageManager」を選択
    - ②Vuforia Engine ARを選択　※筆者環境は「Vuforia Engine AR 8.1.12」
    - ③Installを実行
![vuforia1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/d7ebdc94-f2ca-dfcb-3664-60f98a23244d.png)

- 次に、AR表示用のカメラへの切り替えと、マーカオブジェクトの設置を行います。
    - ①Main Cameraは不要なので削除
    - ②「右クリック→VuforiaEngine」から、AR Cameraオブジェクトを選択
![vuforia2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/4189dd2c-780c-1d71-fe99-27ee1fd6970d.png)

- まずはデフォルトのARマーカを設置します。
    - 「右クリック→VuforiaEngine」から、Imageを選択
![vuforia3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/941aaacb-b33e-727a-c887-a2ac4c65e60e.png)


    - 「Import Default Image Database」という通知が表示されるので、「Import」を選択。※デフォルトのマーカ画像が登録されます。
- ARマーカの下に、立方体を置き、実際にAR表示してみます。
    - ①ImageTarget（マーカ）の子オブジェクトとして、右クリック→3D Object→Cubeで立方体オブジェクト（Cube）を仮置き
        - ※箱の大きさと配置は微調整してください。
    - ②デフォルトのARマーカは以下のパスに配置されています。
       「Assets→Editor→Vuforia→ImageTargetTextures→VuforiaMars_Images」
![vuforia4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/6fc0465d-d56a-fe25-bfeb-ac5915651a7b.png)

- AR表示をしてみます。
    - 以下のように立方体がマーカ上にAR表示されていれば成功
![vuforia5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/320088c9-5fdb-b4ff-c15a-c0664cf86119.png)

- 次に、マーカと3Dオブジェクトをライトのスイッチをイメージしたものに入れ替えます。
- マーカの入れ替え
    - ※先ほど作成した「Image Target」オブジェクトは非表示にしておいてください。
    - VuforiaのDeveloper portal[[1]](https://developer.vuforia.com/)へ行き、以下の手順でオリジナルマーカを設定します。※アカウント登録が必要です。
        - 「ログイン後、Develop→TargetManager→Add Database」を選択
        - Create Databaseという画面が開くので、Database Nameを「LightSwitch」等で設定し、Createを選択　※TypeはDeviceのままで良いです。
        - 作成したデータベース「LightSwitch」を選択し、「Add Target」を開く
        - 以下の設定を行い、「Add」を選択
            - Type：　SingleImage 
            - File：　「Browse...」から任意の画像を設定
                - マーカ画像はなんでも良いですが、スイッチをイメージした以下のイラストを用意したので、ご自由にご利用ください。
                - ちなみにマーカ画像をアップロードすると、特徴点解析が行われ、マーカとしての認識精度が5段階（5つ星）評価されます。本マーカの評価は★2であり、あまり良い画像ではありません。STEP3での実装を見ていただければ分かりますが、マーカをLOSTしやすくするために今回はあえてこのような画像にしました。
![vuforia6.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/e15072ec-b8f6-7b2c-d030-727d28683edf.jpeg)

            - Width：　1
            - Name：　なんでも良いですが、ここでは「light_switch」とします。
        - マーカが登録されますので、「Download Database(All)→Unity Editor→Download」を選択し、Unityパッケージをダウンロードしてください。
        - ダウンロードしたUnityパッケージのファイルをダブルクリックして開き、Unityプロジェクトへインポート
        - 先ほどと同じ手順で「右クリック→VuforiaEngine」から、Imageを選択すると、以下のように入れ替え済みのマーカが表示されるはずです。
![vuforia7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/df8ff366-4956-3912-5edd-154cb6bdd45d.png)

        - 最後に、以下手順でライセンスキーの作成と設定を行います。
            - ヒエラルキータブから、「AR Camera」オブジェクトを選択し、インスペクタータブから「Open Vuforia Engine configguration」を開く
            - ブラウザが開き、vuforiaサイトのLicense Managerが開くので、「Get Development Key」を選択
            - 「Add a free Development License Key」が開くので、License Nameを「light_switch」などで設定
            - Vuforiaの規約を確認した上で、チェックボックスにチェックを入れ「Confirm」を選択
            - すると、License Managerが開くので、設定したLicense Name「light_switch」を選択し、ライセンスキーを確認（コピー）
            - ライセンスキーをUnity上の「App License Key」のテキストボックスへコピペし設定
![vuforia8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/b9306437-c7d0-6044-72b6-ca830b620889.png)

    - 3Dオブジェクト（スイッチっぽいオブジェクト）への入れ替え
        - 以下のようなスイッチをイメージした3Dオブジェクトを適当に作り、大きさと位置を調整します。「ImageTarget」オブジェクトの子オブジェクトとして配置してください。（先ほどの立方体が残っていれば非表示か削除してください。）
![vuforia9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/188bc69a-96be-b19b-5d30-f37bd4f13ef6.png)

    - ここまで実施すると、オリジナルマーカとオリジナル3DオブジェクトでAR表示ができるようになります。

## STEP2.　DeviceConnectの設定・実装
- 続いて、DeviceConnectの設定・実装を進めていきます。
- DeviceConnectの基本的な使い方に関しては、以下の記事をご参照ください。
    - [【IoT】デバイスWebAPIを使って簡易ビデオシースルー型HMDを作ってみた](https://qiita.com/dcm_tanaka/items/02f44af8a2ac7e2e041a)
- 本記事では、Unity上でDeviceConnectの扱い方についてご説明いたします。
- 今回、DeviceConnectのHostプラグインを用いてスマートフォンのライトをUnityから制御したいと思います。上記記事を参考に、Hostプラグインの起動まで実施してください。
- Hostプラグインのlightプロファイルを利用します。デバイス（スマホ）のライトを制御することが可能です。
![dconnect1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/bc06b9a2-3b23-bf29-8882-31684f67eae0.jpeg)


- lightプロファイルを開き、まずはスマホ上で以下のAPIを動作確認してください。

```
POST /gotapi/light/　　　：ライトを点灯させる
DELETE /gotapi/light/　　：ライトを消灯させる
```

- POSTを例に簡単に説明します。
    - ①リクエストパラメータの部分にPOSTリクエスト時に追加可能なパラメータが記載されています。
    - ②「Send Request」でライトを点灯させてみましょう。スマホのライトがつくはずです。
    - ③リクエスト内容が記載されているので確認してください。
    - ④レスポンス内容を確認してください。result=0であれば成功です。（ライトもついているはずです。）
    
![dconnect2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/c64b98c4-2ade-5ade-a0cc-b688e9ff2eaa.jpeg)

- ライトの消灯に関しては、DELETEリクエストを実行してください。

- ここまでのライトの点灯（POST）と消灯（DELETE）の処理を、Unityスクリプトの各メソッド処理としてまとめると、以下の参考のようになるかと思います。

- 【参考】ライトの点灯処理（POST）
    - [スマートフォンのIPアドレス]の部分は、DeviceConnectを導入したスマホIPアドレスを入れてください。ローカルで動作確認する場合は、一般的には「192.168.43.1」だと思います。
    - [各自環境ライトのServiceID]の部分についても、各自の環境で異なりますので、先ほどのリクエストパラメータを参考にServiceIDを記入してください。（「Host」から始まり、「deviceconnect.org」で終わる文字列です。）
    - 今回は簡単化のため、ServiceIDをハードコーディングしていますが、本来はserviceDiscoveryを用いて抽出し自動で挿入するのがスマートですね。
    
```
    /* ライトの点灯処理 */
    public void PostLight()
    {
        StartCoroutine(PostLightMethod());//Postメソッド実行
    }

    private IEnumerator PostLightMethod()
    {
        string url = "http://[スマートフォンのIPアドレス]:4035/gotapi/light";

        WWWForm form = new WWWForm();

        //パラメータの設定例
        form.AddField("serviceId", "[各自環境ライトのServiceID]");
        form.AddField("lightId", "0");
        form.AddField("brightness", "1");

        //POSTリクエストの送信
        UnityWebRequest postRequest = UnityWebRequest.Post(url, form);
        yield return postRequest.SendWebRequest();

        //エラー判定
        if (postRequest.isHttpError || postRequest.isNetworkError)
        {
            Debug.Log(postRequest.error);//エラー確認
        }
        else
        {
            Debug.Log(postRequest.downloadHandler.text);//結果確認
        }
    }
```

- 【参考】ライトの消灯処理（DELETE）
    - [スマートフォンのIPアドレス]、[各自環境ライトのServiceID]に関しては、POSTリクエストの時の説明と同じです。
    
```
    /*  ライトの消灯処理 */
    public void DeleteLight()
    {
        StartCoroutine(DeleteLightMethod());//DELETEメソッド実行
    }

    private IEnumerator DeleteLightMethod()
    {
        string url = "http://[スマートフォンのIPアドレス]:4035/gotapi/light?serviceId=[各自環境ライトのServiceID]&lightId=0";

        //DELETEリクエストの送信
        UnityWebRequest deleteRequest = UnityWebRequest.Delete(url);
        yield return deleteRequest.SendWebRequest();

        //エラー判定
        if (deleteRequest.isHttpError || deleteRequest.isNetworkError)
        {
            Debug.Log(deleteRequest.error);//エラー確認
        }
        else
        {
            Debug.Log(deleteRequest.downloadHandler.text);//結果確認
        }
    }
```

- 適当にUnity上でボタンUIを作成し、上記のPOSTメソッドとDELETEメソッドをアタッチし、動作確認をしてみてください。Unity上からスマホライトのON/OFFを制御できるはずです。
![dconnect3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/995ea16e-190e-de62-287c-bc9acb890d29.jpeg)



## STEP3.　VuforiaとDeviceConnectの結合
- ここまできたら、あとはVuforia（AR表示）と、DeviceConnect（ライト制御）の実装を組み合わせるだけです。
- 「ARマーカから仮想のスイッチが出現し、手で押すとライトが消える」、という演出にしたいので、「マーカがカメラからLOSTしたら、ライト点灯のPOSTメソッドが走る」という処理を考えてみたいと思います。
- 「ImageTarget」オブジェクトにアタッチされている「DefaultTrackableEventHandler.cs」にマーカ発見時と見失った時の処理が記されています。ここにライトのON/OFFの処理を追加してみたいと思います。
    -  （注意事項）今回は簡単化のために直接「DefaultTrackableEventHandler.cs」を変更しますが、一番良いやり方は「DefaultTrackableEventHandler」クラスを継承して実装する形かと思います。例えばVuforiaをアップデートすると、「DefaultTrackableEventHandler.cs」の変更が上書きされる可能性があるため注意が必要です。
- まず「DefaultTrackableEventHandler」クラスに先ほどのDeviceConnectのPOSTメソッドとDELETEメソッドを追記しましょう。
- 次に「OnTrackableStateChanged」メソッドの中の処理について確認してみてください。newStatusとpreviousStatusの値からマーカの状態を判定しているので、マーカが見つかったときに「ライトを消灯」、マーカを見失ったときに「ライトを点灯」するように、先ほど作成したメソッドを呼び出すよう追記します。

```
        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED ||
            newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
            OnTrackingFound();
            DeleteLight();//ライト消灯
        }
        else if (previousStatus == TrackableBehaviour.Status.TRACKED &&
                 newStatus == TrackableBehaviour.Status.NO_POSE)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");
            OnTrackingLost();
            PostLight();//ライト点灯
        }
        else
        {
            OnTrackingLost();
        }
```

- 以上の実装により、「ARマーカから仮想のスイッチが出現し、手で押すとライトが消える」という実装が出来ました。

## デモ
- 動作確認してみましょう。カメラをかざすと、ライトのスイッチがAR表示されます。AR表示されたスイッチを手で押す（マーカを覆う）と、スマホのライトが点灯するはずです。
![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320410/6df48d23-9805-09d9-3d17-9bc730f2300e.gif)

# まとめ
- 思ったよりも地味なデモになってしまい、申し訳ありません。笑
- VuforiaとDeviceConnectを組み合わせることで、何となく考えついたARスイッチを簡単に実装することができました。
- 今回はスマホのライトを照明器具と見立ててデモを作りましたが、DeviceConnect対応デバイスであれば様々な制御が可能です。（スマホではなく本当の意味での照明器具のON/OFFを制御したり、カメラのシャッターをきったり、ロボットカー制御したり、等）
- この記事をきっかけに、UnityでのAR開発やデバイス連携技術に興味を持っていただけますと幸いです。ここまで読んでいただき、ありがとうございました。

# 参考文献
- [1][vuforia engine developer portal](https://developer.vuforia.com/) ,2020/12/23確認
- [2][DeviceConnect-Android](https://github.com/DeviceConnect/DeviceConnect-Android),2020/12/23確認