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

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

[复制链接]
nyszx 发布时间:2017-12-26 15:40
本帖最后由 nyszx 于 2017-12-26 19:31 编辑
- `# @) V8 M; y5 |! x, ^& w8 u& m6 ~8 W9 K0 Q: c* J5 I! u
原帖:【分享】增量式PID的stm32实现,整定过程0 |( }2 m7 S) @6 e3 e
原文网址:http://www.amobbs.com/thread-5575823-1-1.html! ^2 Q) s  T3 r! Y, J' w0 O
出处:阿莫电子论坛 & X3 ^5 r0 M* R8 B
作者:tim4146
+ B9 ~9 K4 B0 i  Y' {  [感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。" L2 I+ K6 O. }0 t
也下面我分享一下近期的这些工作吧。欢迎大家批评指点~
/ B' j. c# ~4 z( N5 \6 P$ c
6 n3 A$ i7 k3 n  F5 t首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。
! k( N: \& H: k0 J资料上比较多的是:
( F; G2 l% m8 t  X, v 1.jpg
7 i9 B" \$ D+ M% s还有一种是:
) M1 s. W8 ?$ x6 u6 V) d 2.jpg
6 f+ H: B0 Y* R# j- a5 A4 l  I感觉第二种的Kp Ki Kd比较清楚,更好理解,下面介绍的就以第二种来吧。(比例、积分、微分三个环节的作用这里就详细展开,百度会有很多)
/ N( _; ]8 l/ U( a
; R/ x$ p3 x' r7 ^+ J7 s硬件部分:
$ |9 ~# H/ |+ ^0 m控制系统的控制对象是4个空心杯直流电机,电机带光电编码器,可以反馈转速大小的波形。电机驱动模块是普通的L298N模块。( E' o1 {% D+ x1 _& b
芯片型号,STM32F103ZET6$ G4 R% l$ }1 P" O1 b

+ D" _- C+ \9 `3 j% [软件部分:8 e6 F0 n* g; p! ]0 b% c0 O6 U
PWM输出:TIM3,可以直接输出4路不通占空比的PWM波) a/ M) L3 e- J8 Q
PWM捕获:STM32除了TIM6 TIM7其余的都有捕获功能,使用TIM1 TIM2 TIM4 TIM5四个定时器捕获四个反馈信号
# ]1 t' ]6 u' e9 s2 yPID的采样和处理:使用了基本定时器TIM6,溢出时间就是我的采样周期,理论上T越小效果会越好,这里我取20ms,依据控制对象吧,如果控制水温什么的采样周期会是几秒几分钟什么的。  {# t: h, m7 i
% T2 h0 o8 P3 s# d" D& d5 ^" f
上面的PWM输出和捕获关于定时器的设置都有例程,我这里是这样的:
6 i1 S5 u& U2 Q9 |6 BTIM3输出四路PWM,在引脚 C 的 GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9输出$ |2 R1 C: y1 c$ y8 p0 Z; ]5 @) X
四路捕获分别是TIM4  TIM1  TIM2  TIM5   ,对应引脚是:  PB7 PE11 PB3 PA1
$ A  u: M& N/ z4 \# S. j) q! G
高级定时器tim1的初始化略不同,它的中断”名称“和通用定时器不同,见代码:
3 K' X: A! j* {
  1. /*功能名称IM3_PWM_Init(u16 arr,u16 psc)1 x4 o/ M+ |) l+ q
  2.         描述      TIM3产生四路PWM
    3 g: g# q1 Z  ~: q+ n) K! a
  3. */- C7 v. s( C& y# j# W
  4. void TIM3_PWM_Init(u16 arr,u16 psc)
    / W" ^6 Q! X8 X0 i  `+ j
  5. {3 v' P) Z6 H/ O( m; V2 M. S8 j. I2 B
  6.         GPIO_InitTypeDef GPIO_InitStructure;1 _8 K! I+ C7 I6 [- r
  7.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    : g  M' c0 J/ O( [
  8.         TIM_OCInitTypeDef  TIM_OCInitStructure;
    ; N; Q- N+ ^/ {& r! w' r

  9. 7 q. Z: L: R+ q0 O/ G
  10. ! q/ @+ w* r5 d. F
  11.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    4 ?: J8 ?4 k6 }# e' R# m
  12.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能
    , r& r; h; q* J1 K; ?

  13. $ t3 J! j. j2 O" e* i, x; H" o
  14.   GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示
    1 K" Q; K9 I" R  X& G
  15. ' w- l, _5 z5 c. s& F7 ~* M8 y3 W( O
  16.    //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形9 ?8 i4 {0 ~, @7 T1 d& }
  17.         GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO1 v- q6 U2 P2 J# X
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    - h& u8 g9 }& }* f" R
  19.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    6 S( ^, S( q' Y1 `& v
  20.         GPIO_Init(GPIOC, &GPIO_InitStructure);# p2 d8 o. U% Y* [2 d
  21.         GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能, s0 h3 A% l, ?' U

  22.   U9 z1 R$ h- i/ G/ f5 e  j
  23.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值- {, i2 d# |% B5 {
  24.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M! w0 E7 y5 W) I' T- J/ }* s; m6 ^
  25.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    . Z# H+ ]" Y  D1 p/ i! I! \
  26.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    8 M. S/ t' B. _3 C
  27.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    - R3 ?$ o6 m3 |4 G3 X$ C6 G
  28. : o. J! F3 n+ [2 B, C' S, l/ L. X

  29. ; _& |- w; l& \' t  I) L
  30.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式11 ]# A9 a- G. X# _# ]2 L% Z, `
  31.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能7 k9 f6 {; ^3 a% C% y2 y6 p
  32.         TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
    9 L9 s+ Z1 @5 F( C# B
  33.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    # J$ [, K. \/ k, x3 x
  34. 0 n; A' f2 \" F( {+ e/ o
  35.         TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    # ^7 G' f; c8 g) D* ?( o; _
  36.         TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR1上的预装载寄存器) {8 y9 Y9 Z3 @5 X5 }1 S
  37. 7 t  G; ?' C! x0 A5 a& C
  38. 0 i6 U2 }& O& j/ j/ ^& ]1 r
  39.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    . D, b. n9 A% ]$ @. j. X' [9 r$ L
  40.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器% [# e, H6 B7 R3 a" ~& L0 |& A/ P/ ^- a+ p

  41. 0 K' n" N! q$ S) b. \" G
  42.         TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx: z0 o& V) j0 [" s3 C& A8 `& h/ |
  43.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR3上的预装载寄存器4 V4 G$ u3 j: i" x; a  J4 `, S
  44. 2 X4 @- \. q6 f9 r
  45.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    - p: G4 d% E. S/ u% c1 z. r2 a
  46.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR4上的预装载寄存器
    & T- T. a5 ?: C7 v' ]: m

  47. 3 s( m. L- @; j  F% T
  48.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
    ) \! P: @# S& a8 b, E6 c( W

  49. / ?) d3 H; ]' E5 n+ I" u5 c

  50. 4 u2 Z% ?4 v1 |( ?% Z) `) ?
  51.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设- `" E, r' q  _( Q: _
  52. 6 X) Y, C) }. O8 l8 o

  53. ( G, W8 R' d( U
  54. }
    ! i3 L2 p5 [; P

  55. ; s( F. k, j" \2 n) Q0 f9 ^
  56. ' W# `0 d6 B% R5 G/ |' Y/ z+ b4 v

  57. 1 Z1 `; |; Q) U- S$ G5 @6 M
  58. /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)) J9 ]+ G( q, l% I% P& H; v
  59.   描述      PWM输入初始化*/
      Z; A+ c5 i' e( T( {) F1 k8 z' W+ @

  60. 0 e  n: q6 C' `- E! k3 f
  61. void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)" ]4 _3 m  g' S" d
  62. {
    - s/ [- r  i& M  n# K) e' k
  63. & Y0 \: E1 v& f" E$ D& L, ]& u
  64.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体
    - w- O: |0 G) r' m1 _' h0 [
  65.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置
    2 Y& S: i: z. [  ?3 h2 j6 t
  66.         TIM_ICInitTypeDef  TIM4_ICInitStructure;                 //TIM4  PWM配置结构体  b9 ~$ }& b/ t' U
  67.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    * O$ b7 O) f! L' g

  68. + t! }/ f9 t; M7 W
  69.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);     //Open TIM4 clock
    / I( \/ ?- M+ d
  70.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock
    ! U- A7 O  h, t1 u: i' z6 U/ I# J# b
  71. " N7 Y$ _# L8 v& D  {& Y; {& s
  72.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //GPIO 7
    , @" `7 m! j1 F6 Q: ?5 o3 Z7 S
  73.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入! F1 w- m7 f- W2 b7 d
  74.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 B" w2 k4 y6 f, w4 `
  75.   GPIO_Init(GPIOB, &GPIO_InitStructure);
    9 L; g0 E) e  ~0 D; d# t, s. F% g

  76. 2 }4 V# v" U% u
  77.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值( w6 u- A' d0 |2 J) _( K: C
  78.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值4 Z/ U; Z- @9 y5 [
  79.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim$ P- C, y6 K2 T% w  l
  80.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    / i4 C. ]# Q6 ]3 d2 ]$ q
  81.         TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    ( y( y2 a: \. J4 g  M* f) a

  82. 3 i3 _' x# h  G2 g1 N# m

  83. $ q, \" b# h; V3 q( J6 z. t5 \5 O
  84.         /*配置中断优先级*/; ]% w( i. a% _8 Y( u4 ~1 a
  85.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;' l* l% s/ E8 D, d8 R
  86.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    $ A+ h5 n+ T7 o- w: S, C
  87.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    " d# m. R4 j9 [. Z, V. r
  88.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    . B. `6 S: J$ ^) x
  89.   NVIC_Init(&NVIC_InitStructure);4 T8 M* G- c  u+ l8 B
  90. 4 ^  u1 m0 e( ^+ J/ X
  91.   TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;, D; i9 c4 H4 B" O9 U
  92.   TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;: K) u# m& b5 ]  ?6 k
  93.   TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    , `* }2 c1 X8 }0 E! z+ `$ [
  94.   TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    ! Y% x' b) p# ]
  95.   TIM4_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤
    2 ?5 W0 P8 B' b, }

  96. * y" d$ M: u* j3 g- K8 d6 M1 E
  97.   TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);     //PWM输入配置' C$ c0 E! }  X# {+ b9 G" [" U  r
  98.   TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);     //选择有效输入端: N( T& }9 H  Y' h* S
  99.   TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);  //配置为主从复位模式
    2 c* V# p" w; c
  100.   TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发' p- g& B5 M# T. M1 }( [6 X6 r
  101.   TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置6 N- T$ d' h% ]% O5 h& U* g# |
  102.   TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    $ \+ ^& A& s$ J( Z% S+ ?( ?1 f
  103.   TIM_Cmd(TIM4, ENABLE);
    6 G, D+ K4 T( \1 j" d2 G
  104. }
    ) Y: w" p5 e- `1 D4 \- [
  105. ; }' l+ }8 a# p+ N/ u
  106. ! R" J4 u. Y0 G( G, h. |, C$ d8 C5 p
  107. void TIM4_IRQHandler(void)
    - q# U2 @6 A6 q
  108. {
    ! }3 c3 ?. R! T/ T% N

  109. % X( {) M  ?6 f7 c3 n2 Z  o
  110.                 if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件6 T; Y8 C( W9 [; g* v& H
  111.                         {
    ) `; \1 I" ]5 h
  112.                                 duty_TIM4    =   TIM_GetCapture1(TIM4);          //采集占空比$ M; x* A1 q" B+ \
  113.                if  (TIM_GetCapture2(TIM4)>600)         period_TIM4        =        TIM_GetCapture2(TIM4);//简单的处理
    # W3 f. V+ k2 Z$ e( Z
  114.                                 CollectFlag_TIM4 = 0;; m) C. Q" L( U( y& C5 h# h
  115.         }5 W! H% i& D: F) W2 d" V1 e
  116.                 TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位$ `* ^. p! D; ]$ ~% L0 f
  117. }
    5 A% W0 z# R1 [* _

  118. # I0 q" u' S7 P& ]- P% L

  119. 4 F* `2 n% e/ E  e. e) x
  120. /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)* B5 c4 Q4 D, ?' f2 F
  121.   描述      PWM输入初始化*/6 ~( c, m! N# \% {5 s7 i5 K: B1 ]

  122. 8 j) ?$ Z  D8 Q7 M/ M" |* p
  123. void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)% B6 N- z: k3 A0 s) f- x; K
  124. {+ G1 r; _7 I  y/ j) H* K1 U' D

  125. - v) p- i* n) }3 B; O
  126.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体' Q: \# q! Z0 v
  127.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置. k5 T& P& Z7 x# h2 E
  128.         TIM_ICInitTypeDef  TIM1_ICInitStructure;                 //PWM配置结构体
    , ]  [) E" r8 v% J
  129.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体; R  T1 f' N+ X! V
  130. ) ]; l9 |/ H7 e  Q
  131.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);     //Open TIM1 clock
    - B# t2 q  f( Q0 h
  132.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //open gpioE clock
    ! e/ e- D: i6 I, y/ C
  133.    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射  TIM1_CH2->PE11, v7 |8 [2 w) L6 _+ Z4 l# K
  134.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;             //GPIO 11* X* N+ V0 k; R6 F) R) @4 w. l5 x6 L: q
  135.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入7 s0 f. e, X1 A, N
  136.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    . U: n6 z. Z7 p& B" a+ I- h
  137.   GPIO_Init(GPIOE, &GPIO_InitStructure);
    9 {# o) L$ J1 x# H, N7 h0 B

  138. 7 ]& u" S, _+ L0 l! D
  139.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    % t2 \; F4 I6 j
  140.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值1 ]) w6 u6 [0 _6 K3 B. t
  141.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    1 B6 h2 [1 _5 {. ?
  142.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    7 ?% o! ^8 t* N: ]1 D
  143.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位" k( H4 _  ^, R" E2 b( {

  144. 1 g" Q2 |; l" e' k+ Z* \& U

  145. , K! Z" o+ G8 ?. X# n4 K) c5 [/ h
  146.         /*配置中断优先级*/
    & z3 D, @  x! @; ?! i  K9 x: g8 C" D
  147.   NVIC_InitStructure.NVIC_IRQChannel =  TIM1_CC_IRQn;   //TIM1捕获中断
    / {: K7 E$ Z3 Z7 D
  148.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/ Z1 n( r. n7 }8 i$ T
  149.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;1 M6 h9 A8 E. h% b' e+ W( ^/ b
  150.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/ M" i+ O, |/ Q8 @4 o; o
  151.   NVIC_Init(&NVIC_InitStructure);
    + _, I# {" t4 @) S" R) ?6 `2 b, Z* g' [
  152. 9 R: W: @, b6 y
  153.   TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;$ A6 P5 O. T; V( B3 s* q
  154.   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    ( O. |7 f0 ]9 _/ [+ ]: W$ a
  155.   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;2 T' [! M3 E" [: v5 z
  156.   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    ' C+ t; @3 F/ V8 C' a) \% e' [
  157.   TIM1_ICInitStructure.TIM_ICFilter = 0x03;   //Filter:过滤
    % [' v. H% e1 `1 e
  158. 5 T! {- C% p. ^( Y6 H+ {7 T7 {1 z
  159.   TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);     //PWM输入配置2 g' g3 h* \7 H8 ^+ x1 R
  160.   TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);     //选择有效输入端! f1 u$ {. F/ [' X, w" I0 [
  161.   TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);  //配置为主从复位模式' ~' P! C: q. N9 C3 G) l
  162.   TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
    ; a. w5 D1 W3 ?( O
  163. // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置7 P# m3 A+ J; n
  164.   TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开
    . d  v0 K1 K* g8 t/ J3 P, J
  165.   //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位0 ~& n3 C5 y, l; g) ]0 @: g) f0 f
  166.   TIM_Cmd(TIM1, ENABLE);
    , [  b( K* L1 W. E
  167. }
    - R2 r( O0 ~+ j9 w

  168. 7 {, q  v5 o& K# J4 h+ `0 T$ w" R
  169. , c# f3 V: X. M0 t
  170. void TIM1_CC_IRQHandler(void)$ A' @1 O0 }2 }1 u
  171. {  f, K' _% l: @, f

  172. ' x/ O" g( R+ `4 r/ \4 c  t5 a( e
  173.         {
    + X7 k+ I, F8 y- Z: i$ M* N
  174.                 if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件. X( Y) {: L$ ?
  175.                         {
    ) H: ^1 C$ B# k5 y0 \4 a2 }* s5 {
  176.                                 duty_TIM1    =   TIM_GetCapture1(TIM1);          //采集占空比
    ) H4 Z6 V+ U* P* v. a
  177.                            if  (TIM_GetCapture2(TIM1)>600)         period_TIM1        =        TIM_GetCapture2(TIM1);, m; W' Z2 _% T/ t
  178.                                 CollectFlag_TIM1 = 0;
    / t# q# a0 G: G4 f
  179.                         }8 y. M% \7 T! A3 c  w( c% ?0 c
  180.         }
    ) Q8 d( ?3 W* }" x
  181.                 TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位- O: B' x* C1 Q
  182. }
    ( Q& J+ ]1 {& s! w' g; y, g

  183. ( v' z" C- c2 N" G2 j
  184. 3 M! I" @$ ^4 A4 n. n
  185. /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc)# `7 C2 E( F" c( m' L2 A* P" t* X; [
  186.   描述      PWM输入初始化*/
    ) h) j# E( P' r# W( }; G7 T

  187. / d* x5 E0 {" Z+ r# B) G+ f  L" W
  188. void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)5 P& ~! H& V+ m, R9 t: c
  189. {
    3 a9 D0 J  q: C% ?
  190. , e( [) w% ~0 ^" w- F
  191.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体9 c9 c7 `( x8 R! x8 t" L+ W  x) N
  192.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置. g) @7 l8 p8 H( `& ?
  193.         TIM_ICInitTypeDef  TIM2_ICInitStructure;                 //TIM2  PWM配置结构体
    * j  Z  t+ o( T
  194.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体
    / n- z7 M/ e9 M+ C! z5 W3 ]
  195. : E. ~: s! Z3 ^: p7 z7 X
  196.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     //Open TIM2 clock
    " S9 E8 {, ^0 `! c4 K: Z
  197. // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //open gpioB clock5 M5 ]% s' v; p& G3 D$ U: i+ ~0 G
  198. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟2 ]  D2 ?, ?6 h4 ^8 g- l
  199. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);          //关闭JTAG
    + u) d$ J! o) p1 {. ~5 }1 K  d
  200.          GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射  TIM2_CH2->PB3% W$ j$ Y2 E3 a0 |: M3 `7 ^3 L7 V
  201. 7 t) v8 s( a+ K! ?1 w0 f
  202.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;             //GPIO 3
    - g% [, P$ h2 G$ K
  203.         GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入4 g+ v) A: s* }! l2 P8 f3 a/ {
  204.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;) x2 E) v# o2 o; j
  205.         GPIO_Init(GPIOB, &GPIO_InitStructure);
    . {; r: G9 {, c, ~- h0 g
  206.   M3 N% a2 j: p) G: k
  207.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值4 G! Q: E$ \/ P
  208.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值1 r' h# @) T; c, |( ]3 w
  209.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    : I1 _8 s& C/ a
  210.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    ( k" ~  i4 [/ _- c
  211.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    4 J$ G, x: h. E2 T% U3 w7 S

  212. 6 z  M" E' L+ h' k3 I

  213. $ Y6 N7 I/ A" a5 Z8 {" C8 N) h
  214.         /*配置中断优先级*/
    ; y  S: g: \: h$ t9 r$ H! o/ w
  215.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;( w) T4 Z0 O2 B
  216.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;' V$ T5 T- E. s5 f: r) q
  217.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;5 t% E* i+ P, }0 E9 a' [( e( N, [
  218.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;! _4 ]. m) Y' n5 ]% j5 D. d8 Z" v+ O
  219.   NVIC_Init(&NVIC_InitStructure);8 U& f  h/ @& d- G: H
  220. 6 z% Q9 \) d6 D5 ?0 a) l& N
  221.   TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
    6 o, P: I! ~' s" x' a* n* e
  222.   TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;, h* \. D0 S% L  \, {# p  J
  223.   TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    % O6 [! Y( p4 X) @/ n1 R' F1 ~. m; D5 N
  224.   TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;. v8 K: f$ d: `- a
  225.   TIM2_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤
    0 X6 F; T% L1 e. U! m7 D' M0 K
  226. : W! ~6 F, o4 q- [! \
  227.   TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure);     //PWM输入配置; w6 d5 P5 ^1 k* I3 C+ n: U
  228.   TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);     //选择有效输入端
    ) G3 O) |2 A+ ^9 G' e, R
  229.   TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);  //配置为主从复位模式+ ?5 Q; I2 u: G$ [9 V# V
  230.   TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发2 O+ J+ @/ H1 g4 C) |3 z
  231.   TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置' N$ O5 Y$ }* Z
  232.   TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    3 ]8 q  j+ g( c. o& u
  233.   TIM_Cmd(TIM2, ENABLE);+ A/ d/ U4 r: [1 ?
  234. }! y! z% |# }0 o: M8 h6 _2 z0 R$ K) d
  235. ; v$ ?+ B: v2 _: d- m) E
  236. 6 S, X: ?* M! n1 D& ?/ I
  237. void TIM2_IRQHandler(void)1 C* d! s$ ]9 f; i- G
  238. {
    * ?& z7 r' H1 t
  239.         {. M7 c" S# y3 D  t: p) j
  240.                 if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    6 C+ I/ O, p( Q# v+ N1 `) t( G" ]
  241.                         {8 f4 Q6 V' n2 ?6 a  i4 s
  242.                                 duty_TIM2    =   TIM_GetCapture1(TIM2);          //采集占空比! p1 V2 S! x" m/ g' O4 M9 S( X; Z
  243.                            if  (TIM_GetCapture2(TIM2)>600)         period_TIM2        =        TIM_GetCapture2(TIM2);4 M) l0 H, S5 D
  244.                                 CollectFlag_TIM2 = 0;
    3 A2 \7 D, E, w" V5 a" u
  245.                         }8 y3 l7 a0 W! Z0 `) }6 x9 o
  246.         }" D- ?  Y1 \- Z5 @. T
  247.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位+ U1 B( `$ P5 n: o
  248. }
    ( P2 \0 W( g$ Y+ y1 O
  249. / q0 V* {8 l6 ]# Y  h7 D
  250. /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
    ! R- s9 [  d# ~7 V5 ]
  251.   描述      PWM输入初始化*/
      A* v, S! Z6 k4 J& I5 e4 S

  252. 7 m' B# ^5 d5 S7 a, u/ F' P
  253. void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)7 z8 E$ A% R( U, J6 [8 J
  254. {
    , J( @* `& N( ?8 q# ~

  255. $ d0 K0 ^* {* Z3 `
  256.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;        //TIM的初始化结构体3 _$ P- d9 W) h1 U0 m0 `
  257.         NVIC_InitTypeDef NVIC_InitStructure;                        //中断配置4 [! d0 m  |9 P' S9 s+ m+ @  l
  258.         TIM_ICInitTypeDef  TIM5_ICInitStructure;                 //TIM4  PWM配置结构体
    8 m! Q' v5 F  t6 p
  259.         GPIO_InitTypeDef GPIO_InitStructure;                         //IO口配置结构体& e) X( q9 E9 D! e6 ]+ k5 u! P
  260. " w1 K3 p; R5 F/ k$ r7 g
  261.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);     //Open TIM4 clock- l1 L  m" t( o* k; \3 s1 d7 H
  262.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //open gpioB clock9 d- v# e8 g2 c8 s: F
  263. , J$ q- B9 J& }. y
  264.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;             //GPIO 1/ L( G( |5 v5 P! Y/ |$ w
  265.   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;          //浮空输入 上拉输入
    ) y3 ]+ U1 S  s/ X! g1 \  t5 ^
  266.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 |3 o0 d; i1 u" O. W# h6 g& G$ J
  267.   GPIO_Init(GPIOA, &GPIO_InitStructure);, P8 O2 S5 X# U4 A5 S6 w& b

  268. 8 h6 {2 D$ |, k5 F2 c: I* R
  269.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值3 `0 m7 a5 U: ?3 j
  270.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    + V% ^) @6 v/ ?
  271.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    ( H( F8 o0 f+ j* W' X
  272.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式( G/ o: W5 `" {  |
  273.         TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位; v3 G7 O( {7 I. N: J( [5 L

  274. / c3 Z' A' Q; D& k6 V
  275. & s. ?% v. P6 n
  276.         /*配置中断优先级*/. S# v5 S) D9 B6 \9 C/ A
  277.         NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;5 p, e7 I1 x& M5 O
  278.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    + d7 f& Z# @* I$ b) J4 ^
  279.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;7 ]! _0 h6 A6 @* ~$ t
  280.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    5 W  `; k' g8 Y# G5 o5 g6 T
  281.   NVIC_Init(&NVIC_InitStructure);9 Y; o. x( q6 O; f& t- U  q
  282. 1 @! i; p+ u* {4 J
  283.   TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;; B* T: _& H4 ~& U9 l  s
  284.   TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    - s) v+ {9 Y& h0 B* s9 J( v, `
  285.   TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;1 Z. j) @3 z2 \" M$ p
  286.   TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    9 p/ m. q+ v4 F2 z1 W+ U
  287.   TIM5_ICInitStructure.TIM_ICFilter = 0x3;   //Filter:过滤- G5 U+ l3 d. C  R  W
  288. + K6 G$ |5 w) y* @/ [3 F
  289.   TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure);     //PWM输入配置, }2 Y2 D& [% q( M* `
  290.   TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2);     //选择有效输入端
    0 O7 T  L( L8 e5 i% w
  291.   TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset);  //配置为主从复位模式' ^( b! t6 O4 @# \$ k# }9 y9 Y" v! c
  292.   TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
    + Y# e* ?2 S  J4 k0 @& i2 H
  293.   TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE);          //中断配置" l; _# |* n& z. N  Q
  294.   TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
    4 l+ m; l0 w) I
  295.   TIM_Cmd(TIM5, ENABLE);
    . t. @5 `" [/ _9 T
  296. }2 f5 X5 S) _2 ~7 H; f% ]  B# Z

  297. " f) ]( \5 V6 ?2 U" q
  298.   X5 j- p& |3 m3 i7 Z9 x7 K
  299. void TIM5_IRQHandler(void)
    0 b( H4 m' y+ K. J6 j* ]
  300. {
    / w7 K: ~8 M0 ~+ t; |$ Y
  301.         {. @2 ]- y/ p. a+ Q) z
  302.                 if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    + W; F# Z9 N4 u& {0 K7 s+ j
  303.                         {
    ' h% y4 e6 R: G% t4 j: N
  304.                                 duty_TIM5    =   TIM_GetCapture1(TIM5);          //采集占空比
    1 t( O' W- p9 I, j4 I2 `
  305.                         if  (TIM_GetCapture2(TIM5)>600)         period_TIM5        =        TIM_GetCapture2(TIM5);; \8 k: M8 E0 F
  306.                                 CollectFlag_TIM5 = 0;' I, t( r$ S% ^9 r; h# Q
  307.                         }
    7 d6 E4 J7 @# k3 s
  308.         }/ }7 F& H3 i, u. R
  309.                 TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位4 s" K7 y+ T( _" ?8 L4 ?3 }
  310. }
