レーダー追尾のα-βフィルタの理論と導出と実装

レーダーのスクリーンを思い浮かべてください。回転するアンテナが1秒に1回、空を一巡し、そのたびに航空機からの反射波(エコー)が点として光ります。ところが、その点は毎回ぴったり同じ航跡上には乗りません。受信機の熱雑音、クラッタ、測角誤差などのために、観測された位置は真の位置の周りでチラチラと揺れます。もしレーダー画面の点をそのまま結んで「これが航空機の軌道だ」と言えば、ギザギザの折れ線になってしまい、次にどこへ向かうのかも予測できません。

α-βフィルタの1サイクル:予測→観測→更新の概念図

上の図はα-βフィルタが毎スキャンで回す「予測→観測→更新」の1サイクルを示しています。前時刻の推定値を等速度モデルで1ステップ進めて予測値を作り、実際の観測値との残差 $r_k$ を計算し、その残差に $\alpha$ と $\beta$ を掛けて位置と速度を同時に補正します。このシンプルな3手が何百スキャンも繰り返されることで、ノイズだらけの観測列から滑らかで信頼性の高い推定が得られるのです。

ここで必要になるのが追尾フィルタ(tracking filter)です。ノイズだらけの観測列から、滑らかで信頼できる位置・速度の推定値を取り出し、さらに次のスキャンで目標がどこに現れるかを予測する。その最も基本的で、いまも現場で広く使われているのが本記事の主役、α-βフィルタです。α-βフィルタは2つの定数 $\alpha$ と $\beta$ だけで動く非常に軽量なアルゴリズムでありながら、適切に設計すれば本格的なカルマンフィルタの定常状態とほぼ同じ性能を発揮します。

α-βフィルタを理解すると、次のような場面で役立つ見通しが得られます。

  • 航空管制・防空レーダー: 多数の航空機を同時に追尾する際、1目標あたりの計算量を最小化しつつ滑らかな航跡を維持する。組込みレーダープロセッサではいまも α-β(およびその拡張 α-β-γ)が現役です。
  • 車載レーダー・センサフュージョン: ミリ波レーダーが検出した先行車の距離・相対速度を平滑化し、ACC(アダプティブクルーズコントロール)や衝突被害軽減ブレーキの判断に使う。
  • カルマンフィルタへの橋渡し: α-βフィルタは「等速度モデルに対する定常カルマンフィルタ」そのものとして導けます。これを理解しておくと、より一般的なカルマンフィルタの理論の構造(予測・更新の2段構え、利得の意味)が驚くほどクリアになります。

本記事の内容

  • 追尾フィルタが解く問題と α-βフィルタの直感
  • 等速度運動モデル(状態空間表現)の構築
  • 予測ステップと観測残差による更新ステップの導出
  • 定常カルマンフィルタの極限としての α-βフィルタの位置づけ
  • トラッキングインデックスと最適な $\alpha, \beta$ の関係
  • $\alpha, \beta$ の大小がもたらす「追従遅れ vs 雑音抑圧」のトレードオフ
  • Python実装: 雑音つき距離観測の生成、α-βフィルタによる追尾、カルマンフィルタとの推定誤差比較

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

  • カルマンフィルタの理論 — α-βフィルタはこの定常解として導けます。予測・更新の枠組みを押さえておくと本記事の見通しが格段によくなります。
  • レーダー方程式 — 観測(距離計測)がどのように得られ、なぜ雑音が乗るのかの背景知識として有用です。
  • CFAR検出 — 追尾の前段にある「検出」を扱います。検出された点(プロット)が追尾フィルタの入力になります。

α-βフィルタとは

一定の速度で動く目標を、ノイズ越しに見つめる

まず最も素朴なアイデアから出発しましょう。航空機が一定の速度でまっすぐ飛んでいるとします。レーダーが一定間隔 $T$ ごとに距離を測ると、真の位置はおよそ $x, x+vT, x+2vT, \dots$ と等間隔に並ぶはずです。ところが実際の観測値には雑音が乗るので、ピタリとは並びません。

ここで2つの相反する欲求が生まれます。一つは「最新の観測を信じて、すぐ追いつきたい」。もう一つは「観測のチラつきに振り回されず、滑らかに保ちたい」。この2つは両立しません。最新値を100%信じれば滑らかさを失い、過去を信じすぎれば反応が鈍くなります。α-βフィルタは、この綱引きを2つのつまみ $\alpha$ と $\beta$ で調整する仕組みだと考えてください。

イメージとしては、車を運転していて前方の車との車間を一定に保ちたいとき、フロントガラス越しに見える相手の位置(観測)と、自分の頭の中にある「相手はだいたいこのくらいの速さで動いているはず」という予測を、ほどよく混ぜて運転判断をする、という状況に似ています。観測をどれだけ信じるかが $\alpha$、観測のズレから速度の見積もりをどれだけ修正するかが $\beta$ です。

予測と更新の2ステップ

α-βフィルタは、各スキャン時刻ごとに次の2ステップを繰り返します。

  1. 予測(predict): 前の時刻の位置・速度推定値から、「今このくらいの位置にいるはず」を等速度モデルで予測する。
  2. 更新(update): 実際の観測値と予測値のズレ(残差、イノベーション)を計算し、そのズレに $\alpha$ と $\beta$ を掛けて位置と速度の推定値を補正する。

