Под брендом GeekTimes
На съемной панели была наклейка Habrahabr. Лишившись этой панели для уменьшения веса, я брендировал робот в ручную. GT вперед!
Аккуратное подключение всех элементов системы
1-Arduino 2560
2-RC-приемник
3,4-Драйвера коллекторных двигателей
5-Блок реле
6-Батарея
Логика работы системы
Управление роботом(ровером) осуществляется средством радиоуправления двумя приемо-передающими устройствами. Первый пульт основного оператора используется для управления движением робота и орудиями, второй оператор дублирует управление орудиями. На ровере оба приемника соединены к Arduino, в которой программно считаем ШИМ от приемника, включаются/выключаются реле для управления клапанами пневмосистемы и выдается ШИМ для драйверов двигателей.
#include <math.h>
#define STEP_PIN1 12
#define STEP_PIN2 10
#define RELEPIN0 2
#define RELEPIN1 3
#define RELEPIN2 4
#define RELEPIN3 5
#define RELEPIN4 6
#define RELEPIN5 7
#define RELEPIN6 8
#define RELEPIN7 9
#define a0 = A0;
#define a1 = A1;
#define a2 = A2;
#define a3 = A3;
#define a4 = A4;
#define a5 = A5;
//Второй пульт
//Временного отключили второй пульт
//#define a6 = A8;
//#define a7 = A9;
int ch0,ch1,ch2,ch3,ch4,ch5,ch6,ch7;
//Задаем максимум и минимум для ШИМа с первого приемника
const int min0=1094;
const int min1=1094;
const int max0=813;
const int max1=813;
int speed1,speed2,speedmot1,speedmot2,sd1,sd2;
byte ch2st,ch3st,ch4st,ch5st,ch6st,ch7st;
byte tormozcount1=0;
byte tormozcount2=0;
byte wasdir1=2;
byte wasdir2=2;
byte skolkotormozit = 7;
int skolkotormozim=0;
void setup() {
Serial.begin(9600);
Serial.println("start");
pinMode(a0, INPUT);
pinMode(a1, INPUT);
pinMode(a2, INPUT);
pinMode(a3, INPUT);
pinMode(a4, INPUT);
pinMode(a5, INPUT);
//Временного отключили второй пульт
//pinMode(a6, INPUT);
//pinMode(a7, INPUT);
pinMode(STEP_PIN1, OUTPUT);
pinMode(STEP_PIN2, OUTPUT);
pinMode(RELEPIN0, OUTPUT);
pinMode(RELEPIN1, OUTPUT);
pinMode(RELEPIN2, OUTPUT);
pinMode(RELEPIN3, OUTPUT);
pinMode(RELEPIN4, OUTPUT);
pinMode(RELEPIN5, OUTPUT);
pinMode(RELEPIN6, OUTPUT);
pinMode(RELEPIN7, OUTPUT);
}
void loop() {
ch0 =pulseIn(a0, HIGH,30000);
if(ch0<900){
//Если нет сигнала с пульта
Serial.println("first no signal");
digitalWrite(STEP_PIN1,LOW);
digitalWrite(STEP_PIN2,LOW);
digitalWrite(RELEPIN0,LOW);
digitalWrite(RELEPIN1,LOW);
digitalWrite(RELEPIN2,LOW);
digitalWrite(RELEPIN3,LOW);
digitalWrite(RELEPIN4,LOW);
digitalWrite(RELEPIN5,LOW);
digitalWrite(RELEPIN6,LOW);
digitalWrite(RELEPIN7,LOW);
}
//Снимаем показания с ШИМа от первого приемника
ch1 =pulseIn(a1, HIGH);
ch2 =pulseIn(a2, HIGH);
ch3 =pulseIn(a3, HIGH);
ch4 =pulseIn(a4, HIGH);
ch5 =pulseIn(a5, HIGH);
//Временного отключили второй пульт
ch6 =1250; //pulseIn(a6, HIGH,2020);
ch7 =1250; //pulseIn(a7, HIGH,2020);
if(ch2>1500){
ch2st=1;
}else{
ch2st=0;
}
if(ch3>1500){
ch3st=0;
}else if(ch3>0){
ch3st=1;
}
if(ch4>1500){
ch4st=1;
}else{
ch4st=0;
}
if(ch5>1500){
ch5st=1;
}else{
ch5st=0;
}
//Каждый канал второго пульта управляет двумя орудиями
if(ch6>1800){
ch6st=2;
}else if(ch6<1200){
ch6st=1;
}else{
ch6st=0;
}
if(ch7>1800){
ch7st=2;
}else if(ch7<1200){
ch7st=1;
}else{
ch7st=0;
}
//Первое орудие----------------------------------------------
if(ch2st==1 or ch6st==1){
digitalWrite(RELEPIN0,HIGH);
}else{
digitalWrite(RELEPIN0,LOW);
}
//Второе орудие----------------------------------------------
if(ch3st==1 or ch6st==2){
digitalWrite(RELEPIN1,HIGH);
}else {
digitalWrite(RELEPIN1,LOW);
}
//Третье орудие----------------------------------------------
if(ch4st==1 or ch7st==1){
digitalWrite(RELEPIN2,HIGH);
}else {
digitalWrite(RELEPIN2,LOW);
}
//Если есть сигнал с первого пульта
if(ch0>0){
ch0=(ch0<55 && ch0>45)?50:ch0;
ch1=(ch1<55 && ch1>45)?50:ch1;
ch0 =ch0-min0;
ch1 =ch1-min1;
//Получаем данные о положении стиков управления от 0 до 100
ch0 =map(ch0,0,max0,0,100);
ch1 =100-map(ch1,0,max1,0,100);
//Превращаем данные о напралении движения в необходимые значения для каждого двигателя
speed1=round((ch0-50)*(abs(ch1-50)+50)/50);
speed2=round((ch1-50)*(abs(ch0-50)+50)/50);
//корректируем значения и считаем скорость моторов
speedmot1=speed1+speed2;
speedmot2=speed1-speed2;
// Выставляем ограничения
speedmot1=(speedmot1>100)?100:speedmot1;
speedmot2=(speedmot2>100)?100:speedmot2;
speedmot1=(speedmot1<-100)?-100:speedmot1;
speedmot2=(speedmot2<-100)?-100:speedmot2;
speedmot1=(speedmot1<20 && speedmot1>-20)?0:speedmot1;
speedmot2=(speedmot2<20 && speedmot2>-20)?0:speedmot2;
sd1=map(abs(speedmot1),0,100,0,255);
sd2=map(abs(speedmot2),0,100,0,255);
//Вывод значений для диагностики
Serial.print(ch0);
Serial.print(";");
Serial.print(ch1);
Serial.print(";");
Serial.print(";");
Serial.print(speed1);
Serial.print(";");
Serial.print(speed2);
Serial.print(";");
Serial.print(speedmot1);
Serial.print(";");
Serial.print(speedmot2);
Serial.print(";");
Serial.print(sd1);
Serial.print(";");
Serial.print(sd2);
Serial.print(";wasdir");
Serial.print(wasdir1);
Serial.print(";");
Serial.print(wasdir2);
Serial.println(";");
//Выдаем ШИМ для драйвера, если мы не в состоянии торможения
if(tormozcount1==0 and tormozcount2==0){
if(sd1<220){
analogWrite(STEP_PIN1,sd1);
}else{
digitalWrite(STEP_PIN1,HIGH);
}
if(sd2<220){
analogWrite(STEP_PIN2,sd2);
}else{
digitalWrite(STEP_PIN2,HIGH);
}
}else{
digitalWrite(STEP_PIN1,LOW);
digitalWrite(STEP_PIN2,LOW);
}
//торможение, выбег ротора
if(speedmot1<40 and speedmot1>-40){
Serial.println("Tormoz menshe 40 1 dvig;");
digitalWrite(RELEPIN3,HIGH);
digitalWrite(STEP_PIN1,LOW);
tormozcount1=0;
}else{
digitalWrite(RELEPIN3,LOW);
}
if(speedmot2<40 and speedmot2>-40 ){
Serial.println("Tormoz menshe 40 2 dvig;");
digitalWrite(RELEPIN4,HIGH);
digitalWrite(STEP_PIN2,LOW);
tormozcount2=0;
}else{
digitalWrite(RELEPIN4,LOW);
}
//Если мы тормозим больше циклов, чем нужно для тормоза считаем, что мы неподвижны
if(speedmot1<40 and speedmot1>-40 and speedmot2<40 and speedmot2>-40){
skolkotormozim++;
}else{
skolkotormozim=0;
}
if( skolkotormozim>skolkotormozit){
wasdir1=2;
wasdir2=2;
digitalWrite( RELEPIN5, LOW);
digitalWrite( RELEPIN6, LOW);
Serial.println("Stop for 10 cicles");
}
//смена направления хода
if((speedmot1 < -10 and wasdir1==1) || (speedmot1 >=5 and wasdir1==0)){
//Если новое направление не соответствует старому
if(tormozcount1<=0){
//если счетчик 0 тормозим
Serial.println("Tormoz vibeg rotora 1 dvig;");
digitalWrite(STEP_PIN1,LOW);
digitalWrite(RELEPIN3,HIGH);
tormozcount1=skolkotormozit;
}
if(tormozcount1>0){
//Если уже тормозим
digitalWrite(STEP_PIN1,LOW);
tormozcount1--;
}
if(tormozcount1==1){
//если оттормозили
tormozcount1--;
digitalWrite(RELEPIN3,LOW);
Serial.println("Change 1dvig ");
if(speedmot1 < 0){
wasdir1=0;
digitalWrite( RELEPIN5, HIGH);
Serial.println("vzad");
}else{
wasdir1=1;
digitalWrite( RELEPIN5, LOW);
Serial.println("vpered");
}
}
}
if((speedmot2 < -10 and wasdir2==1) || (speedmot2 >=5 and wasdir2==0)){
//Если новое направление не соответствует старому
if(tormozcount2<=0){
//если счетчик 0 тормозим
Serial.println("Tormoz vibeg rotora 2 dvig;");
digitalWrite(STEP_PIN2,LOW);
digitalWrite(RELEPIN4,HIGH);
tormozcount2=skolkotormozit;
}
if(tormozcount2>0){
//Если уже тормозим
digitalWrite(STEP_PIN2,LOW);
tormozcount2--;
}
if(tormozcount2==1){
//если оттормозили
tormozcount2--;
digitalWrite(RELEPIN4,LOW);
Serial.print("Change 2dvig ");
if(speedmot2 < 0){
wasdir2=0;
digitalWrite( RELEPIN6, HIGH);
Serial.println("vzad");
}else{
wasdir2=1;
digitalWrite( RELEPIN6, LOW);
Serial.println("vpered");
}
}
}
// Если долго никуда не ехали меняем направление сразу
if(speedmot1 < -40 and wasdir1==2){
wasdir1=0;
digitalWrite( RELEPIN5, HIGH);
Serial.println("Reverse 1 motor");
}
if(speedmot1 > 40 and wasdir1==2){
wasdir1=1;
digitalWrite( RELEPIN5, LOW);
Serial.println("Forward 1 motor");
}
if(speedmot2 < -40 and wasdir2==2){
wasdir2=0;
digitalWrite( RELEPIN6, HIGH);
Serial.println("Reverse 2 motor");
}
if(speedmot2 > 40 and wasdir2==2){
wasdir2=1;
digitalWrite( RELEPIN6, LOW);
Serial.println("Forward 2 motor");
}
/*
//Диагностика
Serial.print(tormozcount1);
Serial.print(";");
Serial.print(tormozcount2);
Serial.print(";");
Serial.print(wasdir1);
Serial.print(";");
Serial.print(wasdir2);
Serial.print(";");
*/
//delay(1000);
}else{
//Если нет сигнала с пульта
}
}
В первой версии робота мы использовали шаговые двигатели NEMA 43, для них скетч было написать несколько сложнее, т.к. приходилось либо подсчет ШИМа с приемников выводить в функцию по таймеру, либо управление ШД. Для этих целей я использовал библиотеку TimerOne.
#include <TimerOne.h>
В функции setup инициируем таймер и прикрепляем к нему функцию отправки на контроллер ШД пульса на совершение одного шага.
Timer1.initialize(500000); // initialize timer1, and set a 1/2 second period
Timer1.pwm(9, 512,300); // setup pwm on pin 9, 50% duty cycle
Timer1.attachInterrupt(callback);
Функция отправки импульса
void callback()
{
digitalWrite(STEP_PIN1, digitalRead(STEP_PIN1) ^ 1);
delayMicroseconds(10);
digitalWrite(STEP_PIN1, digitalRead(STEP_PIN1) ^ 1);
}
В ходе основного цикла программы меняем частоту таймера в зависимости от необходимой скорости.
Timer1.pwm(9, 512,speedmot1);
В случае остановки двигателя отключаем функцию от таймера
Timer1.detachInterrupt();
Конструкция и сжигание Arduino
Конструкцию продумывали и испытывали с целью создать жесткую конструкцию.
Мне кажется на кадре ниже зафиксирован момент, когда при использовании сварки кабель от Arduino 2560 попал на корпус. В результате этого у нас сгорел UART и мы не могли более производить перепрошивку через USB. Самым быстрым решением было перейти на Arduino Uno, которая оказалась под рукой. Отказались от пульта второго оператора, портов хватило в самый раз.
Тест пневмосистемы
Из всех планируемых орудий пришлось оставить только лифт, так как посчитали его самым эффективным. Пневмосистему тестировали от компрессора 6 атм. В боевом состоянии мы уже использовали баллон с CO2 и давлением после редуктора в 10 атм, так же заменили трубку на больший диаметр.
Тест орудия
Учимся ездить на роботе
На одном из таких тестов мы сожгли полевики на контроллерах. На другом залипли реле, переключающие реверс. Затем в код ардуино я ввел выбег ротора, некое время ожидания перед сменой направления движения двигателя. Робот стал хуже в управлении, но контролеры целые.
Уличные тесты робота
Epic Fail
Стоит сразу отметить, что у нас не было весов. Мы просто поднимали робота все вместе, никто не хотел говорить, что ему тяжело и мы все дружно кивали и соглашались, что робот легкий.
Организаторы, особенно хранители арены очень серьезно относятся к вопросу безопасности, многие роботы просто не прошли квалификацию перед боем. При взвешивании оказалось, что наш робот весит 161.5 кг., при максимально разрешнных 105. Питер Редмонд на отрез отказался выпускать нас на сцену. Мы вынуждены были снять всю пневмосистему, орудия, снять юбку, которая защищала нас от подбрасывания, снять защитную стенку и заменить ее на оргстекло из которого сделаны стенки арены. Из 6 аккумуляторов от 3 пришлось отказаться.
Мы прошли квалификацию, но на арене уже могли только таранить. Нас прозвали вытяжкой, а нашего противника холодильником. Наверное, когда говорят «Первый блин комом» имеют ввиду именно такой сценарий развития событий, но мы получили удовольствие.
Видео с Бронебота
Приятного просмотра видео с боя.
P.S.: Немного расстроила организация выступления, т.к. не было никакого внимания на команду, собиравшую робота и на операторов, которые управляли им. Мы готовили костюмы, хотели быть масонами с масонской пирамидой, а получилась просто вытяжка, которая ездила по сцене…
P.P.S.: Мы уже думали полностью менять корпус и концепцию робота к следующим соревнованиям, но «За одного битого двух небитых дают». С нашими шрамами от «Открывашки» корпус ценен как история. На корпусе подписи Питера Редмонда и других гостей из Ирландской Лиги, организаторов и других участников.
P.P.P.S.: Следите за нашей командой вк. Готовьтесь к конкурсу роботов-газонокосилок. На конкурс заявлено более 10 команд со всей России. Нам предложили проводить конкурс в Сколково.
Комментарии (19)
kumbr_87
02.11.2015 17:59Вместо радиолюбительского приемника можно использовать сразу что нибудь типа NRF24L01 или если нужна большая надежность то NRF24L01+PA+LNA SMA Antenna Wireless, дальность до километра, сразу выводит данные в цифровом виде на последовательный порт ардуины, т. е. пропадает необходимость конвертировать ШИМ. Правдля тогда придется приделывать конвертацию к пульту, либо делать свой пульт, либо управлять например подключив еще одну ардуину с передатчиком к ноуту. Из плюсов — канал связи двухсторонний и можно отправлять назад данные мониторинга и прочую дребедень.
И еще, вот планировалось использовать подъемник, но его запуск проводился вручную как я понимаю, а не запрещает регламент делать автоматизацию? например поставить ИК датчик расстояния чтоб подъемник сам запускался когда робот подъехал к предмету, ведь реакция датчика будет быстрее чем у человека, плюс убираются задержки канала в итоге оператору останется только таранить роботов врага а робот уже сам будет их максимально быстро пытаться подкинуть :)
И как я понял аккумуляторы свинцовые? Липошные будут весить при той же емкости в 3 раза меньше и выдавать токи гораздо большие, цена правда кусается да и спецэффекты при подбитии аккумулятора будут красивые :Dwebzuweb
02.11.2015 18:37Организаторы выделили очень ограниченный бюджет, мы искали золотую середину. Аккумы от бесперебойников по 1.100 руб. 12В 9А/ч были в не конкуренции + они гелиевые, а это было важно по регламенту.
Насчет управления, я хотел сделать главному масону посох и в посохе разместить пульт на arduino с декоративными кнопками, связь пробовал через xy-mk-5v, но на это нужно было очень много времени.SADKO
03.11.2015 00:47А с этого момента можно подробней, а если секрет то в личку?
Каков был бюджет, и на сколько можно его превышать за счёт своих средств?
Эх, по уму, надо битвы роботов делать а-ля хактоны, собираются команды, получают равное количество денег, и делают что хотят в условиях ограниченного бюджета и времени.
Alexsey
02.11.2015 18:06P.S.: Немного расстроила организация выступления, т.к. не было никакого внимания на команду, собиравшую робота и на операторов, которые управляли им. Мы готовили костюмы, хотели быть масонами с масонской пирамидой, а получилась просто вытяжка, которая ездила по сцене…
Шоу действительно во многом вышло слабоватым по сравнению с BattleBots, сложилось впечатление что роботы вышли только для показухи и операторы очень старались отделаться минимумом урона. Постоянное появление Can Opener'а с минимальным участием подтверждает эту теорию. Надеюсь в феврале будет лучше.webzuweb
02.11.2015 18:39Абсолютно согласен, это был реслинг, а никак не поножовщина в Бутово… А всем хотелось жести…
SADKO
02.11.2015 23:37А квалификацию прошли лишь самые безопасные боты?
Там вроде бы заявлялись участники с ударным, вращающимся ротором, или вообще всё было условно безопасным?
Тот крендель с тисаком, даже не разу не покусился на откровенно торчащие колёса.webzuweb
03.11.2015 11:36Ударные вращающийся ротор запретили, оставили ребятам только цепь.
Тот крендель с тисаком, даже не разу не покусился на откровенно торчащие колёса.
Это шоу, они за одну команду выступают. Разбить оппонента было бы эффектно, но экономически не целесообразно.
GubkaBob
02.11.2015 19:15Вопрос — у робота сломалось управление или управление было трудным? Почему он так долго разворачивается и неуправляем при движении вперед?
Всегда интересовал этот момент при просмотре боев, потому что часто такое наблюдаю.webzuweb
02.11.2015 19:21У нас конкретно проблема была в том, что мы сняли 3 батареи, которые были подключены параллельно. Учитывая еще и то, что мы много потратили заряда пока решили проблемы перед допуском на арену, тока стало недостаточно для работы двигателей.
У нас все колеса ведущие, при развороте из-за большого трения колеса блокировались.
Эту ошибку мы исправим. Мы думали, что нужно обеспечить максимальное пятно контакта и зацеп, но оказалось, что ирландцы наоборот стараются найти какой-оптимум. Т.е. лучше чтобы колеса пробуксовывали, чем блокировались.
daspisch
Спасибо Вам за интересные статьи и с нетерпением ждём Ваших новых роботов.
Удачи в этом нелёгком и интересном деле!
webzuweb
Спасибо!
У меня есть идея сделать роботизированный протез для детей-инвалидов и дарить его нуждающимся. Мне понравилась идея ребят, но я не понимаю из чего складывается цена в 100.000 руб.
Просьба к тем, кто может мне помочь в разработке 3D-модели для последующей 3D-печати откликнуться. Планирую вместо использования тягового усилия серво-привод, сделать прототип на Arduino, а команду к движению кисти получать от такого датчика закрепленного на предплечии.
Можно привлекать российских известных личностей, чтобы они дарили малышу этот подарок.
Стоимость роботизированного протеза может быть до 5.000 руб., а не 1.000.000 руб.
Если есть желающие поучаствовать в проекте — просьба написать.
webinside
Попробуйте собрать прототип самой простой модельки.
Когда я это сделал, то понял, что не все так просто.
Если «железо» можно купить в Китае, то «мозги» прописать — это очень не простая вещь.
Хороший софт — сейчас основная проблема.
webzuweb
Согласен с Вами, именно поэтому и хочу попробовать. Я ведь с этой целью и написал комментарий, найти единомышленников. А что Вы сделали?
SADKO
Расходы на разработку и подготовку мелкосерийного производства, вот и набегает сотка, легко… + стоимость длинных денег и рисков.
Ценник можно радикально снизить за счёт большой серии, но боюсь что объём рынка здесь ограничен так что не развернуться.
В рамках благотворительной акции, если вам удастся сделать серийные корпус+механику с розничной ценой до 5000 :-) доступной для родителей детей инвалидов, без всяких там звездей, госфондов и прочих примазавшихся, то с меня электроника, и всё что за ней кроется!
Причём дизайн могу сделать такой что BOM будет просто смешным, даже по сравнению с китайским хоббийным хламом, просто нужно хорошо знать предметную область, а не пытаться тупо сложить из готовых кубиков не вдаваясь в подробности…
Тема то по сути старая, ещё мой дед с бионической рукой ходил, лампы — рэле, мотор — концевики вот и вся автоматика.
Пропорциональное управление можно сделать полностью аналоговым, а можно цифровым, можно вообще сделать тактильный фидбэк, что в разы повышает качество жизни пользователя. Только правда в том что нафиг всё это никому не нужно, увы…
… у инвалидов тоже своя правда есть, они быстро адаптируются к ситуации, а электронными протезами восторгаются лишь те кто получил их сразу, а для этой целевой аудитории ценник не принципиален и есть готовые решения.
webzuweb
Здравствуйте!
Что Вы имеете в виду под серийные корпус+механику с розничной ценой до 5000 :-)
3Д-модель проектирую и рисую.
Остается стоимость печати+сервопривод+жилы от троса.
Что такое
Причём дизайн могу сделать такой что ?BOM?
… у инвалидов тоже своя правда есть, они быстро адаптируются к ситуации, а электронными протезами восторгаются лишь те кто получил их сразу, а для этой целевой аудитории ценник не принципиален и есть готовые решения.
Моя идея в том чтобы просто дарить немного радости детям, я хочу сделать эти протезы в виде руки железного человека, человека-паука, халка, принцессы.
SADKO
Bill Of Materials — список компонентов, я к тому что не обязательно брать крутые усилители, если точно знаешь что усиливать.
В данном, конкретном случае, на современной элементной базе можно легко сделать усилители биопотенциалов из «говна и палок», так что бы уложиться в бюджет…
Если по приколу, то я не прочь присоединиться, тем более что были практические наработки.
Но тут ещё есть момент, радовать эта штука начинает не всегда сразу, ей надо учиться пользоваться, и культя должна быть сформирована грамотно, а не абы как. Хотя можно и от мышц заводиться.