IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 位置式PID算法解释 -> 正文阅读

[数据结构与算法]位置式PID算法解释

一、公式拆解

P I D PID PID公式展示:
u ( t ) = K p ( e ( t ) + 1 T t ∫ 0 t e ( t ) d t + T D d e ( t ) d t ) u(t)=K_p(e(t)+\frac{1}{T_t } ∫_0^te(t)dt+T_D \frac {de(t)}{dt}) u(t)=Kp?(e(t)+Tt?1?0t?e(t)dt+TD?dtde(t)?)

K p K_p Kp?乘进去得:
u ( t ) = K p e ( t ) + K p T t ∫ 0 t e ( t ) d t + K p T D d e ( t ) d t u(t)=K_pe(t)+\frac{K_p}{T_t } ∫_0^te(t)dt+K_pT_D \frac {de(t)}{dt} u(t)=Kp?e(t)+Tt?Kp??0t?e(t)dt+Kp?TD?dtde(t)?
K p K_p Kp? 为比例时间系数

K i = K p T t K_i=\frac{K_p}{T_t } Ki?=Tt?Kp?? 为积分时间系数

K d = K p T D K_d=K_pT_D Kd?=Kp?TD? 为微分时间系数

就变成了这个亚子:
u ( t ) = K p e ( t ) + K i ∫ 0 t e ( t ) d t + K d d e ( t ) d t u(t)=K_p e(t)+K_i ∫_0^te(t)dt+K_d\frac{de(t)}{dt} u(t)=Kp?e(t)+Ki?0t?e(t)dt+Kd?dtde(t)?

符号或表达式代表意义
u t u_t ut?PID输出(不是系统输出)
K p K_p Kp?比例系数
K i K_i Ki?积分系数
K d K_d Kd?微分系数
e ( t ) e(t) e(t)目标值和当前值的误差

对于这个式子,其实涵盖了三种控制算法,每一种都可以单独拿出来。

比例控制算法 P P P u ( t ) 1 = K p e ( t ) u(t)_1=K_p e(t) u(t)1?=Kp?e(t)

积分控制算法 I I I u ( t ) 2 = K i ∫ 0 t e ( t ) d t u(t)_2=K_i ∫_0^te(t)dt u(t)2?=Ki?0t?e(t)dt

微分控制算法 D D D u ( t ) 3 = K d d e ( t ) d t u(t)_3=K_d\frac{de(t)}{dt} u(t)3?=Kd?dtde(t)?

你没有看错!PID算法其实就是三个算法的组合,而且,在数学上面就是简单的代数和!

二、基于物理进程的解释

A.比例控制算法

在这里面, e ( t ) = 目 标 值 ? 当 前 值 e(t)=目标值-当前值 e(t)=?,自然是离散数据,也就是说 u ( t ) 1 = K p e ( t ) u(t)_1=K_p e(t) u(t)1?=Kp?e(t)这个输出是根据当前值和目标值的差,乘以了一个比例系数得到的输出,

举个例子,假如我们要给一个100ml的A量筒装满水,此时A量筒里面已经有了20ml的水。而我们运水的工具是一个实际容积未知的B杯子

我们假如B杯子是一个50ml的杯子(我们并不知道是50ml),给其划上100等分的刻度,

那么根据条件可得 T 1 = e ( t ) = 目 标 值 ? 当 前 值 = 100 ? 20 = 80 T_1=e(t)=目标值-当前值=100-20=80 T1?=e(t)=?=100?20=80 。因此现在我们给B杯子装入可达第80刻度线的水并“一滴不漏”倒入A量筒,由于B杯子实际容积为50ml,所以实际倒入A量筒的水体积为40ml。
可以发现,我们要给100ml的量筒装满水,通过量筒的刻度我们轻易能获知还需要倒80ml才能装满杯子,但是B杯子的容积我们并不知道。我们给未知容积的B杯子划上刻线,以获知的80这个数据来给A量筒倒水,最好的情况是我的B杯子容积为100ml,这样的话按照80的刻度,一次性就可以把水加满。由于B杯子容积只有50,所以第一次加水只加了40ml,这便是比例系数的引入。

这之间读者务必注意你的已知和未知
从上文举的例子,我们可以明白,我们获知量和得知该量继而调整输出之间是难以对等的,例子中都是容积,实际上做控制时,将会是跨物理单位的转换,比如你获知你的小车速度设定为3m/s,实际只有2m/s,但你不可能直接给小车送定额的速度。你只能去调整等效电压、电流的大小,或者是PWM的占空比以使得速度达到3m/s,速度和电压必然是不对等的,但这之间会存在一个未知的关系,在这里,你可以认为是比例系数。

单纯以容积的刻度作为比照,此处B杯子50ml(我们刻了100等分刻度),所以虽然我们不知道容积,但这个过程,B杯子一个刻度的水量相当于A量筒的刻度的一半,所以实际上 Kp=0.5
(务必注意此处的0.5是根据刻度转换得来的,也就是你心中的量度转化为实际输出的关系,我看有些文章以容积为对比,这显然错误)
让强迫症把上面物理进程讲完吧

加了40ml水后,A量筒水位达到20+40=60ml的刻度, T 2 = e ( t ) = 目 标 值 ? 当 前 值 = 100 ? 60 = 40 T_2=e(t)=目标值-当前值=100-60=40 T2?=e(t)=?=100?60=40
依照这个40,B杯子加水到40刻度, K p e ( t ) = 0.5 × 40 = 20 m l K_p e(t)=0.5\times40=20ml Kp?e(t)=0.5×40=20ml,倒进A之后水位达到60+20=80ml,如此不断无穷循环下去,A量筒最终会被装满。(例子举的是B容积小于A量筒的情况,各位看官可以自行假想B容积大于A量筒的情形。我们使用时肯定是会逐步修正这个系数的)

B.积分控制算法

照前面比例控制算法,量筒最终会被装满,那么为何还需要别的算法呢?客官莫急,待老朽与您娓娓道来(娓娓道来:形容谈论不倦,或说话动听,没错,我是后者)

刚才比例控制算法那段内容我给“一滴不漏”特别标成了红色,实际上我们真的能一滴不漏吗?当然不能!

回到之前举的装水问题,现在已经装了 80 m l 80ml 80ml,现在我们多加入一个条件,那就是我们每次加水,都会漏掉10ml

现在可得 e ( t ) 2 = 100 ? 80 = 20 e(t)_2=100-80=20 e(t)2?=100?80=20,那么按照比例控制算法,我们通过B杯子加进去能加入10ml的水,但是现在又会漏掉 10 m l 10ml 10ml 10 m l ? 10 m l = 0 m l 10ml-10ml=0ml 10ml?10ml=0ml,哦豁,加完水还是 80 m l 80ml 80ml,之后条件不变,意味着 80 m l 80ml 80ml到头了,升不上去了,可是到此为止离我们的目标100ml还差20ml呢!
现实中就是这样,你不可能想给多少那边就接收多少。
于是这里我们加入积分控制算法,

u ( t ) 2 = K i ∫ 0 t e ( t ) d t u(t)_2=K_i ∫_0^te(t)dt u(t)2?=Ki?0t?e(t)dt
u ( t ) = u ( t ) 1 + u ( t ) 2 = K p e ( t ) + K i ∫ 0 t e ( t ) d t u(t)=u(t)_1+u(t)_2=K_p e(t)+K_i ∫_0^te(t)dt u(t)=u(t)1?+u(t)2?=Kp?e(t)+Ki?0t?e(t)dt

可以看到这个式子与误差的积分成正比,在积分系数已经定下时(时间肯定取单位时间),误差的积分越大,这个积分控制算法得到的也越大,先设它积分系数为0.2,接着上面的80ml继续计算,已知比例控制算法的输入和漏掉的相等,那么这是整个 P I PI PI算法只需要看积分控制算法的输出就行了,,故积分控制输出4ml,很显然,积分控制的加入打破了之前的稳定状态。积分的来源是误差,误差的累加会增大输入,从而不让系统卡在稳定误差,事实上另一方面积分项也加快了整体控制算法的响应速度(因为 e ( t ) e(t) e(t)的符号,让正的值更大,负的更小)。

最后到了目标值后,假如没有波动,回过去看看三种控制算法,貌似无论是比例还是微分控制,输出都是0,只有积分的值是固定输出(积分误差累加),很显然,就是为了消除之前那种稳定状态的静差。(静差:被控量的稳定值和给定值之差,一般用于衡量系统的准确性)静差很难被消除,但是通过积分控制可以尽量去减小,而且积分系数不可太大,太大静差反而更大,有网友给出就平衡车工程经验而言,一般 K i = K p 200 K_i=\frac{K_p}{200} Ki?=200Kp??,稍微来点就可以。

C.微分控制算法

这里就是对误差进行微分:

d e ( t ) d t = E r r o r ( n o w ) ? E r r o r ( p a s t ) \frac{de(t)}{dt}=Error(now)-Error(past) dtde(t)?=Error(now)?Error(past)

当前值<目标值 时:一般在该调节过程中,误差是越来越小的(正实数运算),这也就可以得
E r r o r ( n o w ) ? E r r o r ( p a s t ) < 0 Error(now)-Error(past)<0 Error(now)?Error(past)<0

对于主要的比例控制而言,此时 e ( t ) > 0 e(t)>0 e(t)>0,二者符号相反,换言之,这里微分控制起到了削减比例控制力度的作用

当前值>目标值 时: 输出过大,需要减小,对于比例控制, e ( t ) < 0 e(t)<0 e(t)<0,反观微分控制:
E r r o r ( n o w ) < 0 Error(now)<0 Error(now)<0
E r r o r ( p a s t ) < 0 Error(past)<0 Error(past)<0
∣ E r r o r ( n o w ) ∣ < ∣ E r r o r ( p a s t ) ∣ |Error(now)|<|Error(past)| Error(now)<Error(past)
? E r r o r ( n o w ) < ? E r r o r ( p a s t ) -Error(now)<-Error(past) ?Error(now)<?Error(past)
E r r o r ( n o w ) > E r r o r ( p a s t ) Error(now)>Error(past) Error(now)>Error(past)
E r r o r ( n o w ) ? E r r o r ( p a s t ) > 0 Error(now)-Error(past)>0 Error(now)?Error(past)>0

哦豁,符号又和比例控制相反,太显然了,这明摆着就是和主要的比例控制唱反调啊!太嚣张了!

如果愿意的话,还可以细究一些量的关系,这里就不多说了。

最终结论就是微分起到阻尼作用,减小震荡,提高稳定,减小变化趋势,但是也会降低响应速度。

总结一下,PID三个字母,大致可以说是P主管响应,I减小静差,D抑制震荡,但使用不当还会有反作用。

三、C语言代码框架和解释(单片机可用)

1、定义一个PID结构体代码

//定义一个PID结构体

typedef struct PID

{

	double SetValue; //设定的目标值

	double Kp; //比例系数

	double Kd;  //微分系数

	double Ki; //积分系数

	double Error1; //误差量,即为当前值和目标值的差

	double Error2;//也是误差量,但是这里是Error1之后时间的当前值和目标值的差,假设Error1在前

	double sum_error; //误差的总和,这个就是误差的积分

}PID;

2、初始化PID结构

//初始化PID结构,这里独立出来比较符合单片机的写法,哈哈,也可以写到主函数里面去
void PIDInit(PID*Stru)  //这里就是随便弄了个结构体变量Stru,反正是传递地址,主函数里面随意
{
	memset(Stru,0,sizeof(PID));  /*这里把Stru的内存块中全部替换为0,据我的经验,一开始分配好内存后都是些无意义数据或者残留数据,这里memset函数的作用实际上把Stru里面所有字节最后“sizeof(PID)”位的字节全部替换为0,因为这里位数为sizeof(PID),因此是初始化全部字节为0 */
}

//PID的内部运算函数
double PID_OP(PID*Stru , double NewValue)  //NewValue是新读取到的值
{
	double dError,Error;  //dError即为对误差微分,由于我们这里是处理离散数据,所以待会其实就是作差
	//误差计算
	Error= Stru->SetValue-NewValue;  //这里是目标值和当前读取值的偏差,这里吧,无论是当前值在前还是目标值在前都是无所谓的,因为对于整个PID而言,误差的比例算法、积分算法、微分算法都是统一用的。不过话说回来,我不知道使用者是打算用什么负号表示输出,姑且这么用吧,使用者用时根据自己的理解用吧,毕竟咱这里只是个大体框架。
	//加入微分算法
	dError=Stru->Error2-Stru->Error1;  //当前微分计算(离散)注意,这里如果误差越来越小的话无疑这里微分是负值
	//加入积分算法
	Stru->Sum_Error+=Error;  //积分运算
	Stru->Error1=Stru->Error2;  //新误差值传递给旧误差,如此往复传递,为下个离散时间点PID的计算做准备
	Stru->Error2=Error;  //这里一开始Error2应该是0,但无所谓,这里计算过程用时是循环,后面会有值堆上去。
	return(Stru->Kp*Error+Stru->Ki*Stru->Sum_Error+Stru->Kd*dError);  //按颜色不同,分别为比例、积分、微分。但是可以注意的是,这里并不是初始的PID系数。在这个PID框架里面,很明显,是把初始的PID数学式展开了,后面的微积分两个系数都是和比例系数发生了运算的,这不影响。
}

3、输入函数和输出函数

//输入函数和输出函数,叫输入输出系统好些
//这里输入的就是读取编码盘数据,输出就是“控制器的输出”,大致就是改变PWM啥的吧,这个之后再说也行。这里就不能具体写了,是单片机的特色部分,本来我是不打算写上去的,不过参考的罗世洲先生的代码里面提到了这个,鄙人思索之下还是加上去了,毕竟框架嘛,嚯哈哈
void Input()  //采集编码盘或别的数据
{
	//加油
}
void System_Out(PID_out)  //注意这里不是PID输出,而且根据PID结果进而进行相应硬件输出
{
	//加油
}

4、定时器初始化函数

//定时器初始化函数
//设置定时中断作为控制周期
void time_interrupt_Init(这里可以放装填初值啥的,随意)
{
	这里进行一些启动定时器中断、设置工作方式、装填初值啥的骚操作
}

5、中断函数

//中断函数
void Timer0() interrupt 1   //这里我当做51来做了
{
	定时器硬件的装初值或其它操作语句,具体硬件具体分析;
PID_in=Input();  //执行输入函数,读取输入
	PID_out=PID_OP(&Squ,PID_in);  //运行PID的运算函数,得出PID_out,好用于系统输出函数
	System_Out(PID_out);  //系统输出
}

6、主函数

//主函数
void main()
{
	PID Squ;  //Squirtle是杰尼龟的英文名,杰尼龟在咱们国家被亲切叫做憨批龟,开个玩笑,这个结构体变量就用憨批龟吧!
	double PID_out,PID_in;  //定义PID的数据来源和数据输出
	PIDInit(&Squ);  //执行PID初始化函数,其实这里就只是全部存成0,记得吧,之前说的那个memset函数。
	Squ.Kp=0.5;  //设置比例系数,当然三个系数和目标值不一定得就这么赤裸裸写出来,换成读取感觉更灵活,随意
	Squ.Ki=0.5;  //设置积分系数
	Squ.Kd=0.1;  //设置微分系数,值得注意的是,物理进程里面这里起到阻尼作用,无论是对动态还是静态的阻尼,这都是一个“往回扳”的作用,变化越快阻尼也越大
	Squ.SetValue=100.0;  //设置目标值,这里是随便弄的

	void time_interrupt_Init(这里可以放装填初值啥的,随意)    //初始化定时器

	while(1);  //程序停在这里等待中断发生
	主函数里面还可以放一些存储调试数据的语句,这种语句优先等级低,在不干扰正常操作情况下存储或者发送调试数据,个人认为对调试有一定帮助(强烈推荐eeprom);
}

P.S.:我们在PID控制中不可能处理并且输出连续数据,因此这本文档中都是处理离散数据,而这些离散数据的源头是编码盘或者其它硬件,这也就意味着必须考虑采集数据的装置的采集速度、周期。采集完后又得CPU进行处理,处理又需要时间,电压或者电流的响应也需要时间。为了不让其中发生“碰撞”,这就需要我们去设置一个控制周期,调整好二者,给予二者良好配合的空间。

四、PID归纳

写程序不难,难的是调出一个好的参数
在实际使用中,不一定非要把P、I、D三个算法都用上
依据需要进行组合使用吧


各个系数的作用表
性能指标参数
Kp增大Ki减小Kd增大
偏差增大增大减小
稳态误差减小----------
超调量增大增大减小
振荡频率增大增大增大
写这合并单元格还得我拿html写,可恶啊!
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-07 12:17:52  更:2021-12-07 12:18:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 14:34:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码