ボード線図の描き方(実践)

ボード線図を手描きで正確に描けるようになることは、制御系の直感的な理解に直結します。基本要素のボード線図を覚えて合成すれば、複雑なシステムのボード線図を素早く描くことができます。

本記事では、折れ線近似の考え方、各基本要素のボード線図、複数要素の合成方法、Pythonでの検証までを解説します。

本記事の内容

  • 折れ線近似の考え方
  • 基本要素(積分器・微分器・1次遅れ/進み・2次遅れ)のボード線図
  • 複数要素の合成方法
  • Pythonでの描画と検証

前提知識

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

折れ線近似の考え方

ボード線図の折れ線近似とは、ゲイン線図を直線の組み合わせで近似する手法です。対数スケールの横軸と dB スケールの縦軸を使うと、多くの伝達関数のゲイン特性が直線で近似できます。

基本的な手順は以下の通りです。

  1. 伝達関数を基本要素の積に分解する
  2. 各要素の折れ線近似を描く
  3. 各要素のゲイン [dB] と位相 [deg] を足し合わせる

dB スケールでは積が和になるため、複雑な系のボード線図も単純な足し算で構成できます。

基本要素のボード線図

比例要素: $G(s) = K$

$$ |G(j\omega)|_{\text{dB}} = 20\log_{10} K, \quad \angle G(j\omega) = 0° $$

ゲインは $20\log_{10} K$ [dB] の水平線、位相は $0°$ です。

積分要素: $G(s) = \frac{1}{s}$

$$ |G(j\omega)|_{\text{dB}} = -20\log_{10}\omega, \quad \angle G(j\omega) = -90° $$

ゲインは $-20$ dB/dec の直線で、$\omega = 1$ で 0 dB を通ります。位相は常に $-90°$ です。

$n$ 重積分器 $G(s) = \frac{1}{s^n}$ では傾きが $-20n$ dB/dec、位相が $-90n°$ です。

微分要素: $G(s) = s$

$$ |G(j\omega)|_{\text{dB}} = 20\log_{10}\omega, \quad \angle G(j\omega) = +90° $$

積分要素の逆で、ゲインは $+20$ dB/dec の直線です。

1次遅れ要素: $G(s) = \frac{1}{\tau s + 1}$

折点周波数 $\omega_c = \frac{1}{\tau}$ を境に特性が変わります。

ゲインの折れ線近似:

$$ |G(j\omega)|_{\text{dB}} \approx \begin{cases} 0 \text{ dB} & (\omega \ll \omega_c) \\ -20 \log_{10}\frac{\omega}{\omega_c} & (\omega \gg \omega_c) \end{cases} $$

  • $\omega < \omega_c$: 0 dB の水平線
  • $\omega > \omega_c$: $-20$ dB/dec の直線
  • $\omega = \omega_c$: 実際のゲインは折れ線より約 $-3$ dB 低い

位相の近似:

$$ \angle G(j\omega) = -\arctan(\omega\tau) $$

  • $\omega = 0.1\omega_c$: 約 $-5.7°$
  • $\omega = \omega_c$: $-45°$
  • $\omega = 10\omega_c$: 約 $-84.3°$

1次進み要素: $G(s) = \tau s + 1$

1次遅れ要素の逆数なので、ゲインの傾きと位相の符号が逆転します。

  • $\omega < \omega_c$: 0 dB
  • $\omega > \omega_c$: $+20$ dB/dec
  • 位相: $0° \to +90°$

2次遅れ要素: $G(s) = \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2}$

$$ |G(j\omega)|_{\text{dB}} \approx \begin{cases} 0 \text{ dB} & (\omega \ll \omega_n) \\ -40 \log_{10}\frac{\omega}{\omega_n} & (\omega \gg \omega_n) \end{cases} $$

  • $\omega < \omega_n$: 0 dB の水平線
  • $\omega > \omega_n$: $-40$ dB/dec の直線(1次の2倍の傾き)
  • $\omega = \omega_n$ 付近: $\zeta$ が小さいと共振ピークが現れる

位相は $0° \to -180°$ に変化し、$\omega = \omega_n$ で $-90°$ です。

複数要素の合成

伝達関数を基本要素の積に分解し、各要素のゲイン [dB] と位相 [deg] を足し合わせます。

例題

$$ G(s) = \frac{10}{s(0.1s + 1)(0.5s + 1)} $$

分解すると

$$ G(s) = 10 \cdot \frac{1}{s} \cdot \frac{1}{0.1s + 1} \cdot \frac{1}{0.5s + 1} $$

各要素: 1. 比例: $K = 10$ → $+20$ dB 2. 積分: $\frac{1}{s}$ → $-20$ dB/dec, $-90°$ 3. 1次遅れ: $\tau_1 = 0.5$ → 折点 $\omega_{c1} = 2$ rad/s 4. 1次遅れ: $\tau_2 = 0.1$ → 折点 $\omega_{c2} = 10$ rad/s

合成のゲイン線図: – $\omega < 2$: 傾き $-20$ dB/dec(積分器のみ)、$\omega = 1$ で $+20$ dB - $2 < \omega < 10$: 傾き $-40$ dB/dec(積分器 + 1次遅れ1つ) - $\omega > 10$: 傾き $-60$ dB/dec(積分器 + 1次遅れ2つ)

Pythonでの実装

基本要素のボード線図一覧

import numpy as np
import matplotlib.pyplot as plt
import control as ctrl

omega = np.logspace(-2, 3, 1000)

# 基本要素
elements = {
    'Proportional K=5': ctrl.tf([5], [1]),
    'Integrator 1/s': ctrl.tf([1], [1, 0]),
    'Differentiator s': ctrl.tf([1, 0], [1]),
    '1st Lag (τ=0.5)': ctrl.tf([1], [0.5, 1]),
    '1st Lead (τ=0.5)': ctrl.tf([0.5, 1], [1]),
    '2nd Order (ζ=0.3)': ctrl.tf([4], [1, 1.2, 4]),
}

fig, axes = plt.subplots(2, 3, figsize=(18, 8))

for ax, (name, G) in zip(axes.flat, elements.items()):
    mag, phase, w = ctrl.frequency_response(G, omega)
    mag_db = 20 * np.log10(np.abs(mag))
    phase_deg = np.degrees(np.angle(mag))

    ax_phase = ax.twinx()
    ax.semilogx(w, mag_db, 'b', linewidth=2, label='Gain')
    ax_phase.semilogx(w, phase_deg, 'r--', linewidth=1.5, label='Phase')

    ax.set_ylabel('Gain [dB]', color='blue')
    ax_phase.set_ylabel('Phase [deg]', color='red')
    ax.set_xlabel('ω [rad/s]')
    ax.set_title(name)
    ax.grid(True, which='both', alpha=0.3)
    ax.axhline(y=0, color='k', linewidth=0.5)

plt.tight_layout()
plt.show()

折れ線近似と実際のボード線図の比較

import numpy as np
import matplotlib.pyplot as plt
import control as ctrl

# 1次遅れ系: G(s) = 1/(τs+1)
tau = 0.5
wc = 1 / tau  # 折点周波数
G = ctrl.tf([1], [tau, 1])

omega = np.logspace(-1, 2, 1000)
mag, phase, w = ctrl.frequency_response(G, omega)
mag_db = 20 * np.log10(np.abs(mag))
phase_deg = np.degrees(np.angle(mag))

# 折れ線近似
mag_approx = np.where(omega < wc, 0, -20 * np.log10(omega / wc))
phase_approx = np.where(omega < 0.1 * wc, 0,
                np.where(omega > 10 * wc, -90,
                         -45 * np.log10(omega / (0.1 * wc)) / np.log10(100)))

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)

# ゲイン線図
ax1.semilogx(w, mag_db, 'b', linewidth=2, label='Exact')
ax1.semilogx(omega, mag_approx, 'r--', linewidth=2, label='Asymptotic approx.')
ax1.axvline(x=wc, color='gray', linestyle=':', alpha=0.5)
ax1.annotate(f'ωc = 1/τ = {wc}', xy=(wc, 2), fontsize=10)
ax1.plot(wc, -3, 'ko', markersize=6)
ax1.annotate('-3 dB', xy=(wc, -3), xytext=(wc * 2, -6), fontsize=9,
             arrowprops=dict(arrowstyle='->', color='black'))
ax1.set_ylabel('Gain [dB]')
ax1.set_title(f'1st Order Lag: G(s) = 1/({tau}s + 1)')
ax1.legend()
ax1.grid(True, which='both', alpha=0.3)

# 位相線図
ax2.semilogx(w, phase_deg, 'b', linewidth=2, label='Exact')
ax2.semilogx(omega, phase_approx, 'r--', linewidth=2, label='Approx.')
ax2.axhline(y=-45, color='gray', linestyle=':', alpha=0.5)
ax2.axhline(y=-90, color='gray', linestyle=':', alpha=0.5)
ax2.set_xlabel('Frequency [rad/s]')
ax2.set_ylabel('Phase [deg]')
ax2.legend()
ax2.grid(True, which='both', alpha=0.3)

plt.tight_layout()
plt.show()

合成ボード線図

import numpy as np
import matplotlib.pyplot as plt
import control as ctrl

# G(s) = 10 / (s * (0.1s+1) * (0.5s+1))
G = ctrl.tf([10], np.polymul(np.polymul([1, 0], [0.1, 1]), [0.5, 1]))

# 各要素の伝達関数
G_K = ctrl.tf([10], [1])           # K = 10
G_int = ctrl.tf([1], [1, 0])       # 1/s
G_lag1 = ctrl.tf([1], [0.5, 1])    # 1/(0.5s+1)
G_lag2 = ctrl.tf([1], [0.1, 1])    # 1/(0.1s+1)

omega = np.logspace(-1, 3, 1000)

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 9), sharex=True)

# 各要素のゲイン
components = [
    (G_K, 'K=10', 'gray'),
    (G_int, '1/s', 'green'),
    (G_lag1, '1/(0.5s+1)', 'orange'),
    (G_lag2, '1/(0.1s+1)', 'purple'),
]

for Gc, name, color in components:
    mag, _, w = ctrl.frequency_response(Gc, omega)
    mag_db = 20 * np.log10(np.abs(mag))
    ax1.semilogx(w, mag_db, '--', color=color, linewidth=1, alpha=0.7, label=name)

# 合成ゲイン
mag_total, phase_total, w = ctrl.frequency_response(G, omega)
mag_db_total = 20 * np.log10(np.abs(mag_total))
phase_deg_total = np.degrees(np.unwrap(np.angle(mag_total)))

ax1.semilogx(w, mag_db_total, 'b', linewidth=2.5, label='Total G(s)')
ax1.axhline(y=0, color='k', linewidth=0.5)
ax1.axvline(x=2, color='orange', linestyle=':', alpha=0.5)
ax1.axvline(x=10, color='purple', linestyle=':', alpha=0.5)
ax1.set_ylabel('Gain [dB]')
ax1.set_title('Composite Bode Plot: $G(s) = 10 / (s(0.1s+1)(0.5s+1))$')
ax1.legend(fontsize=8, loc='lower left')
ax1.grid(True, which='both', alpha=0.3)

# 合成位相
# 各要素の位相
for Gc, name, color in components:
    mag_c, _, w_c = ctrl.frequency_response(Gc, omega)
    phase_c = np.degrees(np.unwrap(np.angle(mag_c)))
    ax2.semilogx(w_c, phase_c, '--', color=color, linewidth=1, alpha=0.7, label=name)

ax2.semilogx(w, phase_deg_total, 'b', linewidth=2.5, label='Total')
ax2.axhline(y=-180, color='r', linestyle='--', alpha=0.5)
ax2.set_xlabel('Frequency [rad/s]')
ax2.set_ylabel('Phase [deg]')
ax2.legend(fontsize=8, loc='lower left')
ax2.grid(True, which='both', alpha=0.3)

plt.tight_layout()
plt.show()

各要素のゲインと位相を足し合わせると、全体のボード線図が得られることが確認できます。

まとめ

本記事では、ボード線図の実践的な描き方を解説しました。

  • 折れ線近似により、ボード線図を直線の組み合わせで素早く描ける
  • 1次遅れ要素は折点周波数 $\omega_c = 1/\tau$ で傾きが $-20$ dB/dec 変化
  • 2次遅れ要素は $\omega_n$ で $-40$ dB/dec 変化し、$\zeta$ が小さいと共振ピークが出る
  • dB スケールの利点は、複数要素のゲインが足し算で合成できること

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