Проект «Домовой». Начало

Сегодня мы начинаем публикацию серии статей о проекте  »Домовой» — домашнем роботе на основе робототехнической платформы Strela — в которой автор, Пашкевич Николай, рассказывает о своем опыте работы с платформой, о том, какие задачи ставит перед собой, и как решает их в попытке создать собственного робота.

strela_ready-1

Мы предлагаем вашему вниманию первые три статьи, из которых вы узнаете о целях проекта, знакомитесь с его основными компонентами и даже сможете ознакомиться с первой версией программы робота «Домового».

Вступление

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

Когда я спрашиваю себя, а что бы я хотел получить в конце этого проекта, то мозг переполняется идеями и желаниями что можно сделать, проблема в том что чем круче идея тем сложнее ее реализовать. Таким образом я не ставлю перед собой цель сесть и создать все и сразу, принцип работы будет итеративный и от простого к сложному, задача — решение, задача — решение. Не стоит браться за все сразу, лучше идти по шагам, так проще решать проблемы, проще отлавливать «баги», да и вообще итеративный принцип часто описывается в различных книгах по программированию как — не большие но частые итерации лучше чем редкие и большие, и я в этом убедился ни разу на личном опыте. Так поступлю и здесь.

И все же лично мое видение некоего конечного результата с разумными вполне осуществимыми задумками.
«Домовой» это мобильная платформа позволяющая управлять ей через TCP/IP соединение, несущая на себе управляемую видеокамеру и набор датчиков и сенсоров, под управлением разработочного для этого проекта приложения. В итоге у меня получится мобильное устройство с помощью которого я смогу всегда проехать по квартире находясь в любом месте с интернетом и проверить все ли в порядке, так же если этого удастся добиться возможна функция «патруля» которая позволит платформе самостоятельно делать обход квартиры и определять наличие угроз в виде воды на полу, возгораний, утечки газа или наличия живых организмов когда их дома быть не должно с оповещением меня каким-либо способом.

В целом путь к этому будет не быстрый, так как потребуется много знаний и времени что бы все это реализовать, а уж вопрос функции «патруль» это вообще из разряда «мастер-класс» и не уверен получится ли это реализовать, так как очень сложно научить электронику определять свое точное положение в пространстве с минимальными погрешностями (минимальная погрешность GPS пара метров, сойдет для леса но для квартиры явно великоват разброс) без создания в квартире нечто наподобие GPS, в общем эта тема лишь как интересная и сложная задача, получится ее решить или нет понятия не имею но пока и без нее будет чем заняться.

Что касательно исходного кода, то я всегда буду отдавать предпочтение понятности кода нежели эффективности, до тех пор пока она реально не потребуется. Часто встречал в книгах известных авторов по разработке ПО заметки о том что не стоит производить преждевременную оптимизацию кода, до тех пор пока она реально не потребуется. Но это не значит что надо писать «быдло-код», я категорически против и всегда стараюсь придерживается «хорошего тона» в написании кода. Я не являюсь проффи, я только учусь, поэтому не судите слишком строго.

На этом думаю и все, пора приступить к не спешной работе над проектом.

Начальные компоненты

Не стал я особо привередничать и искать что то крутое, и обзавелся купленной на eBay не дорогой китайской платформой с 4-мя электрическими двигателями и Arduino — совместимой платой «Strela» от ребят из Амперки. Так как пока я явно не хочу заморачиваться с TCP/IP решением ибо там будет на чем съесть стаю собак и еще пару кошечек, для начала решил взять уже имеющиеся у меня модули XBee. XBee в целом хватит для почти всех начальных задач, да и опять таки «стрела» имеет слот под эти модули а эти модули у меня уже есть, зачем покупать что-то еще.

Почему я выбрал «стрелу», просто «бутерброд» из Arduino UNO + Motor Shield + XBee Shield меня не очень радовал, а «стрела» имеет на борту все что нужно для начала на одной плате, плюс она Arduino — совместимая а значит пока меня будут устраивать готовые библиотеки для Arduino я смогу их использовать без изменений.
Да и текстовый LCD мне не очень хотелось выносить с решением вопроса о том как его подключить к Arduino UNO, не потратив при этом пачку портов и не нажив конфликта с тем самым «бутербродом» из «шилдов».

В целом собрав все это в кучу я получил вот такую платформу:boggart-v1-common-view-1

Вот с этим теперь мне и предстоит работать, постепенно модернизируя и улучшая.
Скажу сразу. со временем изменится почти все, так как «стрела» явно не предназначена для работы качестве «мозга» системыы, а XBee потоковое видео просто не потянет, всеми этими функциями в будущем займутся другие «жители» платформы, и не только потому что существующие не предназначены или не потянут, а хотя бы потому что разделение обязанностей отличная вещь. А пока такой вариант позволяет почти моментально получить результат, а когда что то уже работает это существенно повышает мораль и интерес к проекту.

Не много забегая мыслями вперед, пока что на роль будущего главного «мозга» на платформе рассматривается Raspberry Pi (малина) а «стрела» будет осуществлять только управление двигателями и возможно сбор данных с датчиков и сенсоров с передачей их на «малину», ну как минимум на первом этапе а там видно будет.

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

Программа для платформы. v1.0

И так, физически все есть, теперь пришла пора все это оживить. Франкенштейну удалось и у меня получится.
Для начала определюсь с тем что я желаю на начальном этапе:

  • Движение вперед
  • Движение назад
  • Поворот на месте влево
  • Поворот на месте в право
  • Остановка
  • Таймер безопасности
  • Проверка связи с платформой
  • Вывод текущей команды на LCD

Если в с пунктами движения и остановки все понятно то вот что такое «таймер безопасности» который я хочу ввести я сейчас расскажу.

Любое соединение имеет привычку пропадать в самый не подходящий момент, либо ПК на котором работает управляющая программа может внезапно выключится или программа аварийно завершится и т.д. и т.п.
А что если платформа в этот момент активно движется вперед и ждет команды на остановку, а связи с ней нет, вот так и будет она ехать или вертеться на месте до тех пор пока будет «порох в ее пороховницах».
Отсюда вывод — надо предусмотреть вариант внезапной потери связи. Этим вариантом и станет мой «таймер безопасности», идея которого будет до безобразия простая, если с момента поступления последней команды пройдет больше N времени, платформа должна будет прекратить любые движения и просто остановится.

Проверка связи потребуется что бы можно было проверить «в сознании» ли платформа и способна ли она реагировать на команды. Суть данного метода на начальном этапе будет в очередной раз простейшая, отправка команды проверки связи (пинг) на который в ответ платформа должна отослать подтверждение, для начала такой простой схемы будет более чем достаточно.

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

XBee модули я настроил согласно этому не большому руководству.

Формат команд для начала будет просто символы, до боли знакомые любому геймеру это:

  • w — вперед
  • s — назад
  • a — влево
  • d — вправо
  • space — стоп
  • p — пинг (системная — пинг)
  • o — понг (системная- подтверждение)

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

И так, вот такой получится базовый код для платформы:

#include <Wire.h>
#include <Strela.h>
#include <LiquidCrystal_I2C.h>

const char FORWARD_CMD = 'w';
const char BACK_CMD = 's';
const char LEFT_CMD = 'a';
const char RIGHT_CMD = 'd';
const char STOP_CMD = ' ';
const char PING_CMD = 'p';
const char PING_ANSWER = 'o';
const int RIGHT_ENGINE_SPEED = 165;
const int LEFT_ENGINE_SPEED = 185;
const int ZERO_SPEED = 0;
const int SAFETY_INTERVAL = 3000;
enum state {STOP, FORWARD, BACK, RIGHT, LEFT};

state currentState = STOP;
unsigned long lastCmdTime = 0;
LiquidCrystal_I2C lcd(LC_ADDR, LCEN, LCRW, LCRS, LC4, LC5, LC6, LC7);

void setup( void )
{
	motorConnection(1, 0);
	lcd.begin(8, 2);
	Serial1.begin(9600);
	printToLcd("Strela:", "Ready...");
}

void loop( void )
{
	while (Serial1.available() < 1)
	{
		boolean isMoving = currentState != STOP;
		boolean isSafetyIntervalExceeded = (lastCmdTime + SAFETY_INTERVAL) < millis();
		if (isMoving && isSafetyIntervalExceeded)
		{
			stop();
		}
	}

	lastCmdTime = millis();
	switch (Serial1.read())
	{
		case FORWARD_CMD:
			moveForward();
			break;
			
		case BACK_CMD:
			moveBack();
			break;
			
		case LEFT_CMD:
			turnLeft();
			break;
			
		case RIGHT_CMD:
			turnRight();
			break;
			
		case STOP_CMD:
			stop();
			break;
			
		case PING_CMD:
			pingAnswer();
			break;
		
		default:
			printToLcd("Unknown", "command");
	}
}

