前言
( V( e, ]# w$ {前面完成了基于STM32F103C8T6+L298N+MG513P30直流电机的PWM控制和两种方法的编码器实时速度反馈,拿到这个反馈值后我们就可以使用经典的PID算法,对电机的转速进行准确的控制了,这篇文章主要分享PID基本原理和Keil5的PID编程。% c% k3 o& c$ \# a" F& g! j
; [" \; U- s8 U5 A
一、PID的基本原理& H! Y% X3 o/ n
PID算法是上个世纪30年代左右提出的控制算法,大至航空航天、小至家庭温度调控都可以使用PID算法,虽然PID算法从提出到现在已经历经了快一个世纪,其后也出现了很多现代的智能算法,比如蒙特卡洛、智能控制等等,但现在PID仍然经久不衰,可以说目前80%以上的控制仍然使用PID算法。
: w8 W; z8 ~2 O! x+ h' g& [; [
+ i p7 m: p5 `. G, f( W7 yPID算法是自动控制原理课程学习的一部分,但在课程中老师讲解的是最基本的原理,没有任何拓展,更别提应用了,首先,先简单说一下PID控制算法的原理。$ G( @4 h9 M5 a* P6 v+ j9 Z+ [
8 {/ i6 k5 A+ D V% D8 E4 f# X+ j
" U/ N% q; R; i) }4 U4 K! ~6 Y
: N0 g6 ?) ]8 ?
上图为PID算法的控制框图,在我们控制电机速度时,期望输入就是电机的期望速度值,期望输入与由编码器测得的实际速度作差,求出的误差值传给PID的控制部分,算出需要输出的控制信号,将该控制信号传给控制器,也就是输出给电机驱动板L298N,这样形成一个循环,就实现了对电机速度的精准控制。8 G- v4 q& b( m- ^# p; [) D
+ G0 c. @2 b: `4 S& r- S
中间PID的控制部分的连续型公式如下:
8 s2 K* x6 L5 l- S4 M8 ?
6 B9 i. M- ?: P* Q# `. @" S
6 Y8 I$ f8 o v
, z3 a. z: x" ^: @' f% B
但是在计算机中计算机很难实现连续型变量的积分或者微分操作,因此在计算机中,我们使用离散型的积分和微分,就是取时间间隔T为1,离散型PID公式如下:
3 k3 C9 a) [& |6 v. x/ X( k z: `- U8 r W
! `* |' z( V- ]
, i+ h+ j$ Q& d: X" k7 E" p各个项的主要作业及效果如下:
. j' _2 b: [5 j* e: j
* f, p. Q0 g1 EP:增加快速性,过大会引起震荡和超调,P单独作用会一直有静态误差8 n$ E; K- g( j$ E/ z7 N0 o* q
I:减少静态误差,过大会引起震荡0 g7 Z" A1 E6 O& V8 P
D:减小超调,过大会使响应速度变慢2 f$ z: m3 E( F7 Z! t7 k% o; i5 O
在实际的应用中,有可能不需要PID同时使用,比如在速度控制中一般只使用PI控制就够了,各种各样的PI、PD控制大家可以去B站或者看其他博主的博客,已经讲的很详细了。
G; c7 d( O; j( G$ w
/ W! ~8 F1 p2 |' ^1 [二、变式PID4 r/ h7 ]. Z% Z k& z
PID算法有很多进化版本,分类别的简单阐述一下
* T3 q1 l! V }% c$ K/ \1 O9 O# k. o
增量式PID
& x; ~( m# k, ^; x
6 y9 {7 ~/ H. \在电机的速度PID控制算法中,因为我们一般使用PI算法就够了,所以我们可以使用增量式PID算法,这样可以让我们的公式和代码更加简洁。
& f0 S" E" d0 c; j+ ~
7 U) |" [, M9 O5 p3 u
/ _0 `, v8 X7 Z
$ w( ^* i! {* J& y, b/ X3 e
积分限幅6 R. X6 n$ Q3 f0 C. w
0 W4 }) Y& x+ ]6 w) l1 |1 y, r
因为积分的效果是累加,随着时间的推移,积分项的值会升到很高,积分本来的作用是用来减小静态误差,但积分项过大会引起过大的震荡,所以我们可以加一个判断函数if,当积分项的值达到一定值后,就让积分项保持这个值,避免引起更大的震荡。
, L& Q9 a$ W$ n0 R2 V# n1 H" h# Q- @( ]6 A2 K) @6 F) {
积分分离/ G3 Y9 I4 d$ `
3 G8 l1 h9 ?3 B; R
如果刚开始的误差比较大,那么积分项则会在刚开始就累计到了一个很大的数值,那么当第一次实际输出达到期望值时,不会立刻停止,而是会产生一个很大的过冲。这时就需要用到积分分离,就是当误差值过大时,我们就不使用积分项,只让PD项单独作用,当误差值较小后,在加入积分项,以减小静态误差。
) @5 t, v- H2 V+ @7 S
0 H; u+ l" @( N7 L# n三、Keil5程序
, Q" O( M5 s$ I% r( M) w- ]为了使用方便,我们先定一个PID结构体,结构体储存左右轮的PID参数、限幅值、误差等参数。% Z1 D& l" s3 e5 r- R" R
, |. B/ ~. E& p g# a. s
- typedef struct
1 A2 U! D Y9 }! n( V - {
0 A2 B8 W* K. i4 } - //相关速度PID参数; s# R; \& ]" }
- float Velcity_Kp;2 G4 u, u1 ?( n% h) ?
- float Velcity_Ki;5 {7 v R5 y- T |3 h% r, A& V
- float Velcity_Kd;
- a1 Z# f, k/ h J- ?1 D/ a# R - float Ur; //限幅值
* T" @) T5 T& J/ j) d B! T -
$ q' G7 Z _& I) M9 ?0 b# b - u8 PID_is_Enable; //PID使能
' i. X9 x, E8 w) `; V0 @4 U - int Un; //期望输出值
u) S5 V* S3 r - int En_1; //上一次的误差值% ]! z5 J0 g5 }
- int En_2; //上上次的误差值
1 X) N) s1 }* i; f, Z& X( q! r9 h - int PWM; //输出PWM值
9 U! o# `* J& i2 ^+ f5 o - ! R* `% ^) a& ]* D# v1 L
- }PID_InitDefStruct;
复制代码 1 r/ d/ E$ b* f
在程序初始化部分,定义一个初始化函数,对其中的参数进行初始化配置。 h: n! ]9 e7 V# Z5 u3 M4 }; y
1 ^8 G. P$ B/ \+ r* u' E- void PID_Init(PID_InitDefStruct* p)
4 H( R+ w( P5 |# Z4 m9 N3 l* p - {
8 q2 A% f& \* y5 B5 i2 E/ B - p->Velcity_Kp = 5;
$ {% V( \ T4 r8 ?) c1 \ - p->Velcity_Ki = 0.5;
v8 D" F' w1 x; B - p->Velcity_Kd = 0;
, F+ }+ K7 t+ _5 e) G( z - p->Ur = 7100;- ~0 P, w6 ]" d2 N, q; c0 x- \
- p->PID_is_Enable = 1;9 \* G4 T2 |' J5 Q3 W# D
- p->Un = 0;- w6 y; ~, l" u/ h
- p->En_1 = 0;
+ l3 M; f8 S+ p# }) J6 |8 [ - p->En_2 = 0;0 g. i! }' O3 Z1 S
- p->PWM = 0;
( q, }+ P3 T6 l- r - }
复制代码
' M: b, L' ] P1 T0 X4 R当编码器的定时器,每隔10ms反馈一次编码器测出的实际速度后,调用PID函数,求解输出给电机驱动板的PWM值,然后通过Set_Pwm函数进行设置,以此控制电机转速。/ m6 C2 t6 H% Q- D! s0 Y6 e
- z5 l# X( {" ~- void Velocity_PID(int TargetVelocity,int CurrentVelocity,PID_InitDefStruct* p) C3 Z1 B, ?: a8 v% ]
- {
H$ j6 K- B+ K0 l; h5 n6 L' w - if(p->PID_is_Enable == 1)+ \1 V, T* t: J! h) r
- {( e; p' e( m. e. M
- int En = TargetVelocity - CurrentVelocity;//误差值 * `" b% X! i/ W7 _6 q
-
4 Y I$ X5 J4 e - p->Un += p->Velcity_Kp*(En - p->En_1) + p->Velcity_Ki*En + p->Velcity_Kd*(En - 2*p->En_1 + p->En_2);//增量式PID. i, Z2 k1 `( P4 l
-
. M6 ]" \' j0 b6 H - p->En_2=p->En_1;" \6 y f& c" _; z
- p->En_1=En;
. z! E4 Q$ E& B2 g -
, M( T. b) G' w" w - p->PWM = p->Un;
9 A1 ]7 C$ P) o% I+ P -
/ V% I* Y e- p" ]+ K6 j - /*输出限幅*/0 w% ~/ P2 V, s# S8 i+ l; I0 `
- if(p->PWM>p->Ur) p->PWM=p->Ur;8 G1 F/ @. H9 o4 W
- if(p->PWM<-p->Ur) p->PWM=-p->Ur;
- b7 v% G/ H% \, {2 q" ^0 H [# Q - }/ |) |5 b5 m& \8 Y
- else
+ q( Z) |3 b4 I6 [$ } - {
# l4 R% M7 Q& b- O1 }. X - PID_Init(p);
9 O7 ?7 ?2 v6 [5 v9 I1 n2 w# ^9 D - }, B7 Y+ Q5 k) `2 \7 w
-
5 {( @. Y# v! H3 j% T \! P! K8 n - }
复制代码 , @0 x2 f& E* U; [: _( x0 g5 D
测试给电机输入理想转速为1500mm/s,随便设置了一组PI参数,得到实验结果如下:% t+ i) m! n; l) Q% X9 l
: W$ M3 |; V) N
3 @2 L8 Z# [1 L3 ~; d
$ w) ]+ w& O+ r" {可以通过上位机看一下波形,可以看到在稳定状态的静差是比较小的) D% Z" h9 I& U* g! K v$ E
0 D4 Z% |) r- b5 i! {, [* D( D
6 q5 Z9 L6 Z( T& A
- P( v2 U) ]6 V8 }% V0 f/ @总结, p h+ h) C$ E d5 f
对于PID算法这才是万里长征的第一步吧,想要调出完美地控制程序,还需要复杂的PID参数整定,这里可以配合上位机进行调试,以后调出来在分享。4 X7 O6 ?" A: m" J, A7 J! [
$ w3 K! }0 f6 z8 \% j: r$ W
程序在此。# ~* x2 J. q' ~( s
+ h9 T4 T7 R4 `5 i3 M临近开学时间比较仓促,写的挺简单的,大家有问题欢迎私信或者评论,我们一起讨论。
4 s, X# ^/ y2 ]5 z$ E* y' a3 o+ w% z# F/ v
% j: V% X# i; K3 z, e o( C8 T |