Zoom Video SDKを使用したロボットの遠隔監視システムを作ってみる。

この記事は、[Qiita Engineer Festa 2022](https://qiita.com/official-campaigns/engineer-festa/2022)に参加するために執筆した記事です。

Qiita Engineer Festa 2022の[「Zoom API/SDKを使ってみよう」](https://qiita.com/official-events/86d8555146e1b14fef24)のテーマに参加します！！

# 自己紹介ときっかけ

まずは、簡単に自己紹介しようと思います。

私は、明治大学大学院　先端数理科学研究科　ネットワークデザイン専攻のロボティクスを専門とする[研究室](https://moriokalab.com/)の人間です。
日々、ロボットの自律走行の研究や強化学習を用いたロボットの研究を行っています。

特に私の研究は、移動ロボットとスマホアプリが複数参加することができるロボットネットワークを用いて、ロボットを遠隔操作するシステムの開発を行っています。

https://qiita.com/takahiro19981013/items/3ac223b94bd85a54b313

では、なぜ、ZoomのVideo SDKを使おうと思ったのか....................

私が、現在目指している研究では、移動ロボットにカメラを装着し、カメラ映像を確認しながらスマホアプリで移動ロボットを遠隔操作するシステムを開発することです。
つまり、ロボットの前方の映像を確認しながらスマホアプリでロボットを遠隔操作するロボットの巡回監視システムみたいなものです。
そのためには、必要なこととして
* ロボットの映像を低遅延にスマホアプリに送信できること
　映像を見ながらロボットを操作するので遅延があっては、ロボット走行システムとして問題あり。 
* ロボットから受け取った映像を自分の開発するスマホアプリ内に埋め込むことができること
　映像確認と移動ロボットを同じスマホアプリで完結させ、できるだけユーザーの操作量を減らしたい。

が必要不可欠です。
そのために、動画共有サービスや各種動画配信サービスが提供するAPIやSDKを調査したり、実際に使ったりしました。
しかし、上にあげた二点のタスクを達成することがなかなかできませんでした。

そんな中、Zoom Video SDKを見つけ、Qiita Engineer Festaというものもやっていることも知って、これは神様に「Zoom SDKを使って記事を書け」と言われているんじゃないかと思いました（笑）。

# システム構成
システム構成図はざっくりこんな感じ。
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1743941/5fcbefc7-040f-0365-e014-7470cd089e26.png)
* ハードウェア構成
ロボット：　Turtlebot2
ロボット操作用PC:　Surface GO 3
Webカメラ：　Logicool社製Webカメラ
* ソフトウェア構成
ロボットは、ROS（Robot Operating System）を使用し、ロボット操作用PCで管理
ロボットの全面に装着したWebカメラもロボット操作用PCにUSB接続
スマホアプリの十字キーを使ってロボットを遠隔操作
　※　[ロボットネットワーク](https://qiita.com/asmsuechan/items/5f0fe8648ddfca1e5934)や[ロボットの遠隔操作システム](https://qiita.com/takahiro19981013/items/3ac223b94bd85a54b313)に興味がある方はこちら
スマホアプリの十字キーの上にロボットの前方の映像を表示（<font color="Red">今回目指していること</font>）

今回は、二つのZoom Video SDKを使用します。
ロボット操作用PCでWeb用のZoom Video SDK

https://marketplace.zoom.us/docs/sdk/video/web

スマホアプリでAndroid用のZoom Video SDK

https://marketplace.zoom.us/docs/sdk/video/android

# Zoom Video SDKを使ってみる
### Zoom SDKとはどんなものなのか？
Zoom SDKには主に2つのSDK（Meeting SDK ＆ Video SDK）があります。
[この二つはどう違うの？](https://support.zoom.us/hc/ja/articles/360058998832-Video-SDK-%E3%81%A8%E3%83%9F%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0-SDK-%E3%81%AE%E6%AF%94%E8%BC%83)　
注目したのは、Video SDKでは、<font color="Red">デフォルトのZoom Meetings UIを使用せず、Videoのやり取りの部分を使うことができること</font>です。
先ほどのシステム構成図にあるように、これまでに開発したロボット操作用の十字キーの上にロボットの映像だけを埋め込みたいからです！
では、ようやく使っていきましょう。

:::note warn
注意
Zoom Video SDKを使用するにはVideo SDK planに加入する必要があります。[こちらから](https://zoom.us/buy/videosdk)
:::

## Zoom Video SDK for Web
ロボットに装着したWebカメラの映像をスマホアプリに共有するためにWeb用のZoom Video SDKを使用します。
Zoom Video SDK公式ドキュメントを参考にやっていきます。
ページ内の Sample Apps から Video SDK Web Sample を選択し、[サンプルアプリのGithubのページ](https://github.com/zoom/videosdk-web-sample)へ移動します。

Githubページ内のドキュメント通り進めていきます。

### サンプルアプリのインストール
```
git clone https://github.com/zoom/sample-app-videosdk.gi
```
react-demoとpurejs-demoという二つのサンプルアプリがありますが、今回はpurejs-demoを使っていきます。
### purejs-demoディレクトリへの移動とインストール
```
cd sample-app-videosdk/purejs-demo
npm install
```
次にconfig.jsファイルを開きます。
sample-app-videosdk/purejs-demo/src/js/config.jsを開きます。
ここでは、SDK Keyの入力やミーティングを識別するためのセッション名を設定します。
セッション名は、Androidでの開発にも使うので覚えておきましょう。
```js:config.js
export default {
    sdkKey: 'YYdjlwLjfahvaypXrY5KvCiTbX1EOZdKtGMh',
    sdkSecret: 'YGnMpzY02u3IssEsWUidKwKTpFa6mUcyX2Gv',
    topic: 'Turtlebot',// セッション名
    password: '1104',　//　パスワード
    name: 'robot_user',　//　ユーザー名
    sessionKey: '1104',
    user_identity: 'test_user'
};
```

:::note warn
注意
アプリ実行の際にwebpack.config.dev.jsファイルでエラーが発生する場合があるようです。
Webpackのファイルの書式をV３からV４へと変更する必要があります。
変更部分（ファイル内のdevServer{}を変更）
```js:webpack.config.dev.js
devServer: {
    static: {
      directory: path.join(__dirname, ''),
     },
    https:{
    cert: './localhost.crt',
    key: './localhost.key',
    },
    host: '0.0.0.0',
    port: 3000,
    hot: true,
    client:{
      overlay: true,
    },
    
    historyApiFallback: false,
    //disableHostCheck: true,
    headers: {
      'Access-Control-Allow-Origin': https
        ? 'https://0.0.0.0:3000'
        : 'http://0.0.0.0:3000',
      'Cross-Origin-Embedder-Policy': disableCORP ? '' : 'require-corp',
      'Cross-Origin-Opener-Policy': disableCORP ? '' : 'same-origin'
    },
    open: 'index.html'
  },

```
:::
アプリの実行に成功するとこんな感じ。
![zoom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1743941/ebe06902-df30-6988-06dd-9d0cd1ee8b97.png)



## Zoom Video SDK for Android
次にAndroid用のVideo SDKをを用いてスマホアプリを開発していきます。（開発環境は、Android Studio）
参考サイトは[こちら](https://qiita.com/yuppejp/items/46e659aa3d2cb6646923)
### JWTトークンの生成
Video SDK Planに加入した際のSDK Keyをもとにスマホアプリ開発に必要なJWTトークンを発行します。
[こちら](https://qiita.com/yuppejp/items/838e1becfc2774bd4e85#jwt%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E7%94%9F%E6%88%90)のサイトを参考にJWTトークンを生成します。

基本的には、公式ドキュメントの通りに進めて行きます。
今回のこの記事では、自分のアプリ用に変更をした部分を載せていきます。
### Viewの配置
ロボットの映像を表示するViewを既存の十字キーの上に配置します。
```xml:actiity_robot.xml
  <us.zoom.sdk.ZoomVideoSDKVideoView
            android:id="@+id/robotVideoView"
            android:layout_width="match_parent"
            android:layout_height="261dp"
            android:layout_marginStart="10dp"
            android:layout_marginTop="20dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="10dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/joinButton" />
 
                             <!--      十字キーのView　　　　　-->

```
* ユーザー名をapp_userとして参加

```kotlin:RobotActivity.kt
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_robot)
       
   //         中略

        joinButton.setOnClickListener {
            if (ZoomVideoSDK.getInstance().isInSession) {
                leave()
            } else {
             　  join("app_user") // ユーザー名を変更
            }
        }
   }
```
### セッション名やパスワード、JWTトークンの設定
Zoom Video SDKでは、ミーティングの識別をセッション名で行うため、セッション名(今回は”Turtlebot”)を同じにすることでビデオのやり取りを可能にします。
また、先程生成したJWTトークンを” token ”に入力します。
```kotlin:RobotActivity.kt
 private fun join(name: String) {
        val audioOptions = ZoomVideoSDKAudioOption().apply {
            connect = true
            mute = false 
        }
        val videoOptions = ZoomVideoSDKVideoOption().apply {
            //localVideoOn = true // Turn on local/self video upon joining
        }
        val params = ZoomVideoSDKSessionContext().apply {
            sessionName = "Turtlebot" //セッション名
            userName = "app_user"　// ユーザー名
            sessionPassword = "1104" //　パスワード
            token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBfa2V5IjoiWVlkamx3TGpmYWh2YXlwWHJZNUt2Q2lUYlgxRU9aZEt0R01oIiwidHBjIjoiVHVydGxlYm90Iiwicm9sZV90eXBlIjoxLCJzZXNzaW9uX2tleSI6IjExMDQiLCJ1c2VyX2lkZW50aXR5IjoidGVzdF91c2VyIiwiaWF0IjoxNjU3ODc5Mzg2LCJleHAiOjE2NTc4ODY1ODZ9.4urEhbcGX7H-CaYEwG_a_SkU1fbalQUDmDh7hcPvX8w\n" // TODO: Pass in your JWT. In a production app, ensure that you do not hard code JWT or any confidential credentials.
            audioOption = audioOptions
            videoOption = videoOptions
        }

        val session = ZoomVideoSDK.getInstance().joinSession(params)
        if (session != null) {
            Log.d(TAG, "joinSession")
            Log.d(TAG, "  sessionName: $session.sessionName")
            Log.d(TAG, "  sessionID: $session.sessionID")

            val videoHelper = ZoomVideoSDK.getInstance().videoHelper
            videoHelper.startVideo()
            videoHelper.switchCamera()  //内カメか外カメ
        }
    }
```
* ロボット側ユーザーの参加の確認
ロボットが正常に参加すると、Android StudioのLogcatでrobot_userが参加していることがわかるはずです。
```kotlin
override fun onUserJoin(
                userHelper: ZoomVideoSDKUserHelper?,
                userList: MutableList<ZoomVideoSDKUser>?
            ) {
                Log.d(TAG, "onUserJoin")
                userList?.forEach { user ->
                    Log.d(TAG, "  userName: ${user.userName}")　// ログ表示

                    user.videoCanvas.subscribe(
                        remoteVideoView,
                        ZoomVideoSDKVideoAspect.ZoomVideoSDKVideoAspect_Original
                    )
                }
            }
```

変更点は以上！！！！！！

基本的な流れは、セッション名を同じものにし、ロボットとスマホアプリの映像のやり取りを実現させ、受け取ったロボットの映像を十字キーの上のViewに配置するという流れです。（だいぶ簡略化してますが…）
# 実験動画
それでは、実際に研究室内を走行させてみた動画をご覧ください。

https://www.youtube.com/watch?v=JWUuVibXVBI

# まとめ
今回は、Zoom Video SDKを使ってロボットが巡回する遠隔監視システムを作りました。
実際に動かしてみるととても簡単に使うことができ、さらには低遅延なロボット操作をすることができ、とても楽しかった！！
この機会を与えてくれたQiita Engineer Festa 2022とZoomさんに感謝します。