Make your own Robot

The Gridsim library provides a Robot class that manages underlying behavior and drawing of robots, making it easy for you to quickly implement your own functionality and algorithms.

In fact, the default Robot class is an abstract class; you must implement your own Robot subclass. There are five abstract Robot methods that you must implement in your own class. (Inputs and outputs are not shown.)

  • move(): Step-wise movement of the robot on the grid

  • comm_criteria(): Distance-based criteria for whether or not another robot is within communication range of this robot.

  • receive_msg(): Code that is run when a robot receives a message

  • init(): Code that is run once when the robot is created

  • loop(): Code that is run in every step of the simulation

It also includes an optional method you may want to implement in your subclass:

  • msg_received(): Code that is run when a robot’s successfully sends a message to another robot.

In general, you will likely want to implement your own robots with an additional two layers of subclasses. This allows you to separate the physical robot platform you are representing from the algorithsm/code you are running on that platform.

First, you create a subclass that represents the physical robot system you are representing, such as a Turtlebot or Kilobot. This implements abstract methods that are properties of the physical system, such as the communication range (comm_criteria()) and movement restrictions (move()). This is still an abstract class.

Second, you create a subclass of your new class for implementing specific algorithms or code on your new robot platform. Here you will implement message handling (receive_msg() and optionally msg_received()) and onboard code (init() and loop()).

Custom robot example

Below is an example of the structure described above to create a simple robot that bounces around the arena.

First, we create , a robot with a circular communication radius of 5 grid cells that can move in the cardinal directions to any of four cells surrounding it. This robot is already provided in the library as GridRobot; you need not re-implement this robot platform if it meets your needs.

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from typing import Tuple

from .robot import Robot
# If you are building your own Robot class, you would instead use:
# from gridsim import Robot


class GridRobot(Robot):
    STAY = 0
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4
    DIRS = [STAY, UP, DOWN, LEFT, RIGHT]

    def __init__(self, x: int, y: int, comm_range: float = 5):
        """
        Create a robot that moves along the cardinal directions. Optionally, you
        can specify a communication range for the robots.

        Parameters
        ----------
        x : int
            Starting x position (grid cell) of the robot
        y : int
            Starting y position (grid cell) of the robot
        comm_range : float, optional
            Communication radius (in grid cells) of the robot, by default 5
        """
        # Run all of the initialization for the default Robot class, including
        # setting the starting position
        super().__init__(x, y)

        self._comm_range = comm_range
        # Start with the robot stationary
        self._move_cmd = GridRobot.STAY

    def set_direction(self, dir: int):
        """
        Helper function to set the direction the robot will move. Note that this
        will persist (the robot will keep moving) until the direction is
        changed.

        Parameters
        ----------
        dir : int
            Direction to move, one of UP, DOWN, LEFT, RIGHT, or STAY

        Raises
        ------
        ValueError
            If given direction is not one of UP, DOWN, LEFT, RIGHT, or STAY
        """
        if dir in GridRobot.DIRS:
            self._move_cmd = dir
        else:
            raise ValueError('Invalid movement direction "{}"'.format(dir))

    def move(self) -> Tuple[int, int]:
        """
        Determine the cell the Robot will move to, based on the direction set in
        by :meth:`~gridsim.grid_robot.GridRobot.set_motors`.

        Returns
        -------
        Tuple[int, int]
            (x,y) grid cell the robot will move to, if possible/allowed
        """
        x, y = self.get_pos()
        if self._move_cmd == GridRobot.UP:
            y -= 1
        elif self._move_cmd == GridRobot.DOWN:
            y += 1
        elif self._move_cmd == GridRobot.RIGHT:
            x += 1
        elif self._move_cmd == GridRobot.LEFT:
            x -= 1
        # else STAY, which keeps current position
        return x, y

    def comm_criteria(self, dist: int) -> bool:
        """
        Robots can communicate if their Euclidean distance is <= the radius
        specified at initialization (by default, 5 cells)

        Parameters
        ----------
        dist : int
            Euclidean distance of the other robot with which to communicate

        Returns
        -------
        bool
            Whether distance is <= the communication radius
        """
        return dist <= self._comm_range

With our robot platform in place, we can now implement a Robot that implements whatever code we want the robot to run. In this case, it’s a simple robot that chooses a random movement every 10 ticks and changes color when it first communicates with other robots.

 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
29
30
31
32
33
34
35
36
37
38
import random

from gridsim.grid_robot import GridRobot
import gridsim as gs


class RandomRobot(GridRobot):
    # Change direction every 10 ticks
    DIR_DURATION = 10

    def init(self):
        self.set_color(255, 0, 0)
        self._msg_sent = False

        # Next tick when Robot will change direction
        self._next_dir_change = self.get_tick()

    def receive_msg(self, msg: gs.Message, dist: float):
        # This robot got a message from another robot
        self._msg_sent = True

    def loop(self):

        # Change direction every DIR_DURATION ticks
        tick = self.get_tick()
        if tick >= self._next_dir_change:
            new_dir = random.choice(GridRobot.DIRS)
            self.set_direction(new_dir)
            self._next_dir_change = tick + RandomRobot.DIR_DURATION

        # Broadcast a test message to any robots nearby
        msg = gs.Message(self.id, {'test': 'hello'})
        self.set_tx_message(msg)

        # Change color depending on whether messages have been sent or received
        # Robot will be white when it has successfully sent & received a message
        green = 255 * self._msg_sent
        self.set_color(255, green, 0)

Notice that the abstraction layers mean that you have to write very little additional code to implement a new algorithm for your robot.