Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác

Làm cánh tay robot 4 servo với ESP32: điều khiển từng khớp bằng biến trở, nhấn nút ghi tư thế, phát lại chuỗi động tác mượt, lưu vào flash khi mất điện và gợi ý STEM.

Cánh tay robot là mô hình thu nhỏ của robot công nghiệp trong nhà máy: robot gắp, xếp, hàn, sơn đều lặp lại một chuỗi động tác đã được "dạy" trước. Với ESP32, bốn servo và bốn biến trở, học sinh tự làm được một cánh tay 4 khớp: vặn biến trở để điều khiển từng khớp, nhấn nút để ghi lại từng tư thế, rồi cho cánh tay tự lặp lại cả chuỗi động tác.

Bài viết hướng dẫn cấu tạo, nối dây với nguồn riêng cho servo, chương trình ghi nhớ động tác vào bộ nhớ flash (tắt điện không mất), cách chuyển tư thế mượt và gợi ý hoạt động STEM.

1. Cấu tạo cánh tay 4 khớp

Hình minh họa cánh tay robot nhìn ngang với bốn khớp đế xoay, vai, khuỷu, kẹp và bảng servo D23 D22 D21 D19, biến trở D34 D35 D32 D33 tương ứng
Hình minh họa: bốn khớp và chân điều khiển tương ứng.
  • Đế xoay: quay cả cánh tay sang trái, sang phải.
  • Vai và khuỷu: nâng hạ và vươn ra. Hai khớp này chịu lực lớn nhất.
  • Kẹp: mở ra, kẹp vật.

Khung có thể làm bằng bìa cứng, mica hoặc gỗ ép, hoặc dùng bộ khung cánh tay robot bằng mica bán sẵn. Cánh tay càng nhẹ thì servo càng ít bị quá tải.

2. Linh kiện cần chuẩn bị

3. Sơ đồ nối dây

Sơ đồ khối cánh tay robot: bốn biến trở vào D34 D35 D32 D33, nút GHI vào D18 và CHẠY vào D4, bốn servo nhận PWM từ D23 D22 D21 D19, nguồn 5 V riêng cấp cho servo và chân VIN của ESP32, GND chung
Hình minh họa: sơ đồ khối và bảng nối dây.
  • Không lấy nguồn servo từ cổng USB. Bốn servo cùng chuyển động có thể kéo hơn 1 A, làm ESP32 khởi động lại hoặc hỏng cổng USB của máy tính.
  • Nguồn 5 V riêng cấp cho dây đỏ của bốn servo. GND của nguồn này nối chung với GND của ESP32.
  • Khi đã nạp xong chương trình, có thể cấp 5 V vào chân VIN để chạy độc lập. Không cắm USB cùng lúc với nguồn VIN.
  • Thêm một tụ hóa 1000 µF giữa 5 V và GND ở chỗ cắm dây servo.
  • Biến trở nối 3V3 (không nối 5 V), chân giữa vào các chân ADC1: D34, D35, D32, D33.

4. Chương trình ghi nhớ và phát lại

Gói esp32 đã có sẵn thư viện Preferences để lưu dữ liệu vào bộ nhớ flash, không cần cài thêm.

/*
  PYWORLD.VN - Cánh tay robot 4 servo: điều khiển bằng 4 biến trở, ghi nhớ và phát lại động tác (ESP32)
  Servo: đế D23, vai D22, khuỷu D21, kẹp D19 (nguồn 5 V riêng, GND chung). Biến trở: D34, D35, D32, D33
  Nút GHI -> D18, nút CHẠY -> D4 (nút nối xuống GND). Động tác lưu trong bộ nhớ flash, tắt điện không mất
*/

#include <Preferences.h>

const uint8_t SERVO[4] = {23, 22, 21, 19};      // đế xoay, vai, khuỷu, kẹp
const uint8_t BIEN_TRO[4] = {34, 35, 32, 33};
#define NUT_GHI  18
#define NUT_CHAY 4

const int TOI_DA = 20;                           // số tư thế tối đa
uint8_t dongTac[TOI_DA][4];
int soBuoc = 0;
float goc[4] = {90, 90, 90, 90};
Preferences boNho;

void datGoc(int i, float g) {
  g = constrain(g, 0, 180);
  goc[i] = g;
  uint32_t us = 500 + (uint32_t)(1900 * g / 180);
  ledcWrite(SERVO[i], (uint32_t)((uint64_t)us * 65536 / 20000));
}

float docBienTro(int i) {
  long tong = 0;
  for (int k = 0; k < 4; k++) tong += analogRead(BIEN_TRO[i]);
  return (tong / 4) * 180.0f / 4095;
}

// Trả về 1 khi nhả nút sau một lần nhấn ngắn, 2 khi giữ nút quá 2 giây (báo một lần)
int suKienNut(int chan, bool &dangNhan, unsigned long &tNhan, bool &daBaoGiu) {
  bool nhan = (digitalRead(chan) == LOW);
  if (nhan && !dangNhan) { dangNhan = true; tNhan = millis(); daBaoGiu = false; }
  if (nhan && dangNhan && !daBaoGiu && millis() - tNhan > 2000) { daBaoGiu = true; return 2; }
  if (!nhan && dangNhan) {
    dangNhan = false;
    if (!daBaoGiu && millis() - tNhan > 30) return 1;     // bỏ qua dội phím ngắn hơn 30 ms
  }
  return 0;
}

void luuBoNho() {
  boNho.putInt("n", soBuoc);
  boNho.putBytes("dongtac", dongTac, soBuoc * 4);
}

// Đi từ tư thế hiện tại tới tư thế đích trong khoảng 0,8 giây, tăng tốc rồi giảm tốc theo đường cos
bool diToi(const uint8_t dich[4]) {
  float dau[4];
  for (int i = 0; i < 4; i++) dau[i] = goc[i];
  for (int k = 1; k <= 40; k++) {
    float p = (1 - cos(PI * k / 40.0f)) / 2;
    for (int i = 0; i < 4; i++) datGoc(i, dau[i] + (dich[i] - dau[i]) * p);
    delay(20);
    if (digitalRead(NUT_CHAY) == LOW) return false;       // nhấn CHẠY để dừng phát lại
  }
  return true;
}

void phatLai() {
  Serial.printf("Phat lai %d tu the, nhan CHAY de dung\n", soBuoc);
  delay(400);                                             // chờ nhả nút
  while (true) {
    for (int b = 0; b < soBuoc; b++) {
      if (!diToi(dongTac[b])) { Serial.println("Dung phat lai"); delay(400); return; }
      delay(300);
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(NUT_GHI, INPUT_PULLUP);
  pinMode(NUT_CHAY, INPUT_PULLUP);
  for (int i = 0; i < 4; i++) {
    ledcAttach(SERVO[i], 50, 16);
    datGoc(i, docBienTro(i));
  }
  boNho.begin("canhtay", false);
  soBuoc = constrain(boNho.getInt("n", 0), 0, TOI_DA);
  boNho.getBytes("dongtac", dongTac, sizeof(dongTac));
  Serial.printf("Da nap %d tu the tu bo nho\n", soBuoc);
}

void loop() {
  static bool nhanGhi = false, giuGhi = false, nhanChay = false, giuChay = false;
  static unsigned long tGhi = 0, tChay = 0;

  // Chế độ tay: mỗi servo đi theo biến trở của nó, lọc cho đỡ rung
  for (int i = 0; i < 4; i++) {
    float g = goc[i] + 0.2f * (docBienTro(i) - goc[i]);
    if (fabsf(g - goc[i]) > 0.5f) datGoc(i, g);
  }

  int ghi = suKienNut(NUT_GHI, nhanGhi, tGhi, giuGhi);
  if (ghi == 1 && soBuoc < TOI_DA) {
    for (int i = 0; i < 4; i++) dongTac[soBuoc][i] = (uint8_t)lroundf(goc[i]);
    soBuoc++;
    luuBoNho();
    Serial.printf("Ghi tu the %d: de %d, vai %d, khuyu %d, kep %d\n", soBuoc,
                  dongTac[soBuoc - 1][0], dongTac[soBuoc - 1][1], dongTac[soBuoc - 1][2], dongTac[soBuoc - 1][3]);
  } else if (ghi == 2) {
    soBuoc = 0;
    luuBoNho();
    Serial.println("Da xoa het dong tac");
  }

  if (suKienNut(NUT_CHAY, nhanChay, tChay, giuChay) == 1 && soBuoc > 0) {
    phatLai();
  }
  delay(20);
}

Cách dùng:

  1. Vặn bốn biến trở để đưa cánh tay tới tư thế thứ nhất, nhấn GHI. Serial Monitor (115200 baud) in bốn góc vừa lưu.
  2. Đưa tới tư thế tiếp theo, nhấn GHI. Làm tiếp, tối đa 20 tư thế.
  3. Nhấn CHẠY: cánh tay lần lượt đi qua các tư thế đã ghi và lặp lại. Nhấn CHẠY lần nữa để dừng.
  4. Giữ GHI 2 giây để xóa hết và ghi lại từ đầu.

5. Giải thích các phần chính

Hình minh họa hai chế độ: chế độ tay theo biến trở và phát lại, nút GHI lưu tư thế hoặc giữ 2 giây để xóa; đồ thị góc đi từ tư thế A tới B trong 0,8 giây theo đường cos so với đường thẳng
Hình minh họa: các chế độ và đường cong chuyển tư thế.
  • Chế độ tay: mỗi vòng lặp, góc servo được kéo dần về góc của biến trở (goc + 0,2 × (biến trở − goc)). Servo không bị giật khi tay vặn nhanh, và đứng yên khi biến trở dao động nhẹ.
  • Nút nhấn: hàm suKienNut() phân biệt nhấn ngắn và giữ lâu, bỏ qua các lần dội phím ngắn hơn 30 ms.
  • Lưu flash: mỗi lần ghi hoặc xóa, số tư thế và mảng góc được lưu bằng putInt(), putBytes(). Lúc khởi động, chương trình nạp lại bằng getInt(), getBytes().
  • Chuyển tư thế mượt: mỗi lần chuyển tư thế chia thành 40 bước 20 ms, theo đường cong (1 − cos(π × k / 40)) / 2. Cánh tay khởi động và dừng êm, vật đang kẹp không bị văng ra.

6. Gợi ý dùng trong giờ học STEM

Mục tiêu: học sinh hiểu robot công nghiệp được "dạy" động tác thế nào, làm quen với tọa độ khớp, lưu trữ dữ liệu, và thiết kế cơ khí cân bằng lực.

  1. Gắp và đặt. Dạy cánh tay gắp một khối xốp nhỏ từ ô A đặt sang ô B. Thi xem nhóm nào làm được chuỗi động tác ít tư thế nhất mà vẫn chính xác.
  2. Đo phạm vi làm việc. Đặt cánh tay trên tờ giấy kẻ ô, đánh dấu các điểm kẹp với tới được, vẽ vùng làm việc. Liên hệ với độ dài hai đoạn tay.
  3. Lực và đòn bẩy. Treo vật nặng dần ở đầu kẹp, quan sát khớp vai kêu hoặc tụt trước. Giải thích bằng mô-men lực: cánh tay đòn dài thì mô-men lớn.
  4. Mở rộng. Thay biến trở bằng trang web điều khiển qua Wi-Fi như bài xe robot trong cùng loạt, hoặc ghi động tác lên thẻ nhớ.

7. Xử lý lỗi thường gặp

Lỗi thường gặp
Hiện tượngNguyên nhân thường gặpCách xử lý
ESP32 khởi động lại khi nhiều servo cùng quayServo lấy điện chung với ESP32 hoặc nguồn yếuDùng nguồn 5 V riêng từ 2 A; thêm tụ 1000 µF; GND chung
Servo rung khi đứng yênBiến trở dao động, nguồn nhiễuTăng ngưỡng 0,5 độ lên 1–2 độ; dây nguồn servo ngắn, chắc
Khớp vai không nâng nổi cánh tayCánh tay nặng, servo nhựa yếuDùng servo bánh răng kim loại; rút ngắn, làm nhẹ cánh tay; thêm lò xo đỡ
Tắt điện bật lại mất động tácGhi xong chưa kịp lưu; tên namespace khácXem Serial Monitor báo "Da nap n tu the"; giữ đúng tên "canhtay"
Phát lại không dừngNút CHẠY nối sai chân hoặc không nối GNDKiểm tra D4 và GND; nhấn giữ CHẠY tới khi cánh tay dừng

8. Xem thêm

Cùng loạt: ESP32 điều khiển servo SG90 bằng PWM, xe robot ESP32 điều khiển qua Wi-Fi.


Tin tức liên quan

Hướng dẫn ESP32 điều khiển module relay 4 kênh an toàn
Hướng dẫn ESP32 điều khiển module relay 4 kênh an toàn

Hướng dẫn ESP32 điều khiển relay 4 kênh: chọn GPIO an toàn, cấp nguồn 5 V phù hợp, xác định active LOW và lưu ý khi đóng cắt tải.

Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny
Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny

Hướng dẫn cài driver, cài Thonny và nạp firmware MicroPython cho mạch ESP32 Lite, dùng REPL, nháy LED, lưu main.py, đọc nút nhấn, quét I2C và xử lý lỗi thường gặp.

Hướng dẫn dùng mạch tạo trễ NE555 có relay: đấu dây và chỉnh thời gian trễ
Hướng dẫn dùng mạch tạo trễ NE555 có relay: đấu dây và chỉnh thời gian trễ

Hướng dẫn dùng mạch tạo trễ NE555 có relay bản 5 V và 12 V: nguyên lý tạo trễ, ý nghĩa các cọc VCC, GND, COM, CK, CB, cách chỉnh thời gian trễ, sơ đồ đấu dây và xử lý lỗi thường gặp.

Hướng dẫn dùng Deep Sleep trên ESP32: tiết kiệm pin, đánh thức bằng timer và nút nhấn
Hướng dẫn dùng Deep Sleep trên ESP32: tiết kiệm pin, đánh thức bằng timer và nút nhấn

Hướng dẫn đưa ESP32 vào Deep Sleep trên Arduino IDE để tiết kiệm pin: các chế độ nguồn, đánh thức bằng timer và nút nhấn, giữ biến trong bộ nhớ RTC và ước tính thời lượng pin.

ESP32 điều khiển servo SG90 bằng PWM, không cần thư viện: quét góc, biến trở, hiệu chỉnh
ESP32 điều khiển servo SG90 bằng PWM, không cần thư viện: quét góc, biến trở, hiệu chỉnh

Hướng dẫn điều khiển servo SG90 bằng ESP32 với bộ PWM LEDC 50 Hz có sẵn: xung 0,5–2,4 ms, sơ đồ nối, quét góc, vặn biến trở, hiệu chỉnh hai đầu hành trình và thí nghiệm STEM.

Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái
Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái

Hướng dẫn làm bộ điều khiển relay từ xa bằng hai mạch ESP32 và ESP-NOW hai chiều: remote gửi lệnh, mạch relay báo lại trạng thái, lọc theo địa chỉ MAC và bật mã hóa.

Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường
Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường

Làm xe robot tự tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 gắn trên servo quét trái phải, cầu phân áp cho chân ECHO, thuật toán chọn đường, lưu đồ và thí nghiệm STEM.

Hướng dẫn làm cân điện tử với ESP32, HX711 và load cell trên Arduino IDE
Hướng dẫn làm cân điện tử với ESP32, HX711 và load cell trên Arduino IDE

Hướng dẫn nối load cell với module HX711 và ESP32 DevKit V1, gá load cell đúng cách, hiệu chuẩn bằng vật đã biết khối lượng và viết chương trình cân trên Arduino IDE.


Bình luận
Đã thêm vào giỏ hàng