拡張現実におけるWi-Fiポイントの方向性については既に強調しましたが、今日はその位置についてお話しします。
誰が技術的な詳細を気にし、ボウラーがそれと何をしなければならないのか、猫へようこそ。
また、何が起こったかについての写真とビデオのレポートにあなたの注意が向けられます。
問題の声明
ここで説明するWi-Fi方向探知機と拡張現実デバイスがあります。 特定のWi-Fiポイントの場所を強調表示する必要があります。
方向探知機は冷熱の原理に基づいて動作することを簡単に思い出します。これは、対象物をどれだけうまく狙ったかを示しています。
問題
信号源の位置を決定するには、複数の方位光線を、好ましくは異なる位置から交差させる必要があります。 そして、ここから問題が始まります。 さらに、問題は2次元の場合(マップ上に配置する場合)でも始まります。3つ以上の光線が1つのポイントで交差することは望ましくありません。
そして、私たちの場合、2本の光線でさえ交差したくありません。
解決策
したがって、正確な解決策は得られません。 おおよそを探す必要があります。
基本的なアルゴリズムは、スペースをキューブに分割することに基づいています。 キューブごとに 埋める 通過した光線の数を保存します。 最も人気のあるキューブをいくつか描いてみましょう。 私はボールが好きなので、立方体の周りに描かれたボールを描きます。
光線が比較的少ないため、非常にまばらなデータが予想されるため、それらをハッシュテーブルに格納します。 キーとして、キューブ座標のペアと、値として透過光線の数があります。
各方位には、開始点(方向探知機の位置)と方向の座標があります。 厳しい現実には限界があるため、もちろん、光線を横切るのではなく、セグメントを横切ります。 コンピューターのメモリに制限されているだけでなく、無限の距離にあるWi-Fiポイントを検出することもできません:(
キューブをマークするために、コンピューターグラフィックスにBresenhamのアルゴリズムがあります。 確かに、画面はこれまで2次元のみであるため、平面上のBresenhamアルゴリズムはどこにでも記述されていますが、3Dに適応するものは何もありません。
実装について読む前に、ビデオの仕組みをご覧ください。
残念ながら、カメラは電話に焦点を合わせたくありませんでしたが、望めば、探しているポイントの名前が一致していることがわかります。
実装
ボウラーはそれと何の関係があるのですか?伝説では、ピーター1世が最初にコトリン島の海岸に出航したとき、彼を警備していたスウェーデンの兵士がストライカーを非常に早く与えたため、ボイラーで調理されていた火が燃え残っていました。 そしてまさにこの事件のために、島はその名前を得ました。
以前のように、大容量拡張現実デバイスの中で、最も正確で手頃な価格はGoogle Tangoに基づいたデバイスです。 したがって、それらを使用します。
データ準備
Tangoデバイスの現在の「 ポーズ 」と、デバイスが信号ソースにある程度の精度で向けられた瞬間に送られるイベント。 Bresenhamアルゴリズムの場合、セグメントの始まりと終わりを取得する必要があります。
しかし、セグメントの終わりを探し始める前に、以前は未知のWi-Fiポイントを見つけることができるかどうかを確認しましょう。
セグメントの終わりを取得するには、その長さをポイントのzコンポーネントに追加してから回転する必要があります。 なぜzなのか? この座標系では、 zは「前方」であるためです。
セグメントの終わりを取得するprivate float[] calcSagittaEnd(float[] start, float[] rotateMatrix) { float[] end = new float[4]; System.arraycopy(start, 0, end, 0, 3); end[3] = 0f; end[2] += BEARING_LENGTH; Matrix.multiplyMV(end, 0, rotateMatrix, 0, end, 0); return end; }
Tangoライブラリから回転行列を取得します。
回転行列を取得する public boolean addBearing() {
カフェShahherezadのWi-Fiポイントを見つける方法を見てみましょう。
サイコロ充填
x座標を、ベアリングセグメントの投影が最も長い座標に割り当てます。
Bresenhamアルゴリズムでは、各ステップはx座標だけ増加し、十分な誤差がある場合にのみy座標が増加します。 z座標でも同じことを行います。
3DでのBresenhamアルゴリズムの実装 enum class LengthCoord {x, y, z} val space = hashMapOf<Coordinate, Int>() private fun markCubes(start: Coordinate, end: Coordinate, lengthCoord: LengthCoord) { // x , . fun getX(vertex: Coordinate) = when(lengthCoord) { LengthCoord.x -> vertex.x LengthCoord.y -> vertex.y LengthCoord.z -> vertex.z } fun getY(vertex: Coordinate) = when(lengthCoord) { LengthCoord.x -> vertex.y LengthCoord.y -> vertex.x LengthCoord.z -> vertex.y } fun getZ(vertex: Coordinate) = when(lengthCoord) { LengthCoord.x -> vertex.z LengthCoord.y -> vertex.z LengthCoord.z -> vertex.x } fun setXYZ(x: Int, y: Int, z: Int, lengthCoord: LengthCoord): Coordinate = when(lengthCoord) { LengthCoord.x -> Coordinate(x, y, z) LengthCoord.y -> Coordinate(y, x, z) LengthCoord.z -> Coordinate(z, y, x) } val dx = when { getX(end) > getX(start) -> 1 getX(end) < getX(start) -> -1 else -> 0 } val dy = when { getY(end) > getY(start) -> 1 getY(end) < getY(start) -> -1 else -> 0 } val dz = when { getZ(end) > getZ(start) -> 1 getZ(end) < getZ(start) -> -1 else -> 0 } val d = arrayOf(Math.abs(getX(start) - getX(end)), Math.abs(getY(start) - getY(end)), Math.abs(getZ(start) - getZ(end))) var x = getX(start) var y = getY(start) var z = getZ(start) var errY = d[0] / 2 var errZ = d[0] / 2 while (x != getX(end)) { x += dx errY -= d[1] errZ -= d[2] if (errY < 0) { y += dy errY += d[0] } if (errZ < 0) { z += dz errZ += d[0] } addPoint(setXYZ(x, y, z, lengthCoord)) // } addPoint(setXYZ(x, y, z, lengthCoord)) }
addPoint関数。キューブを通過する光線のカウンターを増やします。
楽しいaddPoint private fun addPoint(coord: Coordinate) { lock.withLock{ var v = space[coord] if (v != null) { v++ space[coord] = v.toInt() } else { space[coord] = 1 } } }
最も人気のあるキューブを選択します。並べ替えてから、最初のいくつかを取ります。 現在の実装では、3つ以下です。
交差点を取得します fun getIntersection(): List<Array<Float>> { val resCandidates = ArrayList<Pair<Array<Float>, Int>>() lock.withLock { // , . for ((coord, counter) in space) { if (counter >= showThreshold) { resCandidates.add(Pair(scaleCoordinate(arrayOf( coord.x.toFloat(), coord.y.toFloat(), coord.z.toFloat())), counter)) } } } resCandidates.sortBy { item -> -item.second } // . // return resCandidates.subList(0, minOf( amountCellsToShow, resCandidates.size) ).map { elem -> elem.first } }
おわりに
与えられたベアリングによるオブジェクトの空間的位置の問題を解決するアプローチが検討され、その実装が提案されました。
すべてのソースコードはGitHubで入手できます 。
(Kotlinだけでなく)Kotlinの巨匠からの建設的な批判を歓迎します。
ご清聴ありがとうございました!