Робо-футболист от начинающих. Соревнования в МФТИ. Android & Arduino & Bluetooth

    Данная статья является полу-сиквелом к работе Love, Death and Robots «Машинка на Arduino, управляемая Android-устройством по Bluetooth, — полный цикл», состоящей из двух частей (раз, два). Вещи, описанные там, были немного доработаны-переделаны, а сам робот из ездящей машинки превратился в футболиста. В общем, есть интересный материал о том, как делать не надо.

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

    Физическая часть


    За основу взяты все те же принципы, описанные в первой статье:

    • бутерброд из Arduino Uno и Motor Shield.
    • два мотора, подключенных к Motor Shield.

    А вот изменения:

    • появилась ударная часть, как ни странно, отвечающая за удар по мячу.
    • корпус теперь полностью свой, распечатанный на 3D-принтере.

    Корпус


    Форма — круг, в который вмещается и плата, и два колеса. Удлинение для части, где будет стоять ударная сила.



    При конструировании подобного обратить внимание на:

    • Высокие бортики. Роботы во время игры сталкиваются, бортики защищают не только ваши провода, но и ваших соперников от ваших проводов.
    • Центр тяжести и устойчивость. Центр тяжести конечно там, где плата. Колеса расположены возле нее, поэтому проскальзывать не будут. Плюс сверху на плату кладется батарейка.
    • Чтобы робот не клевал носом или задом, ставим и туда и сюда шарики, идущие в наборе от амперки (если их нет, можно заменить на любую другую скользящую конструкцию).
    • Жесткость конструкции. Платформа не должна провисать под тяжестью плат и моторов. Не поскупитесь, либо используйте твердые материалы (фанеру), либо усильте пластмассовую конструкцию рейками



    А теперь основные глупости


    Шарики, добавленные для отсутствия «клевания», поднимали платформу так, что колеса не доставали до пола. Чтобы этого избежать, либо используем колеса большего диаметра, либо укорачиваем опорные конструкции. В общем, просчитываем это заранее!

    Ударная часть. Она не бьет. Бьет, но недостаточно круто. В нашей первой модели стояла серво-машинка, к которой подсоединялась деталь, похожая на ковш снегоуборочной машины. Меняя положение сервы (от 0 до 30 градусов) можно сымитировать удар. Но сервы оказались медленными, поэтому удар выходит на двоечку.

    Выхода два: добавлять рывок при ударе или заменять сервы на соленоиды. Первый вариант — увеличить импульс можно за счет подачи скорости на колеса во время удара. На практике так: пользователь нажимает кнопку удара, робот стартует с места (чуть-чуть) и одновременно делать удар.

    Второй вариант — соленоиды толкают ударную часть и тут все зависит от мощности (скорости) толчка, которая в свою очередь зависит от характеристик соленоида.



    Программная часть


    По доброй традиции, которой вот уже одна статья, разделим этот раздел на две части. Сначала Android-приложение, потом скетч Arduino.

    Android


    Напомню, приложения написано мной с нуля. За прошедшие полгода немного понял в этом деле поболее, поэтому опишу до чего допер додумался.

    Во-первых, пойдем к упрощению. Теперь протокол общения следующий: «открывающий символ» + «значение» + «закрывающий символ» (Чтобы понять, как я получаю эти значения и о чем вообще речь, смотри полный разбор приложения здесь). Это работает как для значения скорости, так и для угла. Поскольку тип удара только один, ему такие мудрости не нужны, поэтому команда состоит из одного символа "/" (об команде удара через абзац).

       private void sendCommand(String speed, String angle) {
                String speedCommand = "#" + speed + "#"; //добавляем начальный и конечный символ
                String angleCommand = "@" + angle + "@";
    
                try {
                    outputStream.write(speedCommand.getBytes()); //отсылаем обе команды
                    outputStream.write(angleCommand.getBytes());
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
    

    Типичная команда будет выглядеть так: #125#@180@, где 125 — скорость, а 180 — угол. Конечно, это можно еще упростить, но одной из задач было сохранить легкость и читабельность, чтобы потом это можно было легко объяснить, в том числе детям.

    Появилась новая команда sendHit(), которая срабатывает во время нажатия на кнопку «Удар». Она отправляет один знак "/". Поскольку обычный bluetooth 2.0+ не страдает от данных, поступаемых одновременно, то есть умеет ставить их в очередь и не терять, нам это контролировать не надо. Если же вы собираетесь работать с Bluetooth Low Energy 4.0+ (ну вдруг), там уже очередь надо будет организовывать вручную, иначе данные будут теряться.

    ...
      bHit = findViewById(R.id.b_high_hit); //находим кнопку удара
      bHit.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    threadCommand.sendHit(); //при нажатии вызываем отправку команды "удар"
                }
            });
    ...
    
    private void sendHit() {
                try {
                    outputStream.write("/".getBytes()); //отправляем один символ
                }catch (Exception e) {
                    e.printStackTrace(); 
                }
            }
    }
    

    Arduino


    Так поменялся протокол отправки команд, также поменялся алгоритм приема. Он упростился. Также добавился один if, отслеживающий удар. Полный разбор скетча здесь.

    bt.read() считывает один символ. Если он равен "#", значит начинаются символы скорости. Считываем их до тех пор, пока не появится закрывающий символ "#". Здесь нельзя использоваться цикл for, потому что заранее неизвестна длина скорости (она может быть и однозначным, и двузначным, и трехзначным числом). Полученное значение записываем в переменную.

    То же самое происходит с поворотом. После того как считаны и скорость, и угол, передаем все в функцию turn(int speed, int angle).

    void loop() {
     
      if(BTSerial.available() > 0) {//если есть присланные символы
         char a = BTSerial.read(); //считываем первый символ
    
        if(a == '#') { //начинается скорость
          sp="";
          char b = BTSerial.read();
          while( b != '#') { 
            //пока не закрывающий символ, плюсуем символы в переменную
            sp+=b;
            b = BTSerial.read();
          }
         
        } else if (a == '@') {//начинается угол
          val = "";
          char b = BTSerial.read();
          while(b != '@') { //пока не закрывающий символ
          val+=b; //прибавляем символ к переменной
          b = BTSerial.read();
        }
        turn(val.toInt(), sp.toInt()); //скорость и угол считаны, запускаем действие
         
        } else if (a == '/')  { //оп, нужно сделать удар
          Serial.println(a);
          servo.write(30); //делаем удар
          delay(150);
          servo.write(0); //возращаем в исходную позицию
        }
        
        lastTakeInformation = millis();
      } else {
    
         if(millis() - lastTakeInformation > 150) {
          //если команды не приходили больше 150мс
         //останавливаем робота
         lastTakeInformation = 0;
         analogWrite(speedRight, 0);
         analogWrite(speedLeft, 0);
         }
         
      }
    
      delay(5);
    }
    
    

    Функция turn() определяет, в какую сторону двигаться (вперед, назад) и куда поворачивать (вправо, влево, прямо). Ограничение if(speed > 0 && speed < 70) нужно для того, чтобы робот не тормозился, если байты потеряны. Столкнулся с этим, когда повысил скорость передачи (игрался с задержками в 100-300мс между командами) — иногда значение скорости не доходило и превращалось в 0, 40 (хотя, например, на самом деле отправлялось 240). Костыль, но работает.
    Можно назвать «защитой от неконтролируемых факторов».

    void turn(int angle, int speed) {
      
      if(speed >= 0 && speed < 70) return;
      
      if(speed > 0) {
         digitalWrite(dirLeft, HIGH);
         digitalWrite(dirRight, HIGH);
      } else if (sp < 0) {
          digitalWrite(dirLeft, LOW);
          digitalWrite(dirRight, LOW);
      }
      
      if(angle > 149) {
            analogWrite(speedLeft, speed);
            analogWrite(speedRight, speed - 65); //поворот вправо
      } else if (angle < 31) { 
            analogWrite(speedLeft, speed - 65); //поворот влево
            analogWrite(speedRight, speed);
      } else {
          analogWrite(speedLeft, speed);
          analogWrite(speedRight, speed);
      }
       
    }
    

    Соревнования в МФТИ вместо итога


    С нашим роботом мы отправились на соревнования по робо-футболу, которые устраивало и проводилось в университете МФТИ, г. Долгопрудный, 14.04.2019. Нам удалось выйти в 1\4 финала, но дальше не продвинулись.



    Сам процесс интересен был нам, а здесь опишу выводы, которые удалось сделать, посмотрев на робота в поле:

    • нужно мощнее. Желательно четыре колеса или более мощные двигатели и другие колеса. Хотя, конечно, именно четырехколесные модели смотрелись выигрышней
    • управление не кайф. Нужно переводить робота на танковый разворот (разворот на одной точке за счет колес, крутящихся в противоположные стороны), иначе слишком большой радиус разворота. Да и в общем вариант с четырьмя стрелочками, а не кругом с пропорциональной скоростью, для футбола предпочтительнее. Описанный вариант лучше подходит для гонок, где едешь беспрерывно, а тут нужна четкость (повернулся на 10 градусов вокруг своей оси, нацелился на мяч и зажал кнопку вперед. а вот потом, когда уже захватил мяч, хотелось бы гибко маневрировать, а тут нужно пропорциональная скорость… нужно как-то это дело совмещать).

    Замечаниям и предложениям буду очень рад. Под предыдущими статьями комментарии порой интереснее самой статьи. За работу спасибо мне, Саше и Дане.
    • +15
    • 1,8k
    • 7
    Поделиться публикацией

    Похожие публикации

    Комментарии 7

      0
      >управление не кайф.
      Вот тут лучше показать на видео игровой процесс.

      Имхо трехколесный (два ведущих и одно подруливающее [ в виде шара]) может развернуться на месте. Поэтому думаю, что нынешний вариант ходовой части хороший.
        0
        Двухколесный конечно маневренный. И проще считать ходовую геометрию. И проще в управлении.
        Но 2 колеса скорее всего менее адекватно ведут себя при препятствии и ударе.
        Техническое противоречие: надо 4 точки опоры для удара/столкновения с другим роботом. Надо 2 колеса для маневренности.
        Изобретательская задача.
        Как решения: выдвигающиеся упоры. Динамики на видео не вижу, но может быть.
        3 колеса — сложности с танковым разворотом.
        4 колеса близко друг к другу (расстояние между колес меньше ширины колеи) -для танкового разворота.
        2 пары разновеликих колес. Нужны энкодеры для пересчета оборотов разновеликих колес или шаговики (чем черт не шутит?).
        Ну и банально поднять вес и увеличить мощность моторов…
          0
          Как вариант специальная функция поворота — подъем задней оси, чтобы корпус оперся на подруливающее колесо(шар).
            0
            … оперся на подруливающее колесо(шар)...

            Я так понимаю шары есть. Они не дают опрокинуться. Однако зафиксировать на плоскости тяжелЬше… Там что-то типа упоров для крана: и не дает опрокинуться и не дает сдвинуться по плоскости.
          0

          безусловно можно, и делается на двух колесах довольно легко (может и не идеально "на пяточке", но вполне допустимо). одно колесо назад, другое вперёд — готово.

          0
          Для большей четкости и резкости удара делайте механизм, как у автоматического керна (или можете целиком этот керн использовать). Суть в том, что электромагнит не непосредственно бьет по мячу, а сжимает пружину. Потом в конце хода пружина освобождается и бьет.
            +1
            Лучше если электромагнит освобождает сжатую пружину, а потом моторчик её опять сжимает и заряжает механизм для следующего использования.
            Жаль что соревнования на порядок примитивнее, чем это возможно в наше время, было б интереснее, если машинки были автономны

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое