16位单片机C语言实现PID调速心得

2019-04-15 18:11发布

PID作为一种线性控制器在各领域得到广泛使用,具体原理就不细说了,本人其实也是一知半解就不班门弄斧了。 以下仅就这两天对于PID实现电机调速的一些心得跟大家分享一下。 PID主要的实现方式有两种 ,位置式和增量式,公式就不列了,其实核心思想个人认为就是时刻检测误差,再乘一定系数,再用此值修正。 位置式PID计算出的就是控制量u(k),电机控制中此值就是PWM的占空比。 增量式PID计算出的值是△u(k),还需要加上u(k-1)才是控制量 即 u(k)=△u(k)+u(k-1)。 用C语言实现单片机对电机的PID调速,需要注意采样周期,我定为10.25ms测试可用,不知这个时间有没有什么讲究。 使用位置式PID时,我创建的是float工程,最终无论如何也没有调试成功,难道是16为单片机(MC9S12EP100)对浮点运算有要求?后来换了增量式PID,采用整形的工程,先左移8位的计算完右移动的方式,避免系数太小除成 0,最终实现PID调速。 增量式整形工程的PID也有很多细节需要注意。最重要的就是注意最后计算完成右移语句的位置,必须是在完成所有计算之后再右移,否则当偏差不够大时还是会除成0,导致调不到目标转速,总差一点。 一开始我还认为是转速测的不准导致的,后来用平均转速却根本无法计算。这里再多提一句,平均转速对PID计算的精度貌似是没有用的,我这里用滚动计算的方式计算10个齿的平均转速。  if(CrankToothNum>=10)                                          //存十个齿前的齿号
    {
       i_Vary= CrankToothNum -10;
    }
  else
    {
       i_Vary= CrankToothNum+10;     
    }
    CrkAS_tiInsSpeedSum -=  CrkDrv_tiInsSpeed[i_Vary];
    CrkAS_tiInsSpeedSum += ctCrankCyc;                               //减一个加一个 去旧迎新 始终保持存着十个数
但是,这里注意这个平均转速实用在PID里其实△e就是误差e(某齿瞬时转速)-误差e(该齿前十个齿的瞬时转速),相比还不一定有瞬时转速精确。 后来发现是因为右移除的早了,猜测达不到目标转速就是因为刚开始误差够大,除完还不至于是0,等小到一定程度就不能再调了。 在提示一下,增量式PID这两句一定不能颠倒:  pid.gPreError=pid.gExError;  //上上一次偏差存储 用于计算  
 pid.gExError=Error;             //上一次偏差存储 用于计算
最后总结一个PID系数整定的个人理解方法,先把P和D都设为0,只用I,因为只有积分无论如何都是可以调到目标值的只是速度快慢,积分稳了之后再调P。D个人感觉不用也罢 。最主要的就是积分I的系数了。 以上,欢迎交流讨论。