-
-
-
Tổng tiền thanh toán:
-
Thương hiệu: (Đang cập nhật ...) Loại: (Đang cập nhật ...)
Sau khi hoàn thành Bài 5 – ESP-NOW: Giao tiếp 2 ESP32, chúng ta đã xây dựng được đường truyền dữ liệu giữa tay cầm ESP32 và Drone ESP32.
Ở bài này, hệ thống sẽ tiến thêm một bước quan trọng:
Di chuyển joystick → ESP32 đọc giá trị → xử lý lệnh → truyền ESP-NOW → Drone nhận lệnh → tạo tín hiệu điều khiển.
Đây chính là bước biến một bộ mạch ESP32 thành hệ thống điều khiển Drone bằng tay cầm.
Sau bài học, học viên cần thực hiện được chuỗi:
JOYSTICK
↓
ESP32 TAY CẦM
↓
Đọc ADC
↓
Calibration
↓
Dead Zone
↓
Mapping
↓
Đóng gói dữ liệu
↓
ESP-NOW
↓
ESP32 DRONE
↓
Giải mã dữ liệu
↓
Throttle / Roll / Pitch / Yaw
Ở bài này, trọng tâm là đường truyền điều khiển.
Phần MPU6050 + Motor Mixing + PID/ổn định drone sẽ được triển khai sâu hơn ở Bài 7.
Tay cầm của Drone Mini ESP32 gồm các thành phần chính:
Joystick tạo ra tín hiệu điện áp thay đổi theo vị trí cần điều khiển.
ESP32 đọc tín hiệu đó thông qua ADC.
Ví dụ:
Joystick
│
├── X → ADC ESP32
│
└── Y → ADC ESP32
│
↓
Xử lý dữ liệu
│
↓
ESP-NOW
│
↓
ESP32 Drone
Một joystick analog thông thường có hai trục:
Y+
↑
│
X- ← ● → X+
│
↓
Y-
Hai trục tạo ra hai tín hiệu analog:
X → vị trí trái/phải
Y → vị trí lên/xuống
Ngoài ra joystick thường có một nút nhấn:
X axis
Y axis
SW
Tùy thiết kế tay cầm thực tế, cách gán joystick vào Throttle, Roll, Pitch, Yaw có thể khác nhau.
Vì vậy, trong firmware của Drone Mini cần xác định chính xác:
Joystick nào → chức năng nào
Trục X → chức năng nào
Trục Y → chức năng nào
Không nên mặc định theo một chuẩn duy nhất nếu chưa kiểm tra PCB thực tế.
Joystick analog tạo ra điện áp.
ESP32 sử dụng ADC để chuyển điện áp thành giá trị số.
Ví dụ:
Điện áp joystick
↓
ADC
↓
Giá trị số
Giá trị ADC thực tế phụ thuộc vào ESP32, cấu hình ADC và phiên bản Arduino-ESP32.
Do đó, không nên giả định tuyệt đối rằng mọi board ESP32 đều có cùng một giá trị ADC hoặc cùng độ tuyến tính.
Điều quan trọng là chúng ta xác định được:
MIN
CENTER
MAX
của joystick thực tế.
Trước khi truyền ESP-NOW, hãy kiểm tra joystick độc lập.
Ví dụ:
int xValue = analogRead(JOY_X);
int yValue = analogRead(JOY_Y);
Serial.print("X = ");
Serial.print(xValue);
Serial.print(" | Y = ");
Serial.println(yValue);
Di chuyển joystick và quan sát Serial Monitor.
Ví dụ kết quả có thể dạng:
X = 2040 | Y = 2018
X = 2500 | Y = 2020
X = 3100 | Y = 2025
X = 1500 | Y = 2010
Các giá trị trên chỉ là ví dụ minh họa.
GPIO và giá trị ADC phải lấy theo đúng mạch tay cầm đang sử dụng.
Joystick thực tế hiếm khi có:
MIN = 0
CENTER = chính giữa tuyệt đối
MAX = giá trị tối đa
Ví dụ joystick ở giữa có thể đọc:
X = 2037
Y = 1989
thay vì một giá trị lý tưởng.
Nếu không calibration, drone có thể nhận thấy joystick đang lệch dù người dùng không tác động.
Vì vậy cần xác định:
X_MIN
X_CENTER
X_MAX
Y_MIN
Y_CENTER
Y_MAX
Đây là kỹ thuật rất quan trọng đối với tay điều khiển drone.
Giả sử joystick đang ở giữa:
Y
↑
│
┌──┼──┐
│ │ │
───────┼──●──┼──────→ X
│ │
└─────┘
Do nhiễu ADC, giá trị có thể dao động nhẹ:
1995
2002
1998
2007
2001
Nếu không có Dead Zone, hệ thống có thể hiểu rằng người dùng liên tục ra lệnh điều khiển.
Ta tạo một vùng nhỏ quanh vị trí trung tâm:
┌─────────┐
│ Dead │
│ Zone │
│ ● │
└─────────┘
Nếu joystick nằm trong vùng này:
Control = 0
Ví dụ:
int applyDeadZone(int value, int center, int deadZone)
{
if (abs(value - center) <= deadZone) {
return 0;
}
return value - center;
}
Ví dụ:
Center = 2048
Dead Zone = 80
Các giá trị:
1980 → 0
2010 → 0
2048 → 0
2080 → 0
2150 → giá trị điều khiển
Giá trị Dead Zone cần được điều chỉnh dựa trên joystick thực tế.
Sau khi đọc ADC, chúng ta cần chuyển đổi dữ liệu sang một phạm vi dễ xử lý.
Ví dụ:
ADC
↓
Calibration
↓
Dead Zone
↓
Mapping
↓
-1000 ... +1000
Có thể sử dụng:
int controlValue = map(
adcValue,
ADC_MIN,
ADC_MAX,
-1000,
1000
);
Tuy nhiên, đối với vùng trung tâm, nên xử lý riêng để đảm bảo joystick trở về gần 0.
Drone cơ bản có bốn nhóm lệnh:
| Kênh | Chức năng |
|---|---|
| Throttle | Tăng/giảm lực nâng |
| Roll | Nghiêng trái/phải |
| Pitch | Nghiêng trước/sau |
| Yaw | Xoay quanh trục đứng |
Mô hình:
PITCH
↑
│
│
ROLL ←────────┼────────→ ROLL
│
↓
PITCH
YAW
↻
Trong tay cầm, các trục joystick sẽ được ánh xạ vào những kênh này.
Ví dụ minh họa:
Joystick trái Y → Throttle
Joystick trái X → Yaw
Joystick phải X → Roll
Joystick phải Y → Pitch
Đây chỉ là một cách bố trí.
Firmware thực tế phải được thiết lập theo đúng hướng joystick trên tay cầm của bạn.
Một vấn đề rất thường gặp là:
Di chuyển joystick lên nhưng giá trị lại giảm.
Ví dụ:
Joystick ↑
↓
ADC giảm
Trong trường hợp này cần đảo chiều:
value = -value;
Hoặc thay đổi phạm vi mapping.
Ví dụ:
int pitch = map(
yValue,
ADC_MAX,
ADC_MIN,
-1000,
1000
);
Việc này rất quan trọng vì chiều điều khiển sai có thể khiến drone phản ứng ngược với mong muốn.
Sau khi xử lý joystick, chúng ta tạo packet.
Ví dụ:
struct DroneControl {
int16_t throttle;
int16_t roll;
int16_t pitch;
int16_t yaw;
uint8_t arm;
uint8_t button1;
uint32_t counter;
};
Ví dụ dữ liệu:
Throttle = 300
Roll = -120
Pitch = 80
Yaw = 0
Arm = 1
Counter = 1250
Packet này sẽ được gửi qua ESP-NOW.
Toàn bộ quá trình tại tay cầm:
JOYSTICK
↓
analogRead()
↓
Calibration
↓
Dead Zone
↓
Mapping
↓
Throttle/Roll/Pitch/Yaw
↓
DroneControl
↓
ESP-NOW
Đây là phần quan trọng nhất của Bài 6.
Sau khi tạo packet:
DroneControl control;
control.throttle = throttle;
control.roll = roll;
control.pitch = pitch;
control.yaw = yaw;
control.arm = armState;
control.button1 = buttonState;
control.counter++;
Sau đó gửi packet tới ESP32 Drone.
Với Arduino-ESP32 hiện nay, cách triển khai ESP-NOW nên sử dụng API phù hợp với phiên bản ESP32 core đang cài đặt.
Về mặt kiến trúc:
DroneControl
↓
ESP-NOW Peer
↓
ESP32 Drone
Ở phía Drone:
ESP-NOW Receiver
↓
Nhận DroneControl
↓
Kiểm tra dữ liệu
↓
Kiểm tra timeout
↓
Lưu giá trị điều khiển
Ví dụ:
RX:
Throttle = 500
Roll = 100
Pitch = -50
Yaw = 20
Các giá trị này chưa nên đưa trực tiếp vào motor một cách đơn giản.
Chúng cần đi qua tầng điều khiển của drone.
Một firmware drone nên được thiết kế theo từng tầng.
┌─────────────────────────┐
│ USER INPUT │
│ Joystick │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ ESP-NOW RX │
│ Communication │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ Control Command │
│ Throttle/Roll/Pitch/Yaw │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ IMU / MPU6050 │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ Control Algorithm │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ Motor Mixing │
└───────────┬─────────────┘
↓
┌─────────────────────────┐
│ 4 Motors │
└─────────────────────────┘
Bài 6 chủ yếu hoàn thiện từ Joystick đến Control Command.
Một lỗi thiết kế phổ biến là:
Joystick
↓
PWM Motor
Cách này quá đơn giản đối với một hệ thống quadcopter ổn định.
Kiến trúc phù hợp hơn:
Joystick
↓
Lệnh người điều khiển
↓
MPU6050
↓
Thuật toán điều khiển
↓
Motor Mixing
↓
Motor
Joystick đưa ra mong muốn của người điều khiển.
MPU6050 cung cấp trạng thái thực tế của drone.
Bộ điều khiển sẽ kết hợp hai thông tin này.
Tay cầm có thể mất kết nối.
Drone phải biết khi nào packet cuối cùng được nhận.
Ví dụ:
uint32_t lastPacketTime;
Mỗi khi nhận packet:
lastPacketTime = millis();
Sau đó kiểm tra:
if (millis() - lastPacketTime > FAILSAFE_TIMEOUT) {
// Chuyển sang trạng thái an toàn
}
Mục tiêu:
Mất tín hiệu
↓
Timeout
↓
Failsafe
↓
Không tiếp tục sử dụng
lệnh điều khiển cũ
Đây là phần bắt buộc cần kiểm thử trước khi bay.
Không nên để motor hoạt động ngay khi drone vừa cấp nguồn.
Có thể thiết kế trạng thái:
DISARM
↓
Nhận lệnh ARM
↓
Kiểm tra điều kiện
↓
ARM
Ví dụ:
Power ON
↓
DISARM
↓
Kiểm tra ESP-NOW
↓
Kiểm tra MPU6050
↓
Joystick ở trạng thái an toàn
↓
ARM
Trong thiết kế thực tế, điều kiện ARM cần được xây dựng rõ ràng và kiểm thử kỹ.
Một bài test rất quan trọng:
Controller
↓
ESP-NOW
↓
Drone
↓
Serial Monitor
Không gắn cánh quạt.
Di chuyển joystick và quan sát:
Throttle : 0 → 1000
Roll : -1000 → +1000
Pitch : -1000 → +1000
Yaw : -1000 → +1000
Khi joystick trở về giữa:
Roll ≈ 0
Pitch ≈ 0
Yaw ≈ 0
Nếu các giá trị không về gần trung tâm, cần quay lại bước Calibration + Dead Zone.
Đây là bước không được bỏ qua.
Ví dụ:
Joystick phải
↓
Roll +
↓
Drone phải phản ứng đúng
Nếu:
Joystick phải
↓
Roll -
thì cần đảo chiều phần mềm.
Tương tự với:
Không nên thử bay khi chưa xác nhận đúng chiều của tất cả kênh điều khiển.
Thực hiện:
1. Bật Controller
2. Bật Drone
3. Kết nối ESP-NOW
4. Kiểm tra packet
5. Tắt Controller
6. Quan sát Drone
Drone phải chuyển sang trạng thái an toàn theo thiết kế Failsafe.
Sau đó kiểm tra lại:
Controller ON
↓
Kết nối lại
↓
Nhận packet
↓
Hệ thống hoạt động
Khi hệ thống phát triển hoàn thiện, vòng điều khiển sẽ gần giống:
NGƯỜI ĐIỀU KHIỂN
│
↓
Joystick
│
↓
ESP32 Controller
│
↓
ESP-NOW
│
↓
ESP32 Drone
│
┌────────┴────────┐
↓ ↓
Lệnh bay MPU6050
│ │
└────────┬────────┘
↓
Control Algorithm
↓
Motor Mixing
↓
M1 M2 M3 M4
Đây là kiến trúc nền tảng của một hệ thống quadcopter điều khiển bằng tay cầm.
Nguyên nhân:
Giải pháp:
Calibration
+
Dead Zone
+
Filtering nếu cần
Kiểm tra:
X direction
Y direction
Sau đó đảo chiều trong mapping nếu cần.
Kiểm tra:
Kiểm tra:
Kiểm tra ngay:
FAILSAFE TIMEOUT
Không được để drone tiếp tục sử dụng command cũ vô thời hạn.
Hiển thị:
X
Y
SW
trên Serial Monitor.
Xác định:
X_MIN
X_CENTER
X_MAX
Y_MIN
Y_CENTER
Y_MAX
Thiết lập vùng trung tâm để joystick không tạo lệnh ngoài ý muốn.
Chuyển joystick thành:
Throttle
Roll
Pitch
Yaw
Gửi packet:
Throttle
Roll
Pitch
Yaw
Arm
Counter
từ Controller sang Drone.
Tắt Controller và kiểm tra Drone có chuyển sang trạng thái an toàn hay không.