读在线广告智能出价相关的论文时,论文中基于 PID 算法设计了多变量的控制算法,既然遇到了 PID 算法相关的内容,便想着借此契机,简单总结一下 PID 算法,该算法在网上已有较多科普文章,但依旧打算整理一下,加深自己的理解。

PID 算法因为其实现简单且效果拔群的特性,而被广泛使用,比如温度控制元件、无人机飞行姿势、机器人控制等。

在控制系统理论体系中,会根据系统是否有反馈将系统分为开环系统与闭环系统,而 PID 算法是闭环系统中常用的算法,为了方便理解,这里举个具体的例子来解释开环系统与闭环系统:

我开发了一个机器人,在一开始,机器人身上没有安装任何传感器,此时你给机器人一个目标,让它跑到距离它当前位置 10 米远的前方,因为机器人没有环境传感器,即缺乏反馈信息,它在前进时,很可能就会偏离当前目标,如图 1 所示:

图 1

将开环系统的整体结构抽象一下,如图 2 所示:

图 2

结合机器人的例子,其中:

  • Input:机器人接收到一个指令,该指令让机器人前进 10 米

  • Controller:控制器通过计算得出要前进的方向与距离

  • Process:机器人执行控制器的计算结果,前进相应的距离

从图 2 可以看出,整个系统是没有反馈的,此时机器人在前进的过程中,很可能就出现图 1 的情况。

我们为机器人添加环境传感器,它现在可以判断自己与目标的方向与距离的,此时,我们将开环系统转变成了闭环系统,其抽象结构为:

图 3

在闭环系统中,机器人的例子是这样的:

  • Input:机器人接收到一个指令,该指令让机器人前进 10 米

  • Controller:控制器通过计算得出要前进的方向与距离

  • Process:机器人执行控制器的计算结果,前进相应的距离

  • Feedback:传感器收集环境信息,比如当前是否偏离了目标,如果偏离了,就将偏离的距离作为 error ,然后再作为 Controller 输入的一部分

在闭环系统下,机器人可以比较好的到达目标位置了,但如果要求机器人尽可能快的到达目标位置呢?

尽可能快背后的要求是,完成任务的时间尽可能短且要准确的到达目的地,不能有偏差,此时我们可以利用 PID 算法去实现这个目标。在闭环系统中,主要就是利用 PID 算法来实现了 Controller 这块,如图 4 所示:

图 4

理解 PID 算法思想

举一个经典的例子。

我有一个水缸,希望让水缸中的水位保持在 1 米。

假设现在水缸中,其水位为 0.2 米,此时误差(error)为 0.8 米(1-0.2)。

我知道误差为 0.8 后,开始往水缸里加水,目标是让水缸中的水保持在 1 米。

回到 PID 算法,其中的 P(Proportion)表示比例控制,我们将 PID 算法简化成 P 算法(其不考虑 ID 部分的算法逻辑),其公式为:,其中 表示 时刻下当前的水位, 表示 时刻下当前的误差,而 是个超参数,是需要人通过试验效果来设置的,即我对 K_p 取不同的值,选择一个效果好的,这里设 ,那么在 时,,即这一次操作,我向水缸里添加了 0.4 米的水,现在水缸里的水位是 0.6 米(0.2+0.4),此时误差是 0.4 米(1-0.6),当 时,我再次加水,计算过程为 ,我向水缸里,添加 0.2 米的水,水缸总水量为 0.8 米(0.6+0.2),一直重复这个过程,就可以让水缸里的水保持在 1 米。

整个过程,只有 是我们按经验设置的,其中 的值越大,水缸到达 1 米的过程就越快,比如设 ,那么 ,此时在 的时刻,水缸里的水就加到 1 米了,反之 的越小,到达目标的速度就越慢。

PID 算法,似乎只有 P 算法就可以完成的很好,为啥还要 ID 部分的算法呢?

这是因为现实情况中,这个水缸是会漏水的,接着看这个例子。

依旧假设 ,当水缸里的水有 0.8 米时,计算过程为 ,即我要向水缸里加 0.1 米的水,但水缸每 t 时间段下会漏下 0.1 米的水,即我添加完 0.1 米的水后,因为又漏掉了,此时误差还是 0.2 米,然后因为 P 方法,我还会继续往里加 0.1 米的水,这个过程一直反复,但水缸里的水位将固定在 0.8 米,不再变化。

这种情况便是:稳态误差,顾名思义,误差不会在变小。

在实际生活中,漏水的水缸是存在的,比如通过控制汽车时,会有摩擦阻力(漏水),控制无人机时,会有空气阻力(漏水),各种阻力,都是水缸中漏水的那个口子。

PID 中 I(Integration )算法部分就是为了解决稳态误差的问题,原本的 P 算法加上 I 算法后,其公式为:。

依旧回到水缸的例子,当水位维持 0.8 米时,I 算法会计算出 0 到 t 时刻下误差的积分,误差积分再乘以 这个超参数(也是需要人按具体试验结果进行设置),I 算法获得的值添加到 P 算法中,此时水缸漏水是 0.1 米,而 PI 算法共同计算出的值会大于 0.1 米,水缸水位会慢慢上升到 1 米。

现在还剩 PID 算法中的 D(Differentiation)算法,在实际使用时,D 算法确实是按需使用的,即不用 D 算法,也是常规操作。

D 算法主要的作用就是减少系统在到达目标过程中产生的大量震动,依旧以水缸为例,通过 D 算法,可以防止给水缸加水时,超过 1 米的高度,然后减少水位,又低于1米,然后又加水,又超过1米,这种反复震荡的情况,需要注意,震荡通常都会存在,而D算法目标是减少震荡次数,而不是完全消除震荡。

PID 算法公式拆解

个人认为,深入理解 PID 算法的公式,才是掌握 PID 算法的不二法门,虽然前面通过简单的比喻理解了 PID 算法的思想,但还是有必要讨论一下其公式。

先摘抄一下 PID 算法公式:

公式

上述公式中:

  • :某个时刻

  • :某个时刻下的误差

  • :比例增益

  • :积分增益

  • :微分增益

我们以文章一开始提及的,机器人移动到 10 米前的位置为例子,基于 P 算法,机器人从初始位置移动到目标位置时,其距离变化图 5 所示:

图 5

可以发现,机器人为了实现尽快到达目标的目的,在一开始距离比较远时,速度在快速加快,从图 5 来看,就是斜率较大的部分,此时机器人快速到达 10M 处,但是因为速度问题,冲过了 10 米,冲过后,基于 Feedback 的误差,机器人将会往回走,经过几次波动后,到达 10 米的目标位置。

我们将机器人移动过程中,误差变化所对应的函数 也绘制出来,如下图 6 所示:

图 6

从图 6 可以看出,P 算法 会基于误差控制机器人移动速度,在第一个虚线处,error 较大且是正数,P 算法会让机器人快速移动,第二虚线处,error 是负数,P 算法会让机器人往回移动。

I 算法()表示误差在一定时间内的积分,通过图画出来,积分就是 函数曲线与 Time 轴之间的面积,如图 7 所示:

图 7

I 算法通过积分算法,不断积累误差(面积一直在变大),最终乘以 ,便是 I 算法的部分。

D 算法()是误差的微分处理,它表示 函数在某一点的斜率,如图 8 所示:

图 8

从图 8 可知,当斜率变化过快时,D 算法便会获得较大的负数,以抑制输出的上升速度,从而避免机器人在到达目标时,多次震荡。

PID 算法中3 个不同部分相互配合,其直观形式如下 gif 图:

离散化处理

在数字系统中使用 PID 算法时,因为数字系统是离散的(只有 0 和 1),所以我们需要对 PID 算法进行离散化处理,这里的公式变化需要微积分相关的知识。

设系统采样的时间段为 ,将系统获得的误差 序列化为 ,即 时间段下,可以获得 n 个独立的误差,输出也需要由 变化成 .

对于其中第 k 个 () 输出,其公式如下:

公式

(如果不理解,可以尝试从几何角度画图理解一下。)

Python 实现

利用 Python,基于 PID 离散化公式形式,实现 PID 算法,代码如下(代码中给出了关键注释):

import timeclass PID:"""PID算法实现"""def __init__(self, P=0.2, I=0.0, D=0.0, current_time=None):""":param P: P算法超参数:param I: I算法超参数:param D: D算法超参数:param current_time: 当前时刻"""self.Kp = Pself.Ki = Iself.Kd = D# 采样时间段self.sample_time = 0.00self.current_time = current_time if current_time is not None else time.time()self.last_time = self.current_timeself.clear()def clear(self):"""清理系数"""self.SetPoint = 0.0self.PTerm = 0.0self.ITerm = 0.0self.DTerm = 0.0self.last_error = 0.0self.int_error = 0.0# 最大的波动值self.I_max_modify = 20.0self.output = 0.0def update(self, feedback_value, current_time=None):"""math::u(k) = K_pe_k + K_i\sum_{j=0}^ke(j)\Delta t + K_d\frac{e(k) - e(k-1)}{\Delta t}"""# 误差error = self.SetPoint - feedback_valueself.current_time = current_time if current_time is not None else time.time()# 间隔时间delta_time = self.current_time - self.last_timeif(delta_time >= self.sample_time):self.ITerm += error * delta_time# 限制积分项ITerm最大与最小值if(self.ITerm < -self.I_max_modify):self.ITerm = -self.I_max_modifyelif(self.ITerm > self.I_max_modify):self.ITerm = self.I_max_modifyself.DTerm = 0.0if delta_time > 0:self.DTerm = (error - self.last_error) / delta_time# 更新时间self.last_time = self.current_timeself.last_error = error# PID结果self.output = (self.Kp * error) + (self.Ki * self.ITerm) + (self.Kd * self.DTerm)

为了判断写的代码是否正确,这里再写一个简单的测试代码,如下:

from pid import PIDimport time
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import BSpline, make_interp_spline def test_pid():# PID中的超参数P = 1.2I = 1D = 0.001# 循环次数L = 50pid = PID(P, I, D)pid.SetPoint = 0.0pid.sample_time = 0.01feedback = 0feedback_list = []time_list = []setpoint_list = []for i in range(1, L):pid.update(feedback)output = pid.outputif pid.SetPoint > 0:# 更新feedbackfeedback += (output - (1/i)) # 目前只是为了观察if i > 9:pid.SetPoint = 1if i > 30:pid.SetPoint = 1.2time.sleep(0.02)# 为了绘制曲线,记录相应的数值feedback_list.append(feedback)setpoint_list.append(pid.SetPoint)time_list.append(i)time_sm = np.array(time_list)# 在指定的间隔内返回均匀间隔的数字time_smooth = np.linspace(time_sm.min(), time_sm.max(), 300)# 拟合一条曲线 - 拟合后,曲线才能绘制出来feedback_smooth = make_interp_spline(time_list, feedback_list)(time_smooth)plt.plot(time_smooth, feedback_smooth, color='r')# 拟合目标变化句plt.plot(time_list, setpoint_list, color='b')plt.xlim((0, L))plt.ylim((min(feedback_list)-0.5, max(feedback_list)+0.5))plt.xlabel('time (s)')plt.ylabel('PID')plt.title('TEST PID')plt.ylim((1-0.5, 1+0.5))plt.grid(True)plt.show()if __name__ == "__main__":test_pid()

为了方便观察,循环 i>9 时,才将目标设置为 1,然后当 i>30 时,将目标修改为 1.2,然后将 PID 算法的结果通过曲线图绘制出来,从而直观的看出,我们实现的 PID 算法,其效果如何,最终效果如下:

从上图可以看出,效果还不错 o (>_<) o

那么关于 PID 算法,就简单讨论到这里,我是二两,我们下篇文章见。

PID 控制算法原理与 Python 实现相关推荐

  1. PID控制算法原理通俗讲解

    一文读懂PID控制算法(抛弃公式,从原理上真正理解PID控制) PID控制应该算是应用非常广泛的控制算法了.小到控制一个元件的温度,大到控制无人机的飞行姿态和飞行速度等等,都可以使用PID控制.这里我 ...

  2. 数字PID控制算法原理及Matlab仿真

    引言 最近碰到一个项目需要用到PID控制算法,于是在网上找了一些资料学习了一下,发现网上对于PID算法的Matlab仿真方面的内容比较少,所以我就把我自己所学习到的内容分享给大家.本次博文主要介绍了位 ...

  3. 经典PID控制算法原理以及优化思路

    文章目录 0.概念 1.理解 2.实现 3.优化 4.引用 0.概念 PID算法是工业应用中最广泛算法之一,在闭环系统的控制中,可自动对控制系统进行准确且迅速的校正.PID控制,即Proportion ...

  4. 自适应模糊PID控制算法

    一.自适应模糊PID控制 自适应模糊PID控制将模糊控制与传统PID控制相结合,将两种控制方式进行结合,取长补短,对传统的算法进行优化,形成一种新的控制算法,自适应模糊PID控制可以用于很多场景,比如 ...

  5. PID原理及python简单实现与调参

    一.前言 近期在实际项目中使用到了PID控制算法,于是就该算法做一总结. 二.PID控制算法详解 2.1 比例控制算法 例子: 假设一个水缸,需要最终控制水缸的水位永远维持在1米的高度. 水位目标:T ...

  6. PID控制器概述及python实现PID控制算法

    PID控制器简要分析 PID控制器概述 PID控制器的分类 位置式PID 增量式PID 代码实现 参数整定 PID控制器概述 PID控制器是自动控制领域一种常见的控制器,其简单易设计的结构和良好的鲁棒 ...

  7. PID控制算法的原理剖析

    我的微信公众号(ID:00后开发者)从00后的角度出发,专注但不局限于分享电气.嵌入式.机器视觉以及芯片行业的算法.技术文章和最新资讯.如果想查看更多内容,可以关注我的微信公众号. 前言 大学时期,为 ...

  8. 四轴PID控制算法详解(单环PID、串级PID)

    正文开始:这篇文章分为三个部分: PID原理普及 常用四轴的两种PID算法讲解(单环PID.串级PID) 如何做到垂直起飞.四轴飞行时为何会飘.如何做到脱控? PID原理普及 1.  对自动控制系统的 ...

  9. python实现守护进程_守护进程原理及Python实现

    守护进程原理及Python实现 守护进程,不依赖于终端,在后台运行的程序,通常称为daemon(ˈdiːmən或ˈdeɪmən). 一些常见的Linux软件通常都是已守护进程的方式运行,比如: ngi ...

最新文章

  1. 10行代码带你搞定目标检测(附代码)
  2. 信号处理专业名词术语
  3. ROS服务通信机制原理及示例代码
  4. 牛客题霸 [寻找第K大] C++题解/答案
  5. eclipse下载与安装步骤详解,包含解决错误(最全最详细,多图)
  6. 明明的随机数(快排)
  7. 培智学校计算机课教案,培智数学教案
  8. 甜甜用计算机1050除以一个数,上册四年级数学期末试卷带答案
  9. linux中csh怎么运行,bash csh 设置环境变量 方法例子
  10. 京东天猫茅台抢购代码的一些总结
  11. 如何集中远程管理各种USB设备,还不了解一下?
  12. 射频电路PCB的设计技巧
  13. 呵呵,原来梦醒后一切这么美好(KK记)
  14. Appium+Python MAC安装Android夜神模拟器(二)
  15. pytorch 12 支持任意维度数据的梯度平衡机制GHM Loss的实现(支持ignore_index、class_weight,支持反向传播训练,支持多分类)
  16. C++项目--汇总(无工作经验或者不到两年工作经验者)
  17. 空气动力学类毕业论文文献有哪些?
  18. 一文理解分布式常见的一致性算法
  19. 呵,偶居然也能“说教”了
  20. 深度学习-Resolution-robust Large Mask Inpainting with Fourier Convolutions基于傅里叶卷积的对分辨率鲁棒的掩模修复

热门文章

  1. 白盒测试与黑盒测试分别有哪些测试方法?
  2. Hadoop文章汇总
  3. python爬虫五大解析器
  4. 母胎级教学,工业路由器远程维护PLC详细操作指南
  5. DevEco Device Tool 3.1 Release新版本发布,新增资源管理器、SFTP、HDC
  6. 加密算法、散列算法、摘要、签名、证书、MD5、RSA、SSL通讯等等
  7. 计算指数_计算大指数
  8. 利用阿里大于接口发短信(Delphi版)
  9. java 获取对象的字节数
  10. linux pureftpd 教程,Pureftpd攻略之建立账号