Thuật toán PID (Proportional-Integral-Derivative) là nền tảng cốt lõi trong việc cải thiện hiệu suất hệ thống điều khiển thông qua việc bổ sung các điểm cực và điểm không. Để hiểu rõ cách vận hành của PID trong thực tế, chúng ta sẽ phân tích quá trình áp dụng thuật toán này vào bài toán robot dò đường (Line Follower), từ những logic cơ bản nhất đến các bộ điều khiển phức tạp.
Cấu trúc và vai trò của các thành phần PID
Mỗi thành phần trong PID đảm nhận một nhiệm vụ riêng biệt để đưa hệ thống về trạng thái mong muốn:
- P (Proportional - Tỷ lệ): Điều chỉnh dựa trên sai lệch hiện tại. Sai lệch càng lớn, lực tác động càng mạnh. Tuy nhiên, nếu chỉ dùng P, hệ thống thường xuất hiện "sai số xác lập" (steady-state error) do các lực cản môi trường (như ma sát hoặc rò rỉ trong mô hình bể nước).
- I (Integral - Tích phân): Cộng dồn các sai lệch theo thời gian. Nếu sai số tồn tại lâu, bộ I sẽ tích lũy giá trị đủ lớn để triệt tiêu sai số xác lập, giúp robot bám sát vạch chính xác hơn.
- D (Derivative - Vi phân): Dự báo xu hướng của sai lệch dựa trên tốc độ thay đổi. Nó đóng vai trò như một bộ giảm chấn (brake), ngăn chặn việc điều chỉnh quá đà khi robot tiến gần về vạch đích, giúp hệ thống ổn định nhanh hơn và giảm rung lắc.
Các phương pháp điều khiển robot dò đường
1. Điều khiển logic ngưỡng cơ bản (Bang-Bang Control)
Đây là phương pháp đơn giản nhất, thường dùng cho các robot sử dụng 2 cảm biến hồng ngoại. Nguyên lý dựa trên việc kiểm tra ngưỡng sáng/tối để điều chỉnh hướng đi.
#define STEERING_ADJUST 35
void simpleTracking(int baseSpeedL, int baseSpeedR) {
int threshold = 512; // Giá trị ngưỡng phân biệt vạch đen/nền trắng
int leftVal = readSensor(LEFT_PIN);
int rightVal = readSensor(RIGHT_PIN);
if (leftVal < threshold && rightVal < threshold) {
// Cả hai cảm biến đều thấy vạch đen
moveForward(baseSpeedL, baseSpeedR);
} else if (leftVal < threshold) {
// Lệch sang phải, cần rẽ trái
moveForward(baseSpeedL - STEERING_ADJUST, baseSpeedR + STEERING_ADJUST);
} else if (rightVal < threshold) {
// Lệch sang trái, cần rẽ phải
moveForward(baseSpeedL + STEERING_ADJUST, baseSpeedR - STEERING_ADJUST);
} else {
// Cả hai ở ngoài vạch
moveForward(baseSpeedL, baseSpeedR);
}
}
2. Áp dụng PID để điều khiển tốc độ vi sai
Trong thực tế, điều khiển PID cho robot dò đường thường tập trung vào việc tính toán giá trị chênh lệch tốc độ giữa hai bánh xe. Dưới đây là cách triển khai bộ điều khiển PID cải tiến với việc gán giá trị sai số theo trạng thái cảm biến.
float kp = 12.0, ki = 0.2, kd = 2.0;
float currentError = 0, lastError = 0, integral = 0;
void updatePID(int sensorL, int sensorR) {
// Xác định sai số dựa trên trạng thái cảm biến
if (sensorL < 800 && sensorR < 800) {
currentError = 0; // Đang ở giữa vạch
} else if (sensorL < 800 && sensorR > 800) {
currentError = -3; // Lệch phải
} else if (sensorL > 800 && sensorR < 800) {
currentError = 3; // Lệch trái
}
// Tính toán các thành phần PID
float P = currentError;
integral += currentError;
// Giới hạn chống bão hòa tích phân (Anti-windup)
if (integral > 40) integral = 40;
if (integral < -40) integral = -40;
float D = currentError - lastError;
float pidOutput = (kp * P) + (ki * integral) + (kd * D);
lastError = currentError;
// Áp dụng vào động cơ
driveMotors(baseSpeed + pidOutput, baseSpeed - pidOutput);
}
Bộ điều khiển PID vị trí (Position PID) tiêu chuẩn
Để quản lý mã nguồn tốt hơn, các kỹ sư thường đóng gói PID vào cấu trúc dữ liệu để dễ dàng tái sử dụng cho nhiều mục tiêu (như điều khiển hướng hoặc điều khiển tốc độ vòng quét động cơ).
typedef struct {
float Kp;
float Ki;
float Kd;
float prevError;
float integral;
float maxOutput;
} PIDConfig;
/**
* Tính toán giá trị đầu ra PID vị trí
* @param targetValue Giá trị mục tiêu
* @param currentValue Giá trị phản hồi hiện tại
* @param config Cấu trúc tham số PID
* @return Giá trị điều khiển PWM
*/
int computePositionPID(float targetValue, float currentValue, PIDConfig *config) {
float error = targetValue - currentValue;
config->integral += error;
// Tính toán thành phần tỷ lệ, tích phân và vi phân
float pOut = config->Kp * error;
float iOut = config->Ki * config->integral;
float dOut = config->Kd * (error - config->prevError);
// Hạn chế đầu ra để bảo vệ động cơ
float output = pOut + iOut + dOut;
if (output > config->maxOutput) output = config->maxOutput;
if (output < -config->maxOutput) output = -config->maxOutput;
config->prevError = error;
return (int)output;
}
Lưu ý khi tinh chỉnh (Tuning)
Khi áp dụng PID cho robot dò đường, tỉ lệ phổ biến giữa các tham số thường nằm trong khoảng Kp : Ki : Kd = 100 : 1 : 10. Tuy nhiên, con số này phụ thuộc rất lớn vào tần số lấy mẫu và đặc tính cơ khí của robot. Thành phần Kp nên được tăng trước để robot có phản ứng nhanh, sau đó mới thêm Kd để giảm rung lắc và cuối cùng là Ki để tăng độ chính xác khi đi thẳng.