

비상 정지 후 급발진 방지 코드 개선
@6c3a4b2d294963da99b0cbf467af39d4e4f37d3d
+++ vdwd3.0.ino
... | ... | @@ -0,0 +1,194 @@ |
1 | +// Ch1 : Left | |
2 | +#define DIR_1 8 | |
3 | +#define ST_1 9 | |
4 | +#define PWM_1 45 | |
5 | +#define ENCA_1 2 | |
6 | +#define ENCB_1 3 | |
7 | + | |
8 | +// Ch2 : Right | |
9 | +#define DIR_2 22 | |
10 | +#define ST_2 24 | |
11 | +#define PWM_2 44 | |
12 | +#define ENCA_2 18 | |
13 | +#define ENCB_2 19 | |
14 | + | |
15 | +int setRPM1 = 0; | |
16 | +int setRPM2 = 0; | |
17 | + | |
18 | +double kp1 = 0.5, ki1 = 3.0, kd1 = 0.085; | |
19 | +double kp2 = 0.5, ki2 = 3.0, kd2 = 0.085; | |
20 | +double kb1 = 3, kb2 = 3; | |
21 | +double alpha = 0.3; | |
22 | + | |
23 | +volatile long encoderPos1 = 0, encoderPos2 = 0; | |
24 | +double v = 0, w = 0; | |
25 | +double rpm1 = 0, rpm2 = 0; | |
26 | +int calPWM1 = 0, calPWM2 = 0; | |
27 | +double error1 = 0, prevError1 = 0, integral1 = 0, derivative1 = 0; | |
28 | +double error2 = 0, prevError2 = 0, integral2 = 0, derivative2 = 0; | |
29 | +double prevRPM1 = 0, prevRPM2 = 0; | |
30 | +double backError1 = 0, backError2 = 0; | |
31 | + | |
32 | +const unsigned long interval = 30; | |
33 | +unsigned long prevTime = 0; | |
34 | +double dt = interval / 1000.0; | |
35 | + | |
36 | +const double WHEEL_RADIUS = 0.065; // 65mm | |
37 | +const double WHEEL_DISTANCE = 0.437; // 437mm | |
38 | + | |
39 | +void doEncoderA1() { if (digitalRead(ENCB_1) == LOW) encoderPos1--; else encoderPos1++; } | |
40 | +void doEncoderB1() { if (digitalRead(ENCA_1) == HIGH) encoderPos1--; else encoderPos1++; } | |
41 | +void doEncoderA2() { if (digitalRead(ENCB_2) == LOW) encoderPos2++; else encoderPos2--; } | |
42 | +void doEncoderB2() { if (digitalRead(ENCA_2) == HIGH) encoderPos2++; else encoderPos2--; } | |
43 | + | |
44 | +double calculateRPM(long counts) { | |
45 | + return ((double)counts / (4096.0 * 2)) * (60.0 / dt); | |
46 | +} | |
47 | + | |
48 | +int computePID(double currentRPM, double &error, double &prevError, | |
49 | + double &integral, double &derivative, int targetRPM, | |
50 | + double kp, double ki, double kd, bool isMotor1) { | |
51 | + double absTarget = abs(targetRPM); | |
52 | + double absCurrent = abs(currentRPM); | |
53 | + error = absTarget - absCurrent; | |
54 | + integral += error * dt; | |
55 | + | |
56 | + double rawDerivative = -(absCurrent - (isMotor1 ? prevRPM1 : prevRPM2)) / dt; | |
57 | + double filteredDeriv = alpha * rawDerivative + (1 - alpha) * (isMotor1 ? derivative1 : derivative2); | |
58 | + if (isMotor1) prevRPM1 = absCurrent; else prevRPM2 = absCurrent; | |
59 | + | |
60 | + double P = kp * error; | |
61 | + double I = ki * integral; | |
62 | + double D = kd * filteredDeriv; | |
63 | + double output = P + I + D; | |
64 | + double outputSat = constrain(output, 0, 255); | |
65 | + double backError = outputSat - output; | |
66 | + integral += (isMotor1 ? kb1 : kb2) * backError * dt; | |
67 | + | |
68 | + Serial.print(abs(targetRPM)); Serial.print('\t'); | |
69 | + Serial.print(currentRPM, 2); Serial.print('\t'); | |
70 | + if (isMotor1) { | |
71 | + Serial.print("P1 : "); Serial.print(P, 2); Serial.print('\t'); | |
72 | + Serial.print("I1 : "); Serial.print(I, 2); Serial.print('\t'); | |
73 | + Serial.print("D1 : "); Serial.print(D, 2); Serial.println('\t'); | |
74 | + } else { | |
75 | + Serial.print("P2 : "); Serial.print(P, 2); Serial.print('\t'); | |
76 | + Serial.print("I2 : "); Serial.print(I, 2); Serial.print('\t'); | |
77 | + Serial.print("D2 : "); Serial.print(D, 2); Serial.println('\t'); | |
78 | + Serial.println("---------------------------------------------------------"); | |
79 | + } | |
80 | + | |
81 | + return (int)(P + I + D); | |
82 | +} | |
83 | + | |
84 | +void serialSetTwist() { | |
85 | + if (Serial.available()) { | |
86 | + String input = Serial.readStringUntil('\n'); | |
87 | + input.trim(); | |
88 | + | |
89 | + int vIndex = input.indexOf('V'); | |
90 | + int wIndex = input.indexOf('W'); | |
91 | + | |
92 | + bool vUpdated = false; | |
93 | + bool wUpdated = false; | |
94 | + | |
95 | + if (vIndex != -1) { | |
96 | + int end = (wIndex != -1 && wIndex > vIndex) ? wIndex : input.length(); | |
97 | + String vStr = input.substring(vIndex + 1, end); | |
98 | + vStr.trim(); | |
99 | + double parsedV = vStr.toFloat(); | |
100 | + if (String(parsedV) == vStr || vStr.length() > 0) { | |
101 | + v = parsedV; | |
102 | + vUpdated = true; | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + if (wIndex != -1) { | |
107 | + int end = (vIndex != -1 && vIndex > wIndex) ? vIndex : input.length(); | |
108 | + String wStr = input.substring(wIndex + 1, end); | |
109 | + wStr.trim(); | |
110 | + double parsedW = wStr.toFloat(); | |
111 | + if (String(parsedW) == wStr || wStr.length() > 0) { | |
112 | + w = parsedW; | |
113 | + wUpdated = true; | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + if (vUpdated || wUpdated) { | |
118 | + double pkjR = 1; | |
119 | + double pkjL = 1; | |
120 | + double rpm_r = (v + (WHEEL_DISTANCE / 2.0) * w) / WHEEL_RADIUS * 60.0 / (2 * PI); | |
121 | + double rpm_l = (v - (WHEEL_DISTANCE / 2.0) * w) / WHEEL_RADIUS * 60.0 / (2 * PI); | |
122 | + setRPM1 = (int)(rpm_r * pkjR); | |
123 | + setRPM2 = (int)(rpm_l * pkjL); | |
124 | + | |
125 | +// Serial.print("Target Updated → "); | |
126 | +// Serial.print("V: "); Serial.print(v, 3); | |
127 | +// Serial.print(" W: "); Serial.print(w, 3); | |
128 | +// Serial.print(" R: "); Serial.print(setRPM1); | |
129 | +// Serial.print(" L: "); Serial.println(setRPM2); | |
130 | +// } else { | |
131 | +// Serial.println("Error : Enter in V0.3 or W-0.2 format."); | |
132 | + } | |
133 | + } | |
134 | +} | |
135 | + | |
136 | +void setup() { | |
137 | + Serial.begin(115200); | |
138 | + pinMode(DIR_1, OUTPUT); pinMode(PWM_1, OUTPUT); pinMode(ST_1, OUTPUT); | |
139 | + pinMode(ENCA_1, INPUT_PULLUP); pinMode(ENCB_1, INPUT_PULLUP); | |
140 | + attachInterrupt(digitalPinToInterrupt(ENCA_1), doEncoderA1, RISING); | |
141 | + attachInterrupt(digitalPinToInterrupt(ENCB_1), doEncoderB1, RISING); | |
142 | + | |
143 | + pinMode(DIR_2, OUTPUT); pinMode(PWM_2, OUTPUT); pinMode(ST_2, OUTPUT); | |
144 | + pinMode(ENCA_2, INPUT_PULLUP); pinMode(ENCB_2, INPUT_PULLUP); | |
145 | + attachInterrupt(digitalPinToInterrupt(ENCA_2), doEncoderA2, RISING); | |
146 | + attachInterrupt(digitalPinToInterrupt(ENCB_2), doEncoderB2, RISING); | |
147 | + | |
148 | + digitalWrite(ST_1, LOW); digitalWrite(ST_2, LOW); | |
149 | + prevTime = millis(); | |
150 | +} | |
151 | + | |
152 | +void loop() { | |
153 | + unsigned long now = millis(); | |
154 | + if (now - prevTime >= interval) { | |
155 | + prevTime += interval; | |
156 | + | |
157 | + noInterrupts(); long cnt1 = encoderPos1; encoderPos1 = 0; | |
158 | + long cnt2 = encoderPos2; encoderPos2 = 0; interrupts(); | |
159 | + rpm1 = calculateRPM(cnt1); | |
160 | + rpm2 = calculateRPM(cnt2); | |
161 | + | |
162 | + static int zeroRPMCount = 0; | |
163 | + const int maxZeroRPMCount = 10; // 10*30ms = 300ms | |
164 | + | |
165 | + if (rpm1 == 0 && rpm2 == 0 && (setRPM1 != 0 || setRPM2 != 0)) { | |
166 | + zeroRPMCount++; | |
167 | + } else if (rpm1 == 0 && rpm2 == 0 && setRPM1 == 0 && setRPM2 == 0) { | |
168 | + if (integral1 != 0 || integral2 != 0) { | |
169 | + integral1 = 0; | |
170 | + integral2 = 0; | |
171 | + } | |
172 | + } else { | |
173 | + zeroRPMCount = 0; | |
174 | + } | |
175 | + | |
176 | + if (zeroRPMCount > maxZeroRPMCount) { | |
177 | + setRPM1 = 0; | |
178 | + setRPM2 = 0; | |
179 | + integral1 = 0; | |
180 | + integral2 = 0; | |
181 | + calPWM1 = 0; | |
182 | + calPWM2 = 0; | |
183 | + } | |
184 | + | |
185 | + calPWM1 = computePID(rpm1, error1, prevError1, integral1, derivative1, setRPM1, kp1, ki1, kd1, true); | |
186 | + calPWM2 = computePID(rpm2, error2, prevError2, integral2, derivative2, setRPM2, kp2, ki2, kd2, false); | |
187 | + | |
188 | + digitalWrite(DIR_1, setRPM1 >= 0 ? LOW : HIGH); | |
189 | + digitalWrite(DIR_2, setRPM2 >= 0 ? LOW : HIGH); | |
190 | + analogWrite(PWM_1, constrain(abs(calPWM1), 0, 255)); | |
191 | + analogWrite(PWM_2, constrain(abs(calPWM2), 0, 255)); | |
192 | + } | |
193 | + serialSetTwist(); | |
194 | +}(파일 끝에 줄바꿈 문자 없음) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?