Edge Impulse với ESP32: thu dữ liệu MPU6050, huấn luyện nhận dạng cử chỉ, xuất thư viện Arduino
Quy trình Edge Impulse với ESP32 và MPU6050: gửi dữ liệu gia tốc qua data forwarder, gắn nhãn, cửa sổ và đặc trưng phổ, đọc ma trận nhầm lẫn, xuất thư viện Arduino và chạy nhận dạng cử chỉ.
Edge Impulse là dịch vụ web làm trọn quy trình TinyML: thu dữ liệu từ cảm biến, gắn nhãn, trích đặc trưng, huấn luyện, kiểm thử và xuất mô hình thành thư viện Arduino chạy trên vi điều khiển. Bài này đi qua toàn bộ quy trình với bài toán nhận dạng ba cử chỉ (đứng yên, lắc ngang, vẽ vòng tròn) bằng cảm biến gia tốc MPU6050 và ESP32: chương trình gửi dữ liệu lên máy tính, cách thiết kế "impulse", đọc kết quả huấn luyện, và đoạn code chạy mô hình trên ESP32.
1. Quy trình

Edge Impulse có gói dùng miễn phí cho cá nhân, cần tạo tài khoản; điều khoản và giao diện có thể thay đổi theo thời gian, xem trang chính thức của dịch vụ. Dữ liệu cảm biến sẽ được tải lên máy chủ của họ.
2. Nối MPU6050

MPU6050 có cảm biến gia tốc 3 trục và con quay 3 trục; bài này chỉ dùng gia tốc. Cố định cảm biến thật chắc vào vật cần nhận cử chỉ, vì cảm biến lỏng tạo dữ liệu nhiễu. Cần thêm ESP32 DevKit V1, breadboard và dây cắm.
3. Thu dữ liệu
Chương trình dưới đây đọc thẳng thanh ghi gia tốc của MPU6050 qua I2C (không cần thư viện), đổi ra m/s² và in đúng 100 dòng mỗi giây, mỗi dòng ba số cách nhau dấu phẩy. Cách đọc thanh ghi I2C: xem I2C là gì.
// PYWORLD.VN - Gửi dữ liệu gia tốc MPU6050 lên máy tính 100 lần mỗi giây cho công cụ data forwarder của Edge Impulse
// PYWORLD.VN - Đọc thẳng thanh ghi MPU6050 qua I2C, không dùng thư viện; mỗi dòng: ax,ay,az (m/s2)
// PYWORLD.VN - Nối: VCC -> 3V3, GND -> GND, SCL -> GPIO22, SDA -> GPIO21
#include <Wire.h>
const uint8_t DIA_CHI_MPU = 0x68; // chân AD0 nối GND
const uint8_t PWR_MGMT_1 = 0x6B;
const uint8_t ACCEL_CONFIG = 0x1C;
const uint8_t ACCEL_XOUT_H = 0x3B;
const float LSB_MOI_G = 16384.0f; // thang ±2 g
const float G = 9.80665f;
const uint32_t CHU_KY_US = 10000; // 100 Hz
uint32_t lucMau = 0;
void ghiThanhGhi(uint8_t thanhGhi, uint8_t giaTri) {
Wire.beginTransmission(DIA_CHI_MPU);
Wire.write(thanhGhi);
Wire.write(giaTri);
Wire.endTransmission();
}
bool docGiaToc(float &ax, float &ay, float &az) {
Wire.beginTransmission(DIA_CHI_MPU);
Wire.write(ACCEL_XOUT_H);
if (Wire.endTransmission(false) != 0) return false; // giữ đường truyền để đọc tiếp
if (Wire.requestFrom(DIA_CHI_MPU, (uint8_t)6) != 6) return false;
int16_t x = (Wire.read() << 8) | Wire.read(); // byte cao trước
int16_t y = (Wire.read() << 8) | Wire.read();
int16_t z = (Wire.read() << 8) | Wire.read();
ax = x / LSB_MOI_G * G;
ay = y / LSB_MOI_G * G;
az = z / LSB_MOI_G * G;
return true;
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22, 400000);
ghiThanhGhi(PWR_MGMT_1, 0x00); // đánh thức MPU6050 (mặc định đang ngủ)
ghiThanhGhi(ACCEL_CONFIG, 0x00); // thang ±2 g
delay(100);
lucMau = micros();
}
void loop() {
if (micros() - lucMau < CHU_KY_US) return;
lucMau += CHU_KY_US; // giữ nhịp đều, không trôi
float ax, ay, az;
if (docGiaToc(ax, ay, az)) {
Serial.printf("%.3f,%.3f,%.3f\n", ax, ay, az);
}
}

- Cài Node.js, rồi cài bộ công cụ dòng lệnh của Edge Impulse:
npm install -g edge-impulse-cli. - Đóng Serial Monitor (để nhả cổng COM), chạy
edge-impulse-data-forwarder, đăng nhập, chọn dự án, đặt tên ba trục làax, ay, az. Công cụ tự nhận tần số 100 Hz. - Trên trang dự án, mục thu dữ liệu: chọn nhãn (ví dụ "lac ngang"), độ dài mẫu (ví dụ 10 giây), bấm bắt đầu rồi làm cử chỉ. Mỗi nhãn thu vài phút, từ nhiều lần làm khác nhau.
- Chia khoảng 80% dữ liệu để học, 20% để thử.
4. Thiết kế impulse

- Cửa sổ thời gian (window size): 2000 ms, đủ dài để chứa vài nhịp cử chỉ. Bước dời (window increase): ví dụ 500 ms, các cửa sổ chồng lên nhau để có nhiều mẫu học hơn.
- Khối xử lý: phân tích phổ (Spectral Analysis), biến 200 mẫu × 3 trục thành vài chục đặc trưng.
- Khối học: phân loại (Classification), một mạng nơ-ron nhỏ.

Bảng đặc trưng cho thấy vì sao mạng học được: mỗi cử chỉ có "dấu vân tay" riêng về độ lớn dao động và tần số. Muốn hiểu bên trong khối phân loại, xem bài tự huấn luyện mạng nơ-ron nhỏ trong cùng loạt.
5. Huấn luyện và đọc kết quả

Sau khi huấn luyện, trang dự án hiện độ chính xác và ma trận nhầm lẫn: hàng là cử chỉ thật, cột là mô hình đoán. Đường chéo càng cao càng tốt; ô ngoài đường chéo lớn cho biết hai cử chỉ hay bị nhầm. Khi đó: thu thêm dữ liệu cho hai cử chỉ đó, làm cử chỉ khác nhau rõ hơn, hoặc kéo dài cửa sổ. Luôn kiểm thử thêm trên dữ liệu chưa dùng để học (mục kiểm thử) trước khi xuất thư viện.
6. Xuất thư viện và chạy trên ESP32
- Mục triển khai: chọn Arduino library, bản lượng tử hóa int8, tải file .zip.
- Arduino IDE: Sketch → Include Library → Add .ZIP Library, chọn file vừa tải.
- Viết chương trình: thu đủ một cửa sổ mẫu với đúng tần số lúc thu dữ liệu, rồi gọi hàm phân loại của thư viện.
Đoạn code dưới đây dùng hàm đọc gia tốc của chương trình thu dữ liệu ở trên. Tên file thư viện do Edge Impulse đặt theo tên dự án; các hằng số EI_CLASSIFIER_… nằm trong thư viện đó nên đoạn này chỉ biên dịch được sau khi đã thêm thư viện của dự án bạn:
#include <ten_du_an_inferencing.h> // đổi theo tên thư viện Edge Impulse xuất cho dự án của bạn
float bangMau[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE]; // đủ một cửa sổ: số mẫu × 3 trục
void nhanDangCuChi() {
for (size_t i = 0; i < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; i += 3) {
uint32_t hanChot = micros() + (uint32_t)(EI_CLASSIFIER_INTERVAL_MS * 1000);
docGiaToc(bangMau[i], bangMau[i + 1], bangMau[i + 2]);
while ((int32_t)(micros() - hanChot) < 0) {} // giữ đúng 100 Hz như lúc thu dữ liệu
}
signal_t tinHieu;
numpy::signal_from_buffer(bangMau, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &tinHieu);
ei_impulse_result_t ketQua = {0};
if (run_classifier(&tinHieu, &ketQua, false) != EI_IMPULSE_OK) return;
for (size_t k = 0; k < EI_CLASSIFIER_LABEL_COUNT; k++) {
Serial.printf("%s: %.2f\n", ketQua.classification[k].label, ketQua.classification[k].value);
}
}
Gọi nhanDangCuChi() trong loop() (sau khi đã khởi động MPU6050 như chương trình thu dữ liệu). Mỗi lần gọi mất khoảng 2 giây để thu cửa sổ, cộng thời gian suy luận vài chục mili giây. Chỉ coi kết quả là chắc khi xác suất cao (ví dụ trên 0,8).
7. Lỗi hay gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Data forwarder không thấy dữ liệu | Serial Monitor còn mở, sai tốc độ baud | Đóng Serial Monitor; dùng 115200 |
| Tần số nhận ra khác 100 Hz | In thêm dòng chữ khác, vòng lặp chậm | Chỉ in dòng số; giữ nhịp bằng micros() |
| Đọc toàn 0 hoặc lỗi I2C | Sai dây SDA/SCL, MPU6050 chưa được đánh thức | Kiểm tra dây, ghi 0 vào thanh ghi 0x6B |
| Huấn luyện tốt, chạy thật đoán sai | Tần số lấy mẫu, cách gắn cảm biến khác lúc thu | Giữ nguyên tần số, vị trí, hướng cảm biến |
| Hai cử chỉ hay nhầm | Dữ liệu ít, cử chỉ giống nhau | Thêm dữ liệu, chọn cử chỉ khác biệt hơn |
8. Xem thêm
- Các bài cùng loạt AI trên vi điều khiển: TinyML là gì, tự huấn luyện mạng nơ-ron nhỏ chạy trên ESP32, nhận lệnh giọng nói bằng ESP-SR.
Xem thêm