PID 制御2020年4月22日 | |
はじめにバネ-ダンパー-質点系について、PID 制御を行う。 バネ-ダンパー-質点系のシミュレーション2 自由度のバネ-ダンパー-質点系を線形システム状態方程式として解く。SymPy を用いてラグランジュの手法で運動方程式を導出し、線形化する。 from sympy import symbols, diff, Matrix, lambdify from sympy.physics.mechanics import dynamicsymbols from sympy.physics.mechanics import ReferenceFrame, Point, Particle from sympy.physics.mechanics import LagrangesMethod, Lagrangian t = symbols("t") z1, z2, f1, f2 = dynamicsymbols("z1 z2 f1 f2") m1, m2, c1, c2, k1, k2 = symbols("m1 m2 c1 c2 k1 k2") q = Matrix([z1, z2]) N = ReferenceFrame("N") p1 = Point("p1") v1 = z1.diff(t) p1.set_vel(N, v1*N.x) pa1 = Particle("pa1", p1, m1) pa1.potential_energy = k1*z1**2/2 + k2*(z2 - z1)**2/2 p2 = Point("p2") v2 = z2.diff(t) p2.set_vel(N, v2*N.x) pa2 = Particle("pa2", p2, m2) F = c1*v1**2/2 + c2*(v2 - v1)**2/2 fc1 = -F.diff(v1) fc2 = -F.diff(v2) fl = [(p1, (f1 + fc1)*N.x), (p2, (f2 + fc2)*N.x)] L = Lagrangian(N, pa1, pa2) LM = LagrangesMethod(L, q, forcelist = fl, frame = N) LM.form_lagranges_equations() As, Bs, u = LM.linearize(q_ind=q, qd_ind=q.diff(t), A_and_B=True) 具体的な数値を設定する。 m1n = 1. m2n = 1. c1n = 0.5 c2n = 0.5 k1n = 0.2 k2n = 0.2 As_func = lambdify((m1, m2, c1, c2, k1, k2), As, modules="numpy") A0 = As_func(m1n, m2n, c1n, c2n, k1n, k2n) Bs_func = lambdify((m1, m2), Bs, modules="numpy") B0 = Bs_func(m1n, m2n) C0 = [0., 1., 0., 0.] x0 = [0.5, 1., 0., 0.] dt = 0.1 end_time = 10. t = np.linspace(0., end_time, int(end_time/dt) + 1) 線形システム状態方程式をシミュレートする。ここでは、PIC 制御のため SciPy などを用いずに自力で計算する。線形システム状態方程式を次式とする。 $$\dot{x}(t) = A_0 x(t) + B_0 u(t)$$ $$y(t) = C x(t)$$この連続時間表現を、次の離散時間表現で表す。 $$x(t_{k+1}) = A x(t_k) + B u(t_k)$$ $$y(t_k) = C x(t_k)$$ここで \(A = e^{A_0 T}\)、\(B = (e^{A_0 T} - I) A_0^{-1} B_0\) で、\(e^{A_0 T}\) は行列指数関数、\(T = t_{k+1} - t_k\) はサンプリング周期、要するに時間刻み幅である。 以下では入出力は省略している。 from scipy.linalg import expm from matplotlib import pyplot as plt import seaborn as sns sns.set() n = len(x0) A = expm(A0*dt) steps = int(end_time/dt) + 1 x = np.zeros((steps, n)) x[0, :] = x0 x1 = np.array(x0) for step in range(1, steps): x1[:] = A.dot(x1) x[step, :] = x1 plt.plot(t, x[:, 0], label="$x_1$") plt.plot(t, x[:, 1], label="$x_2$") plt.plot(t, x[:, 2], label="$x_3$") plt.plot(t, x[:, 3], label="$x_4$") plt.xlim(0., end_time) plt.xlabel("$t$") plt.ylabel("$x$") plt.legend() plt.show() PID 制御PID 制御は、比例 (P)、積分 (I)、微分 (D) の 3 つの動作で制御を行うものである。目標値を \(r(t)\) として、出力 \(y(t)\) との偏差 \(e(t) = r(t) - y(t)\) に対して、入力 \(u(t)\) を次のように決める。 $$u(t) = k_P e(t) + k_I \int_0^t e(\tau) d\tau + k_D \dot{e}(t)$$\(k_P\)、\(k_I\)、\(k_D\) をそれぞれ比例ゲイン、積分ゲイン、微分ゲインという。これらは制御パラメータで、問題に合わせて調整する必要がある。パラメータの決め方の一つに限界感度法というものがあり、次のように決める。
以下の例では、\(x_2 = z_2\) を出力として、それに対して目標値 \(r = 0\) として PID 制御を適用している。 from numpy.linalg import inv ku = 10. # 限界感度 tu = 2. # 限界周期 ti = 0.5*tu td = 0.125*tu #kp = 0.6*ku #ki = kp/ti #kd = kp*td kp = 4. ki = 1. kd = 1.5 print("kp =", kp) print("ki =", ki) print("kd =", kd) n = len(x0) n2 = int(n/2) A = expm(A0*dt) B = (A - np.eye(n)).dot(inv(A0)).dot(B0) C = np.array(C0) r = 0. u = np.zeros(n2) cu = np.zeros(n2) cu[-1] = 1. e = 0. e0 = 0. int_e = 0. diff_e = 0. steps = int(end_time/dt) + 1 x = np.zeros((steps, n)) x[0, :] = x0 x1 = np.array(x0) for step in range(1, steps): y = C.dot(x1) e = r - y int_e += 0.5*(e + e0)*dt diff_e = (e - e0)/dt u[:] = (kp*e + ki*int_e + kd*diff_e)*cu e0 = e x1[:] = A.dot(x1) + B.dot(u) x[step, :] = x1 plt.plot(t, x[:, 0], label="$x_1$") plt.plot(t, x[:, 1], label="$x_2$") plt.plot(t, x[:, 2], label="$x_3$") plt.plot(t, x[:, 3], label="$x_4$") plt.xlim(0., end_time) plt.xlabel("$t$") plt.ylabel("$x$") plt.legend() plt.show() 参考
| |
PENGUINITIS |