STM32 pid自整定+pid控温+pwm输出 源程序
主要是利用继电器反馈法进行pid参数自动整定。若测出了系统的一阶模型,或得出了系统的临界比例增益,则可很容易地设计出PID调节器。继电型自整定的基本想法是,在控制系统中设置两种模态:测试模态和调节模态。在测试模态下,调节器自动转换成位式调节,即当测量值小于设定值时,调节器输出为满量程,反之为零,使系统产生振荡,振荡过程中调节器自动提取被控对象的特征参数;而在调节模态下由系统的特征参数首先得出PID控制器,然后,由此控制器对系统进行调节。当需要PID参数整定时,开关置于调整处,系统按继电反馈建立起稳定的极限环振荡后,就可以根据系系统响应特征确定PID参数。自整定计算完成后开关置于调节处,系统进入正常控制。内含STM32程序,包含DS18B20读取温度
STM32单片机源程序如下:
#include './pid/pid.h'
#include './led/bsp_led.h'
#include './GeneralTim/bsp_GeneralTim.h'
PID pid; //存放PID算法所需要的数据
uint16_t pw;
uint16_t save_buff[9000] = {0}; //保存数据缓存
extern volatile uint16_t Step_Auto;
extern volatile uint32_t PID_cool_cnt;
extern volatile uint32_t PID_heat_cnt;
extern volatile uint16_t pid_self_first_status_flag; //标志位
extern volatile uint16_t PID_Deal,PID_auto_Deal;
extern volatile uint16_t zero_across_counter;
extern volatile uint16_t k_pid_self_counter;
extern volatile uint16_t CCR4_Val;
extern volatile uint16_t kkk;
void PID_Calc() //pid计算
{
float DelEk;
float out;
if (PID_Deal == 1)
{
pid.Ek=pid.Sv-pid.Pv; //得到当前的偏差值
pid.SEk+=pid.Ek; //历史偏差总和
DelEk=pid.Ek-pid.Ek_1; //最近两次偏差之差
pid.Pout=pid.Kp*pid.Ek; //比例输出
pid.Iout=pid.Ki*pid.SEk; //积分输出
pid.Dout=pid.Kd*DelEk; //微分输出
out= pid.Pout+ pid.Iout+ pid.Dout;
kkk++;
if(out>255)
{
kkk=0;
pid.SEk=0;
}
if(out>255)
{
pid.OUT=255;
}
else if(out<0)
{
pid.OUT=0;
}
else
{
pid.OUT=out;
}
pid.Ek_1=pid.Ek; //更新偏差
// printf ( '\r\n%.1f\r\n',out);
PID_out();
}
}
void PID_out() //输出PID运算结果到负载---每1ms被调用1次
{
uint16_t kk;
if (pid.Pv>pid.Sv)//当前温度大于用户设定温度
{
CCR4_Val=100;
GENERAL_TIM_Init();
}
else
{
kk=100-pid.OUT*100/255;
CCR4_Val=kk;
GENERAL_TIM_Init();
// printf ( '\r\n%3d\r\n',kk);
}
}
void PID_auto()//继电器反馈法自整定pid参数
{
uint8_t i = 0;
float KC = 0;
float TC = 0;
float V_temp = 0,min_temp = 0,max_temp = 0;
float TIME_Hight=0,TIME_LOW=0;
//第一步进入比较初始温度 确定此时温度处于哪种情况
if (PID_auto_Deal== 0)
{
/******************************************************************************************************/
PID_Deal = 0;//退出pid
//程序第一次进入 查看对比当前温度和设定温度
if(pid.Pv < pid.Sv1)//当前温度低于设定温度
{
PID_heat_cnt++; //热
PID_cool_cnt = 0;
if(PID_heat_cnt >= 3)//连续3次结果
{
CCR4_Val=0;//加热
GENERAL_TIM_Init();
if(Step_Auto == 0)
{
Step_Auto = 1;
zero_across_counter++;
// printf ( '\r\n1');
}
}
}
else//当前温度 大于 设定温度 停止加热
{
PID_cool_cnt++;
PID_heat_cnt = 0;
if(PID_cool_cnt > 3)
{
CCR4_Val=100; //不加热
GENERAL_TIM_Init();
if(Step_Auto == 1) //设定温度高于当前温度
{
Step_Auto = 0;
zero_across_counter++;
}
}
}
if(PID_heat_cnt >= 65535)//连续3次结果
{
PID_heat_cnt=65535;
}
if(PID_cool_cnt >= 65534)//连续3次结果
{
PID_cool_cnt=65534;
}
/*****************开始计算强行振荡的周期和幅值****************************/
if((zero_across_counter == 3 ) || (zero_across_counter == 4 ))
{
save_buff[k_pid_self_counter] = pid.Pv;
k_pid_self_counter++;
if(k_pid_self_counter >=9000)
{
k_pid_self_counter = 0;
}
}
else if(zero_across_counter == 5 )//5次过0 则说明出现了振荡 整定成功
{
PID_Deal = 1;
PID_auto_Deal = 1;
zero_across_counter = 0;
max_temp=save_buff[0];
min_temp=save_buff[0];
for(i = 0;i < k_pid_self_counter;i++)
{
if(save_buff[i] >= max_temp)
{
max_temp = save_buff[i];
TIME_LOW=i;
}
if(save_buff[i] <= min_temp)
{
min_temp = save_buff[i];
TIME_Hight=i;
}
}
V_temp = max_temp - min_temp; //最大减最小就是幅值
KC = 127/V_temp;
//如果记录了 最低温度 与 最高温度对应的时间 那么沿用这个公式:TC = 2 * (TIME_Hight - TIME_LOW);
TC = k_pid_self_counter; //TC =2 * (TIME_Hight - TIME_LOW);
pid.Kp = 0.6*KC;
pid.Ki = (0.6*KC)/(0.5*TC)/10;
pid.Kd = (0.6*KC)*(0.125*TC)/60;
printf ( '\r\n整定成功');
}
}
}
复制代码
#include 'stm32f10x.h'
#include 'core_cm3.h'
#include './systick/bsp_SysTick.h'
#include './led/bsp_led.h'
#include './usart/bsp_usart.h'
#include './ds18b20/bsp_ds18b20.h'
#include './pid/pid.h'
#include './timer/timer.h'
#include './AdvanceTim/bsp_AdvanceTim.h'
#include './GeneralTim/bsp_GeneralTim.h'
volatile uint32_t time = 0; // ms 计时变量
volatile uint32_t time2 = 0; // ms 计时变量
float TM1,TM2;
volatile uint8_t ucDs18b20Id[8];
volatile uint16_t Step_Auto;
volatile uint32_t PID_cool_cnt;
volatile uint32_t PID_heat_cnt;
volatile uint16_t pid_self_first_status_flag; //标志位
volatile uint16_t PID_Deal,PID_auto_Deal;
volatile uint16_t zero_across_counter;
volatile uint16_t k_pid_self_counter;
volatile uint16_t CCR4_Val;
volatile uint16_t kkk;
//volatile uint16_t save_buff[9000] = {0};
void delay();
void PID_Init()
{
pid.Sv=30; //用户设定温度
pid.OUT0=1;
zero_across_counter=0;
PID_cool_cnt=0;
PID_heat_cnt=0;
PID_Deal=0;
PID_auto_Deal=0;
pid_self_first_status_flag = 0;
k_pid_self_counter=0;
if( pid.Pv <= pid.Sv) //设定温度高于当前温度
{
pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;
pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;
pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;
pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;
pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;
}
// CCR4_Val=100;
// PID_auto_Deal=1;
// PID_Deal=1;
// pid.Sv=30;
// pid.Kp=38.0999985;
// pid.Ki=0.21666673;
// pid.Kd=2.8575;
}
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
delay();
CCR4_Val=50;
ADVANCE_TIM_Init();
GENERAL_TIM_Init();
/* 配置SysTick 为1us中断一次 */
SysTick_Init();
// /* 端口初始化 */
// LED_GPIO_Config();//初始化了PB1口
USART_Config(); //初始化串口1
printf('\r\n this is a ds18b20 test demo \r\n');
while( DS18B20_Init() )
printf('\r\n no ds18b20 exit \r\n');//初始化DS18B20,不初始化就等待这里?
printf('\r\n ds18b20 exit \r\n');
DS18B20_ReadId(ucDs18b20Id);
TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
pid.Pv=TM1;
PID_Init(); //参数初始化
pid.Sv1=28;
while(1)
{
printf ( '\r\n%.1f',TM1); // 打印通过 DS18B20 序列号获取的温度值
Delay_ms(10);
TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
pid.Pv=TM1;//当前温度
pid.Pv=TM1;//当前温度
PID_auto();
PID_Calc(); //pid计算
// printf ( '\r\n%.1f',TM1); // 打印通过 DS18B20 序列号获取的温度值
// Delay_ms(1000);
// if ( time >= 10 ) /* 10 * 1 ms = 10ms 时间到 */
// {
// time = 0;
// TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
// pid.Pv=TM1;//当前温度
// }
// if ( time2 >= 1000 ) /* 1000 * 1 ms = 1s 时间到 */
// {
// time2 = 0;
// pid.Pv=TM1;//当前温度
// PID_auto();
// PID_Calc(); //pid计算
// }
}
}
void delay(void)
{
int i;
for(i=0; i<10000000; i++)
;
}
复制代码
代码工程文件51hei附件下载:
代码.7z(197.13 KB, 下载次数: 15)