pid通俗解释
单级PID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| typedef struct { float kp, ki, kd; float error, lastError; float integral, maxIntegral; float output, maxOutput; }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; }
void PID_Calc(PID *pid, float reference, float feedback) { pid->lastError = pid->error; pid->error = reference - feedback; 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 mypid = {0}; int main() { PID_Init(&mypid, 10, 1, 5, 800, 1000); while(1) { float feedbackValue = ...; float targetValue = ...; PID_Calc(&mypid, targetValue, feedbackValue); 设定执行器输出大小(mypid.output); delay(10); } }
|

PID三个环节作用
- 比例:起主要控制作用,使反馈量向目标值靠拢,但可能导致振荡
- 积分:消除稳态误差,但会增加超调量
- 微分:产生阻尼效果,抑制振荡和超调,但会降低响应速度
例子
对电机转速进行控制
可用条件:已知电机的实时转速,并且可控制电机中流过的电流大小
PID目标值:需要电机达到的转速
PID反馈值:电机的实时转速
PID输出值:电机中流过的电流大小
分析:电机中流过的电流大小近似正比于电机的扭矩,也就近似正比于电机角加速度的大小,是转速的低阶物理量,因此可以用电流大小作为输出值
串级PID
单级PID目标值和反馈值经过一次PID计算就得到输出值并直接作为控制量,但如果目标物理量和输出物理量之间不止差了一阶的话,中间阶次的物理量我们是无法控制的。比如:目标物理量是位置,输出物理量是加速度,则小球的速度是无法控制的。
而串级PID就可以改善这一点。串级PID其实就是两个单级PID“串”在一起组成的,它的信号框图如下:

外环和内环就分别是一个单级PID,每个单级PID就如我们之前所说,需要获取一个目标值和一个反馈值,然后产生一个输出值。串级PID中两个环相“串”的方式就是将外环的输出作为内环的目标值。
案例
可用条件:小球实时位置、小球实时速度、施加在小球上的控制力
目标值:小球目标位置
外环反馈:小球实时位置
内环反馈:小球实时速度
输出值:施加在小球上的控制力

内环与小球构成了一个恒速系统,PID内环负责小球的速度控制;而如果把内环和小球看作一个整体被控对象,外环又与这个对象一起构成了一个位置控制系统,外环负责位置控制;总体来说,外环负责根据小球位置误差计算出小球需要达到的速度,而内环负责计算出控制力使小球达到这个目标速度,两个环协同工作,就可以完成任务了。
串级PID的内环一般负责低阶物理量(通常是变化更快、更基础的物理量)的调节,而外环负责高阶物理量(通常是变化较慢、更关注的最终目标量、高阶量的导数或相关量)的调节并计算出低阶物理量的目标值。
任务:对电机进行串级角度控制
可用条件:电机实时角度、电机实时转速、可以控制电机电流大小
外环目标值:需要电机达到的角度
外环反馈值:电机的实时角度
内环反馈值:电机的实时速度
输出值:电机电流大小
分析:外环负责电机角度控制,根据电机目标角度和反馈角度计算出目标转速;内环负责转速控制,根据速度反馈和目标转速计算出电流
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
typedef struct { PID inner; PID outer; float output; }CascadePID;
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; } CascadePID mypid = {0}; 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); 设定执行机构输出大小(mypid.output); delay(10); } }
|
调参
需要先断开两环的连接,手动指定内环目标值,进行内环调参,当内环控制效果较好后再接上外环进行外环调参,具体的调参方法与单级PID相同。