Robot¶
The Robot class is the base class for all robots.
Typically, you would use an existing robot subclass:
from aitk.robots import Scribbler, Vehicle
robot1 = Scribbler()
robot2 = Vehicle()
After creating a robot, you can then add devices to it, and then add it to a world.
from aitk.robots import World, Camera
world = World(200, 200)
robot1.add_device(Camera())
world.add_robot(robot1)
world.add_robot(robot2)
__init__(self, x=0, y=0, a=0, color='red', name='Robbie', do_trace=True, height=0.25, max_trace_length=10, **kwargs)
special
¶
A simulated robot.
Option | Type | Description | Default |
---|---|---|---|
x | int | starting location in the horizontal direction. Leave 0 to place in a random location. | 0 |
y | int | starting location in the horizontal direction. Leave 0 to place in a random location. | 0 |
a | float | starting angle in degrees. | 0 |
color | str | the name of a color for the robot | "red" |
name | str | a name to give your robot | "Robbie" |
do_trace | bool | should the robot leave a trace? | True |
height | int | height of robot use number < 1 | 0.25 |
max_trace_length | int | max length of trace, in seconds | 10 |
state | dict | serializable memory for a robot | None |
va | float | angle velocity | None |
vx | float | x velocity | None |
vy | float | y velocity | None |
tva | float | angle target velocity | None |
tvx | float | x target velocity | None |
tvy | float | y target velocity | None |
va_max | float | angle max velocity | None |
vx_max | float | x max velocity | None |
vy_max | float | y max velocity | None |
va_ramp | float | angle linear acceleration | None |
vx_ramp | float | x linear acceleration | None |
vy_ramp | float | y linear acceleration | None |
image_data | dataset-name, index | to use a 3D set of images | None |
body | List | data structure that defines a robot body | None |
devices | List | list of serialized devices | None |
Source code in robots/robot.py
def __init__(
self,
x=0,
y=0,
a=0,
color="red",
name="Robbie",
do_trace=True,
height=0.25,
max_trace_length=10,
**kwargs
):
"""
A simulated robot.
Option | Type | Description | Default
------ | ---- | ----------- | -------
x | int | starting location in the horizontal direction. Leave 0 to place in a random location. | 0
y | int | starting location in the horizontal direction. Leave 0 to place in a random location. | 0
a | float | starting angle in degrees. | 0
color | str | the name of a color for the robot | "red"
name | str | a name to give your robot | "Robbie"
do_trace | bool | should the robot leave a trace? | True
height | int | height of robot use number < 1 | 0.25
max_trace_length | int | max length of trace, in seconds | 10
state | dict | serializable memory for a robot | None
va | float | angle velocity | None
vx | float | x velocity | None
vy | float | y velocity | None
tva | float | angle target velocity | None
tvx | float | x target velocity | None
tvy | float | y target velocity | None
va_max | float | angle max velocity | None
vx_max | float | x max velocity | None
vy_max | float | y max velocity | None
va_ramp | float | angle linear acceleration | None
vx_ramp | float | x linear acceleration | None
vy_ramp | float | y linear acceleration | None
image_data | dataset-name, index | to use a 3D set of images | None
body | List | data structure that defines a robot body | None
devices | List | list of serialized devices | None
"""
# Get the args:
config = {
"x": x,
"y": y,
"a": a, # degrees in the config file
"color": color,
"name": name,
"do_trace": do_trace,
"height": height,
"max_trace_length": max_trace_length,
}
# Update from the kwargs:
config.update(kwargs)
self.world = None
self._devices = []
self._initialize()
self.from_json(config)
add_device(self, device)
¶
Add a device to a robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
device |
Device |
the device to add |
required |
Source code in robots/robot.py
def add_device(self, device):
"""
Add a device to a robot.
Args:
device (Device): the device to add
"""
if device not in self._devices:
device.robot = self
self._devices.append(device)
if self.world is not None:
self.world.update() # request draw
else:
raise Exception("Can't add the same device to a robot more than once.")
add_device_ring(self, device_class, distance_from_center, start_degree, stop_degree, count, **kwargs)
¶
Adds a ring of devices at a given distance from the center of the robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
device_class |
class |
a class or function that receives position, a, and kwargs, and returns a device |
required |
distance_from_center |
float |
in CM |
required |
start_degree |
float |
angle of first device (0 points right) |
required |
stop_degree |
float |
angle of stop degree (counter clockwise) |
required |
count |
int |
number of sensors to add |
required |
Examples:
>>> robot.add_device_ring(RangeSensor, 10, 0, 360, 6, width=20)
Source code in robots/robot.py
def add_device_ring(self, device_class, distance_from_center,
start_degree, stop_degree, count, **kwargs):
"""
Adds a ring of devices at a given distance from the center of
the robot.
Args:
device_class (class): a class or function that receives position,
a, and kwargs, and returns a device
distance_from_center (float): in CM
start_degree (float): angle of first device (0 points right)
stop_degree (float): angle of stop degree (counter clockwise)
count (int): number of sensors to add
Note: use kwargs additional args to pass to device_class
Example:
```python
>>> robot.add_device_ring(RangeSensor, 10, 0, 360, 6, width=20)
```
"""
if isinstance(device_class, str):
DEVICES = importlib.import_module("aitk.robots.devices")
device_class = getattr(DEVICES, device_class)
if stop_degree < start_degree:
stop_degree += 360
span = stop_degree - start_degree
step_angle = span / (count - 1)
angle = start_degree
for i in range(count):
x, y = rotate_around(0, 0, distance_from_center, -angle * PI_OVER_180)
self.add_device(device_class(position=(x, y), a=angle, **kwargs))
angle += step_angle
backward(self, translate)
¶
Set the target translate velocity.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
translate |
float |
the speed to move the robot backward |
required |
Translate should be between 0 and 1, inclusive.
Source code in robots/robot.py
def backward(self, translate):
"""
Set the target translate velocity.
Args:
translate (float): the speed to move the robot backward
Translate should be between 0 and 1, inclusive.
"""
if 0 <= translate <= 1:
self.tvx = round(-translate * self.vx_max, 1)
else:
print("backward value is out of range; should be between 0 and 1, inclusive")
cast_ray(self, x1, y1, a, maxRange, x2=None, y2=None, ignore_robots=None)
¶
Cast a ray into this world and see what it hits.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
x1 |
int |
the x coordinate of ray origin |
required |
y1 |
int |
the y coordinate of ray origin |
required |
a |
int |
the angle of the ray to cast |
required |
maxRange |
float |
the length of ray to cast |
required |
x2 |
int |
the x coordinate of the endpoint |
None |
y2 |
int |
the y coordinate of the endpoint |
None |
ignore_robots |
List |
list of robots to ignore |
None |
Returns list of hits, furthest away first (back to front)
Source code in robots/robot.py
def cast_ray(self, x1, y1, a, maxRange, x2=None, y2=None, ignore_robots=None):
"""
Cast a ray into this world and see what it hits.
Args:
x1 (int): the x coordinate of ray origin
y1 (int): the y coordinate of ray origin
a (int): the angle of the ray to cast
maxRange (float): the length of ray to cast
x2 (int): the x coordinate of the endpoint
y2 (int): the y coordinate of the endpoint
ignore_robots (List): list of robots to ignore
Returns list of hits, furthest away first (back to front)
"""
return cast_ray(self.world, self, x1, y1, a, maxRange, x2, y2, ignore_robots)
del_device(self, device)
¶
Remove a device from a robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
device |
Device |
the device to remove |
required |
Source code in robots/robot.py
def del_device(self, device):
"""
Remove a device from a robot.
Args:
device (Device): the device to remove
"""
if isinstance(device, (str, int)):
device = self[device]
if device in self._devices:
device.robot = None
self._devices.remove(device)
else:
raise Exception("Device %r is not on robot." % device)
display(self, size=100)
¶
Display the robot's image.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
size |
int |
the size of the width and height of the image |
100 |
Source code in robots/robot.py
def display(self, size=100):
"""
Display the robot's image.
Args:
size (int): the size of the width and height of the
image
"""
image = self.get_image(size=size)
display(image)
draw(self, backend)
¶
Draw the robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
backend |
Backend |
the backend on which to draw |
required |
Source code in robots/robot.py
def draw(self, backend):
"""
Draw the robot.
Args:
backend (Backend): the backend on which to draw
"""
if self.do_trace:
time_step = self.world.time_step if self.world is not None else 0.1
if self.max_trace_length > 0:
max_trace_length = int(1.0 / time_step * self.max_trace_length)
else:
max_trace_length = 0
if max_trace_length == 0:
data = []
else:
data = self.trace[-max_trace_length:]
# None indicates a segment break
if all(data): # no segments
segments = [[(point[0], point[1]) for (point, direction) in data]]
else:
segments = []
current = []
for item in data:
if item is None:
segments.append(current)
current = []
else:
point, direction = item
current.append((point[0], point[1]))
if current:
segments.append(current)
for segment in segments:
backend.draw_lines(
segment,
stroke_style=self.trace_color,
)
self.trace = data
backend.pushMatrix()
backend.translate(self.x, self.y)
backend.rotate(self.a)
# Draw first:
for device in self._devices:
if device.type == "bulb":
device.draw(backend)
elif device.type == "beacon":
device.draw(backend)
# body:
for shape in self.body:
shape_name, color, args = shape
if self.stalled:
backend.strokeStyle(Color(255), 1)
else:
backend.strokeStyle(Color(0), 1)
if color is None:
backend.set_fill(self.color)
else:
backend.set_fill(Color(color))
if shape_name == "polygon":
backend.draw_polygon(args)
elif shape_name == "rectangle":
backend.draw_rect(*args)
elif shape_name == "ellipse":
backend.draw_ellipse(*args)
elif shape_name == "circle":
backend.draw_circle(*args)
elif shape_name == "line":
backend.draw_line(*args)
backend.noStroke()
# Draw on top of robot:
for device in self._devices:
if device.type not in ["bulb", "beacon"]:
device.draw(backend)
backend.popMatrix()
text = self._get_current_text(self.world.time)
if text:
backend.set_fill_style(Color(255))
pad = 10
box_pad = 5
width = self.world._backend.char_width * len(text)
height = 20
if self.x - pad - width < 0:
side = 1 # put on right
else:
side = -1 # put on left
if self.y - height < 0:
half = 1 # put on top
else:
half = -1 # put on bottom
points = [
(self.x, self.y),
(self.x + pad * side, self.y + height / 2 * half),
(self.x + pad * side, self.y + height * half),
(self.x + (pad + width + pad) * side, self.y + height * half),
(self.x + (pad + width + pad) * side, self.y),
(self.x + pad * side, self.y),
(self.x + pad * side, self.y + height / 4 * half),
]
backend.set_stroke_style(Color(0))
backend.set_fill_style(Color(255, 255, 255, 200))
backend.draw_polygon(points)
backend.set_fill_style(Color(0))
if side == 1: # right
if half == 1: # bottom
backend.text(text, self.x + (pad + box_pad), self.y + box_pad)
else: # top
backend.text(
text,
self.x + (pad + box_pad),
self.y - self.world._backend.char_height - box_pad,
)
else: # left
if half == 1: # bottom
backend.text(text, self.x - pad - width - box_pad, self.y + box_pad)
else: # top
backend.text(
text,
self.x - pad - width - box_pad,
self.y - self.world._backend.char_height - box_pad,
)
eat(self)
¶
If the robot is close enough to food, then this will eat it, removing it from the world, and requesting a redraw. Returns True if successfully eaten, and False otherwise.
Note: it must be within robot.eat_food_distance
Source code in robots/robot.py
def eat(self):
"""
If the robot is close enough to food, then this
will eat it, removing it from the world, and requesting
a redraw. Returns True if successfully eaten, and False
otherwise.
Note: it must be within robot.eat_food_distance
"""
success = False
if self.world is not None:
for food in [f for f in self.world._food if f.state == "on"]:
if distance(self.x, self.y, food.x, food.y) <= self.eat_food_distance:
success = True
self.world._event("food-off", robot=self, food=food)
return success
forward(self, translate)
¶
Set the target translate velocity.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
translate |
float |
the speed to move the robot forward |
required |
Arg should be between 0 and 1, inclusive.
Source code in robots/robot.py
def forward(self, translate):
"""
Set the target translate velocity.
Args:
translate (float): the speed to move the robot forward
Arg should be between 0 and 1, inclusive.
"""
if 0 <= translate <= 1:
self.tvx = round(translate * self.vx_max, 1)
else:
print("forward value is out of range; should be between 0 and 1, inclusive")
from_json(self, config)
¶
Load a robot from a JSON config dict.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
config |
dict |
a config dict |
required |
Source code in robots/robot.py
def from_json(self, config):
"""
Load a robot from a JSON config dict.
Args:
config (dict): a config dict
"""
DEVICES = importlib.import_module("aitk.robots.devices")
valid_keys = set([
"name", "state", "do_trace", "va", "vx", "vy",
"tva", "tvx", "tvy", "x", "y", "a", "va_max",
"vx_max", "vy_max", "va_ramp", "vx_ramp", "vy_ramp",
"image_data", "height", "color", "max_trace_length",
"body", "devices",
])
config_keys = set(list(config.keys()))
extra_keys = config_keys - valid_keys
if len(extra_keys) > 0:
raise TypeError("invalid key(s) for robot config: %r" % extra_keys)
if "name" in config:
self.name = config["name"]
if "state" in config:
self.state = defaultdict(int)
self.state.update(config["state"])
if "do_trace" in config:
self.do_trace = config["do_trace"]
if "va" in config:
self.va = config["va"]
if "vx" in config:
self.vx = config["vx"]
if "vy" in config:
self.vy = config["vy"]
if "tva" in config:
self.tva = config["tva"]
if "tvx" in config:
self.tvx = config["tvx"]
if "tvy" in config:
self.tvy = config["tvy"]
if "va_max" in config:
self.va_max = config["va_max"]
if "vx_max" in config:
self.vx_max = config["vx_max"]
if "vy_max" in config:
self.vy_max = config["vy_max"]
if "va_ramp" in config:
self.va_ramp = config["va_ramp"]
if "vx_ramp" in config:
self.vx_ramp = config["vx_ramp"]
if "vy_ramp" in config:
self.vy_ramp = config["vy_ramp"]
if "x" in config:
self.x = config["x"]
if "y" in config:
self.y = config["y"]
if "a" in config:
self.a = degrees_to_world(config["a"])
if "image_data" in config:
self.image_data = config["image_data"] # ["dataset", index]
if len(self.image_data) == 0:
self.get_dataset_image = None
else:
self.get_dataset_image = get_dataset(self.image_data[0])
if "height" in config:
self.height = config["height"] # ratio, 0 to 1 of height
if "color" in config:
self._set_color(config["color"])
if "max_trace_length" in config:
self.max_trace_length = config["max_trace_length"]
if "body" in config:
self.body[:] = config["body"]
self._init_boundingbox()
if "devices" in config:
# FIXME: raise if lengths/types don't match
for i, deviceConfig in enumerate(config["devices"]):
if i < len(self):
if self[i].__class__.__name__ == deviceConfig["class"]:
# already a device, let's reuse it:
device = self[i]
device.initialize()
device.from_json(deviceConfig)
else:
raise Exception(
"can't use reset; config changed; use load_world"
)
else:
device = None
try:
device_class = getattr(DEVICES, deviceConfig["class"])
device = device_class(**deviceConfig)
except Exception:
raise Exception(
"Failed to create device: %s(**%s)"
% (deviceConfig["class"], deviceConfig)
)
if device:
self.add_device(device)
get_image(self, size=100)
¶
Get an image of the robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
size |
int |
size in pixels around robot |
100 |
Source code in robots/robot.py
def get_image(self, size=100):
"""
Get an image of the robot.
Args:
size (int): size in pixels around robot
"""
picture = self.world.get_image()
start_x = round(
max(self.x * self.world.scale - size / 2, 0)
)
start_y = round(
max(self.y * self.world.scale - size / 2, 0)
)
rectangle = (
start_x,
start_y,
min(
start_x + size, self.world.width * self.world.scale
),
min(
start_y + size,
self.world.height * self.world.scale,
),
)
picture = picture.crop(rectangle)
return picture
get_image_3d(self, degrees)
¶
Return the 3D image at the proper angle.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
degrees |
int |
the angle of the image to get |
required |
Source code in robots/robot.py
def get_image_3d(self, degrees):
"""
Return the 3D image at the proper angle.
Args:
degrees (int): the angle of the image to get
"""
return self.get_dataset_image(self.image_data[1], degrees)
get_max_trace_length(self)
¶
Get the max length of the trace in seconds.
Source code in robots/robot.py
def get_max_trace_length(self):
"""
Get the max length of the trace in seconds.
"""
return self.max_trace_length
get_pose(self)
¶
Get the pose of the robot (x, y, a) where a (direction) is in degrees.
Source code in robots/robot.py
def get_pose(self):
"""
Get the pose of the robot (x, y, a) where a (direction)
is in degrees.
"""
return (self.x, self.y, world_to_degrees(self.a))
get_time(self)
¶
Get the clock time of the world.
Source code in robots/robot.py
def get_time(self):
"""
Get the clock time of the world.
"""
if self.world:
return self.world.time
get_velocity(self, target=False)
¶
Get the current (or target) translate and rotate velocities of the robot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
target |
bool |
get the target velocities if True; get the actual velocities otherwise |
False |
Source code in robots/robot.py
def get_velocity(self, target=False):
"""
Get the current (or target) translate and rotate velocities
of the robot.
Args:
target (bool): get the target velocities if True; get the actual
velocities otherwise
"""
if not target:
return (self.vx / self.vx_max, self.va / self.va_max)
else:
return (self.tvx / self.vx_max, self.tva / self.va_max)
get_widget(self, size=None, show_robot=None, attributes=None)
¶
Get the robot widget.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
size |
int |
size in pixels around robot |
None |
show_robot |
bool |
show picture of robot |
None |
attributes |
list |
items to include, or "all" |
None |
Source code in robots/robot.py
def get_widget(self, size=None, show_robot=None, attributes=None):
"""
Get the robot widget.
Args:
size (int): size in pixels around robot
show_robot (bool): show picture of robot
attributes (list): items to include, or "all"
"""
from .watchers import RobotWatcher
if self._watcher is None:
size = size if size is not None else 100
show_robot = show_robot if show_robot is not None else True
attributes = attributes if attributes is not None else "all"
self._watcher = RobotWatcher(self, size=size,
show_robot=show_robot,
attributes=attributes)
self.world._watchers.append(self._watcher)
else:
self._watcher.set_arguments(size=size,
show_robot=show_robot,
attributes=attributes)
self._watcher.draw()
return self._watcher.get_widget()
has_image(self)
¶
Does this robot have an associated 3D set of images from a dataset?
Source code in robots/robot.py
def has_image(self):
"""
Does this robot have an associated 3D set of images from
a dataset?
"""
return self.get_dataset_image is not None
motors(self, left, right)
¶
A move function that takes desired motor values and converts to trans and rotate.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
left |
float |
the speed to move the left wheel |
required |
right |
float |
the speed to move the right wheel |
required |
Source code in robots/robot.py
def motors(self, left, right):
"""
A move function that takes desired motor values
and converts to trans and rotate.
Args:
left (float): the speed to move the left wheel
right (float): the speed to move the right wheel
"""
trans = (right + left) / 2.0
rotate = (right - left) / 2.0
self.move(trans, rotate)
move(self, translate, rotate)
¶
Set the target translate and rotate velocities.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
translate |
float |
the speed to move the robot forward/backward |
required |
rotate |
float |
the speed to rotate the robot left/right |
required |
Args should be between -1 and 1.
Source code in robots/robot.py
def move(self, translate, rotate):
"""
Set the target translate and rotate velocities.
Args:
translate (float): the speed to move the robot forward/backward
rotate (float): the speed to rotate the robot left/right
Args should be between -1 and 1.
"""
# values between -1 and 1
# compute target velocities
if self.world is not None:
if self.world.status != "running":
print_once("This world is not running")
if translate is not None:
self.tvx = round(translate * self.vx_max, 1)
if rotate is not None:
self.tva = round(rotate * self.va_max, 1)
pen_down(self, color=None, radius=1)
¶
Put the pen down to change the color of the background image.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
color |
str |
the pen color to draw |
None |
radius |
int |
the size of the dot to draw |
1 |
Source code in robots/robot.py
def pen_down(self, color=None, radius=1):
"""
Put the pen down to change the color of the background image.
Args:
color (str): the pen color to draw
radius (int): the size of the dot to draw
Note: not for use in a robot in a recorder.
"""
from PIL import Image
color = color if color is not None else self.color
self.pen = (Color(color), radius)
if self.world is not None:
if self.world._ground_image is None:
image = Image.new("RGBA", (self.world.width, self.world.height),
color="white")
filename = "ground_image.png"
image.save(filename)
self.world.set_ground_image(filename)
pen_up(self)
¶
Put the pen up to stop changing the color of the background image.
Note: not for use in a robot in a recorder.
Source code in robots/robot.py
def pen_up(self):
"""
Put the pen up to stop changing the color of the background image.
Note: not for use in a robot in a recorder.
"""
self.pen = (None, 0)
reset(self)
¶
Reset the robot's internal stuff. Typically, called from the world.
Source code in robots/robot.py
def reset(self):
"""
Reset the robot's internal stuff. Typically, called from the world.
"""
self.trace[:] = []
self.text_trace[:] = []
self.pen_trace[:] = []
reverse(self)
¶
Flip the target x and a velocities from negative to positive or from positive to negative.
Source code in robots/robot.py
def reverse(self):
"""
Flip the target x and a velocities from negative to
positive or from positive to negative.
"""
self.tvx = -self.tvx
self.tva = -self.tva
rotate(self, rotate)
¶
Set the target rotate velocity.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
rotate |
float |
the speed to rotate the robot |
required |
Arg should be between -1 and 1.
Source code in robots/robot.py
def rotate(self, rotate):
"""
Set the target rotate velocity.
Args:
rotate (float): the speed to rotate the robot
Arg should be between -1 and 1.
"""
# values between -1 and 1
self.tva = rotate * self.va_max
set_color(self, color)
¶
Set the color of a robot, and its trace.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
color |
str |
the color of the robot |
required |
Source code in robots/robot.py
def set_color(self, color):
"""
Set the color of a robot, and its trace.
Args:
color (str): the color of the robot
"""
self._set_color(color)
set_max_trace_length(self, seconds)
¶
Set the max length of trace, in seconds.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seconds |
float |
the length of trace in seconds |
required |
Source code in robots/robot.py
def set_max_trace_length(self, seconds):
"""
Set the max length of trace, in seconds.
Args:
seconds (float): the length of trace in seconds
"""
self.max_trace_length = seconds
set_pose(self, x=None, y=None, a=None, clear_trace=True, show=True)
¶
Set the pose of the robot. a is in degrees.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
x |
int |
the x coordinate of the robot |
None |
y |
int |
the y coordinate of the robot |
None |
a |
int |
the angle of the robot in degrees (zero is to the left, 90 is up) |
None |
clear_trace |
bool |
if True, clear the robot's trace |
True |
show |
bool |
if True, show the updated world |
True |
Source code in robots/robot.py
def set_pose(self, x=None, y=None, a=None, clear_trace=True,
show=True):
"""
Set the pose of the robot. a is in degrees.
Args:
x (int): the x coordinate of the robot
y (int): the y coordinate of the robot
a (int): the angle of the robot in degrees (zero is to the left, 90 is up)
clear_trace (bool): if True, clear the robot's trace
show (bool): if True, show the updated world
Note: the robot must be in a world.
"""
if self.world is None:
raise Exception(
"This robot is not in a world; add to world before setting pose"
)
else:
if a is not None:
a = degrees_to_world(a)
self._set_pose(x, y, a, clear_trace)
# Set all velocities to zero
self.vx = 0.0 # velocity in x direction, CM per second
self.vy = 0.0 # velocity in y direction, degrees per second
self.va = 0.0 # turn velocity
self.tvx = 0.0
self.tvy = 0.0
self.tva = 0.0
self.stalled = False
# Save the robot's pose to the config
self.world.update(show=show)
self.world.save()
set_random_pose(self, clear_trace=True, show=True)
¶
Set the pose of the robot to a random open location in the world.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
clear_trace |
bool |
if True, clear the trace |
True |
show |
bool |
if True, show the updated world |
True |
Source code in robots/robot.py
def set_random_pose(self, clear_trace=True, show=True):
"""
Set the pose of the robot to a random open location in the world.
Args:
clear_trace (bool): if True, clear the trace
show (bool): if True, show the updated world
Note: the robot must be in a world.
"""
if self.world is None:
raise Exception(
"This robot is not in a world; add robot to world before calling set_random_pose"
)
else:
# Direction is in radians, in world coordinates:
x, y, a = self.world._find_random_pose(self)
self._set_pose(x, y, a, clear_trace)
# Set all velocities to zero
self.vx = 0.0 # velocity in x direction, CM per second
self.vy = 0.0 # velocity in y direction, degrees per second
self.va = 0.0 # turn velocity
self.tvx = 0.0
self.tvy = 0.0
self.tva = 0.0
self.stalled = False
# Save the robot's pose to the config
self.world.update(show=show)
self.world.save()
speak(self, text=None)
¶
Show some text in the robot's speech bubble.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
text |
str |
the text to show; use None to clear |
None |
Source code in robots/robot.py
def speak(self, text=None):
"""
Show some text in the robot's speech bubble.
Args:
text (str): the text to show; use None to clear
Note: not for use in a robot in a recorder.
"""
if self.world:
if self.world._recording:
if len(self.text_trace) > 0:
# If same as last, don't add again
if self.text_trace[-1][1] != text:
self.text_trace.append((self.world.time, text))
else:
self.text_trace.append((self.world.time, text))
else:
self.text_trace[:] = [(self.world.time, text)]
stop(self)
¶
Set the target velocities to zeros.
Source code in robots/robot.py
def stop(self):
"""
Set the target velocities to zeros.
"""
self.tvx = 0.0
self.tvy = 0.0
self.tva = 0.0
summary(self)
¶
Prints a summary of information about the robot.
Source code in robots/robot.py
def summary(self):
"""
Prints a summary of information about the robot.
"""
if len(self._devices) == 0:
print(" This robot has no devices.")
else:
for i, device in enumerate(self._devices):
print(
" robot[%s or %r or %r]: %r"
% (i, device.type, device.name, device)
)
print(" " + ("-" * 25))
to_json(self)
¶
Get this robot as a JSON config file.
Source code in robots/robot.py
def to_json(self):
"""
Get this robot as a JSON config file.
"""
robot_json = {
"name": self.name,
"state": self.state,
"va": self.va,
"vx": self.vx,
"vy": self.vy,
"tva": self.tva,
"tvx": self.tvx,
"tvy": self.tvy,
"va_max": self.va_max,
"vx_max": self.vx_max,
"vy_max": self.vy_max,
"va_ramp": self.va_ramp,
"vx_ramp": self.vx_ramp,
"vy_ramp": self.vy_ramp,
"x": self.x,
"y": self.y,
"a": world_to_degrees(self.a),
"image_data": self.image_data,
"height": self.height,
"color": str(self.color),
"max_trace_length": self.max_trace_length,
"body": self.body,
"devices": [device.to_json() for device in self._devices],
"do_trace": self.do_trace,
}
return robot_json
translate(self, translate)
¶
Set the target translate velocity.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
translate |
float |
the speed to move the robot forward/backward |
required |
Arg should be between -1 and 1.
Source code in robots/robot.py
def translate(self, translate):
"""
Set the target translate velocity.
Args:
translate (float): the speed to move the robot forward/backward
Arg should be between -1 and 1.
"""
# values between -1 and 1
self.tvx = round(translate * self.vx_max, 1)
update(self, draw_list=None)
¶
Update the robot, and devices.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
draw_list |
List |
list of items to draw |
None |
Source code in robots/robot.py
def update(self, draw_list=None):
"""
Update the robot, and devices.
Args:
draw_list (List): list of items to draw
"""
# Wrapped worlds:
wrapped = False
if self.x < 0:
self.x = self.world.width
wrapped = True
elif self.x > self.world.width:
self.x = 0
wrapped = True
if self.y < 0:
self.y = self.world.height
wrapped = True
elif self.y > self.world.height:
self.y = 0
wrapped = True
if wrapped:
self.trace.append(None)
self._init_boundingbox()
if self.world.debug and draw_list is not None:
draw_list.append(("strokeStyle", (Color(255), 1)))
draw_list.append(
(
"draw_line",
(
self._bounding_lines[0].p1.x,
self._bounding_lines[0].p1.y,
self._bounding_lines[0].p2.x,
self._bounding_lines[0].p2.y,
),
)
)
draw_list.append(
(
"draw_line",
(
self._bounding_lines[1].p1.x,
self._bounding_lines[1].p1.y,
self._bounding_lines[1].p2.x,
self._bounding_lines[1].p2.y,
),
)
)
draw_list.append(
(
"draw_line",
(
self._bounding_lines[2].p1.x,
self._bounding_lines[2].p1.y,
self._bounding_lines[2].p2.x,
self._bounding_lines[2].p2.y,
),
)
)
draw_list.append(
(
"draw_line",
(
self._bounding_lines[3].p1.x,
self._bounding_lines[3].p1.y,
self._bounding_lines[3].p2.x,
self._bounding_lines[3].p2.y,
),
)
)
# Devices:
for device in self._devices:
device.update(draw_list)
# Update recording info:
if self.world._recording:
if len(self.pen_trace) > 0:
# color of pen:
if self.pen_trace[-1][1][0] != self.pen[0]:
self.pen_trace.append((self.world.time, self.pen))
# same pen color, do nothing
elif self.pen != (None, 0):
self.pen_trace.append((self.world.time, self.pen))
# else do nothing
else: # not recording
if self.pen == (None, 0):
self.pen_trace[:] = []
else:
self.pen_trace[:] = [(self.world.time, self.pen)]
# Alter world:
self._update_ground_image(self.world.time)
return
watch(self, size=None, show_robot=None, attributes=None)
¶
Watch the robot stats with live updates.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
size |
int |
size in pixels around robot |
None |
show_robot |
bool |
show picture of robot |
None |
attributes |
List |
items to include, or "all" |
None |
Source code in robots/robot.py
def watch(self, size=None, show_robot=None, attributes=None):
"""
Watch the robot stats with live updates.
Args:
size (int): size in pixels around robot
show_robot (bool): show picture of robot
attributes (List): items to include, or "all"
"""
widget = self.get_widget(
size=size,
show_robot=show_robot,
attributes=attributes,
)
display(widget)