NearbyInteractionで周囲の端末の位置を測定する

NearbyInteractionとはiOS14で導入された新しいフレームワークです。 近距離無線チップを使い、複数の端末の距離や方向を測定することができます。

UWB

UWBとは、iPhone 11で導入されていた近距離無線チップです。 何に使われているのかよくわからず、売りにしているんだかどうだか、いまいちわからない機能です。

(対応端末を持っている人が近くにいるとAirDropのときに優先的に表示されるそうんだけど、私は実感できたことはないですね……。)

f:id:toyship:20200624050715p:plain
share with UWB

使用可能な端末

このフレームワークは、UWBチップがのっている端末どうしでだけ使えます。

UWBチップは、現状では iPhone 11iPhone 11 ProiPhone 11 Pro Max の3モデルのみに搭載されています。 この端末が複数ないと動作を試すこともできないのですが、そのあたりのことを配慮してくれたのか、シミュレーターでも動作を確認することができます。

サンプルコードをシミュレーターで動かしてみると、だいたいの動作はわかると思います。

https://developer.apple.com/documentation/nearbyinteraction/implementing_interactions_between_users_in_close_proximity

測定できるもの

このフレームワークでは、検知した端末の位置(distance)と相対角度(direction)を測定することができます。

f:id:toyship:20200624051037p:plain
distance and angle

また、周囲に複数端末がある場合、それらすべての端末を認識することができます。

f:id:toyship:20200624051127p:plain
detect many devices

ただし、このdistanceとdirectionの値はoptionalで、端末の状況によってはnilになることがあります。 下の図にあるように、相手の端末がカメラの視野角にはいっていればdistanceとdirectionの両方をとれますが、視野角外ではdistanceしかとることができません。

f:id:toyship:20200624051713p:plain
angle may be nil

正しく位置と角度をとるには、相互の端末を向かい合わせにするのが推奨されています。

f:id:toyship:20200624065131p:plain
device position

また、端末のあいだに壁や人や犬や猫がいた場合にもdistance やdirectionがとれなくなってしまうそうです。

f:id:toyship:20200624051938p:plain
may not detect data

使い方

使い方はわりとシンプルで、下記のようにNISessionを作ってdelegateを設定して、runすれば、delegateの func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject])で周囲の端末の位置情報が取得できるようになっています。

実行時にはユーザーのpermissionを取得するようになっています。(ただし、現状ではinfo.plistへの記述は必要ないようです。) 双方の端末でユーザーのpermissionを得ていないとsessionが張れません。

f:id:toyship:20200624052137p:plain
run a session

このフレームワークでは、周囲の端末の位置検知のみだけができるので、たとえばその端末と通信などをする場合には別の方法が必要です。

上にあげたサンプルコードの場合、Multipeer Connectivityをつかって、周囲の特定の1台の端末とconnectionを張り、そのあとMultipeer Connectivityのsessionを使ってNearbyInteraction session用のtokenを送る、というまわりくどいことをしています。

あと、NearbyInteractionでは、周囲の複数の端末の位置情報がとれるんですが、現状ではその端末の位置情報以外の情報が全くとれないんですよね……。 0.8mと1.2mのところに端末があるんだけど、それがiPhone 11iPhone 11 Proかもわからないし、誰の端末なのかもわからない、という微妙な状況です。

(これについては、今Developer Forumに質問をしているので、結果がわかったら追記します。)

追記:どうやって端末を特定するの?と聞いたら、NINearbyObjectsのdiscoveryTokenを使ってくれ、とのことでした。 こんな感じ(↓)ですね。

func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject]) {
        
        let myToken = session.discoveryToken // 自分のToken
        
        for obj in nearbyObjects{
            
            let foundPeerTokne = obj.discoveryToken // 見つけた端末のToken
        }
}

取得したNINearbyObjectdiscoveryTokenを取得すれば、一意なIDをとることができます。

自分のTokenは自分の持っているsessiondiscoveryTokenなので、このTokenMultipeerConnectivityなどで交換して、どのTokenがどの端末かを確認できますね。

まとめ

  • NearbyInteractionは周囲の端末の位置と角度を高性能で検知できる。
  • 使える端末が限定される。
  • 位置推定時の端末の姿勢に制限がある。
  • NearbyInteractionでデータが送信できるわけではないので、データ送信用の通信路は別途用意する必要がある。

まだβなので、使い所も実装もいろいろと微妙な部分が多いですね……。今後の発展に期待です。

参考