Программирование LEGO NXT роботов на языке NXC - Коммуникации между роботами
Автор: Daniele Benedettelli
Перевод: © Ботов Антон aka =DeaD=, 2009
Эксклюзивно для www.roboforum.ru
копирование на другие ресурсы и публикация перевода
без разрешения его автора запрещены
Содержание
[убрать]Коммуникации между роботами
Если у вас есть больше чем один модуль NXT, тогда эта глава для вас (хотя вообще можно устанавливать связь не только между роботами, но и робота с ПК). Роботы могут связываться друг с другом через радиоканал Bluetooth: так что вы можете иметь нескольких роботов одновременно выполняющих общую задачу (или сражающихся друг с другом), кроме того вы можете построить большого сложного робота из двух NXT, так что можно будет использовать 6 сервомоторов и 8 сенсоров.
Для старого доброго RCX, это очень просто: он отправляет ИК-сообщение и все роботы вокруг принимают его.
Но в NXT реализован совершенно другой подход! Во-первых вам нужно соединить два или более NXT (или NXT и ПК) с помощью меню "Bluetooth" на модуле NXT; только тогда вы сможете отправлять сообщения к подключенным устройствам.
NXT который инициирует подключение называется Мастером, и может иметь до 3 подчиненных устройств, подключенных к линиям 1,2 и 3. Подчиненный модуль всегда видит мастера на линии 0. Вы можете отправлять сообщения в 10 доступных ячеек.
Отправка сообщений мастер-подчиненный
Ниже приведены 2 программы - одна для мастера, а вторая для подчиненного. Эти простые программы покажут вам, как быстрый непрерывный поток строковых сообщений может быть передан с помощью радиоканала между двумя модулями NXT.
Программа для мастера сначала убеждается, что подчиненный правильно подключен на 1 линию (константа BT_CONN) используя функцию BluetoothStatus(conn); после чего создает и отправляет сообщения состоящие из префикса "M" и возрастающих чисел с помощью функции SendRemoteString(conn,queue,string), одновременно получая сообщения от подчиненного функцией ReceiveRemoteString(queue,clear,string) и отображая эту информацию.
//MASTER #define BT_CONN 1 #define INBOX 1 #define OUTBOX 5 sub BTCheck(int conn){ if (!BluetoothStatus(conn)==NO_ERR){ TextOut(5,LCD_LINE2,"Error"); Wait(1000); Stop(true); } } task main(){ string in, out, iStr; int i = 0; BTCheck(BT_CONN); //check slave connection while(true){ iStr = NumToStr(i); out = StrCat("M",iStr); TextOut(10,LCD_LINE1,"Master Test"); TextOut(0,LCD_LINE2,"IN:"); TextOut(0,LCD_LINE4,"OUT:"); ReceiveRemoteString(INBOX, true, in); SendRemoteString(BT_CONN,OUTBOX,out); TextOut(10,LCD_LINE3,in); TextOut(10,LCD_LINE5,out); Wait(100); i++; } }
Программа подчиненного модуля очень похожа, но использует функцию SendResponseString(queue,string) вместо SendRemoteString, потому что подчиненный может отправлять сообщения только мастеру, находящемуся на линии 0.
//SLAVE #define BT_CONN 1 #define INBOX 5 #define OUTBOX 1 sub BTCheck(int conn){ if (!BluetoothStatus(conn)==NO_ERR){ TextOut(5,LCD_LINE2,"Error"); Wait(1000); Stop(true); } } task main(){ string in, out, iStr; int i = 0; BTCheck(0); //check master connection while(true){ iStr = NumToStr(i); out = StrCat("S",iStr); TextOut(10,LCD_LINE1,"Slave Test"); TextOut(0,LCD_LINE2,"IN:"); TextOut(0,LCD_LINE4,"OUT:"); ReceiveRemoteString(INBOX, true, in); SendResponseString(OUTBOX,out); TextOut(10,LCD_LINE3,in); TextOut(10,LCD_LINE5,out); Wait(100); i++; } }
Как можно заметить, при отключении программы на одно из сторон, вторая сторона продолжит отправлять сообщения с возрастающими номерами, не зная, что все сообщения теряются, потому что на той стороне никто их не принимает. Чтобы избежать этой проблемы мы планируем улучшить протокол с помощью подтверждения доставки.
Отправка чисел с подтверждением
Как мы видим в приведенной ниже паре программ: в этот раз мастер отправлять числа командой SendRemoteNumber(conn,queue,number) и останавливается, ожидая подтверждения получения от подчиненного (цикл until, внутри которого мы видим ReceiveRemoteString); только если на той стороне подчиненный работает и отправил нам подтверждение - мастер переходит к отправке следующего сообщения. Подчиненный просто получает числа фукнцией ReceiveRemoteNumber(queue,clear,number) и отправляет подтверждение командой SendResponseNumber. Обе программы должны иметь общий код для подтверждения. В нашем случае я принял 0xFF в качестве такого кода.
Мастер отправляет случайные числа и ждёт подтверждение подчиненного; каждый раз после этого переменная ack должна быть очищена, иначе мастер продолжит отправлять сообщения не ожидая больше никаких подтверждений, потому что мы забыли очистить эту переменну.
Подчиненный в бесконечном цикле проверяет ячейку и если она не пуста, показывает значение на экран и отправляет мастеру подтверждение о получении. В начале программы я отправил подтверждение без информации, которую подтверждаю, чтобы разблокировать мастера. Без этого, если бы мастер запустился раньше - первое его сообщение было бы отправлено в никуда и он бы начал ждать ответа, а подчиненный запустился бы и начал ждать первое сообщение, так что всё бы "зависло", даже если подчиненный нормально запущен. То как мы сделали сейчас - может привести к потере части сообщений, но хотя-бы нет риска зависания пары модулей.
//MASTER #define BT_CONN 1 #define OUTBOX 5 #define INBOX 1 #define CLEARLINE(L) \ TextOut(0,L," "); sub BTCheck(int conn){ if (!BluetoothStatus(conn)==NO_ERR){ TextOut(5,LCD_LINE2,"Error"); Wait(1000); Stop(true); } } task main(){ int ack; int i; BTCheck(BT_CONN); TextOut(10,LCD_LINE1,"Master sending"); while(true){ i = Random(512); CLEARLINE(LCD_LINE3); NumOut(5,LCD_LINE3,i); ack = 0; SendRemoteNumber(BT_CONN,OUTBOX,i); until(ack==0xFF) { until(ReceiveRemoteNumber(INBOX,true,ack) == NO_ERR); } Wait(250); } }
//SLAVE #define BT_CONN 1 #define OUT_MBOX 1 #define IN_MBOX 5 sub BTCheck(int conn){ if (!BluetoothStatus(conn)==NO_ERR){ TextOut(5,LCD_LINE2,"Error"); Wait(1000); Stop(true); } } task main(){ int in; BTCheck(0); TextOut(5,LCD_LINE1,"Slave receiving"); SendResponseNumber(OUT_MBOX,0xFF); //unblock master while(true){ if (ReceiveRemoteNumber(IN_MBOX,true,in) != STAT_MSG_EMPTY_MAILBOX) { TextOut(0,LCD_LINE3," "); NumOut(5,LCD_LINE3,in); SendResponseNumber(OUT_MBOX,0xFF); } Wait(10); //take breath (optional) } }
Прямые команды
Вот еще одна отличная возможность доступная при использовании Bluetooth: мастер может напрямую контролировать подчиненных. В этом примере мастер отправляет подчиненным прямые команды для проигрывания звуков, включения двигателей и для подчиненного модуля NXT даже не нужно писать никакой программы. Это всё реализуется встроенным в NXT программным обеспечением!
//MASTER #define BT_CONN 1 #define MOTOR(p,s) RemoteSetOutputState(BT_CONN, p, s, \ OUT_MODE_MOTORON+OUT_MODE_BRAKE+OUT_MODE_REGULATED, \ OUT_REGMODE_SPEED, 0, OUT_RUNSTATE_RUNNING, 0) sub BTCheck(int conn){ if (!BluetoothStatus(conn)==NO_ERR){ TextOut(5,LCD_LINE2,"Error"); Wait(1000); Stop(true); } } task main(){ BTCheck(BT_CONN); RemotePlayTone(BT_CONN, 4000, 100); until(BluetoothStatus(BT_CONN)==NO_ERR); Wait(110); RemotePlaySoundFile(BT_CONN, "! Click.rso", false); until(BluetoothStatus(BT_CONN)==NO_ERR); //Wait(500); RemoteResetMotorPosition(BT_CONN,OUT_A,true); until(BluetoothStatus(BT_CONN)==NO_ERR); MOTOR(OUT_A,100); Wait(1000); MOTOR(OUT_A,0); }
Подводим итоги
В этой главе вы изучили некоторые простые аспекты использования Bluetooth для связи между роботами. Мы соединили по радиоканалу 2 модуля NXT, отправляли и принимали строки и числа, ожидали подтверждение получения. Последнее очень важно в случае необходимости в надежном канале передачи информации.
В качестве дополнительной возможности вы также научились отдавать мастером прямые команды для подчиненного модуля NXT.