作者:蒋小宁蒋小羊 | 来源:互联网 | 2023-10-13 08:20
关于PID各种整理例程基于stm32,位置式,增量式等1、增量式1.1增量式方式1structt_pid2{floatSetSpeed2;floatActua
关于PID各种整理例程基于stm32,位置式,增量式等
1、增量式 1.1 增量式 方式1 struct t_pid2 { float SetSpeed2; float ActualSpeed2; float err2; float err_next2; float err_last2; float Kp2, Ki2, Kd2; } pid2; void PID2_init ( ) { pid2. SetSpeed2= 0.0 ; pid2. ActualSpeed2= 0.0 ; pid2. err2= 0.0 ; pid2. err_next2= 0.0 ; pid2. err_last2= 0.0 ; pid2. Kp2= 0.2 ; pid2. Ki2= 0.015 ; pid2. Kd2= 0.2 ; } float PID2_realize ( float speed) { float incrementSpeed= 0 ; pid2. SetSpeed2= speed; pid2. err2= pid2. SetSpeed2- pid2. ActualSpeed2; incrementSpeed= pid2. Kp2* ( pid2. err2- pid2. err_next2) + pid2. Ki2* pid2. err2+ pid2. Kd2* ( pid2. err2- 2 * pid2. err_next2+ pid2. err_last2) ; pid2. ActualSpeed2+= incrementSpeed; pid2. err_last2= pid2. err_next2; pid2. err_next2= pid2. err2; return pid2. ActualSpeed2; }
1.1增量式 方式1 积分饱和 所谓的积分饱和 现象是指如果系统存在一个方向的偏差,PID 控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出 U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。
防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算 u(k)时,首先判断上一时刻的控制量 u(k-1)是否已经超出了极限范围: 如果u(k-1)>umax,则只累加负偏差; 如果 u(k-1) 如上图,很明显有三个过冲: 过程①:因为这个过程存在较大幅度变化的误差,因此积分器累积了较大的值,从图中可以看到,积分器的面积比较大(阴影部分)ek为正;
过程②:此时积分已经饱和,产生了较大的过冲,并且在较长的一段时间内,一直处于过冲的状态,ek为负;
过程③:积分脱离饱和状态,产生了积极的调节作用,消除静差,系统输出达到设定值 ek为正;
处理过程:
typedef struct { float setpoint; float proportiongain; float integralgain; float derivativegain; float lasterror; float preerror; float deadband; float result; float maximum; float minimum; } PID; void PIDRegulation ( PID * vPID, float processValue) { float thisError; float increment; float pError, dError, iError; thisError&#61; vPID-> setpoint- processValue; pError&#61; thisError- vPID-> lasterror; iError&#61; 0 ; dError&#61; thisError- 2 * ( vPID-> lasterror) &#43; vPID-> preerror; if ( vPID-> result> vPID-> maximum) { if ( thisError<&#61; 0 ) { iError&#61; thisError; } } elseif ( vPID-> result< vPID-> minimum) { if ( thisError>&#61; 0 ) { iError&#61; thisError; } } else { iError&#61; thisError; } increment&#61; vPID-> proportiongain* pError&#43; vPID-> integralgain* iError&#43; vPID-> derivativegain* dError; vPID-> preerror&#61; vPID-> lasterror; vPID-> lasterror&#61; thisError; vPID-> result&#43;&#61; increment; }
1.2 增量式 方式2 # define DEFAULT_SPEED - 5 # define DEFAULT_PROPORTION 0.11f # define DEFAULT_INTEGRAL 0.12f # define DEFAULT_DERIVATIVE 0.03f void Init_PIDStruct ( ) { vPID. SetPoint &#61; DEFAULT_SPEED; vPID. Proportion &#61; DEFAULT_PROPORTION; vPID. Integral &#61; DEFAULT_INTEGRAL; vPID. Derivative &#61; DEFAULT_DERIVATIVE; vPID. LastError &#61; 0 ; vPID. PrevError &#61; 0 ; vPID. SumError &#61; 0 ; } float IncPIDCalc ( __IO float NextPoint, __IO float TargetVal) { __IO float iError &#61; 0 , iIncpid &#61; 0 ; iError &#61; TargetVal - NextPoint; iIncpid&#61; ( vPID. Proportion * iError) - ( vPID. Integral * vPID. LastError) &#43; ( vPID. Derivative * vPID. PrevError) ; vPID. PrevError&#61; vPID. LastError; vPID. LastError &#61; iError; return ( iIncpid) ; }
2、位置式 2.1 位置式 方式1 struct t_pid { float SetSpeed; float ActualSpeed; float err; float err_last; float Kp, Ki, Kd; float voltage; float integral; } pid; void PID_init ( ) { pid. SetSpeed&#61; 0.0 ; pid. ActualSpeed&#61; 0.0 ; pid. err&#61; 0.0 ; pid. err_last&#61; 0.0 ; pid. voltage&#61; 0.0 ; pid. integral&#61; 0.0 ; pid. Kp&#61; 0.2 ; pid. Ki&#61; 0.015 ; pid. Kd&#61; 0.2 ; } float PID_realize ( float speed) { pid. SetSpeed&#61; speed; pid. err&#61; pid. SetSpeed- pid. ActualSpeed; pid. integral&#43;&#61; pid. err; pid. voltage&#61; pid. Kp* pid. err&#43; pid. Ki* pid. integral&#43; pid. Kd* ( pid. err- pid. err_last) ; pid. err_last&#61; pid. err; pid. ActualSpeed&#61; pid. voltage* 1.0 ; return pid. ActualSpeed; }
2.2 位置式 方式2–积分分离 积分分离的概念&#xff0c;其基本思路是 当被控量与设定值偏差较大时&#xff0c;取消积分作用; 当被控量接近给定值时&#xff0c;引入积分控制&#xff0c;以消除静差&#xff0c;提高精度。
struct t_pid3 { float SetSpeed3; float ActualSpeed3; float err3; float err_last3; float Kp3, Ki3, Kd3; float voltage3; float integral3; } pid3; void PID3_init ( ) { pid3. SetSpeed3&#61; 0.0 ; pid3. ActualSpeed3&#61; 0.0 ; pid3. err3&#61; 0.0 ; pid3. err_last3&#61; 0.0 ; pid3. voltage3&#61; 0.0 ; pid3. integral3&#61; 0.0 ; pid3. Kp3&#61; 0.2 ; pid3. Ki3&#61; 0.04 ; pid3. Kd3&#61; 0.2 ; } float PID3_realize ( float speed) { int index&#61; 0 ; pid3. SetSpeed3&#61; speed; pid3. err3&#61; pid3. SetSpeed3- pid3. ActualSpeed3; pid3. integral3&#43;&#61; pid3. err3; if ( abs ( ( int ) pid3. err3) > 200 ) { index&#61; 0 ; } else { index&#61; 1 ; pid3. integral3&#43;&#61; pid3. err3; } pid3. voltage3&#61; pid3. Kp3* pid3. err3&#43; index* pid3. Ki3* pid3. integral3&#43; pid3. Kd3* ( pid3. err3- pid3. err_last3) ; pid3. err_last3&#61; pid3. err3; pid3. ActualSpeed3&#61; pid3. voltage3* 1.0 ; return pid3. ActualSpeed3; }
2.3 位置式 方式3–积分饱和 struct t_pid4 { float SetSpeed4; float ActualSpeed4; float err4; float err_last4; float Kp4, Ki4, Kd4; float voltage4; float integral4; float umax4; float umin4; } pid4; void PID4_init ( ) { pid4. SetSpeed4&#61; 0.0 ; pid4. ActualSpeed4&#61; 0.0 ; pid4. err4&#61; 0.0 ; pid4. err_last4&#61; 0.0 ; pid4. voltage4&#61; 0.0 ; pid4. integral4&#61; 0.0 ; pid4. Kp4&#61; 0.2 ; pid4. Ki4&#61; 0.1 ; pid4. Kd4&#61; 0.2 ; pid4. umax4&#61; 400 ; pid4. umin4&#61; - 200 ; } float PID4_realize ( float speed) { int index; pid4. SetSpeed4&#61; speed; pid4. err4&#61; pid4. SetSpeed4- pid4. ActualSpeed4; if ( pid4. ActualSpeed4> pid4. umax4) { if ( abs ( pid4. err4) > 200 ) { index&#61; 0 ; } else { index&#61; 1 ; if ( pid4. err4< 0 ) { pid4. integral4&#43;&#61; pid4. err4; } } } else if ( pid4. ActualSpeed4< pid4. umin4) { if ( abs ( pid4. err4) > 200 ) { index&#61; 0 ; } else { index&#61; 1 ; if ( pid4. err4> 0 ) { pid4. integral4&#43;&#61; pid4. err4; } } } else { if ( abs ( pid4. err4) > 200 ) { index&#61; 0 ; } else { index&#61; 1 ; pid4. integral4&#43;&#61; pid4. err4; } } pid4. voltage4&#61; pid4. Kp4* pid4. err4&#43; index* pid4. Ki4* pid4. integral4&#43; pid4. Kd4* ( pid4. err4- pid4. err_last4) ; pid4. err_last4&#61; pid4. err4; pid4. ActualSpeed4&#61; pid4. voltage4* 1.0 ; return pid4. ActualSpeed4; }
2.4 位置式 方式4 struct t_pid5 { float SetSpeed5; float ActualSpeed5; float err5; float err_last5; float Kp5, Ki5, Kd5; float voltage5; float integral5; } pid5; void PID5_init ( ) { pid5. SetSpeed5&#61; 0.0 ; pid5. ActualSpeed5&#61; 0.0 ; pid5. err5&#61; 0.0 ; pid5. err_last5&#61; 0.0 ; pid5. voltage5&#61; 0.0 ; pid5. integral5&#61; 0.0 ; pid5. Kp5&#61; 0.4 ; pid5. Ki5&#61; 0.2 ; pid5. Kd5&#61; 0.2 ; } float PID5_realize ( float speed) { float index; pid5. SetSpeed5&#61; speed; pid5. err5&#61; pid5. SetSpeed5- pid5. ActualSpeed5; if ( abs ( pid5. err5) > 200 ) { index&#61; 0.0 ; } else if ( abs ( pid5. err5) < 180 ) { index&#61; 1.0 ; pid5. integral5&#43;&#61; pid5. err5; } else { index&#61; ( 200 - abs ( pid5. err5) ) / 20 ; pid5. integral5&#43;&#61; pid5. err5; } pid5. voltage5&#61; pid5. Kp5* pid5. err5&#43; index* pid5. Ki5* pid5. integral5&#43; pid5. Kd5* ( pid5. err5- pid5. err_last5) ; pid5. err_last5&#61; pid5. err5; pid5. ActualSpeed5&#61; pid5. voltage5* 1.0 ; return pid5. ActualSpeed5; }
总结&#xff1a;从上面公式可以发现&#xff0c;对照PID控制算法&#xff08;一&#xff09;&#xff0c;其实就分位置式与增量式至于实现过冲&#xff0c;其实是一样的&#xff0c;只不过合并同类项不同而已。