Программирование LEGO NXT роботов на языке NXC - Коммуникации между роботами

Материал из roboforum.ru Wiki
Версия от 09:03, 20 мая 2009; =DeaD= (обсуждение | вклад) (Отправка чисел с подтверждением)
Перейти к: навигация, поиск

Автор: 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 должна быть очищена, иначе мастер продолжит отправлять сообщения не ожидая больше никаких подтверждений, потому что мы забыли очистить эту переменну.

Подчиненный в бесконечном цикле проверяет ячейку и если она не пуста, показывает значение на экран и отправляет мастеру подтверждение о получении. В начале программы я отправил подтверждение безо информации которую подтверждаю, чтобы разблокировать мастера. in fact, without this trick, if the master program is started for first, it would hang even if we start slave later. This way the first few messages get lost, but you can start master and slave programs in different moments without the risk of hanging.

//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)
  }
}

Прямые команды

There's another cool feature about Bluetooth communication: master can directly control its slaves. In the next example, the master sends the slave direct commands to play sounds and move a motor; there is no need for a slave program, since it is the firmware of the slave NXT to receive and manage messages!

//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);
}

Подводим итоги

In this chapter we studied some of the basic aspects of Bluetooth communication between robots: connecting two NXTs, sending and receiving strings, numbers and waiting for delivery ackowledgments. This last aspect is very important when a secure communication protocol is needed. As extra feature, you also learned how to send direct commands to a slave brick.