复制代码
8 S4 Z( n* Z# A- F" f; _
( C5 M. }9 [5 s
PID部分:
$ Z* `0 B" u! ?0 D8 q& z+ K准备部分:先定义PID结构体:8 Q1 B$ n5 H: L* \

  1. * @! s9 j9 K, O) ^; V: P- E( m8 C
  2.     typedef struct* X" o( z3 I; n% V# `+ e" l# @
  3.     {
    & K: q7 x4 g( }3 _6 n+ c
  4.     int setpoint;//设定目标) _: q: T8 s+ Z, N- c
  5.     int sum_error;//误差累计
    : N! f) L- ]; O  [' Q! m9 c
  6.     float proportion ;//比例常数
    0 Y9 a( x8 a: Q2 E
  7.     float integral ;//积分常数. x; {2 X" U( s: p$ }4 G6 a
  8.     float derivative;//微分常数
    3 m' E( {- S, U4 o: z
  9.     int last_error;//e[-1]8 S5 ~; k4 |& O& o) Z7 K3 O3 Z
  10.     int prev_error;//e[-2]
    , ]( S. h8 a: R0 w( ?" M6 V
  11.     }PIDtypedef;
复制代码

- ?7 B. e/ Q" r2 }. U! U" |
& |; p- ^  A/ i+ s  v4 L9 f* G' U- W/ ]/ @' M0 E. I
这里注意一下成员的数据类型,依据实际需要来定的。( P4 d0 O5 l5 h6 C3 W2 s
在文件中定义几个关键变量:
2 M( L9 U1 v, s9 M+ s+ z
  1. float  Kp =     0.32  ; //比例常数
    + _. B- T: `  |: G6 e! n
  2. float  Ti =                0.09 ; //积分时间常数7 I* N* q: [. C6 d9 E
  3. float Td =                0.0028 ;  //微分时间常数
    ! p: w$ J2 r" b1 u
  4. #define T                  0.02 //采样周期
    ) T( @5 h4 u5 n: m6 D7 _4 l# q. o
  5. #define Ki     Kp*(T/Ti)        // Kp Ki Kd 三个主要参数. W& D) N; R5 w9 I0 q
  6. #define Kd                Kp*(Td/T)
复制代码

% \: i% `, \; e4 r9 [" H9 H
' i& F; Y2 l  pC语言好像用#define 什么什么对程序不太好,各位帮忙写个优化办法看看呢? 用const?
8 H. n; |' S4 [3 H2 a2 V
* e  c- I1 {) J6 iPID.H里面主要的几个函数:$ I, \3 T4 |, L, }! S& ~
  1. void PIDperiodinit(u16 arr,u16 psc);        //PID 采样定时器设定
    9 {( Y. ]) T2 u
  2. void incPIDinit(void);                //初始化,参数清零清零' L0 h; e0 m( S8 S) {- Z$ _# P3 ?
  3. int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint);           //PID计算: i) U7 v! i. W/ ~; }6 H2 F/ a; j
  4. void PID_setpoint(PIDtypedef*PIDx,u16 setvalue);  //设定 PID预期值
    - I, z1 d) T& ^" ]" u
  5. void PID_set(float pp,float ii,float dd);//设定PID  kp ki kd三个参数
    - T* e0 I" V# P( G+ k
  6. void set_speed(float W1,float W2,float W3,float W4);//设定四个电机的目标转速
复制代码
8 S& S  h3 W: G! p; d# d7 Q

6 o, ?- B9 C+ ?, o2 y1 k- pPID处理过程:
+ A) b, q8 w2 e* W5 k; K: W# F岔开一下:这里我控制的是电机的转速w,实际上电机的反馈波形的频率f、电机转速w、控制信号PWM的占空比a三者是大致线性的正比的关系,这里强调这个的目的是! E% x) @" ]! _6 \
因为楼主在前期一直搞不懂我控制的转速怎么和TIM4输出的PWM的占空比联系起来,后来想清楚里面的联系之后通过公式把各个系数算出来了。

3 K! F) O- {$ a5 U( N$ ^" r( d% J' m3 j
正题:控制流程是这样的,首先我设定我需要的车速(对应四个轮子的转速),然后PID就是开始响应了,它先采样电机转速,得到偏差值E,带入PID计算公式,得到调整量也就是最终更改了PWM的占空比,不断调节,直到转速在稳态的一个小范围上下浮动。1 A+ M6 N% v# f& S$ P
上面讲到的“得到调整量”就是增量PID的公式:
" F2 B) C- f0 B0 p% w3 `! X" e
  1.     int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint)
    ' Q8 }1 R% h. ?  W4 t
  2.     {0 Z" z4 K6 @* a
  3.     int iError,iincpid;  y% U. `" B) G
  4.     iError=PIDx->setpoint-nextpoint;  //当前误差
    3 F0 V+ C/ m- ^) B- i# i
  5.     /*iincpid=                                               //增量计算
    : a. |7 @3 E0 w- P' ]3 n
  6.     PIDx->proportion*iError                //e[k]项
    * l, f$ d  f4 k1 Q8 G/ O3 k3 ]
  7.     -PIDx->integral*PIDx->last_error          //e[k-1]* ]  r2 d5 \  ~% D  O* b
  8.     +PIDx->derivative*PIDx->prev_error;//e[k-2]; I* ]. G# e1 e5 C
  9.     */1 M6 J1 N6 ^) a8 \3 _
  10.     iincpid=                                                          //增量计算
    2 S' y, g3 `  _7 o0 x* D# W
  11.     PIDx->proportion*(iError-PIDx->last_error)# J) e; n2 ?2 a; b+ ]  K' L
  12.     +PIDx->integral*iError: Q3 O! h. Z1 U. n: W- h, j
  13.     +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);( m7 j9 @  ~( J- s5 r% Q

  14. 5 k! U! `0 i6 |9 G8 y2 C1 A) I8 S
  15.     PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算7 _4 ?( C4 \2 X' H# g' i
  16.     PIDx->last_error=iError;
    5 g: O. k$ C( X" n3 W& q$ b- j
  17.     return(iincpid) ;0 x" d- C: i* @4 ]. e4 P  N: H
  18.     }% g, w4 e3 u% w5 ^5 D5 P5 N

  19. $ @& c% p" E' q  d! i+ X
  20. . C$ w) f9 z7 ]. L, Y; c. `3 ~, h! [
  21. 复制代码6 J+ `  [+ E+ ?7 A$ A% l! T. ~% K: Z
  22. 0 E% k$ t9 y  b4 x* w* k1 S
  23. 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
    $ `; r! b( k# Z* A# v# W3 k
  24. 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)
    * ~% m6 \  S! W5 l
  25. 相关代码:
    $ P; W$ Q6 ]3 t* v; R
  26. 8 }% a4 o- V7 U" f
  27. / d, H) H# C6 D/ T5 B
  28.     void TIM6_IRQHandler(void)        //        采样时间到,中断处理函数4 }- z4 N4 _! [
  29.     {5 b; ~8 d8 w5 E* E5 G

  30. $ A  y( l5 W0 B5 A6 k: p7 L) h
  31.     if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断
    0 U6 C+ c8 R% I$ r
  32.             {
    & {7 B% L1 ^, ^# \
  33.             frequency1=1000000/period_TIM4        ; //通过捕获的波形的周期算出频率
    % U: Z. ]7 D& U8 W
  34.             frequency2=1000000/period_TIM1        ;, T9 p" V3 p, z
  35.             frequency3=1000000/period_TIM2        ;4 K5 v" U7 N$ C% ?2 G% l& s# b
  36.             frequency4=1000000/period_TIM5        ;% p' a2 k6 N/ F1 ?5 m
  37.     /********PID1处理**********/
    7 @3 r2 n6 z( W6 ~+ O
  38.             PID1.sum_error+=(incPIDcalc(&PID1,frequency1));         //计算增量并累加; W4 B+ {* T# d7 S8 k
  39.            pwm1=PID1.sum_error*4.6875  ;   //pwm1 代表将要输出PWM的占空比
    % ^- Y/ q* c4 F4 j9 K
  40.               frequency1=0; //清零
    5 j: o& }, ?. i' x. d
  41.          period_TIM4=0;
    ; v! ]+ i) [& g3 N0 R, Y
  42.     /********PID2处理**********/+ J; X  X% N2 K& c
  43.              PID2.sum_error+=(incPIDcalc(&PID2,frequency2));         //计算增量并累加  Y=Y+Y'% D6 |% {. U  d: Y6 k) O
  44.              pwm2=PID2.sum_error*4.6875 ;   //将要输出PWM的占空比
    + B, n' _; Z6 `/ U- K- G( @
  45.             frequency2=0;
    % @- ?" r5 l/ Y0 m
  46.             period_TIM1=0;
    % D+ P& n- d0 M: e
  47.     /********PID3处理**********/
    0 x. [9 y( J4 R
  48.              PID3.sum_error+=(incPIDcalc(&PID3,frequency3));          //常规PID控制, \( z8 |  T8 R/ L3 a" O
  49.             pwm3=PID3.sum_error*4.6875 ;   //将要输出PWM的占空比- D6 k3 E/ D9 o7 w8 g5 g- @; H
  50.             frequency3=0;1 q. y6 Y& b3 t% p
  51.             period_TIM2=0;6 E! m4 d% t" D" U; `8 w
  52.     /********PID4处理**********/
    / k- y' e5 d/ v
  53.                 PID4.sum_error+=(incPIDcalc(&PID4,frequency4));         //计算增量并累加7 z0 U- p  M4 x/ c) D
  54.              pwm4=PID4.sum_error*4.6875 ;   //将要输出PWM的占空比
    * S( x% M2 g6 m% l+ g
  55.             frequency4=0;
    7 S8 T5 v! c* a7 B- t2 v' U& o, P
  56.             period_TIM5=0;7 C+ Z  q9 p; a
  57.               }7 f% E( |8 c: W+ G7 u, s) a
  58.     TIM_SetCompare(pwm1,pwm2,pwm3,pwm4);             //重新设定PWM值
    9 v. M7 V. P. w* R
  59.     TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位
    2 z, U; U( f9 z1 f
  60.     }
复制代码
2 R; t3 E) Z& y+ [
6 S! o9 r6 ?7 y4 i: B$ \- w$ ]
上面几个代码是PID实现的关键部分, T3 s4 g1 a& U  h7 e

/ d6 ?; X, m% |* k! L; k整定过程:9 n2 x& M! D. H
办法有不少,这里用的是先KP,再TI,再TD,在微调。其他的办法特别是有个尼古拉斯法我发现不适合我这个控制对象。9 g) ]' \0 m/ ]7 ^7 z; Y
先Kp,就是消除积分和微分部分的影响,这里我纠结过到底是让Ti 等于一个很大的值让Ki=Kp*(T/Ti)里面的KI接近零,还是直接定义KI=0,TI=0.
' G# |1 a3 B7 M: R# d2 E# K然后发现前者没法找到KP使系统震荡的临界值,第二个办法可以得到预期的效果:即KP大了会产生震荡,小了会让系统稳定下来,当然这个时候是有稳态误差的。
! _6 g! b& f! c! ?9 Z# q! O随后把积分部分加进去,KI=Kp*(T/Ti)这个公式用起来,并且不断调节TI 。TI太大系统稳定时间比较长。1 w7 }/ Q9 y1 A, R# [8 @, t8 _
然后加上Kd        =Kp*(Td/T),对于系统响应比较滞后的情况效果好像好一些,我这里的电机反映挺快的,所以Td值很小。2 ~% j3 N' L2 K, r
最后就是几个参数调节一下,让波形好看一点。这里的波形实际反映的是采集回来的转速值,用STM32的DAC功能输出和转速对应的电压,用示波器采集的。
9 Z; ~2 o$ x- [' P# B最后的波形是这样的:
  R5 R- ~8 ^3 x! v 3.jpg . K3 Z' n$ t; T8 K, y! B' H
最后欢迎大家拍砖,有批评才会让我更加进步!
& M" O$ `% S  H最后把PID文件放上来 pid.zip (2.88 KB, 下载次数: 134)
收藏 5 评论17 发布时间:2017-12-26 15:40

举报

17个回答
zero99 回答时间:2017-12-26 16:11:12
大佬,这样插代码效果更加哦
. C; B  ]2 S9 h8 C. w7 F% T. H: c% E. h* m5 s& P
  1. 测试代码2 m& W+ V8 c, K, R1 P/ {
  2. 测试代码
    ) `- ~' h1 H# ?9 x3 G
  3. 测试代码3 w! c- U7 z0 e4 p: _0 u7 c
  4. 测试代码
复制代码

: @. y; t; l7 b2 n" j& h
0 A3 X& g7 Y+ \& v  q' }. _ 333333.png . @& _- B. ?; m* N
二子 回答时间:2018-10-3 11:10:48
nyszx 发表于 2017-12-26 15:41- R) o- l3 @& b) N8 k
再付个网上的资料
/ L. [0 q1 B' L6 C, J. H
这是个好东西) i& P2 \5 D% I) A& \& x/ q
书到用时方恨少; y4 n1 ?: G) {* k' m
果断收藏
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
学习了,谢谢!
) J. k+ c( p. `. Q$ }3 O8 O' k
板子粉丝 回答时间: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 手机版