Node.jsでiBeaconの距離推定する

この記事は[mohikanz Advent Calendar 2018](https://adventar.org/calendars/2950) の11日目です。
今回は私が今大学で研究している iBeacon を利用した距離推定について、自身の知識の整理も兼ねてつらつらと書いていこうと思います。

# iBeacon
iBeacon は Apple が開発した BLE(Bluetooth Low Energy) を利用したビーコン規格の1つです。 iOS7 から標準搭載されており、iPhone から扱えるので、 BLEサービス普及の旗手として期待されているビーコン規格です。

# 距離推定におけるパラメータ
距離推定には **TxPower** 、**RSSI** を利用します。

### TxPower
**ビーコンが発する信号の強さ**を示していて、単位は dBm （ディービーエム）です。iBeacon では 1m 離れた地点での受信信号強度を利用します。

### RSSI
Received Signal Strength Indication の略で、**受信した電波の強さ**を示します。単位は同じく dBm です。

# 距離推定の計算
距離の計算には、**自由空間では受信信号強度は距離の二乗に反比例して減衰していく**(フリスの伝達公式)という電波の関係を利用します。
1m 地点の受信信号電力 $p_1[mW]$、距離 r[m] 地点の受信信号電力 $p_r[mW]$とすると、フリスの伝達公式より、$p_1$、$p_r$、$r$ の関係は以下のようになります。

$$ p_r = \frac{p_1}{r^2} [mW] $$

RSSI と TxPower を利用するために、単位を dBm に合わせます。RSSI と TxPower はそれぞれ以下のように表すことが出来ます。

$$ RSSI = 10log_{10} p_r$$
$$ TxPower = 10log_{10} p_1$$

以上の式をまとめると、RSSI 、TxPower、 距離rの関係は以下のようになります。

$$ RSSI = TxPower - 20 log_{10}r [dBm]$$

この式を変形すると、距離 r は RSSI と TxPower で以下のように表すことができます。

$$r = 10^{(TxPower - RSSI)/20} [m]$$

上記の式はあくまでも理想空間におけるもので、実際には障害物の有無などで電波の受信強度というのは変わってきます。
これを考慮したい場合には距離に係る部分を変数にします。要は 20 の部分を 10*n に置き換えればよいです。
このときの n の意味は以下のとおりです。

* n = 2.0 : 障害物のない理想空間
* n < 2.0 : 電波が反射しながら伝搬する空間
* n > 2.0 : 障害物に吸収され減衰しながら伝搬する空間

個人的には`n=1.8`が一番安定するように思います。

# Node.jsで実装してみる
実際に、上記の関係を利用してNode.jsでビーコンの電波を受信し、ビーコンと受信機器の距離を計算するスクリプトを書いてみます。
今回は受信機として [RaspberryPi3 ModelB](https://www.raspberrypi.org/products/raspberry-pi-3-model-b/) を使います。別に RaspberryPi でなくても、BLE 機能を搭載している端末であれば何でも OK です。Node.js のバージョンは 9.2.1 です。

ビーコンの受信には [node-bleacon](https://github.com/sandeepmistry/node-bleacon) を使用します。
現在(2018/12)、**bleacon は Node.js 9.x.x 上でしか動作しない**ので、注意してください。


まずは受信してみましょう。コードは以下のとおりです。

```beacon.js
const Bleacon = require('bleacon');

//Start Beacon Scanning
Bleacon.startScanning();
Bleacon.on("discover", function(bleacon) {
   console.log(bleacon);
});
```

このコードを実行すると、問題なく実行されれば以下のように表示されます。
なお、コードの実行時にはsudoで実行する必要があるので注意してください。

```
{ uuid: '81fb2f79e3014973aa78057588a084f0',
  major: 15,
  minor: 23,
  measuredPower: -86,
  rssi: -46,
  accuracy: 0.17663747648877268,
  proximity: 'immediate' }
{ uuid: '81fb2f79e3014973aa78057588a084f0',
  major: 15,
  minor: 23,
  measuredPower: -86,
  rssi: -44,
  accuracy: 0.16197089955104235,
  proximity: 'immediate' }
{ uuid: '81fb2f79e3014973aa78057588a084f0',
  major: 15,
  minor: 23,
  measuredPower: -86,
  rssi: -46,
  accuracy: 0.17663747648877268,
  proximity: 'immediate' }

...
```

返ってきたオブジェクトを見ると、 rssi と measuredPower(txPower) が格納されていることがわかります。では次に、コレを使って距離を計算してみましょう。
コードは以下のとおりです。

```beacon.js
const Bleacon = require('bleacon');
//Start Beacon Scanning
Bleacon.startScanning();
Bleacon.on("discover", function(bleacon) {
  const rssi = bleacon.rssi;
  const txPower = bleacon.measuredPower;
//Distance Calc
  const r = 10**((txPower-rssi)/20);
  console.log(`distance: ${r}[m]`);
});

```

シンプルですね。最初のスクリプトに距離計算を追加しただけです。
では実験してみましょう！今回は実行環境のRasPiの上にビーコンを添えてみました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/308241/54a98f1b-d47b-5c26-c232-56fc21210d51.jpeg" width=50%>

この状態でコードを実行すると、以下のような結果が返ってきました。

```
distance: 0.007943282347242814[m]
distance: 0.01[m]
distance: 0.007943282347242814[m]
distance: 0.008912509381337459[m]
distance: 0.01[m]
distance: 0.01[m]
distance: 0.001995262314968879[m]
distance: 0.007943282347242814[m]
distance: 0.01[m]
distance: 0.001995262314968879[m]
distance: 0.007943282347242814[m]
distance: 0.01[m]
distance: 0.008912509381337459[m]
distance: 0.008912509381337459[m]
distance: 0.01[m]
distance: 0.01[m]
distance: 0.01[m]
distance: 0.01[m]

...
```
結果をみるに、どうやらちゃんと距離を計算できているようです。やったね！

# おわりに
今回はibeaconによる位置推定のさわりのさわり、ビーコンと受信機の距離推定について書いてみました。
このあとは、複数の受信機とビーコンの距離関係を利用し、三点測量の要領で位置推定をしたりします。
ただ、受信電波ってめちゃくちゃ不安定なので、そのままだとあんまり精度は出ません。
そこをなんとかするのが私の大学での研究なわけです。

明日、12日は[@hiroki-chi](https://qiita.com/hiroki-chi)さんの[AndroidでP2PやるならNearbyを使おう](https://qiita.com/hiroki-chi/items/5ee301697ac7da3e6903)です。

# 参考
[たった5行!最も簡単にiBeaconの電波を「受信」する方法](https://qiita.com/kpkpkp/items/c2899e548da1c5e2c28e)
[RSSI と TxPower からビーコンとの距離および近接度（Proximity）を推定する](https://qiita.com/shu223/items/7c4e87c47eca65724305)