PID学习笔记
PID三个参数的含义
KP比例环节
产生震荡(主导作用)
首先要设置一个输出限幅【一定要加】:限制电机输出的最大值(输出的最大力)
KI 积分环节
消除稳态误差(恒定误差)积分环节-时间的累加
KD 微分环节
为了减小KP产生的剧烈震荡,微分环节求导
PID具体调节时候的先后顺序
先将所有参数置
将输出限幅设为最大值
增大p参数,使响应速度达到比较好的水平
逐渐增加d参数,在保证响应速度的前提下尽可能降低超调或震荡
逐渐增加i参数和积分限幅,使稳态误差消失
串级调参:需要先断开两环的连接(将外环注释掉),手动指定内环目标值,进行内环调参,当内环控制效果较好后再接上外环进行外环调参
具体调节的一些细节:
积分限幅一般比输出限幅小一点
如果最后小车不能停在目标位置需要加低通滤波
PID代码:
外环目标值:需要电机达到的角度
外环反馈值:电机的实时角度
内环反馈值:电机的实时速度
输出值:电机电流大小
typedef struct
{
float kp, ki, kd; //三个系数
float error, lastError; //误差、上次误差
float integral, maxIntegral; //积分、积分限幅
float output, maxOutput; //输出、输出限幅
}PID;
typedef struct
{
PID inner; //内环
PID outer; //外环
float output; //串级输出,等于inner.output
}CascadePID;
//用于初始化pid参数的函数
void PID_Init(PID *pid, float p, float i, float d, float maxI, float maxOut)
{
pid->kp = p;
pid->ki = i;
pid->kd = d;
pid->maxIntegral = maxI;
pid->maxOutput = maxOut;
}
//进行一次pid计算
//参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc(PID *pid, float reference, float feedback)
{
pid->lastError = pid->error;
pid->error = reference - feedback; //计算新error=目标值-反馈值
float dout = (pid->error - pid->lastError) * pid->kd;//微分
float pout = pid->error * pid->kp;//比例
pid->integral += pid->error * pid->ki;//积分
//积分限幅
if(pid->integral > pid->maxIntegral) pid->integral = pid->maxIntegral;
else if(pid->integral < -pid->maxIntegral) pid->integral = -pid->maxIntegral;
pid->output = pout+dout + pid->integral;//输出
//输出限幅
if(pid->output > pid->maxOutput) pid->output = pid->maxOutput;
else if(pid->output < -pid->maxOutput) pid->output = -pid->maxOutput;
}
//参数(PID结构体,外环目标值,外环反馈值即内环目标值,内环反馈值)
void PID_CascadeCalc(CascadePID *pid, float outerRef, float outerFdb, float innerFdb)
{
PID_Calc(&pid->outer, outerRef, outerFdb); //计算外环
PID_Calc(&pid->inner, pid->outer.output, innerFdb); //计算内环
pid->output = pid->inner.output; //内环输出就是串级PID的输出
}
CascadePID mypid = {0}; //创建串级PID结构体变量
int main()
{
PID_Init(&mypid.inner, 10, 0, 0, 0, 1000); //初始化内环参数
PID_Init(&mypid.outer, 5, 0, 5, 0, 100); //初始化外环参数
while(1) //进入循环运行
{
float outerTarget = ...; //获取外环目标值
float outerFeedback = ...; //获取外环反馈值
float innerFeedback = ...; //获取内环反馈值
PID_CascadeCalc(&mypid, outerTarget, outerFeedback, innerFeedback); //进行PID计算
设定执行机构输出大小(mypid.output);
delay(10); //延时一段时间
}
}