🐳

レーダーセンサーのrange bin選択:固定方式と動的方式の実装比較

に公開

はじめに

レーダーセンサーを用いた非接触バイタルサイン計測において、「どの距離ビン(range bin)を解析対象とするか」は測定精度に直結する重要な問題です。

本記事では、固定ビン方式動的ビン選択方式という2つのアプローチを比較し、それぞれの特徴と実装方法について検討します。

固定ビン方式と動的ビン方式の違い

  • 固定ビン方式:測定開始時に手動で1点の距離ビンを設定し、測定中は変更しない
  • 動的ビン方式:振幅スペクトルから周期的にピーク位置を自動検出し、複数候補から最適なビンを選択

固定ビン方式は実装がシンプルですが、対象者の体動や姿勢変化に対応できません。一方、動的ビン方式は環境変化に対応できる反面、アルゴリズムの複雑さが増します。

2. この記事で説明すること

  • 距離スイープにおけるピークビン決定の一般的な手法
  • 固定ビン方式の実装と限界
  • 動的ピーク検出の実装方法(振幅ベース)
  • 周期的更新による位相データ連続性の確保
  • 実際のコード例と結果の比較
  • 各方式の適用場面と今後の発展方向

3. 距離スイープにおけるピークビン決定の一般的な手法

3.1 距離ビン(Range Bin)とは

レーダーセンサーは、送信した電波が対象物で反射して戻ってくるまでの時間から距離を計測します。この距離空間を離散化したものが距離ビンです。

例えば、距離分解能が2.5mmの場合:

  • ビン0: 0-2.5mm
  • ビン1: 2.5-5.0mm
  • ビン80: 200-202.5mm

3.2 ピークビン決定の一般的手法

距離スイープから最適なビンを選択する方法として、以下のアプローチが一般的です:

(1) 最大振幅法(Maximum Amplitude Method)

最もシンプルな方法で、振幅スペクトルが最大となるビンを選択します。

peak_bin = np.argmax(amplitude_spectrum)

利点:実装が簡単、計算コストが低い
欠点:ノイズや不要な反射に影響されやすい

(2) SNRベース選択

信号対雑音比(SNR)が最も高いビンを選択します。

# ノイズフロアの推定
noise_floor = np.median(amplitude_spectrum)
# SNRの計算
snr = amplitude_spectrum - noise_floor
peak_bin = np.argmax(snr)

(3) 探索範囲限定法

事前知識に基づいて探索範囲を制限し、その範囲内でピークを検出します。

search_start = 60  # 150mm付近
search_end = 120   # 300mm付近
search_region = amplitude_spectrum[search_start:search_end]
relative_peak = np.argmax(search_region)
peak_bin = search_start + relative_peak

利点:不要な反射(床、天井など)を除外できる
欠点:範囲設定が不適切だと対象を見逃す

(4) CFAR(Constant False Alarm Rate)検出

レーダー信号処理で広く使われる手法で、局所的なノイズレベルを考慮してターゲットを検出します。

(5) Presence Detector(存在検出器)

複数の距離ビンから動きや存在を統合的に判定する高度な手法です。

3.3 呼吸検出における課題

呼吸による胸部の動きは微小(0.1-10mm程度)であり、以下の課題があります:

  • 体動による距離変化:数cm〜数十cm移動すると最適ビンが変わる
  • 姿勢変化:座位→仰臥位などで反射特性が変化
  • 複数反射点:胸部、腹部、腕などから複数のピークが現れる

そのため、単純な最大振幅法では不十分で、時間的連続性を考慮した動的選択が必要になります。

4. 実装:固定ビンと動的ビン選択

4.1 固定ビン方式の実装

最もシンプルなアプローチで、初期設定した1点のビンを使い続けます。

class RealTimeVitalAnalyzer:
    def __init__(self, target_position=80):
        # 固定ビン位置を設定(例:ビン80 = 200mm付近)
        self.target_position = target_position
        self.enable_auto_peak = False  # 自動検出を無効化
    
    def process_new_frame(self, iq_data):
        # 距離スイープ全体を平均化
        if iq_data.ndim == 2:
            averaged_sweep = np.mean(iq_data, axis=0)
        else:
            averaged_sweep = iq_data
        
        # 固定ビンのIQ値を抽出
        target_iq = averaged_sweep[self.target_position]
        target_phase = np.angle(target_iq)
        
        # 位相データをバッファに保存
        self.iq_buffer.append(target_iq)
        
        # 以降、位相解析処理...

特徴

  • コードが単純で理解しやすい
  • 処理負荷が低い
  • 対象が静止している場合は問題なく動作

限界

  • 対象者が動くと最適ビンがずれる
  • 位相データに不連続(ジャンプ)が生じる可能性
  • 複数の対象や反射源に対応できない

