Emitter and Receiver

Communicate with the Main Supervisor

This page is under construction.

Basics

The emitter and receiver are used to communicate with the Main Supervisor, Erebus’ game manager. The emitter can be used to score victims or hazard maps as well as signify the end of play to receive an exit bonus, while the receiver can be used to notify the robot controller in the event of a Lack of Progress.

First, set up the emitter and receiver:

   
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from controller import Robot, Receiver, Emitter # Import Receiver and Emitter

robot = Robot()

receiver = robot.getDevice("receiver") # Retrieve the receiver and emitter by device name
emitter = robot.getDevice("emitter")
gps = robot.getDevice("gps") # Retrieve the gps by device name

timestep = int(robot.getBasicTimeStep())

receiver.enable(timestep) # Enable the receiver. Note that the emitter does not need to call enable()

while robot.step(timestep) != -1:
    pass
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>

#include <webots/Robot.hpp>
#include <webots/Receiver.hpp> // Include Receiver.hpp and Emitter.hpp
#include <webots/Emitter.hpp>

using namespace webots;
using namespace std;

int main(int argc, char **argv) {

  Robot *robot = new Robot();
  
  Emitter* emitter = robot->getEmitter("emitter"); // Retrieve the receiver and emitter by device name
  Receiver* receiver = robot->getReceiver("receiver");
  GPS* gps = robot->getGPS("gps"); // Retrieve the gps by device name

  int timeStep = (int)robot->getBasicTimeStep();
  
  receiver->enable(timeStep); // Enable the receiver. Note that the emitter does not need to be enabled.
  
  while (robot->step(timeStep) != -1) {
    
  }
  
  delete robot;
  return 0;
}

Note: In contrast to other devices, the emitter does not have to be enabled.

Scoring Game Elements

To earn points for identifying victim letters and hazard maps, the emitter should be used to report the position and the type of the game element. The message to the Main Supervisor should be formatted as follows:

X Pos (integer), Z Pos (integer), Victim Type (character)

Note: The X position and Z Position should be in centimeters. Thus, it is important to multiply the position given by the GPS by 100 to convert meters to centimeters.

   
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from controller import Robot, Receiver, Emitter
import struct # Use the struct module in order to pack the message sent to the Main Supervisor

...
    
victimType = bytes('H', "utf-8") # The victim type being sent is the letter 'H' for harmed victim

position = gps.getValues() # Get the current gps position of the robot
x = int(position[0] * 100) # Get the xy coordinates, multiplying by 100 to convert from meters to cm 
y = int(position[2] * 100) # We will use these coordinates as an estimate for the victim's position

message = struct.pack("i i c", x, y, victimType) # Pack the message.
    
emitter.send(message) # Send out the message
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <cstring>

...

char message[9]; // Here we use a 9 byte array, since sizeof(int + int + char) = 9

const double* position = gps->getValues(); // Get the current gps position of the robot
int x = (int) position[0] * 100; // Get the xy coordinates, multiplying by 100 to convert from meters to cm 
int y = (int) position[2] * 100; // We will use these coordinates as an estimate for the victim's position

int victim_pos[2] = {x, y};

memcpy(message, victim_pos, sizeof(victim_pos)); // Copy the victim position into the message array
message[8] = 'H'; // The victim type is harmed

emitter->send(message, sizeof(message)); // Send out the message array. Note that the 2nd parameter must be the size of the message

Signaling Lack of Progress

To call of lack of progress autonomously (using your program), send the character ‘L’ using the emitter. The letter ‘L’ will be sent back to the robot’s receiver as per the Lack of Progress section below.

   
1
2
message = struct.pack('c', 'L'.encode()) # message = 'L' to activate lack of progress
emitter.send(message) # Send message
1
2
char message[1] = { 'L' }; // message = 'L' to activate lack of progress
emitter->send(message, sizeof(message)); // Send out the message array. Note that the 2nd parameter must be the size of the message

Signaling End of Play

To signal to the Main Supervisor the end of play in order to receive an exit bonus, send the character ‘E’ using the emitter:

   
1
emitter.send(bytes('E', "utf-8")) # Send the letter 'E' to signify exit
1
2
char message = 'E';
emitter->send(&message, 1); // Send the letter 'E' to signify exit

Lack of Progress

The receiver can be used to detect lack of progress messages sent by the Main Supervisor, so that the robot controller can respond accordingly:

   
1
2
3
4
5
6
if receiver.getQueueLength() > 0: # If receiver queue is not empty
    receivedData = receiver.getBytes()
    tup = struct.unpack('c', receivedData) # Parse data into character
    if tup[0].decode("utf-8") == 'L': # 'L' means lack of progress occurred
        print("Detected Lack of Progress!") 
    receiver.nextPacket() # Discard the current data packet
1
2
3
4
5
6
7
if(receiver->getQueueLength() > 0) { // If receiver queue is not empty
    char *message = (char *)receiver->getData(); // Grab data as a string
    if (message[0] == 'L') { // 'L' means a lack of progress occurred
        cout << "Detected Lack of Progress!" << endl;
        receiver->nextPacket(); // Discard the current data packet
    }
}

Game Information

Your program can retrieve the robot’s current game score and the time remaining in your run. Use the emitter to send the character ‘G’ to the supervisor, and it will send back a package that starts with the letter ‘G’ and is followed by the score and time remaining:

   
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
message = struct.pack('c', 'G'.encode()) # message = 'G' for game information
emitter.send(message) # send message

...

if receiver.getQueueLength() > 0: # If receiver queue is not empty
    receivedData = receiver.getBytes()
    tup = struct.unpack('c f i', receivedData) # Parse data into char, float, int
    if tup[0].decode("utf-8") == 'G':
        print(f'Game Score: {tup[1]}  Remaining time: {tup[2]}')
        receiver.nextPacket() # Discard the current data packet
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
char message[1] = { 'G' }; // message = 'G' for game information
emitter->send(message, sizeof(message)); // Send out the message array. Note that the 2nd parameter must be the size of the message

...

if(receiver->getQueueLength() > 0) { // If receiver queue is not empty
    char *receivedData = (char *)receiver->getData(); // Grab data as a string
    float score = 0.0;
    int time = 0;
    if (receivedData[0] == 'G') {
        memcpy(&score, receivedData + 4, 4); // Score stored in bytes 4 to 7
        memcpy(&time, receivedData + 8, 4);  // Remaining time stored in bytes 8 to 11
        cout << "Game Score: " << score << " Remaining time: " << time << endl;
        receiver->nextPacket(); // Discard the current data packet
    }
}

Map Bonus

How to get the map bonus