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

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

[复制链接]
nyszx 发布时间:2017-12-26 15:40
本帖最后由 nyszx 于 2017-12-26 19:31 编辑 2 Z7 q* z! j" c( _% r9 u

5 O/ a- G0 o9 |% W3 J; w8 J原帖:【分享】增量式PID的stm32实现,整定过程& a1 S8 }7 R1 W; e
原文网址:http://www.amobbs.com/thread-5575823-1-1.html
1 f& O  q+ `) W& k3 e出处:阿莫电子论坛
! m$ A7 `) c5 ]6 W6 V: @' H8 @4 v作者:tim41461 X) n$ g$ ?8 p% B, @% @8 {
感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。
4 f7 }: x3 F% d: M8 z+ g- V, L也下面我分享一下近期的这些工作吧。欢迎大家批评指点~
- C2 m# I+ i9 \/ z8 U
) W! `3 o- p# ], b, V+ E, B5 n首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。; G6 o) o% i6 _; R, _! o+ x
资料上比较多的是:" d1 a- v' G% y9 j. t
1.jpg
& x' _# ~7 t, d7 b( j还有一种是:
" i* d$ D! M  E, z1 n  ~, ~* C 2.jpg # e" I2 f, [2 K) d
感觉第二种的Kp Ki Kd比较清楚,更好理解,下面介绍的就以第二种来吧。(比例、积分、微分三个环节的作用这里就详细展开,百度会有很多)+ O- w/ _0 Q3 R; B
" b; s. Z/ r3 l# O9 q1 p
硬件部分:
; {9 ~+ C% {; l6 ]; D* F7 Q控制系统的控制对象是4个空心杯直流电机,电机带光电编码器,可以反馈转速大小的波形。电机驱动模块是普通的L298N模块。% y. W4 l& V2 J0 q( |8 \
芯片型号,STM32F103ZET6! C9 S0 r. `+ P. q+ G9 e3 f

, ?# H8 W' q+ c$ h: s0 p软件部分:# ~% a6 u( W1 N( _. @# k" T1 i8 V
PWM输出:TIM3,可以直接输出4路不通占空比的PWM波& D3 t  D0 n5 z0 A: M
PWM捕获:STM32除了TIM6 TIM7其余的都有捕获功能,使用TIM1 TIM2 TIM4 TIM5四个定时器捕获四个反馈信号& o: I. V9 I5 e' u; w
PID的采样和处理:使用了基本定时器TIM6,溢出时间就是我的采样周期,理论上T越小效果会越好,这里我取20ms,依据控制对象吧,如果控制水温什么的采样周期会是几秒几分钟什么的。
  j* }$ K' l* P2 C- c. L( u* }; p: |1 x. A2 V# S
上面的PWM输出和捕获关于定时器的设置都有例程,我这里是这样的:5 n1 C( y; c- T, n
TIM3输出四路PWM,在引脚 C 的 GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9输出
! H$ B+ {7 W7 N4 x四路捕获分别是TIM4  TIM1  TIM2  TIM5   ,对应引脚是:  PB7 PE11 PB3 PA1

0 Y4 g! y5 C# A5 ^. H高级定时器tim1的初始化略不同,它的中断”名称“和通用定时器不同,见代码:# F0 D0 [+ r2 A
  1. /*功能名称IM3_PWM_Init(u16 arr,u16 psc)
    ; U3 N) O! k* w2 z) t6 H* I4 A
  2.         描述      TIM3产生四路PWM
    0 n; p4 \% h* D7 R3 j7 X5 N
  3. */8 p2 G% ^! Y! ~& j
  4. void TIM3_PWM_Init(u16 arr,u16 psc)( H$ W* ^6 h0 H  Y4 x
  5. {* {* }9 |# F) v% Z' Y
  6.         GPIO_InitTypeDef GPIO_InitStructure;7 g& k+ }% w2 X5 P
  7.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    5 [6 X$ p& j4 ^9 k
  8.         TIM_OCInitTypeDef  TIM_OCInitStructure;' I2 ]$ h4 k3 g* ]  N  v4 ]2 F6 c

  9. 1 u# H9 }+ J/ K1 ~3 a5 H" t

  10. 5 x% [1 v0 E2 L# z
  11.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);6 [7 c- ?8 q1 v4 W
  12.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能9 {2 @" a2 a3 n% r! @
  13. / B* m9 f, p; S9 t  C
  14.   GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示
    ( i6 W: W  m) j: L

  15. 9 f) _# G1 f+ X- c
  16.    //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形6 w- }$ T: s% a' F1 E
  17.         GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO8 Q8 a/ ^. X" U0 o$ B
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出$ @0 W1 @/ A% B( q( w$ T7 ?
  19.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;" X0 v- _8 c! h; ?4 C! ~
  20.         GPIO_Init(GPIOC, &GPIO_InitStructure);! r8 N* B  Q  z/ d! l8 ^
  21.         GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能
    ' h2 p" E$ z7 c$ j/ L* W
  22. * l% b- j9 G$ w2 ?0 Y
  23.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    / q$ z- S$ f/ Z: h# b5 s( [
  24.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M& o8 v3 _# X& Q' q/ o: I4 }+ [
  25.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim5 |/ S; T/ l& ]# O2 v
  26.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    9 z2 Z" v( ]/ h% P
  27.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位) v0 Y4 M) H; n) s8 P
  28.   \7 n2 b# d  X" p) `2 i+ N6 \( v
  29. $ ?' h: `( R- n1 O1 _( v4 b' ]
  30.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式12 L8 F" O/ b/ a- K! Y8 V6 D
  31.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    3 R7 z2 B  p$ k7 K' [) [6 J0 I  _
  32.         TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
    9 I4 {  X. M1 Q7 l% i/ D; x: r
  33.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    & _$ Q4 s+ ~. o! y

  34. 5 u0 A; P  s/ z4 N
  35.         TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    ( |9 p: V7 l  |" u% {5 V
  36.         TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR1上的预装载寄存器
    # I8 p, K$ K/ z. t4 F3 k7 @+ A

  37. 5 K( y+ }+ i' R5 V, e3 a

  38. 8 K5 R' V- o4 R, _8 \
  39.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx: Y) F0 r4 U2 c2 T( Y+ D% f
  40.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器* ?  q# d- N- t# T" A
  41. 8 H) K) s0 `) a$ n' W! E
  42.         TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
      J: \: r! d- G$ I# F4 }/ M( d
  43.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR3上的预装载寄存器+ c6 J: }- X9 D. W# m
  44. ( L* b7 G" i2 Q) p5 A" M
  45.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    / {3 I* M3 e3 x! \/ p' I
  46.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR4上的预装载寄存器
    ( P8 ~2 l, Q) g3 y

  47. 9 \4 N+ E; S: b3 \, Q
  48.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
    $ }* q4 P' W/ G  f! {# q" T

  49. 8 H' p* M9 Q: e
  50. * V% p8 R4 u2 i
  51.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设. o& |$ B$ |6 x9 T. S! b

  52. , ~) M  P7 b" D6 m2 d: a
  53. / J2 k$ T2 c" J* ^+ C2 Q
  54. }
    ' Z! M* a0 C5 [- z: o+ {" \
  55. ! g, b! G- Z' Y
  56. 8 D9 m& {; H, l- W

  57. 4 b2 J0 W$ S* J
  58. /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
    9 n$ y9 _; D5 y" D
  59.   描述      PWM输入初始化*/) k# @& q; q5 L4 a2 ~! B, }

  60. $ e+ A& `' _) i" d7 h: f& T
  61. void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)$ M+ |6 M% N- o2 R- {  ?! r
  62. {. T! m9 C- D6 ?# w  }
  63. ) Q2 f7 q( ?# f4 l% k# I; G
  64.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    . u' i3 G+ e/ ?0 R. d
  65.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    / p! B  n: j8 |$ `; M7 i/ s: ?3 l
  66.         TIM_ICInitTypeDef  TIM4_ICInitStructure;                 //TIM4  PWM配置结构体& q$ r- ]! h* H3 _
  67.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体  K: W. Z9 q$ G. }$ J

  68. 2 }' N- U4 n) C$ T$ |2 g
  69.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);     //Open TIM4 clock( Q! l% E7 r' F# G! f9 E6 J: x
  70.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock, `$ t6 l$ G( g/ Q3 f
  71. 6 T  F' a# W3 T0 d  x2 C
  72.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //GPIO 7
    % f& W6 ?/ r4 J7 c. L9 ^
  73.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入# j8 k( A; _- `6 e
  74.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 P; R' V& E5 u; y5 N
  75.   GPIO_Init(GPIOB, &GPIO_InitStructure);6 b) P& F% G0 m/ c- N. M* H

  76. ! X5 j7 R& x. ?" u
  77.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    ' h, V" Q* j  R% D; L( M
  78.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    5 G- z6 s5 l9 I& }' S
  79.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    8 F: Q1 N0 N  s/ V2 M/ J, [; p* h2 ?
  80.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式: }. G: B. w' Q& }& ~& x
  81.         TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    ' N* p! @) s' Z* T( C. U

  82. # I: Y4 o( F! s/ {/ ^' o
  83. : S1 `8 s/ O: y' v* W
  84.         /*配置中断优先级*/
    + x, V8 @* f! r! s/ C) I
  85.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    0 d* Q: }- Y' Z( W' ~6 d
  86.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    $ s4 D. W2 U1 t, Q1 h0 V* G
  87.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    % G# K6 W* z% }4 H+ q
  88.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;7 U0 J3 V1 z# `! C0 c8 g  a
  89.   NVIC_Init(&NVIC_InitStructure);7 P' `, X% E6 X, [

  90. $ N! T; o5 |7 w! M. N: t$ A
  91.   TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;# H. |$ z: R9 L$ c( }. F
  92.   TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;/ \- ?  g- C. p% C% Y* Q! H9 w
  93.   TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;. s7 w* Y8 X8 L+ ~: ~
  94.   TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;+ Q: Y" J) g2 I
  95.   TIM4_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤
    " O: g! s1 t! P  c4 O

  96. - ?4 k& M6 i/ t1 |% ^
  97.   TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);     //PWM输入配置7 m% W  ]4 y" N  H: P8 O* H
  98.   TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);     //选择有效输入端/ r# P) N8 s6 x( s1 S5 l* B
  99.   TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);  //配置为主从复位模式# V( h% t: k: b+ @$ u6 [6 A' r
  100.   TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发& }1 M  j1 n5 g& }' }
  101.   TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置9 t' c. Z0 s! _& f. U$ n" U2 {+ B- L- c6 c
  102.   TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位/ H& V& x: O7 A4 }
  103.   TIM_Cmd(TIM4, ENABLE);4 C* L, A; ^/ h" W
  104. }5 r) T$ W+ \" N. l; Y

  105. ( `1 ]+ Q; X  }/ L' r
  106. 9 C* l# o9 Y5 H# ?( P7 O: l) N5 v
  107. void TIM4_IRQHandler(void)6 S! b4 f4 ~* y
  108. {0 Y1 K1 S9 H' {5 K7 u6 \

  109. % s+ X2 h- B& N$ Q
  110.                 if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    % J8 L& B6 j3 d. a
  111.                         {2 v6 h& ^' C$ b1 ]+ C5 \4 y
  112.                                 duty_TIM4    =   TIM_GetCapture1(TIM4);          //采集占空比8 s, N/ d5 O8 v  s3 L
  113.                if  (TIM_GetCapture2(TIM4)>600)         period_TIM4        =        TIM_GetCapture2(TIM4);//简单的处理3 P9 d, `5 v3 J/ Q0 |& y
  114.                                 CollectFlag_TIM4 = 0;
    + w! h2 j% Q  [9 L+ `( H6 w' v$ g
  115.         }
    2 J; G6 P7 L! M4 N! D
  116.                 TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位4 ^  }6 k# K7 ]/ d+ X4 W& C' j
  117. }$ e$ x0 v% r7 Q$ T3 b; x9 n9 o
  118. & O8 P8 a( q9 J+ k( R! d6 l( f
  119. / Y. X5 a5 n* I0 J* Z, W2 W
  120. /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)3 J4 S" z1 H" ^
  121.   描述      PWM输入初始化*/6 `- K3 }+ P: I$ V/ _' O" ?) [) P

  122. 4 h9 d4 V0 a  B, o: ]
  123. void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
    $ S# Z$ Z7 ]: ^5 D/ X4 b
  124. {0 L1 Q/ C4 u" m6 @* I/ i
  125.   t! K+ @1 W- K3 T6 k
  126.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    6 r4 C, {1 N; v' D. E4 `. I: t
  127.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    ) s! Z5 i" y/ ?6 I% u! w) h/ B0 N
  128.         TIM_ICInitTypeDef  TIM1_ICInitStructure;                 //PWM配置结构体$ a7 w1 _3 d% `7 x4 l0 y! V
  129.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    : r8 z3 T( d5 o* V# m1 n. B
  130. ' N; v# C- w4 N( x9 m. S9 i
  131.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);     //Open TIM1 clock$ |7 T/ l( C( `7 b. ^& o9 I
  132.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //open gpioE clock
      z1 w3 `6 v3 N% e9 m: g( F
  133.    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射  TIM1_CH2->PE11
    , W: F7 f) A, I) ^
  134.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;             //GPIO 11  }# K* W8 ?; T$ [, D, O
  135.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入; c  E/ w- y8 ^' e) [
  136.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    6 c. ^: i* I6 `, ^. w% S3 F
  137.   GPIO_Init(GPIOE, &GPIO_InitStructure);' G* M' M  G  n5 P- z' Y: v' o
  138. - Y+ K+ h# g7 Y) @
  139.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值2 o2 A& o, O! y& _  e1 _9 w+ M( o0 D$ P
  140.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    * b1 U+ a, h; C$ y3 C: ?4 D
  141.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim* [" Y( S3 Z9 F6 u% a
  142.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    + |; B$ k' t& b1 n  @. P
  143.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    0 S% A' E! R9 a  M

  144. & e2 O& O4 o7 N2 g" \
  145. , C  Z- ?  B& K. T
  146.         /*配置中断优先级*/
    $ e5 s; g0 k6 |3 o* ]: z
  147.   NVIC_InitStructure.NVIC_IRQChannel =  TIM1_CC_IRQn;   //TIM1捕获中断1 b: a2 \0 ~; p9 H3 _
  148.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    $ Y6 i2 U" L0 F( ~
  149.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    # M* p9 G0 ^; R- f* u; _4 q+ {
  150.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    5 Z* h9 {1 Q) K6 x  u# g4 y% I) }
  151.   NVIC_Init(&NVIC_InitStructure);
    2 e$ j* e' ~, x2 b# h

  152. ( J, l% A, h% N; t6 v
  153.   TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;
    ; f: j8 L, E' X3 w
  154.   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    9 r4 ]0 a) {$ y" Z! y
  155.   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;3 H9 D3 E2 B& p% H  i
  156.   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;+ g  x6 r, J5 Q7 ]
  157.   TIM1_ICInitStructure.TIM_ICFilter = 0x03;   //Filter:过滤
    * N3 j3 a: ]; m# f
  158. 0 Q* \/ ]) R1 g1 G3 u
  159.   TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);     //PWM输入配置
    2 f" X- x0 u' o
  160.   TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);     //选择有效输入端5 Y* Z5 K8 Q, ]
  161.   TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);  //配置为主从复位模式
    6 @+ X) M( g8 S" Y# i3 m
  162.   TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发4 v4 o- u0 t0 a7 Q0 K) ^
  163. // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
    " K) ]; l. l- e+ S3 [9 D
  164.   TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开  Z4 V5 j) d/ F, [7 Z
  165.   //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    / \+ t+ l* t: u3 p% D+ f! q
  166.   TIM_Cmd(TIM1, ENABLE);* `" B* @9 ^; o. ~& @
  167. }6 v) X( f/ b3 a2 ]( F: a

  168. * C+ b, o, y' E
  169. $ ]1 q- o4 F, x: [  G5 h+ I
  170. void TIM1_CC_IRQHandler(void)
    ) _% W! I5 _# y% j
  171. {
    7 ~% \4 y  Z' e
  172. 6 f# E; R0 e# E1 T5 {7 r
  173.         {
    7 E4 r& U3 F) ]6 `' @* ~
  174.                 if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
      r* h' w: @; f. s, [
  175.                         {4 `1 Z4 q* K" r
  176.                                 duty_TIM1    =   TIM_GetCapture1(TIM1);          //采集占空比, `; h4 k+ n7 ]" X
  177.                            if  (TIM_GetCapture2(TIM1)>600)         period_TIM1        =        TIM_GetCapture2(TIM1);
    . @" r  O; c: e! b( [
  178.                                 CollectFlag_TIM1 = 0;
    6 ]) a! s' l4 ]; ?: e: \) R5 ^
  179.                         }
    ) I, p" _' r9 t9 G  K; [
  180.         }
    3 h! g$ L0 b, o5 R9 Z
  181.                 TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位; w  B& E$ z- M
  182. }0 v3 [1 u: C9 m% h
  183. 1 |2 e; b3 G$ E4 o. \, I5 w, G

  184. % D/ Q! D, o8 E; H( j1 \
  185. /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc); u# ]6 t3 J, ^6 Q# D0 [# I  d
  186.   描述      PWM输入初始化*/1 @" ?- A4 B& B8 x3 Y
  187. + ?1 a. n% a8 j9 O  \9 N
  188. void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)( t/ U  i+ v& c7 A* P
  189. {' T* W, P) j, o( F1 k4 B4 w- |

  190. 0 E) w. y' R/ h  e- g$ a* B
  191.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    ; R8 E- h0 J* w  S/ y5 N3 Q
  192.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置+ L: _8 N! n  c+ A7 G( ]# j7 n6 `, n
  193.         TIM_ICInitTypeDef  TIM2_ICInitStructure;                 //TIM2  PWM配置结构体8 o3 C7 G/ P4 m. t" D/ m0 b
  194.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    - b# ~( ]) I- _3 c8 A, M

  195. & A$ b- M' J" c9 j% x0 m, ?2 H
  196.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     //Open TIM2 clock
    4 W, J: ?0 e( ?% g2 x, Q. w0 ~" o
  197. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock; r( b* F+ T$ S# S
  198. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟1 y3 b. ^* x8 j# ^
  199. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);          //关闭JTAG
    8 V' Q* L2 h7 h' E" C6 t6 w
  200.          GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射  TIM2_CH2->PB30 U7 [  ]* c0 J; G

  201. ! H8 X* [6 c: A3 ]
  202.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;             //GPIO 3
    " z( Y' B: Y0 Q. S
  203.         GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
    ! m" V9 j# e3 T6 ]
  204.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' U: U, c2 }- p4 R7 i3 T" V
  205.         GPIO_Init(GPIOB, &GPIO_InitStructure);
    ! a, e9 _- G9 Q+ t8 O) {# W

  206.   S5 t8 Z( G; q
  207.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    : ^/ k+ C. {7 Q3 x4 y
  208.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    . L8 i; c+ k3 _6 N( I
  209.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim# s8 ^1 ]- Z$ H' V; H
  210.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    4 Q$ C' Y6 B7 w7 N- g
  211.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    ! \+ U# F5 }. R

  212. : d) r; b& e2 P+ Z

  213. 2 R  w% u; ]9 y; X
  214.         /*配置中断优先级*/  g4 U0 s5 a  E, Y" Z$ M# U6 p
  215.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    ' L: m4 h6 o0 N2 J
  216.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    $ W$ L5 u' L( I4 E& I. K
  217.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;* w; l/ z! o% }- w! i+ G8 T
  218.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    : R; h( \  Q$ V4 y8 x' g% Y" ~
  219.   NVIC_Init(&NVIC_InitStructure);
    6 K0 |8 I3 \; _. M

  220. 7 `+ O  S/ b# a2 a5 v% ]# K+ M
  221.   TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
    ; @1 y, E$ p3 {" u
  222.   TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;; s& ^4 S$ r. K
  223.   TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    , w+ b. [5 k3 a! U' k9 x# ]$ O2 h
  224.   TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  [; r8 b- c2 l" f3 ~; T8 g
  225.   TIM2_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤/ |! S6 n+ Y1 N3 J! u' E# b
  226. 1 _/ U9 K( _  I" L) e
  227.   TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure);     //PWM输入配置" N9 i# ]7 N; `8 \
  228.   TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);     //选择有效输入端* y# q$ s3 ?, C- W% ]8 V7 N" @
  229.   TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);  //配置为主从复位模式
    5 m* y4 O8 M; H) b5 `" A* C0 B
  230.   TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
    ; A# k; T5 Y- @$ V7 E( q
  231.   TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
    ' R7 |: x; A. W* Z+ h2 `
  232.   TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位8 b7 g) T9 L" q, Q$ [- U
  233.   TIM_Cmd(TIM2, ENABLE);% ~* b) V0 f* i! d* R: r. Q; s5 Q
  234. }
    5 e+ q+ `) t" I$ J7 z

  235. 0 i, d9 n8 Y+ I$ ]- L3 T3 z9 U
  236. 9 ~$ \% U9 g$ E: ^4 \
  237. void TIM2_IRQHandler(void)1 ^* X/ d( P7 v% f
  238. {7 `$ t- R# ~$ P7 ?" o
  239.         {
    5 p' t4 g' K4 H% H0 c* F
  240.                 if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    + }, T. S0 T% k' H
  241.                         {( J( C) k# p! a" {% G# T" i
  242.                                 duty_TIM2    =   TIM_GetCapture1(TIM2);          //采集占空比) F+ k1 c5 I4 V' Z$ H1 q( ]0 l
  243.                            if  (TIM_GetCapture2(TIM2)>600)         period_TIM2        =        TIM_GetCapture2(TIM2);- r3 I5 @- C! ?0 I
  244.                                 CollectFlag_TIM2 = 0;
    , ~& s5 o7 b; _  F, c
  245.                         }* C# s5 q9 E7 \( q: V
  246.         }! E/ l: s" j: Q( |7 J% I; q) P
  247.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    2 _$ n$ Y, G  V+ o
  248. }9 s1 E/ W+ \" O3 ?9 \' m

  249. . G1 z7 X( T% N4 e7 r
  250. /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)* o% p/ {$ y) ^" y! h) ?& V# G
  251.   描述      PWM输入初始化*/
    4 }- ^0 p' u- Y4 r, y* T0 Q" K

  252.   B0 W1 A! Q( R1 A* W9 ?1 b, f
  253. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
    ( x/ X) u' g+ p0 z, h$ S$ h) {
  254. {
    ; {* v* ^9 Y& v' K3 p
  255. * E6 L) e% {  _: s0 M4 {  e0 C# ]" P
  256.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    8 A# \- C2 \& D7 B
  257.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    5 M4 r# R+ U/ v2 `) D+ Y, N
  258.         TIM_ICInitTypeDef  TIM5_ICInitStructure;                 //TIM4  PWM配置结构体
    ! P7 H- Q" v: s% U& M- o+ A* z
  259.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体2 D  `# a4 x7 E, q/ C+ T

  260. 8 p3 Q3 b. ]" e4 I/ F. ^
  261.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);     //Open TIM4 clock
    , [1 b/ n3 h: B/ a+ Q+ X% K9 s* f, @
  262.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //open gpioB clock
    ( a1 Q! R# z+ r- i
  263. 2 ^0 X1 J0 F: F. y: z' B
  264.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;             //GPIO 1; Q  D. k, a: r* B
  265.   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
    / n; j5 p7 I( h" b( a$ t% V
  266.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    + q. X3 l  [8 Q. m" y+ M
  267.   GPIO_Init(GPIOA, &GPIO_InitStructure);% X; y8 x4 l0 w# ^& L
  268. : f! H1 h0 k! j1 W& J- d+ \
  269.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    ( f* g' W! N# r( H- ^( h# C9 K
  270.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值' M7 ]5 y9 w, b% v; ]- y1 B, M
  271.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim% I- N+ r+ t: I. U/ @: C) ^
  272.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式' Y2 D6 k9 F+ a, c3 t
  273.         TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位+ U) n* U; Q. a- R$ B
  274. ' a. y% R/ d6 d. n1 m- ^

  275. - z  Z0 W9 |# O! V
  276.         /*配置中断优先级*/" |) ?8 k9 b% U8 ~* j6 V+ v
  277.         NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
    ( X  T# l$ V6 w) ^2 x/ i
  278.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    / H, w9 C6 a5 P2 N9 F, }8 B
  279.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;- |$ h% J0 A! c( d' x' V
  280.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;% k; U( M+ g# R
  281.   NVIC_Init(&NVIC_InitStructure);
    & ~  ^. Y# K% A2 P7 D0 `

  282. " A/ R2 q( h7 v. o; \2 T
  283.   TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;
    . C- w" b' ~$ W6 _9 f' i
  284.   TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;9 K. o# @6 h( ^7 v% |4 p
  285.   TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;0 m* ]- @7 b) N5 D4 h2 E$ n
  286.   TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;# d: O" S2 ^# S2 f% s
  287.   TIM5_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤
    7 E: h  G. A1 e' I( d: m+ u' D5 N
  288. 9 F3 A8 Y; e6 M; |' Q5 g
  289.   TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);     //PWM输入配置2 r% L  \5 `, v9 O: G1 F, d
  290.   TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);     //选择有效输入端. g3 V) W) f. d1 E6 Z
  291.   TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);  //配置为主从复位模式
    & K% E# C( V# ]% `: J; D  o
  292.   TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发/ j0 `7 J% x- E2 ?/ M
  293.   TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置
    # U7 Q* j+ d7 b9 T( Q4 k, K% f
  294.   TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位. H+ L+ x2 @& @- [$ R1 ]* F
  295.   TIM_Cmd(TIM5, ENABLE);1 U, {8 o( l0 Q! i# X7 t" F
  296. }$ L4 Z: B6 x; }% B" s! e
  297. ) Q. z6 ]) C5 ^) U8 K

  298. ; ]; |1 M1 m+ ]2 l, e3 T
  299. void TIM5_IRQHandler(void)
    - \* M6 C- [. M9 S; `" H6 e# v& {
  300. {
    7 n5 F! K3 G& }5 c& s2 c: m
  301.         {. {2 }  w. ~" S% {
  302.                 if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    ; w* k1 i8 T% |5 f, X  U; `$ u6 N
  303.                         {
    . c: w) h  ^5 o; ~3 V% o4 V
  304.                                 duty_TIM5    =   TIM_GetCapture1(TIM5);          //采集占空比
    " V' m6 G5 B" N& N. `
  305.                         if  (TIM_GetCapture2(TIM5)>600)         period_TIM5        =        TIM_GetCapture2(TIM5);2 x7 t/ a& K: f' J) y/ h
  306.                                 CollectFlag_TIM5 = 0;
    * W& @5 m) I8 M% V' ^2 ^
  307.                         }
    # K- o5 f( s# b) G, V; J, B- w& g
  308.         }
    + _. q1 @6 T/ L; ?& }
  309.                 TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位; r! V% z  ]8 ^6 g9 P' J! h
  310. }
