一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 3072|回复: 6

PID C语言

[复制链接]
发表于 2014-3-19 21:59 | 显示全部楼层 |阅读模式
PID算法(c 语言)
, p3 g9 x4 l( B6 @#include <stdio.h> % y9 |+ r7 P/ ]
#include<math.h> , k8 Z; O. @$ d# _- {! {# I
//定义PID 的结构体
& q: I5 @! o- ?3 Tstruct _pid
: y7 P! N& t* m+ k) H9 Q9 z+ E9 ^{ ) R, G- d; E2 \& t. v8 G
int pv; //integer that contains the process value 过  S0 `, a) ?7 q$ T/ e2 l' z
程量 0 u0 W2 R+ C# u* G0 H9 l5 g1 r, Z& Q
int sp; //*integer that contains the set point   设
0 C* l. A; U: \, o定值
* Y1 L% ~' m0 A$ Efloat integral; // 积分值 -- 偏差累计值
$ Z* S0 W8 }- q8 t) afloat pgain; # y6 g2 c# L: i$ b, J% A8 z
float igain;
; ]' {! r$ P: D5 Afloat dgain;
2 o6 E# w  g2 [$ q* W4 I' ]9 fint deadband;    //死区 9 u: p0 q3 b( e$ d8 W2 S' b/ L
int last_error; : N5 V# a# o- o5 E
};
% U' P' U. d* d- K
( K( x2 U7 \0 }  j2 Istruct _pid warm,*pid; 4 i, L( H: v+ J; y) |* L
int process_point, set_point,dead_band; float p_gain, i_gain, d_gain,
. A) d3 U3 F$ V! u8 V7 gintegral_val,new_integ;;
; u% Y5 I: F) a# T  g' _) s4 i1 \' F4 p, V) x7 a5 `$ v
//------------------------
8 Q9 t) M/ T! {" \! a; w3 g----------------------
1 |$ {7 O' u1 i5 l% s6 ^pid_init DESCRIPTION This function initializes the
! c' x2 K2 o0 q, j+ b0 J! Gpointers  in  the  _pid  structure  to  the  process  variable
' Y8 l5 I& r. P7 X7 ~9 Dand the setpoint. *pv and *sp are integer pointers. 9 X. G+ f- R. A
//------------------------
& L; [! R" R3 |0 o---------------------- 4 }( a# m9 H; v
2 Q6 |. k7 K  ]' X
void pid_init(struct _pid *warm, int process_point,
! b+ r2 w. T$ I  Gint set_point) ( c7 V9 S6 u8 ^, O0 }
{
6 f% B: n" {- Z) C' C7 g: @struct _pid *pid; # c5 e7 p. S, S- t, @4 t. h/ K
pid = warm;
) j; j2 y6 |) Q( tpid->pv = process_point;
' T8 F# z! a! j  ?pid->sp = set_point;
! y7 U& s8 c: ~! X2 U} 3 v. T, k! e$ e# `
4 Q+ v5 i( q9 A
//------------------------  f# n' |) p" p# A0 `
----------------------pid_tune DESCRIPTION Sets the proportional gain
3 V9 O7 s" L6 W- [* B3 t- G(p_gain), integral gain (i_gain),
, T2 P+ \. H1 `derivitive  gain  (d_gain),  and  the  dead  band  (dead_band)
' n9 h& M8 ~( R' `. c% N& Kof a pid control structure _pid.  8 ^  Q# T7 ^: C! J
: j% [. \4 j0 o& }0 A
设定PID参数 ---- P,I,D,死区 % P7 u+ \3 A' F0 v8 r7 _
//------------------------( K1 _5 Q3 _7 z! g2 L9 G* I. c/ u$ b
----------------------
6 g4 I* B, b; T! G: J  U9 G1 N1 \1 y! z. G- g$ y! W1 A: a
void pid_tune(struct _pid *pid, float p_gain, float / S/ _2 |4 D2 F  X; i0 W
i_gain, float d_gain, int dead_band) $ U7 a: q5 Q% ]" K* f1 F4 b# X
{
0 C+ ~$ Y: d( p- L! h# z* Z$ x+ Q4 J. mpid->pgain = p_gain; 1 f8 N, D+ t  [3 k# K: Q5 F- q. C, o
pid->igain = i_gain; * r: o/ c6 p& V: Q. h% c6 P
pid->dgain = d_gain;
, E! W3 Q" _% C- wpid->deadband = dead_band; ) \  s4 }8 L5 ~5 J8 P8 M
pid->integral= integral_val; 3 T3 U; f) P/ z
pid->last_error=0; * ]( n% K) Q$ c5 p
}
3 F+ k6 j$ ^+ z2 X3 B, s: Q  E8 @. L) m- }3 `7 ]* b
//------------------------9 n9 u/ `$ N/ B! \- p, ~& w8 C: ~: u
---------------------- pid_setinteg DESCRIPTION Set a new value for the
; V% ]" H1 e/ N: u0 I# q) J( E' `* Y" kintegral term of the pid equation.
% c0 C# b: t" E3 X9 S7 P6 yThis is useful for setting the initial output of the ; I# G- i0 \; f; T5 N
pid controller at start up. 8 F; w/ d3 ^7 ]2 y+ E- n

