はじめに
**ボード線図(Bode plot)**は、線形時不変システムの周波数応答 \(H(j\omega)\) を 振幅[dB] と 位相[deg] に分け、横軸を 対数スケール(log scale)の周波数 で表した2段グラフです。Hendrik Bodeが1930年代に制御工学のために体系化したこの可視化手法は、現在ではフィルタ設計、伝達関数解析、サーボ系の安定性評価など、信号処理と制御の現場で標準ツールになっています。
特にフィルタ設計では、バターワースフィルタの「最大平坦特性」、チェビシェフフィルタの「等リップル」、ハイパスフィルタやローパスフィルタの「ロールオフ率」など、すべての特性がボード線図上で視覚化されます。本記事では、ボード線図の数学的基礎・読み方・Python実装を整理し、各種フィルタの周波数応答を横断的に理解できるハブとして使えるようにまとめます。
数学的基礎
伝達関数と周波数応答
連続時間 LTI システムの伝達関数を \(H(s)\) とします。\(s = j\omega\) を代入して得られる 周波数応答 は、複素数値関数として振幅と位相を持ちます。
\[H(j\omega) = |H(j\omega)| \, e^{j\angle H(j\omega)} \tag{1}\]ボード線図はこの \(H(j\omega)\) を 2 つのプロットに分解します。
- 振幅プロット: \(20\log_{10}|H(j\omega)|\) を縦軸に、\(\log_{10}\omega\) を横軸にとる
- 位相プロット: \(\angle H(j\omega)\) (度)を縦軸に、\(\log_{10}\omega\) を横軸にとる
デシベル定義
振幅をデシベル(dB)で表す理由は、乗算が加算に変わることにあります。\(H_1\) と \(H_2\) を直列接続したシステムの振幅は
\[20\log_{10}|H_1 H_2| = 20\log_{10}|H_1| + 20\log_{10}|H_2| \tag{2}\]となり、ボード線図上では 個別のプロットの和 として描けます。これにより、複数次の極・零点を持つフィルタも 1 次系の重ね合わせで近似的に読み解けるのです。
位相遅延と群遅延
位相プロットから直接読める量として、位相遅延 と 群遅延 が重要です。
\[\tau_p(\omega) = -\frac{\angle H(j\omega)}{\omega}, \quad \tau_g(\omega) = -\frac{d\angle H(j\omega)}{d\omega} \tag{3}\]群遅延 \(\tau_g\) は位相プロットの 傾き に直結し、波形歪みの大きさを表します。FIR フィルタの線形位相は群遅延が定数になることを意味し、ボード線図上では位相が周波数の一次関数で減少します。
1 次・2 次系のボード線図
1 次ローパス
\[H(s) = \frac{1}{1 + s/\omega_c} \tag{4}\]の振幅は
\[|H(j\omega)| = \frac{1}{\sqrt{1 + (\omega/\omega_c)^2}} \tag{5}\]であり、漸近線で読むと
- \(\omega \ll \omega_c\) : \(|H|_{dB} \approx 0\) dB(通過域)
- \(\omega = \omega_c\) : \(|H|_{dB} = -3\) dB(カットオフ点)
- \(\omega \gg \omega_c\) : \(|H|_{dB} \approx -20\log_{10}(\omega/\omega_c)\) dB(-20 dB/dec のロールオフ)
位相は \(\omega_c\) で \(-45^\circ\) 、高域漸近で \(-90^\circ\) になります。1 次ハイパスはこの 鏡像(低域で \(-20\) dB/dec の立ち上がり、\(+90^\circ \to 0^\circ\) への位相回転)として読めます。
2 次系の共振
2 次系
\[H(s) = \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2} \tag{6}\]の振幅プロットは、減衰係数 \(\zeta < 1/\sqrt{2}\) で 共振ピーク を持ちます。ピーク値は
\[M_p = \frac{1}{2\zeta\sqrt{1-\zeta^2}} \tag{7}\]であり、\(\zeta\) が小さいほど鋭いピークが立ちます。チェビシェフフィルタの通過域リップルや楕円フィルタの両域リップルは、この 2 次系の共振が複数組み合わさった結果と見なせます。高域漸近のロールオフは -40 dB/dec で、1 次系の倍の急峻さです。
典型的フィルタとボード線図の対応
| フィルタ | 通過域の振幅特性 | ロールオフ | 位相特性 |
|---|---|---|---|
| バターワース \(N\) 次 | 最大平坦 | \(-20N\) dB/dec | 比較的滑らか |
| チェビシェフ I 型 | 等リップル | \(-20N\) dB/dec | 通過域端で急峻に変化 |
| 楕円(Cauer) | 両域リップル | 最急峻 | 最も急峻 |
| ハイパス | \(f > f_c\) で平坦 | 低域 \(-20N\) /dec | \(+90N^\circ \to 0^\circ\) |
| バンドパス | 帯域内で平坦 | 両側 \(-20N\) /dec | 帯域中心で 0° |
| ノッチ | \(f_0\) で深いディップ | 急峻 | \(f_0\) で \(\pm 90^\circ\) 回転 |
このようにボード線図は 「フィルタの種類を一目で見分ける視覚的辞書」 として機能します。次数 \(N\) はロールオフ率から、減衰特性は通過域・阻止域のリップル形状から、位相歪みは位相プロットの曲がりから読み取れます。
Python による作成
scipy.signal.bode による標準実装
scipy.signal.bode は連続時間伝達関数からボード線図のデータを直接生成できます。
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# --- 1次ローパス: H(s) = 1 / (1 + s/wc) ---
wc = 2 * np.pi * 100 # カットオフ角周波数 [rad/s] (100 Hz)
num = [1.0]
den = [1.0 / wc, 1.0]
system = signal.TransferFunction(num, den)
# bode は 振幅[dB] と 位相[deg] を返す
w, mag, phase = signal.bode(system, w=np.logspace(0, 4, 1000))
f = w / (2 * np.pi) # Hz に変換
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 7), sharex=True)
ax1.semilogx(f, mag, linewidth=2)
ax1.axhline(-3, color='gray', linestyle=':', label='-3 dB')
ax1.axvline(wc / (2 * np.pi), color='r', linestyle='--', label=f'$f_c$ = {wc/(2*np.pi):.0f} Hz')
ax1.set_ylabel('Magnitude [dB]')
ax1.set_title('Bode Plot - 1st-order Lowpass')
ax1.grid(True, which='both', alpha=0.3)
ax1.legend()
ax2.semilogx(f, phase, linewidth=2, color='orange')
ax2.axvline(wc / (2 * np.pi), color='r', linestyle='--')
ax2.axhline(-45, color='gray', linestyle=':', label='-45°')
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Phase [degrees]')
ax2.grid(True, which='both', alpha=0.3)
ax2.legend()
plt.tight_layout()
plt.show()
semilogx で横軸を対数化することがポイントです。リニア軸では低域の構造が潰れてしまい、ボード線図本来の 「数十年(decade)にわたる広帯域挙動を一度に俯瞰する」 という長所が失われます。
デジタルフィルタ(IIR)のボード線図
バターワース や チェビシェフ のような IIR デジタルフィルタには scipy.signal.freqz を使います。
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
fs = 1000.0 # サンプリング周波数 [Hz]
fc = 100.0 # カットオフ周波数 [Hz]
orders = [2, 4, 8]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
for N in orders:
sos = signal.butter(N, fc, btype='low', fs=fs, output='sos')
w, h = signal.sosfreqz(sos, worN=4096, fs=fs)
# 振幅プロット(対数周波数 + dB)
ax1.semilogx(w, 20 * np.log10(np.abs(h) + 1e-12), label=f'N={N}', linewidth=2)
# 位相プロット(unwrap で 360° 跳びを除去)
ax2.semilogx(w, np.degrees(np.unwrap(np.angle(h))), label=f'N={N}', linewidth=2)
ax1.axhline(-3, color='gray', linestyle=':', label='-3 dB')
ax1.axvline(fc, color='r', linestyle='--', alpha=0.5)
ax1.set_ylabel('Magnitude [dB]')
ax1.set_title('Bode Plot - Butterworth Lowpass (digital)')
ax1.set_ylim(-100, 5)
ax1.grid(True, which='both', alpha=0.3)
ax1.legend()
ax2.axvline(fc, color='r', linestyle='--', alpha=0.5)
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Phase [degrees]')
ax2.set_xlim(1, fs / 2)
ax2.grid(True, which='both', alpha=0.3)
ax2.legend()
plt.tight_layout()
plt.show()
このプロットからは、次数 \(N\) を 1 段上げるごとに高域のロールオフが 20 dB/dec ずつ急峻になる こと、位相回転が \(-90^\circ\) ずつ深まることが確認できます。これが「次数から特性を予測する」感覚を養う最良のトレーニングです。
手動計算による複数フィルタの比較
bode / freqz を使わず手動で計算すると、各フィルタの違いを並列に可視化できます。
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
fs = 1000.0
fc = 100.0
N = 4
f = np.logspace(0, np.log10(fs / 2), 2000)
w = 2 * np.pi * f
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
filters = {
'Butterworth': signal.butter(N, fc, btype='low', fs=fs, output='ba'),
'Chebyshev I': signal.cheby1(N, 1.0, fc, btype='low', fs=fs, output='ba'),
'Chebyshev II': signal.cheby2(N, 40.0, fc, btype='low', fs=fs, output='ba'),
'Elliptic': signal.ellip(N, 1.0, 40.0, fc, btype='low', fs=fs, output='ba'),
}
for name, (b, a) in filters.items():
_, h = signal.freqz(b, a, worN=f, fs=fs)
ax1.semilogx(f, 20 * np.log10(np.abs(h) + 1e-12), label=name, linewidth=2)
ax2.semilogx(f, np.degrees(np.unwrap(np.angle(h))), label=name, linewidth=2)
ax1.axvline(fc, color='r', linestyle='--', alpha=0.5, label=f'$f_c$ = {fc} Hz')
ax1.axhline(-3, color='gray', linestyle=':', alpha=0.5)
ax1.set_ylabel('Magnitude [dB]')
ax1.set_title(f'Bode Plot Comparison (N={N})')
ax1.set_ylim(-80, 5)
ax1.grid(True, which='both', alpha=0.3)
ax1.legend()
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Phase [degrees]')
ax2.grid(True, which='both', alpha=0.3)
ax2.legend()
plt.tight_layout()
plt.show()
同じ次数・同じカットオフで並べると、バターワースの平坦な通過域、チェビシェフ I 型の通過域リップル、チェビシェフ II 型の阻止域リップル、楕円フィルタの両域リップルが ボード線図上で直接比較 できます。
設計指針
カットオフから次数を決める
通過域 \(f_p\) の最大減衰 \(A_p\) [dB] と阻止域 \(f_s\) の最小減衰 \(A_s\) [dB] が与えられたとき、バターワース次数は
\[N \geq \frac{\log_{10}\!\left(\dfrac{10^{A_s/10} - 1}{10^{A_p/10} - 1}\right)}{2\log_{10}(f_s / f_p)} \tag{8}\]で求まります。これは ボード線図上の「2 点」から傾きを逆算して必要なロールオフ率を決める 操作です。scipy.signal.buttord がこの計算を自動化します。
位相回りに注意
ロールオフ率を急峻にするほど位相回転が深くなり、群遅延の周波数依存性が増します。
- 音声処理・医療信号: 位相歪みを避けたい → FIR で線形位相を確保、または
filtfiltでゼロ位相化 - リアルタイム制御: 群遅延を最小化したい → ベッセルフィルタや低次バターワース
- 通過域の平坦性が最優先: バターワース
- 遷移帯域の急峻さが最優先: チェビシェフ・楕円
ボード線図の位相プロットは、この 「振幅性能と位相性能のトレードオフ」 を一望で示してくれる重要なツールです。
適応フィルタとの違い
ボード線図は 時不変な伝達関数を持つ固定フィルタ にのみ意味を持ちます。LMS/RLS などの適応フィルタ は係数が時間で変化するため、ある瞬間のスナップショットとしてのみボード線図を描けます。逆に、ボード線図で見た「目標の周波数応答」を適応フィルタが学習で近づける、という関係になります。
FFT / PSD との関係
ボード線図はフィルタ自体の特性を表しますが、実信号 がフィルタを通った結果を見るには FFT や 窓関数 + PSD と組み合わせます。具体的には、入力スペクトル \(X(f)\) と出力スペクトル \(Y(f) = H(f) X(f)\) から \(|H(f)| = |Y(f)/X(f)|\) を推定すれば、実測ボード線図 が得られます。これがシステム同定の基本原理です。
まとめ
- ボード線図は周波数応答 \(H(j\omega)\) を 振幅[dB] + 位相[deg] の 2 段、対数周波数軸 で描く可視化
- デシベルにより直列接続が加算に、対数軸により広帯域挙動が等間隔で読める
- 1 次系のロールオフは \(\pm 20\) dB/dec、2 次系は \(\pm 40\) dB/dec、\(N\) 次は \(\pm 20N\) dB/dec
- バターワース・チェビシェフ・楕円・ハイパス・バンドパス・ノッチなど すべてのフィルタの個性がボード線図に現れる
- Python では
scipy.signal.bode(連続時間)とscipy.signal.freqz(離散時間)が標準
各種フィルタの設計理論については以下の関連記事を参照してください。
関連記事
- バターワースフィルタの設計とPython実装 - 最大平坦特性のロールオフをボード線図上で確認できる代表的 IIR フィルタです。
- チェビシェフフィルタの設計原理とPython実装 - 通過域リップルがボード線図でどう現れるかを学べます。
- ハイパスフィルタの設計とPython実装 - 低域ロールオフと \(+90^\circ\) への位相回転をボード線図で読み解けます。
- ローパスフィルタの設計と比較 - ボード線図比較の最も基本的な題材です。
- バンドパスフィルタの設計とPython実装 - 中心周波数まわりの帯域特性と位相の対称性が観察できます。
- ノッチフィルタの設計とPython実装 - \(f_0\) における深いディップと急峻な位相回転をボード線図で確認できます。
- FIRフィルタとIIRフィルタの比較 - 線形位相 FIR の位相プロットが直線になることをボード線図で理解できます。
- 適応フィルタ(LMS/RLS)の理論とPython実装 - 時不変のボード線図と対比される、時変フィルタの設計手法を解説しています。
- 高速フーリエ変換(FFT)の仕組みとPython実装 - ボード線図の実測に用いる FFT の理論を解説しています。
- 窓関数と PSD 推定の実践 - 実測ボード線図におけるリーケージ抑制と PSD 推定を解説しています。
- ベッセルフィルタの理論とPython実装 - 群遅延が最大限平坦なベッセルは、位相プロットがほぼ直線として可視化されます。バターワース・チェビシェフとの位相比較に最適です。
- 時間周波数解析の選び方ハブ - ボード線図で評価する時不変フィルタの周波数応答を出発点に、時変・非定常信号を扱う FFT・STFT・Wavelet・Hilbert変換の使い分けを整理した姉妹ハブです。
参考文献
- Bode, H. W. (1945). Network Analysis and Feedback Amplifier Design. Van Nostrand.
- Ogata, K. (2010). Modern Control Engineering (5th ed.). Prentice Hall.
- Oppenheim, A. V., & Schafer, R. W. (2009). Discrete-Time Signal Processing (3rd ed.). Prentice Hall.
- scipy.signal.bode — SciPy documentation
- scipy.signal.freqz — SciPy documentation
関連ツール
- DevToolBox - 開発者向け無料ツール集 - JSON整形、正規表現テスターなど85種類以上の開発者向けツール
- CalcBox - 暮らしの計算ツール - 統計計算、周波数変換など61種類以上の計算ツール