电机抖了三天,最后发现是电流采样线走错了
电机抖了三天,最后发现是电流采样线走错了
开头引子
上周给循迹小车调速度环,低速段一直"一顿一顿"地走,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 的虚假电流波动——这正好对应速度环输出的剧烈震荡。
根因分析
- 双电源未共地:电机用 12V 开关电源,Arduino 用 USB 供电,两者的 GND 浮空。功率回路的大电流变化通过寄生电容耦合到采样地,形成共模噪声。
- 采样线走线过长:从驱动器 I_SENSE 到 Arduino A0 用了 20cm 杜邦线,且与电机动力线并行走了十几厘米,相当于天线,直接拾取 PWM 开关噪声。
- 缺少 RC 低通滤波:驱动器输出的电流模拟信号未经任何滤波就直接进 ADC,高频噪声全部被采样进来。Arduino
analogRead()本身只有约 9kHz 等效带宽,挡不住 20kHz 的开关噪声。 - 死区效应叠加:低速时 PWM 占空比很小(<10%),驱动器内部 MOSFET 的死区时间占比相对变大,电流纹波本身就更明显,采样噪声被进一步放大。
解决过程
硬件改动(三件事一起做):
- 单点共地:把 12V 电源的 GND 与 Arduino 的 GND 用一根短粗线(20AWG)直接连到驱动器 GND 端子上,形成星型接地,不再浮空。
- 采样线加 RC 滤波:在 I_SENSE 与 A0 之间串一个 100Ω 电阻,A0 对 GND 并一个 1μF 陶瓷电容,构成截止频率约 1.6kHz 的低通滤波器。
- 分离走线:把采样信号线与电机动力线分开走,垂直交叉而非平行。
软件改动:速度环加入抗饱和与前馈
// 速度环 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 以内。
踩坑小结
- 双电源供电一定要共地,而且最好单点共地到驱动器端子,不要通过面包板长距离串接。
- 模拟采样线越短越好,远离功率线和 PWM 线;实在绕不开就垂直交叉。
- 电流/电压采样信号务必加 RC 低通,截止频率设为信号带宽的 3–5 倍即可,别指望软件平均能滤掉 20kHz 噪声。
- 低速抖动先查采样,再调 PID——噪声喂进闭环,参数怎么调都是错的。
- 调试时串口打印比盲调有用得多,把速度、误差、PWM 输出一起打出来,一眼就能看出是测量问题还是控制问题。
结尾
这次折腾让我意识到:硬件噪声问题用纯软件思路是解不了的。大家调电机闭环时有没有遇到过类似的"假性抖动"?欢迎在评论区聊聊你的踩坑经历。



