Делаем из обычного квадрокоптера — грузовой

Делаем из обычного квадрокоптера - грузовойИз этой статьи мы с вами узнаем, как установить на дрон подъемный механизм управляемый дистанционно. Такой дрон будет способен доставлять небольшие посылки. Дроны могут быстро доставлять товары практически в любое место, они предлагают более безопасную систему доставки и более высокий уровень эффективности.
Но есть также много недостатков, связанных с использованием дронов в качестве системы доставки. Некоторые из этих недостатков заключаются в том, что дроны по-прежнему относительно дороги и требуют технических навыков в работе с ними.
Мастер предлагает рассмотреть свой вариант сборки грузового дрона на базе готовой модели. Давайте посмотрим демонстрационный ролик.
Инструменты и материалы:-Ардуино Нано;-Квадрокоптер с поддержкой протоколов PPM и Betaflight;-Радиопередатчик и приемник;-Аккумулятор для дрона;-Двигатель постоянного тока;-Драйвер двигателя L293d;-Печатная плата;
-Шнурок;
-2 кубика Лего на 4 слота;-Соединительная муфта Lego Technic;-Ось Lego Technic (размер 4);-Паяльные принадлежности;-Инструмент для зачистки проводов;-Провода;-Мультиметр;-Электроизоляционная лента;-Двухсторонний скотч;
Программное обеспечение:
-Arduino Uno IDE;-Конфигуратор Betaflight;Шаг первый: теория и идея
В этом проекте используется теория из предыдущего проекта мастера, где он управлял дроном исключительно с Arduino Uno без использования обычного передатчика или приемника. Вместо передатчика он управлял дроном со своего телефона по Bluetooth.
Идея проекта состоит в том, чтобы сделать дрон-доставщик, у которого есть механизм лебедки, который может опускаться и забирать посылки, пока дрон висит в воздухе. Лебедка будет управляться тем же передатчиком, который используется для управления дроном.
Можно отправлять команды с платы Arduino на дрон, но для этого проекта нужно использовать как передатчик, так и приемник. Взаимодействие происходит следующим образом:
Передатчик отправит сигнал от оператора на приемник.
Затем приемник преобразует входной сигнал передатчика в сигнал PPM, и отправит его на плату Arduino.
Плата Arduino считывает сигнал PPM и разбивает его на значения отдельных каналов.
Затем плата Arduino преобразует, все значения отдельных каналов в один сигнал PPM и отправляет его на дрон.
Как только дрон получит сигнал PPM, он отреагирует соответствующим образом.
Делаем из обычного квадрокоптера - грузовойШаг второй: настройка дрона
Дальше нужно настроить дрон так, чтобы он обменивался данными по протоколу PPM. Большинство дронов в настоящее время по умолчанию настроены на использование протоколов S-bus или I-bus, потому что это намного эффективнее и надежнее, чем PPM. Но для обеспечения связи между Arduino и дроном протокол PPM — лучший вариант.
Подключите контроллер полета вашего дрона к компьютеру.
Откройте Betaflight и найдите вкладку Configuration tab».
Прокрутите вниз, пока не найдете параметр «Receiver Mode»..
Измените «Receiver Mode» на «PPM RX input»..
Нажмите «Save and Reboot» в правом нижнем углу экрана.
Теперь, когда квадрокоптер настроен, перейдем к следующему шагу.
Делаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойШаг третий: электроника
На этом этапе нужно подключить всю электронику.
Сначала нужно найти контакты 5V, Ground и PPM на контроллере полета. Чтобы найти их, нужно использовать схему распиновки от производителей, она выглядит примерно так. Она должна быть доступна, если найти свой конкретный контроллер полета в Интернете и перейти на официальный сайт производителя.
Первым делом необходимо подключить драйвер двигателя L293d к печатной плате. Припаиваем все провода к драйверу мотора L293d. См. фото 1.
Затем подключаем сигнальный контакт контроллера полета (FC) к контакту 2 платы Arduino, контакт заземления (GND) FC — к контакту платы Arduino, а контакт 5V FC — к контакту на плате Arduino. Все 3 этих контакта обычно расположены рядом друг с другом. Контакты 5V и GND будут использоваться для внешнего питания платы Arduino.
Подключаем контакт 5V и GND печатной платы к контактам 5V и GND на плате Arduino соответственно.
Затем нужно выполнить все подключения к приемнику дрона. Необходимо подключить 3 провода. Это земля (черный), 5В (красный) и сигнальный (желтый). Подключаем провод 5 В к контактной площадке 5 В на FC, провод заземления к контакту GND на FC, а сигнальный провод к контакту 3 на плате Arduino.
Теперь, когда первая часть пайки завершена, переходим к выполнению всех необходимых подключений драйвера двигателя L293d к плате FC и Arduino.
Подключаем контакт 1 драйвера двигателя к контактной площадке 5V FC.
Подключаем контакт 2 драйвера двигателя к контакту 6 платы Arduino.
Подключаем контакт 3 драйвера двигателя к одному из контактов двигателя постоянного тока.
Подключаем контакт 4 драйвера двигателя к контакту GND FC.
Подключаем контакт 6 драйвера двигателя к другому контакту двигателя постоянного тока.
Подключаем контакт 7 драйвера двигателя к контакту 7 платы Arduino.
Подключаем контакт 8 драйвера двигателя к клемме 5 В на FC.
После монтажа проверяем его правильность с помощью мультиметра.
Как только все заработает нормально, переходим к следующему шагу.
Делаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойШаг четвертый: код
Ниже находится основной код прошивки. Загружаем файлы и обязательно сохраняем все 5 файлов в одном каталоге.
Как только все будет сохранено, открываем «Delivery_Drone.ino» в Arduino IDE. Если «PPMEncoder.cpp» , «PPMEncoder.h» , «PPMReader.cpp» или «PPMReader.h» еще не добавлены в файл, нажимаем «Эскиз» -> «Добавить файл …» и добавляем необходимые файлы. Возможно в него, потребуется внести некоторые изменения, чтобы дрон мог летать плавно.
Мастер сделал комментарии в коде, но вот основные вещи, на которые нужно обратить внимание:
После открытия PPMEncoder.h, можно увидеть, что есть следующие переменные, которые могут нуждаться в изменении:
PPM_DEFAULT_CHANNELS — это количество каналов, которые мы хотим иметь на дроне.
PPM_PULSE_LENGTH_uS — это длина импульса. Возможно, придется изменить значение. Наиболее распространенные значения — 0,5 или 0,3.
PPM_FRAME_LENGTH_uS — это длина паузы разделения. Чем больше значение, тем стабильнее показания и выше задержка, но чем ниже значение, тем менее стабильны показания, и меньшая задержка.

Средство для восстановления суставов и укреплению тканей
10 часов назад
Средство способное обновить любую деревянную поверхность
10 часов назад

/* This is the header file, the only things that might need to be changed are:
 * PPM_DEFAULT_CHANNELS - This is the number of channels that you want to have on the drone
 * PPM_PULSE_LENGTH_uS - This is the length of the pulse, you might have to play around with the variable find the correct value
 * PPM_FRAME_LENGTH_uS - This is the length of the separation pause. The larger the value, the more stable the readings and the higher the latency, but
 *                       the lower the value, the less stable the readings and a lower latency. Try play around with this value until you find your sweet spot.
 */
#ifndef _PPMEncoder_h_
#define _PPMEncoder_h_

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define PPM_DEFAULT_CHANNELS 8

#define PPM_PULSE_LENGTH_uS 25
#define PPM_FRAME_LENGTH_uS 40000

class PPMEncoder {

  private:
    int16_t channels[10];
    int16_t err;
    uint16_t elapsedUs;

    uint8_t numChannels;
    uint8_t currentChannel;
    byte outputPin;
    boolean state;
  
    uint8_t onState;
    uint8_t offState;


  public:
    static const uint16_t MIN = 1000;
    static const uint16_t MAX = 2000;

    void setChannel(uint8_t channel, uint16_t value);
    void setChannelPercent(uint8_t channel, uint8_t percent);

    void begin(uint8_t pin);
    void begin(uint8_t pin, uint8_t ch);
    void begin(uint8_t pin, uint8_t ch, boolean inverted);

    void interrupt();
};

extern PPMEncoder ppmEncoder;

#endif