この「予測してから観測でつじつまを合わせる」流れは、まさにカルマンフィルタと同じ骨格です。違いは、カルマンフィルタが毎ステップ誤差共分散を計算して最適な利得を求めるのに対し、α-βフィルタは利得を固定値 $\alpha, \beta$ で与えてしまう点にあります。固定するからこそ軽く、しかも定常状態では最適利得に一致させられる、というのがこのフィルタの妙味です。

それでは、この直感を数式に落とし込むために、まず追尾対象の運動を表す「等速度運動モデル」を厳密に定義しましょう。

α-βフィルタの状態空間モデルと信号フロー図

上の図はα-βフィルタを状態空間モデルとして俯瞰したものです。目標の運動(プロセス雑音つき状態遷移)と、レーダーの計測(観測雑音つき観測方程式)というプラント側のブロックに対し、フィルタ側が観測値 $z_k$ を受け取って推定値 $\hat{x}_{k|k}$ を出力し、次のステップへとフィードバックします。この閉ループ構造が、ノイズを除去しながら目標の動きを継続的に追いかける仕組みの根幹です。

等速度運動モデルの定式化

状態ベクトルと運動方程式

追尾したい量は「位置」と「速度」です。1次元(たとえば距離方向)で考え、時刻 $t_k = kT$ における真の位置を $x_k$、速度を $v_k$ とします。これらをまとめた状態ベクトルを次のように定義します。

$$ \bm{s}_k = \begin{bmatrix} x_k \\ v_k \end{bmatrix} $$

目標が完全な等速度で動くなら、サンプリング間隔 $T$ の間に位置は $vT$ だけ進み、速度は変わりません。すなわち、

$$ \begin{aligned} x_k &= x_{k-1} + T v_{k-1} \\ v_k &= v_{k-1} \end{aligned} $$

これを行列でまとめると、状態遷移は次のように書けます。

$$ \bm{s}_k = \bm{F}\,\bm{s}_{k-1} + \bm{w}_k, \qquad \bm{F} = \begin{bmatrix} 1 & T \\ 0 & 1 \end{bmatrix} $$

ここで $\bm{F}$ を状態遷移行列と呼びます。第1行が「新しい位置 = 古い位置 + $T$ × 速度」、第2行が「新しい速度 = 古い速度」を表しているわけです。$\bm{w}_k$ はプロセス雑音で、現実の目標は完全な等速度ではなく、操縦やわずかな加速で運動が揺らぐことを表現します。この揺らぎがあるからこそ、フィルタは過去だけに頼りきらず、観測も取り入れ続ける必要があるのです。

観測モデル

レーダーが測れるのは位置(たとえば距離)だけで、速度は直接測れないとします(ドップラーを使わない素朴な構成)。すると観測 $z_k$ は、真の位置に観測雑音 $n_k$ が加わったものになります。

$$ z_k = \bm{H}\,\bm{s}_k + n_k, \qquad \bm{H} = \begin{bmatrix} 1 & 0 \end{bmatrix} $$

観測行列 $\bm{H}=[1\ 0]$ は「状態ベクトルのうち位置成分だけが見える」ことを意味します。速度 $v_k$ は直接は見えませんが、位置の時間変化を通じて間接的に推定することになります。これがフィルタの腕の見せどころです。

観測雑音 $n_k$ は平均ゼロ・分散 $\sigma_z^2$ の白色雑音とします。プロセス雑音 $\bm{w}_k$ は、加速度の揺らぎ(分散 $\sigma_a^2$)に起因すると考えるのが標準的なモデルです。これらの大小関係が、後で出てくる「トラッキングインデックス」という1つの無次元量に集約され、最適な $\alpha, \beta$ を決めます。

ここまでで、追尾問題を「状態空間モデル」として表現できました。次は、この枠組みの上で α-βフィルタの予測式と更新式を組み立てていきましょう。

予測ステップの導出

記号の約束

推定値には記号を付けて区別します。時刻 $k$ における推定量を、

  • $\hat{x}_{k|k}$, $\hat{v}_{k|k}$: 時刻 $k$ までの観測をすべて使って得た、時刻 $k$ の位置・速度の推定値(フィルタ値、平滑値)
  • $\hat{x}_{k|k-1}$, $\hat{v}_{k|k-1}$: 時刻 $k-1$ までの観測を使って予測した、時刻 $k$ の位置・速度の予測値

と表します。縦棒の左が「いつの時刻の量か」、右が「いつまでの情報を使ったか」です。レーダー追尾の文献では予測値に「$p$(predicted)」、平滑値に「$s$(smoothed)」の添字を付けることも多いですが、本記事ではカルマンフィルタとの対応を見やすくするためこの記法を採用します。

予測式

予測ステップでは、前の時刻の推定値 $\hat{x}_{k-1|k-1}, \hat{v}_{k-1|k-1}$ を等速度モデル $\bm{F}$ で1ステップ進めるだけです。プロセス雑音 $\bm{w}_k$ は平均ゼロなので、予測には寄与しません。状態遷移 $\bm{s}_k = \bm{F}\bm{s}_{k-1}$ の各行をそのまま書き下すと、

$$ \begin{aligned} \hat{x}_{k|k-1} &= \hat{x}_{k-1|k-1} + T\,\hat{v}_{k-1|k-1} \\ \hat{v}_{k|k-1} &= \hat{v}_{k-1|k-1} \end{aligned} $$

位置の予測は「前回の位置に、前回の速度 × 経過時間を足す」、速度の予測は「前回の速度をそのまま使う」という、ごく自然な等速度外挿です。これがフィルタの「現状認識からの一手先読み」に相当します。

この予測値はあくまで「観測を見る前の見積もり」です。次に、実際の観測 $z_k$ が手に入ったとき、この予測をどう修正するか — それが更新ステップであり、$\alpha$ と $\beta$ が登場する核心部分です。

更新ステップの導出

観測残差(イノベーション)

観測 $z_k$ が得られたら、まず予測した位置とのズレを計算します。これを観測残差またはイノベーションと呼びます。

$$ r_k = z_k – \hat{x}_{k|k-1} $$

$r_k$ は「予測がどれだけ外れたか」を表す量です。$r_k$ が大きければ予測が実際とずれているので推定を大きく直す必要があり、小さければ予測はおおむね正しかったので少しだけ直せばよい、というのが基本方針です。重要なのは、位置の残差ひとつから、位置と速度の両方を直す点です。位置の予測が外れたということは、これまで見積もっていた速度も少しずれていた可能性が高い、と考えるわけです。

α-β更新式

α-βフィルタは、この残差 $r_k$ を使って位置と速度の推定値を次のように更新します。

$$ \begin{aligned} \hat{x}_{k|k} &= \hat{x}_{k|k-1} + \alpha\, r_k \\ \hat{v}_{k|k} &= \hat{v}_{k|k-1} + \frac{\beta}{T}\, r_k \end{aligned} $$

それぞれの式の意味を読み解きましょう。

位置の更新式は「予測位置に、残差の $\alpha$ 倍を足す」ものです。$\alpha=1$ なら $\hat{x}_{k|k} = \hat{x}_{k|k-1} + r_k = z_k$ となり、観測値をそのまま採用します(予測を完全に無視)。逆に $\alpha=0$ なら観測を一切信じず予測のまま進みます。実用上は $0<\alpha<1$ とし、予測と観測の間を取ります。$\alpha$ が大きいほど観測寄り(反応が速いが滑らかさを失う)、小さいほど予測寄り(滑らかだが反応が鈍い)です。

速度の更新式は「予測速度に、残差の $\beta/T$ 倍を足す」ものです。なぜ $T$ で割るのか、直感的に説明します。残差 $r_k$ は「位置」の次元を持つズレです。これを「速度」の修正量に変換するには、時間で割って速度の次元にそろえる必要があります。位置のズレ $r_k$ が時間 $T$ の間に蓄積したと考えれば、速度の見積もり誤差はおよそ $r_k/T$ 程度であり、その $\beta$ 倍だけ速度を修正する、という構造になっているのです。

なぜ位置の残差で速度を直せるのか

ここはα-βフィルタの肝なので、もう少し丁寧に考えましょう。速度は直接観測できません。しかし、もし速度の見積もりが小さすぎれば、予測位置は毎回実際より手前に来るので残差 $r_k$ は正の値に偏ります。逆に速度を大きく見積もりすぎれば残差は負に偏ります。つまり、残差の符号と大きさが、速度の見積もり誤差の情報を間接的に運んでいるのです。$\beta$ はこの間接情報をどれだけ速度修正に反映するかのつまみだと理解できます。

このように、α-βフィルタは「予測 → 残差計算 → 位置と速度を $\alpha, \beta$ で補正」という3手で1サイクルを回します。残る大きな疑問は「では $\alpha$ と $\beta$ をどう選べばよいのか」です。やみくもに選ぶのではなく、実はカルマンフィルタの理論がその最適値を教えてくれます。次節でその対応関係を導きましょう。

トラッキングインデックスΛと最適利得αβの関係

上のグラフは、Benedict-Bordner臨界制動解から求めた最適な $\alpha$(青)と $\beta$(緑)をトラッキングインデックス $\Lambda$ の関数として示しています。$\Lambda$ が小さい(目標が等速度的・観測が汚い)ほど両利得は0に近づき、予測を信頼する設計になります。逆に $\Lambda$ が大きい(目標が機動的・観測が綺麗)ほど $\alpha$ は1に近づき、観測をほぼそのまま採用します。この図を手元に置いておけば、$\sigma_a$ と $\sigma_z$ を見積もるだけで最適な利得候補をすぐ読み取れます。

カルマンフィルタの定常解としての位置づけ

カルマンフィルタの更新式との対応

カルマンフィルタの理論では、更新ステップは次の形をしていました。

$$ \hat{\bm{s}}_{k|k} = \hat{\bm{s}}_{k|k-1} + \bm{K}_k\left(z_k – \bm{H}\hat{\bm{s}}_{k|k-1}\right) $$

ここで $\bm{K}_k$ はカルマンゲインで、状態の次元(位置・速度の2成分)に合わせて $2\times1$ のベクトルになります。$\bm{H}=[1\ 0]$ なので、括弧の中はまさに我々の残差 $r_k = z_k – \hat{x}_{k|k-1}$ です。カルマンゲインの成分を $\bm{K}_k = [K_x,\ K_v]^\top$ と書けば、更新式は成分ごとに、

$$ \begin{aligned} \hat{x}_{k|k} &= \hat{x}_{k|k-1} + K_x\, r_k \\ \hat{v}_{k|k} &= \hat{v}_{k|k-1} + K_v\, r_k \end{aligned} $$

となります。これを α-β更新式と見比べると、対応関係は一目瞭然です。

$$ K_x = \alpha, \qquad K_v = \frac{\beta}{T} $$

つまり、α-βフィルタとは「カルマンゲインを定数に固定したカルマンフィルタ」にほかなりません。本来カルマンフィルタのゲインは誤差共分散 $\bm{P}_k$ に応じて毎ステップ変化しますが、等速度モデルで観測雑音・プロセス雑音が定常なら、$\bm{P}_k$ は反復のうちに一定値に収束します。その収束した共分散が与える定常カルマンゲインを $\alpha, \beta$ として採用したものが α-βフィルタなのです。

リカッチ方程式の定常解

ゲインが定常値に落ち着くことは、カルマンフィルタの誤差共分散が満たす離散時間リカッチ方程式の定常解として記述できます。1ステップの共分散の更新は、予測ステップ

$$ \bm{P}_{k|k-1} = \bm{F}\,\bm{P}_{k-1|k-1}\,\bm{F}^\top + \bm{Q} $$

と、更新ステップ

$$ \bm{P}_{k|k} = (\bm{I} – \bm{K}_k \bm{H})\,\bm{P}_{k|k-1} $$

からなります。ここで $\bm{Q}$ はプロセス雑音共分散、$\bm{K}_k = \bm{P}_{k|k-1}\bm{H}^\top(\bm{H}\bm{P}_{k|k-1}\bm{H}^\top + \sigma_z^2)^{-1}$ はカルマンゲインです。これらを反復すると $\bm{P}_{k|k-1} \to \bm{P}_\infty$(定数)に収束し、対応する定常ゲインが $\alpha, \beta$ を与えます。一般には連立方程式を解く必要がありますが、ありがたいことに等速度モデルでは閉じた形の答えが知られています。その鍵となるのが、次に導入する1つの無次元パラメータです。

トラッキングインデックス

プロセス雑音を「区間ごとに分散 $\sigma_a^2$ の加速度がかかる」モデルとすると、その共分散は

$$ \bm{Q} = \sigma_a^2 \begin{bmatrix} T^4/4 & T^3/2 \\ T^3/2 & T^2 \end{bmatrix} $$

の形になります(位置への寄与が $\tfrac12 a T^2$、速度への寄与が $aT$ であることに対応します)。観測雑音の分散を $\sigma_z^2$ とすると、プロセス雑音と観測雑音の相対的な強さを表す無次元量としてトラッキングインデックス $\Lambda$ を次のように定義します。

$$ \Lambda = \frac{\sigma_a\, T^2}{\sigma_z} $$

$\Lambda$ の意味を直感的に読むと、分子は「1区間でプロセス雑音(加速度の揺らぎ)が生む位置のばらつき」、分母は「観測のばらつき」です。つまり $\Lambda$ は「目標がどれだけ予測不能に動くか」対「観測がどれだけ汚いか」の比です。$\Lambda$ が大きいほど目標が機敏に動く(または観測が綺麗)ので観測を信じるべきで、$\alpha, \beta$ は大きくすべきです。$\Lambda$ が小さいほど目標が素直に等速度で動く(または観測が汚い)ので予測を信じ、$\alpha, \beta$ は小さくすべきです。この定性的な見立てを、次に定量式へと落とし込みます。

Benedict–Bordner(臨界制動)の最適解

定常リカッチ方程式の解として、滑らかさと追従性のバランスを最適化する古典的な設計がBenedict–Bordner解です。これは、$\alpha$ と $\beta$ の間に次の関係を課したうえで $\Lambda$ から $\alpha, \beta$ を決めます。まず両者を結ぶ拘束条件は、

$$ \beta = \frac{\alpha^2}{2 – \alpha} $$

です。これは「過渡応答が振動せず、滑らかに目標に収束する(臨界制動に近い)」ための条件で、後述するシミュレーションでも overshoot のない素直な追従として現れます。次に、トラッキングインデックス $\Lambda$ と $\beta$ の関係は、

$$ \Lambda^2 = \frac{\beta^2}{1 – \alpha} $$

で与えられます。この2式を連立させれば、与えられた $\Lambda$ に対する最適な $\alpha, \beta$ が一意に定まります。具体的には、$\beta = \alpha^2/(2-\alpha)$ を上式に代入して $\alpha$ について解けば閉じた形が得られますが、実用上は数値的に解くのが簡単です。

直感の確認をしておきましょう。$\Lambda \to 0$(目標が完全な等速度・観測が汚い)では $\alpha, \beta \to 0$ となり、フィルタは予測(過去の平均)をほぼそのまま信じます。逆に $\Lambda \to \infty$(目標が激しく機動・観測が綺麗)では $\alpha \to 1$ となり、観測値をほぼ生で採用します。先ほどの定性的な見立てと完全に一致します。

このように、追尾対象の機動性と観測雑音という2つの物理量さえ見積もれば、最適な $\alpha, \beta$ が原理的に一意に決まります。とはいえ、$\alpha, \beta$ を変えたときフィルタの挙動が具体的にどう変わるのかを体感しておくことは設計上きわめて大切です。次の節で、まずは小さな具体例で手を動かし、その後 Python で本格的に可視化します。

具体例: 1ステップだけ手で追ってみる

数値を入れて1サイクルを手計算してみましょう。サンプリング間隔 $T = 1$ 秒、$\alpha = 0.5$, $\beta = 0.2$ とします。前の時刻の推定値が $\hat{x}_{k-1|k-1} = 100$ m, $\hat{v}_{k-1|k-1} = 10$ m/s だったとします。

まず予測ステップです。位置の予測は前回位置に「速度 × $T$」を足して、

$$ \hat{x}_{k|k-1} = 100 + 1 \times 10 = 110\ \text{m}, \qquad \hat{v}_{k|k-1} = 10\ \text{m/s} $$

ここで観測値が $z_k = 114$ m だったとします。残差は予測位置とのズレなので、

$$ r_k = z_k – \hat{x}_{k|k-1} = 114 – 110 = 4\ \text{m} $$

予測より $4$ m 先に観測されたので、「位置を少し前へ、かつ速度も少し上方修正すべき」と判断されます。更新式に代入すると、位置は残差の $\alpha=0.5$ 倍だけ進めて、

$$ \hat{x}_{k|k} = 110 + 0.5 \times 4 = 112\ \text{m} $$

速度は残差を $\beta/T = 0.2/1 = 0.2$ 倍して足し、

$$ \hat{v}_{k|k} = 10 + 0.2 \times 4 = 10.8\ \text{m/s} $$

となります。観測値 $114$ m をそのまま信じれば飛びつきすぎ、予測 $110$ m に留まれば反応が鈍い。その中間の $112$ m に落ち着き、同時に速度も $10$ から $10.8$ m/s へとわずかに上方修正されました。位置の残差ひとつが、位置と速度の両方をつじつまの合う方向へ動かしている様子が見て取れます。

この1サイクルを毎スキャン繰り返すのが α-βフィルタです。手計算では1ステップしか追えませんが、何百ステップにもわたる挙動 — 特に $\alpha, \beta$ を変えたときのトレードオフ — はコンピュータに任せましょう。次節で Python により実装し、可視化します。

Pythonによる実装と可視化

雑音つき観測データの生成

まず、等速度で動く目標の真の軌道を作り、そこに観測雑音を加えた距離観測を生成します。途中で速度が変わる「機動」も入れて、フィルタの追従性を試せるようにします。

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)

# シミュレーション設定
T = 1.0          # サンプリング間隔 [s]
N = 120          # スキャン回数
sigma_z = 8.0    # 観測雑音の標準偏差 [m]

# 真の軌道を生成(途中で速度が変化する機動を含む)
true_x = np.zeros(N)   # 真の位置 [m]
true_v = np.zeros(N)   # 真の速度 [m/s]
true_x[0] = 0.0
true_v[0] = 30.0       # 初速 30 m/s
for k in range(1, N):
    v = true_v[k-1]
    if 40 <= k < 70:    # この区間で加速(機動)
        v += 1.5
    true_v[k] = v
    true_x[k] = true_x[k-1] + T * true_v[k-1]

# 観測値(真の位置 + 白色雑音)
z = true_x + rng.normal(0.0, sigma_z, size=N)

plt.figure(figsize=(9, 5))
plt.plot(true_x, 'k-', lw=2, label='True position')
plt.plot(z, 'r.', ms=5, alpha=0.6, label='Noisy measurements')
plt.xlabel('Scan index k')
plt.ylabel('Position [m]')
plt.title('True trajectory and noisy radar measurements')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

真の軌道と雑音つきレーダー観測値

このグラフでは、黒い実線が真の位置、赤い点が雑音つきの観測値です。観測点が真の軌道の周りで上下に散らばっている様子と、スキャン40〜70のオレンジ帯(機動区間)で軌道の傾き(=速度)が増している「機動」がはっきり見て取れます。この散らばった点列だけを入力として、滑らかな位置推定と、見えないはずの速度の推定を取り出すのがこれからの課題です。

α-βフィルタの実装

次に、導出した予測・更新式をそのまま関数にします。初期化は、最初の2点から速度を概算する一般的な方法を使います。