8 y/ z- B* I9 ~- w) @& c设定输出初始值 ' C- g/ x5 ]0 b3 D
//------------------------
: U) Q2 d! m3 N: @---------------------- : g: K& i. Z7 W& w& p# E
" F2 T7 U! M0 Z0 @& }
void pid_setinteg(struct _pid *pid,float new_integ)
; g8 h& ^& P! N  i. W% d1 y{ 4 ?* U, d' k/ R  M- J3 v. `2 b
pid->integral = new_integ; ' k8 w1 Y( A% U8 c! ^, j
pid->last_error = 0; . V# z& y* d5 k) o, k
}
# O1 t$ M  ]' g. r2 E# D. e: C0 u* l/ E0 b) u& }' D3 S2 U" i# G  J- [! W
//------------------------
5 p! m0 z) q- m* M4 Z---------------------- $ l) L% l5 e: a+ C* A. H
pid_bumpless DESCRIPTION Bumpless transfer
. H/ v9 P& F# dalgorithim.
% {' g* ]1 {, B) y0 N1 j" OWhen suddenly changing setpoints, or when restarting ) }0 z1 s: X4 W
the PID equation after an extended pause, 8 i' p, N: n6 N9 @/ X
the derivative of the equation can cause a bump in the controller output. This function will help smooth out % ~: m; h; Y; v
that bump.
4 s3 `  O- ~0 }9 e( e  ^8 QThe process value in *pv should be the updated just / N; W0 x8 q* ?/ [/ f+ f
before this function is used.
  ]/ Y9 v. n* v4 H8 s% _+ y7 K( b( o; m- H1 R
pid_bumpless 实现无扰切换
4 }' A& z1 k% g9 q当突然改变设定值时,或重新启动后,将引起扰动输出。这
9 y% u( O1 f" X- K) O, H" s  O% _个函数将能实现平顺扰动, 在调用该函数之前需要先更新 PV值
( v4 _: }6 i* g3 _9 u//------------------------. k! N  l, a, {4 E% s
---------------------- : n2 H  k' `8 R: N$ ?; l+ d9 S0 B

: n$ E3 ?& O" h3 h, w' P% Jvoid pid_bumpless(struct _pid *pid) 7 a+ A0 G' W7 O5 G
{ / E( S- `. ?! ^
pid->last_error = (pid->sp)-(pid->pv);  //设定值与反馈值偏差 7 f( j- f& G; \  B
} ; X6 ~7 I8 C/ u' M: A4 |
' {  g* e6 E6 o( A
//------------------------
6 b6 _0 E( T& W7 v---------------------- 5 R% \- `) N: x* k2 r' V
pid_calc  DESCRIPTION  Performs  PID  calculations  for  the
/ f" G; B8 q% F/ k_pid structure *a. This function uses the positional form of the pid " z& D( ~' f. w8 D+ W( ^1 d
equation, and incorporates an integral windup
; v3 U; z! D* r# s" g+ Y# _3 kprevention algorithim. + o* \0 Y/ l/ N* L
Rectangular  integration  is  used,  so  this  function  must . H7 g/ A! ~1 L" C6 ]! f
be repeated on a consistent time basis for accurate
8 ]7 O% W) |. F2 H7 `9 Ncontrol.
, U/ o& {2 D+ `) W2 \1 C1 nRETURN VALUE The new output value for the pid loop. 3 {1 c) q, K: Q' u6 ^
USAGE #include "control.h" & s/ G# b, f$ J- L& F1 K* u% }) T- f# L8 y
本函数使用位置式PID计算方式,并且采取了积分饱和限制运算
' n# I! Q8 j8 H& q' n  vPID计算 9 I' ^2 h  f* a" r5 p
//------------------------
# M% X0 h' N' B. I5 @---------------------- " |+ n" S5 v3 B- \6 X

  p6 ~: y9 [& }6 u, H# ~9 y1 ~float pid_calc(struct _pid *pid)
6 ?6 O; j: ~7 s+ \$ {7 ?( v6 x* Q) U+ K0 `0 ~
int err; / E3 T! [, A$ q* _/ n/ m
float pterm, dterm, result, ferror; 1 ~" m# y: _8 W* `5 |

: f0 s- _  _% I/ ?  R9 j// 计算偏差 ) f, j( f/ s' d
err = (pid->sp) - (pid->pv);
1 S$ t3 Q" B5 d1 ~" M // 判断是否大于死区
: L7 p& p0 N+ r* oif (abs(err) > pid->deadband) 7 J$ ^% s) i( Z  \9 P- S
{ 1 p- O$ v: R6 b' _. q6 w& x3 |8 H
ferror = (float) err;   //do integer to float
7 _8 U2 C* j- t4 p9 S# xconversion only once 数据类型转换 6 `1 r; A  f2 y4 R
+ j- U1 [/ I/ U8 b
// 比例项 7 y+ d6 n' v4 ~/ S" _, k
pterm = pid->pgain * ferror; " Q8 x# I1 c; s' v/ P/ e. h
, k6 ?/ d. O" x) C: o  [
if (pterm > 100 || pterm < -100)
( H) C! H/ z- I{
7 v# {4 V/ f# E& ~% d( Opid->integral = 0.0;
' _4 w9 s4 Z' a* @# k} & {3 L' x: }6 B# U8 {
else
! W4 G! c  R# s8 M{
0 b( b5 [$ Q5 S6 d, ~  d5 v// 积分项 4 c, N4 }3 g. x# ~) Q
pid->integral += pid->igain * ferror; 1 G6 \5 ~4 V  q& t7 a- J

, X1 R/ E5 ^7 |! n$ G// 输出为0--100%
) A5 i: M3 F& a$ A/ ^% M' q+ N  c// 如果计算结果大于100,则等于100
: ?2 y6 H" a/ ~; e/ [1 V0 vif (pid->integral > 100.0) 0 j) O, _. e  ]9 Z* ?: Q& F# {
{ pid->integral = 100.0;
/ k( j) b( o( ]. a( d# n5 r( N  m} 6 ^. h7 W9 w  ]% j+ w  P) Z
// 如果计算结果小于0.0,则等于0
* K- c( [  y9 Q5 C, Z, Qelse if (pid->integral < 0.0) ( l% i( x% v& j
pid->integral = 0.0;
0 X8 }+ p& j# A+ r* K1 R2 u. \& C! y4 s# {# f8 e: U# j5 h7 a3 O: I
}
4 s8 t! {2 {8 \/ Y: Z: W+ e+ @9 G2 v: f5 |( x1 E1 E
// 微分项 + ?. u1 ]! A/ N( T
dterm  =  ((float)(err  -  pid->last_error))  *  pid->dgain;
$ n7 I7 V. y+ c8 }
2 N7 g- I0 z( A; d( L' o' p% hresult = pterm + pid->integral + dterm;
. h; m/ g8 E) u6 u' O. a}
3 x0 {* |* i( U: E3 Q( zelse
' Q$ Q' E2 v0 n% h* dresult = pid->integral; // 在死区范围内,保持现有输出 7 b3 p% b7 I/ Z2 ?+ {0 d
9 R/ }( c& R% ^! b9 F% Y% f( g
// 保存上次偏差
6 C% F! o  b7 ~5 Y# wpid->last_error = err; . c6 o/ k* N* Z- _  i% t* c. A5 ]

& j7 G7 k5 E, {- O/ G# y5 X3 h0 {// 输出PID值(0-100)
/ {# N3 b, g2 J* V, ~7 ^" x$ ireturn (result); }
& F1 x0 ^$ d' r4 ]6 p4 f  |; n$ R" x/ g  r/ R
//------------------------: c$ ?$ z7 q3 E
----------------------
$ c3 d# e* g' evoid main(void) ! e8 ^$ j& j3 o9 d
{
! a8 }9 j% o5 C) `8 `2 p! e0 b( bfloat display_value; 7 ?4 w0 R* e: X- h
int count=0; 7 a9 f1 E7 ~) r# K
pid = &warm; 3 k+ N# A2 b8 }- m4 z2 ^9 C

0 V" a+ Z/ Z+ r5 Z/ s- d+ Q// printf("Enter the values of Process point, Set
. q3 u# e4 \' G4 e0 K8 ~point, P gain, I gain, D gain \n"); " P) L: a, c- X! t' j1 \% |2 i+ d
// scanf("%d%d%f%f%f", &process_point, &set_point, 9 M' A% T4 o# v  j0 L* s; G3 c
&p_gain, &i_gain, &d_gain); 4 k  V3 V; ^; H6 O! S: T% }
3 M+ a1 v) o8 {1 ], C1 F6 C  G, \
// 初始化参数 / x0 E8 O* E; w4 s( x, a& A* T
process_point = 30; 0 o: t/ t7 o: E
set_point = 40;
: Z3 X, [! {+ |: Zp_gain = (float)(5.2); " J1 w  R3 U! `& D# \% E
i_gain = (float)(0.77);
. M/ i& R: V+ B9 V; o4 hd_gain = (float)(0.18); , d# k! z0 n  f0 Y) ]& ?
dead_band = 2;  
" C, b! B: v( Y6 j1 sintegral_val =(float)(0.01);
4 b$ E4 C( R" ?" l+ ?
4 J( W. K# w  {printf("The  values  of  Process  point,  Set  point,  P  gain, - `9 D. I5 R1 T
I gain, D gain \n");
( j- E: e" }3 s. r! o7 Eprintf(" %6d %6d %4f %4f %4f\n", process_point, & [* N) g0 N  Q5 x( s
set_point, p_gain, i_gain, d_gain);
. }, Y( e8 I$ F8 J, t1 H. hprintf("Enter the values of Process point\n");
! m$ ?, r: d% O% iwhile(count<=20)
0 t% D: Q& u- |2 l$ g2 d& U{
; i/ T. u4 L( C! q1 Ascanf("%d",&process_point); , a$ p' i: W& d4 A/ b

. ~8 ?. a2 U  G. @& I: B; b// 设定PV,SP 值 ( l; k  h- q. d
pid_init(&warm, process_point, set_point); + P- |/ \: d; Z+ H
/ h! |8 W6 n# d0 ~# C2 T
// 初始化PID 参数值
) O; |  i8 e) _% ?# y% ^& }pid_tune(&warm, p_gain,i_gain,d_gain,dead_band); 6 b2 m' Q  C  r/ y: h- x  c

7 p/ g8 R4 g8 o6 |& Z9 q5 X// 初始化PID 输出值 9 m( M) B7 T- B" D
pid_setinteg(&warm,0.0);
4 U1 o5 y2 C5 g0 z6 S0 x' M5 i//pid_setinteg(&warm,30.0); 2 Y! @9 F" O. b% W0 @$ E
//Get input value for process point   D- k2 G7 f- \9 D3 ?
pid_bumpless(&warm); 2 ]5 a* P8 X, v1 ^, O8 C3 ?

$ H9 E9 J0 ?5 h9 t8 R; N& S5 ^// how to display output ; }$ @" i7 q' a! [: u) A4 ]
display_value = pid_calc(&warm);   g1 M& M0 _) Y* v
5 h& Q" B% c& W4 _) L
printf("%f\n", display_value); ( Q* B* G7 e4 k& k7 B9 U. ~: d
//printf("\n%f%f%f%f",warm.pv,warm.sp,warm.igain,wa
3 Z, F6 G! v* }rm.dgain);
/ z6 ?$ }5 O8 s
, C' d0 _; A& a3 ]2 Ucount++; 9 a% F# B: y5 f4 h8 {
}
: r( X0 Z9 \( [/ b5 M9 J- N2 @. x4 ]}
/ f0 `+ j3 }1 r& R7 }! M/ ?0 O6 S- G3 n9 K" U6 W. A4 r' V
发表于 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)就足够了。" q! Y; b" U) m3 G% u
回复

使用道具 举报

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

使用道具 举报

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

使用道具 举报

发表于 2014-3-20 09:29 | 显示全部楼层
8055 发表于 2014-3-19 22:11
+ f2 ?) w* ^! P6 \: H( @; l# C; ^: j版主,这个更是针对什么设备的?是不是要根据不同的设备来制定不同的PID算法?比如驱动两个电炉,一个功率1 ...
6 W( X' `6 p% l: _. j
用同一个PID算法没问题,但要处理好PID参数,比如热容量差不多的情况下,两个设备的P参数要差一倍左右。
0 L$ |1 I7 ?0 _* F1 o% Y' S
回复

使用道具 举报

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

使用道具 举报

本版积分规则

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

GMT+8, 2026-2-25 15:07 , Processed in 0.034020 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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