たけし備忘録

自分の好奇心の赴くままに勉強メモ LL系が大好き Python bash Julia C

状態フィードバック制御

状態フィードバック制御とは

システム $$ \dot{x} = x + u $$ について考える。このシステムの極は、特性方程式により、

$$ (s-1) = 0 $$ $s=1$が極となる。
したがって、このシステムは不安定である。

$$ x = A \exp{(st)} $$

ここで入力$u$を工夫して

$$ u = kx + v $$

このようにすると、

新たなシステムが

$$ \begin{align} \dot{x} &= x + kx + v \\ &= (1+k)x + v \end{align} $$

この特性方程式

$$ (s-(1+k)) = 0 $$ により $$ s = 1+k $$ となり、$s$を任意の値に設定することが出来る。

任意極配置

$$ \left\{ \begin{array}{c} \dot{\boldsymbol{x}} &=& A\boldsymbol{x}+\boldsymbol{b}u \\ y &=& \boldsymbol{cx} \end{array} \right. $$

これについて状態フィードバック
$$ u = \boldsymbol{kx} + v $$ を行う。ここで$\boldsymbol{k} = [k_1 \, k_2 \, \cdots \, k_n]$である。この$\boldsymbol{k}$をフィードバックゲインと呼ぶ

これよりシステム全体は

$$ \begin{array}{l} \dot{\boldsymbol{x}} &=& A\boldsymbol{x}+\boldsymbol{b}u \\ &=& A\boldsymbol{x}+\boldsymbol{b}(\boldsymbol{kx}+v) \\ &=& (A+\boldsymbol{kb})\boldsymbol{x}+\boldsymbol{b}v \\ \end{array} $$

ここで新たにシステム行列$A$を $$ A_k = A+\boldsymbol{kb} $$ とする。システムの極は、システム行列$A_k$の固有値によって決定する。
$A_k$の特性方程式は $$ |sI-A_k| $$ となる。

任意極配置の設計法

希望する極 $\lambda_1,\, \lambda_2,\, \cdots ,\lambda_n$に配置するような状態フィードバック制御

$$ u = \boldsymbol{kx} + v $$

を求めたい。 そのために、システムを可制御正準形

$$ \left\{ \begin{array}{l} \dot{\tilde{\boldsymbol{x}}} &=& A_c \tilde{\boldsymbol{x}}+\boldsymbol{b_c}u \\ y &=& \boldsymbol{c_c} \tilde{\boldsymbol{x}} \end{array} \right. $$

$$ A_c = \left[ \begin{array}{ccccc} 0 & 1 & 0 & \cdots & 0 \\ 0 & 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & 1 \\ -a_0 & -a_1 & -a_2 & \cdots & -a_{n-1} \end{array} \right] , \boldsymbol{b_c} = \left[ \begin{array}{c} 0 \\ 0 \\ \vdots \\ 0 \\ 1 \end{array} \right] $$

$$ \boldsymbol{c_c} = \left[ \begin{array}{cccc} c_0 \, c_1 \, \cdots \, c_{n-1} \end{array} \right] $$

に変換する

例題1

次のシステムを考える

$$ A = \left[ \begin{array}{cc} 1 & 0 \\ 0 & 2 \end{array} \right] $$

import control as pc
import numpy as np

State = pc.ss([[1, 0],
               [0, 2]],
              [[1], 
               [1]],
              [[1, 1]], 0)
Mc = pc.ctrb(State.A, State.B)
print np.linalg.matrix_rank(Mc)
print Mc
2
[[1 1]
 [1 2]]

よって可制御である

State_canon, T = pc.canonical_form(State, "reachable")
State_canon
A = [[ 3. -2.]
 [ 1.  0.]]

B = [[ 1.]
 [ 0.]]

C = [[ 2. -3.]]

D = [[0]]

閉ループ系の特性多項式を求める

k = pc.place(State.A, State.B, [-2, -3])
k
array([[-12.,  20.]])
Ak = State.A - State.B*k
np.linalg.eig(Ak)
(array([-2., -3.]), matrix([[ 0.8       ,  0.78086881],
         [ 0.6       ,  0.62469505]]))
T
matrix([[-1.,  2.],
        [-1.,  1.]])
L = np.linalg.inv(T*Mc)
L
matrix([[ 1., -3.],
        [ 0.,  1.]])

Python-Controlでの話

Python-Controlモジュールでは少々勝手が変わってくるのでメモしておく

import control as pc
import numpy as np
sys = pc.ss([[1, 0],
             [0, 2]],
            [[1],[1]],
            [[1, 1]],
              0)

可制御行列$M_c$

$M_c$を求めるにはpc.ctrbを使う
$M_c$は

$$ M_{c} = \left[ B \ AB \ A^{2}B \ \cdots \ A^{n-1}B \right] $$

と定義される。

今回は $$ M_c = [A \, AB] = \left[ \begin{array}{cc} 1 & 1 \\ 1 & 2 \end{array} \right] $$ となる

Mc = pc.ctrb(sys.A, sys.B)
Mc
matrix([[1, 1],
        [1, 2]])

可制御正準形

可制御正準形を求めるにはpc.canonical_formを使う
可制御正準形にするためには変換行列$T_c$を

$$ T_c = (M_c L)^{-1} $$

として求める。ここで$L$は

$$ L = \left[ \begin{array}{ccccc} 1 & a_{n-1} & \cdots & a_{2} & a_{1} \\ 0 & 1 & \cdots & a_{3} & a_{2} \\ \vdots & \vdots & \ddots & \dots & \vdots \\ 0 & 0 & \cdots & 1 & a_{n-1} \\ 0 & 0 & \cdots & 0 & 1 \end{array} \right] $$

である。

sys_c, Tc = pc.canonical_form(sys, "reachable")
sys_c, Tc
(A = [[ 3. -2.]
  [ 1.  0.]]

 B = [[ 1.]
  [ 0.]]

 C = [[ 2. -3.]]

 D = [[0]], matrix([[-1.,  2.],
         [-1.,  1.]]))

任意極配置の状態フィードバックゲイン$\boldsymbol{k}$

任意の可制御システム$(A, B)$の極を任意の位置に配置するにはpc.placeを用いる。

Python-Controlでは、
$$ u = -\boldsymbol{kx} + v $$ としたフィードバックでのゲインを求めるようになっている

k = pc.place(sys.A, sys.B, [-2, -3])
k
array([[-12.,  20.]])

確認してみる

np.linalg.eig(sys.A-sys.B*k)
(array([-2., -3.]), matrix([[ 0.8       ,  0.78086881],
         [ 0.6       ,  0.62469505]]))

固有値は極に対応するため、ちゃんと固有値が-2と-3になっている。

開ループ系と閉ループ系の単位ステップ応答の確認

開ループ系

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
x0 = [1.0, 0.1]
out_op, t_op = pc.step(sys, X0=x0)

fig = plt.figure(figsize=(10, 8))
plt.plot(t_op, out)

f:id:takeshiD:20160825160623p:plain

閉ループ系

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)
out_fb, t_fb = pc.step(sys_fb, X0=x0)

fig = plt.figure(figsize=(10, 8))
plt.plot(t_fb, out_fb)

f:id:takeshiD:20160825160626p:plain

閉ループ系の様々な応答の確認

ステップ応答と初期条件応答

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)
out_fb, t_fb = pc.step(sys_fb, X0=x0)
fig = plt.figure(figsize=(10, 8))
plt.plot(t_fb, out_fb, label="step")

sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)
out_fb, t_fb = pc.initial(sys_fb, X0=x0)
plt.plot(t_fb, out_fb, label="initial")

plt.legend()

f:id:takeshiD:20160825160630p:plain

インパルス応答

fig = plt.figure(figsize=(10, 8))
sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)
out_fb, t_fb = pc.impulse(sys_fb)
plt.plot(t_fb, out_fb)

f:id:takeshiD:20160825160633p:plain

線形システム応答

おそらくこれが最も汎用的な応答である。
pc.lsim(sys, U, T, X0)という使い方をする。

sysは状態システム
Uは入力の配列 Tは時間の配列
X0は初期条件である

もちろんそれぞれで、UとTは同次元の配列でなければならない X0は,sysで定義されているsys.Aの行と列の次元に一致しなければならない、などの制約がある。

自由応答(U=0.0)

fig = plt.figure(figsize=(10, 8))

sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)

x0 = [1.0, 0.1]
N = 1024
t = np.linspace(0, 4, 1024)
u = np.zeros(N)
out_fb, t_fb, x_fb = pc.lsim(sys_fb, U=u, T=t, X0=x0)
plt.plot(t_fb, out_fb, label="output")
plt.plot(t_fb, x_fb[:,0], label="$X_1$")
plt.plot(t_fb, x_fb[:,1], label="$X_2$")
plt.legend()

f:id:takeshiD:20160825160635p:plain

状態変数$x_0, x_1$ともに0に収束し、出力も目標値である入力$u$に収束して0になっている。

ステップ応答

fig = plt.figure(figsize=(10, 8))

sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)

x0 = [1.0, 0.1]
N = 1024
t = np.linspace(0, 4, 1024)
u = np.around(np.linspace(0, 1, N))
out_fb, t_fb, x_fb = pc.lsim(sys_fb, U=u, T=t, X0=x0)
plt.plot(t_fb, out_fb, label="Output")
plt.plot(t_fb, x_fb[:,0], label="$X_1$")
plt.plot(t_fb, x_fb[:,1], label="$X_2$")
plt.plot(t_fb, u, label="Input")
plt.legend()

f:id:takeshiD:20160825160639p:plain

矩形波(初期条件 [1.0, 0.1])

fig = plt.figure(figsize=(10, 8))

sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)

x0 = [1.0, 0.1]
N = 1024
t = np.linspace(0, 4, 1024)
u = np.sign(np.sign(np.sin(2*np.pi*t)))
out_fb, t_fb, x_fb = pc.lsim(sys_fb, U=u, T=t, X0=x0)
plt.plot(t_fb, out_fb, label="Output")
plt.plot(t_fb, x_fb[:,0], label="$X_1$")
plt.plot(t_fb, x_fb[:,1], label="$X_2$")
plt.plot(t_fb, u, label="Input")
plt.legend()

f:id:takeshiD:20160825160642p:plain

矩形波(初期条件 [0.0, 0.0])

fig = plt.figure(figsize=(10, 8))

sys_fb = pc.ss(sys.A-sys.B*k, sys.B, sys.C, sys.D)

x0 = [0.0, 0.0]
N = 1024
f = 1
t = np.linspace(0, 4, N)
u = np.sign(np.sin(2*np.pi*f*t))
out_fb, t_fb, x_fb = pc.lsim(sys_fb, U=u, T=t, X0=x0)
plt.plot(t_fb, out_fb, label="Output")
plt.plot(t_fb, x_fb[:,0], label="$X_1$")
plt.plot(t_fb, x_fb[:,1], label="$X_2$")
plt.plot(t_fb, u, label="Input")
plt.legend()

f:id:takeshiD:20160825160645p:plain