【STM32 基于PID的闭环电机控制系统】

STM32 基于PID的闭环电机控制系统

目录

  • STM32 基于PID的闭环电机控制系统
  • 一、PID算法在STM32F103C8T6中的实现思路
  • 二、代码实现与解释
  • 三、PID算法的调试与优化
  • 四、总结
  • 一、PID算法在STM32F103C8T6中的实现思路

    1. 基本概念
      • 目标 :通过PID算法调节电机的转速,使其保持恒定或按照给定的曲线变化。
      • 硬件配置 :
      ○ STM32F103C8T6作为主控制器。
      ○ 电机驱动模块(如L298N、 DRV8825 等)。
      ○ 用于测量电机转速的编码器或霍尔传感器。
      ○ 可选:用于显示目标值和实际值的LCD或LED指示灯。
    2. STM32F103C8T6的功能模块
      • PWM输出 :通过PWM信号控制电机的转速(占空比调节)。
      • ADC输入 :采集编码器或传感器的反馈信号(如转速反馈)。
      • 定时器中断 :用于定期采样系统的反馈信号,并执行PID计算。
    3. PID算法实现的步骤
      1. 初始化STM32的PWM、ADC和定时器模块。
      2. 采集当前系统的状态(如转速)。
      3. 计算PID输出(控制量)。
      4. 根据PID输出调节PWM信号的占空比,控制电机转速。
      5. 重复上述过程,形成闭环控制。

    二、代码实现与解释

    1. 头文件与配置 (main.h)
    #ifndef __MAIN_H
    #define __MAIN_H
    
    #include "stm32f10x.h"
    
    // 定义PWM输出引脚
    #define PWM	GPIO_Pin_6  // PB6用于PWM输出
    
    // 定义ADC输入引脚
    #define ADC_CHANNEL ADC_Channel_0  // 通道0用于转速反馈
    
    // 定义PID参数
    #define KP 2.0f    // 比例系数
    #define KI 1.0f    // 积分系数
    #define KD 0.5f    // 微分系数
    
    // 定义系统参数
    #define SAMPLE_TIME 20  // 采样时间(ms)
    #define SYSTEM_FREQUENCY 1000  // 系统频率(Hz)
    
    #endif
    
    1. PID控制函数 (pid.h 和 pid.c)
      头文件 (pid.h) :
    #ifndef __PID_H
    #define __PID_H
    #include <stdint.h>
    #include <math.h>
    
    typedef struct {
        float setpoint;         // 目标值
        float processVariable;  // 实际值
        float output;           // 输出值
        float integ;            // 积分项
        float lastError;        // 上次误差
        float kp;               // 比例系数
        float ki;               // 积分系数
        float kd;               // 微分系数
    } PID_HandleTypeDef;
    
    void PID_Init(PID_HandleTypeDef *pid, float setpoint, float kp, float ki, float kd);
    void PID_Update(PID_HandleTypeDef *pid, float processVariable);
    float PID_GetOutput(PID_HandleTypeDef *pid);
    #endif
    

    实现文件 (pid.c) :

    #include "pid.h"
    
    void PID_Init(PID_HandleTypeDef *pid, float setpoint, float kp, float ki, float kd) {
        pid->setpoint = setpoint;
        pid->processVariable = 0;
        pid->output = 0;
        pid->integ = 0;
        pid->lastError = 0;
        pid->kp = kp;
        pid->ki = ki;
        pid->kd = kd;
    }
    
    void PID_Update(PID_HandleTypeDef *pid, float processVariable) {
        float error = pid->setpoint - processVariable;
        float deltaError = error - pid->lastError;
        
        // 计算积分项(避免积分饱和)
        pid->integ += error;
        if (pid->integ > 1000) pid->integ = 1000;
        if (pid->integ < -1000) pid->integ = -1000;
        
        // 计算PID输出
        pid->output = pid->kp * error + pid->ki * pid->integ + pid->kd * deltaError;
        
        // 限制输出范围
        if (pid->output > 100) pid->output = 100;
        if (pid->output < 0) pid->output = 0;
        
        pid->lastError = error;
    }
    
    float PID_GetOutput(PID_HandleTypeDef *pid) {
        return pid->output;
    }
    
    1. 主程序 (main.c)
      代码实现 :
    #include "main.h"
    #include "pid.h"
    
    // 初始化PWM和ADC
    void System_Init(void) {
        GPIO_InitTypeDef GPIO_InitStructure;
        ADC_InitTypeDef ADC_InitStructure;
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
        NVIC_InitTypeDef NVIC_InitStructure;
        
        // 使能外设时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph(TIM2), ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        
        // 配置PWM引脚(PB6)
        GPIO_InitStructure.GPIO_Pin = PWM;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        
        // 配置ADC引脚(PB0)
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        
        // 配置ADC
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
        ADC_InitStructure.ADC_ScanConvMode = ADC_ScanConvMode_Disable;
        ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC2;
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
        ADC_InitStructure.ADC_NbrOfChannel = 1;
        ADC_Init(ADC1, &ADC_InitStructure);
        ADC_ChannelConfig(ADC1, ADC_CHANNEL, ADC_SampleTime_13Cycles5);
        ADC_Cmd(ADC1, ENABLE);
        
        // 配置定时器 TIM2,用于PWM和PID周期
        TIM_TimeBaseInitStruct.TIM_Period = 10000; // 10 kHz
        TIM_TimeBaseInitStruct.TIM_Prescaler = 72 - 1; // 72 MHz / 72 = 1 MHz
        TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD不分频;
        TIM_TimeBase_Init(TIM2, &TIM_TimeBaseInitStruct);
        TIM_Cmd(TIM2, ENABLE);
        
        // 配置PWM输出
        TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
        TIM_OCInitStruct.TIM_Pulse = 0;
        TIM_OC_InitStruct TIM_Channel_1, &TIM_OCInitStruct);
        
        // 配置定时器中断(用于采样和PID计算)
        TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
        NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_Init(&NVIC_InitStructure);
    }
    
    // 定时器中断服务程序
    void TIM2_IRQHandler(void) {
        if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
            // 清除中断标志
            TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
            
            // 采集ADC值(模拟电机转速)
            ADC_SoftwareStartConv(ADC1);
            while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
            float processVariable = ADC_GetConversionValue(ADC1);
            
            // PID计算
            PID_Update(&pidHandle, processVariable);
            float output = PID_GetOutput(&pidHandle);
            
            // 更新PWM占空比
            TIM_SetCompare1(TIM2, output * 100);  // 100%对应10000
        }
    }
    
    int main(void) {
        // 初始化系统
        System_Init();
        
        // 初始化PID
        PID_Init(&pidHandle, 50.0f, KP, KI, KD);  // 目标值为50
        pidHandle.integ = 0;
        pidHandle.lastError = 0;
        
        // 进入主循环
        while (1) {
            // 可选:更新目标值或显示当前状态
            // 这里为空循环,由中断服务程序处理PID控制
        }
    }
    

    三、PID算法的调试与优化

    1. 参数调整
      • Kp(比例系数) :过大可能导致系统振荡,过小则响应速度慢。
      • Ki(积分系数) :过大可能导致积分饱和,过小则无法消除静差。
      • Kd(微分系数) :过大可能导致系统不稳定,过小则无法有效抑制动态误差。
    2. 调试技巧
      • 从小的Kp值开始,逐步增大,直到系统开始振荡。
      • 适当增加Ki,确保系统在稳态时无静差。
      • 调整Kd,减少系统的超调和振荡。
    3. 实际应用中的考虑
      • 采样时间 :根据系统的动态特性选择合理的采样时间,以确保闭环控制的稳定性。
      • 抗噪声处理 :在ADC采样和PID计算中加入滤波处理,避免噪声干扰。
      • 输出限制 :防止PWM输出超过硬件限制(如0-100%占空比)。

    四、总结

    通过上述代码示例,可以在STM32F103C8T6微控制器上实现基于PID算法的电机控制。PID算法的核心在于参数的合理选择和系统的稳定性调试。通过不断实验和调整,可以实现高精度的电机控制。后续可以根据具体需求,优化PWM输出策略、加入更加复杂的控制逻辑(如前馈控制)或扩展系统的功能(如多电机同步控制)。

    作者:楼台的春风

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32 基于PID的闭环电机控制系统】

    发表回复