用STM32做了台AGV底盘,编码器老丢步走歪,最后栽在一根排线上

2026-11-110

引子

照着社区教程攒了台差速 AGV 底盘,STM32 读编码器做测速闭环。空载转得好好的,一上低速带载直行就往一边偏,串口一看——左轮编码器计数偶尔整拍往外跳、又或者半天不涨,明显丢步。PID 怎么调都压不住,一度怀疑电机和机械装歪了。

硬件清单

  • 主控:STM32F103C8T6(用 TIM2/TIM3 编码器模式读 AB 相)
  • 驱动器:艾思控 AQMD2408NS-MBE 直流有刷驱动器(适配 12V-45W / 24V-130W,倍流 16A/8A、短接制动峰值 50A,能耗制动 3A,带电刹接口、独立编码器接口、通讯隔离,尺寸 55×55×17mm)
  • 电机:24V 永磁有刷减速电机 ×2,尾端带霍尔 AB 相编码器(11 PPR,倍频后 44 计数/转)
  • 电源:24V 5A 开关电源
  • 上位:CH340 串口模块 + 电脑串口助手(本次排查全程当"穷人示波器"用)

接线说明

电机动力线(U/V 两芯)接驱动器输出端,编码器 A、B 两相走驱动器的独立编码器接口,主控电源与动力电源分开走线、只在电源端单点共地。STM32 的 PWM 占空比经两根信号线进驱动器调速端,编码器脉冲再从驱动器侧引回 TIM2/TIM3。

【配图建议:整机俯视接线走线图,红为动力回路、蓝为编码器信号回路,标出捆扎段】
【配图建议:驱动器独立编码器接口端子特写 + 屏蔽层单点接地位置】

现象描述

低速直行(占空比约 25%)时,车体稳定往左偏;串口每 100ms 打印一次左右轮增量计数,正常应当两边同步平滑增长,实测左轮每隔几秒"卡一下"——某拍突然为 0,下一拍又猛地多出一截,像丢了一拍又补回来。电机听声音无异响,手转编码器轴读数连续,说明编码器本体没坏。

排查过程

第一步,先怀疑 PID 和测速周期。 把速度环 Kp 调小、测速周期从 10ms 拉到 20ms,丢步现象一点没改善,反而低速更抖。判断不是控制参数问题,先排除。

第二步,怀疑机械与电机。 拆下左轮空转、换一只同型号电机对调测试,丢步照样出现在"原来那条线"上,跟电机无关,排除机械和电机本体。

第三步,上串口当示波器抓计数波形。 把左轮编码器每拍的原始计数经串口打出来,发现"卡一拍"的时间点并非随机——它跟占空比、跟电机负载转矩相关:低速大占空比跳变时密集出现。这让我把怀疑转向了信号线,而不是算法。

第四步,改线做对照。 原本编码器的 AB 两根细线和 PWM 调速线、动力线一起被扎带捆在同一束里,从底盘一头拉到另一头。我把编码器线单独抽出来,换带屏蔽的双绞、远离动力线走,丢步立刻稀疏到几乎没有——锁定根因是信号被功率回路干扰。

根因分析

  1. PWM 开关沿耦合进编码器线。 驱动器输出的 PWM 沿很陡,捆扎段里编码器细线与 PWM/动力线长期平行,容性耦合把开关毛刺叠加到 AB 相上,脉冲计数芯片把毛刺误判成边沿,造成"多计数",硬件消抖窗口内又把真边沿吃掉,表现为"丢一拍"。
  2. 回流路径共用、共地不干净。 编码器和功率回路在底盘多处各自接地,电机换向时的大电流回流在地线上压出毫伏级跳动,正好落在编码器阈值附近,临界翻转到 0/1 之间抖动。
  3. 长排线当天线。 低速时电机反电动势小、PWM 占空比变化剧烈,干扰能量相对更强;编码器线越长越贴动力线,拾取的共模噪声越多,所以越是低速带载越明显——这解释了"高速反而正常"。
  4. 测速端没做硬件滤波。 AB 相直接进 MCU 定时器,没在信号进脚前做任何 RC 抑制,毛刺原封不动进了计数逻辑。

解决过程

改了三件事:一是编码器线独立走线、屏蔽层在驱动器侧单点接地;二是在 A、B 相进主控脚前各加一级 RC 低通;三是把编码器接到驱动器自带的独立编码器接口上,借助这一档接口本身的隔离设计,让脉冲回路和调速 PWM、动力回路在驱动板内部就分开。

硬件上每相:信号串 1kΩ,对地 1nF(fc≈160kHz,远高于电机最高转速对应脉冲频率,又压得住 PWM 沿毛刺)。软件上把编码器口配置加了一路数字滤波,并用串口把滤波前后计数同时打印,肉眼验证是否还有异常跳变。

// TIM2 编码器模式:AB 相进脚前已由 1k+1nF RC 滤波
void Encoder_Init(void){
  TIM_Encoder_InitTypeDef e = {0};
  e.EncoderMode   = TIM_ENCODERMODE_TI12;   // AB 双沿,4 倍频
  e.IC1Polarity   = TIM_ICPOLARITY_RISING;
  e.IC1Selection  = TIM_ICSELECTION_DIRECTTI;
  e.IC1Prescaler  = TIM_ICPSC_DIV1;
  e.IC1Filter     = 0x0F;                   // 数字滤波:0~15,越大越钝,先取15
  e.IC2Selection  = TIM_ICSELECTION_DIRECTTI;
  e.IC2Filter     = 0x0F;
  TIM_EncoderInit(TIM2, &e);
  TIM_Cmd(TIM2, ENABLE);
}

// 每 20ms 取一次增量,串口打印,异常拍直接暴露
void SpeedTick(void){                 // 20ms 定时中断里调用
  static int16_t last = 0;
  int16_t now  = (int16_t)TIM2->CNT;
  int16_t inc  = now - last; last = now;   // 本拍增量
  printf("ENC %d\r\n", inc);               // 串口当示波器:肉眼找跳变
}

【配图建议:串口助手滚动打印截图,左为改线前(带 0/突跳),右为改线后(平滑)】

验证结果

改线+加 RC+数字滤波后,串口连续跑了十几分钟低速带载直行,左轮增量计数不再出现 0 拍和突然补拍,左右轮基本同步,车体直行偏差肉眼可接受。用串口这套对比法,没有示波器也定位到了问题。整体温升在长时间低速运行下也不明显,这一档带 24V-130W 电机留的电流余量够用。

踩坑小结

  1. 编码器"丢步"先别怀疑 PID——先分清是算法丢速还是信号丢拍,串口打印原始增量就能区分。
  2. 编码器信号线千万别和 PWM、动力线捆一束;能独立走线就独立走,屏蔽层只在驱动器侧单点接地。
  3. 信号进 MCU 前先做 RC + 定时器数字滤波两级防御,RC 截止频率卡在"电机最高脉冲频率之上、PWM 沿毛刺之下"。
  4. 带独立编码器接口的驱动器会省不少事,脉冲回路和调速回路在板内就分开了,比自己硬拉排线干净。
  5. 手头没示波器时,串口打增量计数是好替代,异常拍在滚动打印里非常显眼。

如果你也碰上编码器偶发丢拍,是先从走线动手,还是先猛加软件滤波扛过去?欢迎评论区聊聊你的处理顺序。

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -