Arduino控制编码器电机:从基础原理到精准调速实战

1次阅读
没有评论

共计 2260 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

为什么需要闭环控制?

刚开始玩电机控制时,我用 PWM 直接驱动电机,发现两个头疼问题:

  • 电机转速会随着负载变化明显波动(空载时转得快,加上负载就变慢)
  • 想精确控制转 10 圈,结果可能转了 9 圈半或 11 圈

这是因为开环控制就像蒙眼走路——我们发出指令后,根本不知道电机实际执行情况。而带编码器的电机给了我们 ” 眼睛 ”,通过反馈信号实现闭环控制。

硬件选型要点

编码器类型选择

常见的有两种编码器:

  • 增量式编码器(需要参考零点)
  • 绝对式编码器(可直接读取位置)

对于大多数 Arduino 项目,增量式编码器性价比更高。我用的就是常见的 600PPR(每转 600 脉冲)正交编码器。

电机驱动方案对比

驱动方式 优点 缺点
L298N 便宜易得 效率低发热大
TB6612 体积小效率高 电流较小
VNH5019 大电流带保护 价格较高

我选择 TB6612,因为它的 MOSFET 驱动效率高达 95%,而且有正反转控制引脚。

核心实现三步走

1. 编码器信号采集

正交编码器有两路输出(A 相和 B 相),关键是要用硬件中断来捕获跳变沿:

// 引脚定义
#define ENCODER_A 2  // 必须接支持中断的引脚
#define ENCODER_B 3

volatile long encoderPos = 0;  // volatile 用于多线程访问

void setup() {pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);
  // 设置中断服务函数
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), doEncoderA, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_B), doEncoderB, CHANGE);
}

// 中断服务函数
void doEncoderA() {if(digitalRead(ENCODER_A) == digitalRead(ENCODER_B)) {encoderPos++;} else {encoderPos--;}
}

void doEncoderB() {if(digitalRead(ENCODER_A) == digitalRead(ENCODER_B)) {encoderPos--;} else {encoderPos++;}
}

2. PID 控制器实现

Arduino 有现成的 PID 库,但理解原理很重要:

// 自定义简化版 PID 实现
class SimplePID {
  public:
    float Kp, Ki, Kd;
    float setpoint;
    float lastError, integral;

    SimplePID(float kp, float ki, float kd) {
      Kp = kp; Ki = ki; Kd = kd;
      reset();}

    void reset() {lastError = integral = 0;}

    float compute(float input) {
      float error = setpoint - input;
      integral += error;
      float derivative = error - lastError;
      lastError = error;
      return Kp*error + Ki*integral + Kd*derivative;
    }
};

3. 电机驱动整合

将编码器计数转换为转速(RPM):

// 计算转速函数
float getRPM() {
  static long lastPos = 0;
  static unsigned long lastTime = 0;

  long deltaPos = encoderPos - lastPos;
  unsigned long deltaTime = millis() - lastTime;

  lastPos = encoderPos;
  lastTime = millis();

  // 600PPR 编码器,4 倍频后每转 2400 计数
  return (deltaPos * 60000.0) / (2400 * deltaTime); 
}

参数整定实战技巧

调 PID 参数就像炒菜放调料,我的经验是:

  1. 先把 Ki 和 Kd 设为 0,逐渐增加 Kp 直到系统开始震荡
  2. 取震荡时 Kp 值的 50% 作为初始值
  3. 慢慢增加 Ki 消除静差(但别加太多!)
  4. 最后加 Kd 抑制超调

用串口绘图工具观察响应曲线最直观:

Arduino 控制编码器电机:从基础原理到精准调速实战

常见坑位预警

信号干扰问题

遇到编码器计数不准时:

  • 给编码器电源加 104 电容
  • 使用双绞线连接
  • 在中断服务函数中做消抖处理

过流保护

电机堵转时电流会飙升,建议:

  • 驱动芯片加散热片
  • 程序里监测电流(可用 ACS712 模块)
  • 设置软件限流
// 简单电流保护示例
if(millis() - lastCheck > 100) {float current = analogRead(CURRENT_PIN) * 0.185; // ACS712 5A 量程
  if(current > 2.0) {  // 超过 2A 停止
    motorStop();}
  lastCheck = millis();}

进阶玩法

等基础调速稳定后,可以尝试:

  • 位置控制模式(先加速后减速的 S 曲线规划)
  • 多电机同步(主从模式或 CAN 总线通信)
  • 结合 ROS 做移动机器人

我的小车项目就是用两个编码器电机 +Arduino 实现的差速转向,里程计精度能达到±2cm:

最后的小建议

调试时一定要有耐心,PID 参数可能需要反复调整几十次。建议先用小电压测试,避免电机高速失控。遇到问题可以先检查:

  1. 编码器接线是否正确(A/ B 相不要接反)
  2. 中断优先级是否冲突
  3. 采样周期是否合适(一般 10-100ms)

完整工程代码已上传 GitHub(搜索 ”Arduino-Encoder-PID”),里面有更详细的注释和测试案例。欢迎交流改进建议!

正文完
 0
评论(没有评论)