PID控制理论


前言

简单的认识pid和使用pid,至于如何优化P、I、D三个参数,提高鲁棒性,需要学习控制理论,工业应用目前95%使用。

https://www.bilibili.com/video/BV1B54y1V7hp?from=search&seid=227773174318710325&spm_id_from=333.337.0.0


概述

1.前导:

https://zhuanlan.zhihu.com/p/74131690
(2)理解P(比例环节)作用:基础比例环节
缺点: 产生稳态误差.
疑问: 何为稳态误差 为什么会产生稳态误差.
(3)理解I(积分环节)作用:消除稳态误差.
缺点: 增加超调
疑问: 积分为何能消除稳态误差?
(4) 理解D(微分环节)作用:加大惯性响应速度,减弱超调趋势
疑问: 为何能减弱超调

2.适用系统
二阶以内线性系统(齐次性、叠加性)

一阶系统举例:

二阶系统举例:

高阶系统简化为二阶系统,那么可以用pid
非线性系统通过李雅普诺夫变换线性化之后为线性系统,可以用pid

3.控制系统概述

前馈控制系统:

闭环控制系统:

双闭环:

复合控制系统(开环+闭环):
前馈-反馈复合控制系统

4.连续与离散信号

入门

1.pid公式解释(抽象派)

2.pid公式解释(形象派)

如上图,期望位置为100,设置KP为0.1,其他I、D为0,那么输出u=Kp*y
可得表格:(e:与目标误差,u:基于误差输出,L:最终输出)

次数euL
00
11001010
290919
3818.127.1
472.97.2934.39
565.616.56140.951
6

最终收敛,如下图所示:

接下来看一下无人机的例子,其产生了稳态误差,需要引入I来解决:

比例项在误差不变的情况下,其值也不变,所以当有稳态误差时,其不能靠比例项来抵消误差。
积分项会累加之前的误差,从而达到解决稳态误差的作用。如图所示:

当误差为0时,积分项的误差还是存在,只是停止积累误差了,如图所示:

由于这个积分项特性,会出现超调量。
出现超调那么引入微分项:

增量式PID:
以上是位置式PID公式,接下来讲解增量式PID公式
将k-1带入公式得:
在这里插入图片描述
由u=u(k)-u(k-1),得:
在这里插入图片描述
在这里插入图片描述
对比区别
• 增量式算法不需要对积分项累加,控制量增量只与近几次的误差有关,计算误差对控制量
计算的影响较小。而 位置式算法要对近几次的偏差的进行积分累加,容易产生较大的累加
误差;
• 增量式算法得出的是控制量的增量,例如在阀门控制中,只输出阀门开度的变化部分,误动作影响小,必要时还可通过逻辑判断限制或禁止本次输出,不会严重影响系统的工作;而
位置式的输出直接对应对象的输出,因此对系统影响较大;
• 增量式算法控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分
部件的对象,如步进电机等,而 位置式算法适用于执行机构不带积分部件的对象,如电液
伺服阀;
• 在进行 PID 控制时,位置式 PID 需要有积分限幅和输出限幅,而 增量式 PID 只需输出
限幅。
位置式 PID 优缺点:
优点:: 位置式 PID 是一种非递推式算法,可直接控制执行机构(如平衡小车),u(k) 的值和执行机构的实际位置(如小车当前角度)是一一对应的,因此在执行机构不带积分部件的对象中可以很好应用;
缺点:: 每次输出均与过去的状态有关,计算时要对 e(k) 进行累加,运算工作量大。
增量式 PID 优缺点:
优点::
1.误动作时影响小,必要时可用逻辑判断的方法去掉出错数据。
2. 手动/自动切换时冲击小,便于实现无扰动切换。
3. 算式中不需要累加。控制增量 ∆u(k) 的确定仅与最近 3 次的采样值有关。在速度闭环控制
中有很好的实时性。
缺点:
1.积分截断效应大,有稳态误差;
4. 溢出的影响大。有的被控对象用增量式则不太好;

其实两个式子表达的内容是一样的。

两pid的c语言实现:

位置式pid:

//结构体
typedef struct
{
    float target_val;           //目标值
    float actual_val;        		//实际值
    float err;             			//定义偏差值
    float err_last;          		//定义上一个偏差值
    float Kp,Ki,Kd;          		//定义比例、积分、微分系数
    float integral;          		//定义积分值
}_pid;
//操作:
#include "./pid/bsp_pid.h"
#include "math.h"
#include "./key/bsp_key.h" 
#include "./protocol/protocol.h"

//定义全局变量

_pid pid;

/**
  * @brief  PID参数初始化
	*	@note 	无
  * @retval 无
  */
void PID_param_init()
{
		/* 初始化参数 */
//    printf("PID_init begin \n");
    pid.target_val=200.0;				
    pid.actual_val=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.integral=0.0;
//		pid.Kp = 0.31;
//		pid.Ki = 0.070;
//		pid.Kd = 0.3;
//		pid.Kp = 0.21;
//		pid.Ki = 0.070;
//		pid.Kd = 0.3;
		pid.Kp = 0.01;//24
		pid.Ki = 0.80;
		pid.Kd = 0.04;

}

/**
  * @brief  设置目标值
  * @param  val		目标值
	*	@note 	无
  * @retval 无
  */
void set_pid_target(float temp_val)
{
  pid.target_val = temp_val;    // 设置当前的目标值
}

/**
  * @brief  获取目标值
  * @param  无
	*	@note 	无
  * @retval 目标值
  */
