位置PID的实现
这里有两点需要注意:
闭环死区的设定
闭环死区是指执行机构的Zui小控制量,无法再通过调节来满足控制精度,如果仍然持续调节,系统则会在目标值前后频繁动作,不能稳定下来。
比如某个系统的控制精度是1,但目标值需要是1.5,则无论怎么调节,Zui终的结果只能控制在 1或 2,始终无法达到预设值。这1.5L小数点后的范围,就是闭环死区,系统是无法控制的,误差会一直存在,容易发生震荡现象。
对应精度要求不高的系统,可以设定闭环死区,比如将允许的误差范围设为0.5,则Zui终结果在 1或 2都认为是没有误差,这时将目标值与实际值之差强制设为 0,认为没有误差,即限定了闭环死区。
积分分离的设定
通过积分分离的方式来实现抗积分饱和,积分饱和是指执行机构达到极限输出能力了,仍无法到达目标值,在很长一段时间内无法消除静差造成的。
例如,PWM输出到了,仍达不到期望位置,此时若一直进行误差累加,在一段时间后, PID的积分项累计了很大的数值,如果这时候到达了目标值或者重新设定了目标值,由于积分由于累计的误差很大,系统并不能立即调整到目标值,可能造成超调或失调的现象。
解决积分饱和的一种方法是使用积分分离,该方法是在累计误差小于某个阈值才使用积分项,累计误差过大则不再继续累计误差,相当于只使用了PD控制器。
控制流程图
带有闭环死区与积分分离的PID控制流程如下图:
完整的位置PID代码如下:
/*** @brief 位置PID算法实现
* @param actual_val:实际值
* @note 无
* @retval 通过PID计算后的输出
*/
#define LOC_DEAD_ZONE 60 /*位置环死区*/
#define LOC_INTEGRAL_START_ERR 200 /*积分分离时对应的误差范围*/
#define LOC_INTEGRAL_MAX_VAL 800 /*积分范围限定,防止积分饱和*/
float location_pid_realize(PID *pid, float actual_val)
{
/*计算目标值与实际值的误差*/
pid->err = pid->target_val - actual_val;
/* 设定闭环死区 */
if((pid->err >= -LOC_DEAD_ZONE) && (pid->err <= LOC_DEAD_ZONE))
{
pid->err = 0;
pid->integral = 0;
pid->err_last = 0;
}
/*积分项,积分分离,偏差较大时去掉积分作用*/
if(pid->err > -LOC_INTEGRAL_START_ERR && pid->err < LOC_INTEGRAL_START_ERR)
{
pid->integral += pid->err;
/*积分范围限定,防止积分饱和*/
if(pid->integral > LOC_INTEGRAL_MAX_VAL)
{
pid->integral = LOC_INTEGRAL_MAX_VAL;
}
elseif(pid->integral < -LOC_INTEGRAL_MAX_VAL)
{
pid->integral = -LOC_INTEGRAL_MAX_VAL;
}
}
/*PID算法实现*/
pid->output_val = pid->Kp * pid->err +
pid->Ki * pid->integral +
pid->Kd * (pid->err - pid->err_last);
/*误差传递*/
pid->err_last = pid->err;
/*返回当前实际值*/
return pid->output_val;
}
速度PID实现
速度PID的实现代码与位置PID的类似:
/*** @brief 速度PID算法实现
* @param actual_val:实际值
* @note 无
* @retval 通过PID计算后的输出
*/
#define SPE_DEAD_ZONE 5.0f /*速度环死区*/
#define SPE_INTEGRAL_START_ERR 100 /*积分分离时对应的误差范围*/
#define SPE_INTEGRAL_MAX_VAL 260 /*积分范围限定,防止积分饱和*/
float speed_pid_realize(PID *pid, float actual_val)
{
/*计算目标值与实际值的误差*/
pid->err = pid->target_val - actual_val;
/* 设定闭环死区 */
if( (pid->err>-SPE_DEAD_ZONE) && (pid->err<SPE_DEAD_ZONE ) )
{
pid->err = 0;
pid->integral = 0;
pid->err_last = 0;
}
/*积分项,积分分离,偏差较大时去掉积分作用*/
if(pid->err > -SPE_INTEGRAL_START_ERR && pid->err < SPE_INTEGRAL_START_ERR)
{
pid->integral += pid->err;
/*积分范围限定,防止积分饱和*/
if(pid->integral > SPE_INTEGRAL_MAX_VAL)
{
pid->integral = SPE_INTEGRAL_MAX_VAL;
}
elseif(pid->integral < -SPE_INTEGRAL_MAX_VAL)
{
pid->integral = -SPE_INTEGRAL_MAX_VAL;
}
}
/*PID算法实现*/
pid->output_val = pid->Kp * pid->err +
pid->Ki * pid->integral +
pid->Kd *(pid->err - pid->err_last);
/*误差传递*/
pid->err_last = pid->err;
/*返回当前实际值*/
return pid->output_val;