复制代码

6 Q( v3 t# c" C$ Q1 d5 O: j; ~3 Z
% v) k' A' c: Q) a( J# vPID部分:3 \; W* P- O" f8 d; B: B8 p
准备部分:先定义PID结构体:+ C- R! a9 f" B. m8 y8 ^  y9 G0 R
  1. . Y: K  D; d* X* k# \2 Z+ Q3 D
  2.     typedef struct
    ! {% l1 |# K4 |4 F2 ?
  3.     {
    # Y5 E+ k1 l0 ~$ E* B3 K
  4.     int setpoint;//设定目标0 b, {8 e7 X$ X
  5.     int sum_error;//误差累计4 }* E4 Q. }, H7 Q
  6.     float proportion ;//比例常数& r1 l. ~( q2 K; s
  7.     float integral ;//积分常数
    $ w: Y. F+ F* B% X
  8.     float derivative;//微分常数) {; e. b/ ~8 K, r! {1 J+ ~. a( H
  9.     int last_error;//e[-1]
    - D$ y4 Q+ h# e8 [
  10.     int prev_error;//e[-2]1 [3 O9 c/ b7 L
  11.     }PIDtypedef;
复制代码

9 X& u1 V* G, j# w; r8 P9 M/ v
4 T# B" K2 y$ p) z. S* s" V6 I* {, I8 S
这里注意一下成员的数据类型,依据实际需要来定的。
1 U4 A; B  i3 O+ V0 D3 x' A5 h5 @在文件中定义几个关键变量:
5 Q, f9 `! R' {, M4 n' ^6 v
  1. float  Kp =     0.32  ; //比例常数
    6 @# l& }! o- M+ X' I- m3 _' N1 P" `
  2. float  Ti =                0.09 ; //积分时间常数
    8 l8 ?, i8 z6 o; S# y. I
  3. float Td =                0.0028 ;  //微分时间常数
    ) @) ~5 n6 M$ l& d5 I
  4. #define T                  0.02 //采样周期$ X* u0 @& g5 A% @& x
  5. #define Ki     Kp*(T/Ti)        // Kp Ki Kd 三个主要参数3 l9 _" z1 B4 O* k# N; x* P2 x- `
  6. #define Kd                Kp*(Td/T)
复制代码
1 q# C0 X8 y3 K! k+ f
" A, T1 k% j0 L% [" l7 G6 A0 Z: p
C语言好像用#define 什么什么对程序不太好,各位帮忙写个优化办法看看呢? 用const?; }- |- X9 k8 ?( i+ M, w' c% f

" d4 j% B# x3 f+ gPID.H里面主要的几个函数:, V. ~/ v6 F4 `, O; C- n1 C+ H
  1. void PIDperiodinit(u16 arr,u16 psc);        //PID 采样定时器设定* u) [" h  i+ e3 R  o  P
  2. void incPIDinit(void);                //初始化,参数清零清零
    % q0 w7 X, s3 A1 T6 |( T! r! |; d, Z
  3. int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint);           //PID计算
    4 r  L! U+ z2 H' V& m! ]2 B: A1 r2 T& f
  4. void PID_setpoint(PIDtypedef*PIDx,u16 setvalue);  //设定 PID预期值
    9 w' B- g& e; r  ]- Y
  5. void PID_set(float pp,float ii,float dd);//设定PID  kp ki kd三个参数
    : X7 I4 G3 Y0 N+ p
  6. void set_speed(float W1,float W2,float W3,float W4);//设定四个电机的目标转速
复制代码
/ T6 _9 h9 ]7 N3 l& h) p8 {0 X6 I4 L
% x0 Y4 @/ p6 Q: h
PID处理过程:3 \8 [0 R- A, i2 d  c& j
岔开一下:这里我控制的是电机的转速w,实际上电机的反馈波形的频率f、电机转速w、控制信号PWM的占空比a三者是大致线性的正比的关系,这里强调这个的目的是9 l% E; q! P. s9 x
因为楼主在前期一直搞不懂我控制的转速怎么和TIM4输出的PWM的占空比联系起来,后来想清楚里面的联系之后通过公式把各个系数算出来了。

5 ?3 F* i2 q1 y% G/ G8 T/ Y4 c6 }. V3 A
正题:控制流程是这样的,首先我设定我需要的车速(对应四个轮子的转速),然后PID就是开始响应了,它先采样电机转速,得到偏差值E,带入PID计算公式,得到调整量也就是最终更改了PWM的占空比,不断调节,直到转速在稳态的一个小范围上下浮动。: m3 }1 q: D) A: `6 A$ U5 E
上面讲到的“得到调整量”就是增量PID的公式:# s. G% X5 y9 Z' R  w
  1.     int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint); I: B! ]. a2 V* n/ b  y
  2.     {5 o8 s* G5 }) }  j* W5 H
  3.     int iError,iincpid;
    5 `0 _9 T# ~) D, W+ L
  4.     iError=PIDx->setpoint-nextpoint;  //当前误差9 P0 I% m" x( T# j; O; e# N: z4 a
  5.     /*iincpid=                                               //增量计算; b5 M' I$ q$ |8 p
  6.     PIDx->proportion*iError                //e[k]项1 s+ l3 V/ V! c* E
  7.     -PIDx->integral*PIDx->last_error          //e[k-1]! n8 ~8 n2 X4 D
  8.     +PIDx->derivative*PIDx->prev_error;//e[k-2]
    ) M5 g4 p: Q+ U5 d5 k1 ~. \
  9.     */. Q* w) x) F- o- K4 P
  10.     iincpid=                                                          //增量计算( d, S  W/ U* s2 s% [9 R
  11.     PIDx->proportion*(iError-PIDx->last_error). f0 g0 S% b, d% O5 ^7 Q+ k
  12.     +PIDx->integral*iError( m1 S- f0 H' B% V; v# j
  13.     +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);, I. a2 Z" h+ t( E$ O+ m$ r1 K
  14. * G7 D( W8 M, F# C& |- T
  15.     PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算) C) b5 D' M$ w, _! n) d
  16.     PIDx->last_error=iError;3 [8 v# }) O9 R* ]
  17.     return(iincpid) ;
    ! H  |! Y6 b' Y9 H7 f6 P) T
  18.     }
    & ~+ d! I( ]; u, _8 ~% l

  19. . t- Q, U& R2 ~! g% e
  20. ' C* X' p- J; ?/ d4 O+ a+ P
  21. 复制代码
    9 g, d8 ]! g- }" g3 R

  22. 2 G  L; B& g2 T" R( q8 T
  23. 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
    1 \0 e6 u9 {9 P5 z. `) w
  24. 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)$ k1 ?' ?$ N! I4 _& b# w' c
  25. 相关代码:
    0 X/ N! B0 ]) A: L$ y% z# h
  26. - d1 J; w* X' V2 y) R: [

  27. & O$ \6 `2 i8 E! ~5 ]% t9 q
  28.     void TIM6_IRQHandler(void)        //        采样时间到,中断处理函数
    7 }! M; H! Q# p' a7 d
  29.     {
    * y5 [4 g5 |9 ~3 M7 L+ w! W) l

  30. 8 X  \& ^9 `7 }9 y
  31.     if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断
    ! ]4 {" h, d8 l" X1 P; G3 x, m/ R1 |
  32.             {
    6 q1 V$ C$ c+ X4 v" \
  33.             frequency1=1000000/period_TIM4        ; //通过捕获的波形的周期算出频率' O7 }( q; u& p" ^% ?! P$ \
  34.             frequency2=1000000/period_TIM1        ;
    ' @4 g7 ?' J' H: {  P( D  ]. k6 t
  35.             frequency3=1000000/period_TIM2        ;1 @) H: Q3 Q& c! W' t/ k/ p/ ?6 y
  36.             frequency4=1000000/period_TIM5        ;" G3 x3 C  @- P0 e
  37.     /********PID1处理**********/
    0 I/ C: K" a" q
  38.             PID1.sum_error+=(incPIDcalc(&PID1,frequency1));         //计算增量并累加2 q4 [, B* x1 K) ]$ u& K9 P
  39.            pwm1=PID1.sum_error*4.6875  ;   //pwm1 代表将要输出PWM的占空比
    # c/ Y' s) D. v9 ]
  40.               frequency1=0; //清零
    # F0 P4 m  C* X* H
  41.          period_TIM4=0;
    . y" m* @4 e& {
  42.     /********PID2处理**********/& h7 K+ u  I; q, n- J( `
  43.              PID2.sum_error+=(incPIDcalc(&PID2,frequency2));         //计算增量并累加  Y=Y+Y'
    9 Y( A8 s, d* D, A& n- u
  44.              pwm2=PID2.sum_error*4.6875 ;   //将要输出PWM的占空比
    5 ]1 A  S$ \! j2 I
  45.             frequency2=0;4 [  X8 ?! N, X6 h4 P
  46.             period_TIM1=0;& \( x7 u8 |& }% E( t5 x
  47.     /********PID3处理**********/7 B+ c0 S' G5 |  z  r
  48.              PID3.sum_error+=(incPIDcalc(&PID3,frequency3));          //常规PID控制
    8 L" y3 Z' c; f
  49.             pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比
    4 B# E) q9 }) `  |; i
  50.             frequency3=0;0 H+ \9 [6 s5 W/ A3 S5 q
  51.             period_TIM2=0;; z6 N5 }9 b7 G9 H6 |" h
  52.     /********PID4处理**********/. G- H( t% M! _( r. g7 k3 A+ L
  53.                 PID4.sum_error+=(incPIDcalc(&PID4,frequency4));         //计算增量并累加
    . b+ n. ~% j: ]0 c; D4 h4 _/ [/ @
  54.              pwm4=PID4.sum_error*4.6875 ;   //将要输出PWM的占空比7 c0 D; \1 Z9 p3 J3 ]& F% O
  55.             frequency4=0;# y* H- R, t1 t% e; S% E9 e, T
  56.             period_TIM5=0;
    3 B* s2 C2 g0 B& x) z
  57.               }( ^, |2 X/ s5 H3 s( j$ e: T
  58.     TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);             //重新设定PWM值
    % A; {. t1 \. q9 ?. t/ ?# v0 R
  59.     TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位  z4 O5 q8 J% }6 |0 w! _
  60.     }
复制代码
, l! W2 L/ Z6 O

- Y, Y; [2 k0 l. W# t$ y7 {4 f上面几个代码是PID实现的关键部分) ?8 @/ b& N. R6 i0 i
% ~9 O. y- [, L- f: x
整定过程:9 ?. p. Q" ^3 ^9 n
办法有不少,这里用的是先KP,再TI,再TD,在微调。其他的办法特别是有个尼古拉斯法我发现不适合我这个控制对象。9 N0 w) Q/ R2 Y  J  `
先Kp,就是消除积分和微分部分的影响,这里我纠结过到底是让Ti 等于一个很大的值让Ki=Kp*(T/Ti)里面的KI接近零,还是直接定义KI=0,TI=0.' k) A3 l4 ]5 M) j9 g
然后发现前者没法找到KP使系统震荡的临界值,第二个办法可以得到预期的效果:即KP大了会产生震荡,小了会让系统稳定下来,当然这个时候是有稳态误差的。& H" _3 ~2 f. w0 U* i, g  m
随后把积分部分加进去,KI=Kp*(T/Ti)这个公式用起来,并且不断调节TI 。TI太大系统稳定时间比较长。
' V( z; k  c, E$ d# o然后加上Kd        =Kp*(Td/T),对于系统响应比较滞后的情况效果好像好一些,我这里的电机反映挺快的,所以Td值很小。
4 a  Q% \+ R+ ]- o: K5 u* k最后就是几个参数调节一下,让波形好看一点。这里的波形实际反映的是采集回来的转速值,用STM32的DAC功能输出和转速对应的电压,用示波器采集的。+ A+ R5 z3 @6 o( L) |
最后的波形是这样的:
9 h) s/ R9 s! I 3.jpg ! h1 M0 _2 O) J3 Y$ i  h; }5 h
最后欢迎大家拍砖,有批评才会让我更加进步!
* q: x% R- o: g; F/ @最后把PID文件放上来 pid.zip (2.88 KB, 下载次数: 134)
赞 收藏 5 评论17 发布时间:2017-12-26 15:40

举报

17个回答
zero99 回答时间:2017-12-26 16:11:12
大佬,这样插代码效果更加哦
& y3 N9 K1 |9 Y4 f9 A0 |1 @9 W, w& a; \
  1. 测试代码
    6 e1 u2 j$ Y/ F* N5 ?4 L, ^8 h
  2. 测试代码
    5 s* U% l! e& z
  3. 测试代码  [9 O8 d3 O, P& U0 A* J2 s
  4. 测试代码
复制代码
: n0 m& D' @" O7 W9 }7 v3 A" S
/ c$ K' K& E6 V6 u* |; a, t4 `
333333.png ) y' F3 {* \; Q; c
二子 回答时间:2018-10-3 11:10:48
nyszx 发表于 2017-12-26 15:41
" r' U! U* t9 R" `: n9 \, w再付个网上的资料
. _! Q0 L& z6 ~$ [3 w7 m) j" O
这是个好东西
) q1 p$ E& U8 l% i0 d书到用时方恨少9 g9 \) h" |2 @8 S3 F0 T5 Z/ Z& Q
果断收藏
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
学习了,谢谢!: Y2 h# g0 j. r
板子粉丝 回答时间: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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版