直流有刷电机实战:STM32驱动、PID速度闭环与编码器测速

说实话,直流有刷电机是电机控制里最基础、也最“友好”的入门对象。你给它通上电它就转,换一下极性它就反转,简单粗暴。但真正要把一个电机控得稳、控得准,让它指哪打哪,那就得把驱动、测速、闭环这三件事串起来。

我个人习惯把这一章叫做“电机控制的第一个闭环”。因为从这一章开始,你不再是让电机瞎转,而是让它按照你设定的速度跑。嗯,咱们一步步来。

3.1 硬件连接:STM32 + 驱动板 + 电机 + 编码器

先看硬件怎么搭。我早期踩过一个坑:驱动板的地线和STM32的地线没共地,结果PWM信号乱飘,电机一顿一顿的。你想想看,数字信号再干净,参考地不一样也是白搭。

典型的连接方式如下:

STM32引脚 驱动板接口 说明
PA0 (TIM2_CH1) PWM_IN 输出PWM控制速度
PB0 DIR 方向控制,高/低电平
PB1 EN 使能,高电平启动
PA6 (TIM3_CH1) 编码器A相 正交编码器输入
PA7 (TIM3_CH2) 编码器B相 正交编码器输入
GND GND 共地,必须连接
注意:驱动板的逻辑电源(通常是5V或3.3V)如果和STM32不一致,需要加电平转换。我曾经直接连5V逻辑的L298N到3.3V的STM32,结果IO口烧了一个。别学我。

3.2 编码器测速:从脉冲到转速

编码器是电机的“眼睛”。没有它,你根本不知道电机实际转了多少。常用的增量式编码器输出两路正交方波(A相和B相),通过相位差判断方向,通过脉冲数计算位置和速度。

STM32的定时器自带编码器模式,这个功能特别好用。你只需要配置好定时器,硬件会自动处理A/B相的边沿计数,CPU几乎零开销。

配置代码示例(基于HAL库):

// 编码器定时器初始化
void Encoder_Init(void)
{
    TIM_Encoder_InitTypeDef sEncoder = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 0;
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 0xFFFF;  // 16位自动重装
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    sEncoder.EncoderMode = TIM_ENCODERMODE_TI12;  // 双沿计数
    sEncoder.IC1Polarity = TIM_ICPOLARITY_RISING;
    sEncoder.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    sEncoder.IC1Prescaler = TIM_ICPSC_DIV1;
    sEncoder.IC1Filter = 0;
    sEncoder.IC2Polarity = TIM_ICPOLARITY_RISING;
    sEncoder.IC2Selection = TIM_ICSELECTION_DIRECTTI;
    sEncoder.IC2Prescaler = TIM_ICPSC_DIV1;
    sEncoder.IC2Filter = 0;

    HAL_TIM_Encoder_Init(&htim3, &sEncoder);
    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}

测速公式很简单:

转速 (RPM) = (脉冲数 / 编码器线数) × (60 / 采样周期秒)

举个例子:编码器线数是400线(每转400个脉冲),采样周期100ms,测到脉冲数200。那么转速 = (200 / 400) × (60 / 0.1) = 300 RPM。

小技巧:采样周期别太短,否则低速时脉冲数太少,测速抖动很大。我一般设50~100ms,低速时适当延长到200ms。你想想看,10ms测一次,电机转得慢可能一个脉冲都没有,算出来转速是0,那PID还怎么调?

3.3 PID速度闭环:让电机听话

PID控制说白了就是三个字:比例、积分、微分。比例看当前误差,积分看历史误差,微分看误差变化趋势。三个分量加起来,就是你要输出的PWM占空比。

我刚开始做PID时,调参全靠蒙。后来发现一个规律:先调P,让系统不震荡;再加I,消除静差;最后加D,抑制超调。这个顺序别搞反了。

位置式PID的实现代码:

typedef struct {
    float target;      // 目标值
    float actual;      // 实际值
    float err;         // 当前误差
    float err_last;    // 上一次误差
    float integral;    // 积分累计
    float Kp, Ki, Kd;  // PID系数
    float output;      // 输出值
    float integral_limit; // 积分限幅
    float output_limit;   // 输出限幅
} PID_TypeDef;