При открытии «PPMEncoder.cpp», может потребоваться изменить это значение:
err — это значение поможет сократить колебания показаний, которые можно получить на Betaflight.

/* In this section of code of the program. The only value that might need to be changed is: 
*  err - This value will help you trim the fluctuating readings that you may get on Betaflight even when you are not transmitting any values.
 */
#include "PPMEncoder.h"

PPMEncoder ppmEncoder;

void PPMEncoder::begin(uint8_t pin) {
  begin(pin, PPM_DEFAULT_CHANNELS, false);
}

void PPMEncoder::begin(uint8_t pin, uint8_t ch) {
  begin(pin, ch, false);
}

void PPMEncoder::begin(uint8_t pin, uint8_t ch, boolean inverted) {
  cli();

  // Store on/off-State in variable to avoid another if in timing-critical interrupt
  onState = (inverted) ? HIGH : LOW;
  offState = (inverted) ? LOW : HIGH;
  
  pinMode(pin, OUTPUT);
  digitalWrite(pin, offState);

  err = 8;
  state = true;
  elapsedUs = 0;
  currentChannel = 0;

  numChannels = ch;
  outputPin = pin;

  for (uint8_t ch = 0; ch < numChannels; ch++) {
    setChannelPercent(ch, 0);
  }

  TCCR1A = 0;

  OCR1A = 100;
  TCCR1B = (1 << WGM12) | (1 << CS11);
  TIMSK1 = (1 << OCIE1A); // enable timer compare interrupt

  sei();
}

void PPMEncoder::setChannel(uint8_t channel, uint16_t value) {
  channels[channel] = constrain(value, PPMEncoder::MIN, PPMEncoder::MAX);
}

void PPMEncoder::setChannelPercent(uint8_t channel, uint8_t percent) {
  percent = constrain(percent, 0, 100);
  setChannel(channel, map(percent, 0, 100, PPMEncoder::MIN, PPMEncoder::MAX));
}

void PPMEncoder::interrupt() {
  TCNT1 = 0;

  if (state) {
    digitalWrite(outputPin, onState);
    OCR1A = PPM_PULSE_LENGTH_uS * 2;

  } else {
    digitalWrite(outputPin, offState);

    if (currentChannel >= numChannels) {
      currentChannel = 0;
      elapsedUs = elapsedUs + PPM_PULSE_LENGTH_uS;
      OCR1A = (PPM_FRAME_LENGTH_uS - elapsedUs) * 2 - err;
      elapsedUs = 0;
    } else {
      OCR1A = (channels[currentChannel] - PPM_PULSE_LENGTH_uS) * 2 - err;
      elapsedUs = elapsedUs + channels[currentChannel];

      currentChannel++;
    }
  }

  state = !state;
}

ISR(TIMER1_COMPA_vect) {
  ppmEncoder.interrupt();
}

Эти файлы единственные файлы, которые возможно нужно будет отредактировать.
Основной код проекта на самом деле довольно простой. Настоящая конфигурация происходит в библиотеках, включенных в код. Это основной код проекта, в коде все прокомментировано.

/*
 * This code just initializes the drone and connect the PPM Reader to the PPM Encoder.
 * The real action happens in "PPMReader.cpp"
 */
#include "PPMReader.h"
#include "PPMEncoder.h"

// This is the output pin that will send to PPM Signal to the drone
#define OUTPUT_PIN 2 

//This pin will read the data from the Receiver
byte interruptPin = 3;

//This is how many channels the transmitter has. Most will have 8 channels
byte channelAmount = 8;

// The array that will store the channel values
int ch[9];

//The pins that are used to connect to the motor.

//This initializes the PPMReader software
PPMReader ppm(interruptPin, channelAmount);

void setup() { 
    // Set the pins to OUTPUT mode
    
    //Initialize the PPM Encoder
    ppmEncoder.begin(OUTPUT_PIN);

    //Set default channel values
    ppmEncoder.setChannelPercent(0, 50);
    ppmEncoder.setChannelPercent(1, 50);
    ppmEncoder.setChannelPercent(3, 50);
}

