Оглавление:

Самобалансирующийся робот от Magicbit: 6 шагов
Самобалансирующийся робот от Magicbit: 6 шагов

Видео: Самобалансирующийся робот от Magicbit: 6 шагов

Видео: Самобалансирующийся робот от Magicbit: 6 шагов
Видео: Балансирующий робот своими руками на ардуино и шаговых двигателях 2024, Июль
Anonim

Из этого туториала Вы узнаете, как создать самобалансирующегося робота с помощью платы разработчика Magicbit. Мы используем magicbit в качестве платы для разработки в этом проекте, основанном на ESP32. Поэтому в этом проекте можно использовать любую плату разработки ESP32.

Запасы:

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

Шаг 1: история

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

Привет, ребята, сегодня в этом уроке мы узнаем о немного сложных вещах. Речь идет о самобалансирующемся роботе, использующем Magicbit с Arduino IDE. Итак, приступим.

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

Шаг 2: теория и методология

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

Чтобы сбалансировать робота, сначала мы получаем данные от какого-либо датчика для измерения угла робота относительно вертикальной плоскости. Для этого мы использовали MPU6050. После получения данных с датчика рассчитываем наклон к вертикальной плоскости. Если робот находится в прямом и сбалансированном положении, угол наклона равен нулю. В противном случае угол наклона может быть положительным или отрицательным. Если робот наклонен вперед, он должен двигаться вперед. Также, если робот наклонен в обратную сторону, робот должен двигаться в обратном направлении. Если этот угол наклона большой, скорость отклика должна быть высокой. Напротив, угол наклона мал, тогда скорость реакции должна быть низкой. Для управления этим процессом мы использовали специальную теорему под названием PID. PID - это система управления, которая используется для управления многими процессами. PID обозначает 3 процесса.

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

У каждой системы есть вход и выход. Таким же образом эта система управления также имеет некоторый ввод. В этой системе управления это отклонение от стабильного состояния. Мы назвали это ошибкой. В нашем роботе погрешность - это угол наклона от вертикальной плоскости. Если робот сбалансирован, угол наклона равен нулю. Таким образом, значение ошибки будет равно нулю. Поэтому выходной сигнал ПИД-системы равен нулю. Эта система включает три отдельных математических процесса.

Первый - ошибка умножения от числового прироста. Это усиление обычно называют 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

Выходной сигнал функции ПИД ограничен диапазоном 0–255 (разрешение ШИМ 8 бит), и он будет подаваться на двигатели в виде сигнала ШИМ.

Шаг 3: Настройка оборудования

Настройка оборудования
Настройка оборудования

Теперь это часть настройки оборудования. Конструкция робота зависит от вас. При проектировании корпуса робота необходимо учитывать его симметричность относительно вертикальной оси, лежащей в оси двигателя. Аккумулятор расположен внизу. Поэтому робота легко балансировать. В нашей конструкции мы крепим доску Magicbit вертикально к корпусу. Мы использовали два мотор-редуктора на 12 В. Но вы можете использовать любые мотор-редукторы. это зависит от размеров вашего робота.

Когда мы обсуждаем схему, она питается от батареи Lipo 7,4 В. Magicbit использовал 5V для питания. Поэтому мы использовали регулятор 7805, чтобы отрегулировать напряжение батареи до 5 В. В более поздних версиях Magicbit этот регулятор не нужен. Потому что он питается до 12 В. Мы напрямую поставляем 7,4 В. для драйвера двигателя.

Подключите все компоненты в соответствии со схемой ниже.

Шаг 4: установка программного обеспечения

В коде мы использовали библиотеку PID для расчета вывода PID.

Перейдите по следующей ссылке, чтобы загрузить его.

www.arduinolibraries.info/libraries/pid

Загрузите последнюю версию.

Для получения более точных показаний сенсора мы использовали библиотеку DMP. DMP означает процесс цифрового движения. Это встроенная функция MPU6050. Этот чип имеет интегрированный блок процесса движения. Так что нужно читать и анализировать. После он генерирует бесшумные точные выходные данные на микроконтроллер (в данном случае Magicbit (ESP32)). Но со стороны микроконтроллера много работы, чтобы снять эти показания и вычислить угол. Так что просто мы использовали библиотеку MPU6050 DMP. Загрузите его, перейдя по следующей ссылке.

