一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 3192|回复: 6

PID C语言

[复制链接]
发表于 2014-3-19 21:59 | 显示全部楼层 |阅读模式
PID算法(c 语言)( `5 v7 {, D" [: L; c* c
#include <stdio.h> " D$ C' L/ r: n$ m
#include<math.h>
  o+ X/ {2 B# }! D5 k' |//定义PID 的结构体 # W0 ~2 p0 K2 y) ^+ J# x
struct _pid
5 a  }* B8 j5 J! d# z{ 3 ?5 |; D" m# V% h
int pv; //integer that contains the process value 过0 R0 h: P2 A* v
程量 ; ?3 S9 [7 b4 h" [% A, ~, c
int sp; //*integer that contains the set point   设
4 w0 k+ d# l; W& ]8 |定值
$ x) C( C$ N$ M3 n* w8 bfloat integral; // 积分值 -- 偏差累计值
3 y  L8 V4 Z( j% ?6 y/ D! Ufloat pgain;
7 \5 C9 K6 S1 Q* R8 v4 R. jfloat igain;
' {4 [, t9 U# o* L3 D! S/ @float dgain; * C7 s8 K  E2 s4 l% v* Z% {! M
int deadband;    //死区 ) C3 k5 \9 \/ J7 d- E2 ~# g9 ?9 ~
int last_error; 5 {% q3 q  s* w# h" d5 H
};
# ?9 H/ v6 `8 _. k6 K0 r0 B! Z! Q1 r3 _' g
struct _pid warm,*pid; 4 g2 w  w5 ?0 W. Q  e3 o: A$ q
int process_point, set_point,dead_band; float p_gain, i_gain, d_gain, 1 B$ {' [) B( \* c! T
integral_val,new_integ;;
: e: M4 w7 m3 D$ k9 n/ U0 v" [& `  L8 Q, I" @0 p8 C9 B
//------------------------/ x0 y0 p2 y1 p( G; ~: V
----------------------
" V; E4 J3 V2 D* Q0 @4 Ipid_init DESCRIPTION This function initializes the $ i6 Y1 |9 E) B% [( f5 J! L9 |9 ~  f
pointers  in  the  _pid  structure  to  the  process  variable 5 k* j1 P, E. w  O) x& f
and the setpoint. *pv and *sp are integer pointers.
2 j% w# f8 \% L. |- M! n//------------------------6 l! y5 K. P5 q2 o- i. `8 K8 W+ E
---------------------- 3 q5 ~* h  L7 v+ P, F+ p, ^/ |% z( i

- L& @: E3 U6 H! m7 x3 s: [9 L0 evoid pid_init(struct _pid *warm, int process_point,
1 v3 X7 l* t5 Z* z9 kint set_point) % y1 Y8 Z) {+ c# ?( P3 z! f4 @
{
  r& O0 t2 X: @+ S3 R  E+ nstruct _pid *pid; 2 q, g' a8 H+ e4 u6 f/ O
pid = warm;
: K/ Y  Q( A) V7 ~. N2 d; zpid->pv = process_point;
1 f) t. [0 u. y: p1 @/ d1 ]6 vpid->sp = set_point;
9 T9 x( N+ e# ~2 V3 T}
+ k5 ?* |& o+ g6 c" h8 k( K$ N: m" P' ]9 Z
//------------------------! k. g* j) j+ Y9 k* x
----------------------pid_tune DESCRIPTION Sets the proportional gain 1 o4 g5 b' u9 E8 F& _7 n" x
(p_gain), integral gain (i_gain), % d5 E* y4 P" V4 m' s2 f
derivitive  gain  (d_gain),  and  the  dead  band  (dead_band) 8 [5 [; y; ]/ K( d9 G) J2 \6 ?! {0 t
of a pid control structure _pid.  0 V" `/ h! a# Y5 {7 z- T: u' c; R

' C" M3 v  l2 }8 F, _+ W设定PID参数 ---- P,I,D,死区 6 x1 h0 K# {" A/ \
//------------------------
+ t! @  N& V% I) _5 M----------------------
1 R, \" c& w+ N* v2 L+ t2 f$ p/ V$ J3 R) h. `
void pid_tune(struct _pid *pid, float p_gain, float
% V2 Q* @5 o4 b( Oi_gain, float d_gain, int dead_band)
4 W% K0 m/ H- }2 S3 d{
( }$ d8 I6 y% E6 a) {pid->pgain = p_gain; & p/ Q; h0 T% _. n
pid->igain = i_gain; / |7 \) n% `0 B7 }, Z7 `
pid->dgain = d_gain; $ u! q% B1 a. d4 w+ S8 V6 z
pid->deadband = dead_band;
; D, f' \( L8 ^6 P6 I7 `pid->integral= integral_val; 0 v- I- h7 @2 K' E$ p
pid->last_error=0; 8 ]/ |2 k. v0 @9 Q) V( c
}
+ ^; |- k  {) B9 m6 ^9 c3 O( o4 h' q# H; W6 V3 K6 }
//------------------------
/ ]% w! }' ?. p& y4 \---------------------- pid_setinteg DESCRIPTION Set a new value for the
, b0 r! M/ F( Dintegral term of the pid equation. 8 c) d9 j3 v- Q9 g% t2 L; I3 u* ]4 n
This is useful for setting the initial output of the 7 ]+ s; ^; w5 B% N6 ~
pid controller at start up.
- E. ~  w- d0 X: J  r' ?7 z* {8 J
5 J4 \& [7 P, m$ C: K& W设定输出初始值
, e# W% \: R  q//------------------------' [: \3 z  R! ~6 H( l
----------------------
$ E1 h; T/ Q% g) Z- w' l& w+ |! V6 i9 J
void pid_setinteg(struct _pid *pid,float new_integ) ! z& ]! T& z; Q! j& C* G% U
{ 7 v- i1 h& H0 G
pid->integral = new_integ;
. M" h) X2 \; mpid->last_error = 0; 8 |8 K4 X9 w/ A8 u# S4 M
} 6 Z" Y) L& I8 r" s, v) I3 Q
5 z0 |! e) A# s) R
//------------------------5 ]) R1 i6 F4 E: n1 J( k; b
----------------------
; }+ B# e: R+ K3 z( A& n; v& z" n2 \+ V0 \pid_bumpless DESCRIPTION Bumpless transfer   F, q3 K5 Z, u' Y
algorithim.
2 L" G9 q, w& h+ P* W1 EWhen suddenly changing setpoints, or when restarting 8 L7 T4 d. ?$ r4 I* i
the PID equation after an extended pause,
+ q( ^) m5 X+ u- M/ dthe derivative of the equation can cause a bump in the controller output. This function will help smooth out
" T3 T! r. v8 t: s; ^that bump. 3 s4 A- R  F, h. b3 y% }; D* |1 t
The process value in *pv should be the updated just % u* ?6 {( c5 O8 c1 F2 e
before this function is used. 7 \% w% I  ~  z; t$ A8 W

8 c9 `4 ]/ n0 {: Q+ a$ k* U& Zpid_bumpless 实现无扰切换 $ f" f  w' g9 N9 y7 K
当突然改变设定值时,或重新启动后,将引起扰动输出。这- q" R. l9 u5 g+ J
个函数将能实现平顺扰动, 在调用该函数之前需要先更新 PV值
' G0 p5 c/ q9 W1 A0 N//------------------------  B9 r7 R2 ]) Q& o  x3 ]
----------------------
4 Y% h7 I+ ^. M0 a7 r& ^# Y) M1 B/ h7 Z# |, [/ J5 W. i
void pid_bumpless(struct _pid *pid)
% U+ L8 R+ C& I8 A{ # V& U! [7 n0 w0 M
pid->last_error = (pid->sp)-(pid->pv);  //设定值与反馈值偏差
( g/ y2 J0 `7 Q) y$ L} ! u" X$ I1 ?  b1 s, s: R/ i

7 l" v: O$ n8 J/ C. w* U//------------------------
+ M& B2 X" N3 y1 U$ P: a----------------------
5 B0 U& ^+ B' j: k; bpid_calc  DESCRIPTION  Performs  PID  calculations  for  the
6 H/ y) K/ ?, }_pid structure *a. This function uses the positional form of the pid + {. D$ N1 f& u) h5 T7 ?" R6 U$ ~
equation, and incorporates an integral windup
( k# d! V' d# F) J. Mprevention algorithim.
! Y  X' w) ]$ `: }$ GRectangular  integration  is  used,  so  this  function  must 9 U! b% @' N$ s: _) p  D9 j
be repeated on a consistent time basis for accurate
. O# H( t; f; i7 ]4 O" vcontrol.
8 B! g( ~$ K) }6 KRETURN VALUE The new output value for the pid loop.
6 x& v$ x- a- z, X. XUSAGE #include "control.h" 1 b# p7 Q$ l' @* e# b  P" M) t
本函数使用位置式PID计算方式,并且采取了积分饱和限制运算 % |% @7 M4 p& L, E3 {2 d, G" O7 C
PID计算 # ]+ |6 d7 X; i3 j
//------------------------
3 \( T. ?6 p* I& v0 r& }----------------------
0 Y, T/ I2 ~8 v6 n
: q/ T( {& L2 w. `1 |2 T1 \. Y5 S/ Cfloat pid_calc(struct _pid *pid) / {& o+ b" b6 S- n$ N+ D  m

. `! L& O! a5 ]int err;
' [  M# e' Z# s' i+ G3 Bfloat pterm, dterm, result, ferror;
. u1 q7 m1 T# a
0 B; Z& q( K/ c// 计算偏差
6 ~& C2 t0 v$ y5 I" x: l2 T8 Herr = (pid->sp) - (pid->pv); ) U6 L% ?) c" O$ ^- g7 D0 s
// 判断是否大于死区
' u4 L/ M& H8 U# E$ f# Y0 Vif (abs(err) > pid->deadband) + ^+ n  H, }! M, \  O0 P$ o
{ 6 _3 G5 I4 \1 [$ s& V
ferror = (float) err;   //do integer to float 1 k, u- a2 J# g- V( [: v
conversion only once 数据类型转换 4 A+ u) g+ h) X
, t0 N/ o) Z: \4 W1 S
// 比例项 , b% J$ X% r& u6 t) B# }
pterm = pid->pgain * ferror;
& o' _) _' D* v' P) `% y" {" @* s8 }! @9 y9 N: m
if (pterm > 100 || pterm < -100) ; V/ z- J: P' f" D$ K0 P5 U7 L
{
$ t* K: {( B1 ?7 T" \6 Ppid->integral = 0.0;
# E' k  j9 _' l3 U- _} 3 ?( M# {# D8 q8 _$ E# O. _/ x
else
/ n' Y  f! a& _. \; A{
  [/ x; L8 H' V1 m, G/ `6 Q// 积分项
- e% ]# G/ `5 X! L3 c; Gpid->integral += pid->igain * ferror;
; d5 j9 l% y8 c3 S: @
; J3 [- g% G: T/ R- I// 输出为0--100%
. _* v# B  u$ L% v  m$ K5 s7 P// 如果计算结果大于100,则等于100
$ j0 b8 q4 M* _& `% |5 zif (pid->integral > 100.0)
2 P( p. B% v5 p3 {{ pid->integral = 100.0; - i; L! s' g& D! G, v$ b8 ]. Z9 G( f
}
# F0 r; ~  K" j0 _* G* o// 如果计算结果小于0.0,则等于0 7 X9 R& z; t# G$ O
else if (pid->integral < 0.0)
; A5 D. }. ?4 Wpid->integral = 0.0;
, C+ J: ^, Y3 b9 n/ I1 v/ w/ I$ c8 q7 d! ~$ |8 p/ M
} 6 ?; t* j$ }6 X' N) W- t, L
0 [% \9 e- e. @) ~+ ^$ n
// 微分项
! M7 g2 c) s/ pdterm  =  ((float)(err  -  pid->last_error))  *  pid->dgain;
  N# u4 a0 |) E5 F: b# a3 L7 i1 e7 T2 G
  U: J7 O$ p+ B) Vresult = pterm + pid->integral + dterm; 9 f" E" s8 }! N, a2 a- D7 o& J) x
} ; h$ |- h! [+ b8 r* }
else
6 o% o! |6 H. Kresult = pid->integral; // 在死区范围内,保持现有输出 ! b$ w) E9 N4 v2 ^& A3 q

) x# h) a# u! ~  Q: D, G// 保存上次偏差 $ a, S7 K( _$ C( y6 m( P8 K: m) c  y
pid->last_error = err; ! ?$ K6 {  P/ x, J7 e

# U# `" j* i( \! _9 U// 输出PID值(0-100)
9 ]% J( S* C1 u8 |. G) N3 rreturn (result); }
0 M0 |% _+ Y  X
) _1 u2 A2 B5 G4 w% X- Q2 n//------------------------
# n; j4 [- }7 w/ l5 O& u---------------------- / J. w  a/ }0 H
void main(void)   w3 I' J/ ~' }2 b6 M
{
* N/ l, ^# ~1 t! R3 M  r; ~float display_value;
/ c3 X% H; I% C: y; w, A# f" Z6 kint count=0; ) Q, \. m: z# o: ?- I$ d( ?
pid = &warm;
0 G# k9 D& X% V7 ?' _! x9 Y7 d* c0 b8 z2 v' [' D7 v
// printf("Enter the values of Process point, Set 5 ?2 s' O1 O9 W
point, P gain, I gain, D gain \n");
2 o4 d3 v/ s# o& `; K, b7 S( V# Z// scanf("%d%d%f%f%f", &process_point, &set_point,
& R; J% @; N7 t) f, v) R&p_gain, &i_gain, &d_gain);
! K1 l/ s5 C2 D, ?9 j
/ C( n" B3 A% O6 N* j$ D* f- ^// 初始化参数
, U/ {% b; p! ?0 uprocess_point = 30; : U! i- L' g5 @$ j: Q
set_point = 40;
& ]3 p/ b' c' e/ M6 U! tp_gain = (float)(5.2); : \. y$ R0 b! M" l& \
i_gain = (float)(0.77); ( o8 N: o/ z% i) U8 V3 Z6 L/ v
d_gain = (float)(0.18);
8 ?, v5 `. B, \& Idead_band = 2;  . k5 @9 n! _  v6 ^8 I
integral_val =(float)(0.01); 0 ?. |/ e. s' u& L; @
$ U7 }5 k- C" o* p
printf("The  values  of  Process  point,  Set  point,  P  gain, # ~! [  A) ~* @0 G- J* x3 _! ~
I gain, D gain \n"); % Z4 c5 j1 a% S! s% z9 C
printf(" %6d %6d %4f %4f %4f\n", process_point,
# I" p6 V- m6 Y% d* U" hset_point, p_gain, i_gain, d_gain);
: l. Z9 Q/ z' N9 _  ^printf("Enter the values of Process point\n");
3 Q2 H3 K& o3 u1 ?: b# D9 Zwhile(count<=20) ) q4 v% S4 \: v' H6 g1 e
{ % G! z- V4 e! K1 j
scanf("%d",&process_point);
$ p" R# U; X" T( i5 p( d' D
/ b6 _7 g* o6 `// 设定PV,SP 值
! |& _) w9 j( q* o  X/ Zpid_init(&warm, process_point, set_point); " B2 g1 m$ }  G5 S
: x, O: |$ v3 v/ O1 r
// 初始化PID 参数值 . b' e3 b) I/ y/ E
pid_tune(&warm, p_gain,i_gain,d_gain,dead_band);
3 @9 s. F8 e3 |! ^5 _2 t% X
6 H- G+ }+ A% E// 初始化PID 输出值 # [' d5 W7 r* h3 w8 P8 V! V
pid_setinteg(&warm,0.0);
8 S4 }% l- Z7 X, r2 ^+ F//pid_setinteg(&warm,30.0); 3 F5 }  X9 p/ r& B0 |& V5 v
//Get input value for process point
. x2 G3 A' G+ F  @* {" c# M% \( S- mpid_bumpless(&warm);
* c1 g: Z# _- C* c
& A" E$ A& O! Z% Y" _( B0 y// how to display output
8 }, H9 z# _# |# Kdisplay_value = pid_calc(&warm); $ Z  t6 O- o/ B7 o! X, e2 K* K) z

; ~; q% L. n) T+ O$ oprintf("%f\n", display_value);
) P1 [5 a$ d: s) ?0 ~//printf("\n%f%f%f%f",warm.pv,warm.sp,warm.igain,wa% M2 G3 u8 d+ }
rm.dgain);
# s) V5 Y& _% z5 o  U4 u" _( I9 u: }9 c: [2 q+ p$ c' U
count++;
7 T1 U& u  `$ }) y7 Z# t}
9 w* t2 W/ w7 U; y0 r2 o, d7 M}" T) A. g: W$ R

: T8 v0 `: b- K2 O3 ~3 Y
发表于 2014-3-19 22:11 | 显示全部楼层
版主,这个更是针对什么设备的?是不是要根据不同的设备来制定不同的PID算法?比如驱动两个电炉,一个功率1000w,另一个2000w,在相同的控制温度下,同一个PID处理程序,温度的变化曲线就相差很大。
回复

使用道具 举报

 楼主| 发表于 2014-3-19 22:13 | 显示全部楼层
在电子数字计算机直接数字控制系统中,PID控制器是通过计算机PID控制算法程序实现的。计算机直接数字控制系统大多数是采样-数据控制系统。进入计算机的连续-时间信号,必须经过采样和整量化后,变成数字量,方能进入计算机的存贮器和寄存器,而在数字计算机中的计算和处理,不论是积分还是微分,只能用数值计算去逼近。 在数字计算机中,PID控制规律的实现,也必须用数值逼近的方法。当采样周期相当短时,用求和代替积分,用差商代替微商,使PID算法离散化,将描述连续-时间PID算法的微分方程,变为描述离散-时间PID算法的差分方程。算法中,为了求和,必须将系统偏差的全部过去值e(j)(j=1,2,3,... ,k)都存储起来。这种算法得出控制量的全量输出u(k),是控制量的绝对数值。在控制系统中,这种控制量确定了执行机构的位置,例如在阀门控制中,这种算法的输出对应了阀门的位置(开度)。所以,将这种算法称为“位置算法”。当执行机构需要的不是控制量的绝对值,而是控制量的增量(例如去驱动步进电动机)时,需要用PID的“增量算法”。已看不出是PID的表达式了,也看不出P、I、D作用的直接关系,只表示了各次误差量对控制作用的影响。数字增量式PID算法,只要贮存最近的三个误差采样值e(k)、e(k-1)、e(k-2)就足够了。0 U0 K; K' n5 D# \7 n7 t
回复

使用道具 举报

 楼主| 发表于 2014-3-19 22:15 | 显示全部楼层
好多是在网上找的,我也在学习当中。
回复

使用道具 举报

发表于 2014-3-19 22:18 | 显示全部楼层
保留,这个应该好好理解
回复

使用道具 举报

发表于 2014-3-20 09:29 | 显示全部楼层
8055 发表于 2014-3-19 22:117 {2 j# V$ ?6 v" H: g
版主,这个更是针对什么设备的?是不是要根据不同的设备来制定不同的PID算法?比如驱动两个电炉,一个功率1 ...
0 ?) J4 K  J1 }' ?8 z0 Q! k
用同一个PID算法没问题,但要处理好PID参数,比如热容量差不多的情况下,两个设备的P参数要差一倍左右。, h7 Z* t5 N9 ^# |5 g* d6 L3 C
回复

使用道具 举报

发表于 2014-3-20 10:26 | 显示全部楼层
收藏学习中、、、
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2026-4-17 18:23 , Processed in 0.033423 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表