ARFoundationを使って点群データ/PointCloudを計測する
PointCloudについて
ARといえば点群、点群といえばARです。
我らが ARFoundation にも当然ながら点群をスキャンする機能があります。
YouTubeのvideoIDが不正です
動画中の黄色い点が点群を構成する特徴点です。
この特徴点の検出精度が高ければ高いほどきれいに空間がスキャンできているはずですが、スマートフォン上で動かしているだけではそれがどれほどの精度で取れているのかわかりません。
というわけで今回はARFoundationのARPointCloudManagerの機能を使って、スマートフォンでスキャンした点群データをUDP
で送信してエディタ上に表示する、ということをやりたいと思います。
概要
今回の概要はこんな感じです。この記事はこの粒度で進みます。
リポジトリはこちら。
環境
platform | version |
---|---|
Unity | 2021.1.15f1 |
ARFoundation | 4.1.7 |
機能
データ
デバイス-ビューア間では以下のデータをやりとりしています。
特徴点の1つ1つがIdentifiedPoint
で、それをIdentifiedPointArray
にまとめて送信。
DevicePose
はビューア上で確認するために送信しています。
IdentifiedPoint
名前 | 意味 |
---|---|
Identify | 特徴点の識別子 |
Confidence | 確度 |
Position | 特徴点の座標 |
CameraPosition | カメラの座標 |
CameraRotation | カメラの傾き |
IdentifiedPointArray
名前 | 意味 |
---|---|
Array | IdentifiedPoint[] |
Time | 送信時の時間 |
DevicePose
名前 | 意味 |
---|---|
Position | カメラの座標 |
Rotation | カメラの傾き |
他にもPingを送信してコネクション確立とかやってますが本筋ではないので割愛。
ちなみに送信するデータをMessagePackで変換かけてるのは、通信量を節約したとかではなく、C#
標準のUDP
はbyte配列しか送れないので、データのシリアライズ/デシリアライズを自前で考えるのがめんどくさかったからです。なんにせよ、頻繁に送信するデータなので圧縮しておくにこしたことはないと思います。
デバイス
まずはスキャンするデバイス側です。
Assets/Scenes/Device.unity
空間上に検知した点群を白いSphereで表示します。表示されている光の三原色のアンカーはそのままUnityのXYZで、デバイスが認識している空間の原点です。上の数字はカメラの座標です。
裏でせっせと点群やカメラの座標を送信していますが、デバイス側でできることはひたすら動かして点群をスキャンすることだけです。[1]
ビューア
デバイスから送られてきた点群を表示するビューアです。
Assets/Scenes/Viewer.unity
Hierarchy
を基準に機能を解説します。
PointDataSupplier
点群の情報を生成する部分です。
- Mock
デバッグ用に適当な情報を生成します。ContextMenu
からSupply
を実行してください。 - UDP
デバイスから送信されてきた点群の情報を受信します。 - CSV
CSVに点群の情報を保存/読込する機能です。float
の浮動小数点の関係でパースすると値が変わったりしますが誤差の範疇ということで……。
PointRoot.PointViewScheme
点群を表示する部分です。
- Scheme
点群を以下の情報によって色分けします。
名前 | 意味 |
---|---|
White | 白 |
Distance | カメラと点群の距離 |
Confidence | 確度 |
- Distance
- Confidence
表示する点群をフィルタリングします。Distanceの単位はメートル、Confidenceの範囲は0〜1です。
AnchorWithMemo
DevicePose
を反映して動きます。
実行
スマートフォンとエディタでそれぞれ実行するとこんな感じになります[2]。白いSphereの1つ1つが点群情報です。
UDP
を介しているので同じLANに繋いでおく必要があります。
これは視点が真上からで、アンカーの青軸がZ=正面です。
部屋を歩き回ってスキャンできているのがわかると思います。
分析
散布図
値をフィルタリングすることで、表示される点群の精度を上げていきたいと思います。
今回使用したのはAndroidのMoto G7 Plusです。普段遣いには十二分ですが、今時のARやれと言われるとちょっと苦しいかも……という感じ。
とりあえず生データをGoogleスプレッドシートにそのまま打ち込んでカメラからの距離
× Confidence
で散布図を作ってみます。
左がみっっっっちり詰まってますが、25mどころか40m近くでもConfidenceが0.75あったりして明らかに変な値が混ざってます。そんなに部屋広くないよ!
ひとまずそれっぽい5mで足切りしてみます。
ほんとならここから間引いて分布を見るのが正しいんでしょうが、つらいのでしません。4m/0.5ぐらいがだいたいの妥当な数字でしょうか。
1m以遠から点群のスキャンが開始されるのは、動かしていたときの感覚と合っています。近すぎて差分が取りづらいとかズレが大きすぎるとかたぶんそんな感じ。
フィルタリング
カメラからの距離4m以内、Confidenceが0.5以上でフィルタリングした結果です。色はConfidence
で色付けしてます。
最初に注目しているのがカーテンで、回転した後の次がテレビです。知ってるとそれっぽく取れてます。
まとめ
というわけで、ARFoundation
を使って点群をスキャンする方法でした。
点群と言いつつもARPointCloudManager
から出てくる情報はXYZだけで色情報のRGBは入っていません。精度も正直どうかなって感じでしたが、これはARFoundation
の主軸がリアルタイム性にあるからかなと思っています。あと使用したAndroidの性能。
ここまでやっておいて手のひらを返すのもなんですが、スマートフォンできれいに点群を測定したければLiDARを積んだiOSでストアからアプリ落としてきてスキャンすればいいと思います。持ってないので使ったことないですがきれいにとれるはず。
ARFoundation
はHololens2
やMagicLeap
にもに対応しているので[3]、そちらを使うならもっと細かくきれいに取れると思います。点群が増える分、ビューアの描画負荷がだいぶ厳しいことになると思いますが……。
実際のARアプリ開発では点群ではなくそれを元に生成されるMeshに対してあれこれしますが、生の点群を今のARFoundation
を使って眺めてみたかったのでやりました。戦闘機を真似て紙飛行機を折るが如き試作でしたが、とりあえずやってみたいことをやってみたので満足です。
おしまい。
覚書
- トラッキング外れのことを考えてDeviceではAnchorを設置するべき
- AndroidにもMeshingほしい……。
- iOSでビルド試してない[4]
- ビューアの点群のDrawCallが厳しいので公式サンプルのようにシェーダでなんとかするべき
- Contributeせずに言うのもなんだけどUnity公式のサンプルは悉く信用ならない
-
SerializeField
をDIとして使うのはさすがに厳しくて設計がぐだぐだになった - テスト素直に書けばよかった
- gitmoji楽しいけどなんか項目はweb向けなきがする
- ノリでURP入れたけどShaderGraphわからん[5]
参考
Discussion