前面我们学了舵机,它能控制角度,但精度有限,而且不能连续旋转很多圈。今天我们要学的步进电机,解决了这两个问题——它能精确控制旋转角度,还能连续旋转,是 3D 打印机、CNC 雕刻机、机器人关节的核心驱动元件。

        学完这篇,你会完成三个项目:

  • 基础驱动:用 ULN2003 驱动板驱动 28BYJ-48 步进电机
  • 精确控制:精确控制步进电机旋转指定角度
  • 综合项目:简易数控转盘:用步进电机 + 按键,实现精确角度旋转的转盘

一、步进电机是什么?

        步进电机是一种将电脉冲信号转换为角位移的执行元件。简单说:每收到一个脉冲信号,电机就转动一个固定的角度(步距角)

步进电机 vs 普通直流电机 vs 舵机
对比项直流电机舵机步进电机
控制方式控制速度控制角度(0~180°)控制角度(可多圈)
精度中等非常高
连续旋转可以不可以可以
位置保持断电后自由转动断电后可能偏移断电后有保持力矩
典型应用风扇、小车机械臂关节3D打印机、CNC
28BYJ-48 步进电机

        我们使用的是最常见的 28BYJ-48 四相五线制步进电机,配合 ULN2003 驱动板使用。

参数数值
工作电压5V
步距角5.625° / 64(经过内部减速齿轮)
一圈所需步数4096 步(半步模式)
驱动方式四相五线
配套驱动板ULN2003

28BYJ-48 内部有 1:64 的减速齿轮组,所以实际步距角 = 5.625° ÷ 64 ≈ 0.088°,一圈需要约 4096 步(半步驱动模式)。


二、ULN2003 驱动板

        步进电机不能直接由 Arduino 的 GPIO 引脚驱动,因为电流不够。ULN2003 驱动板内部集成了达林顿晶体管阵列,可以放大电流来驱动步进电机。

ULN2003 引脚说明
引脚说明
IN1~IN4信号输入,接 Arduino 的 4 个数字引脚
OUT1~OUT4输出,接步进电机的 5 根线
VCC电源正极(5V)
GND电源负极,需与 Arduino 共地
接线方式
ULN2003 引脚接到
IN1D8
IN2D9
IN3D10
IN4D11
VCC5V(外部电源更佳)
GNDGND

        步进电机的 5 根线直接插入 ULN2003 的 OUT 排母座,防呆设计,不会插反。

️ 步进电机工作电流较大,建议 ULN2003 的 VCC 使用外部 5V 电源供电,不要直接从 Arduino 的 5V 引脚取电,否则可能导致 Arduino 供电不足。


三、实验一:基础旋转

        先让步进电机转起来,体验最基本的驱动方式。

// 步进电机引脚定义
#define IN1 8
#define IN2 9
#define IN3 10
#define IN4 11

// 四相八拍驱动序列(半步模式,更平滑)
int stepSequence[8][4] = {
  {HIGH, LOW,  LOW,  LOW},
  {HIGH, HIGH, LOW,  LOW},
  {LOW,  HIGH, LOW,  LOW},
  {LOW,  HIGH, HIGH, LOW},
  {LOW,  LOW,  HIGH, LOW},
  {LOW,  LOW,  HIGH, HIGH},
  {LOW,  LOW,  LOW,  HIGH},
  {HIGH, LOW,  LOW,  HIGH}
};

void setup() {
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  Serial.println("步进电机基础测试");
}

void loop() {
  // 正转一圈(4096步)
  for (int step = 0; step < 4096; step++) {
    int phase = step % 8;
    digitalWrite(IN1, stepSequence[phase][0]);
    digitalWrite(IN2, stepSequence[phase][1]);
    digitalWrite(IN3, stepSequence[phase][2]);
    digitalWrite(IN4, stepSequence[phase][3]);
    delay(2);  // 控制转速,数值越大越慢
  }

  // 停 1 秒
  allLow();
  delay(1000);

  // 反转一圈
  for (int step = 4095; step >= 0; step--) {
    int phase = step % 8;
    digitalWrite(IN1, stepSequence[phase][0]);
    digitalWrite(IN2, stepSequence[phase][1]);
    digitalWrite(IN3, stepSequence[phase][2]);
    digitalWrite(IN4, stepSequence[phase][3]);
    delay(2);
  }

  allLow();
  delay(2000);
}

void allLow() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}
代码解读
  • stepSequence[8][4]:定义了四相八拍的驱动序列,这是半步驱动模式,比四拍模式更平滑
  • step % 8:取模运算,让序列循环使用
  • 正转:按顺序 0→7 执行序列
  • 反转:按倒序 7→0 执行序列
  • delay(2):每步之间的间隔,控制转速
效果
  • 电机正转一圈 → 停 1 秒 → 反转一圈 → 停 2 秒,循环

四、实验二:精确角度控制

        封装一个函数,通过指定角度来控制步进电机旋转到指定位置。

#define IN1 8
#define IN2 9
#define IN3 10
#define IN4 11

// 一圈 4096 步 = 360°
// 每度需要的步数 = 4096 / 360 ≈ 11.38
#define STEPS_PER_DEGREE 11.38

int stepSequence[8][4] = {
  {HIGH, LOW,  LOW,  LOW},
  {HIGH, HIGH, LOW,  LOW},
  {LOW,  HIGH, LOW,  LOW},
  {LOW,  HIGH, HIGH, LOW},
  {LOW,  LOW,  HIGH, LOW},
  {LOW,  LOW,  HIGH, HIGH},
  {LOW,  LOW,  LOW,  HIGH},
  {HIGH, LOW,  LOW,  HIGH}
};

// 当前角度位置(用于追踪)
float currentAngle = 0;

void setup() {
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  Serial.begin(9600);
  Serial.println("步进电机精确角度控制");
}

void loop() {
  // 转到 90°
  rotateTo(90);
  delay(1000);

  // 转到 180°
  rotateTo(180);
  delay(1000);

  // 转到 270°
  rotateTo(270);
  delay(1000);

  // 回到 0°
  rotateTo(0);
  delay(2000);
}

// 旋转到指定角度
void rotateTo(float targetAngle) {
  float diff = targetAngle - currentAngle;
  int steps = (int)(abs(diff) * STEPS_PER_DEGREE);
  bool clockwise = (diff > 0);

  for (int i = 0; i < steps; i++) {
    if (clockwise) {
      step(1);   // 正转一步
    } else {
      step(-1);  // 反转一步
    }
    delay(2);
  }

  currentAngle = targetAngle;
  Serial.print("旋转到: ");
  Serial.print(targetAngle);
  Serial.println("°");
}

// 执行一步
void step(int direction) {
  static int currentStep = 0;
  currentStep += direction;

  if (currentStep > 7) currentStep = 0;
  if (currentStep < 0) currentStep = 7;

  digitalWrite(IN1, stepSequence[currentStep][0]);
  digitalWrite(IN2, stepSequence[currentStep][1]);
  digitalWrite(IN3, stepSequence[currentStep][2]);
  digitalWrite(IN4, stepSequence[currentStep][3]);
}
代码解读
  • STEPS_PER_DEGREE:每度需要约 11.38 步
  • currentAngle:记录当前角度位置,实现相对运动
  • rotateTo():计算目标角度与当前角度的差值,决定正转还是反转
  • step():使用 static 变量记住当前步序位置,支持正转和反转
效果
  • 电机依次转到 90° → 180° → 270° → 0°,每次停顿 1 秒

五、综合项目:简易数控转盘

用步进电机 + 2 个按键,实现一个可以精确控制旋转角度的转盘。

  • 按键 A:顺时针旋转 45°
  • 按键 B:逆时针旋转 45°
接线
元件引脚接到
按键 A一端D2
按键 A另一端GND
按键 B一端D3
按键 B另一端GND
ULN2003IN1~IN4D8~D11
代码
#define IN1 8
#define IN2 9
#define IN3 10
#define IN4 11

#define KEY_CW  2   // 顺时针按键
#define KEY_CCW 3   // 逆时针按键

#define STEPS_PER_DEGREE 11.38
#define ROTATE_ANGLE 45  // 每次旋转 45°

int stepSequence[8][4] = {
  {HIGH, LOW,  LOW,  LOW},
  {HIGH, HIGH, LOW,  LOW},
  {LOW,  HIGH, LOW,  LOW},
  {LOW,  HIGH, HIGH, LOW},
  {LOW,  LOW,  HIGH, LOW},
  {LOW,  LOW,  HIGH, HIGH},
  {LOW,  LOW,  LOW,  HIGH},
  {HIGH, LOW,  LOW,  HIGH}
};

int currentStep = 0;
float currentAngle = 0;

void setup() {
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);

  pinMode(KEY_CW, INPUT_PULLUP);
  pinMode(KEY_CCW, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.println("简易数控转盘");
  Serial.println("按键A: 顺时针45°  按键B: 逆时针45°");
}

void loop() {
  // 顺时针旋转
  if (digitalRead(KEY_CW) == LOW) {
    delay(50);  // 消抖
    if (digitalRead(KEY_CW) == LOW) {
      rotateBy(ROTATE_ANGLE);
      waitKeyRelease(KEY_CW);
    }
  }

  // 逆时针旋转
  if (digitalRead(KEY_CCW) == LOW) {
    delay(50);
    if (digitalRead(KEY_CCW) == LOW) {
      rotateBy(-ROTATE_ANGLE);
      waitKeyRelease(KEY_CCW);
    }
  }
}

// 旋转指定角度(正数顺时针,负数逆时针)
void rotateBy(float angle) {
  int steps = (int)(abs(angle) * STEPS_PER_DEGREE);
  int direction = (angle > 0) ? 1 : -1;

  for (int i = 0; i < steps; i++) {
    doStep(direction);
    delay(2);
  }

  currentAngle += angle;
  // 保持角度在 0~360 范围内
  if (currentAngle >= 360) currentAngle -= 360;
  if (currentAngle < 0) currentAngle += 360;

  Serial.print("当前角度: ");
  Serial.print(currentAngle);
  Serial.println("°");
}

void doStep(int direction) {
  currentStep += direction;
  if (currentStep > 7) currentStep = 0;
  if (currentStep < 0) currentStep = 7;

  digitalWrite(IN1, stepSequence[currentStep][0]);
  digitalWrite(IN2, stepSequence[currentStep][1]);
  digitalWrite(IN3, stepSequence[currentStep][2]);
  digitalWrite(IN4, stepSequence[currentStep][3]);
}

// 等待按键释放,防止连续触发
void waitKeyRelease(int pin) {
  while (digitalRead(pin) == LOW) {
    delay(10);
  }
}
代码解读
  • rotateBy():按指定角度旋转,正数顺时针,负数逆时针
  • waitKeyRelease():等待按键释放,防止按住不放时连续触发
  • 按键消抖:先检测到低电平后,延时 50ms 再确认,避免抖动误触发
  • 角度归一化:用 if 判断让角度始终在 0~360° 范围内
效果
  • 按一次按键 A → 转盘顺时针旋转 45°
  • 按一次按键 B → 转盘逆时针旋转 45°
  • 串口显示当前角度位置

六、进阶:使用 AccelStepper 库

        手动编写驱动序列虽然能学到原理,但实际项目中推荐使用 AccelStepper 库,它支持加减速控制,运行更平滑。

安装库

        Arduino IDE → 工具 → 管理库 → 搜索 "AccelStepper" → 安装

示例代码
#include <AccelStepper.h>

// 定义步进电机:半4拍模式,引脚 8,10,9,11(注意顺序)
AccelStepper stepper(AccelStepper::HALF4WIRE, 8, 10, 9, 11);

void setup() {
  // 设置最大速度和加速度
  stepper.setMaxSpeed(1000);      // 最大速度(步/秒)
  stepper.setAcceleration(500);   // 加速度(步/秒²)

  Serial.begin(9600);
  Serial.println("AccelStepper 库测试");
}

void loop() {
  // 正转 4096 步(一圈)
  stepper.moveTo(4096);
  while (stepper.currentPosition() != 4096) {
    stepper.run();
  }

  delay(1000);

  // 反转回 0
  stepper.moveTo(0);
  while (stepper.currentPosition() != 0) {
    stepper.run();
  }

  delay(2000);
}
AccelStepper 的优势
  • 加减速控制:启动时逐渐加速,停止前逐渐减速,避免丢步
  • 非阻塞运行stepper.run() 可以在 loop() 中持续调用,不阻塞其他代码
  • 位置追踪currentPosition() 实时获取当前位置
  • 速度控制setMaxSpeed() 和 setSpeed() 精确控制转速

七、步进电机使用注意事项

供电问题

        28BYJ-48 工作电流约 200~300mA,建议 ULN2003 使用外部 5V 电源供电,不要从 Arduino 的 5V 引脚取电。

丢步问题

        如果转速过快或负载过大,步进电机会"丢步"(实际转动角度小于预期)。解决方法:

  • 降低转速(增大 delay 值)
  • 减小负载
  • 使用 AccelStepper 库的加减速功能
发热问题

        步进电机在保持位置时也会消耗电流并发热,如果不需要保持力矩,可以在停止时调用 allLow() 断开所有相的电流。

精度说明

        28BYJ-48 由于内部减速齿轮的间隙,实际精度约为 ±1°,不适合需要极高精度的场合。如果需要更高精度,可以选择不带减速齿轮的步进电机(如 NEMA17)。


八、本篇小结

        回顾一下今天学到的核心知识:

  • 步进电机原理:每个脉冲转动一个固定角度,精度远高于直流电机
  • 28BYJ-48:四相五线制,配合 ULN2003 驱动板,一圈 4096 步
  • 驱动序列:四相八拍半步驱动,比四拍模式更平滑
  • 精确角度控制:通过计算步数实现指定角度旋转
  • 综合项目:按键控制的数控转盘,每次精确旋转 45°
  • AccelStepper 库:支持加减速、非阻塞运行,推荐使用

        步进电机让 Arduino 具备了精密运动控制的能力。下一篇,我们学习LCD1602 液晶显示模块——让 Arduino 能够显示文字和自定义字符。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