ハイパスフィルタとは
ハイパスフィルタ(高域通過フィルタ)は、設定した遮断周波数より高い周波数成分を通過させ、低い周波数成分を減衰させるフィルタです。
直流成分(DC)の除去、振動センサの低周波ドリフト抑制、画像処理でのエッジ強調など、幅広い分野で活用されています。
主要パラメータ
| パラメータ | 説明 |
|---|---|
| \(f_c\) | 遮断周波数(-3dB点) |
| \(\omega_c\) | 遮断角周波数(\(\omega_c = 2\pi f_c\) ) |
| \(N\) | フィルタ次数(大きいほど遷移帯域が急峻) |
| 通過帯域 | \(f > f_c\) の周波数範囲 |
| 阻止帯域 | \(f < f_c\) の周波数範囲 |
次数 \(N\) が大きいほど遮断特性が急峻になりますが、位相遅れや数値的不安定性のリスクも増します。
周波数応答の導出
ローパス→ハイパス変換
ハイパスフィルタはローパスフィルタ(LPF)のプロトタイプから周波数変換によって設計できます。
アナログ領域でのローパス→ハイパス変換は、複素変数 \(s\) を以下のように置き換えます。
\[ s \rightarrow \frac{\omega_c^2}{s} \tag{1}\]\(N\) 次のバターワース LPF の伝達関数
\[H_{LP}(s) = \frac{1}{\prod_{k=1}^{N}(s - s_k)} \tag{2}\]に変換 \((1)\) を適用すると、同次のバターワースハイパスフィルタが得られます。1次の場合は次式になります。
\[H_{HP}(s) = \frac{s}{s + \omega_c} \tag{3}\]式 \((3)\) は「微分器 + 1次ローパス」の構造を持ち、\(s \to 0\) (直流)で出力がゼロ、\(s \to \infty\) (高周波)で出力が1に近づくことを示しています。
なぜ周波数軸が反転するのか(変換の導出)
式 \((1)\) が「なぜ」ハイパス特性を生むのかを、周波数軸上の対応関係から導出します。虚軸上 \(s = j\omega\) に変換 \((1)\) を適用すると、
\[ j\omega \;\longrightarrow\; \frac{\omega_c^2}{j\omega} = -j\frac{\omega_c^2}{\omega} = j\left(-\frac{\omega_c^2}{\omega}\right) \]となります。つまり実周波数 \(\omega\) は、新しい周波数 \(\omega' = -\omega_c^2/\omega\) に対応づけられます。ローパスプロトタイプの伝達関数は実係数(インパルス応答が実数)を持つため、振幅特性は周波数について偶関数 \(|H_{LP}(j\omega)| = |H_{LP}(-j\omega)|\) です。符号を無視して整理すると、
\[ |H_{HP}(j\omega)| = \left| H_{LP}\!\left(j\,\frac{\omega_c^2}{\omega}\right) \right| \]が成り立ちます。これは「ハイパスフィルタの周波数 \(\omega\) における応答の大きさは、ローパスプロトタイプの周波数 \(\omega_c^2/\omega\) における応答の大きさに等しい」ことを意味します。
- \(\omega \to 0\) (直流)のとき \(\omega_c^2/\omega \to \infty\) となり、ローパス側は阻止帯域の彼方(完全減衰域)に対応するため \(H_{HP}(0) = 0\) 。
- \(\omega \to \infty\) (高周波)のとき \(\omega_c^2/\omega \to 0\) となり、ローパス側は通過帯域の中心(ゲイン1)に対応するため \(H_{HP}(j\infty) \to 1\) 。
つまり式 \((1)\) は、周波数軸を \(\omega \leftrightarrow \omega_c^2/\omega\) という反比例の関係で折り返し、ローパスの通過帯域と阻止帯域を入れ替えることでハイパス特性を作り出しています。\(\omega = \omega_c\) はこの折り返しの不動点(\(\omega_c^2/\omega_c = \omega_c\) )にあたり、両プロトタイプの -3dB点が一致する理由もここから分かります。
実際にこの導出を検証します。4次バターワースローパスプロトタイプの極(\(\omega_c = 1\)
に正規化)を scipy.signal.buttap で取得し、手計算で \(s \to \omega_c^2/s\)
を適用した極と、scipy.signal.lp2hp が返す極を比較します。
import numpy as np
from scipy import signal
N = 4
wc = 2 * np.pi * 100.0 # rad/s, fc = 100 Hz
# 正規化ローパスプロトタイプ(wc=1)の極
z_lp, p_lp, k_lp = signal.buttap(N)
# wc にスケールしたローパス極
p_lp_scaled = p_lp * wc
# 手計算による変換: wc^2/s = p_lp => s = wc^2 / p_lp
p_hp_manual = wc**2 / p_lp_scaled
# scipy.signal.lp2hp による変換(比較対象)
b_lp, a_lp = signal.zpk2tf(z_lp, p_lp, k_lp)
b_hp, a_hp = signal.lp2hp(b_lp, a_lp, wo=wc)
_, p_hp_scipy, _ = signal.tf2zpk(b_hp, a_hp)
diff = np.max(np.abs(np.sort_complex(p_hp_manual) - np.sort_complex(p_hp_scipy)))
print(f"手計算とscipyの極の最大差: {diff:.3e}")
# --- 周波数軸の反転関係 |H_HP(jω)| = |H_LP(j・ωc^2/ω)| も検証 ---
test_f = np.array([10.0, 30.0, 60.0, 100.0, 150.0, 300.0, 1000.0])
w_test = 2 * np.pi * test_f
_, H_hp = signal.freqs(b_hp, a_hp, worN=w_test)
b_lp_scaled, a_lp_scaled = signal.lp2lp(b_lp, a_lp, wo=wc)
w_inv = wc**2 / w_test # 反転周波数
_, H_lp_at_inv = signal.freqs(b_lp_scaled, a_lp_scaled, worN=w_inv)
for f, hhp, hlp in zip(test_f, np.abs(H_hp), np.abs(H_lp_at_inv)):
print(f"f={f:7.1f} Hz |H_HP(f)|={hhp:.6f} |H_LP(wc^2/f)|={hlp:.6f}")
実行結果は以下の通りです。
| \(f\) [Hz] | \(\lvert H_{HP}(f) \rvert\) | \(f_{inv} = \omega_c^2/(2\pi f)\) [Hz] | \(\lvert H_{LP}(f_{inv}) \rvert\) |
|---|---|---|---|
| 10.0 | 0.000100 | 1000.00 | 0.000100 |
| 30.0 | 0.008100 | 333.33 | 0.008100 |
| 60.0 | 0.128525 | 166.67 | 0.128525 |
| 100.0 | 0.707107 | 100.00 | 0.707107 |
| 150.0 | 0.981044 | 66.67 | 0.981044 |
| 300.0 | 0.999924 | 33.33 | 0.999924 |
| 1000.0 | 1.000000 | 10.00 | 1.000000 |
すべての周波数で両者が完全に一致し、\(f=100\) Hz(\(=f_c\) )ではどちらも \(1/\sqrt{2} \approx 0.7071\) (-3dB点)となっています。周波数軸反転による導出が正しいことが数値的にも裏付けられました。
離散時間ハイパスフィルタ(双一次変換)
式 \((3)\) を双一次変換(Bilinear Transform)でデジタル領域に変換します。
\[s = \frac{2}{T} \cdot \frac{1 - z^{-1}}{1 + z^{-1}} \tag{4}\]これを式 \((3)\) に代入すると、1次デジタルハイパスフィルタの伝達関数が得られます。
\[H(z) = \frac{1 - z^{-1}}{1 + \alpha z^{-1}} \cdot \frac{1}{1 + \frac{1}{\alpha}} \tag{5}\]ここで \(\alpha = 1 - 2f_c / f_s\)
(プリワーピング係数)です。実用上は scipy.signal がこの計算を自動的に行います。
ハイパスフィルタ固有の性質と設計上の注意点
ここまでの変換の導出を踏まえ、ハイパスフィルタに固有の性質——直流完全遮断の代償、微分器的振る舞いによるノイズ増幅、そして実務での設計の使い分け——を掘り下げます。
直流成分の完全遮断とその代償:ステップ応答のオーバーシュート・アンダーシュート
式 \((3)\) より \(H_{HP}(0) = 0/\omega_c = 0\) が任意の次数・任意の設計法(バターワース、チェビシェフ等)のハイパスフィルタで成り立ちます。これは伝達関数の定義
\[ H(s) = \int_{-\infty}^{\infty} h(t) \, e^{-st} \, dt \]において \(s=0\) を代入すると
\[ H(0) = \int_{-\infty}^{\infty} h(t) \, dt \]となることから、\(H(0)=0\) は「インパルス応答 \(h(t)\) の面積(正味の積分値)がゼロである」ことと同値だと分かります。\(h(t)\) が恒等的にゼロでない限り、面積がゼロになるには \(h(t)\) が正負に符号反転する部分を持たなければなりません。
この事実はステップ応答の形にも直接反映されます。単位ステップ入力 \(u(t)\) に対する応答は \(y(t) = \int_0^t h(\tau) \, d\tau\) であり、\(t \to \infty\) で
\[ y(\infty) = \int_0^{\infty} h(\tau) \, d\tau = H(0) = 0 \]に収束します。つまりハイパスフィルタにステップ入力を与えると、出力は必ず0に戻る——直流成分は原理的に保持されないということです。これはDCオフセット除去(マイクのバイアス除去、ECGのベースライン補正など)の理論的根拠そのものです。
一方で、この「面積ゼロ」の制約を満たす過程で、次数が上がるほど極が複素平面上に多く配置され、インパルス応答が振動しながら減衰するようになります。その結果、ステップ応答は一度大きく振れた後、逆方向に振れ(アンダーシュート)ながら0に収束する——という代償を伴います。次数ごとにこれを検証しました。
import numpy as np
from scipy import signal
fs = 1000.0
fc = 50.0
wn = fc / (fs / 2)
u = np.ones(4000) # 単位ステップ(causalフィルタなので先頭からステップ開始)
for order in [1, 2, 4, 8]:
b, a = signal.butter(order, wn, btype='high')
y = signal.lfilter(b, a, u)
overshoot = np.max(y)
undershoot = np.min(y)
idx_over, idx_under = np.argmax(y), np.argmin(y)
print(f"order={order}: overshoot={overshoot:.4f} @ {idx_over/fs*1000:.2f}ms, "
f"undershoot={undershoot:.4f} @ {idx_under/fs*1000:.2f}ms, "
f"|undershoot|/overshoot={abs(undershoot)/overshoot:.3f}, tail={y[-1]:.1e}")
実行結果(\(f_c=50\) Hz, \(f_s=1000\) Hz)は次の通りです。
| 次数 \(N\) | オーバーシュート | アンダーシュート | |アンダーシュート|/オーバーシュート | 終端値(\(t=4\) s) |
|---|---|---|---|---|
| 1 | 0.8633 | 0.0000(なし) | 0.000 | 1.11e-16 |
| 2 | 0.8006 | -0.2087 | 0.261 | 1.11e-16 |
| 4 | 0.6620 | -0.3514 | 0.531 | -2.82e-14 |
| 8 | 0.4451 | -0.3989 | 0.896 | -4.48e-12 |
いずれの次数でも終端値は理論通り0に収束しています(\(H(0)=0\) の数値的確認)。1次フィルタは単調に減衰しアンダーシュートを持ちませんが、次数を上げるほど初期の跳躍(オーバーシュート)は緩和される一方、逆向きの振動(アンダーシュート)が急速に増大し、8次では振れ幅がオーバーシュートの約90%にまで達します。次数を上げて遷移帯域を急峻にするほど、時間領域では「リンギング」という形でコストを支払っていることが分かります。図にすると次の通りです。

実務上の含意: 加速度センサの重力成分除去や心電図のベースライン補正など、入力に階段状の変化(センサ姿勢の急変、電極接触ノイズ等)が混入し得る場面では、高次フィルタほどこのリンギングが計測波形に偽のオーバーシュート/アンダーシュートとして現れ、誤診断や誤検出の原因になり得ます。次数選定はロールオフの急峻さとリンギングの許容量のトレードオフです。
カットオフ付近の微分器的振る舞いとノイズ増幅
1次ハイパスフィルタの伝達関数 \((3)\) について、\(\omega \ll \omega_c\) の極限を考えます。
\[ H_{HP}(j\omega) = \frac{j\omega}{j\omega + \omega_c} \approx \frac{j\omega}{\omega_c} \quad (\omega \ll \omega_c) \]これは理想微分器 \(j\omega\) を \(1/\omega_c\) でスケールしたものと一致し、振幅は周波数に比例(\(\lvert H_{HP}(j\omega)\rvert \approx \omega/\omega_c\) )、位相は常に \(+90^\circ\) 進みます。この近似の精度を数値的に確認しました(\(f_c=100\) Hz)。
import numpy as np
wc = 2 * np.pi * 100.0
test_f = np.array([1.0, 5.0, 10.0, 20.0])
w = 2 * np.pi * test_f
H_exact = (1j * w) / (1j * w + wc)
H_approx = (1j * w) / wc
for f, he, ha in zip(test_f, H_exact, H_approx):
err = abs(abs(he) - abs(ha)) / abs(he) * 100
print(f"f={f:5.1f}Hz |H_exact|={abs(he):.5f} |H_approx|={abs(ha):.5f} 誤差={err:.3f}%")
実行結果、\(f=1\) Hzで誤差0.005%、\(f=5\) Hzで0.125%、\(f=10\) Hzで0.499%、\(f=20\) Hz(\(f_c\) の1/5)でも誤差1.980%と、カットオフの数分の1以下の帯域では近似が極めて良く成り立つことを確認しました。一般に\(N\) 次バターワースハイパスフィルタでは \(\omega \ll \omega_c\) で \(\lvert H_{HP}(j\omega)\rvert \approx (\omega/\omega_c)^N\) という低域漸近則が成り立ち(-20N dB/decadeのロールオフに対応)、次数ごとに数値検証しても比が0.87〜0.97の範囲で理論値に収束することを確認しています。
微分器は「信号の変化率」を強調する演算ですが、これは測定ノイズも同じように強調してしまうという代償を伴います。低周波の目的信号にノイズが混入した状態でハイパスフィルタを適用し、SN比の変化を実測しました。
import numpy as np
from scipy import signal
np.random.seed(42)
fs = 1000.0
t = np.arange(0, 2.0, 1 / fs)
clean = np.sin(2 * np.pi * 1.0 * t) # 目的信号(1Hzの緩やかな変動)
noise = 0.05 * np.random.randn(len(t)) # 広帯域ノイズ
fc_diff = 50.0 # 目的信号帯域よりずっと高いカットオフ = 微分器として使う設定
b, a = signal.butter(1, fc_diff / (fs / 2), btype='high')
skip = 500 # 過渡応答区間を除外
clean_f = signal.lfilter(b, a, clean)[skip:]
noise_f = signal.lfilter(b, a, noise)[skip:]
snr_in = np.var(clean[skip:]) / np.var(noise[skip:])
snr_out = np.var(clean_f) / np.var(noise_f)
print(f"SNR_in = {10*np.log10(snr_in):.2f} dB")
print(f"SNR_out = {10*np.log10(snr_out):.2f} dB")
print(f"劣化量 = {10*np.log10(snr_in) - 10*np.log10(snr_out):.2f} dB")
print(f"信号の振幅比 std(clean_f)/std(clean) = {np.std(clean_f)/np.std(clean[skip:]):.5f}"
f"(理論ゲイン@1Hz = {abs((1j*2*np.pi*1.0)/(1j*2*np.pi*1.0+2*np.pi*fc_diff)):.5f})")
print(f"ノイズの振幅比 std(noise_f)/std(noise) = {np.std(noise_f)/np.std(noise[skip:]):.5f}")
実行結果は次の通りです。
- SNR(入力): 22.68 dB → SNR(出力): -10.34 dB(約33 dB劣化)
- 信号の振幅比: 0.02079(理論ゲイン0.02000とほぼ一致)
- ノイズの振幅比: 0.93057
目的信号(1Hz)はカットオフ(50Hz)よりずっと低いため理論通り約1/50に強く減衰される一方、広帯域ノイズはカットオフ付近・以上の成分が減衰されずにほぼ素通りするため、相対的にノイズが支配的になります。これが「ハイパスフィルタ=微分器はノイズを増幅する」と言われる所以です(正確には信号成分より相対的にノイズが目立つようになる現象で、フィルタの最大ゲインは1を超えません)。なお、filtfilt(順方向・逆方向の2回通し)を用いる場合は振幅ゲインが2乗されるため、上記と同条件でSNR劣化は約67 dBとさらに大きくなることも確認済みです。実務でノイズ下での微分(変化率推定)が必要な場合は、ハイパスフィルタ単体ではなく、平滑化と組み合わせた設計(例: Savitzky-Golay微分フィルタや、後段にローパスを追加したバンドパス的構成)を検討すべきです。
1次RC型ハイパスフィルタと高次設計の使い分け
式 \((3)\) ・\((5)\) の1次ハイパスフィルタは、アナログ回路ではコンデンサと抵抗1つずつで構成できる最も単純な「DCブロッキングフィルタ」に対応します(\(\omega_c = 1/RC\) )。マイクプリアンプの直流バイアス除去やオーディオのAC結合など、位相特性やリンギングを気にせず、ともかく直流だけ落とせればよい用途では、この1次RC型で十分です。実装もシンプルで、位相遅れは緩やかかつ単調(前節で見た通りアンダーシュートも生じません)です。
一方、心電図のベースラインドリフト除去や振動解析のように、目的信号の帯域とノイズ帯域が近接していて急峻な遷移帯域が必要な場面では、4〜8次程度のバターワース/チェビシェフ設計が使われます。ただし前節で確認した通り、次数を上げるほどステップ状の変化に対するリンギング(オーバーシュート・アンダーシュート)が増大するため、次数選定は「遷移帯域の急峻さ」と「時間領域での波形歪み」のトレードオフとして扱う必要があります。位相歪みが問題になる場合は本記事のfiltfilt(ゼロ位相化)や、姉妹記事で扱うベッセルフィルタ(群遅延平坦化)との併用も検討してください。
Pythonによる実装
scipy.signalを用いたIIRハイパスフィルタ
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# --- パラメータ設定 ---
fs = 1000.0 # サンプリング周波数 [Hz]
fc = 100.0 # 遮断周波数 [Hz]
order = 4 # フィルタ次数
# --- バターワース ハイパスフィルタの設計 ---
nyq = fs / 2.0 # ナイキスト周波数
wn = fc / nyq # 正規化遮断周波数
b, a = signal.butter(order, wn, btype='high')
# --- 周波数応答の計算 ---
w, h = signal.freqz(b, a, worN=8000, fs=fs)
# --- プロット ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# ゲイン特性 (dB)
ax1.semilogx(w, 20 * np.log10(np.abs(h) + 1e-10))
ax1.axvline(fc, color='r', linestyle='--', label=f'$f_c$ = {fc} Hz')
ax1.axhline(-3, color='gray', linestyle=':', label='-3 dB')
ax1.set_xlabel('Frequency [Hz]')
ax1.set_ylabel('Gain [dB]')
ax1.set_title(f'Butterworth Highpass Filter (order={order})')
ax1.legend()
ax1.grid(True, which='both', alpha=0.3)
ax1.set_xlim([1, nyq])
ax1.set_ylim([-80, 5])
# 位相特性
ax2.semilogx(w, np.angle(h, deg=True))
ax2.axvline(fc, color='r', linestyle='--')
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Phase [degrees]')
ax2.set_title('Phase Response')
ax2.grid(True, which='both', alpha=0.3)
ax2.set_xlim([1, nyq])
plt.tight_layout()
plt.savefig('highpass_response.png', dpi=150, bbox_inches='tight')
plt.show()
FIRハイパスフィルタの設計
IIRフィルタは位相が非線形ですが、FIRフィルタは線形位相を実現できます。音声・医療信号処理など位相の保存が重要な場面で有用です。
from scipy.signal import firwin, freqz
# --- FIR ハイパスフィルタ ---
numtaps = 101 # フィルタ係数の数(奇数推奨)
fir_hpf = firwin(
numtaps,
fc,
pass_zero=False, # ハイパス指定
fs=fs,
window='hamming'
)
w_fir, h_fir = freqz(fir_hpf, worN=8000, fs=fs)
# IIRとFIRの比較プロット
plt.figure(figsize=(10, 5))
plt.semilogx(w, 20 * np.log10(np.abs(h) + 1e-10),
label='IIR Butterworth (order=4)', linewidth=2)
plt.semilogx(w_fir, 20 * np.log10(np.abs(h_fir) + 1e-10),
label=f'FIR Hamming (taps={numtaps})', linewidth=2, linestyle='--')
plt.axvline(fc, color='r', linestyle=':', alpha=0.7, label=f'$f_c$ = {fc} Hz')
plt.axhline(-3, color='gray', linestyle=':', alpha=0.7)
plt.xlabel('Frequency [Hz]')
plt.ylabel('Gain [dB]')
plt.title('Highpass Filter Comparison: IIR vs FIR')
plt.legend()
plt.grid(True, which='both', alpha=0.3)
plt.xlim([1, nyq])
plt.ylim([-80, 5])
plt.tight_layout()
plt.show()
信号への適用例
実際のノイズ混じり信号にハイパスフィルタを適用します。
# --- テスト信号の生成 ---
t = np.linspace(0, 1.0, int(fs), endpoint=False) # 1秒分
# 目的信号: 200 Hz のサイン波(高周波成分)
signal_pure = np.sin(2 * np.pi * 200 * t)
# ノイズ: 5 Hz の低周波ドリフト + ホワイトノイズ
noise = (
2.0 * np.sin(2 * np.pi * 5 * t) + # 低周波ドリフト(大振幅)
0.3 * np.random.randn(len(t)) # ホワイトノイズ
)
x_noisy = signal_pure + noise
# --- フィルタ適用 ---
# lfilter: 因果的フィルタ(リアルタイム処理向け、位相遅れあり)
y_lfilter = signal.lfilter(b, a, x_noisy)
# filtfilt: ゼロ位相フィルタ(オフライン処理向け、位相遅れなし)
y_filtfilt = signal.filtfilt(b, a, x_noisy)
# --- 比較プロット ---
fig, axes = plt.subplots(3, 1, figsize=(12, 8), sharex=True)
axes[0].plot(t[:300], x_noisy[:300], alpha=0.8, label='ノイズ混じり信号(低周波ドリフトあり)')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Input Signal (with low-frequency drift)')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[1].plot(t[:300], y_lfilter[:300], color='orange', label='lfilter (因果的)')
axes[1].plot(t[:300], signal_pure[:300], color='gray', linestyle='--', alpha=0.6, label='真の信号')
axes[1].set_ylabel('Amplitude')
axes[1].set_title('After lfilter (位相遅れあり)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[2].plot(t[:300], y_filtfilt[:300], color='green', label='filtfilt (ゼロ位相)')
axes[2].plot(t[:300], signal_pure[:300], color='gray', linestyle='--', alpha=0.6, label='真の信号')
axes[2].set_ylabel('Amplitude')
axes[2].set_xlabel('Time [s]')
axes[2].set_title('After filtfilt (ゼロ位相)')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('highpass_filtering_result.png', dpi=150, bbox_inches='tight')
plt.show()
IIR vs FIR 設計の比較
| 特性 | IIR(バターワース等) | FIR |
|---|---|---|
| 位相特性 | 非線形(位相歪みあり) | 線形位相が実現可能 |
| フィルタ係数数 | 少ない(\(2N+1\) 程度) | 多い(数十〜数百) |
| 計算コスト | 低い | 高い(タップ数依存) |
| 安定性 | 設計によっては不安定 | 常に安定 |
| 遮断特性 | 低次でも急峻 | タップ数増加で改善 |
| 遅延 | 非定数 | 定数遅延(\((N-1)/2\) サンプル) |
| 適した用途 | リアルタイム処理 | 音声・医療信号処理 |
設計指針:
- リアルタイムで計算コストを抑えたい → IIR(バターワース/チェビシェフ)
- 位相の線形性が重要(音声・医療信号) → FIR(Hamming/Kaiser窓)
- オフライン処理で位相遅れを完全除去 →
filtfilt(IIR/FIR共通)
次数と遮断特性の影響
# 次数の違いによる周波数応答の比較
fig, ax = plt.subplots(figsize=(10, 6))
for ord_n in [1, 2, 4, 8]:
b_n, a_n = signal.butter(ord_n, wn, btype='high')
w_n, h_n = signal.freqz(b_n, a_n, worN=8000, fs=fs)
ax.semilogx(w_n, 20 * np.log10(np.abs(h_n) + 1e-10), label=f'order={ord_n}')
ax.axhline(-3, color='gray', linestyle=':', label='-3 dB')
ax.axvline(fc, color='r', linestyle='--', alpha=0.5, label=f'$f_c$ = {fc} Hz')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Gain [dB]')
ax.set_title('Effect of Filter Order on Highpass Response')
ax.legend()
ax.grid(True, which='both', alpha=0.3)
ax.set_xlim([1, nyq])
ax.set_ylim([-80, 5])
plt.tight_layout()
plt.show()
次数1では遷移帯域が緩やかで、次数8では急峻な遮断特性が得られます。ただし IIR フィルタでは次数が高くなるほど位相遅れと数値的不安定性のリスクが増すため、次数8以下が実用的な目安です。
近年の研究動向:生体信号処理・画像フォレンジックにおけるハイパスフィルタ
前節で示したステップ応答のオーバーシュート・アンダーシュート(リンギング)は、生体信号処理の分野で近年活発に再検討されているテーマです。
脳波(EEG)のERP解析では、2023年に Psychophysiology 誌で発表された研究(Tanner et al. のフィルタ最適化研究の後継、bioRxivプレプリント2023年6月)が、ハイパスフィルタのカットオフを上げすぎると本来のピークの前後に逆極性の偽ピークが生じることを定量的に示しました。この研究は「ハイパスフィルタは反転しているため、真のピークの前後に偽の逆極性ピークを作り出す」と明記した上で、偽ピークの振幅を真のピークの5%以内に収める閾値を設定し、成分ごとに異なる推奨カットオフ(N170で0.9Hz、MMN/N2pcで0.5Hz、P3/N400で0.2Hzなど)を提示しています。論文は「過度なハイパスフィルタリングによって真のP600効果が見かけ上N400的な効果を生み出しうる」という具体例も示しており、本記事で数値的に確認した「次数を上げるほどアンダーシュートが増大する」という性質が、実データの解釈を誤らせるリスクとして実証的に扱われていることが分かります。同じく2023年に Scientific Reports 誌に掲載された別の研究では、タスク種別ごとに最適なカットオフ(Faceタスクで0.1Hz、Oddballタスクで0.5Hz、Go/No-goタスクで0.75Hz)が異なることが報告されており、ハイパスフィルタの設計が信号の性質に強く依存することが近年のEEG研究でも再確認されています。
心電図(ECG)のベースラインドリフト除去では、0.5〜1Hz程度のカットオフが定番ですが、位相歪みを避けるためにfiltfiltのような双方向(ゼロ位相)フィルタの使用が引き続き標準的な指針とされています。カットオフを上げるほどドリフト除去は改善する一方、S-T部分など臨床的に重要な低周波成分への影響とのトレードオフが継続的な検討課題です。
画像処理・画像フォレンジック分野では、生成画像(GAN・拡散モデル)の検出において、高周波成分に着目したハイパスフィルタ的な前処理が引き続き活用されています。2024年に発表された学習不要(training-free)の画像鑑識手法は、単純なハンドクラフトフィルタで生成画像特有の高周波アーティファクトを捉えられることを示しており、周波数領域での高域強調が偽造検出の有力な手がかりであり続けていることがうかがえます。ただし個別の実装詳細(フィルタ次数やカットオフの具体的な設計)は手法ごとに異なるため、興味のある方は各論文を参照してください。
Sources:
- Optimal Filters for ERP Research II: Recommended Settings for Seven Common ERP Components (PMC10312706)
- EEG is better left alone (PMC9911389, Scientific Reports 2023)
- Data-Independent Operator: A Training-Free Artifact Representation Extractor for Generalizable Deepfake Detection (arXiv:2403.06803)
ハイパスフィルタの実用例
| 用途 | \(f_c\) の目安 | 備考 |
|---|---|---|
| 音声のDC除去(マイク補正) | 20〜80 Hz | DC 成分による信号飽和を防ぐ |
| 加速度センサのドリフト除去 | 0.1〜1 Hz | 重力成分(定常成分)を除去 |
| 心電図(ECG)のベースライン | 0.5〜1 Hz | 呼吸による低周波変動を除去 |
| 画像のエッジ強調 | — | 2次元HPFで輪郭を強調(鮮鋭化) |
| 振動解析の低周波ノイズ除去 | 1〜10 Hz | 機械振動の直流バイアスや電源ハム波を除去 |
関連記事
- ローパスフィルタの設計と比較:移動平均・バターワース・チェビシェフ - ハイパスフィルタの双対であるローパスフィルタの設計を解説しています。
- バターワースフィルタの設計とPython実装 - ハイパス設計に利用するバターワースプロトタイプを詳しく解説しています。
- バンドパスフィルタの設計とPython実装 - ハイパス+ローパスを組み合わせた帯域通過フィルタを解説しています。
- FIRフィルタとIIRフィルタの比較 - FIR/IIRの設計思想の違いを理論から解説しています。
- ノッチフィルタの設計とPython実装 - 特定周波数のみを除去するバンドストップフィルタを解説しています。
- 指数移動平均(EMA)フィルタの周波数特性 - 1次IIRローパスフィルタとして見たEMAの周波数特性をZ変換から導出しています。
- 高速フーリエ変換(FFT)の仕組みとPython実装 - フィルタの効果を周波数領域で確認するために必要なFFTの基礎を解説しています。
- 適応フィルタ(LMS/RLS)の理論とPython実装 - 固定帯域ではなく環境に応じて特性を自動調整する適応フィルタを解説しています。
- チェビシェフフィルタの設計原理とPython実装 - 同次数でバターワースより急峻な遷移帯域を得られる等リップル設計。ハイパスのプロトタイプとして併用できます。
- ウィーナーフィルタの理論とPython実装 - SNRに基づく最適な周波数選択。直流バイアス除去をSNR観点で再解釈できます。
- Z変換と離散時間システムの理論 - 本記事の伝達関数表記の理論的土台。極零点の単位円上配置からハイパス特性を読み解けます。
- DTFT・DFT・FFTの違いを整理する - ハイパスフィルタの周波数応答評価の前提となる、3種類のフーリエ変換の関係を整理しています。
- サビツキー・ゴーレイフィルタの理論とPython実装 - ハイパス的なドリフト除去ではなく形状保存型平滑化を行うFIR。役割の違いから両者の使い分けが理解できます。
- ベッセルフィルタの理論とPython実装 - ハイパス用途で位相歪みを抑えたい場合のプロトタイプとして利用できる、群遅延が最大限平坦なIIR設計を解説しています。
- ボード線図(Bode plot)の理論とPython実装 - HPF の「低域 \(-20N\) dB/dec ロールオフ + 位相 \(+90N^\circ\) 回転」を統一的に可視化するハブ記事です。
- 短時間フーリエ変換(STFT)の理論とPython実装 - HPF 適用前後のスペクトログラム比較で、低周波ドリフトが除去された様子を時間-周波数領域で可視化できます。
- デジタルフィルタ設計指針ハブ:選定の3軸と特性比較 — 本記事のハイパスフィルタを他のフィルタ群と比較しながら選定する際の指針として活用できます。
参考文献
- Proakis, J. G., & Manolakis, D. G. (2006). Digital Signal Processing: Principles, Algorithms, and Applications. Pearson.
- scipy.signal.butter — SciPy documentation
- scipy.signal.filtfilt — SciPy documentation
関連ツール
- DevToolBox - 開発者向け無料ツール集 - JSON整形、正規表現テスターなど85種類以上の開発者向けツール
- CalcBox - 暮らしの計算ツール - 統計計算、周波数変換など61種類以上の計算ツール