def alpha_beta_filter(z, T, alpha, beta):
    """α-βフィルタ。観測列zから位置・速度推定を返す"""
    N = len(z)
    x_est = np.zeros(N)   # 位置推定(フィルタ値)
    v_est = np.zeros(N)   # 速度推定

    # 初期化: 最初の2観測から位置と速度を概算
    x_est[0] = z[0]
    x_est[1] = z[1]
    v_est[0] = (z[1] - z[0]) / T
    v_est[1] = (z[1] - z[0]) / T

    for k in range(2, N):
        # 予測ステップ(等速度外挿)
        x_pred = x_est[k-1] + T * v_est[k-1]
        v_pred = v_est[k-1]
        # 残差(イノベーション)
        r = z[k] - x_pred
        # 更新ステップ
        x_est[k] = x_pred + alpha * r
        v_est[k] = v_pred + (beta / T) * r
    return x_est, v_est

# 中庸な利得で追尾
x_ab, v_ab = alpha_beta_filter(z, T, alpha=0.5, beta=0.15)

plt.figure(figsize=(9, 5))
plt.plot(true_x, 'k-', lw=2, label='True position')
plt.plot(z, 'r.', ms=4, alpha=0.4, label='Measurements')
plt.plot(x_ab, 'b-', lw=1.8, label=r'$\alpha$-$\beta$ estimate ($\alpha$=0.5, $\beta$=0.15)')
plt.xlabel('Scan index k')
plt.ylabel('Position [m]')
plt.title('Alpha-beta filter position tracking')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

α-βフィルタによる位置推定結果

青い実線がα-βフィルタの位置推定です。赤い観測点のチラつきが大きく均(なら)され、黒い真の軌道に近い滑らかな曲線になっていることがわかります。さらに注目すべきは、機動区間(スキャン40〜70)でも推定が遅れながらも真値を追いかけている点です。等速度モデルしか仮定していないにもかかわらず、残差を通じて速度が修正され続けるため、加速にもある程度追従できています。

推定された速度の確認

α-βフィルタは位置だけでなく速度も推定します。直接観測できない速度がどの程度復元できているかを見てみましょう。

plt.figure(figsize=(9, 5))
plt.plot(true_v, 'k-', lw=2, label='True velocity')
plt.plot(v_ab, 'g-', lw=1.6, label=r'$\alpha$-$\beta$ velocity estimate')
plt.xlabel('Scan index k')
plt.ylabel('Velocity [m/s]')
plt.title('Velocity estimate from position-only measurements')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

位置観測から間接推定した速度推定グラフ

緑の線が推定速度、黒が真の速度です。位置しか観測していないのに、速度が概ね正しく復元されている点が印象的です。機動が始まると推定速度が階段状に立ち上がり、機動が終わると元の値へ戻っていく様子も追えています。ただし、雑音の影響で速度推定は位置推定よりもギザギザしており、機動の立ち上がり・立ち下がりには明確な遅れが見えます。この「遅れ」と「ギザつき」こそが、次に調べる $\alpha, \beta$ のトレードオフの正体です。

α, βの大小によるトレードオフ

$\alpha, \beta$ を小さくすると滑らかになりますが反応が鈍り、大きくすると反応が速くなりますが雑音に振り回されます。3通りの設定で比較してみましょう。

settings = [
    (0.15, 0.01, 'small gains (smooth, slow)'),
    (0.5,  0.15, 'medium gains'),
    (0.85, 0.5,  'large gains (responsive, noisy)'),
]

plt.figure(figsize=(10, 6))
plt.plot(true_x, 'k-', lw=2.5, label='True position')
plt.plot(z, 'r.', ms=3, alpha=0.3, label='Measurements')
colors = ['#1f77b4', '#2ca02c', '#ff7f0e']
for (a, b, lab), col in zip(settings, colors):
    x_est, _ = alpha_beta_filter(z, T, a, b)
    plt.plot(x_est, color=col, lw=1.6,
             label=fr'$\alpha$={a}, $\beta$={b}: {lab}')
plt.xlabel('Scan index k')
plt.ylabel('Position [m]')
plt.title('Effect of alpha-beta gains on tracking')
plt.legend(fontsize=9)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

小中大利得による追尾挙動比較

このグラフは $\alpha, \beta$ の効果を端的に示しています。小利得(青)は最も滑らかですが、機動区間で真値から大きく外れ、追従が明らかに遅れています。大利得(橙)は機動への追従は速いものの、観測のチラつきをそのまま拾ってギザギザした推定になっています。中庸(緑)はその中間で、滑らかさと追従性のバランスが取れています。「滑らかさが欲しければ利得を下げ、機動への追従が欲しければ利得を上げる。両取りはできない」という追尾フィルタ設計の本質が、この1枚に凝縮されています。

定常状態でのトレードオフを定量化する

定常区間(等速度で動いている部分)での雑音抑圧と、機動区間での追従遅れを、$\alpha$ を振りながら数値で評価してみましょう。Benedict–Bordner拘束 $\beta=\alpha^2/(2-\alpha)$ を使って $\alpha$ から $\beta$ を決め、それぞれについて2種類の誤差を測ります。

alphas = np.linspace(0.05, 0.95, 25)
steady_idx = np.arange(10, 38)     # 等速度区間(雑音抑圧を測る)
maneuver_idx = np.arange(40, 65)   # 機動区間(追従遅れを測る)

