2017年2月12日日曜日

RaspberryPi + noble で BLE デバイスとの距離を計測してみた

概要

BLE デバイスのアドバタイジングパケットに含まれる「RSSI (受信電波強度)」と「TXPower (送信電波強度)」を使って BLE デバイスと RaspberryPi との距離を算出してみました

環境

BLE デバイスは何でも OK です
今回は手持ちの BLESerial2 を使ってみました

RaspberryPi 側セットアップ

Bluetooth ドングルを RaspberryPi に接続してください
今回は Nodejs 製のライブラリ noble を使って Bluetooth ドングルの制御を行います

noble のインストール

  • sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
  • npm install noble

制御用スクリプト

BLE デバイスをスキャンして、スキャンできた BLE デバイスから必要な情報 (RSSI, TXPower) を取得し距離を算出します

  • vim scan.js
var noble = require('noble');
var serviceUUIDs = ['bd011f227d3c0db6e44155873d44ef40']; // BLESerial2
var allowDuplicates = false;

noble.on('stateChange', function(state) {
  if (state === 'poweredOn') {
    noble.startScanning(serviceUUIDs, allowDuplicates);
  } else {
    noble.stopScanning();
  }
});

var n = 2 
noble.on('discover', function(peripheral) {
  console.log(peripheral.advertisement.localName);
  peripheral.on('rssiUpdate', function(rssi) {
    if (peripheral.advertisement.txPowerLevel !== undefined) {
      // var tx = peripheral.advertisement.txPowerLevel;
      var tx = -67;
      var distance = Math.pow(10.0, (tx - rssi) / (10 * n));
      console.log('tx:' + tx + ', rssi: ' + rssi + ', distance: ' + distance);
    }   
  }); 
  peripheral.on('connect', function() {
    setInterval(function() {
      peripheral.updateRssi();
    }, 1000);
  }); 
  peripheral.on('disconnect', function() {
    console.log('on -> disconnect');
  }); 
  peripheral.connect();
});

処理はそれほど難しくなくスキャンして見つかったデバイスに接続して 1 秒おきに RSSI の情報を取得するリクエストを投げて、そのコールバック内で距離の計算をしています

距離を算出する公式は以下の通りです

var distance = Math.pow(10.0, (tx - rssi) / (10 * n));

n はとりあえず 2.0 で設定しています
このパラメータは各自の環境に合わせて変更する必要がありそうです

また、今回は 1 つの BLE デバイスに絞りたかったので serviceUUIDs に BLESerial2 の UUID を指定しています
ので、スキャンの対象が 1 台のデバイスになっています

動作確認

Bluetooth ドングルが RUNNING になっていることを確認したら早速動作させてみましょう
BLESerial2 側の電源を ON にしてください

  • sudo node scan.js

起動すると以下のようなログが表示されると思います

tx:-67, rssi: -74, distance: 2.2387211385683394
tx:-67, rssi: -75, distance: 2.51188643150958
tx:-67, rssi: -75, distance: 2.51188643150958
tx:-67, rssi: -71, distance: 1.5848931924611136
tx:-67, rssi: -66, distance: 0.8912509381337456
tx:-67, rssi: -70, distance: 1.4125375446227544
tx:-67, rssi: -60, distance: 0.44668359215096315
tx:-67, rssi: -59, distance: 0.3981071705534972
tx:-67, rssi: -59, distance: 0.3981071705534972
tx:-67, rssi: -59, distance: 0.3981071705534972
tx:-67, rssi: -59, distance: 0.3981071705534972
tx:-67, rssi: -56, distance: 0.28183829312644537
tx:-67, rssi: -58, distance: 0.35481338923357547

単位はメートルになります

実際に BLE デバイスを動かしながら距離を測定して精度を確かめてみた感じだと、RSSI が安定してから実際に距離を測れば、誤差は数十cm ほどに収まる感じでした
が、やはり RSSI の値がとても不安定で動きながらだと急に RSSI の値が大きくなったり小さくなったりするので、距離の精度も悪くなりました

考察とポイント

今回の一番のポイントは「tx」の値でした
これを初め TXPowerLevel という値を使っていたのですが、どうやらこれがおかしな値になっていたようです
本来この「tx」は何かというと

BLE デバイスから 1m 離れた地点での電波強度 [dbm]

を設定しなければいけません

実際に peripheral.advertisement.txPowerLevel で値を取得してみると 4 という固定値が返って来ました
しかし、実際に 1m 離れて電波強度を測ってみるとだいたい -65 から -70 くらいになっておりこれが原因でおかしな距離を計算していました

もう少し詳しく調査してみるとこんな表も見つかったので TXPowerLevel にあった電波強度を設定する感じなのかなと思いました

で、いろいろ考えた結果 RSSI を使って距離を求める場合、一番良さそうな方法は

各環境ごとに RSSI と距離の対応表を作成し、RSSI の値が安定したら距離を算出する

という方法が一番いいかなと感じました

やはり一番の問題は RSSI が不安定だということです
RSSI は建物や他の電波の影響をめちゃくちゃ受けるので基本不安定だと思っていたほうがいいです
なので、まずは測定したい地点で安定した RSSI が算出できるような仕組みを作るといいと思います
( 例えば 10 回 RSSI を測定した結果、ノイズ等を除去してその平均を RSSI とする、など)

あとは距離に関してですが、これも計算式だと環境によって、やはりばらつきが出てしまうと思います
なので、RSSI を測定しながら実際メジャーなどを使ってその距離を測定し RSSI と距離の対応表を作るのが一番いいんじゃないかなと思います
( おい、じゃあそもそも今回の計算式とかいらないじゃん!って話にはなってしまうのですが、、、今回やってみた感じだとそれが一番いいかなと)

もちろん n のパラメータなどを各環境にあった値にチューニングすることで精度を上げるようにしてもいいと思います

あとは精度をあげる方法として RSSI の値が整数ではなく浮動小数で測定できるようになるともっといい精度になると思います ( 現状の仕組みだとできないと思いますが )

最後に

BLE デバイスを使って距離を求める方法を紹介しました
何とか頑張れば使えるレベルにまではなると思います
が、かなり精度の高いオペレーションやリアルタイムでの測定などは難しいという印象です

実際に使う場合には BLE 単体ではなく Wifi や超音波センサなど他のセンサと絡めて測定を行うことにより精度のいい測定ができるんじゃないかなと思います

この辺の技術 (位置測定や距離測定) は探してみると結構いろいろ出てくるのでおもしろい分野かなと思います

参考サイト

0 件のコメント:

コメントを投稿