Basic Usage¶
This will walk you through setting up your first robot and complete simulation.
On this page
Test using built in examples¶
The examples are in the examples directory of the source code. In the near future, I’ll set up a way to run the examples directly when you install the package.
Creating a simple robot¶
For more detailed information about developing custom robots, see Make your own Robot.
To start, we will only need to make a simple robot based on the GridRobot
. This needs to implement three methods:
receive_msg()
: Code that is run when a robot receives a messageinit()
: Code that is run once when the robot is createdloop()
: Code that is run in every step of the simulation
Create a file for your robot class. Let’s call it random_robot.py
. Below is a simple Robot that moves randomly and changes direction every 10 seconds. You can copy this or directly download random_robot.py
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 | 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)
# Sample the environment at the current location
c = self.sample()
# Change color depending on whether messages have been sent or received
# Robot will be white when it has successfully sent & received a message
blue = 255 * self._msg_sent
# self.set_color(255, green, 0)
self.set_color(255-c[0], 255-c[1], blue)
|
A minimal simulation example¶
To run a simulation, you need to create a couple of robots, place them in a World
. Then you call the step()
method to execute you simulation step-by-step. step()
will handle running all of the robots’ code, as well as communication and movement.
We also want give our Robots something to sense by adding en environment to the World. An environment here is represented with an image. (You’ll see what this looks like in the next step.) In each cell, the Robots can sense the color of the cell (i.e., the RGB pixel value) at that location with the sample()
method. If you set up the environment with an image whose resolution doesn’t match the grid dimensions, it will be rescaled, possibly stretching the image. To avoid any surprises, you should use an image whose resolution matches your grid dimensions (e.g., for a 50 × 50 grid, use a 50px × 50px image).
Use the code below or download minimal_simulation.py
and the example environment ex_env.png
.
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 | import gridsim as gs
from random_robot import RandomRobot
def main():
grid_width = 50 # Number of cells for the width & height of the world
num_robots = 5
num_steps = 100 # simulation steps to run
# Create a few robots to place in your world
robots = []
for n in range(num_robots):
robots.append(RandomRobot(grid_width/2 - n*2,
grid_width/2 - n*2))
# Create a 50 x 50 World with the Robots
world = gs.World(grid_width, grid_width,
robots=robots,
environment="ex_env.png")
# Run the simulation
for n in range(num_steps):
# Execute a simulation step
world.step()
# To make sure it works, print the tick (world time)
print('Time:', world.get_time())
print('SIMULATION FINISHED')
if __name__ == '__main__':
# Run the simulation if this program is called directly
main()
|
With these files and random_robot.py
in the same directory, and gridsim
installed, you should be able to run the code with:
$ python3 minimal_simulation.py
Adding the Viewer¶
With that simple example, you have no way to see what the robots are doing. For that, we add a Viewer
. This requires adding only two lines of code to our minimal simulation above.
Use the code below or download viewer_simulation.py
.
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 | import gridsim as gs
from random_robot import RandomRobot
def main():
grid_width = 50 # Number of cells for the width & height of the world
num_robots = 5
num_steps = 100 # simulation steps to run
# Create a few robots to place in your world
robots = []
for n in range(num_robots):
robots.append(RandomRobot(grid_width/2 - n*2,
grid_width/2 - n*2))
# Create a 50 x 50 World with the Robots
world = gs.World(grid_width, grid_width,
robots=robots,
environment="ex_env.png")
# Create a Viewer to display the World
viewer = gs.Viewer(world)
# Run the simulation
for n in range(num_steps):
# Execute a simulation step
world.step()
# Draw the world
viewer.draw()
# To make sure it works, print the tick (world time)
print('Time:', world.get_time())
print('SIMULATION FINISHED')
if __name__ == '__main__':
# Run the simulation if this program is called directly
main()
|
Notice that adding the Viewer slows down the time to complete the simulation, because the display rate of the Viewer limits the simulation rate. If you want to run lots of simulations, turn off your Viewer.
Using configuration files¶
Gridsim also provides the ConfigParser
for using YAML configuration files. This simplifies loading parameters and (as described in the next section) saving parameters with simulation results data.
The ConfigParser
is un-opinionated; it doesn’t place any restrictions on what your configuration files look like, as long as they’re valid YAML files.
Compared to our minimal_simulation.py
, we only need one line to create our ConfigParser
, from which we can retrieve any parameter values.
Use the code below or download config_simulation.py
and YAML configuration file simple_config.yml
.
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 gridsim as gs
from random_robot import RandomRobot
def main():
config = gs.ConfigParser('simple_config.yml')
print(config.get('name'))
grid_width = config.get('grid_width')
num_robots = config.get('num_robots')
# You can specify a default value in case a parameter isn't in the
# configuration file
num_steps = config.get('num_steps', default=100)
# Create a few robots to place in your world
robots = []
# Configuration values can also be lists, not just single values.
x_pos = config.get('robot_x_pos')
for n in range(num_robots):
robots.append(RandomRobot(x_pos[n],
grid_width/2 - n*2))
# Create a 50 x 50 World with the Robots
world = gs.World(grid_width, grid_width, robots=robots)
# Run the simulation
for n in range(num_steps):
# Execute a simulation step
world.step()
# To make sure it works, print the tick (world time)
print('Time:', world.get_time())
print('SIMULATION FINISHED')
if __name__ == '__main__':
# Run the simulation if this program is called directly
main()
|
Logging data¶
Gridsim has a built-in Logger
, designed to easily save data from your simulations to HDF5 files. This allows you to store complex data and simulation configurations together in one place. HDF5 files are also easy to read and write in many different programming languages.
There are three main ways to save data to your log files:
Save the parameters in your configuration with
log_config()
. (Note that not all data types can be saved withlog_config
. See its documentation for more details.)Save a single parameter (that’s not in your configuration file) with
log_param()
Save the state of your simulation/robots with
log_state()
. (This requires some setup.)
In order to log the state of the World, you first need to tell the Logger
what you want to save about the log_state
, this function is called and the result is added to your dataset. You can add as many aggregators as you want, each with their own name.
We can extend our config_simulation.py
to show the three types of logging described above. Use the code below or download logger_simulation.py
.
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 | import gridsim as gs
from typing import List
import numpy as np
from datetime import datetime
from random_robot import RandomRobot
def green_agg(robots: List[gs.Robot]) -> np.ndarray:
"""
This is a dummy aggregator function (for demonstration) that just saves
the value of each robot's green color channel
"""
out_arr = np.zeros([len(robots)])
for i, r in enumerate(robots):
out_arr[i] = r._color[1]
return out_arr
def main():
config = gs.ConfigParser('simple_config.yml')
print(config.get('name'))
grid_width = config.get('grid_width')
num_robots = config.get('num_robots')
# You can specify a default value in case a parameter isn't in the
# configuration file
num_steps = config.get('num_steps', default=100)
# Create a few robots to place in your world
robots = []
# Configuration values can also be lists, not just single values.
x_pos = config.get('robot_x_pos')
for n in range(num_robots):
robots.append(RandomRobot(x_pos[n],
grid_width/2 - n*2))
# Create a 50 x 50 World with the Robots
world = gs.World(grid_width, grid_width, robots=robots)
# Logger
trial_num = config.get('trial_num', default=1)
# Create a logger for this world that saves to the `test.h5` file
logger = gs.Logger(world, 'test.h5', trial_num=trial_num,
overwrite_trials=True)
# Tell the logger to run the `green_agg` function every time that
# `log_state` is called
logger.add_aggregator('green', green_agg)
# Save the contents of the configuration, but leave out the 'name' parameter
logger.log_config(config, exclude='name')
# Save the date/time that the simulation was run
logger.log_param('date', str(datetime.now()))
# Run the simulation
for n in range(num_steps):
# Execute a simulation step
world.step()
# Log the state every step
logger.log_state()
# To make sure it works, print the tick (world time)
print('Time:', world.get_time())
print('SIMULATION FINISHED')
if __name__ == '__main__':
# Run the simulation if this program is called directly
main()
|
Complete example¶
Most simulations will involve all of these components, and multiple trials. You can download a complete, detailed example here: complete_simulation.py
, as well as a corresponding YAML configuration file: ex_config.yml
Here, the configuration file is used as a command line argument, so it’s easy to switch what configuration file you use. Run it like this:
$ python3 complete_simulation.py ex_config.yml