电机抖了三天,最后发现是电流采样线走错了

2026-11-102

电机抖了三天,最后发现是电流采样线走错了

开头引子

上周给循迹小车调速度环,低速段一直"一顿一顿"地走,PID 怎么调都救不回来。一开始以为是编码器分辨率不够,后来甚至怀疑电机本身有问题。折腾了三天,最后发现是电流采样线的共地没处理好,采样信号被功率回路噪声糊掉了。下面把排查过程完整记一下,给同样踩坑的朋友参考。

硬件清单

  • 主控:Arduino UNO R3
  • 驱动器:艾思控某款直流有刷驱动器(DRV8833 同档位,PWM 输入 + 电流采样输出)
  • 电机:JGB37-555 直流减速电机,12V,带 AB 相编码器(13 PPR)
  • 电源:12V 3A 开关电源(电机供电)+ 5V USB(主控供电,问题根源之一)
  • 传感器:TCRT5000 四路红外循迹模块
  • 其他:杜邦线若干、面包板、10Ω/0.1W 电阻(采样电阻旁并联用)

接线说明

Arduino UNO          艾思控驱动器           电机/电源
─────────           ─────────────         ─────────
D9  (PWM)    ──→    PWM_IN
D8  (DIR)    ──→    DIR_IN
A0  (ADC)    ←──    I_SENSE (电流采样输出)
D2/D3 (INT)  ←──    编码器 A/B 相

驱动器 VM/GND  ──→  12V 电源正/负
Arduino 5V/GND ──→  USB 供电
⚠️ 12V 电源 GND 与 Arduino GND 最初未共地(这是坑)

现象描述

小车在直线段低速运行时(目标速度 20–40 RPM),电机会出现明显的"蠕动":走一下、停一下,串口打印的速度曲线呈锯齿状,幅值波动可达 ±30%。提高目标速度到 80 RPM 以上反而正常。更奇怪的是,把电机拆下来空载跑,抖动消失;装回小车加载后,抖动重现。

排查过程

第 1 步:怀疑 PID 参数
把 Kp 从 2.0 一路降到 0.3,Ki 从 1.0 降到 0.1,Kd 从 0.5 调到 0,抖动依旧。甚至把积分项完全去掉,只剩纯比例,低速还是抖。→ 排除纯参数问题。

第 2 步:怀疑编码器
用示波器(借同学的)看编码器 A/B 相波形,低速下脉冲干净、无丢步。用 pulseIn() 单独测速,波动也在 ±5% 以内。→ 排除编码器本身。

第 3 步:怀疑机械负载
把小车架空,轮子悬空跑,抖动明显减轻但仍存在。用手轻捏轮子增加负载,抖动立刻加剧。→ 说明问题与负载电流相关,指向电流环/采样。

第 4 步:看电流采样信号
把示波器探头接到 A0(I_SENSE)和 GND 之间。空载时波形平稳;一旦电机带载,采样线上出现峰峰值约 200mV 的高频毛刺,频率与 PWM 开关频率(约 20kHz)一致。而驱动器的电流采样灵敏度是 0.5V/A,200mV 毛刺相当于 ±0.4A 的虚假电流波动——这正好对应速度环输出的剧烈震荡。

根因分析

  1. 双电源未共地:电机用 12V 开关电源,Arduino 用 USB 供电,两者的 GND 浮空。功率回路的大电流变化通过寄生电容耦合到采样地,形成共模噪声。
  2. 采样线走线过长:从驱动器 I_SENSE 到 Arduino A0 用了 20cm 杜邦线,且与电机动力线并行走了十几厘米,相当于天线,直接拾取 PWM 开关噪声。
  3. 缺少 RC 低通滤波:驱动器输出的电流模拟信号未经任何滤波就直接进 ADC,高频噪声全部被采样进来。Arduino analogRead() 本身只有约 9kHz 等效带宽,挡不住 20kHz 的开关噪声。
  4. 死区效应叠加:低速时 PWM 占空比很小(<10%),驱动器内部 MOSFET 的死区时间占比相对变大,电流纹波本身就更明显,采样噪声被进一步放大。

解决过程

硬件改动(三件事一起做):

  1. 单点共地:把 12V 电源的 GND 与 Arduino 的 GND 用一根短粗线(20AWG)直接连到驱动器 GND 端子上,形成星型接地,不再浮空。
  2. 采样线加 RC 滤波:在 I_SENSE 与 A0 之间串一个 100Ω 电阻,A0 对 GND 并一个 1μF 陶瓷电容,构成截止频率约 1.6kHz 的低通滤波器。
  3. 分离走线:把采样信号线与电机动力线分开走,垂直交叉而非平行。

软件改动:速度环加入抗饱和与前馈

// 速度环 PI + 抗积分饱和 + 速度前馈
float speedSetpoint = 30.0;   // 目标转速 RPM
float Kp = 0.8, Ki = 0.3, Kff = 0.02;
float integral = 0;
float integralMax = 50.0;     // 抗饱和限幅
float lastTime = 0;

void loop() {
  float now = millis() / 1000.0;
  float dt = now - lastTime;
  lastTime = now;

  float speedNow = readEncoderSpeed();       // 编码器测速
  float error = speedSetpoint - speedNow;

  // 速度前馈:根据目标速度预估一个基础 PWM
  float pwmFF = Kff * speedSetpoint;

  // PI 反馈
  integral += error * dt;
  integral = constrain(integral, -integralMax, integralMax); // 抗饱和
  float pwmFB = Kp * error + Ki * integral;

  // 合成输出,限幅 0–255
  int pwmOut = constrain((int)(pwmFF + pwmFB), 0, 255);
  analogWrite(9, pwmOut);

  // 串口打印调试(每 100ms 一次)
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 100) {
    Serial.print("spd:"); Serial.print(speedNow, 1);
    Serial.print(" err:"); Serial.print(error, 1);
    Serial.print(" pwm:"); Serial.println(pwmOut);
    lastPrint = millis();
  }
}

验证结果

改动后重新测试:

  • 空载:低速 20 RPM 运行平稳,串口速度波动从 ±30% 降到 ±5% 以内。
  • 带载(小车落地):同样 20–40 RPM 区间不再"蠕动",直线循迹平滑。
  • 波形对比:示波器看 A0 引脚,带载时毛刺峰峰值从 200mV 降到 30mV 以内。

踩坑小结

  1. 双电源供电一定要共地,而且最好单点共地到驱动器端子,不要通过面包板长距离串接。
  2. 模拟采样线越短越好,远离功率线和 PWM 线;实在绕不开就垂直交叉。
  3. 电流/电压采样信号务必加 RC 低通,截止频率设为信号带宽的 3–5 倍即可,别指望软件平均能滤掉 20kHz 噪声。
  4. 低速抖动先查采样,再调 PID——噪声喂进闭环,参数怎么调都是错的。
  5. 调试时串口打印比盲调有用得多,把速度、误差、PWM 输出一起打出来,一眼就能看出是测量问题还是控制问题。

结尾

这次折腾让我意识到:硬件噪声问题用纯软件思路是解不了的。大家调电机闭环时有没有遇到过类似的"假性抖动"?欢迎在评论区聊聊你的踩坑经历。

创作许可协议

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

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