4.2 動的ピーク検出の実装

振幅スペクトルから周期的にピーク位置を検出し、解析ビンを更新します。

ステップ1:ピーク検出メソッド

def detect_peak_position(self, amplitude_data):
    """振幅データから最大ピーク位置を自動検出"""
    try:
        # 探索範囲を設定(例:ビン60-120 = 150-300mm)
        search_start = max(0, self.peak_search_start)
        search_end = min(len(amplitude_data), self.peak_search_end)
        
        if search_end <= search_start:
            return self.target_position  # 現在値を維持
        
        # 探索範囲内で最大振幅のビンを検出
        search_region = amplitude_data[search_start:search_end]
        relative_peak_idx = np.argmax(search_region)
        absolute_peak_position = search_start + relative_peak_idx
        
        return absolute_peak_position
        
    except Exception as e:
        return self.target_position  # エラー時は現在値を維持

ステップ2:周期的更新ロジック

位相データの連続性を保つため、毎フレームではなく一定間隔で更新します。

def process_new_frame(self, iq_data):
    # 振幅スペクトルを計算
    amplitude_data = self.calculate_amplitude(averaged_sweep)
    
    # 自動ピーク検出(100フレームごとに更新)
    if self.enable_auto_peak and (self.frame_count % self.peak_update_interval == 0):
        if amplitude_data is not None:
            new_peak_position = self.detect_peak_position(amplitude_data)
            
            # ピーク位置が変化した場合のみ更新
            if new_peak_position != self.target_position:
                print(f"Peak position updated: {self.target_position}{new_peak_position}")
                self.target_position = new_peak_position
    
    # 現在の解析対象ビンからIQ値を抽出
    target_iq = averaged_sweep[self.target_position]
    target_phase = np.angle(target_iq)
    
    # 以降の処理...

重要なパラメータ

  • peak_update_interval:更新頻度(例:100フレーム = 5秒間隔、20fps時)
  • peak_search_start, peak_search_end:探索範囲(不要な反射を除外)

ステップ3:振幅計算

IQデータから振幅スペクトル(dB表示)を計算します。

def calculate_amplitude(self, iq_data):
    """IQデータから振幅スペクトルを計算(dB)"""
    try:
        # 線形振幅
        amplitude_linear = np.abs(iq_data)
        # dB変換(微小値での発散を防ぐため+1e-12)
        amplitude_db = 20 * np.log10(amplitude_linear + 1e-12)
        return amplitude_db
    except Exception as e:
        return None

4.3 実装のポイント

(1) 更新頻度の調整

# 20fps、100フレームごと = 5秒間隔
self.peak_update_interval = 100

# より頻繁に更新する場合
self.peak_update_interval = 20  # 1秒ごと

頻繁に更新しすぎると位相データに不連続が生じるため、5秒程度の間隔が適当です。

(2) 探索範囲の設定

# 人体の胸部を想定(150mm-300mm付近)
self.peak_search_start = 60   # ビン60 ≈ 150mm
self.peak_search_end = 120    # ビン120 ≈ 300mm

床や天井からの反射を除外し、対象領域に限定します。

(3) 位相データの連続性確保

ピーク位置が急激に変化しないよう、以下の工夫が可能です:

# 変化が大きすぎる場合は更新を保留
if abs(new_peak_position - self.target_position) < 20:  # 20ビン = 50mm以内
    self.target_position = new_peak_position

5. 結果

5.1 固定ビン方式の結果

測定条件

  • 固定ビン位置:80(200mm付近)
  • 対象者:静止座位

観察された現象

  • 対象が静止している場合は安定した位相データ
  • 体動(前後に数cm移動)で信号品質が急激に低下
  • 姿勢変化(前傾→後傾)で最適ビンが変化し、測定不能に

5.2 動的ビン選択の結果

測定条件

  • 初期ピーク位置:自動検出
  • 更新間隔:5秒(100フレーム@20fps)
  • 探索範囲:ビン60-120

観察された現象

  • 体動後も最適ビンを自動再検出
  • 姿勢変化にも対応可能
  • ピーク更新時に位相データに微小な不連続(対策可能)

6. まとめ

固定ビン方式が適している場合

  • 対象が完全に静止している環境(実験室など)
  • 事前に最適ビンが既知
  • 実装の単純さを優先する場合

動的ビン選択が必要な場合

  • 実用環境(対象者が自然に動く)
  • 長時間測定
  • 姿勢変化が予想される場合

今後の発展方向

  1. 機械学習による最適ビン予測

    • 過去の位相データパターンから次の最適ビンを予測
    • 体動や姿勢変化を事前に検出
  2. 複数対象の分離

    • クラスタリングやトラッキングアルゴリズムの導入
    • 個別対象ごとに最適ビンを管理

Discussion