当前位置: 首页 > news >正文

【STM32CubeMX实战】HAL库驱动编码器电机:从PWM调速到速度闭环控制

1. 硬件准备与连接

搞电机控制首先得把硬件捣鼓明白。我用的主控是STM32F103RCT6,这芯片性价比高,资源够用。电机驱动板选的是TB6612,比L298N发热小多了,实测连续工作2小时芯片只是微温。电源部分要注意,12V锂电池最好加个稳压模块,避免电压波动影响单片机工作。

编码器电机建议选500线以上的,我手头这个是600线的JGA25-370电机,转一圈能输出2400个脉冲(4倍频计数)。轮子搭配看项目需求,普通小车用橡胶轮就行,我这次为了测试用的麦克纳姆轮,移动起来特别灵活。

接线时容易踩坑的是PWM信号线。PA8接驱动板的PWMA,注意驱动板的地线一定要和单片机共地!编码器接口接PB6/PB7,记得加10K上拉电阻,不然脉冲信号可能不稳定。USART1留着调试用,后期调PID参数全靠它打印数据。

2. STM32CubeMX工程配置

2.1 时钟树配置

打开CubeMX先配置时钟树,F103系列最大72MHz。我习惯先用图形化界面把HSE选上,然后切到Clock Configuration标签页。这里有个技巧:先把HCLK设到72MHz,然后按Tab键让软件自动计算其他分频系数,比手动调省事多了。

2.2 PWM生成配置

定时器1的CH1用来生成PWM,关键参数这么设:

  • Prescaler设为71(72MHz/(71+1)=1MHz)
  • Counter Period设99(1MHz/(99+1)=10kHz)
  • Pulse先设50(初始占空比50%)

记得把PWM模式选为"PWM mode 1",CH Polarity选High。这里有个细节:如果电机转向反了,不用改接线,直接在代码里把极性反转就行。

2.3 编码器接口配置

定时器4配置为编码器模式:

  • Encoder Mode选"Encoder Mode TI1 and TI2"
  • Polarity都选Rising Edge
  • Counter Period设65535(16位最大值)

建议把编码器的两个通道都配置为Pull-up,这样抗干扰能力强。我遇到过实验室里电钻一开编码器就乱跳的情况,加上拉电阻后稳如老狗。

2.4 定时器中断配置

定时器2用来做速度采样:

  • Prescaler设7199(72MHz/7200=10kHz)
  • Counter Period设99(10kHz/100=100Hz)
  • 记得勾选NVIC中断

中断优先级要特别注意!编码器中断的抢占优先级必须比定时器中断低,不然会出现速度采样不同步的问题。我一般这么设:

  • 定时器2中断:Preemption Priority 0
  • 定时器4编码器中断:Preemption Priority 1

3. HAL库代码实现

3.1 PWM启动与基础控制

在main.c的USER CODE BEGIN 2区域添加:

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIM_Base_Start_IT(&htim2); // 定时器中断 HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL); // 编码器

设置占空比用这个函数:

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dutyCycle);

dutyCycle范围0-7200,对应0-100%占空比。我封装了个实用函数:

void SetMotorSpeed(float percent) { uint16_t pulse = (uint16_t)(percent * 72); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse); }

3.2 编码器速度计算

