1 前言" A; j0 l' _; g6 a' `' J 控制系统通常根据有没有反馈会分为开环系统和闭环系统,在闭环系统的控制中,PID算法非常强大,其三个部分分别为;
PID算法可以自动对控制系统进行准确且迅速的校正,因此被广泛地应用于工业控制系统。 2 开环控制首先来看开环控制系统,如下图所示,隆哥蒙着眼,需要走到虚线旗帜所表示的目标位置,由于缺少反馈(眼睛可以感知当前距离和位置,由于眼睛被蒙上没有反馈,所以这也是一个开环系统),最终隆哥会较大概率偏离预期的目标,可能会运行到途中实线旗帜所表示的位置。
开环系统的整体结构如下所示;
这里做一个不是很恰当的比喻;) o2 S g6 o* d ?. m" m4 a5 Y' y0 x
看来没有反馈的存在,很难准确到达目标位置。 3 闭环控制所以为了准确到达目标位置,这里就需要引入反馈,具体如下图所示;
在这里继续举个不怎么恰当的比喻;隆哥重获光明之后,基本可以看到目标位置了;
虽然在反馈系统下,隆哥最终到达目标位置,但是现在又来了新的任务,就是又快又准地到达目标位置。所以这里隆哥开始采用PID Controller,只要适当调整P,I和D的参数,就可以到达目标位置,具体如下图所示;
隆哥为了最短时间内到达目标位置,进行了不断的尝试,分别出现了以下几种情况;
经过不断的尝试,终于找到了最佳的方式,其过程大概如下图所示;
这里依然举一个不是很恰当的比喻;
P比例则是给定一个速度的大致范围,满足下面这个公式; 因此比例作用相当于某一时刻的偏差(err)与比例系数的乘积,具体如下所示;
I积分则是误差在一定时间内的和,满足以下公式;
如下图所示;
D微分则是误差变化曲线某处的导数,或者说是某一点的斜率,因此这里需要引入微分;
从图中可知,当偏差变化过快,微分环节会输出较大的负数,作为抑制输出继续上升,从而抑制过冲。 综上,,,,分别增加其中一项参数会对系统造成的影响总结如下表所示;[td]
上面扯了这么多,无非是为了初步理解PID在负反馈系统中的调节作用,下面开始推导一下算法实现的具体过程;PID控制器的系统框图如下所示;
是比例增益;是积分增益;是微分增益;4.3 离散化, p1 A! h$ _$ G; h$ O7 j! t 在数字系统中进行PID算法控制,需要对上述算法进行离散化;假设系统采样时间为则将输入序列化得到;
所以最终可以得到式①,也就是网上所说的位置式PID:
将式①再做一下简化;
最终得到增量式PID的离散公式如下:
4.4 伪算法 这里简单总结一下增量式PID实现的伪算法; previous_error := 0 //上一次偏差3 x7 C+ E. w+ ~# ^* X1 p) M integral := 0 //积分和 1 G8 S! h9 ~4 ~* I7 P //循环 //采样周期为dt loop: //setpoint 设定值 //measured_value 反馈值 error := setpoint − measured_value //计算得到偏差+ A; m3 ~. D2 W3 F4 ^$ p integral := integral + error × dt //计算得到积分累加和7 B/ k6 M' ? Y( I3 I derivative := (error − previous_error) / dt //计算得到微分 output := Kp × error + Ki × integral + Kd × derivative //计算得到PID输出' z* S; Q" H. S% ]6 {; v& Z previous_error := error //保存当前偏差为下一次采样时所需要的历史偏差 wait(dt) //等待下一次采用% |2 [5 l- p9 z7 \+ j& S, m goto loop' E5 Y4 T' e$ _$ g' s2 s 5 C++实现 这里是增量式PID算法的C语言实现; pid.cpp #ifndef _PID_SOURCE_#define _PID_SOURCE_ - t7 ~" f# a& ~2 B" D' u! P! ]- } #include #include #include "pid.h" using namespace std;6 T V2 W/ s/ r: t # O5 W9 ]2 f, E9 U3 Z* b class PIDImpl) h# b0 d, h) \( l {6 a2 X8 K+ B. T6 K6 _. E8 L public:& a1 E1 a7 T/ c8 {, N5 Y+ p' f6 P. Q( H PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki );! g. \, d, k+ i% @1 B5 m ~PIDImpl();6 ]$ M* S- ~( P1 ^" _$ x$ I double calculate( double setpoint, double pv );+ E+ N& C3 {8 B. o; M private: double _dt; double _max; double _min;: _2 U! X) n/ u double _Kp;& R& k- j0 C! k8 M7 o. P double _Kd; double _Ki; double _pre_error; double _integral;2 Z5 u7 e/ @! T3 N9 I2 \" @ }; : K- }8 R+ R2 B2 n8 E PID: ID( double dt, double max, double min, double Kp, double Kd, double Ki )# z: `- O8 C7 l: w- `5 T V{ pimpl = new PIDImpl(dt,max,min,Kp,Kd,Ki);2 [! Y; V& h; M& _ } double PID::calculate( double setpoint, double pv )3 K4 A; V9 ]2 m" R3 y" |& b3 h5 u {0 k) X, O N ?$ N1 Z return pimpl->calculate(setpoint,pv); }2 u& Z, R; y3 @9 i1 x/ `& a; w) k PID::~PID() 6 h0 |! l1 u1 F { delete pimpl;; w! e9 K2 C( k$ N/ U4 K! ?4 j } /**2 E" `" i3 p9 M) M+ |3 P* r3 T * Implementation */ PIDImpl: IDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ) :2 {% C3 l, }! ]1 j' H2 N_dt(dt),8 o% j4 s$ C% M# O2 j5 y _max(max)," U6 R: [. _3 ?0 h5 g0 | _min(min),8 r5 E% ~2 w5 b( p% n2 m1 ` _Kp(Kp), _Kd(Kd), _Ki(Ki), Y0 Z6 M+ s7 {2 r$ T+ V$ n; d _pre_error(0),+ z# r( N) g% L1 X _integral(0)3 k# [8 C, Q% P. G { }# Q5 S: L: _' G$ _ j$ K2 f 1 f6 _* y: m( A1 `9 u5 x! i( h" K double PIDImpl::calculate( double setpoint, double pv ) { ) h, ~$ S ~ R6 t // Calculate error0 D) f" W% J0 M& ]! b4 r( \ double error = setpoint - pv; 4 r) R L- O( K4 a, \& k // Proportional term6 n. s! U& m6 D% Y double Pout = _Kp * error; // Integral term _integral += error * _dt; double Iout = _Ki * _integral;) p8 H' I* n, } 8 \* c9 H8 N; T // Derivative term double derivative = (error - _pre_error) / _dt; double Dout = _Kd * derivative; // Calculate total output, g- E+ S( d* D double output = Pout + Iout + Dout;8 @6 Q* m7 d. r3 N : r; X, h; |$ {2 o; l& @ // Restrict to max/min if( output > _max ) output = _max;* ~; Y* k9 f% i else if( output < _min )" B+ C, D( a( b) c' K) } output = _min;3 c9 M( D+ E9 p) v1 M# B" O + g+ j% v6 S, `6 L8 B6 c% t // Save error to previous error9 N @% A$ O* i' u _pre_error = error;5 A! Y% g4 Q' J7 Q1 L, M return output; }5 f6 w2 U0 y4 [( C PIDImpl::~PIDImpl() {+ h# S7 m2 _2 `. C! M9 F! R } ( e' F; M% @1 Y! |4 O #endif pid.h #ifndef _PID_H_5 C1 s9 B/ k( z0 {#define _PID_H_ 8 d% e3 K/ X5 W- u& O- j class PIDImpl;% n/ i; X4 U& |- q6 b3 I class PID { public:: v. ]3 x( Y0 A- [& V2 \ // Kp - proportional gain h* O4 @& B' B4 |& Y2 g! q // Ki - Integral gain // Kd - derivative gain' o0 r; i. ~9 g; h$ x/ p // dt - loop interval time // max - maximum value of manipulated variable. S( O3 q a' E4 A: i* Z, B" K // min - minimum value of manipulated variable3 A- j, j- B+ B3 i7 O9 i PID( double dt, double max, double min, double Kp, double Kd, double Ki ); // Returns the manipulated variable given a setpoint and current process value double calculate( double setpoint, double pv ); ~PID(); ` @! R" Y" v3 S% O, u) ` private:+ V) P, C( Z( k+ D; Q PIDImpl *pimpl;* w5 O: v$ O5 V/ N }; #endif4 Y. I2 e! P, a& f A pid_example.cpp #include "pid.h"3 r. q1 ^4 T) v- s$ i! X0 C% d" X+ S#include 7 f7 S+ p* _5 X- s) f& \+ f2 Q - K) }7 K( `6 \ int main() {1 B, U1 C+ o- v( {% ^, i - z" {: ?( z( ?2 @1 {) G PID pid = PID(0.1, 100, -100, 0.1, 0.01, 0.5); 7 D) N) h# s+ O% c- w double val = 20;$ M9 q7 Q7 w0 L# { for (int i = 0; i < 100; i++) { double inc = pid.calculate(0, val); printf("val:% 7.3f inc:% 7.3f\n", val, inc);- w; Q; C: _9 m; S. Z: p7 j+ g7 Z( k' m$ c val += inc; } return 0; } 编译并测试; g++ -c pid.cpp -o pid.o# To compile example code:! h+ s3 w+ N- J g++ pid_example.cpp pid.o -o pid_example" W0 N# \1 E" @: {( V% V6 a6 s: \ 6 总结 本文总结了PID控制器算法在闭环系统中根据偏差变化的具体调节作用,每个环节可能对系统输出造成什么样的变化,给出了位置式和增量式离散PID算法的推导过程,并给出了位置式算法的C++程序实现。 , B) J' r: W' z |
感謝大神, 真是淺顯易懂 |
微信公众号
手机版