STM32 温度 PID 控制系统实现

简介: 基于 STM32 的温度 PID 控制系统实现方案,包括硬件设计、软件编程和 PID 算法实现。

STM32 温度 PID 控制系统实现

基于 STM32 的温度 PID 控制系统实现方案,包括硬件设计、软件编程和 PID 算法实现。

系统架构

温度传感器 → STM32 ADC → PID 算法 → PWM 输出 → 加热元件
   ↑
温度设定值

硬件组件

  1. STM32 微控制器(如 STM32F103C8T6)
  2. 温度传感器(如 NTC 热敏电阻、DS18B20 或 PT100)
  3. 加热元件(如电阻丝、Peltier 元件)
  4. 驱动电路(MOSFET 或继电器)
  5. 显示设备(可选,如 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 参数整定方法

  1. 手动整定法

    • 先将 Ki 和 Kd 设为 0,增加 Kp 直到系统开始振荡
    • 将 Kp 设为振荡时值的一半
    • 增加 Ki 直到稳态误差在可接受范围内
    • 增加 Kd 以减少超调和提高稳定性
  2. 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

系统优化建议

  1. 抗积分饱和:已在本实现中包含,防止积分项过大导致系统不稳定
  2. 输出限幅:限制 PWM 输出在合理范围内
  3. 采样时间:根据系统时间常数选择合适的 PID 计算频率
  4. 滤波处理:对温度采样值进行滤波处理,减少噪声影响
  5. 自动整定:可扩展实现 PID 参数自动整定功能

应用注意事项

  1. 安全保护:添加过热保护、看门狗定时器等安全机制
  2. 加热元件:根据加热元件特性调整 PWM 频率(电阻丝适合低频,Peltier 元件适合较高频率)
  3. 温度传感器:根据精度要求选择合适的传感器,并考虑校准
  4. 系统响应:不同加热/冷却系统的响应特性不同,需要相应调整 PID 参数
相关文章
|
1天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1055 0
|
10天前
|
人工智能 运维 安全
|
1天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
241 0
|
8天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
8天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
725 23