Examples¶
Basic Examples¶
These three examples show how to create a world, add robots and objects, read sensors and perform actions.
E-puck¶
>>> import pyenki
>>> world = pyenki.World()
>>> epuck = pyenki.EPuck()
>>> world.add_object(epuck)
>>> epuck.position = (-10, 0)
>>> epuck.set_led_ring(True)
>>> world.add_object(pyenki.CircularObject(2.0, 5.0, -1, pyenki.Color(0.3, 0.7, 0)))
>>> world.step(0.1)
>>> epuck.prox_values
[104.60820372038921, ...
>>> epuck.camera_image
[(0.5, 0.5, 0.5), ...
Marxbot¶
>>> import pyenki
>>> # create a world surrounded by a cylindrical wall.
>>> world = pyenki.World(r=20.0, walls_color=pyenki.Color(0.8, 0.2, 0.1))
>>> marxbot = pyenki.Marxbot()
>>> world.add_object(marxbot)
>>> marxbot.position = (0.0, 0.0)
>>> marxbot.angle = 0.0
>>> # Spin the robot on itself
>>> marxbot.left_wheel_target_speed = 5.0
>>> marxbot.right_wheel_target_speed = -5.0
>>> # Read the omnidirectional rgb-d camera
>>> # Distances
>>> marxbot.scanner_distances
[19.68757743875876, ...
>>> # Image
>>> marxbot.scanner_image
[[(0.8, 0.2, 0.1), ...
Thymio¶
>>> import pyenki
>>> world = pyenki.World()
>>> thymio = pyenki.Thymio2(use_aseba_units=False)
>>> world.add_object(thymio)
>>> thymio.position = (14.1, 7.2)
>>> thymio.angle = 4.0
>>> # Spin the robot on itself
>>> thymio.motor_left_target = 10.2
>>> thymio.motor_right_target = -10.2
>>> # Switch the top LED yellow
>>> thymio.set_led_top(0.5, 0.5, 0.0)
>>> world.add_object(pyenki.RectangularObject(5.0, 5.0, 5.0, -1, pyenki.Color(0.8, 0.3, 0)))
>>> world.step(0.1)
>>> thymio.prox_values
[0.0, 0.0, 0.0, 3043.220367053277, 0.0, 0.0, 0.0]
Objects¶
>>> import pyenki
>>> world = pyenki.World()
>>>
>>> c = pyenki.CompositeObject(
>>> [
>>> ([(0, 1), (0, 0.5), (2, 0.5), (2, 1)], 1.0),
>>> ([(0, -0.5), (0, -1), (2, -1), (2, -0.5)], 1.0),
>>> ([(0, 0.5), (0, -0.5), (0.5, -0.5), (0.5, 0.5)], 1.0)
>>> ],
>>> -1, color=pyenki.Color(0, 0.5, 0.5))
>>> world.add_object(c)
>>>
>>> triangle = pyenki.ConvexObject(
>>> [(0.0, 0.0), (1.0, -1.0), (1.0, 1.0)],
>>> 1, -1, color=pyenki.Color(0.5, 0.5, 0.0))
>>> triangle.position = (5, 0)
>>> world.add_object(triangle)
>>>
>>> cylinder = pyenki.CircularObject(1.0, 1.0, -1, color=pyenki.Color(0.5, 0.0, 0.5))
>>> cylinder.position = (10, 0)
>>> world.add_object(cylinder)
>>>
>>> box = pyenki.RectangularObject(2.0, 1.0, 1.0, -1, color=pyenki.Color(0.2, 0.5, 0.7))
>>> box.position = (15, 0)
>>> world.add_object(box)
>>> # at the moment textures are used to compute the sensors (cameras) response
>>> # but are ignored when displaying the object
>>> colorful_box_shape = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]
>>> colorful_box_colors = [pyenki.Color.red, pyenki.Color(0.5, 0.5, 0.0),
pyenki.Color.green, pyenki.Color(0.0, 0.5, 0.5)]
>>> colorful_box = pyenki.ConvexObject(colorful_box_shape, 1, -1, side_color=colorful_box_colors)
>>> colorful_box.position = (20, 0)
>>> world.add_object(colorful_box)
Hello Thymio¶
In the most common case, you want to subclass one or more robots adding the appropriate controller.
Then, you setup up the world, adding as many objects as needed.
Finally, you may either run the simulation in real time inside a Qt application,
or write your own loop where to call World.step
.
In this example, a Thymio will advance as long as there is not a obstacle (a wall) in front of it, when it will stop and switch the LED color to red from green.
import pyenki import sys import math from typing import Union # We sublass `pyenki.Thymio2`. This way, the world update step will automatically call # also the Thymio `controlStep`. class ControlledThymio2(pyenki.Thymio2): # This is the method we have to overwrite def controlStep(self, dt: float) -> None: # Check if there is an obstacle in front of us value = self.prox_values[2] if value > 3000: # A lot of light is reflected => there is an obstacle, # so we stop and switch the LED to red speed: Union[float, int] = 0 if self.use_aseba_units: # Aseba uses integers and # encodes LED values between 0 and 32 (fully lit) self.set_led_top(red=32) else: # The native enki interface uses instead float and # encodes colors between 0 and 1 self.set_led_top(red=1.0) else: if self.use_aseba_units: # Aseba uses integers and encodes speed between -500 and +500 speed = 300 self.set_led_top(green=32) else: # The native enki interface uses instead float and # encodes speed in centimeter per second. speed = 10.0 self.set_led_top(green=1.0) self.motor_left_target = speed self.motor_right_target = speed def setup(aseba: bool = False) -> pyenki.World: # We create an unbounded world world = pyenki.World() # We add a Thymio at the origin, # which can optionally use Aseba-like units and types. thymio = ControlledThymio2(use_aseba_units=aseba) thymio.position = (0, 0) thymio.angle = 0 world.add_object(thymio) # and a wall a bit in forward, in front of the Thymio. wall = pyenki.RectangularObject(l1=10, l2=50, height=5, mass=1, color=pyenki.Color(0.5, 0.3, 0.3)) wall.position = (30, 0) world.add_object(wall) return world def run(world: pyenki.World, gui: bool = False, T: float = 10, dt: float = 0.1, orthographic: bool = False) -> None: if gui: # We can either run a simulation [in real-time] inside a Qt application world.run_in_viewer(cam_position=(0, 0), cam_altitude=70.0, cam_yaw=0.0, cam_pitch=-math.pi / 2, walls_height=10, orthographic=orthographic) else: # or we can write our own loop that run the simulaion as fast as possible. steps = int(T // dt) for _ in range(steps): world.step(dt) if __name__ == '__main__': world = setup('--aseba' in sys.argv) run(world, gui='--gui' in sys.argv, orthographic='--orthographic' in sys.argv)
Proximity Communication¶
In this example, two Thymios placed in front of each other, transmit messages using proximity communication. After each control step, we print the received messages.
import pyenki
import math
# Create a world and place two thymios in front of themselves
world = pyenki.World()
thymio1 = pyenki.Thymio2()
thymio1.position = (0, 0)
world.add_object(thymio1)
thymio2 = pyenki.Thymio2()
thymio2.position = (30, 0)
thymio2.angle = math.pi
world.add_object(thymio2)
# Enable the communication and let them send different payloads
thymio1.prox_comm_enable = True
thymio2.prox_comm_enable = True
thymio1.prox_comm_tx = 111
thymio2.prox_comm_tx = 222
for _ in range(100):
world.step(0.1)
# Check which messages they are receiving
print('thymio1: ', '\t\n'.join(f'Got message {e.rx} [{e.payloads} {e.intensities}]'
for e in thymio1.prox_comm_events))
print('thymio2: ', '\t\n'.join(f'Got message {e.rx} [{e.payloads} {e.intensities}]'
for e in thymio2.prox_comm_events))
Interactive GUI¶
The QWidget that display the world can be run either two modes: - embedded in a standalone QtApplication, like in Hello Thymio, that blocks until it terminates - or using an already running QtApplication, which does not block and allow to visualize the world while manipulating it in an interactive session (e.g., in a jupyter notebook or console)
For instance, this script
import math
from PyQt5 import QtCore
import pyenki
world = pyenki.World(100)
thymio = pyenki.Thymio2()
thymio.position = (0, 0)
thymio.motor_left_target = -10
thymio.motor_right_target = 10
world.add_object(thymio)
# Check if a QtApplication is running
if not QtCore.QCoreApplication.instance():
print('No QtApplication active')
else:
# Create a view --- which will also run ``world.step`` --- and display it
view = pyenki.WorldView(world, run_world_update=True, cam_position=(0, 0),
cam_altitude=80, cam_pitch=-math.pi / 2, cam_yaw=math.pi / 2,
orthographic=True)
view.show()
will spawn a live world view when run inside an jupyter console
$ jupyter console
>>> %gui qt5
>>> %run interactive_view.py