![]() |
RoboClub - Проекты Where AI meet the real world |
![]() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
![]() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]()
|
"Делаем робота вместе!": наша практикаБлагодарим
всех, кто участвует в обсуждении этого проекта! КонцепцияСовременные технологии позволяют решать сложные задачи достаточно
простыми средствами. Предупреждения: Обсуждение этого проекта (как конструктивных решений, так и хода его выполнения) по-прежнему продолжается на форуме. Вводный курс по микроконтроллерам и их программированию можно найти здесь. Шасси
Система управления
Управление двигателямиУправление обоими двигателями построено на одной микросхеме L293D
(отечественный аналог - КР1128КТ4А). Схема включения - типовая для
подобного случая (см. спецификацию)
На фото показан один из каналов, второй -
идентичен. МикроконтроллерОсновной критерий при выборе микроконтроллера в данном случае - простота использования для решения наших задач (с точки зрения быстродействия, объема памяти, энергопотребления и пр. здесь может быть использован практически любой современный микроконтроллер), поэтому останавливаемся на семействе AVR фирмы Atmel. Как нам кажется, при отсутствии опыта работы с контроллерами, начинать проще именно с них; если же опыт есть, то обсуждаемые идеи легко могут быть повторены с использованием знакомых вам средств.
Все, что нужно, чтобы контроллер начал работать - подсоединить к тактовому генератору кварцевый кристалл и установить разъем для подключения программатора. Есть разные средства для разработки программ для AVR, как коммерческие, так и свободно распространяемые. Выбор зависит от ваших возможностей и предпочтений. Подключение контроллера
Несколько слов о питании. Работа двигателей вызывает
сильные электрические помехи, поэтому питание "электронной части" лучше,
по возможности, отделить от питания двигателей. Идеальный вариант -
использовать независимые батареи. Если напряжение питания двигателей
больше, чем требуемое напряжения питания контроллера, можно сделать отвод
от батареи: к примеру, в нашем случае двигатели можно питать от полного
набора из шести батарей, а для питания контроллера сделать отвод от
четвертой батареи. Теперь определяемся с датчиками. Порт В не трогаем: входы аналогового
компаратора (AIN0, AIN1) резервируем на будущее, три оставшихся линии
PB2..PB7 могут понадобиться для подключения дополнительных выходных
устройств, например, индикатора, пьезодинамика и пр. Программирование
Обратите внимание, что если оба сигнала управления двигателем (например, "вперед" и "назад") выключениы, либо оба включены - двигатель будет отключен. Структура программы Прежде всего, уточним алгоритм работы программы: при рассеянном
освещении - "свободный поиск", при появлении источника света - движение к
источнику, при столкновении с препятствием - отъезд.
Максимально упрощая ситуацию, откажемся на первых порах от управления скоростью движения и углом поворота. Тогда получим следующие режимы движения:
Если ваши двигатели подключены по-другому, либо вы используете другую конструкцию (к примеру, когда поворот осуществляется за счет разницы скоростей вращения "бортовых" двигателей), то и "ваша" таблица должна выглядеть иначе. Реализация "свободного поиска" С тем, чтобы наша конструкция "ожила" как можно раньше, начнем со
"свободного поиска". А те, кто хочет получить результат побыстрее, могут просто запрограммировать некую последовательность команд, которая внешне будет смотреться как "случайное блуждание". Попробуем составить таблицу вероятностей переходов:
Блок отъезда Блок отъезда от препятствия реализовать очень просто: устанавливаем на
модель передний и задний контактные датчики - "бамперы" (пример
конструкции простого бампера см. в разделе "Компоненты и
конструкции"). Один из контактов обоих датчиков подключаем к "земле",
а другие контакты подключаем к выводам контроллера INT0 и INT1 (PD2 и
PD3). Программно подключаем к этим линиям "подтягивающие" резисторы. Таким
образом, при отсутствии сигнала эти входы будут установлены в "1", а при
замыкании контакта - будут "сбрасываться" в "0", что и вызовет прерывание.
В принципе, при необходимости "экономить" линии прерываний, можно завести все "бамперы" на одну линию, а то, какой именно сработал, определять по тому, в каком направлении робот двигался перед столкновением. Движение к источнику света Движение на свет также для начала реализуем в простейшем варианте: подключим пару фоторезисторов между входами PD0, PD1 и "землей". Программно подключим к этим входам "подтягивающие" резисторы. Пока освещенность фоторезисторов мала, их сопротивление составляет насколько сотен килоом, поэтому за счет "подтягивающих" резисторов на входах будет высокий уровень. По мере роста освещенности сопротивление резисторов будет падать, и также падать будет напряжение на соответствующих входах, пока не достигнет порога нижнего уровня. Когда это произойдет на данном входе установится логичеcкий "0". Изменить степень чувствительности модели к свету можно поставив переменный резистор последовательно с фоторезистором (чтобы уменьшить чувствительность), либо параллельно (чтобы увеличить ее). Тогда, если оказывается "засвеченным" один из датчиков, программа дает
команду на поворот в его сторону, пока не "сработает" второй датчик. После
этого робот начнет двигаться на источник (т.е. реализуется классическая
реакция клинотаксиса). Если в процессе один из датчиков
"потеряет" источник света, робот выполнит необходимую коррекцию курса.
Если же источник перестанут "видеть" оба датчика - робот вернется к
свободному поиску. Что у нас получилосьЭлектроникаИтак, вот что у нас получилось (первая схема - "контроллерная" часть,
вторая - драйвер двигателей; выводы "к драйверу двигателей" с первой схемы
подключаются к точкам А1, А2, В1 и В2 на второй). Выводы 16 и 17
подключаются на двигатель поворота колес (предположим, у вас это 1А и 2А),
а 18 и 19 - на ходовой двигатель (тогда 1В и 2В). Какой именно из выводов
куда - зависит от вашей конкретной конструкции шасси, надо только, чтобы
сигнал по линии 19 вызывал движение вперед, 18 - назад, 17 - поворот
вправо, 16 - влево. Для повторения схем нужны:
SA1 и SA2 - контактные датчики ("бамперы"). Цепь R3 (10кОм) - SB1 нужна
для "сброса" микроконтроллера и использовалась только на этапе отладки.
ПрограммаПрограмма также получилась несложной (вот готовый файл прошивки - at03.hex).
При начале работы в программе производится установка режимов работы
портов, разрешаются прерывания по входам INT0 и INT1, устанавливается
режим запроса на прерывание по этим входам и разрешается обработка
прерываний. // // Главная программа // int main(void) { DDRB = 0xff; // назначаем все линии порта B на выход PORTB = 0x00; // и устанавливаем на них низкий уровень DDRD = 0x00; // назначаем все линии порта D на вход PORTD = 0xff; // подключаем внутренние "подтягивающие" резисторы // разрешаем прерывания по входам int0, int1 outp((1<<INT0)|(1<<INT1), GIMSK); // запрос на прерывание по спадающим фронтам на входах int0 и int1 outp((0<<ISC00)|(1<<ISC01)|(0<<ISC10)|(1<<ISC11), MCUCR); sei(); // разрешаем обработку прерываний while(1) walk(); //"бесконечный" цикл } Подпрограмма основного движения walk() работает так: пока нет
сигнала ни от одного из датчиков освещенности, производится запрос
следующего направления движения (подпрограмма next_move()), а
затем дается команда на движение в этом направлении (подпрограмма
go()) в течение 2.5 секунд. Обратите внимение: исходное состояние на входах, к которым подключены датчики освещенности - логическая "1", поэтому, когда мы говорим, что на входе появился сигнал от датчика, имеется в виду, что на входе появился логический "0"! // // Подпрограмма основного движения // unsigned char walk(void){ // "Свободное блуждание" while((bit_is_set(IN, LIGHT_R)) && (bit_is_set(IN, LIGHT_L))){ go(next_move()); // определение следующего направления и движение Delay_10ms(250); // задержка 2.5 сек } // Движение на свет while((bit_is_clear(IN, LIGHT_R)) || (bit_is_clear(IN, LIGHT_L))){ if((bit_is_clear(IN, LIGHT_R)) && (bit_is_clear(IN, LIGHT_L))) go(F); else if(bit_is_clear(IN, LIGHT_R)) go(FR); else if(bit_is_clear(IN, LIGHT_L)) go(FL); } return(0); } Подпрограмма next_move() определяет следующее значение направления движения исходя из текущего направления на основании таблицы вероятностей переходов: // таблица вероятностей unsigned char p[7][7] = { {14, 43, 57, 71, 86, 93, 100}, {7, 43, 71, 100, 100, 100, 100}, {7, 50, 93, 100, 100, 100, 100}, {7, 50, 57, 100, 100, 100, 100}, {29, 29, 29, 29, 57, 79, 100}, {36, 36, 36, 36, 71, 93, 100}, {36, 36, 36, 36, 71, 79, 100}, }; // текущее направление движения unsigned char this_move; // // Выбор направления движения в следующем шаге // unsigned char next_move(void){ unsigned char pp, i; pp = rand(); // случайное значение в диапазоне 0..100 for (i=0;i<7;i++){ if (p[this_move][i] > pp) break; } this_move = i; return(i); } Наконец, программа go() "преобразует" требуемое направление ("STOP" тоже условно считаем за "направление") в сигналы, выдаваемые на двигатель. Фактически она реализует таблицу, приведенную выше в разделе "Структура программы": // возможные направления движения enum {STOP, F, FR, FL, B, BR, BL}; // // Подпрограмма управления двигателями // void go(unsigned char direction){ switch (direction) { case STOP: cbi(OUT, MOTOR_F); cbi(OUT, MOTOR_B); cbi(OUT, TURN_R); cbi(OUT, TURN_L); break; case F: sbi(OUT, MOTOR_F); cbi(OUT, MOTOR_B); cbi(OUT, TURN_R); cbi(OUT, TURN_L); break; case FR: sbi(OUT, MOTOR_F); cbi(OUT, MOTOR_B); sbi(OUT, TURN_R); cbi(OUT, TURN_L); break; case FL: sbi(OUT, MOTOR_F); cbi(OUT, MOTOR_B); cbi(OUT, TURN_R); sbi(OUT, TURN_L); break; case B: cbi(OUT, MOTOR_F); sbi(OUT, MOTOR_B); cbi(OUT, TURN_R); cbi(OUT, TURN_L); break; case BR: cbi(OUT, MOTOR_F); sbi(OUT, MOTOR_B); sbi(OUT, TURN_R); cbi(OUT, TURN_L); break; case BL: cbi(OUT, MOTOR_F); sbi(OUT, MOTOR_B); cbi(OUT, TURN_R); sbi(OUT, TURN_L); break; } } Здесь команда sbi включает соответстующий двигатель, а cbi - выключает. Подразумевается, что OUT назначен на PORTB, IN - на PIND, MOTOR_F - на PB7 и так далее, как было определено выше. Если ваши двигатели подключены по-другому (к примеру, поворот осуществляется за счет разницы скоростей вращения "бортовых" двигателей), то вам достаточно внести изменения лишь в эту подпрограмму. Блок отъезда Блок отъезда, как уже отмечалось, реализован в виде программы обработки прерывания. В главной программе мы подали на линии INT0 и INT1 положительное напряжение через "подтягивающие" резисторы, поэтому изначально на них присутствует логическая "1". При столкновении с препятствием контакты бампера замыкают соответствующий вход на "землю", и по спадающему фронту сигнала (так мы задали при инициализации) происходит прерывание. В этот момент основная программа приостанавливается и начинает работать программа обработки прерывания, в данном случае - программа отъезда от препятствия: // // Обработка прерывания от переднего бампера (INT0 = PD2) // SIGNAL(SIG_INTERRUPT0) { if(this_move==FR) go(BL); // если двигались "вправо-вперед" // отъезжаем "влево-назад" if(this_move==FL) go(BR); // и так далее else go(B); Delay_10ms(250); this_move=B; // увеличиваем вероятность продолжения // движения от препятствия } // // Обработка прерывания от заднего бампера (INT1 = PD3) // SIGNAL(SIG_INTERRUPT1) { if(this_move==BR) go(FL); if(this_move==BL) go(FR); else go(F); Delay_10ms(250); this_move=F; } Когда программа обработки прерывания завершена, продолжается выполнение основной программы (т.е. свободный поиск или движение на свет) с того места, где она была приостановлена. Подведем итогиС точки зрения конструкции наш робот оказался достаточно простым.
Построив раз такую модель, аналогичную, при наличии необходимых
комплектующих, можно сделать за выходной. Некоторые дополнительные рекомендацииДля повышения помехоустойчивости системы необходимо сделать
следующее. Продолжение следует!Дальнейшим развитием этого проекта является добавление функции плавного
регулирования скорости робота с использованием широтно-импульсной
модуляции (ШИМ), а также установка на него бесконтактного датчика
препятствий - "ИК-локатора". Задать вопросы по этому проекту, дать свои предложения по дальнейшему его развитию или оставить комментарии можно здесь >>>. |
![]() |