用STM32和TB6612搞定智能小车:从编码器读取到串级PID调参全流程(附避坑经验) STM32与TB6612智能小车开发实战从编码器数据采集到串级PID调参完整指南1. 项目概述与硬件选型智能小车作为嵌入式学习和机器人开发的经典项目涉及电机控制、传感器数据处理和自动控制算法等多个技术领域。本项目基于STM32微控制器和TB6612电机驱动模块实现带编码器反馈的直流电机精准控制。核心硬件组件主控芯片STM32F103C8T672MHz主频足够处理双电机PID运算驱动模块TB6612FNG相比L298N具有更低发热量和更高效率电机类型6V直流减速电机减速比1:48自带AB相编码器编码器分辨率11线/转通过4倍频后为44脉冲/转实测数据电机空载转速约160RPM带载后降至120RPM左右编码器总分辨率44×482112脉冲/转2. 编码器数据采集与速度计算2.1 编码器接口配置使用STM32的定时器编码器模式可自动识别转向并计数// TIM1配置为编码器接口模式电机1 TIM_Encoder_InitTypeDef encoder_config { .EncoderMode TIM_ENCODERMODE_TI12, .IC1Polarity TIM_ICPOLARITY_RISING, .IC1Selection TIM_ICSELECTION_DIRECTTI, .IC1Prescaler TIM_ICPSC_DIV1, .IC1Filter 6, .IC2Polarity TIM_ICPOLARITY_RISING, .IC2Selection TIM_ICSELECTION_DIRECTTI, .IC2Prescaler TIM_ICPSC_DIV1, .IC2Filter 6 }; HAL_TIM_Encoder_Init(htim1, encoder_config); HAL_TIM_Encoder_Start(htim1, TIM_CHANNEL_ALL);2.2 速度测量实现采用定时中断法测量转速在20ms定时中断中读取脉冲差值// 在定时器中断回调函数中 int16_t pulse_diff __HAL_TIM_GET_COUNTER(htim1); __HAL_TIM_SET_COUNTER(htim1, 0); float rpm (pulse_diff * 60.0f) / (2112 * 0.02); // 转换为RPM常见问题排查脉冲计数异常检查编码器接线确保A/B相没有接反速度波动大适当增加滤波器参数ICxFilter方向判断错误交换A/B相接线测试3. 电机驱动与PWM控制3.1 TB6612驱动配置控制信号IN1IN2PWM电机状态正转10PWM正向旋转反转01PWM反向旋转刹车110快速停止待机00X高阻状态PWM配置建议频率10-20kHz超出人耳范围减少噪音分辨率16位定时器可获得更平滑控制// 电机控制函数示例 void Motor_SetSpeed(int motor, float speed) { uint16_t pwm fabs(speed) * PWM_MAX; if(speed 0) { HAL_GPIO_WritePin(IN1_GPIO, IN1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(IN2_GPIO, IN2_PIN, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(IN1_GPIO, IN1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO, IN2_PIN, GPIO_PIN_SET); } __HAL_TIM_SET_COMPARE(htim3, motorMOTOR_LEFT?TIM_CHANNEL_1:TIM_CHANNEL_2, pwm); }4. 串级PID控制实现4.1 控制架构设计位置环(外环) ↓ 速度环(内环) ↓ PWM输出4.2 PID算法实现typedef struct { float Kp, Ki, Kd; float target, actual; float err, last_err; float integral, integral_limit; } PID_TypeDef; float PID_Calculate(PID_TypeDef *pid, float actual) { pid-err pid-target - actual; // 抗积分饱和 if(fabs(pid-err) 10) pid-integral 0; else { pid-integral pid-err; pid-integral constrain(pid-integral, -pid-integral_limit, pid-integral_limit); } float output pid-Kp * pid-err pid-Ki * pid-integral pid-Kd * (pid-err - pid-last_err); pid-last_err pid-err; return constrain(output, -OUTPUT_MAX, OUTPUT_MAX); }4.3 参数整定步骤先调速度环设Kp0.5Ki0Kd0逐步增加Kp直到出现轻微振荡加入Ki消除静差从Kp/10开始最后加入Kd抑制超调再调位置环采用更小的比例系数约为速度环的1/5通常不需要积分项典型参数范围速度环Kp0.8-1.5, Ki0.5-1.0, Kd0.05-0.2位置环Kp0.2-0.5, Kd0.5-1.05. 上位机调试与性能优化5.1 匿名助手配置通过串口发送数据到上位机// 发送实际速度和目标速度到上位机 void SendToAssistant(float target, float actual) { uint8_t buf[8]; memcpy(buf, target, 4); memcpy(buf4, actual, 4); HAL_UART_Transmit(huart1, buf, 8, 100); }调试技巧先测试阶跃响应突然改变目标值观察上升时间、超调量和稳定时间调整参数使响应曲线达到临界阻尼状态5.2 常见问题解决方案问题1电机启动抖动原因PID输出突变解决增加启动斜坡// 渐进式设定目标值 void SetTargetGradually(float *target, float new_target, float step) { if(fabs(*target - new_target) step) { *target (*target new_target) ? step : -step; } }问题2直线行驶偏移原因两轮机械差异解决加入差速补偿// 根据偏移量动态调整两轮速度 float left_speed base_speed offset_compensation; float right_speed base_speed - offset_compensation;问题3转向不精确原因惯性导致过冲解决加入转向减速区if(fabs(target_angle - current_angle) 10) { // 进入减速区 speed BASE_SPEED * (fabs(target_angle - current_angle)/10); }6. 进阶优化方向自适应PID根据误差大小动态调整参数运动规划S曲线速度规划减少机械冲击参数自整定Ziegler-Nichols法等自动整定方法卡尔曼滤波对编码器数据进行滤波处理实测性能指标直线行驶误差±2cm/米转向精度±3度速度控制精度±5RPM项目完整代码已托管至GitHub示例链接包含完整的Keil工程文件上位机通信协议参数调试工具3D打印小车结构设计图