Загрузка данных


/*
 * Захват-манипулятор на 4 сервоприводах SG90
 * Платформа: Arduino Nano
 * Управление: Serial 9600
 *
 * Клешни (G1, G2)  — зеркально (открытие/закрытие).
 * Подъём  (L1, L2) — зеркально, т.к. стоят друг напротив друга.
 *
 * Угол наклона задаётся как delta от 90°:
 *   L1 = 90 - delta   (серво 1)
 *   L2 = 90 + delta   (серво 2)
 *   delta < 0 — вниз,  delta > 0 — вверх
 *
 * Питание серво — ОТДЕЛЬНОЕ 5В ≥ 3А! GND общий с Arduino.
 */

#include <Servo.h>

// ---------- ГРАНИЦЫ УГЛОВ КЛЕШНЕЙ ----------
const int G1_OPEN   = 20;
const int G1_CLOSED = 90;
const int G2_OPEN   = 160;
const int G2_CLOSED = 90;

// ---------- ГРАНИЦЫ НАКЛОНА ПОДЪЁМА ----------
// Задаём смещение относительно центра 90°, симметрично
const int LIFT_DELTA_DOWN = -60;   // насколько опущено (delta)
const int LIFT_DELTA_UP   =  60;   // насколько поднято  (delta)

// ---------- ПИНЫ ----------
const int G1_PIN = 9;
const int G2_PIN = 10;
const int L1_PIN = 11;
const int L2_PIN = 12;

// ---------- СКОРОСТЬ ----------
const int STEP_DELAY_MS = 15;

Servo servoG1, servoG2, servoL1, servoL2;

int curG1, curG2, curL1, curL2;
bool gripperOpen = true;
bool liftIsUp    = false;

// ========== Преобразование delta -> углы L1/L2 ==========
// delta: -70..+70 (0 = середина, + = вверх, − = вниз)
int lift1Angle(int delta) { return 90 - delta; }
int lift2Angle(int delta) { return 90 + delta; }

// ========== Плавное движение всех 4 серво ==========
// tL1delta — задаётся как delta для подъёма
void moveAllDelta(int tG1, int tG2, int liftDelta) {
  liftDelta = constrain(liftDelta,
                        min(LIFT_DELTA_DOWN, LIFT_DELTA_UP),
                        max(LIFT_DELTA_DOWN, LIFT_DELTA_UP));

  int tL1 = lift1Angle(liftDelta);
  int tL2 = lift2Angle(liftDelta);

  // Защита клешней
  tG1 = constrain(tG1, min(G1_OPEN, G1_CLOSED), max(G1_OPEN, G1_CLOSED));
  tG2 = constrain(tG2, min(G2_OPEN, G2_CLOSED), max(G2_OPEN, G2_CLOSED));

  while (curG1 != tG1 || curG2 != tG2 || curL1 != tL1 || curL2 != tL2) {
    if (curG1 < tG1) curG1++; else if (curG1 > tG1) curG1--;
    if (curG2 < tG2) curG2++; else if (curG2 > tG2) curG2--;
    if (curL1 < tL1) curL1++; else if (curL1 > tL1) curL1--;
    if (curL2 < tL2) curL2++; else if (curL2 > tL2) curL2--;

    servoG1.write(curG1);
    servoG2.write(curG2);
    servoL1.write(curL1);
    servoL2.write(curL2);
    delay(STEP_DELAY_MS);
  }
}

// ========== Простые действия ==========
void openGripper() {
  moveAllDelta(G1_OPEN, G2_OPEN, liftCurrentDelta());
  gripperOpen = true;
  Serial.println(F("-> GRIP: OPEN"));
}

void closeGripper() {
  moveAllDelta(G1_CLOSED, G2_CLOSED, liftCurrentDelta());
  gripperOpen = false;
  Serial.println(F("-> GRIP: CLOSE"));
}

// Текущий delta подъёма (по среднему положению L1 и L2)
int liftCurrentDelta() {
  // L1 = 90 - d, значит d = 90 - L1
  return 90 - curL1;
}

void liftArm() {
  moveAllDelta(curG1, curG2, LIFT_DELTA_UP);
  liftIsUp = true;
  Serial.println(F("-> LIFT: UP"));
}

void lowerArm() {
  moveAllDelta(curG1, curG2, LIFT_DELTA_DOWN);
  liftIsUp = false;
  Serial.println(F("-> LIFT: DOWN"));
}

// ========== Составные действия ==========
void pickUp() {
  Serial.println(F(">> PICK: open + lower"));
  moveAllDelta(G1_OPEN, G2_OPEN, LIFT_DELTA_DOWN);
  gripperOpen = true; liftIsUp = false;
  delay(300);

  Serial.println(F(">> PICK: close"));
  moveAllDelta(G1_CLOSED, G2_CLOSED, LIFT_DELTA_DOWN);
  gripperOpen = false;
  delay(300);

  Serial.println(F(">> PICK: lift up"));
  moveAllDelta(G1_CLOSED, G2_CLOSED, LIFT_DELTA_UP);
  liftIsUp = true;
  Serial.println(F("-> PICK done"));
}

void release() {
  Serial.println(F(">> RELEASE: lower"));
  moveAllDelta(curG1, curG2, LIFT_DELTA_DOWN);
  liftIsUp = false;
  delay(300);

  Serial.println(F(">> RELEASE: open"));
  moveAllDelta(G1_OPEN, G2_OPEN, LIFT_DELTA_DOWN);
  gripperOpen = true;
  delay(300);

  Serial.println(F(">> RELEASE: lift up"));
  moveAllDelta(G1_OPEN, G2_OPEN, LIFT_DELTA_UP);
  liftIsUp = true;
  Serial.println(F("-> RELEASE done"));
}

// ========== Ручные команды ==========
void setGrip(int delta) {
  delta = constrain(delta, -70, 70);
  int t1 = 90 - delta;
  int t2 = 90 + delta;
  moveAllDelta(t1, t2, liftCurrentDelta());
  gripperOpen = (delta > 0);
  Serial.print(F("-> GRIP POS "));
  Serial.print(t1); Serial.print(F(" / ")); Serial.println(t2);
}

// L<delta> : delta наклона (-70..+70)
void setLift(int delta) {
  delta = constrain(delta, -70, 70);
  moveAllDelta(curG1, curG2, delta);
  liftIsUp = (delta > 0);
  Serial.print(F("-> LIFT DELTA "));
  Serial.print(delta);
  Serial.print(F("  (L1=")); Serial.print(90 - delta);
  Serial.print(F(" L2=")); Serial.print(90 + delta);
  Serial.println(F(")"));
}

// ========== Инфо ==========
void printStatus() {
  Serial.print(F("STATUS: grip="));
  Serial.print(gripperOpen ? F("OPEN") : F("CLOSED"));
  Serial.print(F("  lift="));
  Serial.print(liftIsUp ? F("UP") : F("DOWN"));
  Serial.print(F("  [G1=")); Serial.print(curG1);
  Serial.print(F(" G2="));   Serial.print(curG2);
  Serial.print(F(" L1="));   Serial.print(curL1);
  Serial.print(F(" L2="));   Serial.print(curL2);
  Serial.println(F("]"));
}

void printHelp() {
  Serial.println(F("===== GRIPPER ARM 4x SG90 ====="));
  Serial.println(F(" O / OPEN    - open gripper"));
  Serial.println(F(" C / CLOSE   - close gripper"));
  Serial.println(F(" U / UP      - lift up"));
  Serial.println(F(" D / DOWN    - lower"));
  Serial.println(F(" P / PICK    - take object"));
  Serial.println(F(" R / RELEASE - put object down"));
  Serial.println(F(" G<deg>      - grip pos (offset), e.g. G45"));
  Serial.println(F(" L<delta>    - lift delta (-70..70), e.g. L40"));
  Serial.println(F(" S / STATUS  - show state"));
  Serial.println(F(" H / HELP    - this help"));
  Serial.println(F("==============================="));
}

// ========== SETUP ==========
void setup() {
  Serial.begin(9600);

  servoG1.attach(G1_PIN);
  servoG2.attach(G2_PIN);
  servoL1.attach(L1_PIN);
  servoL2.attach(L2_PIN);

  // Стартовое: клешни раскрыты, рука опущена
  curG1 = G1_OPEN;   curG2 = G2_OPEN;
  int startDelta = LIFT_DELTA_DOWN;
  curL1 = lift1Angle(startDelta);
  curL2 = lift2Angle(startDelta);

  servoG1.write(curG1);
  servoG2.write(curG2);
  servoL1.write(curL1);
  servoL2.write(curL2);

  delay(500);
  printHelp();
  printStatus();
}

// ========== Обработка команд ==========
void handleCommand(String cmd) {
  cmd.trim();
  if (cmd.length() == 0) return;

  char c = toupper(cmd.charAt(0));

  switch (c) {
    case 'O': openGripper(); break;
    case 'C': closeGripper(); break;
    case 'U': liftArm(); break;
    case 'D': lowerArm(); break;
    case 'P': pickUp(); break;
    case 'R': release(); break;
    case 'S': printStatus(); break;
    case 'H': printHelp(); break;

    case 'G': {
      String num = cmd.substring(1); num.trim();
      if (num.length() == 0) { Serial.println(F("Err: use G<deg>")); return; }
      setGrip(num.toInt());
      break;
    }

    case 'L': {
      String num = cmd.substring(1); num.trim();
      if (num.length() == 0) { Serial.println(F("Err: use L<delta>")); return; }
      setLift(num.toInt());
      break;
    }

    default:
      Serial.print(F("Unknown cmd: ")); Serial.println(cmd);
      Serial.println(F("Type H for help"));
  }
}

// ========== LOOP ==========
void loop() {
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    handleCommand(cmd);
  }
}