完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
(一) 前言
PID算法在工业应用中随处可见。大学期间,想做各类科创也少不了PID算法的身影。PID除了需要理解原理,用合理的代码实线PID算法也让许多同学们苦恼,我总结了常用的多种PID算法的C语言实现,供大家参考学习。 我只是简单总结了常用的一些PID算法。对于模糊PID和专家PID,因为我也没用到过,就没有具体给出代码,这其中最难的还是参数的整定。当然仿真软件也十分有帮助,大家可以看我另外的Simulink的学习。 整理不易,请留个赞呗、 (二) PID原理简单介绍 PID的流程本质上是通过误差信号控制被控量,而控制器本身就是比例、积分、微分三个环节的加和。 先大致介绍一下PID的控制流程。 首先,我们给出定义的连续的PID公式: 但在工程中,我们想要用处理器去计算,必须将其化为离散化模型。这样处理器才可以进行计算。 偏差 err(K)=rin(K)-rout(K); 积分环节用加和的形式表示 err(K)+err(K+1)+……; 微分环节用斜率的形式表示 [err(K)-err(K-1)]/T; 通过以上公式,可以得到PID离散表示形式: [1] 位置式PID 下面是位置式PID: [2] 增量式PID 下面是PID的增量式表示方式: 增量式的结果和近三次的偏差相关,这就很大地提高了系统的稳定性。 注意的是最终的输出结果应该为: u(K)+调节值; (三) 位置式PID——C语言 ① 定义PID变量结构体: struct _pid{ float SetSpeed; //定义设定值 float ActualSpeed; //定义实际值 float err; //定义偏差值 float err_last; //定义上一个偏差值 float Kp,Ki,Kd; //定义比例、积分、微分系数 float voltage; //定义电压值(控制执行器的变量) float integral; //定义积分值 }pid; ②初始化变量: void PID_init(){ pid.SetSpeed=0.0; pid.ActualSpeed=0.0; pid.err=0.0; pid.err_last=0.0; pid.voltage=0.0; pid.integral=0.0; pid.Kp=0.2; pid.Ki=0.015; pid.Kd=0.2; } Kp,Ki,Kd三个参数,调试过程当中,对于要求的控制效果,可以通过调节这三个量直接进行调节。 当然也可以写函数来直接修改比例系数的值,这很简单我就不写了。 ③控制算法: float PID_realize(float speed){ pid.SetSpeed=speed; pid.err=pid.SetSpeed-pid.ActualSpeed; pid.integral+=pid.err; pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); pid.err_last=pid.err; pid.ActualSpeed=pid.voltage*1.0; return pid.ActualSpeed; } 注:这是最基本的算法实现形式,稳定到设定值的速度慢,且没有考虑死区问题,还没有设定阈值,。这只是一种原理上算法的直接实现,并不能直接用于工程中。 (四) 增量型PID——C语言 ①定义并初始化PID struct _pid{ float SetSpeed; //定义设定值 float ActualSpeed; //定义实际值 float err; //定义偏差值 float err_next; //定义上一个偏差值 float err_last; //定义最上前的偏差值 float Kp,Ki,Kd; //定义比例、积分、微分系数 }pid; void PID_init(){ pid.SetSpeed=0.0; pid.ActualSpeed=0.0; pid.err=0.0; pid.err_last=0.0; pid.err_next=0.0; pid.Kp=0.15; pid.Ki=0.20; pid.Kd=0.25; } ②控制算法: float PID_realize(float speed){ pid.SetSpeed=speed; pid.err=pid.SetSpeed-pid.ActualSpeed; float incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last); pid.ActualSpeed+=incrementSpeed; pid.err_last=pid.err_next; pid.err_next=pid.err; return pid.ActualSpeed; } ③测试算法 int main(){ PID_init(); int count=0; while(count<1000) { float speed=PID_realize(150.0); printf("%fn",speed); count++; } return 0; } (五) 积分分离的PID控制算法——C语言 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度与稳定速度。 部分控制代码 if(abs(pid.err)>100) { index=0; }else{ index=1; pid.integral+=pid.err; } pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); 这种控制算法的速度很快 (六) 抗积分饱和的PID控制算法——C语言 积分饱和现象是指当系统是一个方向出现的偏差,使PID控制器的输出与积分作用不断积累更多,导致执行机构超出极限位置。 这时如果控制器输出U (k)继续增加,致动器的开放不可能增加,电脑输出控制量超出了正常操作范围,进入饱和区。当系统出现反向偏差时,u(k)逐渐退出饱和区。你进入饱和区越深,你离开的时间就越长。系统就会失控,导致控制性能的恶化。 防止积分饱和的方法之一——抗积分饱和法,该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围: 若u(k-1)>umax,则只累加负偏差; 若u(k-1) struct _pid{ float SetSpeed; //定义设定值 float ActualSpeed; //定义实际值 float err; //定义偏差值 float err_last; //定义上一个偏差值 float Kp,Ki,Kd; //定义比例、积分、微分系数 float voltage; //定义控制执行器的变量 float integral; //定义积分值 float umax; float umin; }pid; void PID_init(){ pid.SetSpeed=0.0; pid.ActualSpeed=0.0; pid.err=0.0; pid.err_last=0.0; pid.voltage=0.0; pid.integral=0.0; pid.Kp=0.2; pid.Ki=0.1; //这里加大了积分环节的值 pid.Kd=0.2; pid.umax=100; pid.umin=-100; } float PID_realize(float speed){ int index; pid.SetSpeed=speed; pid.err=pid.SetSpeed-pid.ActualSpeed; if(pid.ActualSpeed>pid.umax) { if(abs(pid.err)>100) { index=0; }else{ index=1; if(pid.err<0) { pid.integral+=pid.err; } } }else if(pid.ActualSpeed { index=0; }else{ index=1; if(pid.err>0) { pid.integral+=pid.err; } } }else{ if(abs(pid.err)>200) //积分分离过程 { index=0; }else{ index=1; pid.integral+=pid.err; } } pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); pid.err_last=pid.err; pid.ActualSpeed=pid.voltage*1.0; return pid.ActualSpeed; } (七) 变积分的PID控制算法——C语言 根据系统的偏差大小改变积分速度。 变积分PID的基本思想是设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢; 偏差越小,积分越快。 具体实现思路为: 给积分系数前加上一个比例值index: 当abs(err)<150时, index=1; 当100 最终的比例环节的比例系数值为ki*index; 控制函数为: float PID_realize(float speed){ float index; pid.SetSpeed=speed; pid.err=pid.SetSpeed-pid.ActualSpeed; //变积分过程 if(abs(pid.err)>150) { index=0.0; }else if(abs(pid.err)<100){ index=1.0; pid.integral+=pid.err; }else{ index=(150-abs(pid.err))/20; pid.integral+=pid.err; } pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last); pid.err_last=pid.err; pid.ActualSpeed=pid.voltage*1.0; return pid.ActualSpeed; } 这种控制方式会使系统的稳定速度非常快 这就是一些简单的PID实现的大体思路。在实际工程中需要结合实际再加调整,选择合适项目的PID算法。在有些时候,也许只用到PI或PD即可。 (八) 附言 附送一个参数整定口诀 参数整定找最佳, 从小到大顺序查。 先是比例后积分, 最后再把微分加。 曲线振荡很频繁, 比例度盘要放大。 曲线漂浮绕大弯, 比例度盘往小扳。 曲线偏离回复慢, 积分时间往下降。 曲线波动周期长, 积分时间再加长。 曲线振荡频率快, 先把微分降下来。 动差大来波动慢, 微分时间应加长。 理想曲线两个波, 前高后低四比一。 一看二调多分析, 调节质量不会低。 |
|
|
|
只有小组成员才能发言,加入小组>>
3294 浏览 9 评论
2970 浏览 16 评论
3473 浏览 1 评论
9023 浏览 16 评论
4061 浏览 18 评论
1140浏览 3评论
589浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
579浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2313浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1876浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-4 03:30 , Processed in 1.020351 second(s), Total 80, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号