Mục lục:

Robot tự cân bằng từ Magicbit: 6 bước
Robot tự cân bằng từ Magicbit: 6 bước

Video: Robot tự cân bằng từ Magicbit: 6 bước

Video: Robot tự cân bằng từ Magicbit: 6 bước
Video: Hướng dẫn lập trình ROBOT tự cân bằng dùng động cơ bước .Self balancing robot using stepper motor 2024, Tháng bảy
Anonim

Hướng dẫn này hướng dẫn cách làm một robot tự cân bằng bằng bảng nhà phát triển Magicbit. Chúng tôi đang sử dụng magicbit làm bảng phát triển trong dự án này dựa trên ESP32. Do đó, bất kỳ bảng phát triển ESP32 nào cũng có thể được sử dụng trong dự án này.

Quân nhu:

  • ma thuật
  • Trình điều khiển động cơ L298 cầu H kép
  • Bộ điều chỉnh tuyến tính (7805)
  • Pin Lipo 7.4V 700mah
  • Đơn vị đo lường quán tính (IMU) (6 bậc tự do)
  • động cơ bánh răng 3V-6V DC

Bước 1: Câu chuyện

Câu chuyện
Câu chuyện
Câu chuyện
Câu chuyện

Này các bạn, hôm nay trong hướng dẫn này chúng ta sẽ tìm hiểu về những thứ phức tạp một chút. Đó là về robot tự cân bằng sử dụng Magicbit với Arduino IDE. Vì vậy, hãy bắt đầu.

Trước hết, chúng ta hãy xem robot tự cân bằng là gì. Robot tự cân bằng là robot hai bánh. Điểm đặc biệt là robot có thể tự cân bằng mà không cần sử dụng bất kỳ sự hỗ trợ nào từ bên ngoài. Khi có điện, robot sẽ đứng lên và cân bằng liên tục bằng cách sử dụng các chuyển động dao động. Vì vậy, bây giờ tất cả các bạn có một số ý tưởng sơ bộ về robot tự cân bằng.

Bước 2: Lý thuyết và Phương pháp luận

Lý thuyết và Phương pháp luận
Lý thuyết và Phương pháp luận

Để giữ thăng bằng cho robot, đầu tiên chúng ta lấy dữ liệu từ một số cảm biến để đo góc của robot so với mặt phẳng thẳng đứng. Vì mục đích đó, chúng tôi đã sử dụng MPU6050. Sau khi lấy dữ liệu từ cảm biến, chúng tôi tính toán độ nghiêng so với mặt phẳng thẳng đứng. Nếu rô bốt ở vị trí thẳng và cân bằng thì góc nghiêng bằng không. Nếu không, thì góc nghiêng là giá trị dương hoặc âm. Nếu rô bốt nghiêng về phía trước, thì rô bốt sẽ di chuyển sang hướng phía trước. Ngoài ra, nếu rô bốt nghiêng về phía ngược lại thì rô bốt sẽ di chuyển sang hướng ngược lại. Nếu góc nghiêng này cao thì tốc độ phản hồi phải cao. Ngược lại, góc nghiêng thấp thì tốc độ phản ứng thấp. Để kiểm soát quá trình này, chúng tôi sử dụng định lý cụ thể được gọi là PID. PID là hệ thống điều khiển được sử dụng để điều khiển nhiều quá trình. PID là viết tắt của 3 tiến trình.

  • P- tỷ lệ thuận
  • I- tích phân
  • D- đạo hàm

Mọi hệ thống đều có đầu vào và đầu ra. Theo cách tương tự, hệ thống điều khiển này cũng có một số đầu vào. Trong hệ thống điều khiển này là sự sai lệch so với trạng thái ổn định. Chúng tôi gọi đó là lỗi. Trong rô bốt của chúng tôi, sai số là góc nghiêng so với mặt phẳng thẳng đứng. Nếu rô bốt được cân bằng thì góc nghiêng bằng không. Vì vậy giá trị lỗi sẽ bằng không. Do đó, đầu ra của hệ thống PID bằng không. Hệ thống này bao gồm ba quy trình toán học riêng biệt.

Đầu tiên là lỗi nhân từ độ lợi số. Mức tăng này thường được gọi là Kp

P = lỗi * Kp

Thứ hai là tạo ra tích phân của sai số trong miền thời gian và nhân nó với một số độ lợi. Mức tăng này được gọi là Ki

I = Tích phân (lỗi) * Ki

Một thứ ba là đạo hàm của sai số trong miền thời gian và nhân nó với một số lượng thu được. Mức tăng này được gọi là Kd

D = (d (lỗi) / dt) * kd

Sau khi thêm các thao tác trên, chúng ta nhận được kết quả cuối cùng

ĐẦU RA = P + I + D

Do phần P robot có thể có được vị trí ổn định tỷ lệ với độ lệch. Phần tôi tính toán diện tích lỗi so với đồ thị thời gian. Vì vậy, nó cố gắng đưa robot đến vị trí ổn định luôn chính xác. D phần đo độ dốc theo thời gian so với đồ thị lỗi. Nếu lỗi đang tăng giá trị này là dương. Nếu lỗi đang giảm giá trị này là âm. Do đó, khi robot di chuyển đến vị trí ổn định thì tốc độ phản ứng sẽ giảm xuống và điều này sẽ hữu ích để loại bỏ những khoảng vượt không cần thiết. Bạn có thể tìm hiểu thêm về lý thuyết PID từ liên kết này được hiển thị bên dưới.

www.arrow.com/en/research-and-events/articles/pid-controller-basics-and-tutorial-pid-implementation-in-arduino

Đầu ra của chức năng PID được giới hạn trong phạm vi 0-255 (độ phân giải PWM 8 bit) và sẽ cấp cho động cơ dưới dạng tín hiệu PWM.

Bước 3: Thiết lập phần cứng

Thiết lập phần cứng
Thiết lập phần cứng

Bây giờ đây là phần thiết lập phần cứng. Thiết kế của robot là tùy thuộc vào bạn. Khi bạn thiết kế cơ thể của robot, bạn phải xem xét sự đối xứng của nó với trục thẳng đứng nằm trong trục động cơ. Bộ pin nằm bên dưới. Do đó, robot rất dễ cân bằng. Trong thiết kế của chúng tôi, chúng tôi cố định bảng Magicbit theo chiều dọc thân máy. Chúng tôi đã sử dụng hai động cơ hộp số 12V. Nhưng bạn có thể sử dụng bất kỳ loại động cơ bánh răng nào. điều đó phụ thuộc vào kích thước rô bốt của bạn.

Khi chúng ta thảo luận về mạch, nó được cung cấp bởi pin Lipo 7.4V. Magicbit sử dụng 5V để cấp nguồn. Vì vậy, chúng tôi đã sử dụng bộ điều chỉnh 7805 để điều chỉnh điện áp pin thành 5V. Trong các phiên bản sau của Magicbit, bộ điều chỉnh đó không cần thiết. Bởi vì nó cấp nguồn lên đến 12V. Chúng tôi trực tiếp cung cấp 7.4V cho trình điều khiển động cơ.

Kết nối tất cả các thành phần theo sơ đồ dưới đây.

Bước 4: Thiết lập phần mềm

Trong đoạn mã, chúng tôi đã sử dụng thư viện PID để tính toán đầu ra PID.

Truy cập liên kết sau để tải xuống.

www.arduinolibraries.info/libraries/pid

Tải xuống phiên bản mới nhất của nó.

Để có được các bài đọc cảm biến tốt hơn, chúng tôi đã sử dụng thư viện DMP. DMP là viết tắt của quá trình chuyển động kỹ thuật số. Đây là tính năng có sẵn của MPU6050. Con chip này có bộ phận xử lý chuyển động tích hợp. Vì vậy, nó cần đọc và phân tích. Sau khi nó tạo ra các đầu ra chính xác không ồn ào tới bộ vi điều khiển (trong trường hợp này là Magicbit (ESP32)). Nhưng có rất nhiều công việc trong lĩnh vực vi điều khiển để lấy số đọc đó và tính toán góc. Vì vậy, đơn giản là chúng tôi đã sử dụng thư viện MPU6050 DMP. Tải xuống bằng goint đến liên kết sau.

github.com/ElectronicCats/mpu6050

Để cài đặt các thư viện, trong menu Arduino, đi tới công cụ-> bao gồm thư viện-> thư viện add.zip và chọn tệp thư viện mà bạn đã tải xuống.

Trong mã, bạn phải thay đổi góc điểm đặt một cách chính xác. Các giá trị hằng số PID khác nhau giữa các robot. Vì vậy, để điều chỉnh điều đó, trước tiên hãy đặt các giá trị Ki và Kd bằng 0 và sau đó tăng Kp cho đến khi bạn có được tốc độ phản ứng tốt hơn. Kp nhiều hơn gây ra nhiều vọt lố hơn. Sau đó tăng giá trị Kd. Tăng nó luôn với số lượng rất ít. Giá trị này thường thấp hơn các giá trị khác. Bây giờ tăng Ki cho đến khi bạn có độ ổn định rất tốt.

Chọn đúng cổng COM và loại bo mạch. tải lên mã. Bây giờ bạn có thể chơi với robot tự làm của mình.

Bước 5: Sơ đồ

Sơ đồ
Sơ đồ

Bước 6: Mã

#bao gồm

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = false; // đặt true nếu DMP init thành công uint8_t mpuIntStatus; // giữ byte trạng thái ngắt thực tế từ MPU uint8_t devStatus; // trả về trạng thái sau mỗi hoạt động của thiết bị (0 = thành công,! 0 = lỗi) uint16_t packetSize; // kích thước gói tin DMP dự kiến (mặc định là 42 byte) uint16_t FifoCount; // đếm tất cả các byte hiện có trong FIFO uint8_t FifoBuffer [64]; // Bộ đệm lưu trữ FIFO Quaternion q; // [w, x, y, z] thùng chứa quaternion Trọng lực VectorFloat; // [x, y, z] vector trọng lực float ypr [3]; // [yaw, pitch, roll] thùng chứa yaw / pitch / roll và vector trọng lực double originalSetpoint = 172.5; double setpoint = originalSetpoint; di chuyển képAngleOffset = 0,1; kép đầu vào, đầu ra; int moveState = 0; double Kp = 23; // đặt P kép đầu tiên Kd = 0.8; // giá trị này thường nhỏ gấp đôi Ki = 300; // giá trị này phải cao để PID pid ổn định hơn (& input, & output, & setpoint, Kp, Ki, Kd, DIRECT); // pid khởi tạo int motL1 = 26; // 4 chân cho ổ đĩa động cơ int motL2 = 2; int motR1 = 27; int motR2 = 4; bool dễ bay hơi mpuInterrupt = false; // cho biết chân ngắt MPU có tăng cao hay không void dmpDataReady () {mpuInterrupt = true; } void setup () {ledcSetup (0, 20000, 8); // pwm setup ledcSetup (1, 20000, 8); ledcSetup (2, 20000, 8); ledcSetup (3, 20000, 8); ledcAttachPin (motL1, 0); // mã pin của động cơ ledcAttachPin (motL2, 1); ledcAttachPin (motR1, 2); ledcAttachPin (motR2, 3); // tham gia bus I2C (thư viện I2Cdev không tự động làm việc này) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin (); Wire.setClock (400000); // Đồng hồ I2C 400kHz. Nhận xét dòng này nếu gặp khó khăn khi biên dịch #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire:: setup (400, true); #endif Serial.println (F ("Đang khởi tạo thiết bị I2C…")); pinMode (14, INPUT); // khởi tạo giao tiếp nối tiếp // (115200 được chọn vì nó được yêu cầu cho đầu ra Teapot Demo, nhưng // thực sự tùy thuộc vào bạn tùy thuộc vào dự án của bạn) Serial.begin (9600); while (! nối tiếp); // đợi Leonardo liệt kê, những người khác tiếp tục ngay lập tức // khởi tạo thiết bị Serial.println (F ("Đang khởi tạo thiết bị I2C…")); mpu.initialize (); // xác minh kết nối Serial.println (F ("Đang kiểm tra kết nối thiết bị…")); Serial.println (mpu.testConnection ()? F ("Kết nối MPU6050 thành công"): F ("Kết nối MPU6050 không thành công")); // tải và cấu hình DMP Serial.println (F ("Đang khởi tạo DMP…")); devStatus = mpu.dmpInitialize (); // cung cấp hiệu số con quay hồi chuyển của riêng bạn ở đây, được chia tỷ lệ cho độ nhạy tối thiểu mpu.setXGyroOffset (220); mpu.setYGyroOffset (76); mpu.setZGyroOffset (-85); mpu.setZAccelOffset (1788); // 1688 mặc định ban đầu cho chip thử nghiệm của tôi // đảm bảo rằng nó hoạt động (trả về 0 nếu vậy) if (devStatus == 0) {// bật DMP, bây giờ nó đã sẵn sàng Serial.println (F ("Đang bật DMP… ")); mpu.setDMPEnabled (true); // cho phép phát hiện ngắt Arduino Serial.println (F ("Cho phép phát hiện ngắt (ngắt ngoài Arduino 0)…")); mountInterrupt (14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus (); // đặt cờ Sẵn sàng cho DMP của chúng ta để hàm main loop () biết là có thể sử dụng nó Serial.println (F ("DMP đã sẵn sàng! Đang đợi ngắt đầu tiên…")); dmpReady = true; // lấy kích thước gói DMP dự kiến để so sánh sau đó packetSize = mpu.dmpGetFIFOPacketSize (); // thiết lập PID pid. SetMode (AUTOMATIC); pid. SetSampleTime (10); pid. SetOutputLimits (-255, 255); } else {// LỖI! // 1 = tải bộ nhớ ban đầu không thành công // 2 = Cập nhật cấu hình DMP không thành công // (nếu nó bị hỏng, thông thường mã sẽ là 1) Serial.print (F ("Khởi tạo DMP không thành công (mã")); Serial. print (devStatus); Serial.println (F (")")); }} void loop () {// nếu lập trình không thành công, đừng cố làm gì nếu (! dmpReady) return; // đợi ngắt MPU hoặc (các) gói bổ sung khả dụng trong khi (! mpuInterrupt && FifoCount <packetSize) {pid. Compute (); // khoảng thời gian này được sử dụng để tải dữ liệu, vì vậy bạn có thể sử dụng khoảng thời gian này cho các tính toán khác motorSpeed (đầu ra); } // đặt lại cờ ngắt và lấy byte INT_STATUS mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus (); // lấy số FIFO hiện tại là FifoCount = mpu.getFIFOCount (); // kiểm tra lỗi tràn (điều này sẽ không bao giờ xảy ra trừ khi mã của chúng ta quá kém hiệu quả) if ((mpuIntStatus & 0x10) || FifoCount == 1024) {// đặt lại để chúng ta có thể tiếp tục hoàn toàn mpu.resetFIFO (); Serial.println (F ("FIFO tràn!")); // nếu không, hãy kiểm tra xem có ngắt sẵn sàng dữ liệu DMP không (điều này xảy ra thường xuyên)} else if (mpuIntStatus & 0x02) {// đợi độ dài dữ liệu có sẵn chính xác, phải đợi RẤT ngắn trong khi (có sẵn số lượng 1 gói dữ liệu năm mươi // (this cho phép chúng tôi đọc nhiều hơn ngay lập tức mà không cần đợi ngắt quãng) print ("ypr / t"); Serial.print (ypr [0] * 180 / M_PI); // góc euler Serial.print ("\ t"); Serial.print (ypr [1] * 180 / M_PI); Serial.print ("\ t"); Serial.println (ypr [2] * 180 / M_PI); #endif input = ypr [1] * 180 / M_PI + 180;}} void motorSpeed (int PWM) {float L1, L2, R1, R2; if (PWM> = 0) {// chuyển tiếp L2 = 0; L1 = abs (PWM); R2 = 0; R1 = abs (PWM); if (L1> = 255) { L1 = R1 = 255;}} else {// chiều lùi L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); if (L2> = 255) {L2 = R2 = 255; }} // ổ đĩa động cơ ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1 * 0,97); // 0,97 là tốc độ hoặc, bởi vì động cơ bên phải có tốc độ cao hơn động cơ bên trái, vì vậy chúng tôi giảm nó cho đến khi tốc độ động cơ bằng ledcWrite (3, R2 * 0,97);

}

Đề xuất: