1.根据我控制算法类文章中关于PID的理论的一些描述,同时也根据网络上一些其他的PID文章,以及自己最近一个项目的实践后,总结了几套基于C语言的PID算法,由于网络中很少有人进行分享完整的PID算法实现,我这里分享下。
(1)头文件,定义pid的结构体,类的概念,包含pid的属性和方法
#ifndef __PID_H_ #define __PID_H_ #include <stdint.h> typedef struct _pid { int16_t set_value; // 给定值,rin(k) int16_t actual_value; // 实际值,反馈值,rout(k) int16_t err; // 偏差值,rin(k) - rout(k) int16_t err_last; // 上一次偏差值,rin(k - 1) - rout(k - 1) int16_t err_last_last; // 上一次上一次的偏差值,rin(k - 2) - rout(k - 2) float kp; // 比例系数 float ki; // 积分系数 float kd; // 微分系数 float uk; // pid公式运算结果值 float incremental_value; // 增量值 float integral_value; // 积分值 float umax; // uk的上限值,抗积分饱和用 float umin; // uk的下限值,抗积分饱和用 int16_t err_up_value; // 偏差上限值,积分分离用 int16_t ki_k; // 积分的再次乘机系数,积分分离用 float out_value; // float(*position_type)(struct _pid *ppid); // 位置型PID算法,无积分分离、无抗积分饱和 float(*incremental_type)(struct _pid *ppid); // 增量型PID算法 float(*integral_separation_type)(struct _pid *ppid); // 积分分离PID算法 float(*int_sep_anti_sat_type)(struct _pid *ppid); // 积分分离 + 抗积分饱和PID算法 }_pid_t; _pid_t *pid_create(void); extern _pid_t *pg_pid; #endif
(2).c文件,包含头文件中4个PID算法的实现,包含位置型PID算法、增量型PID算法、积分分离PID算法、积分分离+抗饱和PID算法
#include <stdlib.h> #include <string.h> #include "pid.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "portmacro.h" #include "semphr.h" #include "Debug.h" /************************************* * * Funciton Name : pid_position_type * Function :位置型PID算法,无积分分离、无抗积分饱和 * * @author :why * @note : 积分分离:能解决初期系统偏差值大,累加到积分项中,引起系统超调、振荡问题。积分是为了消除静态误差的,应在误差在小范围时引入积分 * : 抗积分饱和:能解决执行器到达极限值时,但uk还在增大,使得系统进入饱和,进入饱和区的时间越长,当系统出现反向偏差,解决这个反向偏差的时间就会越长,此时系统会像失控一样。 * :进入抗积分饱和,当uk超过上限时,积分向只累加负偏差,当uk超过下限时,积分项只累加正偏差。 * × @param :pid * * * @return : uk *************************************/ static float pid_position_type_f(struct _pid *ppid) { ppid->err = ppid->set_value - ppid->actual_value; // 偏差 ppid->integral_value += ppid->err; // 积分累加 ppid->uk = ppid->kp * ppid->err + ppid->ki * ppid->integral_value + ppid->kd * (ppid->err - ppid->err_last); ppid->err_last = ppid->err; return (ppid->uk); } /************************************* * * Funciton Name : pid_incremental_type_f * Function :增量型PID算法 * * @author :why * @note : 相较于位置型,因为与3个偏差相关,增强了系统稳定性 * × @param :pid * * * @return : uk + 增量值 *************************************/ static float pid_incremental_type_f(struct _pid *ppid) { ppid->err = ppid->set_value - ppid->actual_value; // 偏差 //ppid->integral_value += ppid->err; // 积分累加 ppid->incremental_value = ppid->kp * ( ppid->err - ppid->err_last) + ppid->ki * ppid->err + ppid->kd * (ppid->err - 2 * ppid->err_last + ppid->err_last_last); ppid->uk += ppid->incremental_value; ppid->err_last_last = ppid->err_last; ppid->err_last = ppid->err; return (ppid->uk); } /************************************* * * Funciton Name : pid_integral_separation_type_f * Function :积分分离PID算法 * * @author :why * @note : 能解决初期系统偏差值大,累加到积分项中,引起系统超调、振荡问题。积分是为了消除静态误差的,应在误差在小范围时引入积分 * × @param :pid * * * @return : uk *************************************/ static float pid_integral_separation_type_f(struct _pid *ppid) { ppid->err = ppid->set_value - ppid->actual_value; // 偏差 // 误差过大去除积分效果 if ( abs(ppid->err) > ppid->err_up_value ) { ppid->ki_k = 0; } else { ppid->ki_k = 1; ppid->integral_value += ppid->err; // 积分累加 } ppid->uk = ppid->kp * ppid->err + ppid->ki_k * ppid->ki * ppid->integral_value + ppid->kd * (ppid->err - ppid->err_last); ppid->err_last = ppid->err; return (ppid->uk); } /************************************* * * Funciton Name : pid_int_sep_anti_sat_type_f * Function :积分分离 + 抗积分饱和PID算法 * * @author :why * @note : 能解决初期系统偏差值大,累加到积分项中,引起系统超调、振荡问题。积分是为了消除静态误差的,应在误差在小范围时引入积分 * : 抗积分饱和:能解决执行器到达极限值时,但uk还在增大,使得系统进入饱和,进入饱和区的时间越长,当系统出现反向偏差,解决这个反向偏差的时间就会越长,此时系统会像失控一样。 * :进入抗积分饱和,当uk超过上限时,积分向只累加负偏差,当uk超过下限时,积分项只累加正偏差。 * × @param :pid * * * @return : uk *************************************/ static float pid_int_sep_anti_sat_type_f(struct _pid *ppid) { ppid->err = ppid->set_value - ppid->actual_value; // 偏差 Debug("ppid->err = %d\r\n", ppid->err); if ( ppid->out_value > ppid->umax ) // 抗积分饱和 { // 误差过大去除积分效果 if ( abs(ppid->err) > ppid->err_up_value ) // 积分分离 { ppid->ki_k = 0; } else { ppid->ki_k = 1; if ( ppid->err < 0 ) { ppid->integral_value += ppid->err; // 积分累加 } } } else if ( ppid->out_value < ppid->umin ) // 抗积分饱和 { // 误差过大去除积分效果 if ( abs(ppid->err) > ppid->err_up_value ) // 积分分离 { ppid->ki_k = 0; } else { ppid->ki_k = 1; if ( ppid->err > 0 ) { ppid->integral_value += ppid->err; // 积分累加 } } } else { // 误差过大去除积分效果 if ( abs(ppid->err) > ppid->err_up_value ) { ppid->ki_k = 0; } else { ppid->ki_k = 1; ppid->integral_value += ppid->err; // 积分累加 } } ppid->uk = ppid->kp * ppid->err + ppid->ki_k * ppid->ki * ppid->integral_value + ppid->kd * (ppid->err - ppid->err_last); ppid->err_last = ppid->err; // if ( ppid->uk >= 0.07 && ppid->uk <= 1 ) // { // ppid->uk = 1; // } // // if ( ppid->uk <= -0.07 && ppid->uk >= -1 ) // { // ppid->uk = -1; // } return (ppid->uk); } /************************************* * * Funciton Name : pid_init * Function : pid实例构造 * * @author :why * * *************************************/ static void pid_init(_pid_t *ppid) { ppid->kp = 0.2; ppid->ki = 0.05; ppid->kd = 0; ppid->umax = 4000; ppid->umin = 0; ppid->position_type = pid_position_type_f; ppid->incremental_type = pid_incremental_type_f; ppid->integral_separation_type = pid_integral_separation_type_f; ppid->int_sep_anti_sat_type = pid_int_sep_anti_sat_type_f; } /************************************* * * Funciton Name : pid_create * Function : pid实例创建 * @author : why * * @return : 返回pid实例 * *************************************/ _pid_t *pid_create(void) { _pid_t *ppid = (_pid_t *)pvPortMalloc(sizeof(_pid_t)); memset(ppid, 0, sizeof(_pid_t)); pid_init(ppid); return ppid; }
(3)PID的实现算法有了,但还是要根据实际情况进行调试选取最适合的PID算法以及修改可能存在的不恰当的位置。