ブロック線図の読み方と変換

ブロック線図は、制御系の構造を視覚的に表現するための図法です。複雑なシステムを伝達関数のブロックと信号の流れで表し、等価変換によりシステム全体の伝達関数を求めることができます。

本記事では、ブロック線図の基本要素から等価変換の規則、Python-controlでの実装までを解説します。

本記事の内容

  • ブロック線図の3つの基本要素
  • 直列・並列・フィードバック結合の等価変換
  • 加え合わせ点・引き出し点の移動規則
  • Python-controlによるブロック線図の構築

前提知識

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

ブロック線図の基本要素

ブロック線図は以下の3つの要素で構成されます。

1. ブロック(伝達関数)

入力信号を伝達関数 $G(s)$ に従って変換し出力信号を生成します。

U(s) ---> [G(s)] ---> Y(s) = G(s) U(s)

2. 加え合わせ点

複数の信号を加算(または減算)する点です。丸印 $\oplus$ で表します。

R(s) ---> (+)---> E(s) = R(s) - Y(s)
           ^(-)
           |
          Y(s)

3. 引き出し点

信号を分岐させて複数の経路に送る点です。

            +--->(経路1へ)
Y(s) ---> ●
            +--->(経路2へ)

引き出し点では信号の値は変わりません。分岐後のどちらの経路でも同じ $Y(s)$ が流れます。

等価変換の基本規則

直列結合

2つのブロックが直列に接続されている場合、等価な1つのブロックに変換できます。

U(s) ---> [G1(s)] ---> [G2(s)] ---> Y(s)

$$ Y(s) = G_2(s) \cdot G_1(s) \cdot U(s) $$

等価伝達関数は

$$ \begin{equation} G(s) = G_1(s) \cdot G_2(s) \end{equation} $$

直列結合では伝達関数のになります。

並列結合

2つのブロックが並列に接続されている場合を考えます。

        +--> [G1(s)] --+
U(s) -->|              (+)--> Y(s)
        +--> [G2(s)] --+

$$ Y(s) = G_1(s) U(s) + G_2(s) U(s) = \bigl[ G_1(s) + G_2(s) \bigr] U(s) $$

等価伝達関数は

$$ \begin{equation} G(s) = G_1(s) + G_2(s) \end{equation} $$

並列結合では伝達関数のになります。

フィードバック結合

フィードバック結合は制御系の最も基本的な構造です。

R(s) -->[+]---> E(s) ---> [G(s)] ---> Y(s) --->
          ^(-)                           |
          |                              |
          +-------- [H(s)] <-------------+

$E(s) = R(s) – H(s) Y(s)$ より

$$ Y(s) = G(s) E(s) = G(s) \bigl[ R(s) – H(s) Y(s) \bigr] $$

展開して

$$ Y(s) + G(s) H(s) Y(s) = G(s) R(s) $$

$$ Y(s) \bigl[ 1 + G(s) H(s) \bigr] = G(s) R(s) $$

したがって、閉ループ伝達関数は

$$ \begin{equation} \frac{Y(s)}{R(s)} = \frac{G(s)}{1 + G(s) H(s)} \end{equation} $$

正のフィードバック(加え合わせ点が $+$)の場合は分母の符号が変わります。

$$ \frac{Y(s)}{R(s)} = \frac{G(s)}{1 – G(s) H(s)} $$

$H(s) = 1$(単位フィードバック)のとき

$$ \frac{Y(s)}{R(s)} = \frac{G(s)}{1 + G(s)} $$

加え合わせ点と引き出し点の移動

等価変換を行うために、加え合わせ点や引き出し点を移動させる必要がある場合があります。

加え合わせ点をブロックの前後に移動

ブロックの前方から後方へ移動:

移動前: $R(s)$ と $D(s)$ を加え合わせてから $G(s)$ に入力

$$ Y(s) = G(s) \bigl[ R(s) + D(s) \bigr] $$

移動後: $D(s)$ に $G(s)$ を掛けてから加え合わせ

$$ Y(s) = G(s) R(s) + G(s) D(s) $$

つまり、加え合わせ点を後方に移動するとき、移動する信号に $G(s)$ を掛ける必要があります。

ブロックの後方から前方へ移動:

逆に、後方から前方に移動するときは $\frac{1}{G(s)}$ を掛けます。

引き出し点をブロックの前後に移動

ブロックの後方から前方へ移動:

引き出す信号が $Y(s) = G(s) U(s)$ だったものを、入力側 $U(s)$ から引き出すには $G(s)$ を掛けます。

ブロックの前方から後方へ移動:

入力 $U(s)$ を引き出していたものを出力側で引き出すには $\frac{1}{G(s)}$ を掛けます。

移動 補償ブロック
加え合わせ点を前→後 移動する信号に $G(s)$ を掛ける
加え合わせ点を後→前 移動する信号に $\frac{1}{G(s)}$ を掛ける
引き出し点を後→前 引き出す経路に $G(s)$ を掛ける
引き出し点を前→後 引き出す経路に $\frac{1}{G(s)}$ を掛ける

等価変換の具体例

以下のフィードバック系の伝達関数を求めます。

R -->[+]--> [G1] -->[+]--> [G2] ---> Y
       ^(-)           ^(-)    |
       |              |       |
       +--- [H1] <---+  [H2]<+
                          |
                          +--------->

この系は2つのフィードバックループが入れ子になっています。

ステップ1: 内側のフィードバックループを等価変換します。

$$ G_{inner}(s) = \frac{G_2(s)}{1 + G_2(s) H_2(s)} $$

ステップ2: 外側のフィードバックループを等価変換します。

$$ G_{total}(s) = \frac{G_1(s) \cdot G_{inner}(s)}{1 + G_1(s) \cdot G_{inner}(s) \cdot H_1(s)} $$

ステップ3: $G_{inner}(s)$ を代入して整理します。

$$ G_{total}(s) = \frac{G_1(s) G_2(s)}{1 + G_2(s) H_2(s) + G_1(s) G_2(s) H_1(s)} $$

Pythonでの実装

直列・並列・フィードバック結合

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

# 基本伝達関数の定義
G1 = ctrl.tf([2], [1, 1])       # G1(s) = 2/(s+1)
G2 = ctrl.tf([1], [1, 2])       # G2(s) = 1/(s+2)
H = ctrl.tf([0.5], [1])          # H(s) = 0.5

# 直列結合: G1 * G2
G_series = ctrl.series(G1, G2)

# 並列結合: G1 + G2
G_parallel = ctrl.parallel(G1, G2)

# フィードバック結合: G1*G2 / (1 + G1*G2*H)
G_fb = ctrl.feedback(G1 * G2, H)

print("直列結合:", G_series)
print("並列結合:", G_parallel)
print("フィードバック結合:", G_fb)

# ステップ応答の比較
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
t = np.linspace(0, 8, 500)

configs = [
    (G_series, 'Series: $G_1 \\cdot G_2$'),
    (G_parallel, 'Parallel: $G_1 + G_2$'),
    (G_fb, 'Feedback: $\\frac{G_1 G_2}{1+G_1 G_2 H}$')
]

for ax, (G, title) in zip(axes, configs):
    t_out, y_out = ctrl.step_response(G, T=t)
    ax.plot(t_out, y_out, linewidth=2)
    ax.set_xlabel('Time [s]')
    ax.set_ylabel('Output y(t)')
    ax.set_title(title)
    ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

入れ子のフィードバック系

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

# 入れ子フィードバック系
G1 = ctrl.tf([5], [1, 1])        # G1(s) = 5/(s+1)
G2 = ctrl.tf([3], [1, 3])        # G2(s) = 3/(s+3)
H1 = ctrl.tf([1], [1])           # H1(s) = 1
H2 = ctrl.tf([0.2], [1])         # H2(s) = 0.2

# 内側ループ
G_inner = ctrl.feedback(G2, H2)

# 外側ループ
G_total = ctrl.feedback(G1 * G_inner, H1)

# 手計算の検証
num_manual = np.polymul([5], [3])  # G1 * G2 の分子
den_inner = np.polyadd(np.polymul([1, 3], [1]), np.polymul([3], [0.2]))
den_total_part = np.polymul([1, 1], den_inner)
den_total = np.polyadd(den_total_part, num_manual)
G_manual = ctrl.tf(num_manual, den_total)

print("Python-control:", G_total)
print("手計算:", G_manual)

# ステップ応答
t = np.linspace(0, 5, 500)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# 内側ループの応答
t1, y1 = ctrl.step_response(G2, T=t)
t2, y2 = ctrl.step_response(G_inner, T=t)
ax1.plot(t1, y1, linewidth=2, label='$G_2$ (open)')
ax1.plot(t2, y2, linewidth=2, label='$G_2/(1+G_2 H_2)$ (closed)')
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('Output y(t)')
ax1.set_title('Inner Loop')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 全体系の応答
t3, y3 = ctrl.step_response(G1 * G2, T=t)
t4, y4 = ctrl.step_response(G_total, T=t)
ax2.plot(t3, y3, linewidth=2, label='$G_1 G_2$ (open)')
ax2.plot(t4, y4, linewidth=2, label='Total (closed)')
ax2.set_xlabel('Time [s]')
ax2.set_ylabel('Output y(t)')
ax2.set_title('Total System')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

まとめ

本記事では、ブロック線図の読み方と等価変換について解説しました。

  • ブロック線図はブロック・加え合わせ点・引き出し点の3要素で構成される
  • 直列結合は伝達関数の積、並列結合は和、フィードバック結合は $\frac{G}{1+GH}$
  • 加え合わせ点・引き出し点の移動には補償ブロック $G(s)$ または $\frac{1}{G(s)}$ が必要
  • Python-controlの series, parallel, feedback で簡潔に構築できる

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