Runge-Kutta 用于 PID 和系统的单独计算,无需过滤器

计算科学 Python 数字 龙格库塔
2021-12-11 17:49:11

我需要用 Python 计算一个闭环系统;具体来说,获取PID响应,然后使用输出通过我自己的循环逐个样本获取系统响应。

为此,我使用具有自适应步长的四阶 Runge-Kutta 来求解状态空间表示。到目前为止,我设法获得良好的近似响应的唯一方法是添加一个带有 PID 的低通滤波器:

以传递函数形式,我的 PID 没有过滤器:

G(s)c=Kp+Kis+KdN1+Ns

我的 PID 和过滤器:

G(s)c=10.1s+1(Kp+Kis+KdN1+Ns)

使用示例系统:

G(s)=1s2+s+1
,

其中Kp=Ki=Kd=1N=100

过滤结果:

在此处输入图像描述

并且没有过滤器:

在此处输入图像描述

预期的响应应该类似于第一张图像,但过滤器会为响应添加微小的变化。如果我在一个传递函数中计算 PID + system,我会得到预期的结果并且我不需要过滤器(可能是因为系统充当过滤器),但是,就像我说的,要求是计算PID 分开。

如果我增加 RK4 的相对容差和绝对容差,我也可以获得预期的响应,但是步数的增加量是不可接受的,从 890 到 4269。系统本身无关紧要,可以是任何东西。

关于如何在没有过滤器或大幅增加步骤数的情况下获得准确结果的任何想法?

完整的python代码:

import numpy as np
import control as ctrl
from matplotlib import pyplot as plt
import time


def norm(x):
    return np.linalg.norm(x) / x.size**0.5


def runge_kutta(ss, x, h, inputValue):
    k1 = h * (ss.A * x + ss.B * inputValue)
    k2 = h * (ss.A * (x + k1/2) + ss.B * inputValue)
    k3 = h * (ss.A * (x + k2/2) + ss.B * inputValue)
    k4 = h * (ss.A * (x+k3) + ss.B * inputValue)

    x = x + (1/6) * (k1 + k2*2 + k3*2 + k4)
    y = ss.C * x + ss.D * inputValue
    return y.item(), x


N = 100
kp = 1
ki = 1
kd = 1

pid = ctrl.tf2ss(
    ctrl.TransferFunction([1], [0.1, 1]) *
    ctrl.TransferFunction([N*kd + kp, N*kp + ki, N * ki], [1, N, 0]))

x_pidB = np.zeros_like(pid.B)
x_pidS = np.zeros_like(pid.B)

system = ctrl.tf2ss(ctrl.TransferFunction([1], [1, 1, 1]))
vstadosB = np.zeros_like(system.B)

min_step_decrease = 0.2
max_step_increase = 5
h_ant = 0.0001
rtol = 1e-3
atol = 1e-6
Time = 0
tbound = 30
sp = 1
output = [0]
Time_out = [0]
yb = 0
sf1 = 0.9
sc_t = [0]
start = time.time()
counter = 0

while Time < tbound:
    error = sp - yb
    while True:
        counter += 1
        if Time + h_ant >= tbound:
            h_ant = tbound - Time
            ypids, x_pidSn = runge_kutta(pid, x_pidS, h_ant, error)
        else:
            ypidb, x_pidBn = runge_kutta(pid, x_pidB, h_ant, error)
            ypids, x_pidSn = runge_kutta(pid, x_pidS, h_ant / 2, error)
            ypids, x_pidSn = runge_kutta(pid, x_pidSn, h_ant / 2, error)

            scale = atol + rtol * (np.abs(x_pidSn) + np.abs(x_pidBn)) / 2
            delta1 = np.abs(x_pidBn - x_pidSn)
            error_norm = norm(delta1 / scale)

            if error_norm == 0:
                h_est = h_ant * max_step_increase
            elif error_norm < 1:
                h_est = h_ant * min(max_step_increase,
                                    max(1, sf1 * error_norm**(-1 / (4+1))))
            else:
                h_ant = h_ant * max(min_step_decrease, sf1 * error_norm**(-1 / (4+1)))
                continue

        yb, vstadosB = runge_kutta(system, vstadosB, h_ant, ypids)
        break

    sc_t.append(ypids)
    output.append(yb)
    Time += h_ant
    Time_out.append(Time)

    h_ant = h_est
    x_pidB = x_pidSn
    x_pidS = x_pidSn

print(f'Number of calculations:{counter}')
print(f'Time:{time.time() - start:.3f}')

fig = plt.figure(212)
ax1 = fig.add_subplot(211)
ax1.plot(Time_out, output, label='System response')
ax1.legend()
ax1.grid()

ax2 = fig.add_subplot(212)
ax2.plot(Time_out, sc_t, 'r', label='Control signal')
ax2.legend()
ax2.grid()

plt.show()
0个回答
没有发现任何回复~