直流减速电机 + Arduino 接线方案

发布时间:2026/7/18 3:56:22
直流减速电机 + Arduino 接线方案 总体接线图12V电源 ├── BTS7960 → 37GB545D电机动力线 └── LM2596 → 5V → Arduino / 编码器 / 传感器 Arduino ├── D5 → BTS7960 RPWM ├── D6 → BTS7960 LPWM ├── D2 ← 编码器A相 ├── D3 ← 编码器B相 ├── D7 ← HOME限位 └── A0 ← FSR压力传感器1.BTS7960 接电源和电机BTS7960连接B12V电源正极B-12V电源负极M电机动力线1M-电机动力线22.BTS7960 接 ArduinoBTS7960Arduino作用VCC5V逻辑电源GNDGND信号地RPWMD5正转PWMLPWMD6反转PWMR_END8右桥使能L_END9左桥使能3.37GB545D 编码器接 Arduino编码器线ArduinoVCC5VGNDGNDA相D2B相D34.LM2596降压模块接线12V电源 → LM2596 IN 12V电源 - → LM2596 IN- LM2596 OUT → Arduino 5V LM2596 OUT- → Arduino GND5.FSR402压力传感器接线Arduino 5V │ FSR402 │ Arduino A0 │ 10kΩ电阻 │ GND完整功能代码/* * 37GB545D BTS7960 AB编码器 FSR402 * 控制流程 * 上电回零 - FSR待机检测 - 闭合剪切 * - 保持 - 张开 - 等待压力释放 - 再次待机 * * Arduino Uno 引脚 * D2 - 编码器A * D3 - 编码器B * D5 - BTS7960 RPWM * D6 - BTS7960 LPWM * D7 - HOME限位开关可以通过宏关闭 * D8 - BTS7960 R_EN * D9 - BTS7960 L_EN * A0 - FSR402分压输出 */ // // 1. 功能选择 // // 使用HOME限位开关1 // 不使用HOME限位开关0 #define USE_HOME_SWITCH 1 // 编码器方向修正 // 正向运行时计数增加设为1 // 正向运行时计数减少改为-1 #define ENCODER_SIGN 1 // // 2. 引脚定义 // #define ENC_A_PIN 2 #define ENC_B_PIN 3 #define RPWM_PIN 5 #define LPWM_PIN 6 #define HOME_PIN 7 #define R_EN_PIN 8 #define L_EN_PIN 9 #define FSR_PIN A0 // // 3. 位置参数 // // 回零后张开位置为0 #define OPEN_POSITION 0L // 闭合位置必须根据你的机构重新标定 // 第一次测试先设置小一点例如100200 #define CLOSE_POSITION 200L // 位置允许误差 #define DEAD_ZONE 6L // // 4. 电机控制参数 // // 位置P控制增益 #define KP 0.8f // 电机克服减速箱静摩擦所需的最低PWM #define MIN_PWM 50 // 限制最大PWM避免一开始速度过快 #define MAX_PWM 160 // 回零速度 // 如果回零方向反了把-70改成70 #define HOME_PWM -70 // // 5. 时间参数 // #define HOME_TIMEOUT 6000UL #define CLOSE_TIMEOUT 4000UL #define OPEN_TIMEOUT 4000UL #define HOLD_TIME 300UL // FSR上电空载校准时间 #define FSR_CALIBRATION_TIME 2000UL // FSR采样周期 #define FSR_SAMPLE_INTERVAL 20UL // // 6. FSR参数 // // 触发阈值 空载基准 触发偏移 #define FSR_TRIGGER_OFFSET 100 // 释放阈值必须小于触发偏移形成滞回 #define FSR_RELEASE_OFFSET 40 // 连续多少次超过阈值才触发 #define TRIGGER_CONFIRM_COUNT 5 // 连续多少次低于释放阈值才认为果梗已离开 #define RELEASE_CONFIRM_COUNT 10 // // 7. 编码器变量 // volatile long encoderCount 0; volatile uint8_t previousEncoderState 0; /* * 正交编码器状态转换表。 * 前两位为上一次AB状态后两位为当前AB状态。 */ const int8_t QUADRATURE_TABLE[16] { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 }; // // 8. 状态机 // enum CutterState { STATE_HOMING, STATE_IDLE, STATE_CLOSING, STATE_HOLDING, STATE_OPENING, STATE_WAIT_RELEASE, STATE_ERROR }; CutterState currentState STATE_IDLE; unsigned long stateStartTime 0; unsigned long lastFSRSampleTime 0; unsigned long lastPrintTime 0; int basePressure 0; int currentPressure 0; uint8_t triggerConfirmCounter 0; uint8_t releaseConfirmCounter 0; bool manualCutRequest false; // // 9. 初始化 // void setup() { Serial.begin(115200); // BTS7960 pinMode(RPWM_PIN, OUTPUT); pinMode(LPWM_PIN, OUTPUT); pinMode(R_EN_PIN, OUTPUT); pinMode(L_EN_PIN, OUTPUT); setMotor(0); enableMotorDriver(true); // 编码器 pinMode(ENC_A_PIN, INPUT_PULLUP); pinMode(ENC_B_PIN, INPUT_PULLUP); previousEncoderState (digitalRead(ENC_A_PIN) 1) | digitalRead(ENC_B_PIN); attachInterrupt( digitalPinToInterrupt(ENC_A_PIN), encoderISR, CHANGE ); attachInterrupt( digitalPinToInterrupt(ENC_B_PIN), encoderISR, CHANGE ); // HOME开关 pinMode(HOME_PIN, INPUT_PULLUP); Serial.println(F()); Serial.println(F(37GB545D剪切控制系统启动)); Serial.println(F(请确保FSR上没有果梗或其他物体)); Serial.println(F()); calibrateFSR(); #if USE_HOME_SWITCH enterState(STATE_HOMING); Serial.println(F(开始自动回零)); #else /* * 不使用HOME开关时 * 上电前必须人工把刀片放到完全张开位置。 */ writeEncoderCount(0); enterState(STATE_IDLE); Serial.println(F(未启用HOME开关)); Serial.println(F(已将上电位置设为张开零点)); #endif Serial.println(F(串口命令)); Serial.println(F(C强制执行一次剪切)); Serial.println(F(S软件停止)); Serial.println(F(R故障复位)); Serial.println(F(Z将当前位置设为编码器零点)); } // // 10. 主循环 // void loop() { unsigned long now millis(); handleSerialCommand(); updatePressure(now); printStatus(now); switch (currentState) { // -------------------------------------------------- // 自动回零 // -------------------------------------------------- case STATE_HOMING: #if USE_HOME_SWITCH if (digitalRead(HOME_PIN) LOW) { setMotor(0); writeEncoderCount(0); Serial.println(F(回零完成编码器已清零)); enterState(STATE_IDLE); } else if (now - stateStartTime HOME_TIMEOUT) { enterError(F(故障回零超时)); } else { setMotor(HOME_PWM); } #else setMotor(0); writeEncoderCount(0); enterState(STATE_IDLE); #endif break; // -------------------------------------------------- // 等待FSR触发 // -------------------------------------------------- case STATE_IDLE: positionControl(OPEN_POSITION); if (manualCutRequest) { manualCutRequest false; triggerConfirmCounter 0; Serial.println(F(串口命令触发剪切)); enterState(STATE_CLOSING); } else if (pressureTriggerConfirmed()) { Serial.println(F(FSR检测到果梗开始剪切)); enterState(STATE_CLOSING); } break; // -------------------------------------------------- // 闭合剪切 // -------------------------------------------------- case STATE_CLOSING: if (positionControl(CLOSE_POSITION)) { setMotor(0); Serial.println(F(到达闭合位置)); enterState(STATE_HOLDING); } else if (now - stateStartTime CLOSE_TIMEOUT) { enterError(F(故障闭合动作超时)); } break; // -------------------------------------------------- // 剪切保持 // -------------------------------------------------- case STATE_HOLDING: positionControl(CLOSE_POSITION); if (now - stateStartTime HOLD_TIME) { Serial.println(F(保持完成开始张开)); enterState(STATE_OPENING); } break; // -------------------------------------------------- // 张开复位 // -------------------------------------------------- case STATE_OPENING: #if USE_HOME_SWITCH /* * 如果张开过程中先碰到HOME开关 * 立即停止并重新清零。 */ if (digitalRead(HOME_PIN) LOW) { setMotor(0); writeEncoderCount(0); Serial.println(F(HOME开关触发张开完成)); enterState(STATE_WAIT_RELEASE); break; } #endif if (positionControl(OPEN_POSITION)) { setMotor(0); Serial.println(F(张开位置到达)); enterState(STATE_WAIT_RELEASE); } else if (now - stateStartTime OPEN_TIMEOUT) { enterError(F(故障张开动作超时)); } break; // -------------------------------------------------- // 等待果梗或物体离开FSR // -------------------------------------------------- case STATE_WAIT_RELEASE: positionControl(OPEN_POSITION); if (pressureReleaseConfirmed()) { Serial.println(F(压力已释放重新进入待机)); enterState(STATE_IDLE); } break; // -------------------------------------------------- // 故障状态 // -------------------------------------------------- case STATE_ERROR: setMotor(0); break; } } // // 11. 电机驱动函数 // void enableMotorDriver(bool enable) { digitalWrite(R_EN_PIN, enable ? HIGH : LOW); digitalWrite(L_EN_PIN, enable ? HIGH : LOW); } /* * speed范围-255255 * 正数一个方向 * 负数相反方向 * 0停止 */ void setMotor(int speedValue) { speedValue constrain(speedValue, -255, 255); if (speedValue 0) { analogWrite(RPWM_PIN, speedValue); analogWrite(LPWM_PIN, 0); } else if (speedValue 0) { analogWrite(RPWM_PIN, 0); analogWrite(LPWM_PIN, -speedValue); } else { analogWrite(RPWM_PIN, 0); analogWrite(LPWM_PIN, 0); } } // // 12. 位置闭环控制 // /* * 返回true到达目标位置 * 返回false尚未到达 */ bool positionControl(long targetPosition) { long currentPosition readEncoderCount(); long error targetPosition - currentPosition; if (labs(error) DEAD_ZONE) { setMotor(0); return true; } int controlOutput (int)(KP * error); controlOutput constrain( controlOutput, -MAX_PWM, MAX_PWM ); /* * 小误差时P控制输出可能太小 * 电机无法克服减速箱摩擦因此设置最低PWM。 */ if (controlOutput 0 controlOutput MIN_PWM) { controlOutput MIN_PWM; } if (controlOutput 0 controlOutput -MIN_PWM) { controlOutput -MIN_PWM; } setMotor(controlOutput); return false; } // // 13. 编码器中断 // void encoderISR() { uint8_t currentEncoderState (digitalRead(ENC_A_PIN) 1) | digitalRead(ENC_B_PIN); uint8_t tableIndex (previousEncoderState 2) | currentEncoderState; encoderCount ENCODER_SIGN * QUADRATURE_TABLE[tableIndex]; previousEncoderState currentEncoderState; } /* * Arduino Uno是8位单片机 * 读取4字节long变量时需要临时关闭中断。 */ long readEncoderCount() { noInterrupts(); long value encoderCount; interrupts(); return value; } void writeEncoderCount(long value) { noInterrupts(); encoderCount value; interrupts(); } // // 14. FSR空载校准 // void calibrateFSR() { unsigned long calibrationStart millis(); long pressureSum 0; unsigned int sampleCount 0; while (millis() - calibrationStart FSR_CALIBRATION_TIME) { pressureSum analogRead(FSR_PIN); sampleCount; delay(10); } if (sampleCount 0) { basePressure pressureSum / sampleCount; } currentPressure basePressure; Serial.print(F(FSR空载基准值)); Serial.println(basePressure); Serial.print(F(FSR触发阈值)); Serial.println( basePressure FSR_TRIGGER_OFFSET ); Serial.print(F(FSR释放阈值)); Serial.println( basePressure FSR_RELEASE_OFFSET ); } // // 15. FSR读取和触发判断 // void updatePressure(unsigned long now) { if (now - lastFSRSampleTime FSR_SAMPLE_INTERVAL) { return; } lastFSRSampleTime now; currentPressure analogRead(FSR_PIN); } bool pressureTriggerConfirmed() { int triggerThreshold basePressure FSR_TRIGGER_OFFSET; if (currentPressure triggerThreshold) { if (triggerConfirmCounter TRIGGER_CONFIRM_COUNT) { triggerConfirmCounter; } } else { triggerConfirmCounter 0; } if (triggerConfirmCounter TRIGGER_CONFIRM_COUNT) { triggerConfirmCounter 0; return true; } return false; } bool pressureReleaseConfirmed() { int releaseThreshold basePressure FSR_RELEASE_OFFSET; if (currentPressure releaseThreshold) { if (releaseConfirmCounter RELEASE_CONFIRM_COUNT) { releaseConfirmCounter; } } else { releaseConfirmCounter 0; } if (releaseConfirmCounter RELEASE_CONFIRM_COUNT) { releaseConfirmCounter 0; return true; } return false; } // // 16. 状态切换和故障处理 // void enterState(CutterState newState) { currentState newState; stateStartTime millis(); triggerConfirmCounter 0; releaseConfirmCounter 0; } void enterError(const __FlashStringHelper *message) { setMotor(0); enableMotorDriver(false); currentState STATE_ERROR; Serial.println(message); Serial.println(F(发送R命令进行复位)); } // // 17. 串口调试命令 // void handleSerialCommand() { while (Serial.available() 0) { char command Serial.read(); if (command a command z) { command command - a A; } switch (command) { case C: if (currentState STATE_IDLE) { manualCutRequest true; } break; case S: enterError(F(软件停止命令触发)); break; case R: setMotor(0); enableMotorDriver(true); #if USE_HOME_SWITCH Serial.println(F(故障复位重新回零)); enterState(STATE_HOMING); #else Serial.println(F(故障复位当前位置设为零)); writeEncoderCount(0); enterState(STATE_IDLE); #endif break; case Z: if (currentState STATE_IDLE || currentState STATE_ERROR) { setMotor(0); writeEncoderCount(0); Serial.println(F(当前位置已设为编码器零点)); } break; } } } // // 18. 串口状态打印 // void printStatus(unsigned long now) { if (now - lastPrintTime 250) { return; } lastPrintTime now; Serial.print(F(状态)); Serial.print((int)currentState); Serial.print(F( 编码器)); Serial.print(readEncoderCount()); Serial.print(F( FSR)); Serial.println(currentPressure); }调试校准步骤第一步基础硬件验证不通单片机先测电源给 12V 上电测 LM2596 输出是否为 5V确认无误后再接 Arduino。电机空载测试注释掉回零逻辑直接用setMotor(100)和setMotor(-100)测试正反转确认电机转向、接线无错误。编码器验证打开串口监视器手动转动电机轴观察脉冲数是否连续增减无跳数、无反向。第二步机械参数校准脉冲数标定让电机转一整圈记录encoderCount变化值替换PULSES_PER_REV。行程标定手动让剪刀完全张开记录脉冲数 → 设为OPEN_POSITION手动让剪刀完全闭合刀刃交错到位记录脉冲数 → 设为CLOSE_POSITION压力阈值校准参考你之前的校准方法修改FSR_THRESHOLD。第三步闭环与回零调试先测试回零功能确认触发限位后电机立即停止计数清零。再测试位置闭环发送指令让电机走到指定位置观察是否平稳到位、无振荡。最后联调 FSR 触发验证完整剪切流程。