你好!这里是风筝的博客,
欢迎和我一起交流。
又是一年电赛时节。控制类必不可少的PID算法:
我也是从网上整理而来的:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
float SetValue; //用户期望值,也就是我们期望系统调节到输出此值然后稳定在这一状态
float pid_Calc_out; //PID计算输出结果值,用来驱动执行机构
float error; //当前偏差值
float error_last; //上一次计算的偏差值
float error_pre; //上上一次计算的偏差值,在增量式里用到
float Kp; //比例系数
float Ki; //积分系数
float Kd; //微分系数
float integral; //积分值
}PID;//结构体,方便管理
/*
初始化PID控制器的一些参数
入口参数是PID结构体指针
*/
void PID_Init(PID *pPID)
{
pPID->SetValue=0.0f; // 期望值设置为0
pPID->pid_Calc_out=0.0f;//PID控制器输出设置为0
pPID->error=0.0f;//当前偏差值设置为0
pPID->error_last=0.0f;//上次偏差设置为0
pPID->error_pre=0.0f;//上上次偏差设置为0
pPID->integral=0.0f;//积分值设置为0
pPID->Kp=0.08f;//比例系数设置为0.08
pPID->Ki=0.01f;//积分系数设置为0.01
pPID->Kd=0.2f;//微分系数设置为0.2
}
/*
位置式PID运算核心,输入参数pPID_Calc是类型为PID结构的结构体指针,Next_val是用户期望系统调节到输出此值然后稳定在这一状态的设定值,
返回值即为PID运算输出的结果。
*/
float PID_Calc(PID *pPID_Calc,float Next_val,float actual)//位置式
{
pPID_Calc->SetValue=Next_val;//把用户期望值赋给PID控制器的用户设定值
pPID_Calc->error=pPID_Calc->SetValue-actual;//期望值减去当前值,得到当前偏差
pPID_Calc->integral+=pPID_Calc->error;
//PID积分运算,此处没有作积分限幅处理,是为了完全与数学公式相同,运用到实际的系统需要做积分限幅处理,防止积分饱和。
//当系统存在一个方向的误差时,由于积分的累加作用会使控制量一直增大,可能会使控制量达到执行器的执行阈值,
//如果此时误差方向还是没有改变,控制量会继续增大但是执行器会一直保持在阈值,此时控制量就进入了饱和区。
//进入饱和区越深,退出饱和区时间就会越长,在饱和区时执行器会一直在阈值位置,如果误差发生反向,执行器不会立刻有反应,控制量会慢慢减小,
//等执行器推出饱和区才会有反应。这样就会使控制的动态响应变差,控制性能变差。
//所以必须防止积分饱和
pPID_Calc->pid_Calc_out=(pPID_Calc->Kp*pPID_Calc->error)/* 当前偏差*比例系数Kp */+
(pPID_Calc->Ki*pPID_Calc->integral)/* 积分*积分系数Ki */+
pPID_Calc->Kd*(pPID_Calc->error-pPID_Calc->error_last)/* (当前偏差-上次偏差)*微分系数 */;
//当前偏差*比例系数Kp + 积分*积分系数Ki + (当前偏差-上次偏差)*微分系数 结果就是PID控制器的输出
//pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
pPID_Calc->error_last=pPID_Calc->error;
//PID运算完一次后,当前偏差作为下一次偏差的上次偏差保存到pPID_Calc->error_last
return (pPID_Calc->pid_Calc_out*1.0f);//输出PID运算结果。
}
float PID_ADJ(PID *pPID_Calc,float Next_val)//增量式,只和最近三次偏差有关
{
float Out;
pPID_Calc->SetValue=Next_val;//把用户期望值赋给PID控制器的用户设定值
//得到增量
Out =( pPID_Calc->Kp*(pPID_Calc->error-pPID_Calc->error_last)) +
(pPID_Calc->Ki*pPID_Calc->error) +
pPID_Calc->Kd*(pPID_Calc->error-2*pPID_Calc->error_last+pPID_Calc->error_pre);
pPID_Calc->error_pre = pPID_Calc->error_last;
pPID_Calc->error_last=pPID_Calc->error;
//PID运算完一次后,当前偏差作为下一次偏差的上次偏差保存到pPID_Calc->error_last
pPID_Calc->pid_Calc_out+=Out;
return (pPID_Calc->pid_Calc_out);
}