rms_steady = []
rms_maneuver = []
for a in alphas:
    b = a**2 / (2 - a)             # 臨界制動拘束
    x_est, _ = alpha_beta_filter(z, T, a, b)
    err = x_est - true_x
    rms_steady.append(np.sqrt(np.mean(err[steady_idx]**2)))
    rms_maneuver.append(np.sqrt(np.mean(err[maneuver_idx]**2)))

plt.figure(figsize=(9, 5))
plt.plot(alphas, rms_steady, 'o-', label='RMS error in steady segment (noise)')
plt.plot(alphas, rms_maneuver, 's-', label='RMS error in maneuver segment (lag)')
plt.xlabel(r'$\alpha$  (with $\beta=\alpha^2/(2-\alpha)$)')
plt.ylabel('RMS position error [m]')
plt.title('Noise suppression vs maneuver lag trade-off')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

雑音抑圧と機動追従のトレードオフ曲線

この図がトレードオフの核心です。等速度区間のRMS誤差(雑音由来)は $\alpha$ が大きいほど増加します — 観測を信じるほどチラつきを拾うからです。一方、機動区間のRMS誤差(追従遅れ由来)は $\alpha$ が大きいほど減少します — 反応が速くなるからです。2つの曲線が交差するあたりが、雑音抑圧と追従性をバランスさせる「ちょうどよい $\alpha$」の目安になります。どちらを重視するかは用途次第で、静かな等速目標なら左側(小利得)、機動目標なら右側(大利得)を選ぶことになります。

カルマンフィルタとの推定誤差比較

最後に、同じ等速度モデルに対して毎ステップ最適利得を計算するカルマンフィルタを実装し、固定利得のα-βフィルタと推定精度を比較します。α-βが「定常カルマン」であることを踏まえれば、両者は定常区間でほぼ同等になるはずです。

def kalman_cv(z, T, sigma_z, sigma_a):
    """等速度モデルの標準カルマンフィルタ"""
    F = np.array([[1, T], [0, 1]])
    H = np.array([[1.0, 0.0]])
    # 加速度ゆらぎによるプロセス雑音共分散
    Q = sigma_a**2 * np.array([[T**4/4, T**3/2],
                               [T**3/2, T**2]])
    R = np.array([[sigma_z**2]])
    x = np.array([[z[0]], [0.0]])      # 初期状態
    P = np.eye(2) * 1e3                 # 大きめの初期不確かさ
    x_hist = np.zeros(len(z))
    for k in range(len(z)):
        # 予測
        x = F @ x
        P = F @ P @ F.T + Q
        # 更新
        S = H @ P @ H.T + R
        K = P @ H.T @ np.linalg.inv(S)
        x = x + K @ (z[k] - H @ x)
        P = (np.eye(2) - K @ H) @ P
        x_hist[k] = x[0, 0]
    return x_hist

x_kf = kalman_cv(z, T, sigma_z, sigma_a=1.2)
x_ab, _ = alpha_beta_filter(z, T, alpha=0.5, beta=0.15)

plt.figure(figsize=(9, 5))
plt.plot(true_x, 'k-', lw=2, label='True')
plt.plot(x_ab, 'b-', lw=1.5, label=r'$\alpha$-$\beta$')
plt.plot(x_kf, 'm--', lw=1.5, label='Kalman (CV model)')
plt.xlabel('Scan index k')
plt.ylabel('Position [m]')
plt.title('Alpha-beta vs Kalman filter')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

rms_ab = np.sqrt(np.mean((x_ab - true_x)**2))
rms_kf = np.sqrt(np.mean((x_kf - true_x)**2))
print(f"RMS error  alpha-beta : {rms_ab:.3f} m")
print(f"RMS error  Kalman     : {rms_kf:.3f} m")

α-βフィルタとカルマンフィルタの推定誤差比較

青がα-βフィルタ、マゼンタの破線がカルマンフィルタです。2本の線は等速度区間ではほぼ重なり、α-βフィルタが定常カルマンフィルタを良く近似していることが視覚的に確認できます。出力されるRMS誤差も近い値になります。違いが出るのは機動の立ち上がり付近で、カルマンフィルタは誤差共分散を一時的に増やして利得を自動的に上げ、機動に素早く反応できます。一方、利得を固定したα-βフィルタはこの自動調整ができないため、機動への追従がやや遅れます。これが「計算は軽いが適応性に欠ける」というα-βフィルタの本質的な性質であり、機動目標には利得を自動調整する拡張(IMM など)が使われる理由でもあります。

スキャン全体での誤差推移

最後に、両フィルタの瞬時誤差の絶対値を時系列で重ねて、どの区間で差が出るかを明確にします。

plt.figure(figsize=(9, 5))
plt.plot(np.abs(x_ab - true_x), 'b-', lw=1.3, label=r'$|$error$|$  $\alpha$-$\beta$')
plt.plot(np.abs(x_kf - true_x), 'm-', lw=1.3, label=r'$|$error$|$  Kalman')
plt.axvspan(40, 70, color='orange', alpha=0.15, label='maneuver')
plt.xlabel('Scan index k')
plt.ylabel('Absolute position error [m]')
plt.title('Instantaneous error over time')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

