Съдържание:

Самобалансиращ се робот от Magicbit: 6 стъпки
Самобалансиращ се робот от Magicbit: 6 стъпки

Видео: Самобалансиращ се робот от Magicbit: 6 стъпки

Видео: Самобалансиращ се робот от Magicbit: 6 стъпки
Видео: Робот балансир на шаговых двигателях 2024, Юли
Anonim

Този урок показва как да си направите самобалансиращ се робот с помощта на Magicbit dev board. Ние използваме magicbit като борда за разработка в този проект, който е базиран на ESP32. Следователно всяка платка за разработка на ESP32 може да се използва в този проект.

Консумативи:

  • magicbit
  • Двоен H-мост L298 двигател
  • Линеен регулатор (7805)
  • Lipo 7.4V 700mah батерия
  • Инерционна измервателна единица (IMU) (6 градуса свобода)
  • редукторни двигатели 3V-6V DC

Стъпка 1: История

История
История
История
История

Хей момчета, днес в този урок ще научим за малко сложни неща. Това е самобалансиращ се робот, използващ Magicbit с Arduino IDE. Така че нека започнем.

Първо, нека да разгледаме какво е самобалансиращ се робот. Самобалансиращият се робот е робот на две колела. Особеността е, че роботът може да се балансира, без да използва външна поддръжка. Когато захранването е включено, роботът ще се изправи и след това ще балансира непрекъснато с помощта на колебателни движения. Така че сега имате някаква груба представа за самобалансиращ се робот.

Стъпка 2: Теория и методология

Теория и методология
Теория и методология

За да балансираме робота, първо получаваме данни от някой сензор за измерване на ъгъла на робота спрямо вертикалната равнина. За тази цел използвахме MPU6050. След получаване на данните от сензора изчисляваме наклона към вертикалната равнина. Ако роботът е в изправено и балансирано положение, тогава ъгълът на наклон е нулев. Ако не, тогава ъгълът на наклон е положителна или отрицателна стойност. Ако роботът е наклонен към предната страна, тогава роботът трябва да се премести в предната посока. Също така, ако роботът е наклонен към обратната страна, той трябва да се премести в обратна посока. Ако този ъгъл на наклон е висок, скоростта на реакция трябва да бъде висока. Обратно ъгълът на наклон е нисък, тогава скоростта на реакция трябва да е ниска. За да контролираме този процес, използвахме специфична теорема, наречена PID. PID е система за управление, която се използва за управление на много процеси. PID означава 3 процеса.

  • P- пропорционален
  • I- интеграл
  • D- производно

Всяка система има вход и изход. По същия начин тази система за управление също има някакъв вход. В тази система за управление това е отклонението от стабилното състояние. Нарекохме това като грешка. В нашия робот грешката е ъгълът на наклона от вертикалната равнина. Ако роботът е балансиран, ъгълът на наклон е нулев. Така че стойността на грешката ще бъде нула. Следователно изходът на PID системата е нула. Тази система включва три отделни математически процеса.

Първият е умножена грешка от числовото усилване. Тази печалба обикновено се нарича Kp

P = грешка*Kp

Второто е генериране на интеграла на грешката във времевата област и умножаването му от част от печалбата. Тази печалба се нарича Ki

I = Интегрален (грешка)*Ki

Третата е производна на грешката във времевата област и я умножава по някаква сума на печалбата. Тази печалба се нарича Kd

D = (d (грешка)/dt)*kd

След добавяне на горните операции получаваме крайния резултат

ИЗХОД = P+I+D

Поради частта P роботът може да получи стабилна позиция, която е пропорционална на отклонението. I част изчислява площта на грешката спрямо графиката за време. Затова той се опитва да постави робота в стабилна позиция винаги точно. D част измерва наклона във времето спрямо графиката на грешките. Ако грешката се увеличава, тази стойност е положителна. Ако грешката намалява, тази стойност е отрицателна. Поради това, когато роботът се премести в стабилна позиция, скоростта на реакцията ще бъде намалена и това ще помогне да се премахнат ненужните превишения. Можете да научите повече за PID теорията от тази връзка, показана по -долу.

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

Изходът на PID функцията е ограничен до 0-255 диапазон (8 битова PWM резолюция) и това ще се подава към двигателите като PWM сигнал.

Стъпка 3: Настройка на хардуера

Хардуерна настройка
Хардуерна настройка

Сега това е част за настройка на хардуера. Дизайнът на робота зависи от вас. Когато проектирате тялото на робота, трябва да вземете предвид неговото симетрично спрямо вертикалната ос, която се намира в оста на двигателя. Батерията се намира по -долу. Затова роботът е лесен за балансиране. В нашия дизайн ние фиксираме дъската Magicbit вертикално към тялото. Използвахме два 12V редукторни двигателя. Но можете да използвате всякакъв вид редукторни двигатели. това зависи от размерите на вашия робот.

Когато обсъждаме схемата, тя се захранва от 7.4V Lipo батерия. Magicbit използва 5V за захранване. Затова използвахме регулатор 7805 за регулиране на напрежението на батерията до 5V. В по -късните версии на Magicbit този регулатор не е необходим. Тъй като захранва до 12V. Ние директно доставяме 7.4V за водача на двигателя.

Свържете всички компоненти съгласно схемата по -долу.

Стъпка 4: Настройка на софтуера

В кода използвахме PID библиотека за изчисляване на PID изход.

Отидете на следната връзка, за да го изтеглите.

www.arduinolibraries.info/libraries/pid

Изтеглете последната му версия.

За да получим по -добри показания на сензора, използвахме DMP библиотека. DMP означава процес на цифрово движение. Това е вградена функция на MPU6050. Този чип има интегриран модул за процес на движение. Така че е необходимо четене и анализ. След като генерира безшумни точни изходи към микроконтролера (в този случай Magicbit (ESP32)). Но има много работи в страната на микроконтролера, за да се вземат тези показания и да се изчисли ъгълът. Така че просто използвахме MPU6050 DMP библиотека. Изтеглете го, като отидете на следната връзка.

github.com/ElectronicCats/mpu6050

За да инсталирате библиотеките, в менюто на Arduino отидете на Tools-> include library-> add.zip library и изберете файла на библиотеката, който сте изтеглили.

В кода трябва правилно да промените ъгъла на зададената точка. Стойностите на PID константата са различни от робот до робот. Така че, когато настройвате това, първо задайте нулите на Ki и Kd и след това увеличете Kp, докато постигнете по -добра скорост на реакция. Повече Kp причини за повече превишения. След това увеличете стойността на Kd. Увеличавайте го винаги в много малко количество. Тази стойност обикновено е ниска от другите стойности. Сега увеличавайте Ki, докато получите много добра стабилност.

Изберете правилния COM порт и тип платка. качи кода. Сега можете да играете с вашия DIY робот.

Стъпка 5: Схеми

Схеми
Схеми

Стъпка 6: Код

#включва

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = невярно; // задайте true, ако DMP init е успешен uint8_t mpuIntStatus; // съдържа действителен байт за състояние на прекъсване от MPU uint8_t devStatus; // връщане на състоянието след всяка операция на устройството (0 = успех,! 0 = грешка) uint16_t packetSize; // очакван размер на DMP пакет (по подразбиране е 42 байта) uint16_t fifoCount; // преброяване на всички байтове в момента във FIFO uint8_t fifoBuffer [64]; // FIFO буфер за съхранение Quaternion q; // [w, x, y, z] кватернион контейнер VectorFloat гравитация; // [x, y, z] гравитационен вектор поплавък ypr [3]; // [криволичене, стъпка, ролка] контейнер за криволичене/стъпка/ролка и вектор на гравитацията double originalSetpoint = 172.5; двойна зададена стойност = originalSetpoint; двойно moveAngleOffset = 0,1; двоен вход, изход; int moveState = 0; double Kp = 23; // задайте P първо двойно Kd = 0,8; // тази стойност обикновено е малка двойна Ki = 300; // тази стойност трябва да бъде висока за по -добра стабилност PID pid (& вход, & изход, & зададена точка, Kp, Ki, Kd, DIRECT); // pid инициализира int motL1 = 26; // 4 пина за моторно задвижване int motL2 = 2; int motR1 = 27; int motR2 = 4; летливи bool mpuInterrupt = false; // указва дали прекъсването на прекъсването на MPU е високо void dmpDataReady () {mpuInterrupt = true; } void setup () {ledcSetup (0, 20000, 8); // настройка на pwm ledcSetup (1, 20000, 8); ledcSetup (2, 20000, 8); ledcSetup (3, 20000, 8); ledcAttachPin (motL1, 0); // pinmode на двигатели ledcAttachPin (motL2, 1); ledcAttachPin (motR1, 2); ledcAttachPin (motR2, 3); // присъединяване към I2C шина (библиотеката I2Cdev не прави това автоматично) #ако I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin (); Wire.setClock (400000); // 400kHz I2C часовник. Коментирайте този ред, ако имате затруднения при компилирането #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire:: setup (400, вярно); #endif Serial.println (F ("Инициализиране на I2C устройства …")); pinMode (14, INPUT); // инициализира серийна комуникация // (115200 избрано, защото е необходимо за изхода на Teapot Demo, но // наистина зависи от вас в зависимост от вашия проект) Serial.begin (9600); while (! Сериен); // изчакваме изброяването на Leonardo, други продължават незабавно // инициализираме устройството Serial.println (F ("Инициализиране на I2C устройства …")); mpu.initialize (); // проверява връзката Serial.println (F ("Тестване на връзките на устройства …")); Serial.println (mpu.testConnection ()? F ("MPU6050 връзката е успешна"): F ("MPU6050 връзката е неуспешна")); // зареждаме и конфигурираме DMP Serial.println (F ("Инициализиране на DMP …")); devStatus = mpu.dmpInitialize (); // предоставяте тук свои собствени жироизмествания, мащабирани за минимална чувствителност mpu.setXGyroOffset (220); mpu.setYGyroOffset (76); mpu.setZGyroOffset (-85); mpu.setZAccelOffset (1788); // 1688 фабрична настройка за моя тестов чип // уверете се, че работи (връща 0, ако е така) if (devStatus == 0) {// включете DMP, сега, когато е готов Serial.println (F ("Активиране на DMP … ")); mpu.setDMPEnabled (вярно); // активирайте откриването на прекъсвания на Arduino Serial.println (F ("Активиране на откриването на прекъсвания (Arduino външно прекъсване 0) …")); attachInterrupt (14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus (); // задаваме флага на DMP Ready, така че функцията main loop () да знае, че е добре да го използваме Serial.println (F ("DMP готов! Изчакване на първо прекъсване …")); dmpReady = вярно; // получаваме очаквания размер на пакета DMP за по -късно сравнение packetSize = mpu.dmpGetFIFOPacketSize (); // настройка на PID pid. SetMode (AUTOMATIC); pid. SetSampleTime (10); pid. SetOutputLimits (-255, 255); } else {// ГРЕШКА! // 1 = първоначалното зареждане на паметта е неуспешно // 2 = актуализациите на конфигурацията на DMP са неуспешни // (ако ще се прекъсне, обикновено кодът ще бъде 1) Serial.print (F ("DMP Initialization failed (code")); Serial. print (devStatus); Serial.println (F (")")); }} void loop () {// ако програмирането е неуспешно, не се опитвайте да правите нищо, ако (! dmpReady) се върне; // изчакваме прекъсване на MPU или допълнителни пакети, докато (! mpuInterrupt && fifoCount <packetSize) {pid. Compute (); // този период от време се използва за зареждане на данни, така че можете да го използвате за други изчисления motorSpeed (изход); } // нулиране на флага на прекъсване и получаване на INT_STATUS байт mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus (); // получавам текущ брой FIFO fifoCount = mpu.getFIFOCount (); // проверяваме за препълване (това никога не трябва да се случва, освен ако кодът ни е твърде неефективен) if ((mpuIntStatus & 0x10) || fifoCount == 1024) {// нулиране, за да можем да продължим чисто mpu.resetFIFO (); Serial.println (F ("FIFO препълване!")); // в противен случай проверете за прекъсване за готовност на DMP данни (това трябва да се случва често)} иначе ако (mpuIntStatus & 0x02) {// изчакайте правилната налична дължина на данните, трябва да е МНОГО кратко изчакване while (fifoCount 1 пакет наличен // (това ни позволява веднага да прочетем повече, без да чакаме прекъсване) fifoCount -= packetSize; mpu.dmpGetQuaternion (& q, fifoBuffer); mpu.dmpGetGravity (& gravity, & q); mpu.dmpGetYawPitchRoll (ypr, & q, & gravity); print ("ypr / t"); Serial.print (ypr [0] * 180/M_PI); // ъгли на 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; ако (PWM> = 0) {// посока напред L2 = 0; L1 = abs (PWM); R2 = 0; R1 = abs (PWM); ако (L1> = 255) { L1 = R1 = 255;}} иначе {// посока назад L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); ако (L2> = 255) {L2 = R2 = 255; }} // моторно задвижване ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1*0.97); // 0.97 е факт на скоростта или, тъй като десният двигател има по -висока скорост от левия, затова го намаляваме, докато скоростите на двигателя са равни ledcWrite (3, R2*0,97);

}

Препоръчано: