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