オレンジの帯が機動区間です。この区間の入口で両フィルタとも誤差が跳ね上がりますが、カルマンフィルタの方がやや早く誤差を収束させているのが読み取れます。等速度区間(帯の外)では2本の誤差はほぼ同レベルで推移しており、定常状態での両者の等価性を裏付けています。結論として、観測雑音とプロセス雑音が定常で目標が概ね等速度であれば、軽量なα-βフィルタで本格的なカルマンフィルタにほぼ並ぶ性能が得られるのです。

α-β-γフィルタ:加速度まで状態に追加する

α-βフィルタが等速度モデルを前提とする以上、持続的な加速(機動)が続く局面では追従遅れが避けられません。この弱点を補う自然な拡張が、加速度 $a_k$ を状態に追加した α-β-γフィルタです。3番目のゲイン $\gamma$ が加速度の推定量を更新し、次ステップの予測を等加速度外挿で行うことで、機動中の追従遅れを大幅に減らすことができます。

def alpha_beta_gamma_filter(z, T, alpha, beta, gamma):
    N = len(z)
    x_est = np.zeros(N)
    v_est = np.zeros(N)
    a_est = np.zeros(N)
    x_est[0] = z[0]
    x_est[1] = z[1]
    v_est[0] = (z[1] - z[0]) / T
    v_est[1] = v_est[0]
    for k in range(2, N):
        # 予測(等加速度外挿)
        x_pred = x_est[k-1] + T * v_est[k-1] + 0.5 * T**2 * a_est[k-1]
        v_pred = v_est[k-1] + T * a_est[k-1]
        a_pred = a_est[k-1]
        r = z[k] - x_pred
        # 更新(α, β, γ の3つで補正)
        x_est[k] = x_pred + alpha * r
        v_est[k] = v_pred + (beta / T) * r
        a_est[k] = a_pred + (gamma / (0.5 * T**2)) * r
    return x_est, v_est, a_est

x_abg, v_abg, a_abg = alpha_beta_gamma_filter(z, T, alpha=0.5, beta=0.20, gamma=0.05)

α-β-γフィルタとα-βフィルタの追従性比較

上段が位置推定、下段が速度推定の比較です。機動区間(スキャン40〜70)に注目すると、α-βフィルタ(青)に比べてα-β-γフィルタ(赤破線)の速度推定がより素早く立ち上がり、加速の開始に追従できていることが見て取れます。一方、等速度区間では3つのゲインを持つ分だけわずかにギザつきが増える傾向があります。加速度を明示的に推定することで機動追従は向上しますが、ゲインの設計が2パラメータから3パラメータへと複雑になるというトレードオフも伴います。

まとめ

本記事では、レーダー追尾の基礎であるα-βフィルタについて、直感・導出・実装の3面から解説しました。

  • 追尾フィルタの役割: ノイズだらけの観測列から滑らかな位置・速度推定を取り出し、次のスキャンの位置を予測する。α-βフィルタはその最も軽量な実装である。
  • 予測と更新の2ステップ: 等速度モデルで1手先を予測し、観測残差 $r_k = z_k – \hat{x}_{k|k-1}$ に $\alpha$ と $\beta/T$ を掛けて位置・速度を補正する。位置の残差ひとつから速度まで直せるのが核心。
  • カルマンフィルタの定常解: α-βフィルタはカルマンゲインを定数 $K_x=\alpha,\ K_v=\beta/T$ に固定したものに等しい。等速度モデルの定常リカッチ解が最適な $\alpha, \beta$ を与える。
  • トラッキングインデックス: 機動性と観測雑音の比 $\Lambda = \sigma_a T^2/\sigma_z$ が1つの無次元量に集約され、Benedict–Bordner拘束 $\beta=\alpha^2/(2-\alpha)$ とともに最適利得を決める。
  • トレードオフ: 利得を上げれば機動追従が速くなるが雑音に弱く、下げれば滑らかだが追従が遅れる。両取りはできない。
  • 検証: Pythonシミュレーションで、α-βフィルタが定常区間でカルマンフィルタにほぼ並ぶ性能を示し、機動区間ではカルマンの自動利得調整に一歩譲ることを確認した。

α-βフィルタを足がかりにすると、追尾の世界が広がります。加速度まで状態に加えたα-β-γフィルタは機動目標に強く、利得を自動調整したいならカルマンフィルタの理論へ、目標が複数あって観測の対応付けが必要なら最近傍法や確率的データアソシエーション、運動モデルが複数あるなら IMM(相互作用多重モデル)へと進みます。観測がどう得られるかの背景はレーダー方程式、追尾の前段にある検出はCFAR検出で押さえておくと、レーダー信号処理の全体像がつながります。

次のステップとして、以下の記事も参考にしてください。

カルマンフィルタの理論
α-βフィルタの定常解が対応する、より一般的なカルマンフィルタを解説します。毎ステップ最適なゲインを求める予測・更新の枠組みと、リカッチ方程式による誤差共分散の更新を詳しく扱います。
画像なし
モノパルス追尾レーダー
1回の送受信パルスで目標の角度誤差信号を取り出すモノパルス方式を解説します。追尾フィルタへ入力される角度計測値がどのように生成されるかの理解に直結します。