基于STM32 HAL库的直流有刷电机PWM调速与PID闭环控制实战 1. 硬件系统搭建与CubeMX工程配置直流有刷电机控制项目的第一步是搭建完整的硬件系统。我推荐使用STM32F103C8T6这款性价比极高的Cortex-M3内核微控制器搭配TB6612电机驱动模块。这套组合在实际项目中非常稳定我自己在多个机器人项目中都验证过它的可靠性。硬件连接需要注意几个关键点12V电源需要先经过降压模块分别输出3.3V给MCU和5V给驱动模块。TB6612的STBY引脚必须接高电平才能工作这个细节新手特别容易忽略。记得我第一次调试时就因为忘记给STBY供电排查了半天才发现问题。在CubeMX配置时我习惯先设置RCC和SYSRCC选择外部高速时钟(HSE)SYS调试接口选择Serial Wire 这两个是基础配置后续所有项目都会用到。重点来了PWM输出引脚的配置。以PA1为例查看数据手册可知它对应TIM2_CH2。在CubeMX中配置TIM2时需要理解几个关键参数Prescaler (PSC)决定定时器时钟分频Counter Period (ARR)自动重装载值 这两个参数共同决定PWM频率。我常用的计算公式是PWM频率 定时器时钟/((PSC1)*(ARR1))。比如要生成10kHz PWM假设定时器时钟为72MHz可以设置PSC71ARR99。2. PWM驱动实现与电机控制硬件配置完成后就该编写PWM驱动代码了。HAL库让这个过程变得非常简单主要用到两个关键函数HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); // 启动PWM __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, dutyCycle); // 设置占空比电机控制逻辑其实很直观正转AIN11, AIN20反转AIN10, AIN21刹车AIN11, AIN21滑行AIN10, AIN20我在项目中遇到过电机启动不顺畅的问题后来发现是PWM占空比变化太快。解决方法是用for循环逐步增加占空比模拟软启动效果for(int i0; imaxDuty; i){ __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, i); HAL_Delay(10); // 10ms步进 }实测发现电机在低占空比时可能出现死区不转的情况。这是因为静摩擦力大于驱动力矩。我的经验是设置一个最小有效占空比(约15%)低于这个值直接给0。3. 编码器接口与速度测量要实现闭环控制首先需要测量电机实际转速。霍尔编码器是最常用的方案我推荐使用STM32的编码器接口模式比外部中断方式更可靠。在CubeMX中配置编码器接口选择TIMx如TIM3选择Encoder Mode配置对应引脚为Encoder Input读取计数值的代码很简单int16_t encoderCount __HAL_TIM_GET_COUNTER(htim3); __HAL_TIM_SET_COUNTER(htim3, 0); // 清零重新计数 HAL_Delay(100); // 采样周期100ms float speed encoderCount * 60.0 / (PPR * 0.1); // 转为RPM其中PPR是编码器每转脉冲数需要根据实际编码器参数填写。我踩过的一个坑忘记定期清零计数器会导致数值溢出。后来我改用定时器溢出中断自动处理代码更健壮。4. PID算法实现与参数整定PID控制器是电机控制的核心我习惯用结构体封装PID参数typedef struct { float Kp, Ki, Kd; float error, lastError, integral; float output; } PID_Controller;位置式PID的实现代码float PID_Update(PID_Controller* pid, float setpoint, float actual){ pid-error setpoint - actual; pid-integral pid-error; float derivative pid-error - pid-lastError; pid-output pid-Kp * pid-error pid-Ki * pid-integral pid-Kd * derivative; pid-lastError pid-error; return pid-output; }参数整定是PID控制的难点我总结了一套实用方法先设Ki0, Kd0逐渐增大Kp直到系统出现振荡取振荡时Kp值的60%作为最终Kp逐渐增加Ki观察系统消除稳态误差的能力最后微调Kd改善动态响应实测中我发现采样周期对PID性能影响很大。根据香农定理采样频率至少是控制带宽的2倍。对于电机控制我通常用10-50ms的采样周期。5. 系统集成与调试技巧将各个模块集成后调试阶段可能会遇到各种问题。我分享几个实用技巧先用开环测试电机响应// 测试正反转 setMotorDirection(FORWARD); setMotorSpeed(50); // 50%占空比 HAL_Delay(2000); setMotorDirection(REVERSE);加入PID前先验证编码器读数while(1){ printf(Encoder: %d\n, getEncoderCount()); HAL_Delay(100); }使用串口绘图工具观察PID响应曲线我常用的是SerialPlot或Vofa遇到振荡时按顺序检查采样周期是否合适微分项是否引入噪声积分项是否累积过大实际项目中我会加入抗积分饱和和输出限幅// 抗积分饱和 if(fabs(pid-integral) integralLimit){ pid-integral (pid-integral 0) ? integralLimit : -integralLimit; } // 输出限幅 pid-output constrain(pid-output, -outputLimit, outputLimit);这套系统我已经在智能小车、机械臂等多个项目中使用过稳定性非常好。刚开始调试时可能会觉得PID参数难调但掌握方法后其实很有规律。建议先用模拟软件如MATLAB练习再到实际硬件上微调。