|
原文在这里http://www.geek-workshop.com/thread-704-1-1.html;
http://www.geek-workshop.com/thread-197-1-1.html
我先是在他们的程序基础上做的,但是一直有瑕疵,经过总结做了些许修改,得到了比较满意的结果,所以和想大家分享一下
上代码:- /***************************
- PID temp control for Arduino
- by yby
- 数据采集 2014-05-11
-
- PID控制
- PID整定
- 温度控制
- ************头文件***************/
- #include <OneWire.h> //单总线的库
- #include <DallasTemperature.h> //DS18B20头文件
- /************ 传感器参数 ***********/
- #define ONE_WIRE_BUS 2 //2号引脚是温度传感器所在引脚
- OneWire oneWire(ONE_WIRE_BUS);
- DallasTemperature sensors(&oneWire);
- /********** 温度传感器滤波用数值 *********/
-
- double Input=0.0; //定义输入
- float f_angle = 0.0; //滤波前的值
- float f_angle_old=0.0; // 滤波处理后的温度值
- /*********** PID控制器参数 *********/
- unsigned long lastTime; // 前次时间
- float Output = 0.0; // PID输出值
- double lastErr=0.0,lLastErr=0.0; //前一次差值、前前一次差值e(k-1)、e(k-2)
- double q0,q1,q2;
- /************ 程序初始化 ***********/
- void setup() {
- sensors.begin(); //温度传感器初始化
- Serial.begin(9600); // 开启串口以便监视数据
- delay(1000); //1s延时用以准备
- }
- /************** 主程序 *************/
- void loop()
- { unsigned long now = millis(); // 当前时间(ms)
- double timeChange =(double)(now-lastTime)/1000; // 采样时间(s)
-
-
- sensors.requestTemperatures();
- float temp=sensors.getTempCByIndex(0); //温度传感器所采集到的值赋给temp
- f_angle= temp; //temp赋给 f_angle用以消抖滤波
- /************** 温度传感器消抖滤波*************/
- if((f_angle_old-f_angle>3)||(f_angle-f_angle_old>3)) //温度是一个大滞后,缓变值,所以如果突然变化超过3度,
- Input = f_angle_old; //其实一度以上就可以把它当作噪声消除了
- else Input = f_angle; //此处滤波只是为了消除他对PID输出的影响,因此实际图表中仍能看到抖波
-
-
-
- /************ PID控制器 ***********/
- now = millis(); // 当前时间(ms)
-
- float Kp = 0.54009801, KI =10.2, Kd =0.00973,Ki=0.0; // 比例系数、积分系数、微分系数
- float SampleTime = 4.35; // 采样时间(s)
- float Setpoint = 70; // 设定目标值(degree)
- float outMin = 0, outMax = 50; // 输出上限、输出下限 阀值最高只有50度,
- if(timeChange >= SampleTime) //判断是否到达设定采样时间
- {double error = (double)Setpoint - Input; // 偏差值
-
-
- // if((Setpoint-Input<0.4)&&(Setpoint-Input>0)) //这段注解了,可以作为积分分离PID,这样就可以只在一定范围内用PID
- // {Ki=KI;} //为追求快速达到预定值的效果,可以在此范围以外使用PI调节
- // else if((Setpoint-Input>-0.8)&&(Setpoint-Input<0))
- // {Ki=KI;}
- // else Ki=0;
-
-
- q0=(100.0/Kp)*(1+1/KI+Kd/1); //这就是我与原本那几位用的不一样的公式的地方
- q1=-(100.0/Kp)*(1+2*Kd/1); //公式我会在后面列出来
- q2=(100.0/Kp)*Kd/1; //主要原因我认为是出在积分处,原帖的积分计算方式虽然在数学层面上是一样的,但是在编程时还是有问题
- Output = q0* error +q1*lastErr+q2*lLastErr; // 计算输出值
- Output = constrain(Output, outMin, outMax); // 限定值域
-
- analogWrite(5,Output/140*255); //我的加热器是用的PTC加热板,驱动方式是用的大功率电机驱动板,所以这里直接用PWM驱动5号引脚就好了
- //140是PTC满载时所能达到的温度,到140就恒温了
- lLastErr = lastErr; //上一次的偏差值已经成为上上次的偏差值了
- lastErr = error; //这次的偏差值已经成为上一次的了
- lastTime = now; // 记录本次时间
- }
- /************ 参数上传 ***********/
- // Serial.print(now); // 计算时间
- // Serial.print(",");
- Serial.print(f_angle, 6); // 输出的温度
- Serial.print(",");
- Serial.println(Output,6); // PID输出阀值
- // Serial.println(";");
- delay(90);
- f_angle_old = f_angle; //滤波对比用的,和上面的原理一样
- }
复制代码
这个公式的主要好处在于比之前使用的公式在实际使用中积分能力更强,虽然不是很明白为什么,
大概是因为计算时,位置型PID更适合计算机编程使用吧,这是书上写的。
下面是波形图
第一张是用的原帖的算法,可以看出曲线一直在震荡,我试了很多积分值大概测了20组左右都无法得到满意的波形
最后一张是用新算法得到的,效果还不错
备注一下:在曲线图中,灰色直线是设定的目标值,蓝颜色曲线是控制过程中的输出阀值,红色曲线为实际所测温度变化曲线。
图中的蓝色曲线之所以看起来不是圆滑的曲线就是因为温控系统有很大的滞后性,所以在采样频率上设置的非常低,接近5s左右,因此我们会看到锯齿状边缘。
单纯的比例调节和PD调节都具有一定的局限性。滞后性的存在更需要积分环节,不然温度永远无法达到恒温(此处恒温也是有一定的区间+/-0.5摄氏度)。
PID参数的整定有很多方法,网上有很多,我就不赘述了,个人觉得最重要的还是多做实验,自然就会找到其中的规律,第一次发帖如有谬误请多包含
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|