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

Arduino控制Dobot引脚为LOW时仍移动无法立即停止问题

问题根因
  • 状态检测粒度过粗:当前代码仅在整段moveX()、PutDown()函数执行完成后才读取一次按钮电平,而上述函数内部包含多步运动指令、最长1300ms的阻塞delay(),一旦进入函数,无论中途按钮是否切为LOW,都必须等函数内所有代码跑完才会判断状态,必然出现多跑数步动作的情况。
  • Dobot指令队列缓存未清空:所有SetPTPCmd()调用都是先把运动指令写入Dobot机载的指令队列,并非发完就立刻执行完成,就算靠break跳出循环,之前已经写入队列的未执行指令仍会被机械臂依次跑完,不会即时停止。
  • 初始化逻辑错误:InitRAM()、ProtocolInit()被放在loop()的死循环外层,每次loop重启都会重复初始化协议与运动参数,额外增加运行不稳定风险;外加2秒的定时检测间隔,进一步拉长了按钮响应延迟。
  • 缺少按钮消抖处理:机械按钮电平跳变时的毫秒级抖动,也会导致状态识别出错。
修复方案
  • 新增全局紧急停止标志,将按钮检测放到10ms级的定时器中断中执行,同时做20ms消抖处理,只要检测到按钮切为LOW,立刻置位停止标志。
  • 检测到停止信号时,第一时间调用Dobot协议自带的停止、清队列接口,直接终止机械臂运动、清空所有缓存的未执行指令,不要仅靠break跳出循环。
  • 把所有阻塞式delay()替换为基于millis()的非阻塞延时,延时过程中持续运行协议处理、检测停止标志,避免程序卡死在延时阶段无法响应信号。
  • 将InitRAM()、ProtocolInit()移到setup()中仅执行一次,去掉loop()内多余的死循环嵌套,删除不必要的2秒检测间隔。
  • 按钮引脚改为INPUT_PULLUP上拉输入模式,降低外部电平干扰。
核心修改代码示例
#include "stdio.h"
#include "Protocol.h"
#include "command.h"
#include "FlexiTimer2.h"
#include "PROTOCOLID.h"
#include "stdint.h"
#include <Arduino.h>

const int CL1 = 3; //Start Suction
const int CL3 = 2; // Stop Suction
const int StartButton = 10;
const int StopButton = 4;

#define SERIAL_TX_BUFFER_SIZE 64
#define SERIAL_RX_BUFFER_SIZE 256

EndEffectorParams gEndEffectorParams;
JOGJointParams  gJOGJointParams;
JOGCoordinateParams gJOGCoordinateParams;
JOGCommonParams gJOGCommonParams;
JOGCmd          gJOGCmd;
PTPCoordinateParams gPTPCoordinateParams;
PTPCommonParams gPTPCommonParams;
PTPCmd          gPTPCmd;
PTPJumpParams   gPTPJumpParams;
HOMECmd         ghomeCmd;
WAITCmd         gwaitCmd;
uint64_t gQueuedCmdIndex;

// 新增全局紧急停止标志
volatile bool isEmergencyStop = false;

void Serialread()
{
  while(Serial1.available()) {
        uint8_t data = Serial1.read();
        if (RingBufferIsFull(&gSerialProtocolHandler.rxRawByteQueue) == false) {
            RingBufferEnqueue(&gSerialProtocolHandler.rxRawByteQueue, &data);
        }
  }
}

// 定时器中执行按钮检测,带消抖
void ButtonCheck() {
  static uint32_t lastDebounceTime = 0;
  static int lastButtonState = HIGH;
  int currentState = digitalRead(StartButton);
  if (currentState != lastButtonState) {
    lastDebounceTime = millis();
  }
  if (millis() - lastDebounceTime > 20) {
    if (currentState == LOW) {
      isEmergencyStop = true;
      // 立刻停止运动、清空指令队列
      SetQueuedCmdStopExec(true, &gQueuedCmdIndex);
      SetQueuedCmdClear();
      ProtocolProcess();
      StopSuck();
    } else {
      isEmergencyStop = false;
    }
  }
  lastButtonState = currentState;
}

int Serial_putc( char c, struct __file * )
{
    Serial.write( c );
    return c;
}

void printf_begin(void)
{
    fdevopen( &Serial_putc, 0 );
}

void InitRAM(void)
{
    gPTPCoordinateParams.xyzVelocity = 200;
    gPTPCoordinateParams.rVelocity = 200;
    gPTPCoordinateParams.xyzAcceleration = 200;
    gPTPCoordinateParams.rAcceleration = 200;
    SetPTPCoordinateParams(&gPTPCoordinateParams, true,  &gQueuedCmdIndex);
    ProtocolProcess();

    gPTPCommonParams.velocityRatio = 200;
    gPTPCommonParams.accelerationRatio = 200;
    SetPTPCommonParams(&gPTPCommonParams, true, &gQueuedCmdIndex);
    ProtocolProcess();

    gPTPCmd.ptpMode = MOVL_XYZ;
    gQueuedCmdIndex = 0;
}

void Suck()
{
  digitalWrite(CL1,LOW);
  digitalWrite(CL3,HIGH);
}
void StopSuck()
{
  digitalWrite(CL1,HIGH);
  digitalWrite(CL3,LOW);
}

// 非阻塞延时工具函数
bool NonBlockDelay(uint32_t delayTime) {
  static uint32_t startTick = 0;
  static bool isStart = false;
  if (!isStart) {
    startTick = millis();
    isStart = true;
  }
  if (millis() - startTick >= delayTime) {
    isStart = false;
    return true;
  }
  return false;
}

void PutDown()
{
  if (isEmergencyStop) return;
  gPTPCmd.z = 40;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;
                
  gPTPCmd.x = 0;
  gPTPCmd.y = 250;
  gPTPCmd.z = 10;
  gPTPCmd.r = 0;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  gPTPCmd.z = -10;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  while(!NonBlockDelay(1000)) {
    ProtocolProcess();
    if (isEmergencyStop) return;
  }
  StopSuck();

  gPTPCmd.z = 40;
  SetPTPCmd(&gPTPCmd, true,  &gQueuedCmdIndex);
  ProtocolProcess();
}

void move1()
{
  if (isEmergencyStop) return;               
  gPTPCmd.x = 235;
  gPTPCmd.y = 0;
  gPTPCmd.z = 40;
  gPTPCmd.r = 0; 
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  gPTPCmd.z = -30;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  Suck();
  while(!NonBlockDelay(1300)) {
    ProtocolProcess();
    if (isEmergencyStop) return;
  }
}

void move2()
{
  if (isEmergencyStop) return;            
  gPTPCmd.x = 255;
  gPTPCmd.y = 0;
  gPTPCmd.z = 40;
  gPTPCmd.r = 0; 
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  gPTPCmd.z = -30;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  Suck();
  while(!NonBlockDelay(1300)) {
    ProtocolProcess();
    if (isEmergencyStop) return;
  }
}

void move3()
{
  if (isEmergencyStop) return;             
  gPTPCmd.x = 275;
  gPTPCmd.y = 0;
  gPTPCmd.z = 40;
  gPTPCmd.r = 0; 
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  gPTPCmd.z = -30;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();  
  if (isEmergencyStop) return;

  Suck();
  while(!NonBlockDelay(1300)) {
    ProtocolProcess();
    if (isEmergencyStop) return;
  }
}

void move4()
{
  if (isEmergencyStop) return;
  gPTPCmd.x = 295;
  gPTPCmd.y = 0;
  gPTPCmd.z = 40;
  gPTPCmd.r = 0; 
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  gPTPCmd.z = -30;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess(); 
  if (isEmergencyStop) return;

  gPTPCmd.z = 40;
  SetPTPCmd(&gPTPCmd, true, &gQueuedCmdIndex);
  ProtocolProcess();
  if (isEmergencyStop) return;

  Suck();
  while(!NonBlockDelay(1300)) {
    ProtocolProcess();
    if (isEmergencyStop) return;
  }
}

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200); 
    printf_begin();
    pinMode(CL1,OUTPUT);
    pinMode(CL3,OUTPUT);
    pinMode(StartButton,INPUT_PULLUP);
    pinMode(StopButton,INPUT_PULLUP);
    // 定时器10ms触发,同时处理串口读取和按钮检测
    FlexiTimer2::set(10, [](){
      Serialread();
      ButtonCheck();
    }); 
    FlexiTimer2::start();
    // 初始化仅执行一次
    ProtocolInit();
    InitRAM();
    printf("\r\n======Enter demo application======\r\n");
}

void loop() 
{
  ProtocolProcess();
  if (isEmergencyStop) return;
  static uint8_t step = 0;
  static uint32_t stepTick = millis();
  
  // 每50ms推进一个动作步,动作内部自带停止检测
  if (millis() - stepTick > 50) {
    stepTick = millis();
    switch(step) {
      case 0: move1(); step++; break;
      case 1: PutDown(); step++; break;
      case 2: move2(); step++; break;
      case 3: PutDown(); step++; break;
      case 4: move3(); step++; break;
      case 5: PutDown(); step++; break;
      case 6: move4(); step++; break;
      case 7: PutDown(); step = 0; break;
      default: step = 0;
    }
  }
}
注意事项
  • 所有运动函数每发送完一条PTP指令都要加停止标志判断,不要把多步运动全部发完再检测状态。
  • 发送停止、清队列指令后要多执行几次ProtocolProcess(),确保指令完整发送到Dobot机载控制器,避免残留指令继续执行。
  • 上拉输入模式下,按钮需接在对应IO引脚和GND之间,按下时为LOW电平、松开为HIGH电平,抗干扰能力远高于普通浮空输入模式。

内容的提问来源于stack exchange,提问作者Look Ace

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:45:44