float get_pid_target(void)
{
  return pid.target_val;    // 设置当前的目标值
}

/**
  * @brief  设置比例、积分、微分系数
  * @param  p:比例系数 P
  * @param  i:积分系数 i
  * @param  d:微分系数 d
	*	@note 	无
  * @retval 无
  */
void set_p_i_d(float p, float i, float d)
{
  	pid.Kp = p;    // 设置比例系数 P
		pid.Ki = i;    // 设置积分系数 I
		pid.Kd = d;    // 设置微分系数 D
}

/**
  * @brief  PID算法实现
  * @param  val		实际值
  * @note 	无
  * @retval 通过PID计算后的输出
  */
float PID_realize(float temp_val)
{
	/*计算目标值与实际值的误差*/
    pid.err=pid.target_val-temp_val;
	/*误差累积*/
    pid.integral+=pid.err;
	/*PID算法实现*/
    pid.actual_val=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
	/*误差传递*/
    pid.err_last=pid.err;
	/*返回当前实际值*/
    return pid.actual_val;
}
/**
  * @brief  定时器周期调用函数
  * @param  无
	*@note 	无
  * @retval 无
  */
void time_period_fun()
{
	static int flag=0;
	static int num=0;
	static int run_i=0;
		
	if(!flag)
	{
		float val=PID_realize(pid.actual_val);
		printf("val,%f;act,%f\n",pid.target_val,val);	
		run_i++;
		if(abs(val-pid.target_val)<=1)
		{
			num++;
		}
		else//必须满足连续次数
		{
			num=0;
		}
		if(num>20)//稳定次数
		{
			printf("PID算法运行%d 次后稳定\r\n",run_i);
			flag=1;
		}
	}
}

其中time_period_fun被当作终端的回调函数被使用:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM_PIDHandle))
    {
        LED1_TOGGLE;  //红灯周期闪烁
				time_period_fun();
    }
}

增量pid:

typedef struct
{
  float target_val;     //目标值
	float actual_val;     //实际值
	float err;            //定义当前偏差值
	float err_next;       //定义下一个偏差值
	float err_last;       //定义最后一个偏差值
	float Kp, Ki, Kd;     //定义比例、积分、微分系数
}_pid;

看出其成员不一样,是根据其公式来定的。

进阶

1.pid参数整定

主要方法有试凑法、临界比例法、一般调节法。

2.其余相关控制知识
1.积分限幅
以上讲了算法的理论,但是实际应用中还需要注意很多事项,比如当我们按住飞行器,在PI系统中,I的量会随着时间累计,可能导致I变得巨大,无人机冲上天回不来,这时我们就要对积分限幅。
2.积分分离
改变期望高度,这时e会有一个阶跃,那么p也会阶跃,I在一个较短的时间内会快速变大,这时可能会导致巨大的超调量:这时我们可以定一个阈值,超过500的误差时,,我们直接让积分值=0。

3.微分先行:

之前是让期望高度和最终输出差值作为积分项的输入,现在直接让最终输出作为微分项的输入,这样当期望高度,也就是 输入突变的情况下,不会直接影响到D。

使用

1.前期分析
2.RM电机M3508速度环
3.RM电机6020角度双环

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个积分限幅的电压电流双闭环PID程序的示例代码: ``` // 定义PID结构体 typedef struct { float SetPoint; // 设定值 float Kp; // 比例系数 float Ki; // 积分系数 float Kd; // 微分系数 float lastError; // 上一次误差 float integral; // 积分误差 float integralLimit; // 积分限幅 } PID; // 定义电压和电流PID结构体 PID voltagePID = {0, 0.1, 0.01, 0.001, 0, 0, 10}; PID currentPID = {0, 0.5, 0.05, 0.005, 0, 0, 5}; // 输入电压和电流 float inputVoltage = 0; float inputCurrent = 0; // 输出PWM信号 float outputPWM = 0; // 采样时间 float sampleTime = 0.001; // 控制器计算函数 void calcPID(PID *pid, float input, float setpoint) { float error = setpoint - input; // 计算误差 pid->integral += error * sampleTime; // 累计积分误差 // 积分限幅 if (pid->integral > pid->integralLimit) { pid->integral = pid->integralLimit; } else if (pid->integral < -pid->integralLimit) { pid->integral = -pid->integralLimit; } float derivative = (error - pid->lastError) / sampleTime; // 计算微分误差 pid->lastError = error; // 保存上一次误差 float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative; // 计算PID输出 pid->SetPoint = output; // 更新PID设定值 } // 主函数 int main() { while (1) { // 读取电压和电流 inputVoltage = readVoltage(); inputCurrent = readCurrent(); // 计算电压和电流PID输出 calcPID(&voltagePID, inputVoltage, 220); // 设定电压为220V calcPID(&currentPID, inputCurrent, 5); // 设定电流为5A // 计算PWM输出 outputPWM = voltagePID.SetPoint + currentPID.SetPoint; // 输出PWM信号 writePWM(outputPWM); // 等待采样时间 delay(sampleTime); } } ``` 该程序在原有的电压电流双闭环PID程序的基础上,增加了积分限幅的功能。积分限幅的作用是当积分误差达到一定的值时,将积分误差限制在一定范围内,避免积分饱和导致系统不稳定。在计算PID输出时,首先判断积分误差是否超过了积分限幅,如果超过则将积分误差限制在限幅范围内。其中,积分限幅值可以根据具体应用进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值