Данная статья является полу-сиквелом к работе Love, Death and Robots «Машинка на Arduino, управляемая Android-устройством по Bluetooth, — полный цикл», состоящей из двух частей (раз, два). Вещи, описанные там, были немного доработаны-переделаны, а сам робот из ездящей машинки превратился в футболиста. В общем, есть интересный материал о том, как делать не надо.
Предыдущая инструкция была разделена на две части: программную и физическую. Изменений в обоих направлениях было не так много, поэтому в этот раз все в едином экземпляре. Кратко буду напоминать, зачем нужна описываемая часть, но для полного понимания лучше пробежаться по первым двум частям.
За основу взяты все те же принципы, описанные в первой статье:
А вот изменения:
Форма — круг, в который вмещается и плата, и два колеса. Удлинение для части, где будет стоять ударная сила.
При конструировании подобного обратить внимание на:
Шарики, добавленные для отсутствия «клевания», поднимали платформу так, что колеса не доставали до пола. Чтобы этого избежать, либо используем колеса большего диаметра, либо укорачиваем опорные конструкции. В общем, просчитываем это заранее!
Ударная часть. Она не бьет. Бьет, но недостаточно круто. В нашей первой модели стояла серво-машинка, к которой подсоединялась деталь, похожая на ковш снегоуборочной машины. Меняя положение сервы (от 0 до 30 градусов) можно сымитировать удар. Но сервы оказались медленными, поэтому удар выходит на двоечку.
Выхода два: добавлять рывок при ударе или заменять сервы на соленоиды. Первый вариант — увеличить импульс можно за счет подачи скорости на колеса во время удара. На практике так: пользователь нажимает кнопку удара, робот стартует с места (чуть-чуть) и одновременно делать удар.
Второй вариант — соленоиды толкают ударную часть и тут все зависит от мощности (скорости) толчка, которая в свою очередь зависит от характеристик соленоида.
По доброй традиции, которой вот уже одна статья, разделим этот раздел на две части. Сначала Android-приложение, потом скетч Arduino.
Напомню, приложения написано мной с нуля. За прошедшие полгода немного понял в этом деле поболее, поэтому опишу до чегодопер додумался.
Во-первых, пойдем к упрощению. Теперь протокол общения следующий: «открывающий символ» + «значение» + «закрывающий символ» (Чтобы понять, как я получаю эти значения и о чем вообще речь, смотри полный разбор приложения здесь). Это работает как для значения скорости, так и для угла. Поскольку тип удара только один, ему такие мудрости не нужны, поэтому команда состоит из одного символа "/" (об команде удара через абзац).
Типичная команда будет выглядеть так: #125#@180@, где 125 — скорость, а 180 — угол. Конечно, это можно еще упростить, но одной из задач было сохранить легкость и читабельность, чтобы потом это можно было легко объяснить, в том числе детям.
Появилась новая команда sendHit(), которая срабатывает во время нажатия на кнопку «Удар». Она отправляет один знак "/". Поскольку обычный bluetooth 2.0+ не страдает от данных, поступаемых одновременно, то есть умеет ставить их в очередь и не терять, нам это контролировать не надо. Если же вы собираетесь работать с Bluetooth Low Energy 4.0+ (ну вдруг), там уже очередь надо будет организовывать вручную, иначе данные будут теряться.
Так поменялся протокол отправки команд, также поменялся алгоритм приема. Он упростился. Также добавился один if, отслеживающий удар. Полный разбор скетча здесь.
bt.read() считывает один символ. Если он равен "#", значит начинаются символы скорости. Считываем их до тех пор, пока не появится закрывающий символ "#". Здесь нельзя использоваться цикл for, потому что заранее неизвестна длина скорости (она может быть и однозначным, и двузначным, и трехзначным числом). Полученное значение записываем в переменную.
То же самое происходит с поворотом. После того как считаны и скорость, и угол, передаем все в функцию turn(int speed, int angle).
Функция turn() определяет, в какую сторону двигаться (вперед, назад) и куда поворачивать (вправо, влево, прямо). Ограничение if(speed > 0 && speed < 70) нужно для того, чтобы робот не тормозился, если байты потеряны. Столкнулся с этим, когда повысил скорость передачи (игрался с задержками в 100-300мс между командами) — иногда значение скорости не доходило и превращалось в 0, 40 (хотя, например, на самом деле отправлялось 240). Костыль, но работает.
Можно назвать «защитой от неконтролируемых факторов».
С нашим роботом мы отправились на соревнования по робо-футболу, которые устраивало и проводилось в университете МФТИ, г. Долгопрудный, 14.04.2019. Нам удалось выйти в 1\4 финала, но дальше не продвинулись.
Сам процесс интересен был нам, а здесь опишу выводы, которые удалось сделать, посмотрев на робота в поле:
Замечаниям и предложениям буду очень рад. Под предыдущими статьями комментарии порой интереснее самой статьи. За работу спасибо мне, Саше и Дане.
Предыдущая инструкция была разделена на две части: программную и физическую. Изменений в обоих направлениях было не так много, поэтому в этот раз все в едином экземпляре. Кратко буду напоминать, зачем нужна описываемая часть, но для полного понимания лучше пробежаться по первым двум частям.
Физическая часть
За основу взяты все те же принципы, описанные в первой статье:
- бутерброд из 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 градусов вокруг своей оси, нацелился на мяч и зажал кнопку вперед. а вот потом, когда уже захватил мяч, хотелось бы гибко маневрировать, а тут нужно пропорциональная скорость… нужно как-то это дело совмещать).
Замечаниям и предложениям буду очень рад. Под предыдущими статьями комментарии порой интереснее самой статьи. За работу спасибо мне, Саше и Дане.
Комментарии (7)
AntonSor
15.04.2019 22:43Для большей четкости и резкости удара делайте механизм, как у автоматического керна (или можете целиком этот керн использовать). Суть в том, что электромагнит не непосредственно бьет по мячу, а сжимает пружину. Потом в конце хода пружина освобождается и бьет.
shalm
16.04.2019 07:41Лучше если электромагнит освобождает сжатую пружину, а потом моторчик её опять сжимает и заряжает механизм для следующего использования.
Жаль что соревнования на порядок примитивнее, чем это возможно в наше время, было б интереснее, если машинки были автономны
Javian
>управление не кайф.
Вот тут лучше показать на видео игровой процесс.
Имхо трехколесный (два ведущих и одно подруливающее [ в виде шара]) может развернуться на месте. Поэтому думаю, что нынешний вариант ходовой части хороший.
fndrey357
Двухколесный конечно маневренный. И проще считать ходовую геометрию. И проще в управлении.
Но 2 колеса скорее всего менее адекватно ведут себя при препятствии и ударе.
Техническое противоречие: надо 4 точки опоры для удара/столкновения с другим роботом. Надо 2 колеса для маневренности.
Изобретательская задача.
Как решения: выдвигающиеся упоры. Динамики на видео не вижу, но может быть.
3 колеса — сложности с танковым разворотом.
4 колеса близко друг к другу (расстояние между колес меньше ширины колеи) -для танкового разворота.
2 пары разновеликих колес. Нужны энкодеры для пересчета оборотов разновеликих колес или шаговики (чем черт не шутит?).
Ну и банально поднять вес и увеличить мощность моторов…
Javian
Как вариант специальная функция поворота — подъем задней оси, чтобы корпус оперся на подруливающее колесо(шар).
fndrey357
Я так понимаю шары есть. Они не дают опрокинуться. Однако зафиксировать на плоскости тяжелЬше… Там что-то типа упоров для крана: и не дает опрокинуться и не дает сдвинуться по плоскости.
DolgopolovDenis Автор
безусловно можно, и делается на двух колесах довольно легко (может и не идеально "на пяточке", но вполне допустимо). одно колесо назад, другое вперёд — готово.