void loop() {
    // Read the latest valid values from all channels
    for (int channel = 1; channel <= channelAmount; ++channel) {
        ch[channel] = (ppm.latestValidChannelValue(channel, 0) - 1000)/10;
    }
    //Send the values of each channel to the drone via a Single PPM signal
    ppmEncoder.setChannelPercent(0, ch[1]);
    ppmEncoder.setChannelPercent(1, ch[2]);
    ppmEncoder.setChannelPercent(2, ch[3]);
    ppmEncoder.setChannelPercent(3, ch[4]);
    ppmEncoder.setChannelPercent(4, ch[5]);
    ppmEncoder.setChannelPercent(5, ch[6]);
    ppmEncoder.setChannelPercent(6, ch[7]);
    ppmEncoder.setChannelPercent(7, ch[8]);
    
}

Как уже упоминалось, в «основном» разделе кода Arduino мало что происходит. Код, отвечающий за управление шкивом, находится в файле «PPMReader.cpp», который отвечает за прием и сохранение значений каждого канала после того, как приемник получил эти значения от передатчика. Причина, по которой код находится в этом разделе, заключается в том, что, поскольку этот раздел кода работает с выводом прерывания, при изменении состояния двигателя шкива меньше задержка и больше надежность.
Ниже код "PPMReader.cpp"

Эффективное средство для борьбы с алкогольной зависимостью
9 часов назад
Мужской триммер для бороды и волос. -50%!
6 часов назад

/*
 * The reason why I put the code responsible for the motor control in this section is 
 * because there will be a shorter latency while transmitting signals to the drone.
 * The reason for that is because this section of the code runs with an interrupt pin. If you wish to change any part of the code, do it under
 * the function "PPMReader::handleInterrupt(void)"
 */
#include "PPMReader.h"

static PPMReader *PPMReader::ppm;

static void PPMReader::PPM_ISR(void) {
  ppm->handleInterrupt(); 
}


PPMReader::PPMReader(byte interruptPin, byte channelAmount):
    interruptPin(interruptPin), channelAmount(channelAmount) {
    // Setup an array for storing channel values
    rawValues = new unsigned [channelAmount];
    validValues = new unsigned [channelAmount];
    for (int i = 0; i < channelAmount; ++i) {
        rawValues[i] = 0;
        validValues[i] = 0;
    }
    m1 = 6, m2 = 7;
    s1 = 0, s2= 0;
    // Attach an interrupt to the pin
    pinMode(interruptPin, INPUT);
    pinMode(m1, OUTPUT);
    pinMode(m2, OUTPUT);
    
    if(ppm == NULL) {
        ppm = this;
        attachInterrupt(digitalPinToInterrupt(interruptPin), PPM_ISR, RISING);
    }
}

PPMReader::~PPMReader(void) {
    detachInterrupt(digitalPinToInterrupt(interruptPin));
    if(ppm == this) ppm = NULL;
    delete [] rawValues;
    delete [] validValues;
}

void PPMReader::handleInterrupt(void) {
    // Remember the current micros() and calculate the time since the last pulseReceived()
    unsigned long previousMicros = microsAtLastPulse;
    microsAtLastPulse = micros();
    unsigned long time = microsAtLastPulse - previousMicros;

    if (time > blankTime) {
        /* If the time between pulses was long enough to be considered an end
         * of a signal frame, prepare to read channel values from the next pulses */
        pulseCounter = 0;
        /* Channel 6 represents a 3-position toggle switch on the transmitter
         * Position 1 - Turn the pulley motor off. Value sent to Arduino - 1000
         * Position 2 - Turn the motor clockwise - Lower the Package. Value sent to Arduino - 1500
         * Position 3 - Turn the motor anticlockwise - Lift the Package. Value sent to Arduino - 2000
         */
        if(validValues[6] >= 1100){
          if(validValues[6] < 1600){
            s1 = 1, s2 = 0;
          }
          else{
            s1 = 0, s2 = 1;
          }
              digitalWrite(m1, s1);
              digitalWrite(m2, s2);
        }
        else{
            digitalWrite(m1, LOW);
            digitalWrite(m2, LOW);
        }
    }
    else {
        // Store times between pulses as channel values
        if (pulseCounter < channelAmount) {
            rawValues[pulseCounter] = time;
            if (time >= minChannelValue - channelValueMaxError && time <= maxChannelValue + channelValueMaxError) {
                validValues[pulseCounter] = constrain(time, minChannelValue, maxChannelValue);
            }
        }
        ++pulseCounter;
    }
}

unsigned PPMReader::rawChannelValue(byte channel) {
    // Check for channel's validity and return the latest raw channel value or 0
    unsigned value = 0;
    if (channel >= 1 && channel <= channelAmount) {
        noInterrupts();
        value = rawValues[channel-1];
        interrupts();
    }
    return value;
}

unsigned PPMReader::latestValidChannelValue(byte channel, unsigned defaultValue) {
    // Check for channel's validity and return the latest valid channel value or defaultValue.
    unsigned value;
    if(micros() - microsAtLastPulse > failsafeTimeout) return defaultValue;
    if (channel >= 1 && channel <= channelAmount) {
        noInterrupts();
        value = validValues[channel-1];
        interrupts();
        if(value < minChannelValue) return defaultValue; // value cannot exceed maxChannelValue by design
    }
    return value;
}

Все пять фрагментов можно скачать ниже и незабываем вносить изменения в соответствии со своим квадрокоптером.
Delivery_Drone.inoPPMEncoder.cppPPMEncoder.hPPMReader.cppPPMReader.hШаг пятый: сборка
Изначально для этого проекта мастер использовал Arduino Uno, но вскоре заменил его на Arduino Nano из-за экономии места.
Первый шаг - покрыть все открытые контакты каким-либо изолятором. Корпус некоторых дронов сделан из токопроводящих материалов, и эта мера предотвратит замыкание.
Обязательно нужно проложить провода двигателя через раму дрона к его нижней части. Здесь будет находиться лебедка.
Следующим шагом является установка платы Arduino и драйвера двигателя L293d в дрон.
Последний шаг - установить приемник на дрон.
Делаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойШаг шестой: лебедка
Для механизма лебедки мастер использовал несколько деталей Lego, старый шнурок и моторедуктор.
Сначала вырезаем в кубике Lego паз для шкива двигателя.
Делаем из обычного квадрокоптера - грузовойПрипаиваем провода двигателя к Arduino и помешаем его в кубик Lego.
Фиксируем его с помощью изоленты и двустороннего скотча.
Делаем из обычного квадрокоптера - грузовойНа вал двигателя надеваем соединитель оси Lego technic (или можно использовать подходящую трубку). Затем нужно закрепить на валу один конец шнурка, а остальное намотать.
Эта конструкция выдерживает максимум 1 кг веса.
Делаем из обычного квадрокоптера - грузовойДелаем из обычного квадрокоптера - грузовойШаг седьмой: управление дроном и лебедкой
Основным преимуществом этого дрона является его способность сбрасывать и забирать посылки, находясь в воздухе.
На изображении показан тип передатчика, который мастер использует для управления дроном. Он использует трехпозиционный тумблер, чтобы контролировать состояние вала двигателя. Ниже возможные действия двигателя в зависимости от того, в каком положении находится тумблер:
Положение-1: двигатель лебедки выключен
Положение-2: вал двигателя вращается по часовой стрелке, опуская пакет
Положение-3: вал двигателя вращается против часовой стрелки, поднимая пакет вверх.
Поскольку управление двигателем лебедки зависит от состояния тумблера, можно продолжать управлять дроном, переключаясь между различными состояниями двигателя.
Что касается управления дроном, то элементы управления остались прежними. Единственное дополнение - это функциональность трехпозиционного переключателя.
Делаем из обычного квадрокоптера - грузовойШаг восьмой: летные испытания
По словам мастера, квадрокоптер неплохо летал во время тестовых полетов и практически доставил посылку прямо к отметке «Х» . Единственное, на что следует обратить внимание, это то, что чем длиннее веревка, тем сложнее управлять дроном. На видео видно, как коптер доставляет посылку и отцепляет груз без вмешательства человека.
Это возможно, если не закреплять конец веревки на валу двигателя, а просто намотать ее. Тогда при опускании груза веревка соскользнет с вала. Правда в этом случае есть риск потерять груз по пути.

Как улучшить зрение за короткий срок
7 часов назад
Мужской триммер для бороды и волос. -50%!
7 часов назад

Читайте также