Робот пылесос - Часть2: Электроника и программа



В конце первой части статьи была представлена электронная схема простого управляющего алгоритма. В этой части будет рассмотрена электронная и программная составляющая системы управления робота, на основе микроконтроллера.
Электроника:
Плата управления построена на микроконтроллере atmega16, изначально она разрабатывалась, как универсальный модуль, поэтому оказалось плохо защищена от помех электродвигателя турбины. Проблема решилась экранированием проводов двигателя и установкой на него конденсатора в 0.1мкф, также необходимо кинуть вывод RESET контроллера прямо (без резистора) на +5v, это позволяет избавиться от произвольно сброса.


Плата контроллера.

Драйвер двигателей собран на микросхеме L298, по стандартной схеме.

Плата драйвера двигателей.

Остальная электроника и органы управления собраны на макетной плате.

Макетная плата.


Общая электронная схема.

Как вы можете видеть, на схеме нет устройства для контроля заряда аккумулятора и сенсоров для поиска зарядной станции. Всё это было в прошлой модификации робота и достаточно неплохо работало, но так-так получилось всё немного кривовато и требует доработки, то описывать в данной статье эти недостающие элементы я не буду. Однако, чтобы не быть голословным, вот вам видео поиска ЗС, роботом прошлой модификации.


Поиск зарядной станции

Программа:
Наверное, самая интересная часть всей работы – это создание алгоритма и написание программы управления роботом.
Алгоритм уборки разделён на 4 режима:
•Режим ожидания
•Спираль
•Движение вдоль стены
•“Газонокосилка”

Разберём каждый из них по отдельности.

Режим ожидания
Здесь всё просто, приводные двигатели выключены, турбина и щётка тоже, индикатор мигает с низкой частотой, никакой реакции на срабатывание бампера, по нажатию кнопки — переход на следующий режим.

Спираль
Данный алгоритм хорош для комнат с минимальным количеством мебели. После обнаружения препятствия робот переходит на следующий режим, так как этим препятствием, скорее всего, окажется стена, логично было бы сделать переход на режим движения вдоль стены.


На блок-схеме, проверка наличия препятствий дана условно, в программе же она производится постоянно, а не один раз за итерацию.

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


“Газонокосилка”
Алгоритм был предложен на робофоруме и опробован в среде logo. Является хорошей заменой случайному блужданию, в чём можно убедиться, прогнав алгоитм в logo на модели своей комнаты:




Конечно, в реальных условиях всё не так идеально, но зато из сенсоров, для данного алгоритма уборки, нужны только датчики соударений.
Приводить блок-схему этого алгоритма не буду, на робофоруме, есть код на logo.

Программа писалась на чистом С без asm вставок.
Код распилен на несколько частей:
main.c -документ с main функцией и главным циклом.
Periphery.с -аппаратные зависимости, настройка периферии контролера.
Action.c -функциональная часть программы
util/drivers.c -функции управления устройствами
util/timer.c -служба таймеров

Periphery.c
Содержит только одну функцию — Periphery() в которой прописывается настройка периферии контроллера. Функция вызывается единожды, из главной функции программы.

util/drivers.c
Содержит макросы для управления уборочным узлом:

#define ON_Cleaner PORTC |= (1<<4)
#define OFF_Cleaner PORTC &= ~(1<<4)


А также функцию управления приводными двигателями:
void M_drive(signed char l_vector, char l_speed, signed char r_vector, char r_speed);

*_vector – направление вращение двигателя: 1-вперед, 0-стоп, -1-назад
*_speed – скорость вращения, число от 0 до 10

util/timer.c
Внутри две функции:
void Timer_ControlTimer(void); //служба таймеров

Функция управления временными задержками. Вызывается только по прерыванию с таймера-счётчика2, каждую 1/1000сек.

void Timer_Task(int *Time);	//установка задач в очередь таймеров

Единственным параметром передаётся указатель на переменную, от которой будет отсчитываться время. Переменная должна быть заранее инициализирована некоторым, отличным от нуля значением. Как только значение переменной станет равно нулю указатель на неё будет удалён из очереди таймеров. Длину очереди таймеров можно задать с помощью макроса SIZE_ARRAY_HOURS. Обратите внимание что функция Timer_Task не является аналогом функции _delay(), так-как отдаёт управление сразу же, проверять дотикал ли таймер необходимо вручную. Например, вот так выглядит организация задержек в функции управления индикатором:
void led()
	{
	if(led_time!=0)		               //если время не пришло
		{
		return;
		}
		
	if(Mode>0)			       //если уборка
		{
		if(PORTC & (1<<5))
			{
			PORTC &= ~(1<<5);
			
			led_time=300;	       //тушим на 0.3 сек 
			Timer_Task(&led_time);
			}
		else
			{
			PORTC |= (1<<5);
			
			led_time=200;	       //включаем на 0.2 сек
			Timer_Task(&led_time);
			}
		}
	else			               //если режим ожидания
		{
		if(PORTC & (1<<5))
			{
			PORTC &= ~(1<<5);
			
			led_time=1000;
			Timer_Task(&led_time);	//тушим на 2.5 сек 
			}
		else
			{
			PORTC |= (1<<5);
			
			led_time=1000;
			Timer_Task(&led_time);	//включаем на 1 сек
			}
		}
	}


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

Программное устройство:
-Управление циклом уборки.

Модули выполняют разную работу в зависимости от текущего режима. Программа внутри них организована по типу конечного автомата, с помощью конструкции switch – case. Модули могут взаимодействовать с помощью глобальных переменных или изменяя счётчики конечного автомата друг друга.

Вызов функций модулей производится из главного цикла программы:
int main(void)
	{
	Periphery();			//настройка периферии 

	//главный цикл программы 
	while(1)
		{
 		button();         //обработчик кнопки mode
		cycle();          //управление циклом уборки
		drive();          //привод
		cleaner();        //уборочный узел
		bumper();         //бампер
		led();            //индикатор режима работы
		}
	}


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

В Action.c так же есть функция util_mode(char _mode), она используется для смены режима работы. Помимо присваивания нового значения переменной Mode, в определении функции прописано обнуление счётчиков конечных автоматов и переменных-таймеров.

void util_mode(char _mode)
	{
	if(_mode>=AMOUNT_MODE)	//если такого режима не существует 
		{
		Mode=0;
		}
	else
		{
		Mode=_mode;
		}
	
	//сброс переменных и счетчиков
	drive_counter=0;
	bumper_counter=0;
	
	drive_time=0;
	led_time=0;
	}


Исходники
Проект собирается с помощью компилятора avrGCC, Makefile присутствует.

Заключение:
Робот в собранном виде:



Испытания:


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

UPD 2014-06-06
Перезолил исходники на GitHub.

to be continued...

Комментарии (1)

RSS свернуть / развернуть
+
0
Большое спасибо за столь подробное описание проекта!
avatar

admin

  • 30 мая 2014, 22:25

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