Webots¶
Webots is an open-source robot simulator. The phyelds-webots
package runs phyelds programs on its robots: Webots moves the robots and simulates their
radios, and phyelds computes fields on top of them.
pip install phyelds-webots
Install it in the Python that Webots runs the controllers with, which must be Python 3.12
or later. By default Webots runs python3 from the PATH; you can pick another one in the
preferences of Webots, or in the runtime.ini of a controller. The controller module
comes with Webots.
How it works¶
In Webots each robot runs its own controller, in its own process: nobody sees the whole
swarm. So each robot is a device that talks over a radio, as a board does (see
MicroPython and the ESP32). phyelds.webots.AggregateController runs the
rounds of a robot on phyelds.vm.device.Device:
- Devices: each robot is a device, identified by its name. Robot names must be unique.
- Rounds:
run_round()runs the program once, and returns its result. Call it after eachrobot.step. - Neighbors: the robots whose
ReceivertheEmitterof the robot reaches. Itsrangefield sets the communication range; aninfra-redemitter also needs a line of sight. - Synchronous rounds: Webots delivers a message in the step after it was sent. With a round at every step of every robot, a robot receives in each round what its neighbors sent in the previous one, as in the phyelds simulator. A neighbor that stops sending is forgotten at the next round.
- Sensors: every robot senses
time(the simulation time) and, if you pass the name of aGPSdevice withgps=, itsposition.sensors=adds more, from a function called at each round. - Outputs: what the program
stores is inoutputs; the controller applies it to the robot (motors, LEDs).
The robot needs an Emitter and a Receiver, by default named emitter and receiver
(emitter= and receiver= choose others).
Example¶
Five robots stand in a line, 10 cm apart, and their radio reaches 15 cm. Each robot
computes how many hops it is from the robot named source, and lights its LED when it is
close to it:
@aggregate
def hops_to_source():
"""Hops to the source; the LED shows the area around it."""
distance = distance_to(sense("source"), hops_distance())
store("led", distance < 2)
return distance
def main() -> None:
"""Run a round at every step, and apply the outputs to the robot."""
robot = Robot()
is_source = robot.getName() == "source"
aggregate_controller = AggregateController(
robot, hops_to_source, sensors=lambda: {"source": is_source}
)
led = robot.getDevice("led")
last = None
while robot.step(int(robot.getBasicTimeStep())) != -1:
hops = aggregate_controller.run_round()
led.set(int(aggregate_controller.outputs["led"]))
if hops != last:
print("phyelds", robot.getName(), hops, flush=True)
last = hops
The world and the controller are in
docs/examples/webots:
open worlds/line.wbt in Webots to run it. The tests run the same world in Webots,
headless.
Notes¶
- Messages are bytes: they are encoded with the wire
format, so what travels must be something the codec
knows. Values of your own dataclasses need
register. - Compact paths:
paths="compact"makes the messages smaller (see compact paths). All the robots must use the same mode. - Headless runs:
webots --batch --mode=fast --no-rendering --minimize --stdout --stderr world.wbtruns a world without a window, for instance in CI underxvfb-run.