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