void PID_Calc(PID_TypeDef *pid)
{
    pid->err = pid->target - pid->actual;

    // 比例项
    float p_out = pid->Kp * pid->err;

    // 积分项(带限幅)
    pid->integral += pid->Ki * pid->err;
    if (pid->integral > pid->integral_limit)
        pid->integral = pid->integral_limit;
    else if (pid->integral < -pid->integral_limit)
        pid->integral = -pid->integral_limit;

    // 微分项
    float d_out = pid->Kd * (pid->err - pid->err_last);

    // 总输出
    pid->output = p_out + pid->integral + d_out;

    // 输出限幅
    if (pid->output > pid->output_limit)
        pid->output = pid->output_limit;
    else if (pid->output < -pid->output_limit)
        pid->output = -pid->output_limit;

    pid->err_last = pid->err;
}
核心要点:积分限幅一定要加。如果不加,电机堵转时积分会一直累加,等堵转解除后输出值飞上天,电机猛地一窜——我遇到过,差点把实验台掀了。

3.4 整体控制流程

把上面三块拼起来,就是一个完整的速度闭环。主循环里做的事情很简单:读编码器 → 算转速 → PID计算 → 更新PWM。就这么循环,电机就会自动往目标速度靠。

下面这张图把整个流程串起来了,你看一眼就明白:

直流有刷电机速度闭环控制流程图 设定目标速度 计算误差 e = target - actual PID控制器 P项 + I项 + D项 更新PWM占空比 直流有刷电机 + 编码器 实际转速反馈 反馈回路 控制周期建议 速度环:10ms ~ 50ms 编码器采样:50ms ~ 200ms PWM频率:10kHz ~ 20kHz

这个流程里有个关键点:控制周期要固定。我习惯用定时器中断来触发控制循环,而不是在主循环里用delay。因为delay不准,CPU负载一变周期就飘,PID参数全白调。

3.5 调参实战:从震荡到稳定

调参是门手艺活。我总结了一个快速调参法,适合新手:

  1. 先给一个目标速度,比如500 RPM。
  2. Ki和Kd设为0,只留Kp。从很小的值开始(比如0.1),慢慢往上加。
  3. 观察电机响应:如果电机嗡嗡响、速度来回晃,说明Kp太大了,减半再试。
  4. 找到临界值:当Kp大到让系统刚好开始震荡时,记下这个值。然后取它的60%作为最终Kp。
  5. 加Ki:从0.01开始,每次加0.01,直到稳态误差消失。注意积分限幅别太大。
  6. 最后加Kd:如果超调严重,加一点点Kd(0.001级别),能有效抑制过冲。
避坑指南:我曾经调一个带负载的电机,Kp调得很大,结果电机一启动就啸叫,编码器脉冲乱跳。后来发现是PWM频率太低(只有1kHz),电机线圈在低频下共振。把PWM频率提到16kHz,问题立刻消失。所以PWM频率别省,10kHz起步。

3.6 常见问题与排查

现象 可能原因 排查方法
电机不转 使能引脚没拉高、PWM占空比0、驱动板供电不足 用示波器看PWM波形,测驱动板电源电压
电机只往一个方向转 DIR引脚电平固定、编码器方向判断反了 检查DIR控制逻辑,交换编码器A/B相试试
速度波动大 编码器采样周期太短、Kp太大、机械振动 延长采样周期,降低Kp,检查电机安装是否牢固
电机发热严重 PWM频率太低、死区时间不够、电机堵转 提高PWM频率,检查驱动板死区设置,避免长时间堵转

嗯,这一章的内容就这些。从硬件连线到编码器测速,从PID代码到调参实战,每一步都是我自己摸爬滚打总结出来的。你照着做一遍,应该能跑通第一个速度闭环。记住:电机控制没有玄学,只有信号和时序。出问题了,拿示波器看波形,拿逻辑分析仪抓时序,答案就在那里。