github.com/ElectronicCats/mpu6050

Чтобы установить библиотеки, в меню Arduino перейдите в инструменты-> включить библиотеку-> добавить.zip-библиотеку и выберите файл библиотеки, который вы загрузили.

В коде вы должны правильно изменить заданный угол. Значения констант 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 = false; // устанавливаем истину, если инициализация DMP прошла успешно 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 gravity; // [x, y, z] вектор гравитации float ypr [3]; // [рыскание, тангаж, крен] рыскание / тангаж / крен контейнер и вектор силы тяжести double originalSetpoint = 172.5; двойная уставка = исходная уставка; double movingAngleOffset = 0,1; двойной ввод, вывод; int moveState = 0; double Kp = 23; // установить P first double Kd = 0.8; // это значение обычно маленькое double Ki = 300; // это значение должно быть высоким для большей стабильности PID pid (& input, & output, & setpoint, Kp, Ki, Kd, DIRECT); // инициализация pid int motL1 = 26; // 4 контакта для моторного привода int motL2 = 2; int motR1 = 27; int motR2 = 4; volatile 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); // режим вывода двигателей ledcAttachPin (motL2, 1); ledcAttachPin (motR1, 2); ledcAttachPin (motR2, 3); // подключаемся к шине I2C (библиотека I2Cdev не делает этого автоматически) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin (); Wire.setClock (400000); // Тактовая частота I2C 400 кГц. Прокомментируйте эту строку, если возникают трудности с компиляцией #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire:: setup (400, true); #endif Serial.println (F («Инициализация устройств I2C…»)); pinMode (14, ВХОД); // инициализируем последовательную связь // (выбрано 115200, потому что это требуется для вывода Teapot Demo, но это действительно зависит от вас // в зависимости от вашего проекта) Serial.begin (9600); в то время как (! серийный); // ждем перечисления Леонардо, остальные сразу же продолжат // инициализировать устройство 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, чтобы функция main loop () знала, что его можно использовать Serial.println (F («DMP готов! Ожидание первого прерывания…»)); dmpReady = true; // получить ожидаемый размер пакета DMP для последующего сравнения packetSize = mpu.dmpGetFIFOPacketSize (); // настройка PID pid. SetMode (АВТОМАТИЧЕСКИЙ); pid. SetSampleTime (10); pid. SetOutputLimits (-255, 255); } else {// ОШИБКА! // 1 = начальная загрузка памяти не удалась // 2 = не удалось обновить конфигурацию DMP // (если что-то сломается, обычно код будет 1) Serial.print (F («Ошибка инициализации DMP (код»)); Serial. печать (devStatus); Serial.println (F (")")); }} void loop () {// если программирование не удалось, не пытайтесь ничего делать if (! dmpReady) return; // ждем прерывания MPU или доступных дополнительных пакетов while (! 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 (это должно происходить часто)} else if (mpuIntStatus & 0x02) {// ждать правильной доступной длины данных, должно быть ОЧЕНЬ короткое ожидание while (доступен пакет fifoCount 1 // (это позволяет нам немедленно читать больше, не дожидаясь прерывания) fifoCount - = packetSize; mpu.dmpGetQuaternion (& q, fifoBuffer); mpu.dmpGetGravity (& gravity, & q); mpu.dmpGetYawPitchRoll (ypr, & q, & gravity); #if LOG_INPUT Serial. print ("ypr / t"); Serial.print (ypr [0] * 180 / M_PI); // углы Эйлера 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) {// направление вперед L2 = 0; L1 = abs (PWM); R2 = 0; R1 = abs (PWM); if (L1> = 255) { L1 = R1 = 255;}} else {// направление назад L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); if (L2> = 255) {L2 = R2 = 255; }} // моторный привод ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1 * 0,97); // 0,97 - факт скорости или, поскольку правый двигатель имеет более высокую скорость, чем левый, поэтому мы уменьшаем ее до тех пор, пока скорости двигателя не станут равными ledcWrite (3, R2 * 0.97);

}

Рекомендуемые: