gyeongsangseaman 07-30
과적분 보정 함수 추가
@9246661c8da274f562f73f6f6c14ace56623a225
 
Back_calculate_0_280.ino (added)
+++ Back_calculate_0_280.ino
@@ -0,0 +1,223 @@
+// === 1번 모터 핀 정의 ===
+#define DIR_1 8
+#define ST_1 9
+#define PWM_1 45
+#define ENCA_1 2
+#define ENCB_1 3
+
+// === 2번 모터 핀 정의 ===
+#define DIR_2 22
+#define ST_2 24
+#define PWM_2 44
+#define ENCA_2 18
+#define ENCB_2 19
+
+// === 목표 RPM (부호 포함) ===
+int setRPM1 = 0;
+int setRPM2 = 0;
+
+// === PID 계수 ===
+double kp1 = 0.5, ki1 = 3, kd1 = 0.01;
+double kp2 = 0.0, ki2 = 0.0, kd2 = 0.0;
+double kb1 = 2; // ← Back Calculation 계수
+
+// === 상태 변수 ===
+// 1번 모터
+volatile long encoderPos1 = 0;
+double rpm1 = 0;
+int calPWM1 = 0;
+double error1 = 0, prevError1 = 0, integral1 = 0, derivative1 = 0;
+double prevRPM1 = 0;
+double alpha = 0.2;
+double backError1 = 0;
+
+// 2번 모터
+volatile long encoderPos2 = 0;
+double rpm2 = 0;
+int calPWM2 = 0;
+double error2 = 0, prevError2 = 0, integral2 = 0, derivative2 = 0;
+
+// 제어 주기
+const unsigned long interval = 30;
+unsigned long prevTime = 0;
+double dt = interval / 1000.0;
+
+// === 인터럽트 핸들러 ===
+void doEncoderA1() {
+  if (digitalRead(ENCB_1) == LOW) encoderPos1++;
+  else encoderPos1--;
+}
+void doEncoderB1() {
+  if (digitalRead(ENCA_1) == HIGH) encoderPos1++;
+  else encoderPos1--;
+}
+void doEncoderA2() {
+  if (digitalRead(ENCB_2) == LOW) encoderPos2--;
+  else encoderPos2++;
+}
+void doEncoderB2() {
+  if (digitalRead(ENCA_2) == HIGH) encoderPos2--;
+  else encoderPos2++;
+}
+
+// === RPM 계산 함수 ===
+double calculateRPM(long counts) {
+  return ((double)counts / (4096.0 * 2)) * (60.0 / dt);
+}
+
+// === PID 계산 함수 ===
+int computePID(double currentRPM, double &error, double &prevError,
+               double &integral, double &derivative, int targetRPM,
+               double kp, double ki, double kd, bool isMotor1) {
+  error = targetRPM - currentRPM;
+  integral += error * dt;
+
+  if (isMotor1) {
+    double rawDerivative = -(currentRPM - prevRPM1) / dt;
+    derivative = alpha * rawDerivative + (1 - alpha) * derivative1;
+    prevRPM1 = currentRPM;
+
+    double P = kp * error;
+    double I = ki * integral;
+    double D = kd * derivative;
+    double output = P + I + D;
+
+    // Back Calculation
+    double outputSat = constrain(output, 0, 255);
+    backError1 = outputSat - output;
+    integral += kb1 * backError1 * dt;
+
+    Serial.print(setRPM1); Serial.print('\t');
+    Serial.print(currentRPM, 2); Serial.print('\t');
+    Serial.print("P : "); Serial.print(P, 2); Serial.print('\t');
+    Serial.print("I : "); Serial.print(I, 2); Serial.print('\t');
+    Serial.print("D : "); Serial.print(D, 2); Serial.println('\t');
+
+    return (int)output;
+  } else {
+    derivative = (error - prevError) / dt;
+    prevError = error;
+    return (int)((kp * error) + (ki * integral) + (kd * derivative));
+  }
+}
+
+// === 실시간 입력 ===
+void serialSetRPM() {
+  if (Serial.available()) {
+    String input = Serial.readStringUntil('\n');
+    input.trim();
+
+    int rIndex = input.indexOf('R');
+    int lIndex = input.indexOf('L');
+
+    if (rIndex == -1 && lIndex == -1) {
+      Serial.println("입력 오류: 'R' 또는 'L'이 포함되어야 합니다. 예: R100 L-80");
+      return;
+    }
+
+    bool validInput = false;
+
+    if (rIndex != -1) {
+      int end = (lIndex != -1 && lIndex > rIndex) ? lIndex : input.length();
+      String rValStr = input.substring(rIndex + 1, end);
+      rValStr.trim();
+
+      if (rValStr.length() == 0) {
+        Serial.println("오류: R 뒤에 숫자가 없습니다. 예: R100");
+      } else {
+        int rpmVal = rValStr.toInt();
+        if (String(rpmVal) != rValStr || abs(rpmVal) > 400) {
+          Serial.println("오류: R 값 형식 또는 범위 초과");
+        } else {
+          setRPM1 = rpmVal;
+          Serial.print("모터1 (R) 목표 RPM → "); Serial.println(setRPM1);
+          validInput = true;
+        }
+      }
+    }
+
+    if (lIndex != -1) {
+      int end = (rIndex != -1 && rIndex > lIndex) ? rIndex : input.length();
+      String lValStr = input.substring(lIndex + 1, end);
+      lValStr.trim();
+
+      if (lValStr.length() == 0) {
+        Serial.println("오류: L 뒤에 숫자가 없습니다. 예: L-80");
+      } else {
+        int rpmVal = lValStr.toInt();
+        if (String(rpmVal) != lValStr || abs(rpmVal) > 400) {
+          Serial.println("오류: L 값 형식 또는 범위 초과");
+        } else {
+          setRPM2 = rpmVal;
+          Serial.print("모터2 (L) 목표 RPM → "); Serial.println(setRPM2);
+          validInput = true;
+        }
+      }
+    }
+
+    if (!validInput) {
+      Serial.println("R숫자 L숫자 형식으로 입력해주세요. 예: R100 L-80");
+    }
+  }
+}
+
+void setup() {
+  Serial.begin(19200);
+
+  pinMode(DIR_1, OUTPUT); pinMode(PWM_1, OUTPUT); pinMode(ST_1, OUTPUT);
+  pinMode(ENCA_1, INPUT_PULLUP); pinMode(ENCB_1, INPUT_PULLUP);
+  attachInterrupt(digitalPinToInterrupt(ENCA_1), doEncoderA1, RISING);
+  attachInterrupt(digitalPinToInterrupt(ENCB_1), doEncoderB1, RISING);
+
+  pinMode(DIR_2, OUTPUT); pinMode(PWM_2, OUTPUT); pinMode(ST_2, OUTPUT);
+  pinMode(ENCA_2, INPUT_PULLUP); pinMode(ENCB_2, INPUT_PULLUP);
+  attachInterrupt(digitalPinToInterrupt(ENCA_2), doEncoderA2, RISING);
+  attachInterrupt(digitalPinToInterrupt(ENCB_2), doEncoderB2, RISING);
+
+  digitalWrite(ST_1, LOW);
+  digitalWrite(ST_2, LOW);
+  prevTime = millis();
+}
+
+void loop() {
+  unsigned long now = millis();
+
+  if (now - prevTime >= interval) {
+    prevTime += interval;
+
+    // 모터 1
+    noInterrupts(); long currentPos1 = encoderPos1; encoderPos1 = 0; interrupts();
+    rpm1 = calculateRPM(currentPos1);
+    calPWM1 = computePID(rpm1, error1, prevError1, integral1, derivative1, abs(setRPM1), kp1, ki1, kd1, true);
+    digitalWrite(DIR_1, setRPM1 >= 0 ? HIGH : LOW);
+    calPWM1 = constrain(abs(calPWM1), 0, 255);
+    analogWrite(PWM_1, calPWM1);
+    
+    // 모터 2
+    noInterrupts(); long currentPos2 = encoderPos2; encoderPos2 = 0; interrupts();
+    rpm2 = calculateRPM(currentPos2);
+    calPWM2 = computePID(rpm2, error2, prevError2, integral2, derivative2, abs(setRPM2), kp2, ki2, kd2, false);
+    digitalWrite(DIR_2, setRPM2 >= 0 ? HIGH : LOW);
+    calPWM2 = constrain(abs(calPWM2), 0, 255);
+    analogWrite(PWM_2, calPWM2);
+
+    // 시리얼 출력
+    // Serial.print("R:"); Serial.println(setRPM1);
+    // Serial.print("RPM:"); Serial.println(rpm1, 2);
+    // Serial.print("PWM:"); Serial.println(calPWM1);
+    // Serial.println(" ");
+    // Serial.print("L:"); Serial.println(setRPM2);
+    // Serial.print("RPM:"); Serial.println(rpm2, 2);
+    // Serial.print("PWM:"); Serial.println(calPWM2);
+    // Serial.println("--------------------");
+
+    // Serial Plotter용 출력
+    Serial.print(setRPM1); Serial.print('\t');
+    Serial.println(rpm1, 2);
+    // Serial.print(setRPM2); Serial.print('\t');
+    // Serial.println(rpm2, 2);
+  }
+  
+
+  serialSetRPM();
+}
Add a comment
List