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