Наверняка каждый робототехник знает, что роботы, как и компьютеры, умеют мыслить только нулями и единичками и, например число 3 запомнить им не под силу. Они долго думают и в итоге заменяют его двумя единицами, переводя тем самым в "двоичный код".
Многие из робототехников привыкли иметь дело с высокоуровневой разработкой - готовые модули, готовые библиотеки, им не особо интересно, что там, внутри, как устроен тот или иной позаимствованый компонент.
В этом проекте мы заглянем в самую душу наших роботов, посмотрим на то, как они запоминают привычную нам информацию, как передают ее друг другу и как они действуют, если им требуется перевести ее в привычную человеку форму.
В этом проекте мы заглянем в самую душу наших роботов, посмотрим на то, как они запоминают привычную нам информацию, как передают ее друг другу и как они действуют, если им требуется перевести ее в привычную человеку форму.
Основная наша задача в данном проекте - разобраться с тем, а что же это такое - двоичное число, как перевести привычное нам десятичное чисто в двоичное и наоборот. Но, стоп! Это же такая скукота, зачем это вообще нужно? Давайте будем действовать от реальной задачи.
Мы построили двух одинаковых роботов - Правого и Левого. Они - полные зеркальные копии друг друга. Кстати, вы тоже можете собрать таких, а чтобы облегчить вам задачу мы подготовили и выложили инструкцию по их сборке в формате LEGO Digital Designer.
Роботы друг с другом никак не соединены, но им нравится стоять рядом, ведь вместе они создают очень симметричную композицию. Мы будем поворачивать ротор у Правого робота и симметрия будет нарушаться. Задача Левого робота - поддерживать эту симметрию, поворачивая свой ротор в тоже положение, в которое повернут ротор у Правого.
Повернув ротор, мы изменяем текущее значение энкодера мотора. Это значение Правый робот должен передать Левому. Для этого у него есть светодиод на датчике освещенности, который он может включать и выключать. Таким образом он может передать целых два числа
0 - светодиод выключена
1 - светодиод включен
Если нам нужно передать число большее 1, на помощь придет двоичный код. Посмотрите на таблицу ниже, число 2 будет представлено как 10, число 6 как 110, а число 12 как 1100.
Этот принцип сохраняется и для больших чисел, просто последовательности 0 и 1 будут длиннее. Для перевода из десятичного числа в двоичное есть несколько способов, например вот такой:
Итак, угол поворота ротора в двоичном виде мы получили, как теперь его передать? Светодиод у Правого робота и датчик освещенности у Левого позволяют нам создать такое распространенное устройство как Универсальный асинхронный приёмопередатчик (УАПП, англ. Universal Asynchronous Receiver-Transmitter, UART) — узел вычислительных устройств, предназначенный для организации связи с другими цифровыми устройствами. Преобразует передаваемые данные в последовательный вид так, чтобы было возможно передать их по одной физической цифровой линии другому аналогичному устройству.
Передача данных в UART осуществляется по одному биту в равные промежутки времени. Этот временной промежуток определяется заданной скоростью UART и для конкретного соединения указывается в бодах (что в данном случае соответствует битам в секунду).
Для начала давайте проведем небольшой эксперимент, будем включать и выключать в цикле светодиод датчика и посмотрим за сколько выполнится 1000 таких переключений: В конструкции роботов мы использовали блоки LEGO NXT, для их программирования воспользуемся языком NXC:
int t;
task main()
{
t = 0;
while(t < 1000)
{
t = t + 1;
SetSensorLight(IN_3,true);
SetSensorLight(IN_3,false);
}
ClearScreen();
NumOut(0, LCD_LINE4, CurrentTick());
Wait(10000);
}
Запустив эту программу мы с удивлением обнаружили цифру 42000. Значит каждое полное переключение занимает целых 42000/1000 = 42 мс, а переключение из режима в режим вероятно около - 42/2 = 21 мс. Это довольно много, быстрый UART нам не создать. С другой стороны наш проект учебный, для нас главное - наглядность и понимание принципов работы устройства, гигабитных скоростей будем достигать совсем в других проектах :).
Теперь давайте напишем программу для Правого, передающего робота.
// угол, на который повернут ротор у Правого робота
int a;
// длительность одного бита данных, мс
int t;
// массив для хранения числа a в двоичной форме
bool b[8];
// в этой переменной запоминаем горит ли светодиод на датчике
bool y;
task main()
{
while(true)
{
// светодиод на датчике выключен
y = false;
// сохраняем в переменной а угол поворота ротора
a = MotorRotationCount(OUT_B);
// Это время в мс, в течении которого передается 1 бит информации
t = 200;
// ограничиваем а (угол поворота ротора) пределами 0..255
if (a < 0)
{
a = 0;
}
// Мы будем передавать только 1 байт, это максимум 255
if(a > 255)
{
a = 255;
}
// Очищаем экран и выводим на него a в десятичной форме
ClearScreen();
NumOut(0,13,a);
// Переводим a в двочиный вид и сохраняем в массив b[8]
// выводим на экран также двоичиное пердставление числа
for (int i=7; i >= 0; i--)
{
if (a / pow(2, i) >= 1)
{
b[i] = true;
a = a - pow(2,i);
NumOut((7-i)*8,0,1);
}
else
{
b[i] = false;
NumOut((7-i)*8,0,0);
}
}
// включаем светодиод и передаем стартовый бит
SetSensorLight(IN_3,true);
y = true;
// Время передачи меньше на 21 мс, именно столько включается светодиод
Wait(t-21);
// передаем байт данных бит за битом мз массива b[8]
for(int i = 7;i >= 0;i--)
{
if(b[i] == true)
{
SetSensorLight(IN_3,true);
// длительность задержки различна, в зависимости от того горел ли светодиод
if(y == true)
{
Wait(t-1);
}
else
{
Wait(t-21);
}
y = true;
}
else
{
SetSensorLight(IN_3,false);
if(y == false)
{
Wait(t-1);
}
else
{
Wait(t-21);
}
y = false;
}
}
// включаем светодиод и передаем стоповый, 10-й, бит
SetSensorLight(IN_3,true);
if(y == true)
{
Wait(t-1);
}
else
{
Wait(t-21);
}
y = true;
// выключаем светодиод, готовимся к следующей итерации цикла
SetSensorLight(IN_3,false);
y = false;
Wait(t*4);
}
}
После запуска программы робот начинает бодро моргать светодиодом датчика, передавая бит за битом показания энкодера на моторе с ротором. Теперь второму, Левому роботу, нужно принять эту двоичную посылку и перевести ее в десятичную форму. Принцип такого перекодирования также довольно прост:
После того как число переведено в десятичную форму вступит в дело ПД-регулятор и развернет ротор принимающего робота в тоже положение, что и у передающего.
Программа для Левого, принимающего робота у нас выглядит следующим образом.
// принятое число в десятичной форме
int a, a_tmp;
// массив для хранения принятого числа в двоичной форме
bool b[8];
// длительность одного бита данных, мс
int t;
// управляющее воздействие ПД-регулятора
int u;
// пропорциональный коэтф-т ПД-регулятора
int pk;
// дифф.коэф-т ПД-регулятора
int dk;
// "старая" ошибка в дифф.компоненты ПД-регулятора
int es;
// данные с датчика
int D1;
// ошибка ПД-регулятора
int e;
// ПД-регулятор устанавливает ротор в положение a градусов
task motor()
{
// значение коэф-тов ПД-реглятора
pk = 2;
dk = 4;
es = 0;
while(true)
{
// вычисляем ошибку (отклонение от целевой величины)
e = a - MotorRotationCount(OUT_B);
// формируем управляющее воздействие
u = e*pk+dk*(e-es);
// juhfybxbdftv управляющее воздействие диапазоном -100..100
if(u < -100)
{
u = -100;
}
if(u > 100)
{
u = 100;
}
// опдаем управляющее воздействие на мотор
OnFwd(OUT_B,u);
// сохраняем значение ошибки
es = e;
}
}
task main()
{
SetSensorLight(IN_3,false);
a = 0;
t = 200;
start motor;
while(true)
{
// ждем стартовый бит
while(Sensor(IN_3)<65);
// ждем первый бит данных
Wait(t+t/2);
// считываем все биты данных и записываем в массив b[8]
for(int i = 7;i >= 0;i--)
{
if(Sensor(IN_3)>65)
{
b[i] = true;
Wait(t);
}
else
{
b[i] = false;
Wait(t);
}
}
// очищаем экран
ClearScreen();
// если стоповый бит не получен - ошибка приема-передачи
if(Sensor(IN_3)<65) TextOut(0, LCD_LINE6, "ERROR");
// выводим на экран принятое двоичное число
for(int i = 7;i >= 0;i--)
{
if(b[i] == true)
{
NumOut((7-i)*8,0,1);
}
else
{
NumOut((7-i)*8,0,0);
}
}
// переводим число в десятичное
a_tmp = 0;
for(int i = 7;i >= 0;i--)
{
if(b[i] == true)
{
a_tmp = a_tmp + 1 * pow(2, i);
}
}
a = a_tmp;
// выводим десятичное число на экран
NumOut(0,13,a);
Wait(t*2);
}
}
С учетом стартовых и стоповых битов скорость нашего UART составляет 4 бита/сек.
Скачать программы для обоих роботов, а также инструкцию по их сборке можно по ссылке.
Скачать программы для обоих роботов, а также инструкцию по их сборке можно по ссылке.