You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

步进电机移动至目标位置时实时更新速度的问题排查

问题:电位器数值无法实时更新

需求:需要实时更新电位器(速度、加速度)的数值,而非仅在启动时读取一次,但尝试多种方法均无效。

核心问题分析:

  • 代码中多处使用阻塞式循环(如while(digitalRead(gotoPositionAPin));),导致程序卡在循环内无法执行电位器读取逻辑。
  • AccelStepper的runToNewPosition()方法是阻塞式的,调用后会持续执行直到电机到达目标位置,期间无法处理其他任务(包括电位器采样)。
  • 电位器读取的CheckPots()函数依赖loopCtr计数,一旦程序进入阻塞循环,计数无法递增,电位器数值自然无法更新。

当前代码逻辑:

  • 调节速度电位器设置电机最大速度,调节加速度电位器设置加速度
  • 按右键保存当前位置为A,按左键保存当前位置为B
  • 按对应按钮控制电机移动至A/B位置,移动时使用之前设置的速度和加速度

解决方案:重构为非阻塞式逻辑

要实现电位器实时更新,必须消除所有阻塞代码,让loop()能持续循环执行,同时处理电机运行、按钮检测和电位器采样:

  1. 替换阻塞式电机控制:用moveTo()设置目标位置,在loop()中调用stepper1.run()实现非阻塞式步进控制。
  2. 移除按钮阻塞循环:通过检测按钮状态变化(上升沿/下降沿)触发动作,避免用while等待按钮释放。
  3. 调整电位器采样逻辑:直接在每次loop()中采样(或合理间隔采样),确保采样不会被阻塞打断。

修改后的完整代码
#include <AccelStepper.h>

// 定义步进电机引脚
AccelStepper stepper1(1, 12, 11); 

#define stepsPerRev      1600
#define stepPin          12
#define dirPin           11
#define ledPin           13
#define rotateLeftPin    7
#define rotateRightPin   6
#define savePositionAPin 5
#define savePositionBPin 4
#define gotoPositionAPin 3
#define gotoPositionBPin 2
#define maxSpeedPin      0
#define accelPin         1

#define printDebug       0

#define MAX_STEPS_PER_SECOND  1000
#define MIN_STEPS_PER_SECOND  27
#define ACCEL_RATIO           1

// 按钮状态变量,用于检测状态变化
bool lastLeftBtn = LOW;
bool lastRightBtn = LOW;
bool lastSaveABtn = LOW;
bool lastSaveBBtn = LOW;
bool lastGotoABtn = LOW;
bool lastGotoBBtn = LOW;

int maxSpeed = 0;
int accel = 0;
long savedPosA = 0;
long savedPosB = 0;

float fStepsPerSecond = 0.0;

void setup() 
{
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  // 使用内部上拉,避免外部下拉电阻
  pinMode(rotateLeftPin, INPUT_PULLUP);
  pinMode(rotateRightPin, INPUT_PULLUP);
  pinMode(savePositionAPin, INPUT_PULLUP);
  pinMode(savePositionBPin, INPUT_PULLUP);
  pinMode(gotoPositionAPin, INPUT_PULLUP);
  pinMode(gotoPositionBPin, INPUT_PULLUP);
  
  if (printDebug)
  {
    Serial.begin(9600);
  }
  
  blink(2);

  // 初始化电位器数值
  UpdatePots();
  stepper1.setMaxSpeed(fStepsPerSecond);
  stepper1.setAcceleration(accel);
}

void loop() 
{  
  // 实时更新电位器数值
  UpdatePots();
  // 更新电机速度和加速度设置
  stepper1.setMaxSpeed(fStepsPerSecond);
  stepper1.setAcceleration(accel);

  // 处理左右旋转按钮(非阻塞)
  HandleRotateButtons();

  // 处理位置保存按钮
  HandleSaveButtons();

  // 处理 goto 位置按钮
  HandleGotoButtons();

  // 非阻塞式运行步进电机
  stepper1.run();
}

// 闪烁LED
void blink(int howManyTimes) 
{
  for (int i=0; i < howManyTimes; i++) 
  {
    digitalWrite(ledPin, HIGH);
    delay(200);
    digitalWrite(ledPin, LOW);
    delay(200);
  }
}

// 实时更新电位器数值
void UpdatePots(void)
{
  // 读取速度电位器并转换
  int currentSpeedPot = analogRead(maxSpeedPin);
  float fMaxSpeed = currentSpeedPot / 1023.0;
  fStepsPerSecond = MIN_STEPS_PER_SECOND + (fMaxSpeed * (MAX_STEPS_PER_SECOND - MIN_STEPS_PER_SECOND));
  fStepsPerSecond = constrain(fStepsPerSecond, MIN_STEPS_PER_SECOND, MAX_STEPS_PER_SECOND);

  // 读取加速度电位器
  accel = analogRead(accelPin) / ACCEL_RATIO;
}

// 处理左右旋转按钮
void HandleRotateButtons()
{
  bool leftBtn = digitalRead(rotateLeftPin);
  bool rightBtn = digitalRead(rotateRightPin);

  // 左旋转:按钮按下时持续运行
  if (leftBtn == LOW && lastLeftBtn == HIGH)
  {
    stepper1.setSpeed(-fStepsPerSecond);
  }
  else if (leftBtn == HIGH && lastLeftBtn == LOW)
  {
    stepper1.setSpeed(0);
  }

  // 右旋转
  if (rightBtn == LOW && lastRightBtn == HIGH)
  {
    stepper1.setSpeed(fStepsPerSecond);
  }
  else if (rightBtn == HIGH && lastRightBtn == LOW)
  {
    stepper1.setSpeed(0);
  }

  // 更新按钮状态
  lastLeftBtn = leftBtn;
  lastRightBtn = rightBtn;

  // 如果正在旋转,维持电机运行
  if (leftBtn == LOW || rightBtn == LOW)
  {
    stepper1.runSpeed();
  }
}

// 处理位置保存按钮
void HandleSaveButtons()
{
  bool saveABtn = digitalRead(savePositionAPin);
  bool saveBBtn = digitalRead(savePositionBPin);

  // 保存位置A(按钮按下时触发一次)
  if (saveABtn == LOW && lastSaveABtn == HIGH)
  {
    savedPosA = stepper1.currentPosition();
    if (printDebug)
    {
      Serial.print("Saved A at :");
      Serial.println(savedPosA);
    }
  }

  // 保存位置B
  if (saveBBtn == LOW && lastSaveBBtn == HIGH)
  {
    savedPosB = stepper1.currentPosition();
    if (printDebug)
    {
      Serial.print("Saved B at :");
      Serial.println(savedPosB);
    }
  }

  lastSaveABtn = saveABtn;
  lastSaveBBtn = saveBBtn;
}

// 处理 goto 位置按钮
void HandleGotoButtons()
{
  bool gotoABtn = digitalRead(gotoPositionAPin);
  bool gotoBBtn = digitalRead(gotoPositionBPin);

  // 移动到位置A(按钮按下时触发一次)
  if (gotoABtn == LOW && lastGotoABtn == HIGH)
  {
    if (printDebug)
    {
      Serial.print("cur pos = ");
      Serial.println(stepper1.currentPosition());
      Serial.print("Going to A = ");
      Serial.println(savedPosA);
      Serial.print("Speed = ");
      Serial.println(fStepsPerSecond);
      Serial.print("Accel = ");
      Serial.println(accel);
    }
    stepper1.moveTo(savedPosA);
  }

  // 移动到位置B
  if (gotoBBtn == LOW && lastGotoBBtn == HIGH)
  {
    if (printDebug)
    {
      Serial.print("cur pos = ");
      Serial.println(stepper1.currentPosition());
      Serial.print("Going to B = ");
      Serial.println(savedPosB);
      Serial.print("Speed = ");
      Serial.println(fStepsPerSecond);
      Serial.print("Accel = ");
      Serial.println(accel);
    }
    stepper1.moveTo(savedPosB);
  }

  lastGotoABtn = gotoABtn;
  lastGotoBBtn = gotoBBtn;
}

关键修改说明:

  • 按钮处理:使用INPUT_PULLUP模式,通过检测按钮状态的上升沿/下降沿触发动作,彻底移除阻塞式while循环。
  • 电机控制:用moveTo()设置目标位置,stepper1.run()在每次loop()中执行步进动作,实现非阻塞运行,期间程序可以正常处理电位器采样。
  • 电位器采样:UpdatePots()在每次loop()中执行,确保数值实时更新,同时用constrain()限制速度范围,避免超出边界值。

内容的提问来源于stack exchange,提问作者Klāvs Skrastiņš

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:50:18