Sekarang, dalam postingan ini kita akan belajar membuat robot self balancing atau robot keseimbangan. Langsung aja kita lanjut ke prakteknya.
Apa Itu Robot Self Balancing?
Robot self-balancing adalah robot beroda dua yang menyeimbangkan dirinya sendiri agar tidak jatuh. Robot self-balancing menggunakan sistem “closed-loop feedback control”; ini berarti bahwa data real-time dari sensor gerak digunakan untuk mengontrol motor dan dengan cepat mengkompensasi gerakan miring apa pun untuk menjaga robot tetap tegak. Sistem kontrol umpan balik self-balancing serupa dapat dilihat di banyak aplikasi lain.
Di antara robot Self Balancing beroda dua, Robot Segway dan Ninebot, telah menjadi populer dan digunakan untuk bepergian atau sebagai pengangkut patroli. Selain itu, robot beroda Self Balancing seperti Anybots QB saat ini digunakan sebagai platform robot layanan.
Bahan-bahan yang Diperlukan
Di bagian ini, kita akan membahas komponen-komponennya, mengapa kita memilihnya, dan secara langsung kita akan membuat robotnya juga.
Untuk penjelasan yang lebih baik, saya telah membagi ini menjadi tiga bagian yang berbeda;
- Bagian Mekanik, di sini kita akan belajar membuat badan robot dengan cara yang sangat sederhana dan mudah.
- Bagian Elektronik, disini kita akan belajar tentang komponen elektronik, perancangan rangkaian, dan perakitan semua komponen. akhir bagian ini, robot kami akan siap untuk ke level berikutnya dan terakhir yaitu pengkodean dan kalibrasi Arduino.
- Arduino Coding & Calibration, di bagian terakhir ini, kita akan membahas kode Arduino dan cara mengkalibrasi robot untuk self-balancing.
- Setelah menyelesaikan tiga langkah ini, robot kami akan siap dioperasikan.
Part Mekanik yang Dibutuhkan
- PVC SHEET
- GRAPHITE PENCILS
- GEAR MOTOR
- MOTOR BRACKET
- WHEELS
Part Elektronik yang Diperlukan
- Arduino Nano
- L298n motor driver
- MPU6050 module
- 18650 Li-ion Battery
- 18650 Li-ion battery Holder
- Rocker Switch
Mulai Membuat Robot
Kita akan mulai membuat bagian mekanik terlebih dahulu. Untuk ini, saya telah menggunakan beberapa lembar PVC dan Pensil untuk membuat tubuh robot. Kita akan membuat robot bertingkat karena kita akan menggunakan mpu6050 yang bekerja paling baik jika ditempatkan pada ketinggian di atas alas. Kita akan membahas lebih lanjut tentang mpu6050 di bagian selanjutnya dari bagian elektronik artikel ini. Untuk saat ini, kita akan pergi untuk bagian mekanik saja. Setelah itu, kita akan membutuhkan dua buah motor gir, dua braket motor, dan dua roda.
Saya telah menggunakan lem tembak untuk merekatkan semua bagian. Ini akan memakan waktu tidak lebih dari 20-25 menit untuk memberikan tampilan yang sempurna seperti ini. Bagian mekanik kita sudah siap dan sekarang kita lanjut ke bagian elektronik.
Perakitan Part Elektronik
Pada bagian ini, kita akan membahas komponen elektronik yang kita gunakan untuk membuat robot ini. Kita juga akan belajar mengapa kita memilih semua komponen ini. Di sini kita juga akan menghubungkan semua komponen bersama-sama sesuai dengan diagram rangkaian yang telah saya lampirkan dengan artikel ini.
ARDUINO NANO
Arduino NANO adalah otak dari robot ini. Di sini saya memilihnya karena Ini adalah pengontrol mikro yang sempurna untuk mempelajari elektronik hobi dan pemrograman, dan ukurannya membuatnya sangat baik untuk membangun proyek yang memerlukan faktor bentuk kecil.
Driver Motor L298N
Driver Motor L298N adalah pengontrol yang menggunakan H-Bridge yang mudah untuk mengontrol arah dan kecepatan hingga 2 motor DC. L298N adalah driver motor H-Bridge ganda yang memungkinkan kontrol kecepatan dan arah dua motor DC secara bersamaan. Modul tersebut dapat menggerakkan motor DC yang memiliki tegangan antara 5 hingga 35V, dengan arus puncak hingga 2A.
MPU6050
MPU6050 adalah Micro Electro-mechanical system (MEMS), terdiri dari akselerometer tiga sumbu dan giroskop tiga sumbu. yang berarti memberikan enam nilai sebagai output:
tiga nilai dari akselerometer
tiga dari giroskop
Ini membantu kita untuk mengukur kecepatan, orientasi, percepatan, perpindahan, dan fitur seperti gerak lainnya. Ini sangat akurat, karena berisi perangkat keras konversi analog ke digital 16-bit untuk setiap saluran. Oleh karena itu, ia menangkap saluran x, y, dan z secara bersamaan. Sensor menggunakan I2C Bus untuk berinteraksi dengan Arduino. MPU6050 dapat mengukur rotasi sudut menggunakan giroskop on-chip dengan empat rentang skala penuh yang dapat diprogram yaitu ±250 °/dtk, ±500 °/dtk, ±1000 °/dtk, dan ±2000 °/dtk.
Ini adalah detail dasar dari komponen yang saya gunakan di robot ini. Ada banyak lagi komponen seperti sakelar Rocker, Kabel Jumper, dll.
Source Code dan Kalibrasi
Ada beberapa langkah yang harus diikuti untuk mengkalibrasi & mengunggah kode. Untuk pemahaman yang lebih baik, saya telah membagi bagian pengkodean dan kalibrasi Arduino ini menjadi beberapa bagian yang berbeda.
Mengukur Sudut Kemiringan Menggunakan Akselerometer
MPU6050 memiliki akselerometer 3-sumbu dan giroskop 3-sumbu. Akselerometer mengukur percepatan di sepanjang tiga sumbu dan giroskop mengukur laju sudut di sekitar ketiga sumbu. Untuk mengukur sudut kemiringan robot diperlukan nilai percepatan sepanjang sumbu y dan z. Fungsi atan2(y,z) memberikan sudut dalam radian antara sumbu z positif bidang dan titik yang diberikan oleh koordinat (z,y) pada bidang itu, dengan tanda positif untuk sudut berlawanan arah jarum jam (setengah kanan -bidang, y > 0), dan tanda negatif untuk sudut searah jarum jam (setengah bidang kiri, y < 0). Kita menggunakan library ini yang ditulis oleh Jeff Rowberg untuk membaca data dari MPU6050. Unggah kode yang diberikan di bawah ini dan lihat bagaimana sudut kemiringan bervariasi.
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include "math.h"
MPU6050 mpu;
int16_t accY, accZ;
float accAngle;
void setup() {
mpu.initialize();
Serial.begin(9600);
}
void loop() {
accZ = mpu.getAccelerationZ();
accY = mpu.getAccelerationY();
accAngle = atan2(accY, accZ)*RAD_TO_DEG;
if(isnan(accAngle));
else
Serial.println(accAngle);
}
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include "math.h"
MPU6050 mpu;
int16_t accY, accZ;
float accAngle;
void setup() {
mpu.initialize();
Serial.begin(9600);
}
void loop() {
accZ = mpu.getAccelerationZ();
accY = mpu.getAccelerationY();
accAngle = atan2(accY, accZ)*RAD_TO_DEG;
if(isnan(accAngle));
else
Serial.println(accAngle);
}
Coba gerakkan robot ke depan dan ke belakang sambil tetap memiringkannya pada sudut tertentu. Anda akan mengamati bahwa sudut yang ditampilkan di serial monitor Anda tiba-tiba berubah. Hal ini disebabkan komponen percepatan horizontal merubah nilai percepatan sumbu y dan z.
Mengukur Sudut Kemiringan Menggunakan Giroskop
Giroskop 3-sumbu MPU6050 mengukur laju sudut (kecepatan rotasi) di sepanjang tiga sumbu. Untuk robot self-balancing kita, kecepatan sudut sepanjang sumbu x saja sudah cukup untuk mengukur laju jatuhnya robot.
Dalam kode yang diberikan di bawah ini, kita membaca nilai gyro tentang sumbu x, mengubahnya menjadi derajat per detik dan kemudian mengalikannya dengan waktu loop untuk mendapatkan perubahan sudut. Kami menambahkan ini ke sudut sebelumnya untuk mendapatkan sudut saat ini.
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
int16_t gyroX, gyroRate;
float gyroAngle=0;
unsigned long currTime, prevTime=0, loopTime;
void setup() {
mpu.initialize();
Serial.begin(9600);
}
void loop() {
currTime = millis();
loopTime = currTime - prevTime;
prevTime = currTime;
gyroX = mpu.getRotationX();
gyroRate = map(gyroX, -32768, 32767, -250, 250);
gyroAngle = gyroAngle + (float)gyroRate*loopTime/1000;
Serial.println(gyroAngle);
}
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
int16_t gyroX, gyroRate;
float gyroAngle=0;
unsigned long currTime, prevTime=0, loopTime;
void setup() {
mpu.initialize();
Serial.begin(9600);
}
void loop() {
currTime = millis();
loopTime = currTime - prevTime;
prevTime = currTime;
gyroX = mpu.getRotationX();
gyroRate = map(gyroX, -32768, 32767, -250, 250);
gyroAngle = gyroAngle + (float)gyroRate*loopTime/1000;
Serial.println(gyroAngle);
}
Posisi MPU6050 saat program mulai berjalan adalah titik inklinasi nol. Sudut kemiringan akan diukur sehubungan dengan titik ini.
Jaga agar robot tetap stabil pada sudut yang tetap dan Anda akan mengamati bahwa sudutnya akan bertambah atau berkurang secara bertahap. Itu tidak akan tetap stabil. Hal ini disebabkan oleh penyimpangan yang melekat pada giroskop.
Dalam kode yang diberikan di atas, waktu loop dihitung menggunakan fungsi millis() yang dibangun ke dalam Arduino IDE. Pada langkah selanjutnya, kita akan menggunakan interupsi pengatur waktu untuk membuat interval pengambilan sampel yang tepat. Periode sampling ini juga akan digunakan dalam menghasilkan output menggunakan kontroler PID.
Menggabungkan Hasil Dengan Filter Complementary
Google mendefinisikan komplementer sebagai “menggabungkan sedemikian rupa untuk meningkatkan atau menekankan kualitas satu sama lain atau lainnya”.
Kita memiliki dua pengukuran sudut dari dua sumber yang berbeda. Pengukuran dari akselerometer dipengaruhi oleh gerakan horizontal yang tiba-tiba dan pengukuran dari giroskop secara bertahap menjauh dari nilai sebenarnya. Dengan kata lain, pembacaan akselerometer dipengaruhi oleh sinyal durasi pendek dan pembacaan giroskop oleh sinyal durasi panjang. Pembacaan ini, dengan cara, saling melengkapi. Gabungkan keduanya menggunakan Filter complementary dan kami mendapatkan pengukuran sudut yang stabil dan akurat. Filter complementary pada dasarnya adalah filter lolos tinggi yang bekerja pada giroskop dan filter lolos rendah yang bekerja pada akselerometer untuk menyaring penyimpangan dan kebisingan dari pengukuran.
currentAngle = 0.9934 * (previousAngle + gyroAngle) + 0.0066 * (accAngle)
0,9934 dan 0,0066 adalah koefisien filter untuk konstanta waktu filter 0,75 detik. Filter lolos rendah memungkinkan sinyal yang lebih lama dari durasi ini untuk melewatinya dan filter lolos tinggi memungkinkan sinyal yang lebih pendek dari durasi ini untuk melewatinya. Respons filter dapat diubah dengan memilih konstanta waktu yang tepat. Menurunkan konstanta waktu akan memungkinkan lebih banyak percepatan horizontal untuk melewatinya.
Kontrol PID untuk Menghasilkan Output
PID adalah singkatan dari Proporsional, Integral, dan Derivatif. Masing-masing istilah ini memberikan respons unik terhadap robot self balancing.
Istilah proporsional, seperti namanya, menghasilkan respons yang sebanding dengan perubahan. Untuk sistem ini, perubahannya adalah sudut kemiringan robot.
Istilah integral menghasilkan respons berdasarkan akumulasi perubahan. Ini pada dasarnya adalah jumlah dari semua perubahan dikalikan dengan periode sampling. Ini adalah respons berdasarkan perilaku sistem sebelumnya.
Suku turunan sebanding dengan turunan galat. Ini adalah perbedaan antara perubahan saat ini dan perubahan sebelumnya dibagi dengan periode sampling. Ini bertindak sebagai istilah prediktif yang merespons bagaimana robot mungkin berperilaku dalam loop pengambilan sampel berikutnya.
Mengalikan masing-masing istilah ini dengan konstanta yang sesuai (yaitu, Kp, Ki, dan Kd) dan menjumlahkan hasilnya, kami menghasilkan output yang kemudian dikirim sebagai perintah untuk menggerakkan motor.
Menyetel Konstanta PID
- Atur Ki dan Kd ke nol dan tingkatkan Kp secara bertahap sehingga robot mulai berosilasi di sekitar posisi nol.
- Meningkatkan Ki agar respon robot lebih cepat saat tidak seimbang. Ki harus cukup besar agar sudut kemiringan tidak bertambah. Robot harus kembali ke posisi nol jika miring.
- Naikkan Kd untuk mengurangi osilasi. Overshoot juga harus dikurangi sekarang.
- Ulangi langkah di atas dengan menyempurnakan setiap parameter untuk mencapai hasil terbaik.
Source Code Lengkap Robot Self Balancing
//Robot Self Balancing
#include<Arduino.h>
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
#define MIN_ABS_SPEED 30
MPU6050 mpu;
// Variable Kontrol/status MPU
bool dmpReady = false; // set true jika init DMP berhasil
uint8_t mpuIntStatus; // menyimpan byte status interupsi aktual dari MPU
uint8_t devStatus; // mengembalikan status setelah setiap operasi perangkat (0 = berhasil, !0 = error)
uint16_t packetSize; // ukuran paket DMP yang diharapkan (default adalah 42 byte)
uint16_t fifoCount; // jumlah semua byte saat ini di FIFO
uint8_t fifoBuffer[64]; // buffer penyimpanan FIFO
// variable orientasi/gerakan
Quaternion q; // [w, x, y, z] quaternion container
VectorFloat gravity; // [x, y, z] gravity vector
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
//PID
double originalSetpoint = 172.50;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output;
//sesuaikan nilai-nilai ini agar sesuai dengan desain Anda sendiri
double Kp = 60;
double Kd = 2.2;
double Ki = 270;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
double motorSpeedFactorLeft = 0.6;
double motorSpeedFactorRight = 0.5;
//MOTOR CONTROLLER
int ENA = 5;
int IN1 = 6;
int IN2 = 7;
int IN3 = 9;
int IN4 = 8;
int ENB = 10;
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);
volatile bool mpuInterrupt = false; // menunjukkan apakah pin interupsi MPU menjadi tinggi
void dmpDataReady()
{
mpuInterrupt = true;
}
void setup()
{
// menggabungkan dengan bus I2C (library I2Cdev tidak melakukan ini secara otomatis)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz jika CPU 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
mpu.initialize();
devStatus = mpu.dmpInitialize();
// sediakan offset gyro Anda sendiri di sini, diskalakan untuk sensitivitas minimum
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 default pabrik untuk chip pengujian saya
// pastikan itu berhasil (mengembalikan 0 jika demikian)
if (devStatus == 0)
{
// nyalakan DMP, sekarang sudah siap
mpu.setDMPEnabled(true);
// aktifkan deteksi interupsi Arduino
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// atur flag DMP Ready sehingga fungsi loop() utama tahu tidak apa-apa untuk menggunakannya
dmpReady = true;
// dapatkan ukuran paket DMP yang diharapkan untuk perbandingan nanti
packetSize = mpu.dmpGetFIFOPacketSize();
//setup PID
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else
{
// ERROR!
// 1 = beban memori awal gagal
// 2 = Pembaruan konfigurasi DMP gagal
// (kalo mau putus biasanya kodenya 1)
Serial.print(F("Inisialisasi DMP gagal (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
}
void loop()
{
// jika pemrograman gagal, jangan mencoba melakukan apa pun
if (!dmpReady) return;
// tunggu interupsi MPU atau paket tambahan tersedia
while (!mpuInterrupt && fifoCount < packetSize)
{
//tidak ada data mpu - melakukan perhitungan dan keluaran PID ke motor
pid.Compute();
motorController.move(output, MIN_ABS_SPEED);
}
// setel ulang tanda interupsi dan dapatkan byte INT_STATUS
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// dapatkan jumlah FIFO saat ini
fifoCount = mpu.getFIFOCount();
// periksa overflow (ini seharusnya tidak pernah terjadi kecuali kode kita terlalu tidak efisien)
if ((mpuIntStatus & 0x10) || fifoCount == 1024)
{
// reset sehingga kita dapat melanjutkan dengan bersih
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// jika tidak, periksa interupsi siap data DMP (ini harus sering terjadi)
}
else if (mpuIntStatus & 0x02)
{
// tunggu panjang data yang tersedia benar, harus menunggu SANGAT singkat
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// membaca paket dari FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// lacak jumlah FIFO di sini jika ada> 1 paket yang tersedia
// (ini memungkinkan kita segera membaca lebih banyak tanpa menunggu interupsi)
fifoCount -= packetSize;
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
input = ypr[1] * 180 / M_PI + 180;
}
}