RSSIベースの距離推定は、すべてのBluetooth Beaconデプロイメントの基盤であるが、生のRSSI測定値は非常にノイズが多いことで有名である。3メートル先に配置されたビーコンが、マルチパスフェージング、人体の遮蔽、RF干渉により、数秒以内に-75 dBmから-55 dBmまで変動することがある。適切なフィルタリングなしには、近接検出はコイントスと同じになり、位置誤差は容易に5メートルを超える。本記事では、単純な移動平均からカルマンフィルタやパーティクルフィルタまで、本番ビーコンシステムで実際に機能するフィルタリングアルゴリズムを、実際のパラメータ、トレードオフ表、実装ガイダンスとともに解説する。

## 1. RSSIノイズの問題

RSSI(Received Signal Strength Indicator)は、受信したBluetoothアドバタイズメントパケットの電力レベルを測定する。自由空間では、RSSIは予測可能なパスロスモデルに従う:

RSSI(d) = TX_power - 10*n*log10(d/d0) - X_sigma

ここで、nはパスロス指数(自由空間では2.0、屋内では2.7-4.3)、d0は基準距離(1 m)、X_sigmaはシャドウイングを表す平均ゼロのガウス乱数変数で、典型的な屋内環境では標準偏差σ = 4-8 dBである。

実際には、ガウス仮定は近似に過ぎない。実際のRSSI分布は2つの成分の混合としてより適切に記述される:

視線内(LOS)成分:直接経路が存在する場合、RSSIは比較的安定している(σ ≈ 2-3 dB)。

非視線内(NLOS)成分:直接経路が遮断されている場合、RSSIは10-20 dB低下し、変動が非常に大きくなる(σ ≈ 6-10 dB)。

このバイモーダルな挙動が「飛び跳ねる」距離推定値の根本原因である。ビーコンと受信機の間を歩く人が瞬間的に15 dBのRSSI低下を引き起こす可能性があり、対数ロスモデルの下では見かけ上の距離が3 mから17 mに変化する。

### 実測RSSI特性

環境 パスロス指数(n) シャドウイングσ(dB) LOS/NLOS比
オープンオフィス(パーティション) 2.3 3.8 85/15
廊下(狭い) 2.0 2.5 90/10
混合オフィス(間仕切り) 2.7 5.2 60/40
倉庫(金属棚) 3.1 7.0 45/55
工業環境(機械) 3.4 8.1 35/65

倉庫と工業環境では、市販のビーコン位置システムが頻繁に失敗する理由が明確になる:半数以上の測定値がNLOSであり、σが7 dBを超えている。

## 2. フィルタリングが必要な理由:生RSSIのコスト

4メートルのトリガー閾値を持つ近接検出アプリケーションを考える。n=2.7、σ=5.2 dBの生RSSIを使用する場合:

– 3 m(ゾーン内):P(RSSI < 閾値) ≈ 18% → 偽陰性率

– 5 m(ゾーン外):P(RSSI ≥ 閾値) ≈ 12% → 偽陽性率

30%の合計エラー率は、生RSSIを信頼性のある近接トリガーには使用できないことを示している。フィルタリングはNサンプル平均で有効σを√N分の1に減少させるが、これはレイテンシのコストを伴う。

基本的なトレードオフ:

安定性(σの減少) ⟷ レイテンシ(応答の遅延)

以下で議論する各フィルタは、このトレードオフ曲線上のどこかに位置する。アプリケーションに適したポイントを選択することが技術の要諦である。

## 3. 単純移動平均(SMA)

最も単純なアプローチ:直近N個のRSSIサンプルのスライディングウィンドウを維持し、その算術平均を出力する。

#define WINDOW_SIZE 8

typedef struct {
    int8_t buffer[WINDOW_SIZE];
    uint8_t index;
    uint8_t count;
    int32_t sum;
} sma_filter_t;

int8_t sma_update(sma_filter_t *f, int8_t rssi) {
    if (f->count == WINDOW_SIZE) {
        f->sum -= f->buffer[f->index];
    } else {
        f->count++;
    }
    f->buffer[f->index] = rssi;
    f->sum += rssi;
    f->index = (f->index + 1) % WINDOW_SIZE;
    return (int8_t)(f->sum / f->count);
}

特性

パラメータ
有効σ減少 √N(8サンプル → 2.83倍減少 → σ_eff = σ/2.83)
群遅延 (N-1)/2 × サンプリング間隔
メモリ N バイト
サンプルあたりの計算 O(1)(ランニングサム使用)

N=8、アドバタイズ間隔100 msの場合、群遅延は350 msである。これは近接検出には許容されるが、リアルタイム位置測位には遅すぎる。

問題:SMAはすべてのサンプルに等しい重みを与える。200 ms前の測定値が最新のものと同じ影響力を持つため、ビーコンが実際に移動した際にSMAの反応が鈍くなる。

## 4. 重み付き移動平均(WMA)

WMAは直近のサンプルに高い重みを割り当て、応答性を向上させる:

// 線形重み:w[i] = i+1、総重み = N*(N+1)/2
int8_t wma_update(sma_filter_t *f, int8_t rssi) {
    for (int i = WINDOW_SIZE - 1; i > 0; i--) {
        f->buffer[i] = f->buffer[i-1];
    }
    f->buffer[0] = rssi;

    int32_t weighted_sum = 0;
    for (int i = 0; i < WINDOW_SIZE; i++) {
        weighted_sum += f->buffer[i] * (WINDOW_SIZE - i);
    }
    return (int8_t)(weighted_sum / (WINDOW_SIZE * (WINDOW_SIZE + 1) / 2));
}

WMAは同じウィンドウサイズのSMAと比較して群遅延を約30%削減するが、ノイズ除去がわずかに低下する(有効Nが約15%減少)。

フィルタ N=8 σ減少 群遅延(100ms間隔) 応答性
SMA 2.83倍 350 ms
WMA 2.45倍 240 ms

## 5. 指数重み付き移動平均(EWMA)

EWMAは、状態変数を1つしか必要とせずバッファがないため、市販のビーコンSDK(Apple iBeacon、Google Eddystoneを含む)で最も人気のあるRSSIフィルタである:

typedef struct {
    float filtered;
    float alpha;
    uint8_t initialized;
} ewma_filter_t;

float ewma_update(ewma_filter_t *f, float rssi) {
    if (!f->initialized) {
        f->filtered = rssi;
        f->initialized = 1;
    } else {
        f->filtered = f->alpha * rssi + (1.0f - f->alpha) * f->filtered;
    }
    return f->filtered;
}

スムージング係数αがトレードオフを制御する:

α = 1.0:フィルタリングなし(生RSSI)

α = 0.3:中程度のスムージング(N≈6のSMAと同等)

α = 0.1:強いスムージング(N≈20のSMAと同等)

α = 0.05:非常に強い(N≈40のSMAと同等)

EWMAの「同等N」は概ね N_eq ≈ (2-α)/α であり、NサンプルのSMAと同じ分散減少を与える。

### αの選択

アプリケーション 推奨α 整定時間 有効σ
リアルタイム位置測位(資産追跡) 0.3-0.4 0.3-0.5秒 σ/2.0
近接検出(プッシュ通知) 0.15-0.25 0.8-1.5秒 σ/3.5
屋内ナビゲーション(経路案内) 0.2-0.3 0.5-1.0秒 σ/2.8
静的監視(在席検知) 0.05-0.1 2-4秒 σ/5.0

整定時間は概ね 5/α × 間隔 である。α=0.2、間隔100 msの場合、ステップ変化後に新しい定常状態に収束するのに約2.5秒かかる。

EWMAは非対称である:RSSI上昇(ビーコン接近)には素早く応答するが、RSSI低下(ビーコン離脱)には遅く応答する。なぜならフィルタは常に遅れるからである。近接入退室検出では、この非対称性により「粘着する退室」が発生する—ビーコンが離れてから数秒間、システムはビーコンが近くにあると報告し続ける。

## 6. 外れ値除去のためのメディアンフィルタ

メディアンフィルタは平均化とは根本的に異なる:ウィンドウの平均ではなく中央値を出力する。これにより外れ値に対して免疫を持つ。

// 小さいN用の単純挿入ソート
int8_t median_filter(int8_t *buf, uint8_t n) {
    int8_t temp[n];
    memcpy(temp, buf, n);
    for (int i = 1; i < n; i++) {
        int8_t key = temp[i];
        int j = i - 1;
        while (j >= 0 && temp[j] > key) {
            temp[j+1] = temp[j];
            j--;
        }
        temp[j+1] = key;
    }
    return temp[n / 2];
}

RSSIにメディアンが有効な理由:バイモーダルなLOS/NLOS分布は、NLOS測定値が本質的に外れ値であることを意味する—10-20 dBの急激な低下。平均ベースのフィルタはこれらの外れ値を取り込み、推定値を低く(遠い距離に)バイアスする。N≥5のメディアンフィルタは単純にそれらを破棄する。

フィルタタイプ 入力: [-70, -72, -55, -71, -68, -73, -70] 出力 真のRSSI ≈ -71
SMA (N=7) 平均 = -68.4 -68.4 dBm 2.6 dBバイアス
EWMA (α=0.3) -67.8 dBm 3.2 dBバイアス
メディアン (N=7) ソート: [-73,-72,-71,-70,-68,-55] -70 dBm 1.0 dBバイアス

-55 dBmの外れ値(おそらくマルチパス反射)は、平均ベースのフィルタで2-3 dBの正のバイアスを作り出し、距離を25%過小評価することに換算される。メディアンフィルタはほとんど影響を受けない。

ハイブリッドアプローチ:まずメディアンフィルタ(N=5)で外れ値を除去し、次にメディアン出力にEWMA(α=0.2)でスムージングを適用する。この2段階設計は外れ値除去と低レイテンシ追跡を組み合わせており、本番システムに推奨されるアプローチである。

## 7. RSSI推定のためのカルマンフィルタ

カルマンフィルタはRSSIをプロセスノイズと測定ノイズを持つ状態としてモデル化し、線形ガウス仮定の下で最小平均二乗誤差(MMSE)推定を生成する。

### 状態モデル

状態:      x[k] = x[k-1] + w[k]     (一定RSSI + プロセスノイズ)
測定:      z[k] = x[k] + v[k]       (観測RSSI + 測定ノイズ)

プロセスノイズ:     w ~ N(0, Q)
測定ノイズ: v ~ N(0, R)

静止ビーコンの場合、RSSIは急速には変化しないためQは小さくすべきである(0.1-0.5)。移動ビーコンの場合、変化を追跡できるようQは大きくすべきである(1.0-5.0)。

### 実装

typedef struct {
    float x;    // 状態推定
    float p;    // 推定不確実性
    float q;    // プロセスノイズ分散
    float r;    // 測定ノイズ分散
} kalman_rssi_t;

float kalman_update(kalman_rssi_t *kf, float measurement) {
    // 予測
    kf->p = kf->p + kf->q;

    // 更新
    float k = kf->p / (kf->p + kf->r);  // カルマンゲイン
    kf->x = kf->x + k * (measurement - kf->x);
    kf->p = (1.0f - k) * kf->p;

    return kf->x;
}

### QとRのチューニング

Q/Rの比がフィルタの挙動を決定する:

Q/R比 挙動 アプリケーション
0.01 非常に滑らか、応答が遅い 静的資産監視
0.1 バランス型 一般的な屋内位置測位
0.5 応答性高、中程度のノイズ 移動資産追跡
1.0+ ほぼフィルタリングなし、高速応答 高移動性シナリオ

Rを実測σ²から設定し(例:σ=5.2 → R=27.0)、Q=2.7(Q/R=0.1)とすると、カルマンフィルタはσ_eff約2.3 dBを達成する—2.3倍の減少—整定時間は約1.5秒である。

### EWMAに対する利点

カルマンフィルタはゲインを動的に適応させる:推定不確実性が高い場合(初期化直後やステップ変化後)、ゲインは高くフィルタは素早く応答する。信頼度が高まるにつれ、ゲインは低下しフィルタはより滑らかになる。EWMAは固定αを使用し、適応できない。

特性 EWMA (α=0.2) カルマン (Q=2.7, R=27.0)
定常状態σ_eff 2.6 dB 2.3 dB
ステップ応答(90%立ち上がり) 2.5秒 1.2秒
適応ゲイン なし あり
メモリ 4バイト 16バイト
サンプルあたりの計算 2演算 5演算

## 8. 非線形環境のためのパーティクルフィルタ

深刻なマルチパスがある環境(倉庫、工場)では、ガウス仮定が崩れる。RSSI分布はマルチモーダルになり、線形フィルタ(EWMA、カルマン)は誤った値に収束する。

パーティクルフィルタはRSSI確率分布を重み付きサンプル(粒子)の集合として表現する:

import numpy as np

class ParticleFilterRSSI:
    def __init__(self, n_particles=100, rssi_range=(-100, -30)):
        self.n = n_particles
        self.particles = np.random.uniform(rssi_range[0], rssi_range[1], n_particles)
        self.weights = np.ones(n_particles) / n_particles

    def predict(self, process_std=2.0):
        self.particles += np.random.normal(0, process_std, self.n)

    def update(self, measurement, measurement_std=5.0):
        likelihood = np.exp(-0.5 * ((self.particles - measurement) / measurement_std) ** 2)
        self.weights *= likelihood
        self.weights /= self.weights.sum()

    def estimate(self):
        return np.sum(self.particles * self.weights)

    def resample(self):
        indices = np.random.choice(self.n, self.n, p=self.weights)
        self.particles = self.particles[indices]
        self.weights.fill(1.0 / self.n)

    def step(self, measurement):
        self.predict()
        self.update(measurement)
        est = self.estimate()
        n_eff = 1.0 / np.sum(self.weights ** 2)
        if n_eff < self.n / 2:
            self.resample()
        return est

### NLOS支配環境での性能

倉庫テスト(55% NLOS)、4 m距離のビーコン追跡:

フィルタ 平均誤差(m) P95誤差(m) 収束時間
SMA (N=8) 2.1 5.8 0.8秒
EWMA (α=0.2) 1.9 5.2 1.0秒
カルマン (Q/R=0.1) 1.6 4.5 1.2秒
パーティクル (100粒子) 0.9 2.1 1.5秒

パーティクルフィルタはこの過酷な環境でEWMAと比較して平均誤差を43%削減する。ただし、100倍のメモリと計算を必要とするため、リソース制約のあるビーコンタグでは実用的でない。

推奨:計算能力がある受信機/ゲートウェイ側(スマートフォン、Raspberry Piゲートウェイ)でパーティクルフィルタを使用する。ビーコンタグ自体では、タグは送信のみであるためフィルタリングは受信機で行われ、EWMAまたはカルマンで十分である。

## 9. フィルタ比較:包括的トレードオフ表

フィルタ σ減少(σ=5.2 dB) レイテンシ メモリ 計算 外れ値耐性 適応性
生RSSI 1.0倍 (5.2 dB) 0 ms 0 B 0演算 なし N/A
SMA (N=4) 2.0倍 (2.6 dB) 150 ms 4 B 1演算 なし なし
SMA (N=8) 2.8倍 (1.8 dB) 350 ms 8 B 1演算 なし なし
WMA (N=8) 2.5倍 (2.1 dB) 240 ms 8 B 8演算 なし なし
EWMA (α=0.3) 1.8倍 (2.9 dB) 100 ms 4 B 2演算 なし なし
EWMA (α=0.15) 2.9倍 (1.8 dB) 800 ms 4 B 2演算 なし なし
メディアン (N=5) 1.7倍 (3.1 dB) 200 ms 5 B 10演算 あり なし
メディアン+EWMA 2.7倍 (1.9 dB) 300 ms 9 B 12演算 あり なし
カルマン 2.3倍 (2.3 dB) 150 ms 16 B 5演算 部分的 あり
パーティクル (100) 3.5倍 (1.5 dB) 400 ms 800 B 300演算 あり あり

主な観察:

メディアン+EWMAは組込みシステムに最適なバランスを提供:カルマンに近い性能とよりシンプルな実装および外れ値除去。

カルマンはQ/Rを適切にチューニングでき、適応挙動が必要な場合に最適。

パーティクルフィルタは過酷なマルチパスで優れるが、計算コストのためゲートウェイ専用。

SMAはレイテンシが問題にならない静的監視に適している。

## 10. 適応フィルタリング戦略

固定パラメータフィルタは、RSSI統計が環境やビーコンの移動性とともに変化するため最適でない。適応戦略は性能を大幅に向上させる:

