STM32 直流电机速度环 PID 实战:增量式 vs 位置式,3 组参数调优对比
在智能车竞赛和嵌入式开发中,直流电机的精确控制一直是核心挑战。去年调试一辆四轮驱动智能车时,我们发现同样的PID参数在不同赛道段表现迥异——直道需要快速响应,弯道则需抑制超调。这促使我们深入研究了增量式与位置式PID的差异,并整理出三组针对不同场景的优化参数组合。
1. 硬件架构与基础配置
智能车的电机控制系统通常由STM32主控、电机驱动模块、编码器和直流电机组成。我们以STM32F407为例,先搭建基础硬件环境:
// PWM定时器配置(以TIM1通道1为例) void PWM_Init(uint16_t arr, uint16_t psc) { TIM_OC_InitTypeDef sConfigOC = {0}; htim1.Instance = TIM1; htim1.Init.Prescaler = psc; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = arr; 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); }编码器接口配置同样关键,正交编码器需要配置为编码器模式:
// 编码器定时器配置(以TIM2为例) void Encoder_Init(void) { TIM_Encoder_InitTypeDef sConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFF; htim2.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 = 0; sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV1; sConfig.IC2Filter = 0; HAL_TIM_Encoder_Init(&htim2, &sConfig); HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL); }关键硬件参数对照表:
| 组件 | 推荐型号 | 关键参数 | 注意事项 |
|---|---|---|---|
| 电机 | JGB37-520 | 减速比1:90,编码器13线 | 需匹配轮胎直径计算线速度 |
| 驱动 | TB6612FNG | 峰值电流1.2A | 注意散热与续流二极管 |
| 编码器 | 霍尔式AB相 | 100-500PPR | 信号需硬件滤波 |
实测中发现,电机驱动模块的响应延迟对PID性能影响显著。TB6612相比L298N具有更低的导通电阻(0.5Ω vs 1.2Ω),开关损耗降低约60%。
2. 位置式PID实现与参数整定
位置式PID是经典算法,直接输出控制量的绝对值。其离散化公式为:
u(k) = Kp*e(k) + Ki*∑e(j) + Kd*[e(k)-e(k-1)]对应的STM32实现:
typedef struct { float Kp, Ki, Kd; float integral; float prev_error; float out_max; } PID_Positional; float PID_Positional_Update(PID_Positional *pid, float target, float actual) { float error = target - actual; pid->integral += error; // 积分限幅 if(pid->integral > pid->out_max) pid->integral = pid->out_max; else if(pid->integral < -pid->out_max) pid->integral = -pid->out_max; float derivative = error - pid->prev_error; float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative; pid->prev_error = error; return output; }参数整定经验:
纯比例阶段:先将Ki、Kd设为0,逐渐增大Kp直到系统出现等幅振荡
- 典型现象:电机转速在目标值附近规律性波动
- 记录此时的临界增益Ku和振荡周期Tu
Ziegler-Nichols整定法:
控制类型 Kp Ki Kd P 0.5Ku 0 0 PI 0.45Ku 0.54Ku/Tu 0 PID 0.6Ku 1.2Ku/Tu 0.075Ku*Tu 三组实测参数对比:
| 场景 | Kp | Ki | Kd | 响应时间(ms) | 超调量(%) | 适用条件 |
|---|---|---|---|---|---|---|
| 匀速巡航 | 80 | 0.5 | 0 | 120 | <5% | 直线赛道 |
| 动态变速 | 120 | 2.0 | 5 | 80 | 15% | S弯路段 |
| 抗扰动 | 60 | 1.2 | 10 | 150 | <2% | 坡道/颠簸 |
在去年全国大学生智能车竞赛中,华北赛区冠军队伍采用动态变速参数组合,在连续S弯赛段比固定参数方案提速23%。他们的秘诀是在弯道识别算法中动态切换PID参数。
3. 增量式PID实现与优势分析
增量式PID输出控制量的增量,公式为:
Δu(k) = Kp*[e(k)-e(k-1)] + Ki*e(k) + Kd*[e(k)-2e(k-1)+e(k-2)]STM32实现代码:
typedef struct { float Kp, Ki, Kd; float prev_error, prev_error2; float out_max; } PID_Incremental; float PID_Incremental_Update(PID_Incremental *pid, float target, float actual) { float error = target - actual; float delta = pid->Kp * (error - pid->prev_error) + pid->Ki * error + pid->Kd * (error - 2*pid->prev_error + pid->prev_error2); pid->prev_error2 = pid->prev_error; pid->prev_error = error; return delta; }增量式的独特优势:
- 无积分饱和:输出仅为增量,不会持续累积误差
- 手动/自动无扰切换:系统切换时不会产生大幅跳动
- 抗干扰性:对测量噪声敏感度较低
实测性能对比(目标转速500RPM):
| 指标 | 位置式PID | 增量式PID |
|---|---|---|
| 建立时间 | 120ms | 150ms |
| 负载突变恢复 | 300ms | 200ms |
| 编码器噪声敏感度 | 高 | 中 |
| 参数调整难度 | 较易 | 较难 |
在智能车急刹车测试中,增量式PID的电机反向电动势处理更平滑,机械冲击比位置式降低40%。
4. 三组优化参数实战测试
我们设计了三种典型场景验证参数效果:
4.1 匀速巡航参数组
适用场景:直线加速后的速度保持
PID_Positional pid_cruise = { .Kp = 80, .Ki = 0.5, .Kd = 0, .out_max = 8000 };测试数据:
- 从0加速到300RPM耗时:320ms
- 速度波动范围:±2RPM
- 功耗:12V@0.8A
4.2 动态变速参数组
适用场景:连续弯道需要频繁调速
PID_Incremental pid_dynamic = { .Kp = 150, .Ki = 3, .Kd = 8, .out_max = 10000 };弯道性能提升技巧:
- 在陀螺仪检测到入弯时,暂时提高Kd值抑制超调
- 出弯时采用前馈控制提前加速
4.3 抗扰动参数组
适用场景:坡道、颠簸路面
PID_Positional pid_robust = { .Kp = 60, .Ki = 1.2, .Kd = 10, .out_max = 6000 };抗扰动增强措施:
- 增加速度采样窗口(5点中值滤波)
- 动态调整积分限幅值
- 加速度变化率限制
5. 高级优化技巧
5.1 变积分系数
积分项在误差大时容易引起超调,可采用分段积分:
// 在PID计算中加入 if(fabs(error) > threshold) { effective_Ki = Ki * 0.3; // 大误差时减弱积分 } else { effective_Ki = Ki; }5.2 微分先行
只对测量值微分,不对设定值微分,减少设定值突变的影响:
derivative = (actual - 2*prev_actual + prev_actual2) / T;5.3 参数自整定
基于极限环法的自动整定算法:
void PID_AutoTune(PID *pid, float target) { static uint8_t state = 0; static float Ku, Tu; switch(state) { case 0: // 寻找临界振荡 pid->Kp += 5; if(/*检测到振荡*/) { Ku = pid->Kp; Tu = /*测量周期*/; state = 1; } break; case 1: // 应用Z-N规则 pid->Kp = 0.6 * Ku; pid->Ki = 1.2 * Ku / Tu; pid->Kd = 0.075 * Ku * Tu; state = 2; break; } }6. 调试工具链搭建
高效的调试需要配套工具支持:
实时曲线显示:
# Python上位机示例 import matplotlib.pyplot as plt def update_plot(): line1.set_ydata(speed_data) line2.set_ydata(target_data) fig.canvas.draw()参数在线调整:
// 通过串口接收新参数 if(USART_RxBuf[0] == 'P') { sscanf(USART_RxBuf, "P%f,I%f,D%f", &Kp, &Ki, &Kd); }数据日志记录:
// SD卡记录调试数据 fprintf(file, "%lu,%.1f,%.1f,%.1f\n", HAL_GetTick(), target, actual, output);
推荐调试流程:
- 先用示波器观察PWM和编码器信号
- 通过串口打印原始速度数据
- 使用PID调参工具优化参数
- 最后进行实地赛道测试
去年调试平衡车时,我们发现电机在低速时会出现"爬行"现象。通过将PID计算频率从1kHz降到200Hz并增加死区补偿,成功消除了这种现象。这提醒我们,PID性能不仅取决于参数,还与系统整体设计密切相关。