PID算法理解和代码以及PID调参

写在前面

在一年前自己学习了一下PID算法,但是对具体的过程,计算式子,并没有做到很好的理解,于是自己总结并再次学习了一遍,以此记录,希望能和大家一起讨论
---------------------------------------------------------------------------2022.9.1修改------------------------------------------------------------------------------------------
评论区有朋友指出问题,重新审查一遍发现自己确实不对,将KI和Ti两个参数直接混淆,在调参方法部分直接贴上原图与原文链接

正文

1.PID算法的基本公式

在离散化的系统中,积分是误差的累计和,微分等于当前误差值与之前的误差值的差
故PID算法的公式如下:
在这里插入图片描述
基本概念得先理解
我们调整的参数是前面的Kp,Ki,Kd
而式子中的Kp×e(k),Ki×Σe(Kn),Kd*(e(k) - e(k-1))是增益,也就是P、I、D分别对应的输出
u(k)是PID算法整个的输出,也就是控制量

2.理解PID算法公式

  • PID的输出就类似于一个控制量,也就是你控制一个系统的控制量的变化,就是PID算法的输出。
    做一个形象的比喻:你需要控制燃气灶的温度,那么PID算法的输出就是火力的大小。你通过控制火力的大小来控制温度。
  • 在控制电机里面,速度环就是控制电流的大小,来控制电机的转速达到目标转速。PID的输出就是电流的值。
  • 位置环一般是用的双环控制,内环是速度环,外环是位置环。大概如下图:
    在这里插入图片描述
  • 首先位置先做一次PID计算,得到的输出是速度的值。再由速度做一次PID计算,得到的输出是电流的值,再把电流发给电调即可

3.对P、I、D三个参数的理解

P的控制作用

  • 根据公式,Kp×e(k),当我们将Kp趋于无穷大的时候,就类似一个开关的作用,用灶做比喻,当目标值大于实际值的时候,开关打开,开始加热,当目标值小于实际值的时候,火力断开,让他降温。但我们实际控制的火力有大小的。那么我们可以这么理解
    当差距不大的时候,Kp*e(k)较小,那么需要输出的火力较小
    当差距较大,并且温度低于目标温度的时候,那么需要输出的火力就会比较大
    当差距较小,并且温度高于目标温度的时候,那么就让输出的火力为负值(假设可以降低温度),降低温度。
  • 简而言之:P是为了建立目标值和当前值的一种线性关系,当你的P较大的时候,你会很快的到达目标值的附近,但是由于P过大,很容易就超过目标值,而超过目标值的时候,你的P很大,又会瞬间拉回来,但拉回来的程度会过大,也就会出现在目标值附近震荡,没办法收敛于目标值的情况。
    形象的比喻就是先粗略的调试一下

D的控制作用

  • 由上面的理论可以得到,在趋于目标值了之后,我们的P作用就不大了,并且越靠近目标值,P的作用越小,因为P太粗暴了,让整个系统都震荡了起来。

  • 而D的作用,根据公式。Kd*(e(k) - e(k-1)),我们可以当从0→目标值的时候,e(k)是在不断减小的,此时e(k) - e(k-1)是一个负值,再*Kd,我们可以得到,D的作用是为了给系统一个阻尼,并且系统变化的越快,这个阻尼也就越大,这就能抵消P那么粗暴的效果,让P温柔一点,在变化小的时候,D给小阻尼。在突然一个大变化的时候,阻尼就会很大, 这也就让系统不会变化过快,从而使得系统从震荡趋于稳定。

  • 网上看到的这句话我觉得是最形象的比喻了。
    在这里插入图片描述

I的控制作用

  • 当PD控制了之后,整个系统就已经可以趋于平衡了,但是如果是在有负载的情况下,尤其是电机要带动某个东西的情况下,很有可能会出现PID输出和消耗抵消了,从而使得系统没有办法达到目标值,例如你的温度已经到达95度,你想要去100度,但是散热的效果和你火力增加温度的效果抵消了,让你永远停在了95度,没有办法继续往上增加。
  • 这时候就需要I的作用,根据公式K1×Ki×Σe(Kn),K1可以暂时不管,K1就是为了实现在某些情况下加入I,某些情况下不加入I。因为正常情况下你的系统只会受到小的扰动,但是如果突然你的系统受到一个很大的变化,如果这时候还要积分,很有可能导致这个积出来的值对整个系统造成比较大的问题,甚至引起系统的崩溃。
  • 这个公式里面,就是把你之前所有的偏差进行累加,并把这个累加的值体现在输出上,这样,如果你的温度停在了95度,那么你的累加值也就会越来越大,最终你的火力也会往上走,实现温度的提升。
  • I其实是对P的一个补充作用,但我们不能让I不断的累加,必须有一个累加的最大值,因为累加过大,会让积分量太大,有可能出现一个跳变,难以控制。
  • 在实际的控制中,I参数其实是有一个让系统超前的作用,而超前的作用就是与D相反,D的增益起到了一个阻尼的作用,减慢你的变换,而I的增益起到了一个补助加火的作用,加速了你的变换。

4.补充一下电流环

在我们实际的控制电机的过程中,我们以DJI官网3508电机为例,其搭配有一个C620电调,在我们实际的控制中,我们用PID算法解算出来的是一个电流值,我们通过通信发送给电调,让电调控制电流的输出,但是在这个里面,其实还有一层电流环的控制,所以当我们发送了电流之后,其实电调内部还有一圈电流环,这也就导致了我们的输出其实没有那么好的实时性,是有一定延时的。

5.调参方法

  • (1)
    PID调试一般原则
    a.在输出不振荡时,增大比例系数P。
    b.在输出不振荡时,减小积分系数Ki。
    c.在输出不振荡时,增大微分系数Kd。
    (他们三个任何谁过大都会造成系统的震荡。)
  • (2) 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

以上图片来自 https://blog.csdn.net/wb790238030/article/details/92809538?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control

6.参考代码

//结构体部分
typedef struct
{
	uint8_t enable 					: 1;	//输出使能
	uint8_t enable_lim_sum_error	: 1;	//积分限幅使能
	uint8_t enable_lim_ouput		: 1;	//输出限幅使能
	uint8_t count					: 4;	
	
	double kp;  			//比例参数
	double ki;  			//积分参数
	double kd;  			//微分参数

	double lim_sum_error;	//误差积分限幅	
	double lim_output;		//输出限幅 防止输出过大

	double sum_error;		//误差积分
	double last_error;		//上一次的误差
	double last_last_error;	//上上一次的误差
	
	double kd_output;
	double ki_output;
	double kp_output;
	double error_dec;

	double pid_output;
	double error;
} PID_HANDLE, *P_PID_HANDLE;

//代码运算部分
double pidProcess(P_PID_HANDLE phdl, double input, double measure)
{
    double output = 0;
    double error = input - measure;         //计算误差

    phdl->sum_error += error;

    //误差积分限幅
    if(phdl->enable_lim_sum_error == 1 && fabs(phdl->sum_error) > phdl->lim_sum_error)
    {
        if(phdl->sum_error > 0)
            phdl->sum_error = phdl->lim_sum_error;
        else
            phdl->sum_error = -phdl->lim_sum_error;
    }

    output =        phdl->kp * error
                +   phdl->ki * phdl->sum_error
                +   phdl->kd * (error - phdl->last_error) ;
		phdl->kp_output = phdl->kp * error;
		phdl->ki_output = phdl->ki * phdl->sum_error;
		phdl->kd_output = phdl->kd * (error - phdl->last_error);

		phdl->error_dec = error - phdl->last_error;
		
    //输出限幅
    if(phdl->enable_lim_ouput == 1 && fabs(output) > phdl->lim_output)
    {
        if(output > 0)
            output = phdl->lim_output;
        else
            output = -phdl->lim_output;
    }

    //更新误差值
    phdl->last_error = error;

    //是否使能输出
    if(phdl->enable == 1)
	{
		phdl->pid_output = output;
		return output;
	}
    else
	{
		phdl->pid_output = 0;
		return 0;
	}
}

7.实际控制遇到的情况

结合实际控制电机说明几种情况
①堵转
在实际的控制电机的过程中,堵转是一种很常见的情况。下面结合PID算法总结一下。
当我们的实际值和我们的目标值之间保持了一个恒定差值,这个时候会出现什么情况呢?
根据计算式子,会发现D的增益Kd*(e(k) - e(k-1))为0,P的增益Kp×e(k)为一个恒定值,而I的值Ki×Σe(Kn)却是在不断累计的,这也就导致了我们发送给电调的值始终保持在我们限定的最大值输出,也就是lim_output这一个值,但是电机因为处于堵转状态,没有办法旋转,而电流在其中流动,能量守恒,这一部分电流就会以热量的形式散发出去,也就导致了电机的过热。
试想一下:如果不给output限定值,那么不断累计,最终发送的电流过大,会直接导致电调或者电机的烧毁。

  • 111
    点赞
  • 1053
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暮尘依旧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值