亚博智能论坛  
  
查看: 826|回复: 1

一起来学习PID 【转载】

  [复制链接]

该用户从未签到

0

主题

13

帖子

21

积分

新手上路

Rank: 1

积分
21
发表于 2017-6-23 15:55:00 | 显示全部楼层 |阅读模式
本帖最后由 nature 于 2017-6-23 15:57 编辑
2 ^8 x; L( Q, w6 I" r3 _  J
' p+ D" g4 V+ V$ J. \' [  j$ k这个是我从各网站看到然后总结的,并结合自己的经验解说的。后面会结合2013大学生电子设计竞赛国赛中C题倒立摆进行讲述(持续更新)
* k5 v5 b  m- P1 y一、总体原则! v7 w7 u. p7 Q: y" A, _
    PID调试一般原则 ! x# \9 O2 A: ?
    a.在输出不振荡时,增大比例增益P。 5 j1 P2 u9 F4 ]- P: j: L% C
    b.在输出不振荡时,减小积分时间常数Ti。 % F3 I" K9 |& Z7 w* k' R6 X# l
    c.在输出不振荡时,增大微分时间常数Td。
% Y# k. Q/ p) i- \6 W% S6 |5 L  c二、各环节作用" w0 i0 L- i" }
    [P]比例调节作用:是按比例反应系统的偏差,系统一旦出现了偏差,比例调节立即产生调节作用用以减少偏差。比例作用大,可以加快调节,减少误差,但是过大的比例,使系统的稳定性下降,甚至造成系统的不稳定。反之,过小,更不上系统需求。
# r  ]6 A* w6 N/ h# T    [I]积分调节作用:是使系统消除稳态误差,提高无差度。因为有误差,积分调节就进行,直至无差,积分调节停止,积分调节输出一常值。积分作用的强弱取决与积分时间常数Ti,Ti越小,积分作用就越强。反之Ti大则积分作用弱,加入积分调节可使系统稳定性下降,动态响应变慢。一般情况是将时间常数设置很小。积分作用常与另两种调节规律结合,组成PI调节器或PID调节器。
5 M' I6 h; m1 L1 O- |    [D]微分调节作用:微分作用反映系统偏差信号的变化率,具有预见性,能预见偏差变化的趋势,因此能产生超前的控制作用,在偏差还没有形成之前,已被微分调节作用消除。因此,可以改善系统的动态性能。在微分时间选择合适情况下,可以减少超调,减少调节时间。微分作用对噪声干扰有放大作用,因此过强 的加微分调节,对系统抗干扰不利。此外,微分反应的是变化率,而当输入没有变化时,微分作用输出为 零。微分作用不能单独使用,需要与另外两种调节规律相结合,组成PD或PID控制器。* a! M0 _, o3 h, H
三、细说[分为PID、PI、PD]
0 n; l( J/ e  ?" ]% ?9 L# k    1、确定比例增益P
! J4 \6 b  @& f# C$ U    确定比例增益P 时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0(具体见PID的参数设定说明),使PID为纯比例调节。输入设定为系统允许的最大值的60%~70%,由0逐渐加大比例增益P,直至系统出现振荡;再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的60%~70%。比例增益P调试完成。4 w; d" i3 t& Y5 |' o4 g6 N$ F
     【解说】我们知道P是调节与预设值(即输入值)的偏差的作用的,因此P很大时,当反馈值很小时也会造成很大的波动,最终是震荡状态,这个可以试出来的,这时的P是Pmax。反之P太小时,根本不可能靠近预设值,当刚好能靠近预设值时,这时的P就是Pmin。一般取P = Pmax(*60~70%)。
6 ~, p3 z- e2 ?( q    2、确定积分时间常数Ti+ ^$ ?) ], h6 G% a" H& G# R1 S
    比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录
: {( X7 W0 y* {此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。
6 E1 x  y; C! ~    【解说】
8 s7 ?" ]7 O. Z7 O    3、确定积分时间常数Td积分时间常数Td一般不用设定,为0即可。若要设定,与确定 P和Ti 的方法相同,取不振荡时的30%。
  ~2 b/ c3 Q* P4 x7 v& @9 I    【解说】; ^4 T' }/ K) h" \
    【PID算法演示小软件(绿色版)】
7 x8 T& c3 }: ]5 p7 x四、具体实践, ]3 J5 Q6 a% p* `+ W  S0 y7 k
  1. unsigned long lastTime;
    ! i" S9 W) X/ o8 U; a' J$ ?6 L) K
  2. double Input, Output, Setpoint;
    " k; S3 w: J: ^9 M$ a* i5 D8 _
  3. double etSum, lastErr;, Z; E0 d- ~( D9 N4 B- ?5 O
  4. double kp, ki, kd;9 `% k" r* {7 m# i
  5. //output = kp*et + ki*etSum + kd*det;
    4 c8 ~1 {' E) U' @
  6. void PidFunction()
    9 a# t' i8 l) U  b2 d' n+ D" o
  7. {
    : H! O! T8 f3 _6 W: v3 n+ D
  8. /*How long since we last calculated*/ - L. i1 u* G* Z/ `
  9. unsigned long now = millis(); //得到当前时间
    / i6 K5 H  ]  _( F/ }* e; Q6 X, C
  10. double timeChange = (double)(now - lastTime); //得到当前时间与上次时间之间的间隔/ P. {" I4 p% o! V& O
  11. /*Compute all the working error variables*/ //4 F$ w$ n; `3 T. @
  12. double et = Setpoint - Input; //反馈值与输入值的差值 e(t) = 比例: _* ?8 {) O& G0 W4 a
  13. etSum += (et* timeChange); //差值*时间间隔乘积累加 = 积分 理论是零
    ! W) W# n8 a0 {* ^; I9 H! A
  14. double dEt = (et - lastErr) / timeChange; //差值-上一次差值 = 微分
    ' i1 v/ k; u; i6 U$ U
  15. /*Compute PID Output*/ //
    ! [% f8 U1 m4 K$ q$ Z
  16. Output = kp * et + ki * etSum + kd * dEt; //输出 = 比例 + 积分 + 微分/ ?# A5 G3 E8 ]4 |# S
  17. /*Remember some variables for next time*/ 0 ^6 p8 X4 U) i" ~/ W) B! A
  18. lastErr = et; //下次循环: 当前比例成为过去比例; N4 {" w7 H: \4 A9 s
  19. lastTime = now; //下次循环: 当前时间成为过去时间
    0 `$ @2 u; L5 C; k
  20. }
    ( s/ b( b& S8 K$ x% t
  21. void ParaSet(double Kp, double Ki, double Kd) //设置比例、积分、微分的系数7 h3 f% w( F$ P, M6 y
  22. {
    ( Y2 E7 Q& ?+ x6 t; \: Z
  23. kp = Kp;
    8 W5 G3 L" s3 C- M! i
  24. ki = Ki; ; A# P$ Q' z# E& u8 c6 x7 s
  25. kd = Kd;( q* q8 t) s( k. k- Z" f
  26. }
复制代码
9 ~2 m* K3 I4 b/ P5 H, ]2 _
上面的程序代码是整个PID控制的模板。具体的看你的系统需求。下面我讲讲倒立摆(还不知道倒立摆是什么的可以去网上了解下)的PID控制:
0 F# g& V8 N1 ]- Y1 ], Y【单个传感器实现倒立摆】  S6 ^6 k/ D+ [5 d& N. d; l
   角度传感器(精密电位器):我们知道一般的精密电位器就可以当作传感器来使用,电位器三个脚,其中一端接GND,一端接VCC,那么中间端作为电压输出,通过AD采样既可以获取摆转过的角度位置。自然在摆的最高点就有一个角度值。为了获得较好的控制周期,我们采用定时器中断的方式,即在定时器中断中进行采样和数据处理,并进行电机控制(放心,STM32运行速度足够了)。这里取定时器三毫米中断一次,注意,AD采样的时间一定要小于定时器中断时间,否则会出现时序错乱,可以在AD初始化中设定采样周期,或者在中断函数里面进行适当的处理,比如分三次采样取平均值(这个需要设定更小的中断时间,如一毫秒中断一次,我就是这样的做的)。定时器初始化和一些变量的定义我就没贴代码了,具体的代码如下:
  1. void TIM3_IRQHandler(void). \& @2 P6 a! w9 Z- I+ K( [! N
  2. {
    9 W$ {3 w9 d* V! L7 Z' ]
  3. static int timercount;, u, A; w2 `* _0 S3 {7 j

  4. 2 Q% I2 C" V! N, O! N
  5. if(TIM3->SR&0X0001){ //溢出中断5 ~: t' g+ W. x( x2 W
  6. LED1 = ~LED1;
    ' _6 C5 }2 c" p" A+ B
  7. timercount++;
    ( E3 q/ r/ R6 C; O
  8. }
      C' t& @8 i; \% Q4 S4 z- h
  9. TIM3->SR &= ~(1<<0); //清除中断标志位6 E, k8 q) J% k4 e" L3 J% F

  10. * N; L( \, j! ?5 g3 B2 c$ W
  11. if(timercount == 1){
    3 |8 Y) H8 e( _" a! u. l
  12. ang[0] = Get_Adc(ADC_CH1);3 G# e+ Q" h' L+ H
  13. }! L" w. b8 }/ M$ ~: W
  14. if(timercount == 2){4 N9 d2 R+ a4 q2 C
  15. ang[1] = Get_Adc(ADC_CH1);5 N: W; V/ N( ]3 w4 D# L# M. O. F
  16. }$ b! L8 }$ C# @1 }" H0 ?( n% M6 |. h
  17. if(timercount == 3){6 Y& B6 E) r9 P7 v5 o- _8 o; {: T
  18. ////////////angle PID///////////
      P, r) N* a2 ?
  19. ang[2] = Get_Adc(ADC_CH1); //采集AD值 0 @5 ]$ X# G, l
  20. angleLast = angle; //保存上一次的采样角度值
    ; v! [' L3 T% P$ N0 v
  21. angle = (ang[0]+ang[1]+ang[2])/3; //取三次采集的平均值
    , B" [2 L, n/ r/ d( d3 \
  22. degreeLast = degree; //保存上一次的计算角度值, v& u: `4 G) \% j+ `0 Y
  23. degree = calcAngle(angle); //获取新的计算角度值6 i) x0 l: Y9 V# b
  24. # G: ^$ |' U8 H8 O3 i3 [! t* T* f
  25. velocity = (angle - angleLast); //得到采样角速度差值(微分值)1 y0 J+ L9 P( {
  26. if(abs(angle-setPoint)<=4) //输入值与反馈值差的绝对值 < 4) D" I, d% b% I# n3 G* E, D0 O
  27. error_p = 0; //p = 0
    3 {$ F1 p0 P. L1 R6 `
  28. else
    % k% l- `( d' a; Q% F: l
  29. error_p = setPoint-angle; //p = 输入值与反馈值差
    6 \8 p7 W2 h; `0 K
  30. error_d = velocity; //d = 微分值$ E& s2 ~4 R$ _3 t7 ?! H: ^8 C+ c7 v
  31. error_i = error_i + (error_p*timechange);, u$ j$ C, M- d( F- T1 L
  32. 5 a% ]- U  @2 R' V6 }7 a5 X& A
  33. myspeed = - (kp*error_p + ki*error_i + kd*error_d);
    7 P. y2 X1 ?% t

  34. ' z+ d3 `5 c$ I! t
  35. setMotorSpeed(myspeed); //电机PID控制4 l; [( U: o, _( ]5 Y
  36. 3 t, V" E) {' |  c
  37. //printf(\"[%d %d %d %7d]\t\",angle,degree,myspeed,error_i);( K" P: |( M' @, B2 k+ \0 y( n" ^, W  e
  38. }
    ' `" C2 z* q2 {. J2 ^
  39. if(timercount == 3){( x8 d- @( S$ d+ d' L0 D
  40. timercount = 0;
    ' A" V( u2 F0 B& ?, H
  41. error_i = isOverFlow(error_i);; X( @% Z- P; X1 F& r# I! L
  42. }5 @" ^# _6 Y$ i, s" z
  43. }
复制代码

/ C: v2 p* z. o& W' k1 t电机速度控制代码:电机速度控制代码:
  1. void setMotorSpeed(long mySpeed)
    2 v4 O3 G( |, `/ i
  2. {
    1 J% A2 T. D2 ?: e# s! g+ C4 G3 Y9 n3 C' q
  3.     long output = 0;) V2 a4 [/ B; h
  4.     mySpeed = constrain(mySpeed,-41665,41665);  //限制速度上下限& |* q9 T0 z% Z
  5.     if(mySpeed == 0)mySpeed = 1;                //保证速度不为零,也可以不用% z+ F5 X" B5 B& `- i, z3 J/ P( y
  6.     if(mySpeed>0){                               + C8 R" g3 h7 E% C3 r( S, E
  7.         output = mySpeed*1.0;                   //这个具体的看系统进行调整放大  
    % N8 T# z5 k: R% s1 |% v
  8.         //printf(\"R:%d\t\",output);
    0 J( }- |2 g- b0 W
  9.         TurnLeft(output);. C( T! e- m% V8 Y- }
  10.     }
    5 B' ?" X0 W- D# ]" a6 i. S
  11.     else{3 S/ n* I+ c- I0 P3 a
  12.         output = mySpeed*(-1.0);
    : V8 D$ z4 M% @7 H
  13.         //printf(\"L:%d\t\",output);
    ; R7 Z/ i3 i: f+ L8 v9 B
  14.         TurnRight(output);
    3 @7 V" K. `1 Q$ ^
  15.     }
    ) S# Y0 o; w1 Y2 s6 d  Q5 d2 C
  16. }
    4 i: d' C. @# M( x
复制代码
速度控制的话,我的速度值调节范围为0~5000,这个可以通过定时器来设定的。* X7 d+ p. O* I4 ?, _7 ]
+ O2 C9 I6 F& Z! |2 C# R
【多个传感器实现倒立摆】
$ L) T5 B  i) z. v PID算法演示软件.rar (194.03 KB, 下载次数: 2)

3 F9 J* }7 ]; J* A( E+ e' U! R
- ]( I, n; ?  r' t
  g6 b; f3 s' p: \' p7 z' }  c
$ v! {; F* }2 S
4 O2 [4 F& h( F/ Y, e

2 g% d, |3 j2 Z( x1 a% K
, C0 w* G+ n: q0 d. I3 ^5 e% b
0 w: D) B. v' U3 I+ i3 o

9 Z- T! q" c: T" e3 F

3 Y9 Z% j, z$ c& d+ j' N# K# o! @2 P) f5 ]9 L% d5 m5 _2 b5 ?

. J& t2 m9 F- b+ Q& k. G0 t; |$ u  {! h* B. v6 e$ ^$ l
2 c" j& V) O7 Y
* _% s* R! {& M! M2 u2 _8 y
回复

使用道具 举报

该用户从未签到

0

主题

4

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2017-10-29 20:50:15 | 显示全部楼层
good data for me
回复

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表