STM32 温度 PID 控制系统实现
基于 STM32 的温度 PID 控制系统实现方案,包括硬件设计、软件编程和 PID 算法实现。
系统架构
温度传感器 → STM32 ADC → PID 算法 → PWM 输出 → 加热元件
↑
温度设定值
硬件组件
- STM32 微控制器(如 STM32F103C8T6)
- 温度传感器(如 NTC 热敏电阻、DS18B20 或 PT100)
- 加热元件(如电阻丝、Peltier 元件)
- 驱动电路(MOSFET 或继电器)
- 显示设备(可选,如 OLED 显示屏)
软件实现
1. PID 控制器头文件 (pid_controller.h)
#ifndef PID_CONTROLLER_H
#define PID_CONTROLLER_H
typedef struct {
float Kp; // 比例增益
float Ki; // 积分增益
float Kd; // 微分增益
float integral; // 积分项累积
float prev_error; // 上一次误差
float output_min; // 输出最小值
float output_max; // 输出最大值
float integral_min; // 积分项最小值
float integral_max; // 积分项最大值
} PIDController;
// 函数声明
void PID_Init(PIDController* pid, float Kp, float Ki, float Kd, float min, float max);
float PID_Calculate(PIDController* pid, float setpoint, float measured);
void PID_Reset(PIDController* pid);
void PID_SetTunings(PIDController* pid, float Kp, float Ki, float Kd);
void PID_SetOutputLimits(PIDController* pid, float min, float max);
void PID_SetIntegralLimits(PIDController* pid, float min, float max);
#endif
2. PID 控制器实现 (pid_controller.c)
#include "pid_controller.h"
#include <math.h>
// 初始化PID控制器
void PID_Init(PIDController* pid, float Kp, float Ki, float Kd, float min, float max) {
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->output_min = min;
pid->output_max = max;
pid->integral_min = -1000.0f; // 默认值,可通过PID_SetIntegralLimits修改
pid->integral_max = 1000.0f; // 默认值,可通过PID_SetIntegralLimits修改
}
// 计算PID输出
float PID_Calculate(PIDController* pid, float setpoint, float measured) {
// 计算误差
float error = setpoint - measured;
// 比例项
float proportional = pid->Kp * error;
// 积分项(带抗积分饱和)
pid->integral += error;
// 积分项限幅
if (pid->integral > pid->integral_max) {
pid->integral = pid->integral_max;
} else if (pid->integral < pid->integral_min) {
pid->integral = pid->integral_min;
}
float integral_term = pid->Ki * pid->integral;
// 微分项
float derivative = error - pid->prev_error;
float derivative_term = pid->Kd * derivative;
pid->prev_error = error;
// 计算总输出
float output = proportional + integral_term + derivative_term;
// 输出限幅
if (output > pid->output_max) {
output = pid->output_max;
} else if (output < pid->output_min) {
output = pid->output_min;
}
return output;
}
// 重置PID控制器
void PID_Reset(PIDController* pid) {
pid->integral = 0.0f;
pid->prev_error = 0.0f;
}
// 设置PID参数
void PID_SetTunings(PIDController* pid, float Kp, float Ki, float Kd) {
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
}
// 设置输出限幅
void PID_SetOutputLimits(PIDController* pid, float min, float max) {
pid->output_min = min;
pid->output_max = max;
}
// 设置积分项限幅
void PID_SetIntegralLimits(PIDController* pid, float min, float max) {
pid->integral_min = min;
pid->integral_max = max;
}
3. 温度传感器接口 (temperature_sensor.c)
这里以 NTC 热敏电阻为例:
#include "temperature_sensor.h"
#include "main.h"
#include "adc.h"
#include <math.h>
// NTC参数
#define NTC_BETA 3950.0f // B值
#define NTC_R25 10000.0f // 25°C时的电阻值
#define SERIES_RESISTOR 10000.0f // 分压电阻
// 读取ADC值并转换为温度
float Read_Temperature(void) {
uint32_t adc_value = 0;
float voltage, ntc_resistance, temperature;
// 启动ADC转换
HAL_ADC_Start(&hadc1);
// 等待转换完成
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
adc_value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
// 计算电压 (假设VREF = 3.3V)
voltage = (adc_value * 3.3f) / 4095.0f;
// 计算NTC电阻
ntc_resistance = SERIES_RESISTOR * (3.3f / voltage - 1.0f);
// 使用Steinhart-Hart方程计算温度
float steinhart;
steinhart = ntc_resistance / NTC_R25; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= NTC_BETA; // 1/B * ln(R/Ro)
steinhart += 1.0f / (25.0f + 273.15f); // + (1/To)
steinhart = 1.0f / steinhart; // 取倒数
temperature = steinhart - 273.15f; // 转换为摄氏度
return temperature;
}
4. 主应用程序 (main.c)
#include "main.h"
#include "stm32f1xx_hal.h"
#include "pid_controller.h"
#include "temperature_sensor.h"
#include <stdio.h>
// 全局变量
PIDController temp_pid;
float temperature_setpoint = 50.0f; // 目标温度 50°C
float current_temperature = 0.0f;
float pid_output = 0.0f;
// 外设句柄
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
void Update_PWM_Duty(float duty_percent);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
// 初始化PID控制器
// Kp, Ki, Kd 需要根据实际系统调整
PID_Init(&temp_pid, 10.0f, 0.1f, 1.0f, 0.0f, 100.0f);
PID_SetIntegralLimits(&temp_pid, -100.0f, 100.0f);
// 启动PWM定时器
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
// 设置初始PWM占空比为0%
Update_PWM_Duty(0.0f);
char msg[50];
sprintf(msg, "Temperature PID Control Started\r\n");
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
while (1) {
// 读取当前温度
current_temperature = Read_Temperature();
// 计算PID输出
pid_output = PID_Calculate(&temp_pid, temperature_setpoint, current_temperature);
// 更新PWM输出
Update_PWM_Duty(pid_output);
// 打印调试信息(可选)
static uint32_t last_print = 0;
if (HAL_GetTick() - last_print > 1000) {
// 每秒打印一次
sprintf(msg, "Set: %.1f°C, Actual: %.1f°C, PWM: %.1f%%\r\n",
temperature_setpoint, current_temperature, pid_output);
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
last_print = HAL_GetTick();
}
// 延时,控制PID计算频率(例如每100ms计算一次)
HAL_Delay(100);
}
}
// 更新PWM占空比
void Update_PWM_Duty(float duty_percent) {
// 限制占空比在0-100%范围内
if (duty_percent < 0.0f) duty_percent = 0.0f;
if (duty_percent > 100.0f) duty_percent = 100.0f;
// 计算PWM比较值
uint32_t pulse = (uint32_t)((duty_percent / 100.0f) * htim2.Init.Period);
// 更新PWM占空比
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);
}
// 系统时钟配置
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {
0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {
0};
// 配置HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
// ADC初始化
static void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {
0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
// 配置ADC通道
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
// PWM定时器初始化
static void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {
0};
TIM_MasterConfigTypeDef sMasterConfig = {
0};
TIM_OC_InitTypeDef sConfigOC = {
0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1; // 72MHz/72 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1; // 1MHz/1000 = 1kHz PWM频率
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_PWM_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比为0
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}
// USART初始化(用于调试输出)
static void MX_USART1_UART_Init(void) {
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
// GPIO初始化
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {
0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置PWM输出引脚 (PA0 - TIM2_CH1)
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置ADC输入引脚 (PA1 - ADC1_IN1)
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
PID 参数整定方法
手动整定法:
- 先将 Ki 和 Kd 设为 0,增加 Kp 直到系统开始振荡
- 将 Kp 设为振荡时值的一半
- 增加 Ki 直到稳态误差在可接受范围内
- 增加 Kd 以减少超调和提高稳定性
Ziegler-Nichols 方法:
- 先将 Ki 和 Kd 设为 0
- 增加 Kp 直到系统开始等幅振荡(临界振荡)
- 记录临界增益 Ku 和振荡周期 Tu
- 根据下表设置 PID 参数:
控制器类型 | Kp | Ki | Kd |
---|---|---|---|
P | 0.5Ku | - | - |
PI | 0.45Ku | 0.54Ku/Tu | - |
PID | 0.6Ku | 1.2Ku/Tu | 0.075KuTu |
参考代码 利用STM32进行温度PID控制 www.youwenfan.com/contentald/56674.html
系统优化建议
- 抗积分饱和:已在本实现中包含,防止积分项过大导致系统不稳定
- 输出限幅:限制 PWM 输出在合理范围内
- 采样时间:根据系统时间常数选择合适的 PID 计算频率
- 滤波处理:对温度采样值进行滤波处理,减少噪声影响
- 自动整定:可扩展实现 PID 参数自动整定功能
应用注意事项
- 安全保护:添加过热保护、看门狗定时器等安全机制
- 加热元件:根据加热元件特性调整 PWM 频率(电阻丝适合低频,Peltier 元件适合较高频率)
- 温度传感器:根据精度要求选择合适的传感器,并考虑校准
- 系统响应:不同加热/冷却系统的响应特性不同,需要相应调整 PID 参数