本帖最后由 nyszx 于 2017-12-26 19:31 编辑
- p. w% t% `7 h; [3 D/ n/ d; r9 r8 Y% @8 K6 `3 i9 T
原帖:【分享】增量式PID的stm32实现,整定过程
4 j8 X4 Q+ V* e, X: R) z0 E原文网址:http://www.amobbs.com/thread-5575823-1-1.html
, A& A. ^8 f3 Q- E6 ~1 g出处:阿莫电子论坛 8 n. p5 u/ F) Z
作者:tim4146
; j: t' F6 U7 h# M1 z4 P- W感谢大家最近的帮忙,让我顺利做完增量PID功能,虽然PID不是什么牛逼的东西,但是真心希望以后刚刚接触这块的人能尽快进入状态。' P+ G I( f3 s! K
也下面我分享一下近期的这些工作吧。欢迎大家批评指点~
W A# M% f/ {, o6 Z% |/ h; {8 s" I7 s1 i) Q5 I
首先说说增量式PID的公式,这个关系到MCU算法公式的书写,实际上两个公式的写法是同一个公式变换来得,不同的是系数的差异。
& L1 `- \1 X" f! v) e$ j" g资料上比较多的是:" `( a3 H2 I# i; ?4 V; r9 r
7 p! P& D8 _$ L* ?" B0 u5 b) z$ ]* D还有一种是:. q ~- h0 D/ v; i+ G
# ^# ^& f& M, R! Q感觉第二种的Kp Ki Kd比较清楚,更好理解,下面介绍的就以第二种来吧。(比例、积分、微分三个环节的作用这里就详细展开,百度会有很多)
& Q" A, q' S! ~! S* \ X( U% j( o: K& U/ _0 b% U9 g: ]6 h* @
硬件部分:, W" |9 t2 ?; C2 r' u" K
控制系统的控制对象是4个空心杯直流电机,电机带光电编码器,可以反馈转速大小的波形。电机驱动模块是普通的L298N模块。1 Q% W5 f3 f8 ^/ j$ d! ?6 U0 s
芯片型号,STM32F103ZET6: V3 c) t% Q/ S* T
" h6 r9 d- f1 x$ _
软件部分:$ n0 F2 e" r/ L0 R9 I
PWM输出:TIM3,可以直接输出4路不通占空比的PWM波
- F8 R- v [4 P* w: S$ [ H, vPWM捕获:STM32除了TIM6 TIM7其余的都有捕获功能,使用TIM1 TIM2 TIM4 TIM5四个定时器捕获四个反馈信号: s: m1 v- B6 ^. K6 W: ^* X
PID的采样和处理:使用了基本定时器TIM6,溢出时间就是我的采样周期,理论上T越小效果会越好,这里我取20ms,依据控制对象吧,如果控制水温什么的采样周期会是几秒几分钟什么的。( U& [5 ?; D8 Q4 d# @9 L# D n
; }3 Y) q* _( g5 J# ?0 ^* I9 l上面的PWM输出和捕获关于定时器的设置都有例程,我这里是这样的:# M* ?: D7 [% ^2 n1 S# o
TIM3输出四路PWM,在引脚 C 的 GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9输出
5 p. B0 `3 A: S/ [- K4 f四路捕获分别是TIM4 TIM1 TIM2 TIM5 ,对应引脚是: PB7 PE11 PB3 PA1
) T, O/ \3 U7 G% O高级定时器tim1的初始化略不同,它的中断”名称“和通用定时器不同,见代码:
, d: i$ t) ^/ B% o/ L& v0 U- /*功能名称IM3_PWM_Init(u16 arr,u16 psc)
0 Y5 C0 o* X) q0 L. K M, U( e - 描述 TIM3产生四路PWM- k2 i- y6 Z1 `1 {: o
- */' V) L4 C2 a$ @2 A1 d# g
- void TIM3_PWM_Init(u16 arr,u16 psc)
. Y9 G' H+ ]* c+ W' i7 J - {
& z0 Q( _2 N4 w5 w - GPIO_InitTypeDef GPIO_InitStructure;8 N4 k% G; G6 R3 H/ _! |2 o& ?' e
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;( o0 @9 W9 X6 b8 f0 Q4 G
- TIM_OCInitTypeDef TIM_OCInitStructure;! d1 @1 b/ |& y) t4 F+ k q
- 1 M4 u) E" j, q; U5 ~" @$ _
8 J9 H1 R8 a9 A( ] M# H8 I6 Z- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
- d+ k6 t& r/ X - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟使能
- V, W. z3 X$ g' a0 f - 2 ~. {- S: i; _# c6 }, }- m
- GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9 //用于TIM3的CH2输出的PWM通过该LED显示
& F! J, t( [3 v* n( y& k - 9 A$ i( D! w/ x% E1 p9 u: r
- //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形
" A" g$ e8 u6 g - GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; //初始化GPIO
! V* k' j0 [1 n6 ` - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
: z' z+ \- g" b/ _' ~4 w" t" G9 | - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
3 U9 Y+ S( U# ^8 U7 G* R: o - GPIO_Init(GPIOC, &GPIO_InitStructure);, @2 z% p v& `* ^
- GPIO_ResetBits(GPIOC,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);//默认电机使能端状态:不使能6 Y& j: c' L5 v# D
- 7 ^5 q- o6 D' U% q& C3 d
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
& n& T1 ? \0 S% U! b: ^ - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 这里是72分频,那么时钟频率就是1M: a% S7 `9 y/ W1 ?$ P& h! a
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
% i7 n# N2 Q8 o8 M7 ^ - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式) c, I+ ]* t6 x
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
- c$ ?9 S% E) e$ H - ! T. r( e1 c9 ^1 Z( z9 \2 u" S8 g
- 4 P) v5 K& O& e. _( g
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式13 T, S) H, X* F/ E2 F
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能+ r/ t0 ^- e0 `
- TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值6 x: V% b8 ~ I% C( Y
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高# W- U5 ~8 ^, y2 Y `+ m" T' M3 ^
- 0 e; l/ v- f7 v+ f1 f, D8 G
- TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
* A# h- ^2 F2 b+ b( B( f' G - TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR1上的预装载寄存器
3 V! k# i Q& I0 F
- [" v; }" j w
* ?$ K! m# r+ D- TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx' o. J- e" _% i. u/ ~/ b
- TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器
4 f4 e9 n( |; m9 ]+ r8 M2 B - % N j8 L7 @8 K, ~0 F' }
- TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
- J9 C; m+ X* g+ v/ d6 A" h - TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR3上的预装载寄存器+ ^( W- B0 z7 K t, h: _* e) G
% n- Q, M7 L! t$ r/ M- TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
4 b) m7 {2 Q$ ~ c/ e - TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIMx在CCR4上的预装载寄存器
: o R+ i. ~$ J5 g0 f
6 f4 c' Y6 u& P. u- M j' O4 a! j! @- TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
4 H! I R) X: t/ F( G2 z0 ?2 T( [5 ?6 C; r
9 Q7 h3 n# U9 t; @4 R" ]- ) }" b% d% ^. Q+ Q! a
- TIM_Cmd(TIM3, ENABLE); //使能TIMx外设) M3 o/ V2 x2 x3 [
- & [9 o& n$ J+ e/ Z- U
8 z; i0 T. M% C- }; z3 y E# s$ y3 i8 M* a) p
* D7 @% T. Q) U# {* ~
/ l& k. B1 {, _0 ]) N- ) q# b- s' w; d( C7 F5 e
- /*功能名称TIM4_PWMINPUT_INIT(u16 arr,u16 psc)
+ E7 |1 ^% A3 Z# [ - 描述 PWM输入初始化*/+ z+ ~- A1 Q" D- X5 M: G" V* s5 u
/ r' ^ `2 _* \* S- void TIM4_PWMINPUT_INIT(u16 arr,u16 psc)/ ^' D j5 M6 S8 }9 n6 A( v" z
- {$ C- F6 z6 t+ n( W" I8 h
- ( F; J! p1 v5 _% u3 R
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体# f) Q/ r% n- v
- NVIC_InitTypeDef NVIC_InitStructure; //中断配置
3 {! M$ |/ J: a9 Q9 Y( d( ^# L - TIM_ICInitTypeDef TIM4_ICInitStructure; //TIM4 PWM配置结构体' Y& [% c3 O9 N7 I+ ]/ K
- GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体
1 [1 ~' N j% v: `9 F! i0 F6 O$ x - 3 Z, N+ M' R9 s$ M6 e
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //Open TIM4 clock
/ I6 q0 f0 S8 |% E - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //open gpioB clock; m5 z# c0 y* `8 K
- " R; M" s6 C5 w7 e# y* A3 M8 ?
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIO 7
: c' m! N2 o/ y+ m | - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入# E0 n) ~1 d4 s& {" i
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
$ j! V" r$ u0 n7 X/ ]" e - GPIO_Init(GPIOB, &GPIO_InitStructure);0 h) Q4 V4 k* u5 {( Z
) e) R0 P9 A& O- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值0 n8 \3 h5 a) z) ]6 c5 P3 O
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值, K$ y0 f" H! h& ?/ g6 K
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim" s6 F" Z/ k' ~5 Z6 Q* j
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
/ R9 j7 Z4 C/ a - TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
; I( R( a) M' M k9 w' d2 J$ I: q - / C- O1 v, z+ |0 }! g$ k
- . T P: Q7 F/ Y, u; c- d9 ?* P
- /*配置中断优先级*/* a/ i9 o+ N( t& U ]
- NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;# V- z- G2 S$ C# u6 u2 t
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;1 l9 `/ U" L4 L# ]5 M- O
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;1 c' w0 S( @5 U/ R( e4 z3 m4 \
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;" [' w0 p: b7 b7 L. H8 k& m( Z
- NVIC_Init(&NVIC_InitStructure);
r2 a" `0 ~# y& n; P, i
3 w& ~$ h$ n7 ^6 d! j% N- x+ k- TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2;& Z9 ?% m4 G& G1 t$ ?1 S, y
- TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;2 E3 L: F; j: T7 `
- TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
, o5 X: h2 K( l. ]; G U - TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;2 Z. h7 j4 U# E9 f: r
- TIM4_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤
" y1 a2 C, `$ U- s0 K- p
% x; N1 U m+ {) j- TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure); //PWM输入配置5 x4 K, Q+ V9 t' T; ~6 L' D
- TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2); //选择有效输入端
4 N) z* n3 ~% K5 H t) ? - TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset); //配置为主从复位模式
* {# p% R7 O" Y8 v - TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发# J" Q; Y4 r3 u$ l. x! X2 x
- TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置
2 R. X$ S* m/ Y - TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位) p: d3 r$ ~) {$ M1 \/ n
- TIM_Cmd(TIM4, ENABLE);/ Q; ~( f9 ]& ^) L$ p8 k
- }9 k) {9 u8 F3 _, g' Q
- / q+ u9 w' |: l
- 5 ^' U; w( O) \) w }8 [, j7 @
- void TIM4_IRQHandler(void)
* W$ w) l& A! v1 Q; t7 _ - {
9 g) p' v) ^) B9 d" z
6 ^" v/ m& ]. z1 _! w- if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
) k. M/ m$ \. z9 A1 r" E6 Z/ F - {
& _& p ?# o; e g - duty_TIM4 = TIM_GetCapture1(TIM4); //采集占空比
$ \. I) V h: u! p* }. i5 i - if (TIM_GetCapture2(TIM4)>600) period_TIM4 = TIM_GetCapture2(TIM4);//简单的处理 h9 y/ o. t" I) W T
- CollectFlag_TIM4 = 0;
1 V* p0 g- e5 f6 W2 z - }
& i) L* P2 s" M! J - TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位1 `+ m9 m0 h% h( Q' f; c% F3 d5 T
- }0 G+ B# W* Z8 Y# x! \; h. B7 ~
0 I2 @0 k8 w" l8 S3 Z; k- 9 R+ G8 e2 E( P
- /*功能名称TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
1 l" |. U6 s4 u- b - 描述 PWM输入初始化*/" \4 c% f: d/ M* V
- 4 X+ Z. w2 S. k4 F t3 t2 o
- void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
; A: k7 o- G' m2 r( V7 k - {
/ B& i$ A. p f. m* b: l" {/ ~# N - . y: q/ v* q& I; C$ [+ u! c
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体& A* W6 p7 ^8 |% s
- NVIC_InitTypeDef NVIC_InitStructure; //中断配置
$ K5 j$ n' N( d/ R( J - TIM_ICInitTypeDef TIM1_ICInitStructure; //PWM配置结构体# |& y& H; b) Z! U: h0 |! C- t( ^$ V+ Q
- GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体
4 B+ `9 n& e1 f. T9 R1 l6 }
6 Z) ^" ^9 V$ J2 }. V- RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //Open TIM1 clock
2 F( S0 J3 s t. R: Q$ O - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //open gpioE clock) {/ X3 i+ N. C5 T# i3 N
- GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); //Timer1完全重映射 TIM1_CH2->PE11
2 G$ _6 \' N6 |) i a - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //GPIO 11
- G( [7 p! U1 j& g9 X7 r6 j - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入# @3 _) t/ ]- l/ n x7 L
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 t# ~* ^" ?" r. O2 v( v$ @
- GPIO_Init(GPIOE, &GPIO_InitStructure);
# I, N7 S! W1 J5 ]& V3 M! B - - {: Z, |& G& f& u* W0 } {; U2 P: @
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值7 u! n, q! L, F- p W: v
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值! Q5 Q8 i4 u& O1 u" S4 k
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim& |- I! D1 A1 U/ H
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式5 e! [1 e* ]9 L5 s3 p* ~2 G/ u
- TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
0 ?" H) E- ?* r* Q6 P/ m6 ]- L9 r% D* g
5 L/ B* z4 W+ Y& \9 Y$ f- 9 [; x0 C7 I9 o
- /*配置中断优先级*/
/ I4 [% _6 l! J8 G& Q - NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn; //TIM1捕获中断
5 d5 S6 s6 l# m' Z" x - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;6 l: ^, r# u& `5 i( A e5 S" L2 F
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;' `7 V1 }: H, k6 i$ _
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;6 B5 ? Y6 V! Y6 m, h% F# I6 n; D
- NVIC_Init(&NVIC_InitStructure);
8 n5 e4 |: v; s& x# L3 K
. N' C$ Z: T7 d- TIM1_ICInitStructure.TIM_Channel = TIM_Channel_2;5 Y! s* b$ [2 W2 L2 X- W
- TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;+ n7 o7 R; v m
- TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;7 A5 n# p1 E! P: R: K
- TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;2 i& H" ]" |( X1 j! e6 K" S3 d
- TIM1_ICInitStructure.TIM_ICFilter = 0x03; //Filter:过滤3 P8 _; I3 y& X( a5 L$ A3 o
- 7 c- ^/ R, P3 Z
- TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure); //PWM输入配置
4 Z$ N. S* m C7 L9 w - TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2); //选择有效输入端
3 T$ O) S; j& h: r, O+ D2 b - TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset); //配置为主从复位模式
K: S$ Q' L0 o2 i" j% W - TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
( Y( `# k$ d% X% D G) X( i - // TIM_ITConfig(TIM1, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置
4 v s q d9 H% s" \ - TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); //通道2 捕获中断打开6 }) @, S6 Z( I6 l) B$ H
- //TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
9 y5 @" a! i1 a* Y$ M3 i - TIM_Cmd(TIM1, ENABLE);
$ z E! i) M( E) K, y( F - }% k7 N. }+ f- z
- 9 }: e$ V( X. ] j$ q3 K' h
- ' ^) s9 a0 q3 ?5 O" N: M& x) ]
- void TIM1_CC_IRQHandler(void)
6 R6 U/ T4 E4 D% ^ - {
7 m. c7 B, M U I' P - . k4 e& f4 B+ m
- {, k0 B5 l$ m# e% K
- if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
% v! B9 a+ M6 m- c; C - {
% d" B! A2 G8 h: {1 _ - duty_TIM1 = TIM_GetCapture1(TIM1); //采集占空比6 g& e) S& h" M! q
- if (TIM_GetCapture2(TIM1)>600) period_TIM1 = TIM_GetCapture2(TIM1);
( _& V7 z# ], n0 S4 ` |' v0 | - CollectFlag_TIM1 = 0;
! g! Z w7 a6 I9 e$ a! U - }" O( Y L5 _& {5 b% e% x
- }
, s6 H5 \2 ]* o- L3 t( \2 ` - TIM_ClearITPendingBit(TIM1, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
. E! \/ w* v* |* S - } |3 d) [( y" d5 S% b( t2 J
4 J) K# ^; v6 {* q7 h
' n% a6 V& q3 J3 n# }8 A- /*功能名称TIM2_PWMINPUT_INIT(u16 arr,u16 psc)
" \; w/ U2 a# w - 描述 PWM输入初始化*/- M' C8 M, W& i' g- d
- / q. ?6 g5 k7 s. f* H
- void TIM2_PWMINPUT_INIT(u16 arr,u16 psc)( X5 h5 V" u4 \2 ]% p* Q! o; Y
- {
. Q8 X5 o) v. \( [. [ - ) ?5 E) A3 a7 X7 m6 `( f" @; f
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体
' Q5 x5 ]! B, o, L - NVIC_InitTypeDef NVIC_InitStructure; //中断配置
( ?- z% n2 X( w$ m7 L/ r0 f$ { - TIM_ICInitTypeDef TIM2_ICInitStructure; //TIM2 PWM配置结构体
: Z1 d9 b: I3 }9 [: u5 ` - GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体
' G: j( k" D3 [0 q4 F, d9 V
& [3 S; @0 W. A2 h7 q- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //Open TIM2 clock
$ o& K1 W( ]) s% p& Q0 o& T - // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //open gpioB clock. ~& [4 f8 v* {5 r R
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟( _' z- T; w4 [0 Q9 ~* M3 O
- GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG' U! }4 S7 V1 z
- GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //Timer2完全重映射 TIM2_CH2->PB3
2 o4 v3 W/ U6 p - $ U o5 w8 h+ h" }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //GPIO 3
3 j" I- P+ i" L7 u) o - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入 上拉输入
/ e6 b$ l( h2 A - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% c- f+ S, L$ ]1 A7 l( [
- GPIO_Init(GPIOB, &GPIO_InitStructure);
# z. H0 n, ^/ d; s( S0 p1 s
6 Y; A8 O- `9 ^1 {" |0 E+ M# l- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值3 M& G( d/ f8 l2 r% S
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
7 f+ z# x$ V; q( d+ P: V G& g' T# W- Y7 { - TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
8 e# y# C% O" _# ^) @ - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
. ^: N g5 ]# V* ^+ l9 I, S - TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
2 |* M$ `3 t, O! M3 q2 k w) `
$ l7 _* J8 h8 H* F& A$ ?: h- ( B* I6 G3 f* F, D# C& ~
- /*配置中断优先级*/$ ^, L# f! N" y, J1 x4 c0 y
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
+ [/ q0 S7 \) M0 Q Y% X) b - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;% ^+ }* v6 K# A
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
2 a f2 w% @, m3 R" h, K" E - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
" C8 W7 |( q7 O: L( }# I - NVIC_Init(&NVIC_InitStructure);
9 c) k5 g. S5 s' Z6 j+ E) {0 Y
. U8 W6 T8 `$ q; B0 Q- `- TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2;
" W" @9 t( M4 G0 m9 e - TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;9 R! m: z* A: P. J3 A( R& H+ l$ ?
- TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;# ?" f- H# R& L- s8 I+ j. V
- TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;( Z# k' g1 S" P C5 \/ N# q! t- a6 _/ W
- TIM2_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤+ z& o# a i& M. ?5 E2 n; l
2 h6 y% s. Q. {% q; V- TIM_PWMIConfig(TIM2, &TIM2_ICInitStructure); //PWM输入配置
- L5 z! \7 s3 H; c - TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //选择有效输入端
& x3 \7 c/ |5 J. p - TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); //配置为主从复位模式% n% d- ?2 u8 J9 a# m, y1 q- D8 |
- TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发
! V+ D. a6 O; _( Z - TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置& y3 Q1 C& |6 x' R7 O
- TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位4 l, b7 [+ w# \/ }' R; g
- TIM_Cmd(TIM2, ENABLE);
* F; k& X3 p, t( C% [2 @' b - }
4 b7 {; i9 I3 U8 M; e
1 {( F+ J% b( x
" k8 y% |% M. \2 _$ d8 y3 j- void TIM2_IRQHandler(void)
4 s" H9 |" ?' f z# k9 U% \8 S - {
/ u9 a" j7 a/ D6 L+ x2 n H - {
4 V! [+ Z! y( v5 {4 f/ b' ~ - if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
9 ?6 h3 T4 D! { - {
0 ~0 _( P1 h& @& t H - duty_TIM2 = TIM_GetCapture1(TIM2); //采集占空比2 M E7 i7 e: F; i( B9 h
- if (TIM_GetCapture2(TIM2)>600) period_TIM2 = TIM_GetCapture2(TIM2);
: [9 M8 y# s% g' s7 s$ G - CollectFlag_TIM2 = 0;
" H4 e& j, s6 W - }% O+ k6 ?( E- y6 o( D0 J4 [1 x
- }1 t% j+ f; i8 }2 u" h% \
- TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位. D' A2 s7 R$ r3 [% i% w* F
- }
' a: D) R' c( G. R1 y
9 x7 k- K# z8 C& P; g- /*功能名称TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
2 v( ]9 F2 j8 U" L - 描述 PWM输入初始化*/
+ D! A+ d5 A- m( g4 o) g
, A" k0 ~/ t7 D( l" s1 [- T- void TIM5_PWMINPUT_INIT(u16 arr,u16 psc)
8 U, N0 ]: M( {0 H2 ] - {6 g2 C8 m! h" E
- 8 U4 J! q/ p5 s1 b2 |: [3 g
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM的初始化结构体0 k7 V( m; n) X) d( h" e' P- G. V
- NVIC_InitTypeDef NVIC_InitStructure; //中断配置% F; Y% s& y8 V) G( z" C
- TIM_ICInitTypeDef TIM5_ICInitStructure; //TIM4 PWM配置结构体
; q- A {7 M" r; ^& f - GPIO_InitTypeDef GPIO_InitStructure; //IO口配置结构体8 {8 r* T/ \( e* Q! h3 l
- B% Q# t0 E# ]% r% \- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //Open TIM4 clock( Z, e+ ^3 l4 p1 }% k6 t- ?4 @
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //open gpioB clock3 B6 ~' c/ b' C, a: l9 P
- 5 T: i! y( ]2 \+ B" v" g+ M6 \
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //GPIO 1( M8 k: w5 N, o$ _4 @
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空输入 上拉输入; r& d( ?# k* Z/ k
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
0 y( b2 C% w! [$ D: ~ - GPIO_Init(GPIOA, &GPIO_InitStructure);1 a- G# q. Z7 J+ e2 n2 @
- }" ?' A* n& F/ ^5 J2 j+ _- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值" D3 I! Z" c; |
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值, H' O5 g6 ^* g2 h a
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim- c m9 f3 |9 f# v$ o
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式+ T$ v: [( k+ D1 ~, `2 W4 D" t
- TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
; S! F( z4 n5 Q5 }
: ~6 Q* h9 `, S, |7 @+ a+ R! M
5 X+ y7 E' P" I8 V5 k* W6 `/ H- /*配置中断优先级*/
, h. u G# b4 [* `- @$ P8 r - NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
. d( d4 R8 }0 o. x. T; w5 Y - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
2 ]2 [. Z$ ^0 p3 V4 M+ r - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
: R% M r+ L& v. U4 C - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
' [& ^( V/ M3 n# E* N/ q# O7 K" L - NVIC_Init(&NVIC_InitStructure);# V, F- ?' E5 ~" v: C7 n! C7 \+ G
8 O3 d' g, ]; f! _! a8 X- TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2;+ H! Q. r" a& U+ s% f& h' s. q9 x
- TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;$ M" {7 k L8 m
- TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;1 l% m. J4 P9 C- E6 I7 R# ~3 s' _" D
- TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;4 _0 y' C/ X/ W1 P
- TIM5_ICInitStructure.TIM_ICFilter = 0x3; //Filter:过滤% G9 A+ z+ _: \3 D# G5 v' c5 t
/ R8 c- W1 j+ |. ?3 E; f. `- TIM_PWMIConfig(TIM5, &TIM5_ICInitStructure); //PWM输入配置0 ^( j7 U3 g6 k# d9 | a
- TIM_SelectInputTrigger(TIM5, TIM_TS_TI2FP2); //选择有效输入端
( M) T+ I) Q2 G - TIM_SelectSlaveMode(TIM5, TIM_SlaveMode_Reset); //配置为主从复位模式
6 O, U# J9 k5 m) _( @; m8 g! y - TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);//启动定时器的被动触发5 Z- H8 H1 u. X7 S/ S- J
- TIM_ITConfig(TIM5, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置! E! f% n( O ~; N7 Q0 ]
- TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
0 I9 y% a' v3 G" n- k - TIM_Cmd(TIM5, ENABLE);- c/ q" I; f( R4 F" `
- }( ?" K* V3 A- y) u9 G
- a; T4 ~* I1 |1 `4 |7 B
- 8 P, i0 C, w2 j! H& O: e
- void TIM5_IRQHandler(void)
0 v, x9 E! D: U* J - {
* }1 o9 G! t: c - {! J g$ Q7 N2 i# F; J4 w
- if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
7 M. n+ W8 X$ B& o - {" e# c8 F2 f' a3 E
- duty_TIM5 = TIM_GetCapture1(TIM5); //采集占空比$ D5 a9 }' ]% V, j! e6 C8 S) `4 ~0 L
- if (TIM_GetCapture2(TIM5)>600) period_TIM5 = TIM_GetCapture2(TIM5);; y& D" p3 D4 W2 m) o2 l; {
- CollectFlag_TIM5 = 0;% f( ]5 R; n& Z) W4 ^1 ?# Z; s
- }
& @4 x3 l7 o: C - }
% o& G+ d: r8 S: `: d W# \ N C - TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位, C7 _ T" f0 p
- }
复制代码 0 r g4 V6 x5 S" ]- r5 T" w9 f
2 q9 q& B0 Q7 |" f
PID部分:
: g6 F9 ^) `4 {准备部分:先定义PID结构体:
: K, i. I: X6 ?9 J- ; \- @' w6 H# q) v$ \) T
- typedef struct
: h* F8 \" M8 w$ L9 _ - {9 J& e: O2 E0 \1 D% k9 s) x, A
- int setpoint;//设定目标! a9 C2 u" m% p) M$ b7 \# u
- int sum_error;//误差累计
- ?" K7 Z9 p6 {* K - float proportion ;//比例常数" S5 G3 [+ K/ s9 c B( }
- float integral ;//积分常数
N5 m: x4 h! T - float derivative;//微分常数, t; E) {+ r8 d+ {! A
- int last_error;//e[-1]& C* ]4 O* g! X y0 j! q
- int prev_error;//e[-2]
7 n) S) O% n2 X+ l' @: F - }PIDtypedef;
复制代码 3 \+ j; R; i9 _! o
5 [ e, U3 K0 s9 q# G3 u; j1 [
- w) a+ e6 m# X \% F这里注意一下成员的数据类型,依据实际需要来定的。: b# u1 y% u9 L
在文件中定义几个关键变量:! O& e7 o" h- K( S- g3 d8 H- p' I
- float Kp = 0.32 ; //比例常数5 p$ q& J% M! X0 n* [8 n! ^3 I3 `+ o
- float Ti = 0.09 ; //积分时间常数% k' ]1 [4 V/ R- t8 e
- float Td = 0.0028 ; //微分时间常数3 ?) D# R2 N; ?! `6 A- W
- #define T 0.02 //采样周期
, \& N4 t4 p B# y p - #define Ki Kp*(T/Ti) // Kp Ki Kd 三个主要参数' ~7 T" p! n3 w2 d. t- K4 m
- #define Kd Kp*(Td/T)
复制代码
, ]& V% P! u, m
( B9 X4 I; u4 LC语言好像用#define 什么什么对程序不太好,各位帮忙写个优化办法看看呢? 用const?; w' c; b( a" n% R& ^7 P
2 [; f$ Q3 K+ Z: B6 `2 y# }. K6 b6 {: rPID.H里面主要的几个函数:
9 X3 `' ~* x' `7 F- void PIDperiodinit(u16 arr,u16 psc); //PID 采样定时器设定7 O+ H- c2 P/ m" `) K# \$ r
- void incPIDinit(void); //初始化,参数清零清零
% O0 K$ Z) G) e: v+ X - int incPIDcalc(PIDtypedef*PIDx,u16 nextpoint); //PID计算( z9 @1 d5 R4 b8 V7 _
- void PID_setpoint(PIDtypedef*PIDx,u16 setvalue); //设定 PID预期值
* j8 v2 @5 D" H4 X - void PID_set(float pp,float ii,float dd);//设定PID kp ki kd三个参数
2 L! c/ s; j, e - void set_speed(float W1,float W2,float W3,float W4);//设定四个电机的目标转速
复制代码
7 k5 d2 d% Y) F6 Q) J8 I$ A k1 o# G2 ^- g, Q/ h) r( G
PID处理过程:5 F0 l* H* h! e G
岔开一下:这里我控制的是电机的转速w,实际上电机的反馈波形的频率f、电机转速w、控制信号PWM的占空比a三者是大致线性的正比的关系,这里强调这个的目的是
7 o4 O* N( D2 s+ ~因为楼主在前期一直搞不懂我控制的转速怎么和TIM4输出的PWM的占空比联系起来,后来想清楚里面的联系之后通过公式把各个系数算出来了。! v4 m9 l2 {& T9 U) K4 n6 L
: p! Z) [9 Y) T- k$ R4 I正题:控制流程是这样的,首先我设定我需要的车速(对应四个轮子的转速),然后PID就是开始响应了,它先采样电机转速,得到偏差值E,带入PID计算公式,得到调整量也就是最终更改了PWM的占空比,不断调节,直到转速在稳态的一个小范围上下浮动。$ i. q3 d6 s# S
上面讲到的“得到调整量”就是增量PID的公式:
6 O3 w+ {$ j1 p+ T4 K4 ?+ o# t- int incPIDcalc(PIDtypedef *PIDx,u16 nextpoint)
- T! I! [* U6 N9 e/ u$ ]( [' E: [ - {
8 i1 t- L) @' q; u - int iError,iincpid;4 R. f* h6 p/ V1 a) m Z2 ~
- iError=PIDx->setpoint-nextpoint; //当前误差6 t3 s0 |6 ?5 a! R, {# w
- /*iincpid= //增量计算
: B0 @4 I4 K W* q. n l - PIDx->proportion*iError //e[k]项
% [6 j% v7 r* [$ T. M - -PIDx->integral*PIDx->last_error //e[k-1]* y$ n s; M! \- p1 ~
- +PIDx->derivative*PIDx->prev_error;//e[k-2]
4 Z# Q+ e, ^4 @/ g/ I - */2 c% z. z" F+ k \4 h7 F3 V
- iincpid= //增量计算+ P9 ]" ?+ k6 m' y+ b0 b V
- PIDx->proportion*(iError-PIDx->last_error)
m9 `! m* o6 a- H! U" L! i - +PIDx->integral*iError
* r: R) ]0 [: ? - +PIDx->derivative*(iError-2*PIDx->last_error+PIDx->prev_error);
7 B; V) o+ w8 N2 `. a7 \
8 i2 f$ ?- R6 d- PIDx->prev_error=PIDx->last_error; //存储误差,便于下次计算& c- C% H- S7 N- Q7 n: w
- PIDx->last_error=iError;
. p' q+ O0 o# F/ {4 g0 o3 H - return(iincpid) ;
4 }) J1 d H- A$ _5 w - }
7 S; t0 `2 u) v9 m2 ` p7 J - ( `* v& y! ]! K/ L; s+ l* A
" F3 m( f$ s: b3 A9 X3 ^- 复制代码
$ Q8 `; O1 z0 { z - 0 e/ ]: c7 o1 S% @! T' k+ B( k
- 注释掉的是第一种写法,没注释的是第二种以Kp KI kd为系数的写法,实际结果是一样的。
' K% Q1 C5 l/ \8 D0 O) x. |. a - 处理过程放在了TIM6,溢出周期时间就是是PID里面采样周期(区分于反馈信号的采样,反馈信号采样是1M的频率)) A2 l; ~# S& h9 L* R* F3 {% t
- 相关代码:
4 V y: e7 Q4 E' @
9 c! B; y# z+ h, a! x% @7 @ ^8 F% O- # {0 V7 s4 L* R
- void TIM6_IRQHandler(void) // 采样时间到,中断处理函数
; t4 z: _ z- D& Z - {
% N& I# f0 _) a
& s7 L/ Z& q: ?) G& ?- if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)//更新中断1 W7 K3 |) O9 X& j( Q, [; u
- {
, d. U' W7 P( s - frequency1=1000000/period_TIM4 ; //通过捕获的波形的周期算出频率
4 ?* w* F8 U4 r; c - frequency2=1000000/period_TIM1 ;
; ^7 s1 s8 ~, `5 x" H3 j - frequency3=1000000/period_TIM2 ;
# i. \5 x( \2 e! g* d7 ]% n# }1 W - frequency4=1000000/period_TIM5 ;; T5 e% T6 V( f) f
- /********PID1处理**********// h/ l8 t; _5 a( c
- PID1.sum_error+=(incPIDcalc(&PID1,frequency1)); //计算增量并累加
' t! B+ n% u, ]% I - pwm1=PID1.sum_error*4.6875 ; //pwm1 代表将要输出PWM的占空比 ?% {+ l! B, d' q: G+ Q
- frequency1=0; //清零
" F# P! E8 c8 R1 F; s; m - period_TIM4=0;. z+ b7 k/ X$ v% D; `4 E
- /********PID2处理**********/5 o; A, ^- X9 ?# W; F& O; T5 A$ U; \
- PID2.sum_error+=(incPIDcalc(&PID2,frequency2)); //计算增量并累加 Y=Y+Y'
- d" T# p6 S* X - pwm2=PID2.sum_error*4.6875 ; //将要输出PWM的占空比
& d c- V; Z9 G c" ] - frequency2=0;
8 e% O }& z6 ]& _% X# { - period_TIM1=0;
; ~4 ?# p+ ]0 f4 A/ y7 E( C1 Y6 X% _ - /********PID3处理**********/
" p, I$ D4 r+ o) ~$ r - PID3.sum_error+=(incPIDcalc(&PID3,frequency3)); //常规PID控制* |2 m" r( X0 H2 {& B1 d
- pwm3=PID3.sum_error*4.6875 ; //将要输出PWM的占空比
P Z: g: ]* k+ l6 l - frequency3=0;7 i/ M+ h# c2 V7 g3 z9 c9 i
- period_TIM2=0;
4 v8 n6 N& i& v - /********PID4处理**********/
# [ }/ s1 S! Y* \ - PID4.sum_error+=(incPIDcalc(&PID4,frequency4)); //计算增量并累加
+ {4 `+ ] K1 r, ` - pwm4=PID4.sum_error*4.6875 ; //将要输出PWM的占空比
1 x) V/ G; O) N5 q- `) L - frequency4=0;% y9 h8 y3 p+ c9 b# J3 [
- period_TIM5=0;+ q- s# k8 v- C* c8 @; P" y0 e: M! y
- }
[; K8 H" E5 q3 f - TIM_SetCompare(pwm1,pwm2,pwm3,pwm4); //重新设定PWM值( m. @: o* z! `* z
- TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除中断标志位
$ b- W+ g1 _* _( B' e& c - }
复制代码
+ N; W Z+ T/ c/ a$ Z& C8 K2 l/ @& j2 W8 a! j# k7 v+ @- D
上面几个代码是PID实现的关键部分! H3 Q9 J. ]) i( J; B
W- N4 \5 b" h$ [
整定过程:
2 E, r* f, u! c2 w S办法有不少,这里用的是先KP,再TI,再TD,在微调。其他的办法特别是有个尼古拉斯法我发现不适合我这个控制对象。& x+ Y5 Q! _/ V$ K: U& _
先Kp,就是消除积分和微分部分的影响,这里我纠结过到底是让Ti 等于一个很大的值让Ki=Kp*(T/Ti)里面的KI接近零,还是直接定义KI=0,TI=0.
n( Q8 ^8 x2 z" j" ~, M* b( l) p; Z然后发现前者没法找到KP使系统震荡的临界值,第二个办法可以得到预期的效果:即KP大了会产生震荡,小了会让系统稳定下来,当然这个时候是有稳态误差的。6 T0 j" `/ q5 A# y. X2 Q& `6 S! R
随后把积分部分加进去,KI=Kp*(T/Ti)这个公式用起来,并且不断调节TI 。TI太大系统稳定时间比较长。
! ] ~4 o. U) W6 x/ a0 d+ Y5 l然后加上Kd =Kp*(Td/T),对于系统响应比较滞后的情况效果好像好一些,我这里的电机反映挺快的,所以Td值很小。
1 ~) A* P0 b/ C$ o: w最后就是几个参数调节一下,让波形好看一点。这里的波形实际反映的是采集回来的转速值,用STM32的DAC功能输出和转速对应的电压,用示波器采集的。! K( ~4 s/ e. r7 o" u q
最后的波形是这样的:
' T3 H2 P! l, I6 W2 E* ?7 l5 n' z3 o
* c( r0 i4 X0 ?1 {! l
最后欢迎大家拍砖,有批评才会让我更加进步!
& h4 M0 P$ |$ v' l最后把PID文件放上来
pid.zip
(2.88 KB, 下载次数: 134)
|
这是个好东西
书到用时方恨少( Y$ ^; r6 Y: b4 ~
果断收藏