STM32 增量式 PID 电机调速实战:从编码器读取到 PWM 输出 3 步闭环
STM32 增量式 PID 电机调速实战从编码器读取到 PWM 输出 3 步闭环在智能车和机器人开发中精确控制电机转速是确保系统稳定运行的关键。本文将带你用STM32实现一个完整的增量式PID电机调速系统包含编码器测速、PID计算和PWM输出的全流程代码实现。不同于理论讲解我们直接从嵌入式工程师的角度出发提供可立即复用的代码模块和调试技巧。1. 系统架构设计与硬件连接直流有刷电机的闭环控制系统需要三个核心组件编码器测速模块、PID控制算法和PWM驱动模块。整个系统的信号流如下图所示编码器脉冲 → STM32定时器 → 速度计算 → PID算法 → PWM占空比 → 电机驱动 → 电机转速硬件连接要点编码器信号线接至STM32定时器的编码器接口如TIM4_CH1/TIM4_CH2PWM输出引脚连接电机驱动芯片的输入如TIM3_CH1电机驱动输出接电机两极提示正交编码器建议使用带硬件滤波的电路避免脉冲抖动影响测速精度。常用电机驱动芯片配置对比驱动芯片最大电流工作电压控制方式典型应用TB66121.2A2.5-13.5V双路PWM小型机器人DRV88331.5A2.7-10.8VPH/EN模式微型车模L298N2A5-35V双全桥教育套件2. 编码器测速与速度计算STM32的定时器编码器接口模式能自动计数正交编码脉冲我们只需定期读取计数值并清零。以下是配置步骤// TIM4编码器接口模式初始化 void Encoder_Init(void) { TIM_Encoder_InitTypeDef sConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim4.Instance TIM4; htim4.Init.Prescaler 0; htim4.Init.CounterMode TIM_COUNTERMODE_UP; htim4.Init.Period 0xFFFF; htim4.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 6; // 适当滤波 sConfig.IC2Polarity TIM_ICPOLARITY_RISING; sConfig.IC2Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler TIM_ICPSC_DIV1; sConfig.IC2Filter 6; HAL_TIM_Encoder_Init(htim4, sConfig); sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(htim4, sMasterConfig); HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL); }速度计算采用M法固定时间测脉冲数在定时中断中执行// 5ms定时中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM6) { // 5ms定时器 static int32_t last_count 0; int32_t current_count TIM4-CNT; g_motor_speed (current_count - last_count) * 200; // 转换为RPM last_count current_count; } }速度计算优化技巧对于低转速电机可改用T法测脉冲间隔时间添加滑动平均滤波speed_filter 0.9*speed_filter 0.1*raw_speed考虑齿轮比换算实际转速 编码器转速 / 减速比3. 增量式PID算法实现增量式PID相比位置式PID具有抗积分饱和、手动/自动切换无冲击等优势。其离散公式为Δu(k) Kp*[e(k)-e(k-1)] Ki*e(k) Kd*[e(k)-2e(k-1)e(k-2)]STM32上的C语言实现typedef struct { float Kp, Ki, Kd; float prev_error, prev_prev_error; int16_t output_limit; } IncPID_Controller; void IncPID_Init(IncPID_Controller *pid, float Kp, float Ki, float Kd, int16_t limit) { pid-Kp Kp; pid-Ki Ki; pid-Kd Kd; pid-output_limit limit; pid-prev_error pid-prev_prev_error 0; } int16_t IncPID_Compute(IncPID_Controller *pid, int16_t target, int16_t feedback) { float error target - feedback; float delta pid-Kp*(error - pid-prev_error) pid-Ki*error pid-Kd*(error - 2*pid-prev_error pid-prev_prev_error); pid-prev_prev_error pid-prev_error; pid-prev_error error; // 输出限幅 static int16_t output 0; output (int16_t)delta; output (output pid-output_limit) ? pid-output_limit : (output -pid-output_limit) ? -pid-output_limit : output; return output; }电机控制中常用PI而非完整PID的原因电机转速变化相对缓慢微分作用易引入高频噪声积分项已能有效消除静差微分带来的改善有限减少一个参数可简化调试过程4. PWM输出与电机驱动将PID输出转换为PWM占空比通过H桥驱动电机。关键配置// TIM3 PWM初始化 void PWM_Init(void) { TIM_MasterConfigTypeDef sMasterConfig {0}; TIM_OC_InitTypeDef sConfigOC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; // 1MHz计数频率 htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // 1kHz PWM htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(htim3, sMasterConfig); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); } // 设置电机方向和PWM void Motor_SetOutput(int16_t pwm) { if(pwm 0) { HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, pwm); } else { HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, -pwm); } }PWM频率选择建议有刷电机1-20kHz过高会导致MOS管发热无刷电机8-16kHz避免可闻噪声舵机50-333Hz5. 参数调试实战技巧调试增量式PI参数时建议按以下步骤初始化所有参数为0逐步增加Kp直到系统开始振荡记录临界振荡时的Kp值Kc和振荡周期Pc根据Ziegler-Nichols法则设置初始参数Kp 0.6*KcKi 0.5*Pc微调观察响应曲线优化目标上升时间快超调量10%稳态误差接近0典型调试问题解决方案现象可能原因解决方法电机抖动Kp过大减小Kp增加低通滤波转速不稳积分饱和限制积分项使用抗饱和算法响应慢Ki太小适当增加Ki但需防止振荡反向超调微分过强减小Kd或去掉微分项通过串口输出实时数据辅助调试printf(Target:%d,Actual:%d,PWM:%d\r\n, target_speed, actual_speed, pwm_output);可将数据导入Excel或MATLAB绘制响应曲线直观分析系统性能。