本帖最后由 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
& x' _# ~7 t, d7 b( j还有一种是:
" i* d$ D! M E, z1 n ~, ~* C
# 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
- /*功能名称IM3_PWM_Init(u16 arr,u16 psc)
; U3 N) O! k* w2 z) t6 H* I4 A - 描述 TIM3产生四路PWM
0 n; p4 \% h* D7 R3 j7 X5 N - */8 p2 G% ^! Y! ~& j
- void TIM3_PWM_Init(u16 arr,u16 psc)( H$ W* ^6 h0 H Y4 x
- {* {* }9 |# F) v% Z' Y
- GPIO_InitTypeDef GPIO_InitStructure;7 g& k+ }% w2 X5 P
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
5 [6 X$ p& j4 ^9 k - TIM_OCInitTypeDef TIM_OCInitStructure;' I2 ]$ h4 k3 g* ] N v4 ]2 F6 c
1 u# H9 }+ J/ K1 ~3 a5 H" t
5 x% [1 v0 E2 L# z- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);6 [7 c- ?8 q1 v4 W
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟使能9 {2 @" a2 a3 n% r! @
- / B* m9 f, p; S9 t C
- GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9 //用于TIM3的CH2输出的PWM通过该LED显示
( i6 W: W m) j: L
9 f) _# G1 f+ X- c- //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形6 w- }$ T: s% a' F1 E
- GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO8 Q8 a/ ^. X" U0 o$ B
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出$ @0 W1 @/ A% B( q( w$ T7 ?
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;" X0 v- _8 c! h; ?4 C! ~
- GPIO_Init(GPIOC, &GPIO_InitStructure);! r8 N* B Q z/ d! l8 ^
- GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能
' h2 p" E$ z7 c$ j/ L* W - * l% b- j9 G$ w2 ?0 Y
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
/ q$ z- S$ f/ Z: h# b5 s( [ - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 这里是72分频,那么时钟频率就是1M& o8 v3 _# X& Q' q/ o: I4 }+ [
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim5 |/ S; T/ l& ]# O2 v
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
9 z2 Z" v( ]/ h% P - TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位) v0 Y4 M) H; n) s8 P
- \7 n2 b# d X" p) `2 i+ N6 \( v
- $ ?' h: `( R- n1 O1 _( v4 b' ]
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式12 L8 F" O/ b/ a- K! Y8 V6 D
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
3 R7 z2 B p$ k7 K' [) [6 J0 I _ - TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
9 I4 { X. M1 Q7 l% i/ D; x: r - TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
& _$ Q4 s+ ~. o! y
5 u0 A; P s/ z4 N- TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
( |9 p: V7 l |" u% {5 V - TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR1上的预装载寄存器
# I8 p, K$ K/ z. t4 F3 k7 @+ A
5 K( y+ }+ i' R5 V, e3 a
8 K5 R' V- o4 R, _8 \- TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx: Y) F0 r4 U2 c2 T( Y+ D% f
- TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器* ? q# d- N- t# T" A
- 8 H) K) s0 `) a$ n' W! E
- TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
J: \: r! d- G$ I# F4 }/ M( d - TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR3上的预装载寄存器+ c6 J: }- X9 D. W# m
- ( L* b7 G" i2 Q) p5 A" M
- TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
/ {3 I* M3 e3 x! \/ p' I - TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR4上的预装载寄存器
( P8 ~2 l, Q) g3 y
9 \4 N+ E; S: b3 \, Q- TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
$ }* q4 P' W/ G f! {# q" T
8 H' p* M9 Q: e- * V% p8 R4 u2 i
- TIM_Cmd(TIM3, ENABLE); //使能TIMx外设. o& |$ B$ |6 x9 T. S! b
, ~) M P7 b" D6 m2 d: a- / J2 k$ T2 c" J* ^+ C2 Q
- }
' Z! M* a0 C5 [- z: o+ {" \ - ! g, b! G- Z' Y
- 8 D9 m& {; H, l- W
4 b2 J0 W$ S* J- /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
9 n$ y9 _; D5 y" D - 描述 PWM输入初始化*/) k# @& q; q5 L4 a2 ~! B, }
$ e+ A& `' _) i" d7 h: f& T- void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)$ M+ |6 M% N- o2 R- { ?! r
- {. T! m9 C- D6 ?# w }
- ) Q2 f7 q( ?# f4 l% k# I; G
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体
. u' i3 G+ e/ ?0 R. d - NVIC_InitTypeDef NVIC_InitStructure; //中断配置
/ p! B n: j8 |$ `; M7 i/ s: ?3 l - TIM_ICInitTypeDef TIM4_ICInitStructure; //TIM4 PWM配置结构体& q$ r- ]! h* H3 _
- GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体 K: W. Z9 q$ G. }$ J
2 }' N- U4 n) C$ T$ |2 g- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //Open TIM4 clock( Q! l% E7 r' F# G! f9 E6 J: x
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //open gpioB clock, `$ t6 l$ G( g/ Q3 f
- 6 T F' a# W3 T0 d x2 C
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIO 7
% f& W6 ?/ r4 J7 c. L9 ^ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入# j8 k( A; _- `6 e
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 P; R' V& E5 u; y5 N
- GPIO_Init(GPIOB, &GPIO_InitStructure);6 b) P& F% G0 m/ c- N. M* H
! X5 j7 R& x. ?" u- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
' h, V" Q* j R% D; L( M - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
5 G- z6 s5 l9 I& }' S - TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
8 F: Q1 N0 N s/ V2 M/ J, [; p* h2 ? - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式: }. G: B. w' Q& }& ~& x
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
' N* p! @) s' Z* T( C. U
# I: Y4 o( F! s/ {/ ^' o- : S1 `8 s/ O: y' v* W
- /*配置中断优先级*/
+ x, V8 @* f! r! s/ C) I - NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
0 d* Q: }- Y' Z( W' ~6 d - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
$ s4 D. W2 U1 t, Q1 h0 V* G - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
% G# K6 W* z% }4 H+ q - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;7 U0 J3 V1 z# `! C0 c8 g a
- NVIC_Init(&NVIC_InitStructure);7 P' `, X% E6 X, [
$ N! T; o5 |7 w! M. N: t$ A- TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;# H. |$ z: R9 L$ c( }. F
- TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;/ \- ? g- C. p% C% Y* Q! H9 w
- TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;. s7 w* Y8 X8 L+ ~: ~
- TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;+ Q: Y" J) g2 I
- TIM4_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤
" O: g! s1 t! P c4 O
- ?4 k& M6 i/ t1 |% ^- TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure); //PWM输入配置7 m% W ]4 y" N H: P8 O* H
- TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2); //选择有效输入端/ r# P) N8 s6 x( s1 S5 l* B
- TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset); //配置为主从复位模式# V( h% t: k: b+ @$ u6 [6 A' r
- TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发& }1 M j1 n5 g& }' }
- TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置9 t' c. Z0 s! _& f. U$ n" U2 {+ B- L- c6 c
- TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位/ H& V& x: O7 A4 }
- TIM_Cmd(TIM4, ENABLE);4 C* L, A; ^/ h" W
- }5 r) T$ W+ \" N. l; Y
( `1 ]+ Q; X }/ L' r- 9 C* l# o9 Y5 H# ?( P7 O: l) N5 v
- void TIM4_IRQHandler(void)6 S! b4 f4 ~* y
- {0 Y1 K1 S9 H' {5 K7 u6 \
% s+ X2 h- B& N$ Q- if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
% J8 L& B6 j3 d. a - {2 v6 h& ^' C$ b1 ]+ C5 \4 y
- duty_TIM4 = TIM_GetCapture1(TIM4); //采集占空比8 s, N/ d5 O8 v s3 L
- if (TIM_GetCapture2(TIM4)>600) period_TIM4 = TIM_GetCapture2(TIM4);//简单的处理3 P9 d, `5 v3 J/ Q0 |& y
- CollectFlag_TIM4 = 0;
+ w! h2 j% Q [9 L+ `( H6 w' v$ g - }
2 J; G6 P7 L! M4 N! D - TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位4 ^ }6 k# K7 ]/ d+ X4 W& C' j
- }$ e$ x0 v% r7 Q$ T3 b; x9 n9 o
- & O8 P8 a( q9 J+ k( R! d6 l( f
- / Y. X5 a5 n* I0 J* Z, W2 W
- /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)3 J4 S" z1 H" ^
- 描述 PWM输入初始化*/6 `- K3 }+ P: I$ V/ _' O" ?) [) P
4 h9 d4 V0 a B, o: ]- void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
$ S# Z$ Z7 ]: ^5 D/ X4 b - {0 L1 Q/ C4 u" m6 @* I/ i
- t! K+ @1 W- K3 T6 k
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体
6 r4 C, {1 N; v' D. E4 `. I: t - NVIC_InitTypeDef NVIC_InitStructure; //中断配置
) s! Z5 i" y/ ?6 I% u! w) h/ B0 N - TIM_ICInitTypeDef TIM1_ICInitStructure; //PWM配置结构体$ a7 w1 _3 d% `7 x4 l0 y! V
- GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体
: r8 z3 T( d5 o* V# m1 n. B - ' N; v# C- w4 N( x9 m. S9 i
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //Open TIM1 clock$ |7 T/ l( C( `7 b. ^& o9 I
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //open gpioE clock
z1 w3 `6 v3 N% e9 m: g( F - GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射 TIM1_CH2->PE11
, W: F7 f) A, I) ^ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //GPIO 11 }# K* W8 ?; T$ [, D, O
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入; c E/ w- y8 ^' e) [
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
6 c. ^: i* I6 `, ^. w% S3 F - GPIO_Init(GPIOE, &GPIO_InitStructure);' G* M' M G n5 P- z' Y: v' o
- - Y+ K+ h# g7 Y) @
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值2 o2 A& o, O! y& _ e1 _9 w+ M( o0 D$ P
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
* b1 U+ a, h; C$ y3 C: ?4 D - TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim* [" Y( S3 Z9 F6 u% a
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
+ |; B$ k' t& b1 n @. P - TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
0 S% A' E! R9 a M
& e2 O& O4 o7 N2 g" \- , C Z- ? B& K. T
- /*配置中断优先级*/
$ e5 s; g0 k6 |3 o* ]: z - NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn; //TIM1捕获中断1 b: a2 \0 ~; p9 H3 _
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
$ Y6 i2 U" L0 F( ~ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
# M* p9 G0 ^; R- f* u; _4 q+ { - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
5 Z* h9 {1 Q) K6 x u# g4 y% I) } - NVIC_Init(&NVIC_InitStructure);
2 e$ j* e' ~, x2 b# h
( J, l% A, h% N; t6 v- TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;
; f: j8 L, E' X3 w - TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
9 r4 ]0 a) {$ y" Z! y - TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;3 H9 D3 E2 B& p% H i
- TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;+ g x6 r, J5 Q7 ]
- TIM1_ICInitStructure.TIM_ICFilter = 0x03; //Filter:过滤
* N3 j3 a: ]; m# f - 0 Q* \/ ]) R1 g1 G3 u
- TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure); //PWM输入配置
2 f" X- x0 u' o - TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2); //选择有效输入端5 Y* Z5 K8 Q, ]
- TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset); //配置为主从复位模式
6 @+ X) M( g8 S" Y# i3 m - TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发4 v4 o- u0 t0 a7 Q0 K) ^
- // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置
" K) ]; l. l- e+ S3 [9 D - TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开 Z4 V5 j) d/ F, [7 Z
- //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
/ \+ t+ l* t: u3 p% D+ f! q - TIM_Cmd(TIM1, ENABLE);* `" B* @9 ^; o. ~& @
- }6 v) X( f/ b3 a2 ]( F: a
* C+ b, o, y' E- $ ]1 q- o4 F, x: [ G5 h+ I
- void TIM1_CC_IRQHandler(void)
) _% W! I5 _# y% j - {
7 ~% \4 y Z' e - 6 f# E; R0 e# E1 T5 {7 r
- {
7 E4 r& U3 F) ]6 `' @* ~ - if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
r* h' w: @; f. s, [ - {4 `1 Z4 q* K" r
- duty_TIM1 = TIM_GetCapture1(TIM1); //采集占空比, `; h4 k+ n7 ]" X
- if (TIM_GetCapture2(TIM1)>600) period_TIM1 = TIM_GetCapture2(TIM1);
. @" r O; c: e! b( [ - CollectFlag_TIM1 = 0;
6 ]) a! s' l4 ]; ?: e: \) R5 ^ - }
) I, p" _' r9 t9 G K; [ - }
3 h! g$ L0 b, o5 R9 Z - TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位; w B& E$ z- M
- }0 v3 [1 u: C9 m% h
- 1 |2 e; b3 G$ E4 o. \, I5 w, G
% D/ Q! D, o8 E; H( j1 \- /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc); u# ]6 t3 J, ^6 Q# D0 [# I d
- 描述 PWM输入初始化*/1 @" ?- A4 B& B8 x3 Y
- + ?1 a. n% a8 j9 O \9 N
- void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)( t/ U i+ v& c7 A* P
- {' T* W, P) j, o( F1 k4 B4 w- |
0 E) w. y' R/ h e- g$ a* B- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体
; R8 E- h0 J* w S/ y5 N3 Q - NVIC_InitTypeDef NVIC_InitStructure; //中断配置+ L: _8 N! n c+ A7 G( ]# j7 n6 `, n
- TIM_ICInitTypeDef TIM2_ICInitStructure; //TIM2 PWM配置结构体8 o3 C7 G/ P4 m. t" D/ m0 b
- GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体
- b# ~( ]) I- _3 c8 A, M
& A$ b- M' J" c9 j% x0 m, ?2 H- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //Open TIM2 clock
4 W, J: ?0 e( ?% g2 x, Q. w0 ~" o - // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //open gpioB clock; r( b* F+ T$ S# S
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟1 y3 b. ^* x8 j# ^
- GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG
8 V' Q* L2 h7 h' E" C6 t6 w - GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射 TIM2_CH2->PB30 U7 [ ]* c0 J; G
! H8 X* [6 c: A3 ]- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //GPIO 3
" z( Y' B: Y0 Q. S - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入 上拉输入
! m" V9 j# e3 T6 ] - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' U: U, c2 }- p4 R7 i3 T" V
- GPIO_Init(GPIOB, &GPIO_InitStructure);
! a, e9 _- G9 Q+ t8 O) {# W
S5 t8 Z( G; q- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
: ^/ k+ C. {7 Q3 x4 y - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
. L8 i; c+ k3 _6 N( I - TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim# s8 ^1 ]- Z$ H' V; H
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
4 Q$ C' Y6 B7 w7 N- g - TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
! \+ U# F5 }. R
: d) r; b& e2 P+ Z
2 R w% u; ]9 y; X- /*配置中断优先级*/ g4 U0 s5 a E, Y" Z$ M# U6 p
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
' L: m4 h6 o0 N2 J - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
$ W$ L5 u' L( I4 E& I. K - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;* w; l/ z! o% }- w! i+ G8 T
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
: R; h( \ Q$ V4 y8 x' g% Y" ~ - NVIC_Init(&NVIC_InitStructure);
6 K0 |8 I3 \; _. M
7 `+ O S/ b# a2 a5 v% ]# K+ M- TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
; @1 y, E$ p3 {" u - TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;; s& ^4 S$ r. K
- TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
, w+ b. [5 k3 a! U' k9 x# ]$ O2 h - TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; [; r8 b- c2 l" f3 ~; T8 g
- TIM2_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤/ |! S6 n+ Y1 N3 J! u' E# b
- 1 _/ U9 K( _ I" L) e
- TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure); //PWM输入配置" N9 i# ]7 N; `8 \
- TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //选择有效输入端* y# q$ s3 ?, C- W% ]8 V7 N" @
- TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //配置为主从复位模式
5 m* y4 O8 M; H) b5 `" A* C0 B - TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
; A# k; T5 Y- @$ V7 E( q - TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置
' R7 |: x; A. W* Z+ h2 ` - TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位8 b7 g) T9 L" q, Q$ [- U
- TIM_Cmd(TIM2, ENABLE);% ~* b) V0 f* i! d* R: r. Q; s5 Q
- }
5 e+ q+ `) t" I$ J7 z
0 i, d9 n8 Y+ I$ ]- L3 T3 z9 U- 9 ~$ \% U9 g$ E: ^4 \
- void TIM2_IRQHandler(void)1 ^* X/ d( P7 v% f
- {7 `$ t- R# ~$ P7 ?" o
- {
5 p' t4 g' K4 H% H0 c* F - if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
+ }, T. S0 T% k' H - {( J( C) k# p! a" {% G# T" i
- duty_TIM2 = TIM_GetCapture1(TIM2); //采集占空比) F+ k1 c5 I4 V' Z$ H1 q( ]0 l
- if (TIM_GetCapture2(TIM2)>600) period_TIM2 = TIM_GetCapture2(TIM2);- r3 I5 @- C! ?0 I
- CollectFlag_TIM2 = 0;
, ~& s5 o7 b; _ F, c - }* C# s5 q9 E7 \( q: V
- }! E/ l: s" j: Q( |7 J% I; q) P
- TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
2 _$ n$ Y, G V+ o - }9 s1 E/ W+ \" O3 ?9 \' m
. G1 z7 X( T% N4 e7 r- /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)* o% p/ {$ y) ^" y! h) ?& V# G
- 描述 PWM输入初始化*/
4 }- ^0 p' u- Y4 r, y* T0 Q" K
B0 W1 A! Q( R1 A* W9 ?1 b, f- void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
( x/ X) u' g+ p0 z, h$ S$ h) { - {
; {* v* ^9 Y& v' K3 p - * E6 L) e% { _: s0 M4 { e0 C# ]" P
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体
8 A# \- C2 \& D7 B - NVIC_InitTypeDef NVIC_InitStructure; //中断配置
5 M4 r# R+ U/ v2 `) D+ Y, N - TIM_ICInitTypeDef TIM5_ICInitStructure; //TIM4 PWM配置结构体
! P7 H- Q" v: s% U& M- o+ A* z - GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体2 D `# a4 x7 E, q/ C+ T
8 p3 Q3 b. ]" e4 I/ F. ^- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //Open TIM4 clock
, [1 b/ n3 h: B/ a+ Q+ X% K9 s* f, @ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //open gpioB clock
( a1 Q! R# z+ r- i - 2 ^0 X1 J0 F: F. y: z' B
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //GPIO 1; Q D. k, a: r* B
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入 上拉输入
/ n; j5 p7 I( h" b( a$ t% V - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ q. X3 l [8 Q. m" y+ M - GPIO_Init(GPIOA, &GPIO_InitStructure);% X; y8 x4 l0 w# ^& L
- : f! H1 h0 k! j1 W& J- d+ \
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
( f* g' W! N# r( H- ^( h# C9 K - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值' M7 ]5 y9 w, b% v; ]- y1 B, M
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim% I- N+ r+ t: I. U/ @: C) ^
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式' Y2 D6 k9 F+ a, c3 t
- TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位+ U) n* U; Q. a- R$ B
- ' a. y% R/ d6 d. n1 m- ^
- z Z0 W9 |# O! V- /*配置中断优先级*/" |) ?8 k9 b% U8 ~* j6 V+ v
- NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
( X T# l$ V6 w) ^2 x/ i - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/ H, w9 C6 a5 P2 N9 F, }8 B - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;- |$ h% J0 A! c( d' x' V
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;% k; U( M+ g# R
- NVIC_Init(&NVIC_InitStructure);
& ~ ^. Y# K% A2 P7 D0 `
" A/ R2 q( h7 v. o; \2 T- TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;
. C- w" b' ~$ W6 _9 f' i - TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;9 K. o# @6 h( ^7 v% |4 p
- TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;0 m* ]- @7 b) N5 D4 h2 E$ n
- TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;# d: O" S2 ^# S2 f% s
- TIM5_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤
7 E: h G. A1 e' I( d: m+ u' D5 N - 9 F3 A8 Y; e6 M; |' Q5 g
- TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure); //PWM输入配置2 r% L \5 `, v9 O: G1 F, d
- TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2); //选择有效输入端. g3 V) W) f. d1 E6 Z
- TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset); //配置为主从复位模式
& K% E# C( V# ]% `: J; D o - TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发/ j0 `7 J% x- E2 ?/ M
- TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置
# U7 Q* j+ d7 b9 T( Q4 k, K% f - TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位. H+ L+ x2 @& @- [$ R1 ]* F
- TIM_Cmd(TIM5, ENABLE);1 U, {8 o( l0 Q! i# X7 t" F
- }$ L4 Z: B6 x; }% B" s! e
- ) Q. z6 ]) C5 ^) U8 K
; ]; |1 M1 m+ ]2 l, e3 T- void TIM5_IRQHandler(void)
- \* M6 C- [. M9 S; `" H6 e# v& { - {
7 n5 F! K3 G& }5 c& s2 c: m - {. {2 } w. ~" S% {
- if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
; w* k1 i8 T% |5 f, X U; `$ u6 N - {
. c: w) h ^5 o; ~3 V% o4 V - duty_TIM5 = TIM_GetCapture1(TIM5); //采集占空比
" V' m6 G5 B" N& N. ` - if (TIM_GetCapture2(TIM5)>600) period_TIM5 = TIM_GetCapture2(TIM5);2 x7 t/ a& K: f' J) y/ h
- CollectFlag_TIM5 = 0;
* W& @5 m) I8 M% V' ^2 ^ - }
# K- o5 f( s# b) G, V; J, B- w& g - }
+ _. q1 @6 T/ L; ?& } - TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位; r! V% z ]8 ^6 g9 P' J! h
- }
复制代码
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
- . Y: K D; d* X* k# \2 Z+ Q3 D
- typedef struct
! {% l1 |# K4 |4 F2 ? - {
# Y5 E+ k1 l0 ~$ E* B3 K - int setpoint;//设定目标0 b, {8 e7 X$ X
- int sum_error;//误差累计4 }* E4 Q. }, H7 Q
- float proportion ;//比例常数& r1 l. ~( q2 K; s
- float integral ;//积分常数
$ w: Y. F+ F* B% X - float derivative;//微分常数) {; e. b/ ~8 K, r! {1 J+ ~. a( H
- int last_error;//e[-1]
- D$ y4 Q+ h# e8 [ - int prev_error;//e[-2]1 [3 O9 c/ b7 L
- }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- float Kp = 0.32 ; //比例常数
6 @# l& }! o- M+ X' I- m3 _' N1 P" ` - float Ti = 0.09 ; //积分时间常数
8 l8 ?, i8 z6 o; S# y. I - float Td = 0.0028 ; //微分时间常数
) @) ~5 n6 M$ l& d5 I - #define T 0.02 //采样周期$ X* u0 @& g5 A% @& x
- #define Ki Kp*(T/Ti) // Kp Ki Kd 三个主要参数3 l9 _" z1 B4 O* k# N; x* P2 x- `
- #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
- void PIDperiodinit(u16 arr,u16 psc); //PID 采样定时器设定* u) [" h i+ e3 R o P
- void incPIDinit(void); //初始化,参数清零清零
% q0 w7 X, s3 A1 T6 |( T! r! |; d, Z - int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint); //PID计算
4 r L! U+ z2 H' V& m! ]2 B: A1 r2 T& f - void PID_setpoint(PIDtypedef*PIDx,u16 setvalue); //设定 PID预期值
9 w' B- g& e; r ]- Y - void PID_set(float pp,float ii,float dd);//设定PID kp ki kd三个参数
: X7 I4 G3 Y0 N+ p - 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
- int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint); I: B! ]. a2 V* n/ b y
- {5 o8 s* G5 }) } j* W5 H
- int iError,iincpid;
5 `0 _9 T# ~) D, W+ L - iError=PIDx->setpoint-nextpoint; //当前误差9 P0 I% m" x( T# j; O; e# N: z4 a
- /*iincpid= //增量计算; b5 M' I$ q$ |8 p
- PIDx->proportion*iError //e[k]项1 s+ l3 V/ V! c* E
- -PIDx->integral*PIDx->last_error //e[k-1]! n8 ~8 n2 X4 D
- +PIDx->derivative*PIDx->prev_error;//e[k-2]
) M5 g4 p: Q+ U5 d5 k1 ~. \ - */. Q* w) x) F- o- K4 P
- iincpid= //增量计算( d, S W/ U* s2 s% [9 R
- PIDx->proportion*(iError-PIDx->last_error). f0 g0 S% b, d% O5 ^7 Q+ k
- +PIDx->integral*iError( m1 S- f0 H' B% V; v# j
- +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);, I. a2 Z" h+ t( E$ O+ m$ r1 K
- * G7 D( W8 M, F# C& |- T
- PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算) C) b5 D' M$ w, _! n) d
- PIDx->last_error=iError;3 [8 v# }) O9 R* ]
- return(iincpid) ;
! H |! Y6 b' Y9 H7 f6 P) T - }
& ~+ d! I( ]; u, _8 ~% l
. t- Q, U& R2 ~! g% e- ' C* X' p- J; ?/ d4 O+ a+ P
- 复制代码
9 g, d8 ]! g- }" g3 R
2 G L; B& g2 T" R( q8 T- 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
1 \0 e6 u9 {9 P5 z. `) w - 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)$ k1 ?' ?$ N! I4 _& b# w' c
- 相关代码:
0 X/ N! B0 ]) A: L$ y% z# h - - d1 J; w* X' V2 y) R: [
& O$ \6 `2 i8 E! ~5 ]% t9 q- void TIM6_IRQHandler(void) // 采样时间到,中断处理函数
7 }! M; H! Q# p' a7 d - {
* y5 [4 g5 |9 ~3 M7 L+ w! W) l
8 X \& ^9 `7 }9 y- if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断
! ]4 {" h, d8 l" X1 P; G3 x, m/ R1 | - {
6 q1 V$ C$ c+ X4 v" \ - frequency1=1000000/period_TIM4 ; //通过捕获的波形的周期算出频率' O7 }( q; u& p" ^% ?! P$ \
- frequency2=1000000/period_TIM1 ;
' @4 g7 ?' J' H: { P( D ]. k6 t - frequency3=1000000/period_TIM2 ;1 @) H: Q3 Q& c! W' t/ k/ p/ ?6 y
- frequency4=1000000/period_TIM5 ;" G3 x3 C @- P0 e
- /********PID1处理**********/
0 I/ C: K" a" q - PID1.sum_error+=(incPIDcalc(&PID1,frequency1)); //计算增量并累加2 q4 [, B* x1 K) ]$ u& K9 P
- pwm1=PID1.sum_error*4.6875 ; //pwm1 代表将要输出PWM的占空比
# c/ Y' s) D. v9 ] - frequency1=0; //清零
# F0 P4 m C* X* H - period_TIM4=0;
. y" m* @4 e& { - /********PID2处理**********/& h7 K+ u I; q, n- J( `
- PID2.sum_error+=(incPIDcalc(&PID2,frequency2)); //计算增量并累加 Y=Y+Y'
9 Y( A8 s, d* D, A& n- u - pwm2=PID2.sum_error*4.6875 ; //将要输出PWM的占空比
5 ]1 A S$ \! j2 I - frequency2=0;4 [ X8 ?! N, X6 h4 P
- period_TIM1=0;& \( x7 u8 |& }% E( t5 x
- /********PID3处理**********/7 B+ c0 S' G5 | z r
- PID3.sum_error+=(incPIDcalc(&PID3,frequency3)); //常规PID控制
8 L" y3 Z' c; f - pwm3=PID3.sum_error*4.6875 ; //将要输出PWM的占空比
4 B# E) q9 }) ` |; i - frequency3=0;0 H+ \9 [6 s5 W/ A3 S5 q
- period_TIM2=0;; z6 N5 }9 b7 G9 H6 |" h
- /********PID4处理**********/. G- H( t% M! _( r. g7 k3 A+ L
- PID4.sum_error+=(incPIDcalc(&PID4,frequency4)); //计算增量并累加
. b+ n. ~% j: ]0 c; D4 h4 _/ [/ @ - pwm4=PID4.sum_error*4.6875 ; //将要输出PWM的占空比7 c0 D; \1 Z9 p3 J3 ]& F% O
- frequency4=0;# y* H- R, t1 t% e; S% E9 e, T
- period_TIM5=0;
3 B* s2 C2 g0 B& x) z - }( ^, |2 X/ s5 H3 s( j$ e: T
- TIM_SetCompare(pwm1,pwm2,pwm3,pwm4); //重新设定PWM值
% A; {. t1 \. q9 ?. t/ ?# v0 R - TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位 z4 O5 q8 J% }6 |0 w! _
- }
复制代码 , 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
! h1 M0 _2 O) J3 Y$ i h; }5 h
最后欢迎大家拍砖,有批评才会让我更加进步!
* q: x% R- o: g; F/ @最后把PID文件放上来
pid.zip
(2.88 KB, 下载次数: 134)
|
0 |1 @9 W, w& a; \
/ c$ K' K& E6 V6 u* |; a, t4 `
这是个好东西
书到用时方恨少9 g9 \) h" |2 @8 S3 F0 T5 Z/ Z& Q
果断收藏