Программирование LEGO NXT роботов на языке NXC - Коммуникации между роботами — различия между версиями
=DeaD= (обсуждение | вклад) (Создана новая страница размером =Коммуникации между роботами= If you own more than one NXT this chapter is for you (though you can still communic...) |
=DeaD= (обсуждение | вклад) (→Отправка чисел с подтверждением) |
||
(не показано 10 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
+ | <p align=center><b>Автор: Daniele Benedettelli</b><br><br><i>Перевод: © Ботов Антон aka =DeaD=, 2009<br><br>Эксклюзивно для www.roboforum.ru<br> копирование на другие ресурсы и публикация перевода<br>без разрешения его автора запрещены</i></p> | ||
+ | |||
=Коммуникации между роботами= | =Коммуникации между роботами= | ||
− | + | Если у вас есть больше чем один модуль 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" и возрастающих чисел с помощью функции | |
− | BluetoothStatus(conn) | + | SendRemoteString(conn,queue,string), одновременно получая сообщения от подчиненного функцией ReceiveRemoteString(queue,clear,string) и отображая эту информацию. |
− | + | ||
− | ReceiveRemoteString(queue,clear,string) | + | //MASTER |
− | //MASTER | + | #define BT_CONN 1 |
− | #define BT_CONN 1 | + | #define INBOX 1 |
− | #define INBOX 1 | + | #define OUTBOX 5 |
− | #define OUTBOX 5 | + | |
− | sub BTCheck(int conn){ | + | sub BTCheck(int conn){ |
− | if (!BluetoothStatus(conn)==NO_ERR){ | + | if (!BluetoothStatus(conn)==NO_ERR){ |
− | TextOut(5,LCD_LINE2,"Error"); | + | TextOut(5,LCD_LINE2,"Error"); |
− | Wait(1000); | + | Wait(1000); |
− | Stop(true); | + | Stop(true); |
− | } | + | } |
− | } | + | } |
− | task main(){ | + | |
− | string in, out, iStr; | + | task main(){ |
− | int i = 0; | + | string in, out, iStr; |
− | BTCheck(BT_CONN); //check slave connection | + | int i = 0; |
− | while(true){ | + | BTCheck(BT_CONN); //check slave connection |
− | iStr = NumToStr(i); | + | while(true){ |
− | out = StrCat("M",iStr); | + | iStr = NumToStr(i); |
− | TextOut(10,LCD_LINE1,"Master Test"); | + | out = StrCat("M",iStr); |
− | TextOut(0,LCD_LINE2,"IN:"); | + | TextOut(10,LCD_LINE1,"Master Test"); |
− | TextOut(0,LCD_LINE4,"OUT:"); | + | TextOut(0,LCD_LINE2,"IN:"); |
− | ReceiveRemoteString(INBOX, true, in); | + | TextOut(0,LCD_LINE4,"OUT:"); |
− | SendRemoteString(BT_CONN,OUTBOX,out); | + | ReceiveRemoteString(INBOX, true, in); |
− | TextOut(10,LCD_LINE3,in); | + | SendRemoteString(BT_CONN,OUTBOX,out); |
− | TextOut(10,LCD_LINE5,out); | + | TextOut(10,LCD_LINE3,in); |
− | Wait(100); | + | TextOut(10,LCD_LINE5,out); |
− | i++; | + | Wait(100); |
− | } | + | i++; |
− | } | + | } |
− | + | } | |
− | SendRemoteString | + | |
+ | Программа подчиненного модуля очень похожа, но использует функцию 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 | + | //MASTER |
− | #define BT_CONN 1 | + | #define BT_CONN 1 |
− | #define | + | #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){ | + | sub BTCheck(int conn){ |
− | if (!BluetoothStatus(conn)==NO_ERR){ | + | if (!BluetoothStatus(conn)==NO_ERR){ |
− | TextOut(5,LCD_LINE2,"Error"); | + | TextOut(5,LCD_LINE2,"Error"); |
− | Wait(1000); | + | Wait(1000); |
− | Stop(true); | + | Stop(true); |
− | } | + | } |
− | } | + | } |
− | task main(){ | + | |
− | + | task main(){ | |
− | + | BTCheck(BT_CONN); | |
− | 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); | |
− | until( | + | MOTOR(OUT_A,100); |
− | + | Wait(1000); | |
− | + | MOTOR(OUT_A,0); | |
− | Wait( | + | } |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | Wait(1000); | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Подводим итоги== | ==Подводим итоги== | ||
− | + | В этой главе вы изучили некоторые простые аспекты использования Bluetooth для связи между роботами. Мы соединили по радиоканалу 2 модуля NXT, отправляли и принимали строки и числа, ожидали подтверждение получения. Последнее очень важно в случае необходимости в надежном канале передачи информации. | |
− | + | ||
− | + | В качестве дополнительной возможности вы также научились отдавать мастером прямые команды для подчиненного модуля NXT. | |
− |
Текущая версия на 09:15, 20 мая 2009
Автор: 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.