1. 编码器测速基础原理编码器是电机控制系统中不可或缺的传感器它就像电机的眼睛能精确反馈电机的运动状态。常见的增量式编码器会输出两路相位差90°的方波信号A相和B相通过分析这两路信号我们不仅能测量转速还能判断转向。想象一下自行车轮上的反光板轮子转得越快反光板闪烁的频率就越高。编码器的工作原理与此类似只不过它用磁极或光栅代替了反光板。以常见的霍尔编码器为例电机转一圈会产生固定数量的脉冲如11PPR我们通过统计单位时间内的脉冲数就能计算出实际转速。在实际项目中我常用三种测速方法M法频率测量法固定时间内统计脉冲数适合高速测量T法周期测量法测量两个脉冲间的时间间隔适合低速测量M/T法结合两者优点全速度范围内都能保持较高精度2. STM32硬件配置要点2.1 定时器资源分配STM32的定时器就像瑞士军刀功能强大但资源有限。以STM32F103系列为例高级定时器TIM1, TIM8通用定时器TIM2-TIM5基本定时器TIM6, TIM7在四电机项目中我这样分配资源TIM2/TIM3/TIM4/TIM5分别处理四个编码器输入TIM610ms定时中断用于速度计算TIM8生成PWM驱动电机2.2 CubeMX配置实操打开CubeMX后关键配置步骤如下编码器模式配置// 以TIM2为例 htim2.Instance TIM2; htim2.Init.Prescaler 0; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 65535; // 16位最大值 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1;编码器接口设置sEncoderConfig.EncoderMode TIM_ENCODERMODE_TI12; // 双通道模式 sEncoderConfig.IC1Polarity TIM_ICPOLARITY_RISING; sEncoderConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sEncoderConfig.IC1Prescaler TIM_ICPSC_DIV1; sEncoderConfig.IC1Filter 0; // 根据实际噪声调整 // 通道2配置类似...中断优先级管理HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0); // 编码器中断 HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 2, 0); // 速度计算中断特别注意编码器中断的优先级必须高于速度计算中断否则会出现计数丢失3. 多电机同步测量架构设计3.1 数据结构优化面对四个电机时良好的数据结构能大幅提升代码可维护性。我设计了一个Motor结构体typedef struct { int32_t lastCount; // 上次计数值 int32_t totalCount; // 累计计数值含溢出 int16_t overflowNum; // 溢出次数 float speed; // 计算出的转速(r/s) uint8_t direct; // 旋转方向 } Motor; Motor motor[4]; // 四电机实例这种设计有三个优势相关变量集中管理方便扩展新功能如位置记录支持多实例复用代码3.2 溢出处理机制16位定时器最大计数值为65535高速旋转时容易溢出。我的解决方案是void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim2) { if(__HAL_TIM_GET_COUNTER(htim2) 10000) motor[0].overflowNum; else motor[0].overflowNum--; __HAL_TIM_SET_COUNTER(htim2, 10000); } // 其他定时器处理类似... }这里设置10000为中心值当计数器向上溢出时加1向下溢出时减1。实测中这种对称设计能有效避免累计误差。4. 转速计算与滤波4.1 实时速度计算在10ms定时中断中我采用M法计算转速motor[0].totalCount __HAL_TIM_GET_COUNTER(htim2) motor[0].overflowNum * 20000; motor[0].speed (float)(motor[0].totalCount - motor[0].lastCount) / (4 * REDUCTION_RATIO * PULSE_PER_ROUND) * 100; motor[0].lastCount motor[0].totalCount;公式解析44倍频计数REDUCTION_RATIO减速比如30PULSE_PER_ROUND编码器每转脉冲数如11100将100ms转换为秒10ms采样周期×104.2 数字滤波实践原始速度数据常有毛刺我常用三种滤波方式移动平均滤波#define FILTER_SIZE 5 float speed_buf[FILTER_SIZE]; float filtered_speed 0; // 在中断中更新 for(int iFILTER_SIZE-1; i0; i--) speed_buf[i] speed_buf[i-1]; speed_buf[0] raw_speed; for(int i0; iFILTER_SIZE; i) filtered_speed speed_buf[i]; filtered_speed / FILTER_SIZE;一阶低通滤波float alpha 0.2; // 滤波系数 filtered_speed alpha * raw_speed (1-alpha) * filtered_speed;中值滤波适合消除突发噪声5. 四电机同步实战技巧5.1 数据同步采集多电机控制最大的挑战是如何保证数据同步性。我的解决方案是使用同一个定时器TIM6触发所有编码器的读数采用DMA批量读取计数器值uint32_t encoder_values[4]; HAL_TIM_Encoder_Start_DMA(htim2, encoder_values[0], 1); HAL_TIM_Encoder_Start_DMA(htim3, encoder_values[1], 1); // 其他定时器类似...5.2 调试经验分享在调试四电机系统时我总结出几个常见问题计数异常检查编码器电源是否稳定我常用示波器观察A/B相波形方向错误交换A/B相接线或修改CubeMX中的极性设置速度跳动适当增加输入捕获滤波器值IC1Filter/IC2Filter中断冲突确保所有中断都有唯一优先级一个实用的调试技巧是添加串口打印printf(M1:%6d M2:%6d M3:%6d M4:%6d\r\n, (int)(motor[0].speed*100), (int)(motor[1].speed*100), (int)(motor[2].speed*100), (int)(motor[3].speed*100));6. 性能优化策略6.1 计算效率提升速度计算涉及浮点运算在F1系列上效率较低。我的优化方法是使用定点数运算int32_t speed (delta_count * 15625) 10; // 等价于*15.258789预计算常数值#define SPEED_CONSTANT (100.0f/(4*REDUCTION_RATIO*PULSE_PER_ROUND))6.2 内存优化对于资源受限的MCU可以使用__packed关键字压缩结构体将不频繁访问的变量放到__IO区域启用编译优化选项-O27. 扩展应用PID闭环控制有了准确的转速测量自然要实现闭环控制。一个简单的PID实现typedef struct { float Kp, Ki, Kd; float integral; float last_error; } PID; float PID_Update(PID* pid, float target, float feedback) { float error target - feedback; pid-integral error; float derivative error - pid-last_error; pid-last_error error; return pid-Kp*error pid-Ki*pid-integral pid-Kd*derivative; }使用时注意积分项需要限幅防止饱和微分项可以加入滤波器不同转速段可能需要不同的PID参数在实际四轮小车上我还会加入转向差速控制left_speed base_speed - turn_adjust; right_speed base_speed turn_adjust;通过这套系统成功实现了小车的直线保持和精准转向实测速度控制误差小于2%。