共计 1435 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点
刚接触 Arduino 电机控制时,我遇到了几个头疼的问题:

- 速度波动大 :用 PWM 直接驱动电机,负载变化时转速像坐过山车
- 编码器读数跳变 :手拨电机转轴时,计数器显示的值总莫名其妙乱跳
- 驱动模块发烫 :L298N 接上没几分钟就开始烫手,有次还烧了保险丝
后来发现这些都不是个例——开环控制没有反馈,电机当然会 ” 自由发挥 ”;编码器信号线没做消抖,Arduino 读取的自然是 ” 毛刺版 ” 数据;而驱动模块发热多半是接线错误导致的短路。
硬件设计
驱动芯片选型
先对比两款常用驱动:
| 特性 | L298N | TB6612FNG |
|---|---|---|
| 工作电压 | 4.5-46V | 2.5-13.5V |
| 峰值电流 | 2A(每路) | 1.2A(连续) |
| 效率 | 较低 (需散热片) | 高 (内置 MOSFET) |
| 价格 | ¥15 左右 | ¥25 左右 |
新手建议 :预算有限选 L298N,追求效率用 TB6612FNG
电路设计
这是带光耦隔离的经典接法(防止电机干扰 MCU):
[VCC]---[10Ω]---[LED]---[光耦输入端]
|
[220Ω]
|
[GND]
编码器消抖电路更简单——在信号线对地接个 0.1μF 电容就行,成本不到五毛钱。
软件实现
PWM 频率设置
Arduino 默认 PWM 频率是 490Hz,但电机响应更好的配置:
// 对于 Timer1 控制的引脚 (D9,D10)
TCCR1B = (TCCR1B & 0b11111000) | 0x02; // 设置频率为 3.9kHz
编码器中断服务
这个带滤波的例程能稳定计数:
constexpr uint8_t ENCODER_PIN = 2; // 使用 D2(中断 0)
volatile int32_t pulseCount = 0;
void setup() {attachInterrupt(digitalPinToInterrupt(ENCODER_PIN), []{
static uint32_t lastTime = 0;
uint32_t now = micros();
// 防抖时间阈值 (根据实际调整)
if(now - lastTime > 200) pulseCount++;
lastTime = now;
}, RISING);
}
PID 闭环控制
核心算法其实就三行:
error = targetSpeed - currentSpeed;
integral += error * dt;
output = Kp*error + Ki*integral + Kd*(error-lastError)/dt;
避坑指南
- 电源去耦 :在驱动模块电源引脚并接 100uF 电解电容 +0.1uF 陶瓷电容
- 中断保护 :在读取 pulseCount 前先
noInterrupts(),读完再interrupts() - 软启动 :用 for 循环逐步增加 PWM 占空比,别直接给最大值
验证测试
用这个代码通过串口输出转速 (单位:RPM):
void loop() {
static uint32_t lastPrint = 0;
if(millis() - lastPrint > 100) {
// 编码器 500 线,计算转速公式:float rpm = (pulseCount*60000.0)/(500*100);
Serial.println(rpm);
pulseCount = 0;
lastPrint = millis();}
}
在串口绘图器里能看到漂亮的阶跃响应曲线。测试发现 PWM 频率在 3 -8kHz 时电流纹波最小。
进阶思考
- 如何用两个编码器实现位置同步控制?
- 电机堵转时怎样自动降低 PWM 占空比?
- 能否用蓝牙模块实现无线调速?
经过两周折腾,我的小车现在能稳稳保持设定速度了。建议新手先用旧电机做实验——我已经烧坏三个电机才摸索出这些经验 (笑)。
正文完
