你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

(转载)增量式PID的stm32实现,整定过程

[复制链接]
nyszx 发布时间:2017-12-26 15:40
本帖最后由 nyszx 于 2017-12-26 19:31 编辑
- p. w% t% `7 h; [3 D/ n/ d; r9 r8 Y% @8 K6 `3 i9 T
原帖:【分享】增量式PID的stm32实现,整定过程
4 j8 X4 Q+ V* e, X: R) z0 E原文网址:http://www.amobbs.com/thread-5575823-1-1.html
, A& A. ^8 f3 Q- E6 ~1 g出处:阿莫电子论坛 8 n. p5 u/ F) Z
作者:tim4146
; j: t' F6 U7 h# M1 z4 P- W感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。' P+ G  I( f3 s! K
也下面我分享一下近期的这些工作吧。欢迎大家批评指点~
  W  A# M% f/ {, o6 Z% |/ h; {8 s" I7 s1 i) Q5 I
首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。
& L1 `- \1 X" f! v) e$ j" g资料上比较多的是:" `( a3 H2 I# i; ?4 V; r9 r
1.jpg
7 p! P& D8 _$ L* ?" B0 u5 b) z$ ]* D还有一种是:. q  ~- h0 D/ v; i+ G
2.jpg
# ^# ^& f& M, R! Q感觉第二种的Kp Ki Kd比较清楚,更好理解,下面介绍的就以第二种来吧。(比例、积分、微分三个环节的作用这里就详细展开,百度会有很多)
& Q" A, q' S! ~! S* \  X( U% j( o: K& U/ _0 b% U9 g: ]6 h* @
硬件部分:, W" |9 t2 ?; C2 r' u" K
控制系统的控制对象是4个空心杯直流电机,电机带光电编码器,可以反馈转速大小的波形。电机驱动模块是普通的L298N模块。1 Q% W5 f3 f8 ^/ j$ d! ?6 U0 s
芯片型号,STM32F103ZET6: V3 c) t% Q/ S* T
" h6 r9 d- f1 x$ _
软件部分:$ n0 F2 e" r/ L0 R9 I
PWM输出:TIM3,可以直接输出4路不通占空比的PWM波
- F8 R- v  [4 P* w: S$ [  H, vPWM捕获:STM32除了TIM6 TIM7其余的都有捕获功能,使用TIM1 TIM2 TIM4 TIM5四个定时器捕获四个反馈信号: s: m1 v- B6 ^. K6 W: ^* X
PID的采样和处理:使用了基本定时器TIM6,溢出时间就是我的采样周期,理论上T越小效果会越好,这里我取20ms,依据控制对象吧,如果控制水温什么的采样周期会是几秒几分钟什么的。( U& [5 ?; D8 Q4 d# @9 L# D  n

; }3 Y) q* _( g5 J# ?0 ^* I9 l上面的PWM输出和捕获关于定时器的设置都有例程,我这里是这样的:# M* ?: D7 [% ^2 n1 S# o
TIM3输出四路PWM,在引脚 C 的 GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9输出
5 p. B0 `3 A: S/ [- K4 f四路捕获分别是TIM4  TIM1  TIM2  TIM5   ,对应引脚是:  PB7 PE11 PB3 PA1

) T, O/ \3 U7 G% O高级定时器tim1的初始化略不同,它的中断”名称“和通用定时器不同,见代码:
, d: i$ t) ^/ B% o/ L& v0 U
  1. /*功能名称IM3_PWM_Init(u16 arr,u16 psc)
    0 Y5 C0 o* X) q0 L. K  M, U( e
  2.         描述      TIM3产生四路PWM- k2 i- y6 Z1 `1 {: o
  3. */' V) L4 C2 a$ @2 A1 d# g
  4. void TIM3_PWM_Init(u16 arr,u16 psc)
    . Y9 G' H+ ]* c+ W' i7 J
  5. {
    & z0 Q( _2 N4 w5 w
  6.         GPIO_InitTypeDef GPIO_InitStructure;8 N4 k% G; G6 R3 H/ _! |2 o& ?' e
  7.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;( o0 @9 W9 X6 b8 f0 Q4 G
  8.         TIM_OCInitTypeDef  TIM_OCInitStructure;! d1 @1 b/ |& y) t4 F+ k  q
  9. 1 M4 u) E" j, q; U5 ~" @$ _

  10. 8 J9 H1 R8 a9 A( ]  M# H8 I6 Z
  11.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    - d+ k6 t& r/ X
  12.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能
    - V, W. z3 X$ g' a0 f
  13. 2 ~. {- S: i; _# c6 }, }- m
  14.   GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示
    & F! J, t( [3 v* n( y& k
  15. 9 A$ i( D! w/ x% E1 p9 u: r
  16.    //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形
    " A" g$ e8 u6 g
  17.         GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO
    ! V* k' j0 [1 n6 `
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    : z' z+ \- g" b/ _' ~4 w" t" G9 |
  19.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    3 U9 Y+ S( U# ^8 U7 G* R: o
  20.         GPIO_Init(GPIOC, &GPIO_InitStructure);, @2 z% p  v& `* ^
  21.         GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能6 Y& j: c' L5 v# D
  22. 7 ^5 q- o6 D' U% q& C3 d
  23.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    & n& T1 ?  \0 S% U! b: ^
  24.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M: a% S7 `9 y/ W1 ?$ P& h! a
  25.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    % i7 n# N2 Q8 o8 M7 ^
  26.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式) c, I+ ]* t6 x
  27.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    - c$ ?9 S% E) e$ H
  28. ! T. r( e1 c9 ^1 Z( z9 \2 u" S8 g
  29. 4 P) v5 K& O& e. _( g
  30.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式13 T, S) H, X* F/ E2 F
  31.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能+ r/ t0 ^- e0 `
  32.         TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值6 x: V% b8 ~  I% C( Y
  33.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高# W- U5 ~8 ^, y2 Y  `+ m" T' M3 ^
  34. 0 e; l/ v- f7 v+ f1 f, D8 G
  35.         TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    * A# h- ^2 F2 b+ b( B( f' G
  36.         TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR1上的预装载寄存器
    3 V! k# i  Q& I0 F

  37. - [" v; }" j  w

  38. * ?$ K! m# r+ D
  39.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx' o. J- e" _% i. u/ ~/ b
  40.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
    4 f4 e9 n( |; m9 ]+ r8 M2 B
  41. % N  j8 L7 @8 K, ~0 F' }
  42.         TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    - J9 C; m+ X* g+ v/ d6 A" h
  43.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR3上的预装载寄存器+ ^( W- B0 z7 K  t, h: _* e) G

  44. % n- Q, M7 L! t$ r/ M
  45.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    4 b) m7 {2 Q$ ~  c/ e
  46.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR4上的预装载寄存器
    : o  R+ i. ~$ J5 g0 f

  47. 6 f4 c' Y6 u& P. u- M  j' O4 a! j! @
  48.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
    4 H! I  R) X: t/ F( G2 z0 ?2 T( [5 ?6 C; r

  49. 9 Q7 h3 n# U9 t; @4 R" ]
  50. ) }" b% d% ^. Q+ Q! a
  51.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设) M3 o/ V2 x2 x3 [
  52. & [9 o& n$ J+ e/ Z- U

  53. 8 z; i0 T. M% C
  54. }; z3 y  E# s$ y3 i8 M* a) p

  55. * D7 @% T. Q) U# {* ~

  56. / l& k. B1 {, _0 ]) N
  57. ) q# b- s' w; d( C7 F5 e
  58. /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
    + E7 |1 ^% A3 Z# [
  59.   描述      PWM输入初始化*/+ z+ ~- A1 Q" D- X5 M: G" V* s5 u

  60. / r' ^  `2 _* \* S
  61. void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)/ ^' D  j5 M6 S8 }9 n6 A( v" z
  62. {$ C- F6 z6 t+ n( W" I8 h
  63. ( F; J! p1 v5 _% u3 R
  64.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体# f) Q/ r% n- v
  65.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    3 {! M$ |/ J: a9 Q9 Y( d( ^# L
  66.         TIM_ICInitTypeDef  TIM4_ICInitStructure;                 //TIM4  PWM配置结构体' Y& [% c3 O9 N7 I+ ]/ K
  67.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    1 [1 ~' N  j% v: `9 F! i0 F6 O$ x
  68. 3 Z, N+ M' R9 s$ M6 e
  69.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);     //Open TIM4 clock
    / I6 q0 f0 S8 |% E
  70.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock; m5 z# c0 y* `8 K
  71. " R; M" s6 C5 w7 e# y* A3 M8 ?
  72.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //GPIO 7
    : c' m! N2 o/ y+ m  |
  73.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入# E0 n) ~1 d4 s& {" i
  74.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    $ j! V" r$ u0 n7 X/ ]" e
  75.   GPIO_Init(GPIOB, &GPIO_InitStructure);0 h) Q4 V4 k* u5 {( Z

  76. ) e) R0 P9 A& O
  77.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值0 n8 \3 h5 a) z) ]6 c5 P3 O
  78.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值, K$ y0 f" H! h& ?/ g6 K
  79.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim" s6 F" Z/ k' ~5 Z6 Q* j
  80.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    / R9 j7 Z4 C/ a
  81.         TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    ; I( R( a) M' M  k9 w' d2 J$ I: q
  82. / C- O1 v, z+ |0 }! g$ k
  83. . T  P: Q7 F/ Y, u; c- d9 ?* P
  84.         /*配置中断优先级*/* a/ i9 o+ N( t& U  ]
  85.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;# V- z- G2 S$ C# u6 u2 t
  86.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;1 l9 `/ U" L4 L# ]5 M- O
  87.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;1 c' w0 S( @5 U/ R( e4 z3 m4 \
  88.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;" [' w0 p: b7 b7 L. H8 k& m( Z
  89.   NVIC_Init(&NVIC_InitStructure);
      r2 a" `0 ~# y& n; P, i

  90. 3 w& ~$ h$ n7 ^6 d! j% N- x+ k
  91.   TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;& Z9 ?% m4 G& G1 t$ ?1 S, y
  92.   TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;2 E3 L: F; j: T7 `
  93.   TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    , o5 X: h2 K( l. ]; G  U
  94.   TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;2 Z. h7 j4 U# E9 f: r
  95.   TIM4_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤
    " y1 a2 C, `$ U- s0 K- p

  96. % x; N1 U  m+ {) j
  97.   TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);     //PWM输入配置5 x4 K, Q+ V9 t' T; ~6 L' D
  98.   TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);     //选择有效输入端
    4 N) z* n3 ~% K5 H  t) ?
  99.   TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);  //配置为主从复位模式
    * {# p% R7 O" Y8 v
  100.   TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发# J" Q; Y4 r3 u$ l. x! X2 x
  101.   TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
    2 R. X$ S* m/ Y
  102.   TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位) p: d3 r$ ~) {$ M1 \/ n
  103.   TIM_Cmd(TIM4, ENABLE);/ Q; ~( f9 ]& ^) L$ p8 k
  104. }9 k) {9 u8 F3 _, g' Q
  105. / q+ u9 w' |: l
  106. 5 ^' U; w( O) \) w  }8 [, j7 @
  107. void TIM4_IRQHandler(void)
    * W$ w) l& A! v1 Q; t7 _
  108. {
    9 g) p' v) ^) B9 d" z

  109. 6 ^" v/ m& ]. z1 _! w
  110.                 if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    ) k. M/ m$ \. z9 A1 r" E6 Z/ F
  111.                         {
    & _& p  ?# o; e  g
  112.                                 duty_TIM4    =   TIM_GetCapture1(TIM4);          //采集占空比
    $ \. I) V  h: u! p* }. i5 i
  113.                if  (TIM_GetCapture2(TIM4)>600)         period_TIM4        =        TIM_GetCapture2(TIM4);//简单的处理  h9 y/ o. t" I) W  T
  114.                                 CollectFlag_TIM4 = 0;
    1 V* p0 g- e5 f6 W2 z
  115.         }
    & i) L* P2 s" M! J
  116.                 TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位1 `+ m9 m0 h% h( Q' f; c% F3 d5 T
  117. }0 G+ B# W* Z8 Y# x! \; h. B7 ~

  118. 0 I2 @0 k8 w" l8 S3 Z; k
  119. 9 R+ G8 e2 E( P
  120. /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
    1 l" |. U6 s4 u- b
  121.   描述      PWM输入初始化*/" \4 c% f: d/ M* V
  122. 4 X+ Z. w2 S. k4 F  t3 t2 o
  123. void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
    ; A: k7 o- G' m2 r( V7 k
  124. {
    / B& i$ A. p  f. m* b: l" {/ ~# N
  125. . y: q/ v* q& I; C$ [+ u! c
  126.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体& A* W6 p7 ^8 |% s
  127.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    $ K5 j$ n' N( d/ R( J
  128.         TIM_ICInitTypeDef  TIM1_ICInitStructure;                 //PWM配置结构体# |& y& H; b) Z! U: h0 |! C- t( ^$ V+ Q
  129.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    4 B+ `9 n& e1 f. T9 R1 l6 }

  130. 6 Z) ^" ^9 V$ J2 }. V
  131.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);     //Open TIM1 clock
    2 F( S0 J3 s  t. R: Q$ O
  132.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //open gpioE clock) {/ X3 i+ N. C5 T# i3 N
  133.    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射  TIM1_CH2->PE11
    2 G$ _6 \' N6 |) i  a
  134.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;             //GPIO 11
    - G( [7 p! U1 j& g9 X7 r6 j
  135.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入# @3 _) t/ ]- l/ n  x7 L
  136.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 t# ~* ^" ?" r. O2 v( v$ @
  137.   GPIO_Init(GPIOE, &GPIO_InitStructure);
    # I, N7 S! W1 J5 ]& V3 M! B
  138. - {: Z, |& G& f& u* W0 }  {; U2 P: @
  139.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值7 u! n, q! L, F- p  W: v
  140.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值! Q5 Q8 i4 u& O1 u" S4 k
  141.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim& |- I! D1 A1 U/ H
  142.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式5 e! [1 e* ]9 L5 s3 p* ~2 G/ u
  143.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    0 ?" H) E- ?* r* Q6 P/ m6 ]- L9 r% D* g

  144. 5 L/ B* z4 W+ Y& \9 Y$ f
  145. 9 [; x0 C7 I9 o
  146.         /*配置中断优先级*/
    / I4 [% _6 l! J8 G& Q
  147.   NVIC_InitStructure.NVIC_IRQChannel =  TIM1_CC_IRQn;   //TIM1捕获中断
    5 d5 S6 s6 l# m' Z" x
  148.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;6 l: ^, r# u& `5 i( A  e5 S" L2 F
  149.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;' `7 V1 }: H, k6 i$ _
  150.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;6 B5 ?  Y6 V! Y6 m, h% F# I6 n; D
  151.   NVIC_Init(&NVIC_InitStructure);
    8 n5 e4 |: v; s& x# L3 K

  152. . N' C$ Z: T7 d
  153.   TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;5 Y! s* b$ [2 W2 L2 X- W
  154.   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;+ n7 o7 R; v  m
  155.   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;7 A5 n# p1 E! P: R: K
  156.   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;2 i& H" ]" |( X1 j! e6 K" S3 d
  157.   TIM1_ICInitStructure.TIM_ICFilter = 0x03;   //Filter:过滤3 P8 _; I3 y& X( a5 L$ A3 o
  158. 7 c- ^/ R, P3 Z
  159.   TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);     //PWM输入配置
    4 Z$ N. S* m  C7 L9 w
  160.   TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);     //选择有效输入端
    3 T$ O) S; j& h: r, O+ D2 b
  161.   TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);  //配置为主从复位模式
      K: S$ Q' L0 o2 i" j% W
  162.   TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
    ( Y( `# k$ d% X% D  G) X( i
  163. // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
    4 v  s  q  d9 H% s" \
  164.   TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开6 }) @, S6 Z( I6 l) B$ H
  165.   //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    9 y5 @" a! i1 a* Y$ M3 i
  166.   TIM_Cmd(TIM1, ENABLE);
    $ z  E! i) M( E) K, y( F
  167. }% k7 N. }+ f- z
  168. 9 }: e$ V( X. ]  j$ q3 K' h
  169. ' ^) s9 a0 q3 ?5 O" N: M& x) ]
  170. void TIM1_CC_IRQHandler(void)
    6 R6 U/ T4 E4 D% ^
  171. {
    7 m. c7 B, M  U  I' P
  172. . k4 e& f4 B+ m
  173.         {, k0 B5 l$ m# e% K
  174.                 if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    % v! B9 a+ M6 m- c; C
  175.                         {
    % d" B! A2 G8 h: {1 _
  176.                                 duty_TIM1    =   TIM_GetCapture1(TIM1);          //采集占空比6 g& e) S& h" M! q
  177.                            if  (TIM_GetCapture2(TIM1)>600)         period_TIM1        =        TIM_GetCapture2(TIM1);
    ( _& V7 z# ], n0 S4 `  |' v0 |
  178.                                 CollectFlag_TIM1 = 0;
    ! g! Z  w7 a6 I9 e$ a! U
  179.                         }" O( Y  L5 _& {5 b% e% x
  180.         }
    , s6 H5 \2 ]* o- L3 t( \2 `
  181.                 TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    . E! \/ w* v* |* S
  182. }  |3 d) [( y" d5 S% b( t2 J

  183. 4 J) K# ^; v6 {* q7 h

  184. ' n% a6 V& q3 J3 n# }8 A
  185. /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc)
    " \; w/ U2 a# w
  186.   描述      PWM输入初始化*/- M' C8 M, W& i' g- d
  187. / q. ?6 g5 k7 s. f* H
  188. void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)( X5 h5 V" u4 \2 ]% p* Q! o; Y
  189. {
    . Q8 X5 o) v. \( [. [
  190. ) ?5 E) A3 a7 X7 m6 `( f" @; f
  191.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    ' Q5 x5 ]! B, o, L
  192.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    ( ?- z% n2 X( w$ m7 L/ r0 f$ {
  193.         TIM_ICInitTypeDef  TIM2_ICInitStructure;                 //TIM2  PWM配置结构体
    : Z1 d9 b: I3 }9 [: u5 `
  194.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    ' G: j( k" D3 [0 q4 F, d9 V

  195. & [3 S; @0 W. A2 h7 q
  196.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     //Open TIM2 clock
    $ o& K1 W( ]) s% p& Q0 o& T
  197. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock. ~& [4 f8 v* {5 r  R
  198. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟( _' z- T; w4 [0 Q9 ~* M3 O
  199. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);          //关闭JTAG' U! }4 S7 V1 z
  200.          GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射  TIM2_CH2->PB3
    2 o4 v3 W/ U6 p
  201. $ U  o5 w8 h+ h" }
  202.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;             //GPIO 3
    3 j" I- P+ i" L7 u) o
  203.         GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
    / e6 b$ l( h2 A
  204.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% c- f+ S, L$ ]1 A7 l( [
  205.         GPIO_Init(GPIOB, &GPIO_InitStructure);
    # z. H0 n, ^/ d; s( S0 p1 s

  206. 6 Y; A8 O- `9 ^1 {" |0 E+ M# l
  207.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值3 M& G( d/ f8 l2 r% S
  208.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    7 f+ z# x$ V; q( d+ P: V  G& g' T# W- Y7 {
  209.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    8 e# y# C% O" _# ^) @
  210.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    . ^: N  g5 ]# V* ^+ l9 I, S
  211.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    2 |* M$ `3 t, O! M3 q2 k  w) `

  212. $ l7 _* J8 h8 H* F& A$ ?: h
  213. ( B* I6 G3 f* F, D# C& ~
  214.         /*配置中断优先级*/$ ^, L# f! N" y, J1 x4 c0 y
  215.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    + [/ q0 S7 \) M0 Q  Y% X) b
  216.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;% ^+ }* v6 K# A
  217.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    2 a  f2 w% @, m3 R" h, K" E
  218.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    " C8 W7 |( q7 O: L( }# I
  219.   NVIC_Init(&NVIC_InitStructure);
    9 c) k5 g. S5 s' Z6 j+ E) {0 Y

  220. . U8 W6 T8 `$ q; B0 Q- `
  221.   TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
    " W" @9 t( M4 G0 m9 e
  222.   TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;9 R! m: z* A: P. J3 A( R& H+ l$ ?
  223.   TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;# ?" f- H# R& L- s8 I+ j. V
  224.   TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;( Z# k' g1 S" P  C5 \/ N# q! t- a6 _/ W
  225.   TIM2_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤+ z& o# a  i& M. ?5 E2 n; l

  226. 2 h6 y% s. Q. {% q; V
  227.   TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure);     //PWM输入配置
    - L5 z! \7 s3 H; c
  228.   TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);     //选择有效输入端
    & x3 \7 c/ |5 J. p
  229.   TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);  //配置为主从复位模式% n% d- ?2 u8 J9 a# m, y1 q- D8 |
  230.   TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
    ! V+ D. a6 O; _( Z
  231.   TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置& y3 Q1 C& |6 x' R7 O
  232.   TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位4 l, b7 [+ w# \/ }' R; g
  233.   TIM_Cmd(TIM2, ENABLE);
    * F; k& X3 p, t( C% [2 @' b
  234. }
    4 b7 {; i9 I3 U8 M; e

  235. 1 {( F+ J% b( x

  236. " k8 y% |% M. \2 _$ d8 y3 j
  237. void TIM2_IRQHandler(void)
    4 s" H9 |" ?' f  z# k9 U% \8 S
  238. {
    / u9 a" j7 a/ D6 L+ x2 n  H
  239.         {
    4 V! [+ Z! y( v5 {4 f/ b' ~
  240.                 if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    9 ?6 h3 T4 D! {
  241.                         {
    0 ~0 _( P1 h& @& t  H
  242.                                 duty_TIM2    =   TIM_GetCapture1(TIM2);          //采集占空比2 M  E7 i7 e: F; i( B9 h
  243.                            if  (TIM_GetCapture2(TIM2)>600)         period_TIM2        =        TIM_GetCapture2(TIM2);
    : [9 M8 y# s% g' s7 s$ G
  244.                                 CollectFlag_TIM2 = 0;
    " H4 e& j, s6 W
  245.                         }% O+ k6 ?( E- y6 o( D0 J4 [1 x
  246.         }1 t% j+ f; i8 }2 u" h% \
  247.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位. D' A2 s7 R$ r3 [% i% w* F
  248. }
    ' a: D) R' c( G. R1 y

  249. 9 x7 k- K# z8 C& P; g
  250. /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
    2 v( ]9 F2 j8 U" L
  251.   描述      PWM输入初始化*/
    + D! A+ d5 A- m( g4 o) g

  252. , A" k0 ~/ t7 D( l" s1 [- T
  253. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
    8 U, N0 ]: M( {0 H2 ]
  254. {6 g2 C8 m! h" E
  255. 8 U4 J! q/ p5 s1 b2 |: [3 g
  256.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体0 k7 V( m; n) X) d( h" e' P- G. V
  257.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置% F; Y% s& y8 V) G( z" C
  258.         TIM_ICInitTypeDef  TIM5_ICInitStructure;                 //TIM4  PWM配置结构体
    ; q- A  {7 M" r; ^& f
  259.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体8 {8 r* T/ \( e* Q! h3 l

  260. - B% Q# t0 E# ]% r% \
  261.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);     //Open TIM4 clock( Z, e+ ^3 l4 p1 }% k6 t- ?4 @
  262.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //open gpioB clock3 B6 ~' c/ b' C, a: l9 P
  263. 5 T: i! y( ]2 \+ B" v" g+ M6 \
  264.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;             //GPIO 1( M8 k: w5 N, o$ _4 @
  265.   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入; r& d( ?# k* Z/ k
  266.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    0 y( b2 C% w! [$ D: ~
  267.   GPIO_Init(GPIOA, &GPIO_InitStructure);1 a- G# q. Z7 J+ e2 n2 @

  268. - }" ?' A* n& F/ ^5 J2 j+ _
  269.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值" D3 I! Z" c; |
  270.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值, H' O5 g6 ^* g2 h  a
  271.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim- c  m9 f3 |9 f# v$ o
  272.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式+ T$ v: [( k+ D1 ~, `2 W4 D" t
  273.         TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    ; S! F( z4 n5 Q5 }

  274. : ~6 Q* h9 `, S, |7 @+ a+ R! M

  275. 5 X+ y7 E' P" I8 V5 k* W6 `/ H
  276.         /*配置中断优先级*/
    , h. u  G# b4 [* `- @$ P8 r
  277.         NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
    . d( d4 R8 }0 o. x. T; w5 Y
  278.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    2 ]2 [. Z$ ^0 p3 V4 M+ r
  279.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    : R% M  r+ L& v. U4 C
  280.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ' [& ^( V/ M3 n# E* N/ q# O7 K" L
  281.   NVIC_Init(&NVIC_InitStructure);# V, F- ?' E5 ~" v: C7 n! C7 \+ G

  282. 8 O3 d' g, ]; f! _! a8 X
  283.   TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;+ H! Q. r" a& U+ s% f& h' s. q9 x
  284.   TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;$ M" {7 k  L8 m
  285.   TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;1 l% m. J4 P9 C- E6 I7 R# ~3 s' _" D
  286.   TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;4 _0 y' C/ X/ W1 P
  287.   TIM5_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤% G9 A+ z+ _: \3 D# G5 v' c5 t

  288. / R8 c- W1 j+ |. ?3 E; f. `
  289.   TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);     //PWM输入配置0 ^( j7 U3 g6 k# d9 |  a
  290.   TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);     //选择有效输入端
    ( M) T+ I) Q2 G
  291.   TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);  //配置为主从复位模式
    6 O, U# J9 k5 m) _( @; m8 g! y
  292.   TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发5 Z- H8 H1 u. X7 S/ S- J
  293.   TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置! E! f% n( O  ~; N7 Q0 ]
  294.   TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    0 I9 y% a' v3 G" n- k
  295.   TIM_Cmd(TIM5, ENABLE);- c/ q" I; f( R4 F" `
  296. }( ?" K* V3 A- y) u9 G
  297.   a; T4 ~* I1 |1 `4 |7 B
  298. 8 P, i0 C, w2 j! H& O: e
  299. void TIM5_IRQHandler(void)
    0 v, x9 E! D: U* J
  300. {
    * }1 o9 G! t: c
  301.         {! J  g$ Q7 N2 i# F; J4 w
  302.                 if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    7 M. n+ W8 X$ B& o
  303.                         {" e# c8 F2 f' a3 E
  304.                                 duty_TIM5    =   TIM_GetCapture1(TIM5);          //采集占空比$ D5 a9 }' ]% V, j! e6 C8 S) `4 ~0 L
  305.                         if  (TIM_GetCapture2(TIM5)>600)         period_TIM5        =        TIM_GetCapture2(TIM5);; y& D" p3 D4 W2 m) o2 l; {
  306.                                 CollectFlag_TIM5 = 0;% f( ]5 R; n& Z) W4 ^1 ?# Z; s
  307.                         }
    & @4 x3 l7 o: C
  308.         }
    % o& G+ d: r8 S: `: d  W# \  N  C
  309.                 TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位, C7 _  T" f0 p
  310. }
复制代码
0 r  g4 V6 x5 S" ]- r5 T" w9 f
2 q9 q& B0 Q7 |" f
PID部分:
: g6 F9 ^) `4 {准备部分:先定义PID结构体:
: K, i. I: X6 ?9 J
  1. ; \- @' w6 H# q) v$ \) T
  2.     typedef struct
    : h* F8 \" M8 w$ L9 _
  3.     {9 J& e: O2 E0 \1 D% k9 s) x, A
  4.     int setpoint;//设定目标! a9 C2 u" m% p) M$ b7 \# u
  5.     int sum_error;//误差累计
    - ?" K7 Z9 p6 {* K
  6.     float proportion ;//比例常数" S5 G3 [+ K/ s9 c  B( }
  7.     float integral ;//积分常数
      N5 m: x4 h! T
  8.     float derivative;//微分常数, t; E) {+ r8 d+ {! A
  9.     int last_error;//e[-1]& C* ]4 O* g! X  y0 j! q
  10.     int prev_error;//e[-2]
    7 n) S) O% n2 X+ l' @: F
  11.     }PIDtypedef;
复制代码
3 \+ j; R; i9 _! o
5 [  e, U3 K0 s9 q# G3 u; j1 [

- w) a+ e6 m# X  \% F这里注意一下成员的数据类型,依据实际需要来定的。: b# u1 y% u9 L
在文件中定义几个关键变量:! O& e7 o" h- K( S- g3 d8 H- p' I
  1. float  Kp =     0.32  ; //比例常数5 p$ q& J% M! X0 n* [8 n! ^3 I3 `+ o
  2. float  Ti =                0.09 ; //积分时间常数% k' ]1 [4 V/ R- t8 e
  3. float Td =                0.0028 ;  //微分时间常数3 ?) D# R2 N; ?! `6 A- W
  4. #define T                  0.02 //采样周期
    , \& N4 t4 p  B# y  p
  5. #define Ki     Kp*(T/Ti)        // Kp Ki Kd 三个主要参数' ~7 T" p! n3 w2 d. t- K4 m
  6. #define Kd                Kp*(Td/T)
复制代码

, ]& V% P! u, m
( B9 X4 I; u4 LC语言好像用#define 什么什么对程序不太好,各位帮忙写个优化办法看看呢? 用const?; w' c; b( a" n% R& ^7 P

2 [; f$ Q3 K+ Z: B6 `2 y# }. K6 b6 {: rPID.H里面主要的几个函数:
9 X3 `' ~* x' `7 F
  1. void PIDperiodinit(u16 arr,u16 psc);        //PID 采样定时器设定7 O+ H- c2 P/ m" `) K# \$ r
  2. void incPIDinit(void);                //初始化,参数清零清零
    % O0 K$ Z) G) e: v+ X
  3. int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint);           //PID计算( z9 @1 d5 R4 b8 V7 _
  4. void PID_setpoint(PIDtypedef*PIDx,u16 setvalue);  //设定 PID预期值
    * j8 v2 @5 D" H4 X
  5. void PID_set(float pp,float ii,float dd);//设定PID  kp ki kd三个参数
    2 L! c/ s; j, e
  6. void set_speed(float W1,float W2,float W3,float W4);//设定四个电机的目标转速
复制代码

7 k5 d2 d% Y) F6 Q) J8 I$ A  k1 o# G2 ^- g, Q/ h) r( G
PID处理过程:5 F0 l* H* h! e  G
岔开一下:这里我控制的是电机的转速w,实际上电机的反馈波形的频率f、电机转速w、控制信号PWM的占空比a三者是大致线性的正比的关系,这里强调这个的目的是
7 o4 O* N( D2 s+ ~因为楼主在前期一直搞不懂我控制的转速怎么和TIM4输出的PWM的占空比联系起来,后来想清楚里面的联系之后通过公式把各个系数算出来了。
! v4 m9 l2 {& T9 U) K4 n6 L

: p! Z) [9 Y) T- k$ R4 I正题:控制流程是这样的,首先我设定我需要的车速(对应四个轮子的转速),然后PID就是开始响应了,它先采样电机转速,得到偏差值E,带入PID计算公式,得到调整量也就是最终更改了PWM的占空比,不断调节,直到转速在稳态的一个小范围上下浮动。$ i. q3 d6 s# S
上面讲到的“得到调整量”就是增量PID的公式:
6 O3 w+ {$ j1 p+ T4 K4 ?+ o# t
  1.     int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint)
    - T! I! [* U6 N9 e/ u$ ]( [' E: [
  2.     {
    8 i1 t- L) @' q; u
  3.     int iError,iincpid;4 R. f* h6 p/ V1 a) m  Z2 ~
  4.     iError=PIDx->setpoint-nextpoint;  //当前误差6 t3 s0 |6 ?5 a! R, {# w
  5.     /*iincpid=                                               //增量计算
    : B0 @4 I4 K  W* q. n  l
  6.     PIDx->proportion*iError                //e[k]项
    % [6 j% v7 r* [$ T. M
  7.     -PIDx->integral*PIDx->last_error          //e[k-1]* y$ n  s; M! \- p1 ~
  8.     +PIDx->derivative*PIDx->prev_error;//e[k-2]
    4 Z# Q+ e, ^4 @/ g/ I
  9.     */2 c% z. z" F+ k  \4 h7 F3 V
  10.     iincpid=                                                          //增量计算+ P9 ]" ?+ k6 m' y+ b0 b  V
  11.     PIDx->proportion*(iError-PIDx->last_error)
      m9 `! m* o6 a- H! U" L! i
  12.     +PIDx->integral*iError
    * r: R) ]0 [: ?
  13.     +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);
    7 B; V) o+ w8 N2 `. a7 \

  14. 8 i2 f$ ?- R6 d
  15.     PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算& c- C% H- S7 N- Q7 n: w
  16.     PIDx->last_error=iError;
    . p' q+ O0 o# F/ {4 g0 o3 H
  17.     return(iincpid) ;
    4 }) J1 d  H- A$ _5 w
  18.     }
    7 S; t0 `2 u) v9 m2 `  p7 J
  19. ( `* v& y! ]! K/ L; s+ l* A

  20. " F3 m( f$ s: b3 A9 X3 ^
  21. 复制代码
    $ Q8 `; O1 z0 {  z
  22. 0 e/ ]: c7 o1 S% @! T' k+ B( k
  23. 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
    ' K% Q1 C5 l/ \8 D0 O) x. |. a
  24. 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)) A2 l; ~# S& h9 L* R* F3 {% t
  25. 相关代码:
    4 V  y: e7 Q4 E' @

  26. 9 c! B; y# z+ h, a! x% @7 @  ^8 F% O
  27. # {0 V7 s4 L* R
  28.     void TIM6_IRQHandler(void)        //        采样时间到,中断处理函数
    ; t4 z: _  z- D& Z
  29.     {
    % N& I# f0 _) a

  30. & s7 L/ Z& q: ?) G& ?
  31.     if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断1 W7 K3 |) O9 X& j( Q, [; u
  32.             {
    , d. U' W7 P( s
  33.             frequency1=1000000/period_TIM4        ; //通过捕获的波形的周期算出频率
    4 ?* w* F8 U4 r; c
  34.             frequency2=1000000/period_TIM1        ;
    ; ^7 s1 s8 ~, `5 x" H3 j
  35.             frequency3=1000000/period_TIM2        ;
    # i. \5 x( \2 e! g* d7 ]% n# }1 W
  36.             frequency4=1000000/period_TIM5        ;; T5 e% T6 V( f) f
  37.     /********PID1处理**********// h/ l8 t; _5 a( c
  38.             PID1.sum_error+=(incPIDcalc(&PID1,frequency1));         //计算增量并累加
    ' t! B+ n% u, ]% I
  39.            pwm1=PID1.sum_error*4.6875  ;   //pwm1 代表将要输出PWM的占空比  ?% {+ l! B, d' q: G+ Q
  40.               frequency1=0; //清零
    " F# P! E8 c8 R1 F; s; m
  41.          period_TIM4=0;. z+ b7 k/ X$ v% D; `4 E
  42.     /********PID2处理**********/5 o; A, ^- X9 ?# W; F& O; T5 A$ U; \
  43.              PID2.sum_error+=(incPIDcalc(&PID2,frequency2));         //计算增量并累加  Y=Y+Y'
    - d" T# p6 S* X
  44.              pwm2=PID2.sum_error*4.6875 ;   //将要输出PWM的占空比
    & d  c- V; Z9 G  c" ]
  45.             frequency2=0;
    8 e% O  }& z6 ]& _% X# {
  46.             period_TIM1=0;
    ; ~4 ?# p+ ]0 f4 A/ y7 E( C1 Y6 X% _
  47.     /********PID3处理**********/
    " p, I$ D4 r+ o) ~$ r
  48.              PID3.sum_error+=(incPIDcalc(&PID3,frequency3));          //常规PID控制* |2 m" r( X0 H2 {& B1 d
  49.             pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比
      P  Z: g: ]* k+ l6 l
  50.             frequency3=0;7 i/ M+ h# c2 V7 g3 z9 c9 i
  51.             period_TIM2=0;
    4 v8 n6 N& i& v
  52.     /********PID4处理**********/
    # [  }/ s1 S! Y* \
  53.                 PID4.sum_error+=(incPIDcalc(&PID4,frequency4));         //计算增量并累加
    + {4 `+ ]  K1 r, `
  54.              pwm4=PID4.sum_error*4.6875 ;   //将要输出PWM的占空比
    1 x) V/ G; O) N5 q- `) L
  55.             frequency4=0;% y9 h8 y3 p+ c9 b# J3 [
  56.             period_TIM5=0;+ q- s# k8 v- C* c8 @; P" y0 e: M! y
  57.               }
      [; K8 H" E5 q3 f
  58.     TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);             //重新设定PWM值( m. @: o* z! `* z
  59.     TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位
    $ b- W+ g1 _* _( B' e& c
  60.     }
复制代码

+ N; W  Z+ T/ c/ a$ Z& C8 K2 l/ @& j2 W8 a! j# k7 v+ @- D
上面几个代码是PID实现的关键部分! H3 Q9 J. ]) i( J; B
  W- N4 \5 b" h$ [
整定过程:
2 E, r* f, u! c2 w  S办法有不少,这里用的是先KP,再TI,再TD,在微调。其他的办法特别是有个尼古拉斯法我发现不适合我这个控制对象。& x+ Y5 Q! _/ V$ K: U& _
先Kp,就是消除积分和微分部分的影响,这里我纠结过到底是让Ti 等于一个很大的值让Ki=Kp*(T/Ti)里面的KI接近零,还是直接定义KI=0,TI=0.
  n( Q8 ^8 x2 z" j" ~, M* b( l) p; Z然后发现前者没法找到KP使系统震荡的临界值,第二个办法可以得到预期的效果:即KP大了会产生震荡,小了会让系统稳定下来,当然这个时候是有稳态误差的。6 T0 j" `/ q5 A# y. X2 Q& `6 S! R
随后把积分部分加进去,KI=Kp*(T/Ti)这个公式用起来,并且不断调节TI 。TI太大系统稳定时间比较长。
! ]  ~4 o. U) W6 x/ a0 d+ Y5 l然后加上Kd        =Kp*(Td/T),对于系统响应比较滞后的情况效果好像好一些,我这里的电机反映挺快的,所以Td值很小。
1 ~) A* P0 b/ C$ o: w最后就是几个参数调节一下,让波形好看一点。这里的波形实际反映的是采集回来的转速值,用STM32的DAC功能输出和转速对应的电压,用示波器采集的。! K( ~4 s/ e. r7 o" u  q
最后的波形是这样的:
' T3 H2 P! l, I6 W2 E* ?7 l5 n' z3 o 3.jpg * c( r0 i4 X0 ?1 {! l
最后欢迎大家拍砖,有批评才会让我更加进步!
& h4 M0 P$ |$ v' l最后把PID文件放上来 pid.zip (2.88 KB, 下载次数: 134)
收藏 5 评论17 发布时间:2017-12-26 15:40

举报

17个回答
zero99 回答时间:2017-12-26 16:11:12
大佬,这样插代码效果更加哦* ?4 Y5 p5 l6 H9 c8 u

0 j* R3 r1 R6 W
  1. 测试代码. z+ g, ~4 E3 R- q2 d. z# l
  2. 测试代码
    4 f% @& |- |; W  ~
  3. 测试代码
    ' @. _( B/ c, c8 K' c8 E- s
  4. 测试代码
复制代码

. l: F! B8 c, ^3 e8 I1 I0 n$ a3 @
; g; v1 z: k' m3 D/ ] 333333.png & p. t  |7 a/ Y7 n, e
二子 回答时间:2018-10-3 11:10:48
nyszx 发表于 2017-12-26 15:41
) {- W8 [: n2 W& A! s1 ?再付个网上的资料

  _8 o  {1 q, }这是个好东西
' d8 Y* t  }/ q5 N  d书到用时方恨少( Y$ ^; r6 Y: b4 ~
果断收藏
tlj1b455c 回答时间:2020-8-16 12:15:37
我想问一下,那个编码器得到的脉冲数和占空比有啥关系呀
nyszx 回答时间:2017-12-26 15:41:49
再付个网上的资料 PID控制经典培训教程.pdf (406.07 KB, 下载次数: 183)
nyszx 回答时间:2017-12-26 19:24:38
我马上改过来
东方惑思 回答时间:2017-12-26 20:48:07
学习了,谢谢!
' |. i4 k. q3 b* K  a
板子粉丝 回答时间:2018-5-12 12:58:31
不错,学习了
西点钟灵毓秀 回答时间:2018-7-22 02:13:36
学习了,正好需要
libin1009 回答时间:2018-7-22 15:44:52
学习一下!
shwp 回答时间:2018-8-20 14:08:14
学习
wjwjwjwj997 回答时间:2018-8-21 08:14:50
学习了,正好需要
coobakl 回答时间:2018-8-21 16:10:40
学习了,正好需要
chao2018 回答时间:2018-9-20 09:48:26
谢谢分享!!
gengkeju 回答时间:2018-10-8 14:32:36
赞,学习一下
zjczm 回答时间:2018-10-9 17:31:58
有空学习。
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版