我最近在学习PID算法,对此很感兴趣。因此与大伙分享下,有不足的地方欢迎指出,很是谢谢。c++
PID算法的基本内容本篇博客就不作阐述了,网上有不少资料。算法
文章的主题是用C语言实现PID算法。为了更好的理解,我采用软件模拟室内温度控制的方式与大伙分享。windows
模拟原理很简单,假定23摄氏度为人类温馨的环境。夏天室外温度为37度,咱们想要室内保持23左右。因此须要用到PID算法调节。假设室内温度每分钟上升0.1摄氏度,咱们拥有制冷剂,每释放一包可下降5摄氏度,有制热剂,同理每放出一包可升高五度(一切为理想状态,房间温度到处相同,每次释放温度控制包都能立刻起做用)。函数
实现起来也很简单,假定放出初始时为37摄氏度,则调用制冷剂函数,使房间温度降低,直到23度左右时中止。转而使用PID算法精调,使之更接近于23摄氏度。到这里假如看懂了,咱们能够发散思惟,当实际温度在23正负0.5度以内,咱们中止加热或者制冷,以节省资源,当温度超出此范围时再调用相应的函数。学习
具体请看代码,我写了详细的备注,变量名与函数名都是用单词的意思。测试
<textarea readonly=”readonly” name=”code” class=”c++”> /* 功能:用软件模拟PID控制温度 模型一:夏天要求室内的温度为23摄氏度左右,假定房间受室外高温影响,1分钟上升0.1摄氏度(上限为室外温度37摄氏度)。 释放一包制冷剂可下降5摄氏度,一包制热剂,可上升五度,温度变化由传感器采集,可是我用软件模拟温度变化, 假设一切处于理想状态。 */ #include "SoftSimulation_PID_Header.h" int main() { init_PID(); time_t timep; struct tm *tp; while (1) { if (pid.ActualTemperature < outdoorTemperature) { // 获取时间 time(&timep); tp = gmtime(&timep); AcquireTemperature(tp->tm_hour,tp->tm_min,tp->tm_sec); } PID_controlTemperature(); /* 当温度处于人体温馨的温度时,则不用制冷或加热 */ while (fabs(pid.SetTemperature - pid.ActualTemperature) < ComfortTemperature) { // 获取时间 time(&timep); tp = gmtime(&timep); // 获取计算机时间,10秒钟打印一次 printf("%d/%d/%d %d/%d/%d\n",1900+tp->tm_year,(1+tp->tm_mon),tp->tm_mday,tp->tm_hour + 8, tp->tm_min,tp->tm_sec); printf("Display:设定温度为:%.2f\t室内温度为:%f\t\n\n", pid.SetTemperature, pid.ActualTemperature); // 获取实时温度 AcquireTemperature(tp->tm_hour, tp->tm_min, tp->tm_sec); Sleep(500); } } } /* Acquire indoor Temperature */ void AcquireTemperature(int hour,int minute,int second) { static int lastTime[3] = { 0 }; static float Minute = 0; if (!lastTimeFlag ) { lastTime[0] = hour; lastTime[1] = minute; lastTime[2] = second; lastTimeFlag = 1; } else { if (pid.ActualTemperature <= outdoorTemperature) { Minute = (hour - lastTime[0]) * 60 + (minute - lastTime[1]) + float(second - lastTime[2]) / 60.0; pid.ActualTemperature = pid.ActualTemperature + Minute * IncrementTemperature; lastTime[0] = hour; lastTime[1] = minute; lastTime[2] = second; //printf("Test:设定温度为:%.2f\t室内温度为:%f\t\n\n", pid.SetTemperature, pid.ActualTemperature); } else { // 温度升高过快,只记录时间,程序会在PID_controlTemperature函数中处理 lastTime[0] = hour; lastTime[1] = minute; lastTime[2] = second; } } } /* release the refrigeration */ void Refrigeration() { pid.ActualTemperature = pid.ActualTemperature - 5; } /* release the heating */ void Heating() { pid.ActualTemperature = pid.ActualTemperature + 5; } /* the PID arithmetic control the temperature of indoor */ void PID_controlTemperature() { if (pid.SetTemperature >= pid.ActualTemperature + TemperatureErr) { // 室内温度偏低,调用制热模块 Heating(); } // 室内温度处于23度左右,使用PID精调 else if (fabs(pid.SetTemperature - pid.ActualTemperature) < TemperatureErr) { IncrementPID_realize(); // 增量式PID控制 //PositionPID_realize(); // 位置式PID控制,二者只要一个就能够了,之因此都贴出来,为了试试两种方式有什么不一样 } else if (pid.SetTemperature + TemperatureErr <= pid.ActualTemperature) { // 室内温度偏高,调用制冷模块 Refrigeration(); } else { printf("区间考虑不周全\n"); } } /* initialize PID */ void init_PID() { pid.Kp = 0.2; pid.Ki = 0.015; pid.Kd = 0.2; pid.SetTemperature = 23.0; pid.ActualTemperature = outdoorTemperature; lastTimeFlag = 0; pid.ErrVal[0] = 0; pid.ErrVal[1] = 0; pid.ErrVal[2] = 0; pid.Integral = 0; printf("accomplish initialize PID \n"); } /* 功能:使用增量式PID调节 公式:U(k)+Kp*[E(k)-E(k-1)]+Ki*E(k)+Kd*[E(k)-2E(k-1)+E(k-2)] 备注:网上的公式Kp乘以的东西有两种,第一种是Kp*[E(k)-E(k-1)],第二种是Kp*([E(k)-E(k-1)]+Ki*E(k)+Kd*[E(k)-2E(k-1)+E(k-2)]) 这两种我都测试过,都能实现功能,前者更快一些,后者慢,可是我我的喜欢第二种 */ void IncrementPID_realize() { pid.ErrVal[0] = pid.SetTemperature - pid.ActualTemperature; // 计算设定值与实际值直接的偏差 float Temp0 = pid.Kp * (pid.ErrVal[0] - pid.ErrVal[1]); //float Temp0 = (pid.ErrVal[0] - pid.ErrVal[1]); float Temp1 = pid.Ki * pid.ErrVal[0]; float Temp2 = pid.Kd * (pid.ErrVal[0] - 2 * pid.ErrVal[1] + pid.ErrVal[2]); float Increment = Temp0 + Temp1 + Temp2; // 经过公式计算增量 // float Increment = pid.Kp * (Temp0 + Temp1 + Temp2); // 经过公式计算增量 pid.ActualTemperature += Increment; // 计算实际温度 pid.ErrVal[1] = pid.ErrVal[0]; pid.ErrVal[2] = pid.ErrVal[1]; //printf("Realize:设定温度为:%.2f\t室内温度为:%.2f\t\n\n", pid.SetTemperature, pid.ActualTemperature); } /* 功能:使用位置式PID调节 公式:Kp*(E(k)+Ki*∑E(j)+Kd*[E(k)-E(k-1)]) 备注:参考增量式的备注 */ void PositionPID_realize() { pid.ErrVal[0] = pid.SetTemperature - pid.ActualTemperature; // 计算设定值与实际值直接的偏差 pid.Integral += pid.ErrVal[0]; pid.ActualTemperature = pid.Kp * pid.ErrVal[0] + pid.Ki * pid.Integral + pid.Kd * (pid.ErrVal[0] - pid.ErrVal[1]); pid.ErrVal[1] = pid.ErrVal[0]; } </textarea>
头文件以下:
#include <stdio.h> #include<stdlib.h> #include<math.h> #include<time.h> #include<windows.h> typedef struct PID_Value { float ErrVal[3]; // 三次偏差值 float Kp; // 比例系数 float Ki; // integral 系数 float Kd; // derivative 系数 float Integral; // 积分 float SetTemperature; float ActualTemperature; }PID_ValueStr,*PID_ValueT; PID_Value pid; #define outdoorTemperature 36.2 #define TemperatureErr 3 #define ComfortTemperature 0.5 #define IncrementTemperature 1 // 每分钟房间上升的温度 int lastTimeFlag = 0; void AcquireTemperature(int hour, int minute, int second); void PID_controlTemperature(); void Refrigeration(); void init_PID(); void IncrementPID_realize(); void PositionPID_realize();
如需转载请标明出处,谢谢。ui