最专业的企业营销型网站建设公司,泰安创意网络公司,网站建设问题分类和排除方法分析,微网页制作模板关于PID控制算法基础概念在本文不再重复了#xff0c;详情可参考#xff1a;PID控制算法基础介绍
本文主要从PID算法代码实现#xff0c;代码解析#xff0c;理论进阶#xff0c;PID调参等方向进行阐述。 目录位置式PID和增量式PID1.1 位置式PID1.2 增量式PID1.3 位置式和…关于PID控制算法基础概念在本文不再重复了详情可参考PID控制算法基础介绍
本文主要从PID算法代码实现代码解析理论进阶PID调参等方向进行阐述。 目录位置式PID和增量式PID1.1 位置式PID1.2 增量式PID1.3 位置式和增量式PID区别积分限幅位置式PID参数调试代码实现位置式PID和增量式PID
1.1 位置式PID
1基本的连续PID控制 公式 2离散PID控制 将连续的进行离散化。 展开简化一下就是 e(k): 用户设定的值目标值 - 控制对象的当前的状态值
比例P : e(k)
积分I : ∑e(i) 误差的累加
微分D : e(k) - e(k-1) 这次误差-上次误差
也就是位置式PID是当前系统的实际位置与你想要达到的预期位置的偏差进行PID控制
因为有误差积分 ∑e(i)一直累加也就是当前的输出u(k)与过去的所有状态都有关系用到了误差的累加值误差e会有误差累加输出的u(k)对应的是执行机构的实际位置一旦控制输出出错(控制对象的当前的状态值出现问题 )u(k)的大幅变化会引起系统的大幅变化
并且位置式PID在积分项达到饱和时,误差仍然会在积分作用下继续累积一旦误差开始反向变化系统需要一定时间从饱和区退出所以在u(k)达到最大和最小时要停止积分作用并且要有积分限幅和输出限幅
所以在使用位置式PID时一般我们直接使用PD控制
而位置式 PID 适用于执行机构不带积分部件的对象如舵机、平衡小车的直立、温控系统的控制
1.2 增量式PID
1公式推导过程 ak-1时刻的离散表示 b利用deltUk U(k) - U(K-1)得到本次的增量 比例P : e(k)-e(k-1) 本次误差-上次误差
积分I : e(k) 本次误差
微分D : e(k) - 2e(k-1)e(k-2) 本次误差-2*上次误差上上次误差
增量式PID根据公式可以很好地看出一旦确定了 KP、TI 、TD只要使用前后三次测量值的偏差 即可由公式求出控制增量
而得出的控制量Δu(k)对应的是近几次位置误差的增量而不是对应与实际位置的偏差。 没有误差累加
也就是说增量式PID中不需要累加。控制增量Δu(k)的确定仅与最近3次的采样值有关容易通过加权处理获得比较好的控制效果并且在系统发生问题时增量式不会严重影响系统的工作。
1.3 位置式和增量式PID区别
1、增量式算法不需要做累加控制量增量的确定仅与最近几次偏差采样值有关计算误差对控制 量计算的影响较小。而位置式算法要用到过去偏差的累加值容易产生较大的累加误差。
2、增量式算法得出的是控制量的增量例如在阀门控制中只输出阀门开度的变化部分误动作 影响小必要时还可通过逻辑判断限制或禁止本次输出不会严重影响系统的工作。 而位置式的输出直接对应对象的输出因此对系统影响较大。
3、增量式PID控制输出的是控制量增量并无积分作用因此该方法适用于执行机构带积分部件的对象如步进电机等而位置式PID适用于执行机构不带积分部件的对象如电液伺服阀。
4、在进行PID控制时位置式PID需要有积分限幅和输出限幅而增量式PID只需输出限幅
积分限幅
积分限幅实际比较多所以也提一下。 1针对问题 在开始控制后有一个较大误差且有一个非常大干扰的话例如外界阻力阻止其误差减小如旋翼飞机被人死死的按住。虽然他的PI都在输出但是误差一直没变。这时候随着时间的拉长积分的输出值会越来越大。 这将导致以下问题 a这种输出可能超过了控制系统承受范围 b而一旦这种阻力去掉会使得这种输出很大很大引起很大的超调和震荡。
2解决方法 引入积分限幅。原理给定一个积分输出的最大值限制一旦超出则不再增加。
位置式PID参数调试
PID调节的具体过程一般有理论计算整定法和工程整定法。对于复杂的、非线性的系统通常用工程整定法即根据工程经验试凑出合理的参数。PID参数的设置步骤一般如下 1首先选择一个合适的并且尽量短的采样时间让系统工作 2首先加入比例环节调节比例系数直到系统的输出出现临界振荡 3若单独的比例环节不能满足设计要求则此时加入积分环节调整好的比例系数缩小为原来的0.8然后调节积分时间参数使得系统能保持较小的稳态误差和较小的振荡时间此时可以同时调整比例系数和积分时间常数直到得到较为满意的结果 4如若还不是特别满意可以增加微分环节从小到大逐渐增加微分时间常数同时相应的更改比例系数和积分时间试凑出合适参数。
实际使用时PID可能不是都使用的有 1PID0 有自然的阻尼的情况。如水中弹簧震荡水有阻尼或者阻尼能够较好的平衡掉该中情况下PI的震荡。 2PDI0 没有外界阻力情况物理机械能够保证自平衡。 比如在水平面上小车控制运动到指定位置且这个小车电机等没有阻力到位置后不需要再输出就可以保持。
调整PID参数对系统的影响 PID参数调整口诀 参数整定找最佳从小到大顺序查 先是比例后积分最后再把微分加 曲线振荡很频繁比例度盘要放大 曲线漂浮绕大弯比例度盘往小扳 曲线偏离回复慢积分时间往下降 曲线波动周期长积分时间再加长 曲线振荡频率快先把微分降下来 动差大来波动慢微分时间应加长 理想曲线两个波前高后低四比一 一看二调多分析调节质量不会低
注比例度盘kp倒数 积分时间Ti倒数 微分时间Td
代码实现
typedef struct PID
{ float P,I,D,limit;
}PID;typedef struct Error
{float Current_Error;//当前误差float Last_Error;//上一次误差float Previous_Error;//上上次误差
}Error;/*! * brief 位置式PID* *sptr 误差参数* *pid: PID参数* NowPlace当前位置* Point 目标位置 */// 位置式PID控制
float PID_Realize(Error *sptr,PID *pid, int32 NowPlace, float Point)
{int32 iError, // 当前误差Realize; //实际输出 iError Point - NowPlace; // 计算当前误差sptr-Current_Error pid-I * iError; // 误差积分sptr-Current_Error sptr-Current_Error pid-limit?pid-limit:sptr-Current_Error;//积分限幅sptr-Current_Error sptr-Current_Error -pid-limit?-pid-limit:sptr-Current_Error;Realize pid-P * iError //比例P sptr-Current_Error //积分I pid-D * (iError - sptr-Last_Error); //微分Dsptr-Last_Error iError; // 更新上次误差return Realize; // 返回输出值
}/*! * brief 增量式PID* *sptr 误差参数* *pid: PID参数* NowPlace实际值* Point 期望值*/
// 增量式PID控制
int32 PID_Increase(Error *sptr, PID *pid, int32 NowPlace, int32 Point)
{int32 iError, //当前误差Increase; //最后得出的实际增量iError Point - NowPlace; // 计算当前误差Increase pid-P * (iError - sptr-Last_Error) //比例P pid-I * iError //积分I pid-D * (iError - 2 * sptr-Last_Error sptr-Previous_Error); //微分Dsptr-Previous_Error sptr-Last_Error; // 更新前次误差sptr-Last_Error iError; // 更新上次误差return Increase; // 返回增量
}实际使用时需要定时周期性采集数据进行PID计算需要根据场景决定合适的采样周期。
上面那套比较简单方便理解下面这套比较完善更实用都可参考
#define LimitMax(input, max) \{ \if (input max) \{ \input max; \} \else if (input -max) \{ \input -max; \} \}enum PID_MODE
{PID_POSITION 0,PID_DELTA
};typedef struct
{//PID运算模式uint8_t mode;//PID 三个基本参数float Kp;float Ki;float Kd;float max_out; //PID最大输出 (输出限幅)float max_iout; //PID最大积分输出 (积分限幅)float set; //PID目标值float fdb; //PID当前值float out; //三项叠加输出float Pout; //比例项输出float Iout; //积分项输出float Dout; //微分项输出//微分项最近三个值 0最新 1上一次 2上上次float Dbuf[3]; //误差项最近三个值 0最新 1上一次 2上上次float error[3];
} pid_type_def;void PID_init(pid_type_def *pid, uint8_t mode, const float PID[3], float max_out, float max_iout)
{if (pid NULL || PID NULL){return;}pid-mode mode;pid-Kp PID[0];pid-Ki PID[1];pid-Kd PID[2];pid-max_out max_out;pid-max_iout max_iout;pid-Dbuf[0] pid-Dbuf[1] pid-Dbuf[2] 0.0f;pid-error[0] pid-error[1] pid-error[2] pid-Pout pid-Iout pid-Dout pid-out 0.0f;
}void PID_clear(pid_type_def *pid)
{if (pid NULL){return;}//当前误差清零pid-error[0] pid-error[1] pid-error[2] 0.0f;//微分项清零pid-Dbuf[0] pid-Dbuf[1] pid-Dbuf[2] 0.0f;//输出清零pid-out pid-Pout pid-Iout pid-Dout 0.0f;//目标值和当前值清零pid-fdb pid-set 0.0f;
}float PID_calc(pid_type_def *pid, float ref, float set)
{//判断传入的PID指针不为空if (pid NULL){return 0.0f;}//存放过去两次计算的误差值pid-error[2] pid-error[1];pid-error[1] pid-error[0];//设定目标值和当前值到结构体成员pid-set set;pid-fdb ref;//计算最新的误差值pid-error[0] set - ref;//判断PID设置的模式if (pid-mode PID_POSITION)//位置式PID{//比例项计算输出pid-Pout pid-Kp * pid-error[0];//积分项计算输出pid-Iout pid-Ki * pid-error[0];//存放过去两次计算的微分误差值pid-Dbuf[2] pid-Dbuf[1];pid-Dbuf[1] pid-Dbuf[0];//当前误差的微分用本次误差减去上一次误差来计算pid-Dbuf[0] (pid-error[0] - pid-error[1]);//微分项输出pid-Dout pid-Kd * pid-Dbuf[0];//对积分项进行限幅LimitMax(pid-Iout, pid-max_iout);//叠加三个输出到总输出pid-out pid-Pout pid-Iout pid-Dout;//对总输出进行限幅LimitMax(pid-out, pid-max_out);}else if (pid-mode PID_DELTA)//增量式PID{//以本次误差与上次误差的差值作为比例项的输入带入计算pid-Pout pid-Kp * (pid-error[0] - pid-error[1]);//以本次误差作为积分项带入计算pid-Iout pid-Ki * pid-error[0];//迭代微分项的数组pid-Dbuf[2] pid-Dbuf[1];pid-Dbuf[1] pid-Dbuf[0];//以本次误差与上次误差的差值减去上次误差与上上次误差的差值作为微分项的输入带入计算pid-Dbuf[0] (pid-error[0] - 2.0f * pid-error[1] pid-error[2]);pid-Dout pid-Kd * pid-Dbuf[0];//叠加三个项的输出作为总输出 注意这里的符号是pid-out pid-Pout pid-Iout pid-Dout;//对总输出限幅LimitMax(pid-out, pid-max_out);}return pid-out;
}参考和推荐 https://blog.csdn.net/kevinshift/article/details/124753170
https://blog.csdn.net/as480133937/article/details/89508034
https://zhuanlan.zhihu.com/p/85020348?utm_sourceqq https://zhuanlan.zhihu.com/p/84767869
https://blog.csdn.net/kevinshift/article/details/124753170