【问题标题】:Is there a better way to simulate PID control in Python with Scipy's solve_ivp()?有没有更好的方法在 Python 中使用 Scipy 的 solve_ivp() 模拟 PID 控制?
【发布时间】:2019-11-12 00:35:38
【问题描述】:

我正在处理一个家庭作业问题。我正在尝试使用 Scipy 的 integrate.solve_ivp() 函数在 Python 中模拟 PID 控制。

我的方法是在函数的右侧运行 PID 代码,使用全局变量并在每个时间步的末尾将它们附加到全局矩阵中,如下所示:

solution = integrate.solve_ivp(rhs, tspan, init, t_eval=teval)

这是我的代码:

def rhs(dt, init):

    global old_time, omega0dot, rhs_t, omega0dotmat
    timestep = dt - old_time
    old_time = dt

    # UNPACK INITIAL
    x = init[0]
    y = init[1]
    z = init[2]
    xdot = init[3]
    ydot = init[4]
    zdot = init[5]
    alpha = init[6]
    beta = init[7]
    gamma = init[8]
    alphadot = init[9]
    betadot = init[10]
    gammadot = init[11]

    # SOLVE EQUATIONS
    (xddot, yddot, zddot, alphaddot, betaddot, gammaddot) = dynamics(k_d, k_m, x, y, z, xdot, ydot, zdot, alpha, beta, gamma, alphadot, betadot, gammadot, omega0dot)

    # CONTROL SYSTEMS
    z_des = 10

    err_z = z_des - z

    zPID = (1*err_z) + hover

    omega0dot = zPID

    rhs_t.append(dt)
    omega0dotmat.append(omega0dot)

    return [xdot, ydot, zdot, xddot, yddot, zddot, alphadot, betadot, gammadot, alphaddot, betaddot, gammaddot]

全局变量在这个函数之外初始化。您可能会注意到我专门尝试模拟四旋翼飞行器,其中四旋翼的线性和角运动取决于omega0dot,它代表转子速度,我试图用 PID 控制它。

我的困难在于integrate.solve_ivp()时间步长。 PID 控制的积分和微分部分都依赖于时间步长,但solve_ivp() 函数具有可变时间步长,有时甚至会在时间上倒退,有时甚至没有时间步长(即 dt

我想知道是否有更好的方法来进行此 PID 控制,或者我是否将 solve_ivp() 中的 dt 术语解释错了。

【问题讨论】:

  • 顺便说一句,我尝试根据此线程 (stackoverflow.com/questions/54494770/…) 中的答案修复时间步,但我仍然偶尔会得到 0 个时间步。
  • 这是个坏主意。 solve_ivp 的方法都具有自适应时间步长,并且每一步都包含在接近但不在解曲线上的点处对导函数的多次评估。最好只求解没有任何全局变量的方程,然后从解样本中构造所需的数组。
  • 不幸的是,我无法在事后求解方程,因为转子速度,以及四轴飞行器的高度和姿态,都取决于前一个时间步长的计算。在这方面我错了吗?这就像模拟一个没有 PID 的系统,然后在事后计算 PID - 但模拟显然必须在首次运行时考虑 PID,因为 PID 会改变系统
  • 这在代码中是不可见的,多余的值是用来填充一些数组的,但不用来影响动态的。

标签: python scipy ode runge-kutta


【解决方案1】:

让我们看一个更简单的系统,带阻尼的无处不在的弹簧

    y'' + c*y' + k*y = u(t)

其中u(t) 可以代表电磁体施加的力(这立即提出了通过引入更现实的电压和磁场关系来使系统更复杂的方法)。

现在在 PID 控制器中,参考输出 e = yr - y 出现错误,并且

    u(t) = kD*e'(t) + kP*e(t) + kI*integral(e(t))

要使用 ODE 求解器处理此问题,我们立即看到需要使用新组件 E(t) 扩展状态,其中 E'(t)=e(t)。下一个困难是实现不一定可微的表达式的导数。这可以通过使用非标准的一阶实现(标准将使用[y,y',E] 作为状态)完全避免区分这个表达式来实现。

本质上,将公式中的所有派生表达式以其集成形式收集为

    v(t)=y'(t)+c*y-kD*e(t). 

然后回到导数得到一阶系统

    v'(t) = y''(t) + c*y'(t) - kD*e'(t) 
          = kP*e(t) + kI*E(t) - k*y(t)

    y'(t) = v(t) - c*y(t) + kD*e(t)
    E'(t) = e(t)

这现在允许将受控系统实现为 ODE 系统,而无需涉及全局内存或类似的技巧

def odePID(t,u,params):
    c,k,kD,kP,kI = params
    y,v,E = u
    e = yr(t)-y
    return [ v-c*y+kD*e, kP*e+kI*E-k*y, e ]

您应该能够在更复杂的模型中使用类似的一阶系统变换。

【讨论】:

  • 哇!我很惊讶你在 6 个月后又回来了!很高兴找到一些闭包,因为我相信这种方法会起作用
  • @squeegene :问题已被编辑,因此出现在“活动”列表中。大多数搜索结果只查看拉普拉斯图片和框图,从不了解这样的技巧。它有一种“未完成”的感觉。
猜你喜欢
  • 2013-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2012-09-17
  • 1970-01-01
  • 2021-10-19
相关资源
最近更新 更多