### 10.1 移動アウェアEWMA

短いウィンドウのRSSI分散を使用してビーコンの移動を検出し、αを動的に調整する:

float adaptive_alpha(float *recent_rssi, uint8_t n) {
    float mean = 0;
    for (int i = 0; i < n; i++) mean += recent_rssi[i];
    mean /= n;

    float var = 0;
    for (int i = 0; i < n; i++) {
        float d = recent_rssi[i] - mean;
        var += d * d;
    }
    var /= n;

    if (var > 25.0f)      return 0.4f;   // 高速移動
    else if (var > 10.0f) return 0.25f;  // 低速移動
    else                  return 0.1f;   // 静止
}

### 10.2 環境アウェアカルマン

局所的なNLOS確率に基づいてRを調整する。直近の測定値が予測状態から大きく逸脱している場合、Rを一時的に増加させる:

void adaptive_kalman(kalman_rssi_t *kf, float measurement, float *residuals, uint8_t n) {
    float mean_res = 0;
    for (int i = 0; i < n; i++) mean_res += residuals[i];
    mean_res /= n;

    float res_var = 0;
    for (int i = 0; i < n; i++) {
        float d = residuals[i] - mean_res;
        res_var += d * d;
    }
    res_var /= n;

    if (res_var > kf->r * 1.5f) {
        kf->r = kf->r * 1.3f;
    } else {
        kf->r = kf->r * 0.95f;
    }
    if (kf->r < 5.0f) kf->r = 5.0f;
    if (kf->r > 100.0f) kf->r = 100.0f;

    kalman_update(kf, measurement);
}

### 10.3 HMMベースLOS/NLOS検出

隠れマルコフモデルは各測定値をLOSまたはNLOSとして分類し、異なるフィルタを適用できる:

LOS状態:低Rのカルマンを適用(測定値を信頼)

NLOS状態:高Rのカルマンを適用(測定値を割引)または完全に破棄

遷移行列は訓練データから経験的に学習される。オフィス環境の典型的な遷移確率:

次: LOS 次: NLOS
現在: LOS 0.92 0.08
現在: NLOS 0.35 0.65

これはLOSが持続する傾向(92%のLOS維持確率)を示し、NLOSもある程度持続的(65%)であることを意味する。HMMは85-90%の分類精度を達成し、混合環境でのフィルタ性能を大幅に向上させる。

## 11. 実装上の考慮事項

### 11.1 制約のあるMCUでの整数演算

8ビットまたは16ビットMCU(ビーコンタグで一般的)では、浮動小数点演算は高コストである。EWMAは固定小数点で実装できる:

// Q8形式(8小数ビット)の固定小数点EWMA
// alpha = 0.2 → alpha_q8 = 51 (0.2 * 256)
typedef struct {
    int16_t filtered;
    uint8_t initialized;
} ewma_fixed_t;

int8_t ewma_fixed_update(ewma_fixed_t *f, int8_t rssi) {
    int16_t rssi_q8 = (int16_t)rssi << 8;
    if (!f->initialized) {
        f->filtered = rssi_q8;
        f->initialized = 1;
    } else {
        f->filtered = (51 * rssi_q8 + 205 * f->filtered) >> 8;
    }
    return (int8_t)(f->filtered >> 8);
}

これは整数の乗算とシフトのみを使用し、浮動小数点ライブラリを必要としない。

### 11.2 アドバタイズ間隔の処理

ビーコンのアドバタイズ間隔は20 ms(高レート位置測位)から1000 ms(バッテリー節約モード)まで変動する。フィルタパラメータは間隔に応じてスケールする必要がある:

– 100 ms間隔、α=0.2:時定数 = 0.5秒

– 1000 ms間隔、α=0.2:時定数 = 5.0秒(遅すぎる)

解決策:所望の時定数τからαを計算する:

α = 1 - exp(-Δt / τ)

ここでΔtはアドバタイズ間隔、τは所望の時定数(例:0.5秒)。これにより間隔に関係なく一貫したフィルタ挙動が保証される。

### 11.3 マルチビーコンフィルタリング

複数のビーコンを同時に追跡する場合、各ビーコンに独自のフィルタインスタンスが必要である。典型的なゲートウェイ(ESP32、520 KB SRAM)のメモリ予算:

フィルタタイプ ビーコンあたりのメモリ 最大ビーコン数(50 KB予算)
EWMA (float) 8 B 6,250
カルマン (float) 32 B 1,562
メディアン+EWMA (N=5, int8) 14 B 3,571
パーティクル (100, float) 1,600 B 31

EWMAとカルマンは数千のビーコンにスケールする。パーティクルフィルタは数十に限定される。

## 12. デプロイメント推奨事項

オフィス、小売、倉庫、工業環境でのフィールドテストに基づき、推奨されるフィルタ設定を以下に示す:

### 近接検出(プッシュ通知、ゾーン入退室)

フィルタ:   メディアン (N=5) + EWMA (α=0.15)
間隔:       100-200 ms
σ_eff:      ~2.0 dB
レイテンシ: ~400 ms
備考:       メディアンがNLOS外れ値を除去し、EWMAが残りのノイズをスムージング。
            距離閾値にヒステリシスを使用(3mで入室、5mで退室)してバウンスを防止。

### 屋内位置測位(三辺測量、フィンガープリンティング)

フィルタ:   カルマン (Q=0.5, R=σ_measured²)
間隔:       100-300 ms
σ_eff:      ~2.3 dB
レイテンシ: ~150 ms
備考:       環境に合わせてRをチューニング。NLOSが多いエリアでは適応Rを使用。
            位置更新レートはスムーズなUIのために2-5 Hzとすべき。

### 資産追跡(RTLS、フォークリフト追跡)

フィルタ:   適応EWMA (α: 0.1-0.4、移動アウェア)
間隔:       200-500 ms
σ_eff:      ~2.5 dB (動的)
レイテンシ: 100-800 ms (動的)
備考:       RSSI分散で移動を検出し、αを切替。
            マルチパスが深刻な場合はゲートウェイでパーティクルフィルタを使用。

### 静的監視(在席検知、ルームレベルプレゼンス)

フィルタ:   EWMA (α=0.05) または SMA (N=20)
間隔:       500-1000 ms
σ_eff:      ~1.2 dB
レイテンシ: 2-5秒
備考:       プレゼンス検出ではレイテンシは許容可能。
            超低σにより信頼性の高いルームレベル分類が可能。

### 深刻なマルチパス(倉庫、工場)

フィルタ:   ゲートウェイでパーティクルフィルタ (50-100粒子)
            + モバイル受信機でカルマン
間隔:       100-200 ms
σ_eff:      ~1.5 dB
レイテンシ: ~400 ms
備考:       パーティクルフィルタがマルチモーダルRSSI分布を処理。
            ゲートウェイ側処理でタグの計算負荷を軽減。

## 結論

RSSIフィルタリングはオプションではない—ビーコンシステムにおける最も影響力のある信号処理ステップである。アルゴリズムの選択は、アプリケーションのレイテンシ許容度、計算予算、RF環境に依存する:

– クイックプロトタイピングと単純な近接用:α=0.2のEWMAで十分。

– 本番の近接検出用:メディアン+EWMAハイブリッドが最適な汎用選択。

– 位置測位システム用:環境チューニング済みQ/Rのカルマンフィルタ

– 過酷なマルチパス用:ゲートウェイ側のパーティクルフィルタ

最も一般的な間違いは、すべてのシナリオに単一の固定フィルタを使用することである。実際の環境は変化する—人が動き、ドアが開き、Wi-Fiチャネルがシフトする。移動性とNLOS条件を検出する適応戦略は、固定パラメータフィルタと比較して位置誤差を30-50%削減でき、追加ハードウェアコストは不要である。

フィルタは存在しない情報を復元できないことを忘れてはならない。ビーコンが1秒に1回しかアドバタイズしない場合、どれほどのフィルタリングを行ってもサブ秒のレイテンシは得られない。適切なアドバタイズレートから始め、精度要件を満たす最もシンプルなフィルタを選択し、測定が複雑性の必要性を証明した場合のみ複雑さを追加する。ハードウェア選択時には、構成可能なアドバタイズ間隔と出力電力をサポートする信頼性の高いBluetoothモジュールを検討する—これらのパラメータは、フィルタが処理する生のRSSI品質に直接影響する。