کنترل PID چیست و چرا اینقدر مهم است؟
PID مخفف Proportional-Integral-Derivative است — یک الگوریتم ریاضی که در همه چیز از موشک تا ماشین لباسشویی استفاده میشود. وقتی میخواهی یک ربات روی دو چرخ تعادل داشته باشد، هر لحظه باید بدانی «چقدر از حالت عمودی منحرف شدهام؟» و «چقدر باید موتور را بچرخانم تا جبران کنم؟» PID این محاسبه را در هر ۱۰ms انجام میدهد.
MPU-6050 یک تراشه شتابسنج (Accelerometer) و ژیروسکوپ (Gyroscope) سهمحوره است که از طریق I2C به آردوینو وصل میشود. شتابسنج زاویه استاتیک را میدهد اما پر از نویز است. ژیروسکوپ تغییر زاویه را خیلی دقیق میدهد اما drift دارد. فیلتر مکمل (Complementary Filter) این دو را با هم ترکیب میکند تا زاویه دقیق و پایدار بدست بیاید.
🛠️ کارت پروژه
| چه یاد میگیریم؟ | PID پایه، خواندن MPU-6050، کد آردوینو قابل استفاده در همه پروژهها |
| قطعات | Arduino Uno/Nano + MPU-6050 (~۱۵۰K تومان) |
| پیشنیاز | آشنایی پایه با آردوینو و C++ |
BOM
| قطعه | مشخصات | قیمت |
|---|---|---|
| Arduino Nano | یا Uno | ~۱۰۰K |
| MPU-6050 ماژول | GY-521 | ~۴۰K |
| سیم جامپر | F-M | ~۱۰K |
اتصال MPU-6050 به آردوینو
| MPU-6050 | Arduino Uno/Nano |
|---|---|
| VCC | 3.3V |
| GND | GND |
| SDA | A4 |
| SCL | A5 |
کد کامل: خواندن زاویه با فیلتر مکمل
#include <Wire.h>
#include <MPU6050.h> // کتابخانه: Jeff Rowberg's MPU6050
MPU6050 mpu;
// ===== PID پارامترها =====
float Kp = 30.0; // تناسبی — واکنش فوری
float Ki = 0.5; // انتگرالی — خطای ماندگار
float Kd = 1.2; // مشتقی — کاهش نوسان
float setpoint = 0.0; // زاویه هدف (عمود = صفر درجه)
float input, output;
float errSum = 0, lastErr = 0;
unsigned long lastTime;
// ===== فیلتر مکمل =====
float angle = 0.0;
const float alpha = 0.98; // ۹۸٪ ژیرو + ۲٪ شتابسنج
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
if (!mpu.testConnection()) Serial.println("MPU-6050 خطا!");
lastTime = millis();
}
void loop() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// زاویه از شتابسنج (درجه)
float accelAngle = atan2(ay, az) * 180.0 / PI;
// نرخ چرخش از ژیرو (درجه/ثانیه)
float gyroRate = gx / 131.0;
// فیلتر مکمل
unsigned long now = millis();
float dt = (now - lastTime) / 1000.0;
lastTime = now;
angle = alpha * (angle + gyroRate * dt) + (1 - alpha) * accelAngle;
// ===== محاسبه PID =====
float err = setpoint - angle;
errSum += err * dt;
errSum = constrain(errSum, -50, 50); // Anti-windup
float dErr = (err - lastErr) / dt;
output = Kp*err + Ki*errSum + Kd*dErr;
output = constrain(output, -255, 255);
lastErr = err;
Serial.print("Angle: "); Serial.print(angle, 2);
Serial.print(" PID out: "); Serial.println(output, 1);
delay(10);
}
راهنمای تنظیم Kp/Ki/Kd
| پارامتر | افزایش آن | کاهش آن | شروع با |
|---|---|---|---|
| Kp | واکنش سریعتر، نوسان بیشتر | کند و بیتفاوت | 10–40 |
| Ki | خطا به صفر میرسد، لرزش | خطای ماندگار | 0.0–1.0 |
| Kd | نوسان کم میشود | بیش از حد کمک نمیکند | 0.5–3.0 |
روش Ziegler–Nichols ساده: ابتدا Ki=0, Kd=0 بگذار. Kp را آرام بالا ببر تا ربات شروع به نوسان با دامنه ثابت کند. آن را Ku بنام. سپس: Kp = 0.6×Ku، Ki = 1.2×Ku/Tu، Kd = 0.075×Ku×Tu (Tu = دوره تناوب نوسان).
چکلیست
| مرحله | نتیجه مورد انتظار | ✓ |
|---|---|---|
| MPU-6050 detect میشود | Serial: «خطا» نشان نمیدهد | ⬜ |
| زاویه صفحه افقی ≈ ۰° | Serial Monitor زاویه نزدیک صفر | ⬜ |
| کج کردن ۴۵° → ≈۴۵° | زاویه درست نشان میدهد | ⬜ |
| PID output منطقی است | کج که میکنی output بزرگ میشود | ⬜ |