位置式PID调节PWM占空比控制电动机转速的程序方案

2025-12-15

位置式PID调节PWM占空比控制电动机转速的程序方案

位置式PID控制原理

位置式PID算法的离散形式为:

其中:

  • :当前时刻控制输出(PWM占空比)
  • :当前误差 = 目标转速 – 实际转速
  • :误差累积和(积分项)
  • :PID参数

硬件配置

所需外设

  • 编码器接口:测量电机转速
  • PWM输出:控制电机驱动
  • 定时器:用于定时采样和PID计算

引脚配置示例

// PWM输出引脚 (控制电机)
#define MOTOR_PWM_PIN    GPIO_Pin_8
#define MOTOR_PWM_PORT   GPIOA
#define MOTOR_PWM_TIM    TIM1

// 编码器输入引脚
#define ENCODER_A_PIN    GPIO_Pin_6
#define ENCODER_B_PIN    GPIO_Pin_7
#define ENCODER_PORT     GPIOA
#define ENCODER_TIM      TIM3

完整程序代码

1. PID结构体定义

typedef struct {
    float Target;       // 目标转速
    float Current;      // 当前转速
    float Error;        // 当前误差
    float LastError;    // 上一次误差
    float PreError;     // 上上次误差
    float Kp;          // 比例系数
    float Ki;          // 积分系数
    float Kd;          // 微分系数
    float Integral;    // 积分累计
    float IntegralMax; // 积分限幅
    float Output;      // PID输出
    float OutputMax;   // 输出限幅
    float OutputMin;   // 输出下限
} PID_TypeDef;

// 初始化PID参数
void PID_Init(PID_TypeDef *pid, float kp, float ki, float kd, float max_output)
{
    pid->Kp = kp;
    pid->Ki = ki;
    pid->Kd = kd;
    pid->Integral = 0;
    pid->Target = 0;
    pid->Current = 0;
    pid->Output = 0;
    pid->OutputMax = max_output;
    pid->OutputMin = 0;
    pid->IntegralMax = max_output * 0.8f; // 积分限幅为输出的80%
}

2. 位置式PID计算函数

// 位置式PID计算
float PID_Calculate(PID_TypeDef *pid, float target, float current)
{
    float increment = 0;
    
    // 更新目标值和当前值
    pid->Target = target;
    pid->Current = current;
    
    // 计算误差
    pid->PreError = pid->LastError;
    pid->LastError = pid->Error;
    pid->Error = pid->Target - pid->Current;
    
    // 积分项处理(带积分限幅和抗积分饱和)
    pid->Integral += pid->Error;
    
    // 积分限幅
    if (pid->Integral > pid->IntegralMax) {
        pid->Integral = pid->IntegralMax;
    } else if (pid->Integral < -pid->IntegralMax) {
        pid->Integral = -pid->IntegralMax;
    }
    
    // 位置式PID计算
    pid->Output = pid->Kp * pid->Error + 
                  pid->Ki * pid->Integral + 
                  pid->Kd * (pid->Error - pid->LastError);
    
    // 输出限幅
    if (pid->Output > pid->OutputMax) {
        pid->Output = pid->OutputMax;
    } else if (pid->Output < pid->OutputMin) {
        pid->Output = pid->OutputMin;
    }
    
    return pid->Output;
}

3. 编码器速度测量

// 编码器相关变量
volatile int32_t EncoderCount = 0;
volatile int32_t LastEncoderCount = 0;
volatile float MotorSpeed = 0;     // 转速 RPM

// 编码器计数读取(在编码器中断中调用)
void Encoder_Update(void)
{
    EncoderCount = (int32_t)TIM_GetCounter(ENCODER_TIM);
    TIM_SetCounter(ENCODER_TIM, 0); // 清零计数器
}

// 速度计算函数(在定时中断中调用,如10ms)
void Speed_Calculate(void)
{
    static uint32_t last_time = 0;
    uint32_t current_time = HAL_GetTick();
    uint32_t time_diff = current_time - last_time;
    
    if (time_diff >= 10) { // 10ms计算一次速度
        int32_t pulse_diff = EncoderCount;
        EncoderCount = 0;
        
        // 计算转速 (RPM)
        // 假设编码器分辨率: 1000脉冲/转
        // 公式: RPM = (脉冲数 / 编码器分辨率) × (60000 / 采样时间ms)
        MotorSpeed = (pulse_diff / 1000.0f) * (60000.0f / time_diff);
        
        last_time = current_time;
    }
}

4. PWM输出控制

// PWM初始化
void PWM_Init(void)
{
    TIM_OC_InitTypeDef sConfigOC = {0};
    
    // PWM频率设置 (例如: 10kHz)
    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 84 - 1;    // 84MHz/84 = 1MHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 100 - 1;      // 1MHz/100 = 10kHz
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim1);
    
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 0;  // 初始占空比0%
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
    
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}

// 设置PWM占空比
void Set_PWM_Duty(float duty_cycle)
{
    uint32_t pulse;
    
    // 限制占空比范围 0~100%
    if (duty_cycle < 0) duty_cycle = 0;
    if (duty_cycle > 100) duty_cycle = 100;
    
    // 计算PWM脉冲值
    pulse = (uint32_t)(duty_cycle * (TIM1->ARR + 1) / 100.0f);
    
    // 更新PWM占空比
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse);
}

5. 主控制程序

PID_TypeDef MotorPID;
float TargetSpeed = 1000.0f; // 目标转速 1000 RPM

int main(void)
{
    // 系统初始化
    HAL_Init();
    SystemClock_Config();
    
    // 外设初始化
    PWM_Init();
    Encoder_Init();  // 编码器初始化(需要自己实现)
    TIM_Init();      // 定时器初始化(用于定时采样)
    
    // PID参数初始化(需要根据实际电机调试)
    PID_Init(&MotorPID, 0.5f, 0.01f, 0.05f, 100.0f);
    
    while (1)
    {
        // 在定时中断中调用Speed_Calculate()更新速度
        // 主循环中可以处理目标速度设定等
        
        // 每50ms执行一次PID控制
        static uint32_t last_pid_time = 0;
        if (HAL_GetTick() - last_pid_time >= 50) {
            float pwm_output;
            
            // PID计算
            pwm_output = PID_Calculate(&MotorPID, TargetSpeed, MotorSpeed);
            
            // 更新PWM输出
            Set_PWM_Duty(pwm_output);
            
            last_pid_time = HAL_GetTick();
            
            // 可以通过串口输出调试信息
            printf("Target: %.1f, Current: %.1f, PWM: %.1f%%\r\n", 
                   TargetSpeed, MotorSpeed, pwm_output);
        }
        
        // 其他任务...
        HAL_Delay(1);
    }
}

6. 定时器中断处理

// 定时器中断回调函数(例如1kHz)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2) { // 假设TIM2用于定时采样
        // 更新编码器计数
        Encoder_Update();
        
        // 计算速度(每10次调用计算一次速度)
        static uint8_t speed_counter = 0;
        if (++speed_counter >= 10) {
            Speed_Calculate();
            speed_counter = 0;
        }
    }
}

PID参数整定方法

手动整定步骤:

  1. 先调Kp:将Ki、Kd设为0,逐渐增大Kp直到系统开始振荡
  2. 再调Ki:加入Ki消除静差,但不要太大以免超调
  3. 最后调Kd:加入Kd抑制超调,改善动态性能

典型参数范围(需要根据实际电机调整):

// 直流有刷电机典型参数
#define KP_START  0.1f    // 开始调试值
#define KI_START  0.001f  
#define KD_START  0.01f

// 无刷电机典型参数  
#define KP_BLDC   0.3f
#define KI_BLDC   0.005f
#define KD_BLDC   0.02f

参考代码 位置式PID调节PWM占空比控制电动机转速 www.3dddown.com/csa/70961.html

注意事项

  1. 采样时间选择:速度采样和PID计算周期要匹配,通常10-50ms
  2. 积分抗饱和:必须对积分项进行限幅
  3. 输出限幅:PWM占空比限制在安全范围内
  4. 编码器分辨率:根据实际编码器参数调整速度计算公式
  5. 电机死区:有些电机在低占空比时不转动,需要设置最小启动占空比

调试技巧

  1. 串口监控:实时输出目标速度、实际速度、PWM占空比
  2. 阶跃响应:观察系统对目标速度突变的响应
  3. 抗扰动测试:在电机运行时施加负载,观察恢复性能