本系列旨在以我本身写的PID lib为例,讲一下PID的几点基本优化,PID的基本原理网上有不少资料,所以本系列将不会涉及PID的基本实现原理,在这里特别推荐Matlab tech talk的PID教程:https://ww2.mathworks.cn/videos/series/understanding-pid-control.html。html
因为笔者大一在读,尚未学习自动控制原理等课程,所以本系列将不会从自控原理角度展开,相反的,本系列将试图从“直觉”展开,经过直观的描述让你们从直觉上感觉并理解PID的一些包括微分先行、积分分离等基础的优化。算法
因为笔者水平有限,文中不免存在一些不足和错误之处,诚请各位批评指正。安全
咱们都知道PID只适用于线性非时变的控制场合,但执行器或者被控对象不多能够作到严格线性,在对控制效果要求较高的场合,咱们只能选取工做范围中近似线性的部分。又或者单纯的限制执行器的做用量,以此来保护系统的安全,如阀门的限位、电机的限速等。不管出于以上哪个目的,咱们都须要对PID的输出量进行限制,咱们通常称之为输出限幅。ide
但在某些状况下,输出限幅的存在或者执行器自己的限制致使了被控对象没法达到咱们的指望值,这就意味着偏差将会持续存在,持续存在的偏差会使积分项过度积累,从而致使咱们在下调指望值使偏差反向时,积分项须要一段时间来降低至最大输出一下,这段过程当中PID的输出将会持续保持最大,从而致使响应的严重滞后,咱们将这种状况称之为积分饱和(integral windup)。图像上能够看到,积分饱和致使的响应滞后和个人PID库与PID基本优化(三)结尾提到的状况很是类似:学习
咱们通常经过对积分限幅的方式实现积分抗饱和,为了解决积分饱和问题,咱们须要先分析是什么致使了积分的过分积累,是输出限幅致使的没法消除的偏差。所以一种思路即是在PID输出达到输出限幅时中止积分过程。考虑到过大的积分项会致使系统的超调和不稳定,所以咱们也能够设定一个合适的积分阈值,来经过这个阈值对积分进行限幅。这两种方式相互独立互不影响,所以咱们能够在积分抗饱和中同时采用以上两种策略。优化
通过积分抗饱和处理后,咱们能够看到系统响应的滞后已经被大幅改善:code
static void f_Integral_Limit(PID_TypeDef *pid) { float temp_Output, temp_Iout; temp_Iout = pid->Iout + pid->ITerm; temp_Output = pid->Pout + pid->Iout + pid->Dout; if (ABS(temp_Output) > pid->MaxOut) { if (pid->Err * pid->Iout > 0) { //在取消积分做用前,一样须要先判断当前周期内积分是否积累 //若是积分为减少趋势则不须要限制其减少 //缘由与(三)中相同。 pid->ITerm = 0; } } if (temp_Iout > pid->IntegralLimit) { pid->ITerm = 0; pid->Iout = pid->IntegralLimit; } if (temp_Iout < -pid->IntegralLimit) { pid->ITerm = 0; pid->Iout = -pid->IntegralLimit; } }
本系列关于PID基本优化策略不出意外的话就到此结束了,在系列结束的前最后我再扯一下个人电机堵转保护的实现,库中剩余部分再也不单独讲解了,所有代码已经发布在Github中,连接在个人PID库与PID基本优化(一)htm
电机堵转保护实现的关键和前提就在准确判断电机是否堵转,识别太严苛或者太宽松都会严重影响实际应用,所以咱们须要尽量的提升识别的准确性,而提升识别准确性的关键又在准确提取电机堵转发生的典型特征。对象
电机堵转发生时,电机转速会很是小或者直接为零,同时电机温度会上升,考虑到大多数电机内没有集成温度传感器,我并无选择应用温度上升这一特征。这样就只能从转速极小这一特征入手:blog
最简单的方式是判断转速是否小于一个阈值,可是用脚趾头想想就知道这个思路不可行,由于电机自己目标速度为零的时候就会被误判为堵转,这是咱们不但愿看到的。那既然上一个思路在目标速度为零的时候会发生误判,那咱们就能够将目标速度归入判断。
思路进行到这里,咱们不难想到,若是电机的实际转速与目标转速相差很远,这就是电机堵转的一个重要特征。但在电机起转时,电机实际转速与目标转速相差很远,这显然不是堵转。所以只有这样的状态持续一段时间,一段远比电机起转所需时间要长的时间,这样咱们咱们离精准判断只差最后一步,在PID输出自己就不大的时候,电机由于正常的负载致使没法旋转,这显然也没有必要看成堵转处理,所以咱们还须要将这种状况挑出来。不过这种状况不常出现,由于即便电机的目标转速很小,PID算法中的积分项也会使电机保持目标转速。
下面咱们直接上代码:
static void f_PID_ErrorHandle(PID_TypeDef *pid) { //排除PID输出自己很小的状况 if (pid->Output < pid->MaxOut * 0.01) return; //考虑到该判断策略的灵活性,0.9这个常数的选取是很灵活的 if ((ABS(pid->Target - pid->Measure) / pid->Target) > 0.9f) { //电机堵转计数 pid->ERRORHandler.ERRORCount++; } else { pid->ERRORHandler.ERRORCount = 0; } if (pid->ERRORHandler.ERRORCount > 1000) { //上述现象持续一段时间则被认定为电机堵转 pid->ERRORHandler.ERRORType = Motor_Blocked; } }