小兴带你玩转平衡小车-从TB6612驱动到STM32精准调速
1. TB6612驱动模块与JGB37-520电机初探第一次拿到TB6612驱动板和JGB37-520电机时我完全被这个小巧的组合惊艳到了。TB6612这个只有指甲盖大小的芯片居然能输出1.2A的持续电流峰值可达3.2A而且效率比老旧的L298N高出不少。JGB37-520电机自带霍尔编码器的设计更是贴心六根彩色线缆红、黑、黄、绿、蓝、白对应不同功能接线时完全不用担心搞混。说到选型这里有个小插曲。当时我在某宝选购时发现JGB37-520居然有不同减速比版本19:1、30:1、56:1。经过反复比较最终选择了30:1减速比的型号空载转速200RPM。这个速度对于平衡小车来说刚刚好——太快了容易失控太慢了又不够灵活。电机额定电压12V实测用3节18650锂电池串联供电非常稳定。2. 硬件连接的那些坑2.1 接口匹配的血泪史刚开始我以为所有2.54mm间距的接口都是通用的结果吃了大亏。JGB37-520电机用的是PH2.0接口而我准备的却是XH2.54连接线。这个错误导致我第一次通电测试时编码器信号完全读不出来。后来用万用表一根根测线序才发现问题所在。建议大家一定要提前确认接口规格PH2.0的插头明显比XH2.54要小巧精致得多。2.2 TB6612的稳定之道TB6612虽然性能强悍但对电源稳定性要求很高。我的第一个版本板子就因为没有在VM引脚附近放置足够的滤波电容至少4个100nF陶瓷电容导致PWM调速时电机出现奇怪的抖动。后来在芯片每个电源引脚都加了去耦电容问题立刻解决。这里特别提醒STBY引脚一定要接高电平3.3V否则驱动芯片会进入休眠状态。3. STM32的PWM精准控制3.1 定时器配置秘籍要让STM32产生稳定的PWM信号定时器配置是关键。我使用的是TIM3的通道1PA6引脚预分频值设为0自动重装载值899这样产生的PWM频率就是72MHz/(8991)80kHz。这个频率远高于电机响应的上限既能避免可闻噪音又不会让TB6612过度发热。void PWM_Init(uint16_t per, uint16_t psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; GPIO_InitTypeDef GPIO_InitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Pin GPIO_Pin_6; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); TIM_TimeBaseInitStruct.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStruct.TIM_Period per; TIM_TimeBaseInitStruct.TIM_Prescaler psc; TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStruct); TIM_OCInitStruct.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStruct.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse 0; TIM_OC1Init(TIM3, TIM_OCInitStruct); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3,ENABLE); }3.2 电机转向控制技巧TB6612控制电机转向需要配合IN1和IN2引脚。我的做法是将PA4和PA5分别接驱动板的IN1和IN2正转PA4高电平PA5低电平反转PA4低电平PA5高电平刹车两个引脚同为高电平停止两个引脚同为低电平实测发现快速切换方向时最好先刹车1-2ms可以显著减少机械冲击。这个细节对延长电机寿命很有帮助。4. 编码器数据采集实战4.1 定时器编码器模式JGB37-520的霍尔编码器输出AB两相脉冲我用TIM2的编码器接口来捕获。配置时需要注意将TIM2的CH1和CH2PA0和PA1设置为输入模式开启编码器接口模式3TI1和TI2边沿都计数设置合适的滤波器我用的值是6void Encoder_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_ICInitTypeDef TIM_ICInitStruct; GPIO_InitTypeDef GPIO_InitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStruct); TIM_TimeBaseStruct.TIM_Prescaler 0; TIM_TimeBaseStruct.TIM_Period 65535; TIM_TimeBaseStruct.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStruct); TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICInitStruct.TIM_ICFilter 6; TIM_ICInit(TIM2, TIM_ICInitStruct); TIM_Cmd(TIM2, ENABLE); }4.2 速度计算优化编码器每转输出11个脉冲经过30:1减速后轮轴每转实际会产生330个脉冲。我每50ms读取一次计数器值然后通过这个公式计算转速RPM转速 (脉冲数 × 60) / (330 × 0.05)为了减少抖动我还加了滑动平均滤波保留最近5次采样值取中间3个值的平均数。这样处理后的速度曲线非常平滑为后续PID控制打下了良好基础。5. PID闭环控制入门5.1 从开环到闭环的跨越刚开始我只用PWM开环控制发现电机负载变化时转速波动很大。加上PID闭环后效果立竿见影。我的第一个版本只用了比例控制P虽然比开环好但还是有稳态误差。后来加入积分项I成功消除了静差。微分项D的加入则让系统响应更快速且稳定。5.2 参数整定经验谈调参时我遵循先P后I最后D的原则把I和D设为0逐渐增大P直到系统出现轻微震荡记录此时的P值临界增益Kc和震荡周期Pc根据Ziegler-Nichols公式P0.6KcI2P/PcDP*Pc/8在这个基础上微调实测发现对于我的小车P800、I50、D100时效果最佳。这个过程中用串口实时输出速度曲线会事半功倍。6. 系统集成与调试6.1 电源管理要点整个系统最耗电的就是电机我用了单独的18650电池组供电12V通过LM2596降压模块给STM32供电。这里有个重要经验一定要在电机电源和MCU电源之间加磁珠隔离否则电机启停时的电流突变会导致MCU复位。我在PCB上预留了磁珠位置实测效果比简单的0欧电阻好很多。6.2 抗干扰设计编码器信号线最容易受干扰我的解决方案是使用双绞线在信号线上加100Ω终端电阻PCB上走线尽量短在信号线对地加100pF电容这些措施实施后编码器读数稳定性大幅提升即使在电机全速运行时也不会出现误码。