STM32 增量式 PID 电机调速实战:从编码器读取到 PWM 输出 3 步闭环
在智能车和机器人开发中,精确控制电机转速是确保系统稳定运行的关键。本文将带你用STM32实现一个完整的增量式PID电机调速系统,包含编码器测速、PID计算和PWM输出的全流程代码实现。不同于理论讲解,我们直接从嵌入式工程师的角度出发,提供可立即复用的代码模块和调试技巧。
1. 系统架构设计与硬件连接
直流有刷电机的闭环控制系统需要三个核心组件:编码器测速模块、PID控制算法和PWM驱动模块。整个系统的信号流如下图所示:
编码器脉冲 → STM32定时器 → 速度计算 → PID算法 → PWM占空比 → 电机驱动 → 电机转速硬件连接要点:
- 编码器信号线接至STM32定时器的编码器接口(如TIM4_CH1/TIM4_CH2)
- PWM输出引脚连接电机驱动芯片的输入(如TIM3_CH1)
- 电机驱动输出接电机两极
提示:正交编码器建议使用带硬件滤波的电路,避免脉冲抖动影响测速精度。
常用电机驱动芯片配置对比:
| 驱动芯片 | 最大电流 | 工作电压 | 控制方式 | 典型应用 |
|---|---|---|---|---|
| TB6612 | 1.2A | 2.5-13.5V | 双路PWM | 小型机器人 |
| DRV8833 | 1.5A | 2.7-10.8V | PH/EN模式 | 微型车模 |
| L298N | 2A | 5-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-333Hz
5. 参数调试实战技巧
调试增量式PI参数时,建议按以下步骤:
- 初始化所有参数为0,逐步增加Kp直到系统开始振荡
- 记录临界振荡时的Kp值(Kc)和振荡周期(Pc)
- 根据Ziegler-Nichols法则设置初始参数:
- Kp = 0.6*Kc
- Ki = 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绘制响应曲线,直观分析系统性能。