在定时器中断回调里处理速度采样:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim2) { int16_t encoder_count = (int16_t)__HAL_TIM_GET_COUNTER(&htim4); float rpm = (encoder_count * 600.0f) / (4 * 600 * 0.01f); // 600线编码器,4倍频,10ms采样周期 __HAL_TIM_SET_COUNTER(&htim4, 0); printf("Speed:%.2f rpm\n", rpm); } }

这个公式解释下:encoder_count是10ms内的脉冲数,4*600表示每转总脉冲数(4倍频),0.01是采样周期(10ms)。乘以600是把rpm换算成每分钟转数。

3.3 闭环PID控制实现

先定义PID结构体:

typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller;

PID计算函数:

float PID_Update(PID_Controller* pid, float setpoint, float measurement) { float error = setpoint - measurement; pid->integral += error * 0.01f; // 10ms采样周期 if(pid->integral > 1000) pid->integral = 1000; if(pid->integral < -1000) pid->integral = -1000; float derivative = (error - pid->prev_error) / 0.01f; pid->prev_error = error; return pid->Kp*error + pid->Ki*pid->integral + pid->Kd*derivative; }

在中断回调里调用:

PID_Controller speed_pid = {.Kp=0.5f, .Ki=0.1f, .Kd=0.01f}; float target_rpm = 100.0f; // 目标转速 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim2) { // ... 速度计算代码同上 ... float output = PID_Update(&speed_pid, target_rpm, rpm); SetMotorSpeed(output); } }

4. 调试技巧与常见问题

4.1 PID参数整定方法

调PID有个口诀:"先调P,后调I,最后加D"。具体步骤:

  1. 先把Ki和Kd设0,Kp从0.1开始慢慢加大,直到电机出现等幅振荡
  2. 取振荡时Kp值的0.6倍作为最终Kp
  3. 逐渐增加Ki,直到系统能快速消除静差
  4. 最后加少量Kd抑制超调

实测我的电机参数:

  • 直流电机:Kp=0.5, Ki=0.2, Kd=0.05
  • 减速电机:Kp=1.2, Ki=0.5, Kd=0.1

4.2 抗干扰处理

遇到电机干扰导致单片机复位?试试这些方法:

  1. 在电机电源并个100uF电解电容
  2. 编码器信号线加磁环
  3. 单片机复位脚加0.1uF电容
  4. 软件上做速度滤波:
#define FILTER_SIZE 5 float speed_buffer[FILTER_SIZE]; float filtered_speed = 0; // 在中断回调里 for(int i=FILTER_SIZE-1; i>0; i--) { speed_buffer[i] = speed_buffer[i-1]; } speed_buffer[0] = rpm; filtered_speed = 0; for(int i=0; i<FILTER_SIZE; i++) { filtered_speed += speed_buffer[i]; } filtered_speed /= FILTER_SIZE;

4.3 堵转保护

电机卡住时电流会飙升,加个软件保护:

if(fabs(rpm) < 5 && fabs(output) > 30) { // 转速低于5rpm但输出大于30% static uint8_t stall_count = 0; if(++stall_count > 10) { SetMotorSpeed(0); // 10个周期后强制停止 printf("Motor Stall!\n"); } } else { stall_count = 0; }

5. 进阶优化方向

5.1 速度前馈控制

单纯PID在负载突变时响应慢,可以加上前馈:

float feedforward = target_rpm * 0.72f; // 根据电机特性调整系数 float output = PID_Update(&speed_pid, target_rpm, rpm) + feedforward;

5.2 自适应PID

根据转速自动调整参数:

if(target_rpm > 300) { speed_pid.Kp = 0.3f; speed_pid.Ki = 0.05f; } else { speed_pid.Kp = 0.8f; speed_pid.Ki = 0.2f; }

5.3 上位机调试

用串口发送PID参数和速度指令:

if(USART1_RxFlag) { // 收到数据 sscanf(USART1_RxBuffer, "P%f I%f D%f S%f", &speed_pid.Kp, &speed_pid.Ki, &speed_pid.Kd, &target_rpm); USART1_RxFlag = 0; }

搭配Python上位机实时绘图,调参效率能提升10倍。我用PyQt写了个简易调试工具,可以实时显示转速曲线并修改PID参数。

http://www.jsqmd.com/news/797384/

相关文章:

  • 6G Open-RAN安全新突破!这款模拟器揭秘信任感知ISAC的核心能力【附python代码】
  • OneNote插件安装避坑指南:从Gem、Onetastic到HighLight,一次搞定所有疑难杂症
  • 2026年不锈钢工程雕塑品牌推荐榜:户外、拉丝等多样类型,设计安装实力之选 - 速递信息
  • CANN 3D最大池化反向传播算子
  • HFSS仿真避坑指南:手把手教你设置波端口与积分线(附空气波导完整流程)
  • 2026年郑州留学头部中介测评,收费合理哪家更靠谱 - 速递信息
  • ADRC-2.最速跟踪微分器TD-参数整定与工程实践
  • 别再死磕DPHY了!手把手教你用CPHY给MIPI接口提速(附带宽计算与选型指南)
  • 【小版本更迭】Claude Code v2.1.138:常规维护与内部逻辑优化
  • 低代码平台排名:从四大维度看市场与技术领导者 - 速递信息
  • 2026年车叶草苷对照品优质厂家推荐:成都钠钶锂生物科技有限公司 - 品牌推荐官
  • 新书发布|《IRIS 编程技术指南》正式发售
  • 20+高效Obsidian模板:专业Zettelkasten笔记系统构建指南
  • 4G5G专题:物理层数据高速公路——PDSCH与PUSCH信道详解
  • 2026 上海婚钻回收行情指南,个人闲置钻石稳妥变现技巧 - 奢侈品回收测评
  • 终极指南:3个简单步骤彻底解决魔兽争霸3在Windows 10/11的卡顿闪退问题
  • 城通网盘下载加速指南:如何用开源工具突破100KB/s限制
  • 泉盛UV-K5/K6对讲机LOSEHU固件实战应用与功能革新
  • PotPlayer+SVP 4真能点石成金?实测补帧效果与常见误区全解析
  • Notepad++ Compare插件安装与配置全攻略
  • 品牌排行榜前列|2026广州聚杰芯科交通流量调查系统,头部品牌值得信赖 - 品牌速递
  • 科研绘图新纪元:深度拆解 3DCellForge,AI 驱动的交互式 3D 细胞建模神器
  • GEE实战:5分钟用Landtrendr和Bfast监测你家后山的森林变化(附完整代码)
  • ANSYS Workbench 2023 R2 新手避坑指南:从Spaceclaim模型简化到稳态热分析完整流程
  • 一建机电备考笔记(32)(起重技术-稳定性要求、桅杆)(含考频+题型)
  • 【信息科学与工程学】【解决方案体系】第十二篇 视频行业精细化策略库构建与应用研究(包含短视频/长视频)——视频内容分析
  • 2026 成都 LV 包包高价变现全攻略|五大奢侈品机构分级测评,成色折价与估价逻辑深度解析 - 奢侈品回收测评
  • 2026交通流量调查系统十大排行,广州聚杰芯科以技术优势脱颖而出 - 品牌速递
  • AI入门:适合小白
  • 如何为Unity游戏添加多语言支持:XUnity.AutoTranslator完整指南