inline void moveForward( void )
{
	if (currentState != FORWARD)
	{
		drive(RIGHT_ENGINE_SPEED, LEFT_ENGINE_SPEED);
		currentState = FORWARD;
		printToLcd("Move:", "Forward");
	}
}

inline void moveBack( void )
{
	if (currentState != BACK)
	{
		drive((RIGHT_ENGINE_SPEED * -1), (LEFT_ENGINE_SPEED * -1));
		currentState = BACK;
		printToLcd("Move:", "Back");
	}	
}

inline void turnLeft( void )
{
	if (currentState != LEFT)
	{
		drive(RIGHT_ENGINE_SPEED, ZERO_SPEED);
		currentState = LEFT;
		printToLcd("Turn:", "Left");
	}
}

inline void turnRight( void )
{
	if (currentState != RIGHT)
	{
		drive(ZERO_SPEED, LEFT_ENGINE_SPEED);
		currentState = RIGHT;
		printToLcd("Turn:", "Right");
	}
}

inline void stop( void )
{
	if (currentState != STOP)
	{
		drive(ZERO_SPEED, ZERO_SPEED);
		currentState = STOP;
		printToLcd("Strela:", "Stop");
	}
}

inline void pingAnswer( void )
{
	Serial1.write(PING_ANSWER);
}

void printToLcd( const char *inputLine1, const char *inputLine2 )
{
	lcd.clear();
	lcd.home();
	lcd.print( inputLine1 );
	lcd.setCursor(0, 1);
	lcd.print( inputLine2 );
}


* Код протестирован в Arduino IDE 1.0.5-r2

Данный код рассчитан на компиляцию в Arduino IDE с использованием библиотек Wire. LiquidCrystal. Strela. Библиотека Wire уже имеется в составе IDE собственно как и библиотека LiquidCrystal, хотя в последней по умолчанию отсутствуют файлы для I2C на котором работает LCD на стреле, поэтому вам придется взять полную версию. Ссылки на библиотеки вы можете найти тут как собственно и примеры кода для работы со стрелой. Я целенаправленно не даю прямые ссылки на библиотеки, так как разработчики стрелы могут внести какие-либо изменения в месторасположения библиотек, или изменить что-то еще, таким образом надеюсь что информация на сайте производителя стрелы будет актуальной.

Работа с Serial в данном коде происходит через объект Serial1 (единица на конце), так как стрела имеет виртуальные порты, и поэтому просто Serial это для связи по USB а Serial1 это через модуль беспроводной связи.

В моем коде константы RIGHT_ENGINE_SPEED и LEFT_ENGINE_SPEED имеют разные значения, дело в том что у меня платформу «вело» в один бок при езде прямо, пришлось внести корректировки в значения скорости моторов. В будущем я планирую организовать систему которая позволит платформе самостоятельно настраивать работу моторов.

Ну что же, если «залить» код на платформу и открыть монитор порта, можно посылать описанные выше команды и платформа будет их послушно выполнять, а если команды не будет дольше чем указано в константе SAFETY_INTERVAL то платформа просто остановится, данная проверка реализована в цикле активного ожидания команды, в будущем можно задействовать таймер микроконтроллера и прерывание от него, но пока не стану усложнять.

Отлично, все работает как задумано.

it_is_alive

Но у меня как и у Франкенштейна тоже не обошлось без неожиданностей. Дело в том что XBee подкинул эту самую неожиданность, примерно через каждые 8 команды модуль впадает в «спячку» на 3-4 секунды а потом платформа получала все что я послал за эти 3-4 секунды, и это все делает динамическое управление платформой не возможным. Но если посылать команды примерно раз в секунду, проблем нет, но это же смешно, если активно «рулить» платформой может потребоваться до 5 команд в секунду а то и больше, смотря как рулить короче.

В общем теперь придется решать эту проблему.

Автор: Пашкевич Николай
pashkevich.me

  • Петрович

    А не могли бы Вы подробно освятить вопрос питания платформы? Какие источники использовались, время работы от АКБ и т.п.

    • kirill_amperka

      Уточним у автора и осветим обязательно.