Рука-манипулятор для соревневаний. Серво без delay ? Глючит энкодер.
- Войдите на сайт для отправки комментариев
Ср, 09/11/2016 - 22:27
Здавствуйте форумчане.
Есть такие вот соревнования для подростков:
http://http://robolymp.ru/rules-and-regulations/manipulyatory/
С сыном собрали из робота-пешехода вот это:
Код:
#include <Wire.h> #include <Multiservo.h> Multiservo PLECHO,LOKOT,ZAHVAT; //ДАТЧИК ЦВЕТА int s0 = 8; int s1 = 9; int s2 = 12; int s3 = 11; int out = 10; int red,green,blue=0; int CVET;//хранилище кода цвета //МОТОР LEGO int IN1 = 5;// Input1 подключен к выводу 5 int IN2 = 6;// Input2 подключен к выводу 6 int ENA = 7;// ENA подключен к выводу 7-скорость int NAPRAVLENIE;//0-вправо,1-влево volatile unsigned int encoder = 300;//начальное значение шагов энкодера int Zahvat=150;//180-сжатие,115-открыть int del=1000; volatile unsigned long lastmillis = 0; void setup() { Wire.begin(); pinMode (ENA, OUTPUT); pinMode (IN1, OUTPUT); pinMode (IN2, OUTPUT); pinMode(s0, OUTPUT); pinMode(s1, OUTPUT); pinMode(s2, OUTPUT); pinMode(s3, OUTPUT); pinMode(out, INPUT); digitalWrite(s0, HIGH); digitalWrite(s1, HIGH); Serial.begin(9600); PLECHO.attach(0); LOKOT.attach(1); ZAHVAT.attach(4); PLECHO.writeMicroseconds(600); LOKOT.writeMicroseconds(1250); ZAHVAT.write(120); attachInterrupt(4,Probeg,CHANGE);//на 19-ом пине меги } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<< void loop >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> void loop() { // все срабатывает одновременно ,а нужно последоветельно . // delay убрал во всей программе тк не срабатывал энкодер. PODNYAT(); MOTOR_RIGHT(600); OPUSTIT(); ZAXVAT_VZYAT(); PODNYAT(); } void Probeg() //поворот робота(подразумевается,что этим смогу определить положение) { if( NAPRAVLENIE == 0 ){//когда поворачивается вправо encoder = encoder+1; } else{ encoder = encoder-1; } } void MOTOR_RIGHT(int d)// значение,на сколько повернется робот { if(encoder < d) { digitalWrite (IN1, HIGH); digitalWrite (IN2, LOW); analogWrite(ENA,160); NAPRAVLENIE=0; } else if(encoder > d) { digitalWrite (IN1, LOW); digitalWrite (IN2, HIGH); analogWrite(ENA,160); NAPRAVLENIE=1; } else if(encoder = d){ digitalWrite (IN1, LOW); digitalWrite (IN2, LOW); } } //<<<<<< ВЗЯТЬ КУБИК >>>>>>>>>> void ZAXVAT_VZYAT(){ for (int i=120;i<=150;++i){ ZAHVAT.write(i); //delay(20); }} //<<<<<< ОПУСТИТЬ КУБИК >>>>>>>>>> void ZAXVAT_OTPUSTIT(){ for (int i=150;i>=120;--i){ ZAHVAT.write(i); // delay(20); }} //<<<<<< ПОДНЯТЬ ЛОКОТЬ >>>>>>>>>> void PODNYAT(){ for (int i=1250;i>=900;--i){ LOKOT.write(i); // delay(20); }} //<<<<<< ОПУСТИТЬ ЛОКОТЬ >>>>>>>>>> void OPUSTIT(){ for (int i=900;i<=1250;++i){ LOKOT.write(i); // delay(20); }}
Прерывания и серво вместе корректно не работают. По отдельности ( тест мотора и тест серво ) работают правильно. Мотор с энкодером встает на правильные градусы. Сервы то же .А в одной программе работать не хотят. Убрал все delay . Все команды начинают работать одновременно. А нужно чтобы плавно и последовательно .
Может кто то делал подобную программу?
Подскажите пожалуйста.
Если добавлять detachInterrupt, то вообще вырубается энкодер и мотор крутит бесконечно
Скажу честно, всю программу перечитывать не буду, тем более что не оригинал. Ошибка здесь скорее всего заключается в системном подходе.
И так есть функции которые вызываются.
Функции которые отвечаю за определённые суставы, допустим у нас есть плечо и локоть (ну там кисть и т.д. сейчас не суть).
Что делаете вы, вы говорите "Согни плечо до 90 град, согни локоть на 70 град, согни кисть на 30 град", а затем думаете что он согнёт сначала плечо, потом локоть, потом кисть - и видимо логично выходит "фиг вам". А именно - сгибается всё и сразу - и именно так всё и должно быть.
Вам в логике надо прописывать. Согни плечо на 90 град, когда он будет согнут - сгибай локоть до 70 град....
Вы в программе по сути задаёте координату к которой надо прийти - устанавливая углы.
Хотите - определённую траекторию движения, сначала это, потом то, потом вон то вон.
Если у вас нет обратной связи с моторов (а её скорее всего нет), то вы не можете знать, достиг мотор заданного угла или нет. Тогда вам на помощь приходит либо делей, либо флаг. Делей - это зло, лучше флаг.
Делайте проверку условий и вводити их. К примеру знаете что надоизменить угол с 90 на 60, и знаете что скорость вращения 1 градус в секунду для двигателя. Значит, 90-60 = 30 , задержку надо в 90 сек (конечно в реальности в разы меньше). Следовательно прописываете условия, "прочесть текущее значени, если текущее значение и требуемое различны, тогда: поднять флаг, прочесть текущее время, запомнить время, текущий угол - требуемый угол = модуль числа, модуль числа, получили градусы премещения, зная градус и количество градусов в секунду - вычислили сколько секунд будет идти движение, в счётчик добавляем отсечку по времени. Дальше у нас идёт цикл по условиям, если текущее время < время старта + время перемещения - нифига не делаем (крутимся в петле, по сути это тот же делей, но тут вопрос оптимизации вы можеет выполнять другие действия или не выполнять пока флаг не опустится), как только текущее время > время старта+время перемещения, опустить флаг - сделать следующее действие - тут уже действует следующий сустав.
Надеюсь понятно.
В такой логике суставы будут работать последовательно, поскольку у каждого в петле будет крутиться. Так же будет приоритет, т.е. определённая последовательность в изменении координаты. К примеру если первым идёт плечо - в нём локоть - в нём кисть, и вы скажите координаты, сначала двинется плечо, когда встанет на место, потом только локоть, потом только кисть.
Если захотите изменить кисть, надо будет задать нулевые изнения суставов с высшим приоритетом.
Надеюсь хоть что-то было понятно и полезно.
Большое спасибо за Ваш коментарий.
Прочитал, и понял что моих знаний явно не достаточно для решения этой задачи.
А что значит ''поднять флаг, опустить флаг" ?
Подскажите пожалуйста.
Как плавно вращать серву и задоно определять ее положение посмотрите здесь https://www.youtube.com/watch?v=yVxjzu__ukI&t=581s
Спасибо. Очень хорошее видео.
Евгений79, почитайте за "автоматное программирование". Это то, что Вам пытался разъяснить Phoenix.
Каждый сустав может представлять из себя такой конечный автомат: "останов (в положении..)", "движение (к положению)", "завершено (движение)". Процедура такого автомата представляет из себя обычный switch(){} или набор if(){}else{} блоков действий от заданного состояния:
Если состояние "останов", то проверяем есть ли команда и выполняем её. Команда запускает мотор или чего ещё, включает условие (например время завершения или условие по энкодеру) и изменяет состояние автомата на "движение".
Если состояние "движение", то проверяем достижение условия (или каких-то катастроф. случаев - аварий -> переход в состояние "ошибка движения") и пока оно не выполнено - возвращаем управление. Как только условие сработало, если надо то останавливаем мотор и изменяем свое состояние на "завершено".
Состояние завершено может выставлять флаги или вызывать внешние (callback) функции для уведомления остальной части программы что действие завершено и можно делать что-то ещё и переходим к состоянию "останов".
Управляющая программа может быть реализована точно также в виде своего конечного автомата. Надо только правильно расписать его состояния и переходы между ними. Весь loop() при автоматном программировании сводится к поочередному вызову набора конечных автоматов.
Суть автоматного программирования - разделение ситуаций на состояния и введение перемнной "текущее состояние автомата", которая используется в едином блоке ветвтления по состояниям автомата. И второй момент - конечный автомат - фиговина, которая сама себя переводит из одного состояния в другое или это делается извне командами управления автоматом. Собственно и фсё. :)
Пример реализации такого подхода есть в моей библиотеке тут: https://github.com/dimanus/ARHAT_H смотрите файлы tsc.h, tsc.c это как раз подход по унификации для автоматного программирования. Там же есть вариант реализации работы с прерываниями и управления по энкодерам или таймингам, в частности съем показаний с узв. датчика HCSR-04 по прерываниям (без ожидания эхо как в pulseIn()) - hcsr04.h, pcint.h, но там реализация достаточно мутная - попытка имитировать темплейты из С++ средствами С, зато есть примеры.
В целом, автоматное программирование - это то, что "доктор прописал" для задач управления. И, по большому счету, управляющий микроконтроллер - это не "AVR", "ARM" или что-то ещё. Такой подход должен быть зашит непосредственно в систему команд. Вот тогда - это действительно управляющий камень. А так .. баловство это всё.