ステップ応答とインパルス応答

ステップ応答とインパルス応答は、LTIシステムの特性を完全に記述する2つの基本的な応答です。インパルス応答がわかれば、畳み込み積分により任意の入力に対する応答を計算できます。

本記事では、インパルス応答の定義と伝達関数との関係、ステップ応答との関係、畳み込み積分の仕組み、Pythonでの可視化までを解説します。

本記事の内容

  • インパルス応答の定義と伝達関数との関係
  • ステップ応答とインパルス応答の微積分関係
  • 畳み込み積分による任意入力への応答
  • 応答特性の読み取り方
  • Pythonでの可視化

前提知識

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

インパルス応答とは

定義

インパルス応答 $g(t)$ は、システムに単位インパルス(ディラックのデルタ関数)$\delta(t)$ を入力したときの出力です。

$$ u(t) = \delta(t) \quad \Rightarrow \quad y(t) = g(t) $$

デルタ関数のラプラス変換は $\mathcal{L}[\delta(t)] = 1$ なので

$$ Y(s) = G(s) \cdot U(s) = G(s) \cdot 1 = G(s) $$

したがって

$$ \begin{equation} g(t) = \mathcal{L}^{-1}[G(s)] \end{equation} $$

つまり、インパルス応答は伝達関数の逆ラプラス変換です。

具体例: 1次遅れ系

$G(s) = \frac{1}{Ts + 1}$ の場合

$$ g(t) = \mathcal{L}^{-1}\left[\frac{1}{Ts + 1}\right] = \mathcal{L}^{-1}\left[\frac{1/T}{s + 1/T}\right] = \frac{1}{T} e^{-t/T} \quad (t \geq 0) $$

具体例: 2次遅れ系(不足減衰)

$G(s) = \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2}$ の場合($0 < \zeta < 1$)

$$ g(t) = \frac{\omega_n}{\sqrt{1-\zeta^2}} e^{-\zeta\omega_n t} \sin(\omega_d t) \quad (t \geq 0) $$

ここで $\omega_d = \omega_n\sqrt{1-\zeta^2}$ は減衰固有振動数です。

ステップ応答とインパルス応答の関係

ステップ応答の定義

ステップ応答 $h(t)$ は、単位ステップ入力に対する出力です。

$$ U(s) = \frac{1}{s} \quad \Rightarrow \quad Y(s) = G(s) \cdot \frac{1}{s} $$

微積分関係

ステップ入力は $u(t) = \int_0^t \delta(\tau) d\tau$(インパルスの積分)なので、LTIシステムの線形性と時不変性より

$$ \begin{equation} h(t) = \int_0^t g(\tau) d\tau \end{equation} $$

逆に

$$ \begin{equation} g(t) = \frac{dh(t)}{dt} \end{equation} $$

つまり、ステップ応答を微分するとインパルス応答インパルス応答を積分するとステップ応答が得られます。

検証: 1次遅れ系

インパルス応答 $g(t) = \frac{1}{T} e^{-t/T}$ を積分すると

$$ h(t) = \int_0^t \frac{1}{T} e^{-\tau/T} d\tau = \left[-e^{-\tau/T}\right]_0^t = 1 – e^{-t/T} $$

これは確かに1次遅れ系のステップ応答です。

畳み込み積分

任意入力に対する応答

LTIシステムでは、任意の入力 $u(t)$ に対する出力は畳み込み積分で計算できます。

$$ \begin{equation} y(t) = \int_0^t g(t – \tau) u(\tau) d\tau = g(t) * u(t) \end{equation} $$

これは、入力を微小なインパルスの重ね合わせとみなし、各インパルスに対する応答を足し合わせたものです。

ラプラス変換との対応

畳み込み積分のラプラス変換は積になります。

$$ \mathcal{L}[g(t) * u(t)] = G(s) \cdot U(s) = Y(s) $$

これが伝達関数による入出力関係 $Y(s) = G(s) U(s)$ の時間領域での表現です。

応答特性の読み取り方

ステップ応答から読み取れる情報

特性 読み取り方
定常値 十分時間が経った後の値
立ち上がり時間 $t_r$ 定常値の 10% から 90% に達する時間
オーバーシュート $M_p$ 定常値からの超過量
整定時間 $t_s$ 定常値の $\pm 2\%$ に収まる時間
振動の有無 不足減衰か過減衰かの判定

インパルス応答から読み取れる情報

特性 読み取り方
安定性 $t \to \infty$ で $g(t) \to 0$ なら安定
減衰の速さ 包絡線の減衰率
振動数 振動の周期から固有振動数がわかる
系の次数 応答の複雑さからおおよその次数が推定できる

Pythonでの実装

ステップ応答とインパルス応答の比較

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

# 2次遅れ系
wn = 2.0
zeta = 0.3
G = ctrl.tf([wn**2], [1, 2*zeta*wn, wn**2])

t = np.linspace(0, 8, 1000)

# ステップ応答
t_step, y_step = ctrl.step_response(G, T=t)

# インパルス応答
t_imp, y_imp = ctrl.impulse_response(G, T=t)

# ステップ応答の数値微分 → インパルス応答の近似
dt = t[1] - t[0]
y_diff = np.gradient(y_step, dt)

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# ステップ応答
axes[0, 0].plot(t_step, y_step, 'b', linewidth=2)
axes[0, 0].axhline(y=1.0, color='k', linestyle='--', alpha=0.3)
axes[0, 0].set_xlabel('Time [s]')
axes[0, 0].set_ylabel('h(t)')
axes[0, 0].set_title(f'Step Response (ζ={zeta}, ωn={wn})')
axes[0, 0].grid(True, alpha=0.3)

# インパルス応答
axes[0, 1].plot(t_imp, y_imp, 'r', linewidth=2)
axes[0, 1].axhline(y=0, color='k', linewidth=0.5)
axes[0, 1].set_xlabel('Time [s]')
axes[0, 1].set_ylabel('g(t)')
axes[0, 1].set_title(f'Impulse Response (ζ={zeta}, ωn={wn})')
axes[0, 1].grid(True, alpha=0.3)

# 微積分関係の検証: dh/dt ≈ g(t)
axes[1, 0].plot(t_imp, y_imp, 'r', linewidth=2, label='g(t) = impulse response')
axes[1, 0].plot(t_step, y_diff, 'b--', linewidth=1.5, label="dh/dt (numerical)")
axes[1, 0].set_xlabel('Time [s]')
axes[1, 0].set_ylabel('Response')
axes[1, 0].set_title('Verification: g(t) = dh(t)/dt')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

# 積分関係の検証: ∫g(τ)dτ ≈ h(t)
y_int = np.cumsum(y_imp) * dt
axes[1, 1].plot(t_step, y_step, 'b', linewidth=2, label='h(t) = step response')
axes[1, 1].plot(t_imp, y_int, 'r--', linewidth=1.5, label='∫g(τ)dτ (numerical)')
axes[1, 1].set_xlabel('Time [s]')
axes[1, 1].set_ylabel('Response')
axes[1, 1].set_title('Verification: h(t) = ∫g(τ)dτ')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

異なるシステムの応答比較

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

# 3つのシステム
systems = {
    '1st Order (T=1)': ctrl.tf([1], [1, 1]),
    '2nd Order (ζ=0.3)': ctrl.tf([4], [1, 1.2, 4]),
    '2nd Order (ζ=1.0)': ctrl.tf([4], [1, 4, 4]),
}

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
t = np.linspace(0, 8, 1000)

for name, G in systems.items():
    t_s, y_s = ctrl.step_response(G, T=t)
    t_i, y_i = ctrl.impulse_response(G, T=t)

    ax1.plot(t_s, y_s, linewidth=2, label=name)
    ax2.plot(t_i, y_i, linewidth=2, label=name)

ax1.axhline(y=1.0, color='k', linestyle='--', alpha=0.3)
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('h(t)')
ax1.set_title('Step Response Comparison')
ax1.legend()
ax1.grid(True, alpha=0.3)

ax2.axhline(y=0, color='k', linewidth=0.5)
ax2.set_xlabel('Time [s]')
ax2.set_ylabel('g(t)')
ax2.set_title('Impulse Response Comparison')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

畳み込み積分による任意入力の応答

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

# システム: 1次遅れ系
T_sys = 1.0
G = ctrl.tf([1], [T_sys, 1])

# 時間軸
dt = 0.01
t = np.arange(0, 10, dt)

# 任意入力: 矩形波
u = np.zeros_like(t)
u[(t >= 1) & (t < 3)] = 1.0
u[(t >= 5) & (t < 7)] = 0.5

# インパルス応答
g = (1 / T_sys) * np.exp(-t / T_sys)

# 畳み込み積分で出力を計算
y_conv = np.convolve(g, u)[:len(t)] * dt

# python-controlでの検証
t_lsim, y_lsim = ctrl.forced_response(G, T=t, U=u)

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

ax1.plot(t, u, 'b', linewidth=2)
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('u(t)')
ax1.set_title('Input Signal')
ax1.grid(True, alpha=0.3)
ax1.set_xlim([0, 10])

ax2.plot(t, y_conv, 'r', linewidth=2, label='Convolution')
ax2.plot(t_lsim, y_lsim, 'b--', linewidth=1.5, label='python-control')
ax2.set_xlabel('Time [s]')
ax2.set_ylabel('y(t)')
ax2.set_title('Output: Convolution vs python-control')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.set_xlim([0, 10])

plt.tight_layout()
plt.show()

畳み込み積分の結果と python-control の forced_response の結果がほぼ一致することが確認できます。

まとめ

本記事では、ステップ応答とインパルス応答について解説しました。

  • インパルス応答は伝達関数の逆ラプラス変換 $g(t) = \mathcal{L}^{-1}[G(s)]$
  • ステップ応答はインパルス応答の積分 $h(t) = \int_0^t g(\tau) d\tau$、逆に $g(t) = \frac{dh}{dt}$
  • 畳み込み積分 $y(t) = g(t) * u(t)$ により任意の入力に対する応答が計算できる
  • ステップ応答から定常値・オーバーシュート・整定時間などの過渡特性を読み取れる

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