为什么要多环
单环只能单一控制速度或者位置,如果想要同时控制多个量如速度,位置,角度,就需要多个PID
- 速度环一般PI控制,位置环一般PD
程序
主函数
/*定义内环变量*/
float InnerTarget, InnerActual, InnerOut;
float InnerKp = 值, InnerKi = 值, InnerKd = 值;
float InnerError0, InnerError1, InnerErrorInt;
/*定义外环变量*/
float OuterTarget, OuterActual, OuterOut;
float OuterKp = 值, OuterKi = 值, OuterKd = 值;
float OuterError0, OuterError1, OuterErrorInt;
int main(void)
{
Timer_Init();
while (1)
{
/*用户在此处根据需求写入外环PID控制器的目标值*/
OuterTarget = 用户指定的一个值;
}
}
内环
void TIM2_IRQHandler(void)
{
static uint64_t Count1, Count2;
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Count1 ++;
if (Count1 >= 内环调控时间)
{
Count1 = 0;
/*内环每隔时间T1,程序执行到这里一次*/
/*执行内环PID控制*/
InnerActual = 读取内环实际值();
InnerError1 = InnerError0;
InnerError0 = InnerTarget - InnerActual;
InnerErrorInt += InnerError0;
InnerOut = InnerKp * InnerError0
+ InnerKi * InnerErrorInt
+ InnerKd * (InnerError0 - InnerError1);
if (InnerOut > 上限) {InnerOut = 上限;}
if (InnerOut < 下限) {InnerOut = 下限;}
/*内环PID的输出值作用于执行器*/
输出至被控对象(InnerOut);
}
}
}
外环
外环调控周期要大于等于内环,具体周期给多少,要不断尝试
Count2 ++;
if (Count2 >= 外环调控时间)
{
Count2 = 0;
/*外环每隔时间T2,程序执行到这里一次*/
/*执行外环PID控制*/
OuterActual = 读取外环实际值();
OuterError1 = OuterError0;
OuterError0 = OuterTarget - OuterActual;
OuterErrorInt += OuterError0;
OuterOut = OuterKp * OuterError0
+ OuterKi * OuterErrorInt
+ OuterKd * (OuterError0 - OuterError1);
if (OuterOut > 上限) {OuterOut = 上限;}
if (OuterOut < 下限) {OuterOut = 下限;}
/*外环PID的输出值作用于内环PID的目标值*/
InnerTarget = OuterOut;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
双环PID调参
- 参数越大,响应越快,但会出现抖动;参数越小,响应越慢,但更加平滑,需要自己取舍
- 同时调节内环和外环是不可行的
- 因为内环可以独立工作,我们要先调内环(要把外环给注释调),内环调好了,在内环的基础上调节外环
内环
Kp调法
- 先让出现抖动,再减小使抖动消失
Ki调法
- 与Kp类似
- 先让出现抖动,再减小使抖动消失
外环
Kp
外环Kp不用怕超调,直接调到抖动再减小,用Kd减少超调
以一定速度到达指定位置
不能使innertarget=目标速度
正确做法是更改限幅,速度要小限幅就小
封装
typedef struct {
float Target;
float Actual;
float Out;
float Kp;
float Ki;
float Kd;
float Error0;
float Error1;
float ErrorInt;
float OutMax;
float OutMin;
} PID_t;
初始化赋值方法
PID_t Inner = {
.Kp = 0.3,
.Ki = 0.3,
.Kd = 0,
.OutMax = 100,
.OutMin = -100
};
```c
void PID_Update(PID_t *p)
{
p->Error1 = p->Error0;
p->Error0 = p->Target - p->Actual;
if (p->Ki != 0)
{
p->ErrorInt += p->Error0;
}
else
{
p->ErrorInt = 0;
}
p->Out = p->Kp * p->Error0
+ p->Ki * p->ErrorInt
+ p->Kd * (p->Error0 - p->Error1);
if (p->Out > p->OutMax) {p->Out = p->OutMax;}
if (p->Out < p->OutMin) {p->Out = p->OutMin;}
}