wirepod-vector-sdk-audio 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anki_vector/__init__.py +43 -0
- anki_vector/animation.py +272 -0
- anki_vector/annotate.py +590 -0
- anki_vector/audio.py +212 -0
- anki_vector/audio_stream.py +335 -0
- anki_vector/behavior.py +1135 -0
- anki_vector/camera.py +670 -0
- anki_vector/camera_viewer/__init__.py +121 -0
- anki_vector/color.py +88 -0
- anki_vector/configure/__main__.py +331 -0
- anki_vector/connection.py +838 -0
- anki_vector/events.py +420 -0
- anki_vector/exceptions.py +185 -0
- anki_vector/faces.py +819 -0
- anki_vector/lights.py +210 -0
- anki_vector/mdns.py +131 -0
- anki_vector/messaging/__init__.py +45 -0
- anki_vector/messaging/alexa_pb2.py +36 -0
- anki_vector/messaging/alexa_pb2_grpc.py +3 -0
- anki_vector/messaging/behavior_pb2.py +40 -0
- anki_vector/messaging/behavior_pb2_grpc.py +3 -0
- anki_vector/messaging/client.py +33 -0
- anki_vector/messaging/cube_pb2.py +113 -0
- anki_vector/messaging/cube_pb2_grpc.py +3 -0
- anki_vector/messaging/extensions_pb2.py +25 -0
- anki_vector/messaging/extensions_pb2_grpc.py +3 -0
- anki_vector/messaging/external_interface_pb2.py +169 -0
- anki_vector/messaging/external_interface_pb2_grpc.py +1267 -0
- anki_vector/messaging/messages_pb2.py +431 -0
- anki_vector/messaging/messages_pb2_grpc.py +3 -0
- anki_vector/messaging/nav_map_pb2.py +33 -0
- anki_vector/messaging/nav_map_pb2_grpc.py +3 -0
- anki_vector/messaging/protocol.py +33 -0
- anki_vector/messaging/response_status_pb2.py +27 -0
- anki_vector/messaging/response_status_pb2_grpc.py +3 -0
- anki_vector/messaging/settings_pb2.py +72 -0
- anki_vector/messaging/settings_pb2_grpc.py +3 -0
- anki_vector/messaging/shared_pb2.py +54 -0
- anki_vector/messaging/shared_pb2_grpc.py +3 -0
- anki_vector/motors.py +127 -0
- anki_vector/nav_map.py +409 -0
- anki_vector/objects.py +1782 -0
- anki_vector/opengl/__init__.py +103 -0
- anki_vector/opengl/assets/LICENSE.txt +21 -0
- anki_vector/opengl/assets/cube.jpg +0 -0
- anki_vector/opengl/assets/cube.mtl +9 -0
- anki_vector/opengl/assets/cube.obj +1000 -0
- anki_vector/opengl/assets/vector.mtl +67 -0
- anki_vector/opengl/assets/vector.obj +13220 -0
- anki_vector/opengl/opengl.py +864 -0
- anki_vector/opengl/opengl_vector.py +620 -0
- anki_vector/opengl/opengl_viewer.py +689 -0
- anki_vector/photos.py +145 -0
- anki_vector/proximity.py +176 -0
- anki_vector/reserve_control/__main__.py +36 -0
- anki_vector/robot.py +930 -0
- anki_vector/screen.py +201 -0
- anki_vector/status.py +322 -0
- anki_vector/touch.py +119 -0
- anki_vector/user_intent.py +186 -0
- anki_vector/util.py +1132 -0
- anki_vector/version.py +15 -0
- anki_vector/viewer.py +403 -0
- anki_vector/vision.py +202 -0
- anki_vector/world.py +899 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/METADATA +80 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/RECORD +71 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/WHEEL +5 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/licenses/LICENSE.txt +180 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/top_level.txt +1 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/zip-safe +1 -0
anki_vector/objects.py
ADDED
|
@@ -0,0 +1,1782 @@
|
|
|
1
|
+
# Copyright (c) 2018 Anki, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License in the file LICENSE.txt or at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Object and Light Cube recognition.
|
|
16
|
+
|
|
17
|
+
Vector can recognize and track a number of different types of objects.
|
|
18
|
+
|
|
19
|
+
These objects may be visible (currently observed by the robot's camera)
|
|
20
|
+
and tappable (in the case of the Light Cube that ships with the robot).
|
|
21
|
+
|
|
22
|
+
The Light Cube is known as a :class:`LightCube` by the SDK. The cube
|
|
23
|
+
has controllable lights, and sensors that can determine when it's being
|
|
24
|
+
moved or tapped.
|
|
25
|
+
|
|
26
|
+
Objects can generate events which can be subscribed to from the anki_vector.events
|
|
27
|
+
class, such as object_appeared (of type EvtObjectAppeared), and
|
|
28
|
+
object_disappeared (of type EvtObjectDisappeared), which are broadcast
|
|
29
|
+
based on both robot originating events and local state.
|
|
30
|
+
|
|
31
|
+
All observable objects have a marker of a known size attached to them, which allows Vector
|
|
32
|
+
to recognize the object and its position and rotation ("pose"). You can attach
|
|
33
|
+
markers to your own objects for Vector to recognize by printing them out from the
|
|
34
|
+
online documentation. They will be detected as :class:`CustomObject` instances.
|
|
35
|
+
|
|
36
|
+
Vector connects to his Light Cube with BLE.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# __all__ should order by constants, event classes, other classes, functions.
|
|
40
|
+
__all__ = ['LIGHT_CUBE_1_TYPE', 'OBJECT_VISIBILITY_TIMEOUT',
|
|
41
|
+
'EvtObjectAppeared', 'EvtObjectDisappeared', 'EvtObjectFinishedMove', 'EvtObjectObserved',
|
|
42
|
+
'Charger', 'CustomObjectArchetype', 'CustomObject', 'CustomObjectMarkers', 'CustomObjectTypes',
|
|
43
|
+
'FixedCustomObject', 'LightCube', 'ObservableObject']
|
|
44
|
+
|
|
45
|
+
# TODO Curious why events like the following aren't listed? At least some do seem to be supported in other parts of anki_vector.
|
|
46
|
+
# EvtObjectTapped, EvtObjectConnectChanged, EvtObjectConnected, EvtObjectLocated, EvtObjectMoving, EvtObjectMovingStarted, EvtObjectMovingStopped
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
import collections
|
|
50
|
+
import math
|
|
51
|
+
import time
|
|
52
|
+
|
|
53
|
+
from . import connection, lights, util
|
|
54
|
+
from .events import Events
|
|
55
|
+
|
|
56
|
+
from .messaging import protocol
|
|
57
|
+
|
|
58
|
+
#: Length of time in seconds to go without receiving an observed event before
|
|
59
|
+
#: assuming that Vector can no longer see an object.
|
|
60
|
+
OBJECT_VISIBILITY_TIMEOUT = 0.8
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class EvtObjectObserved(): # pylint: disable=too-few-public-methods
|
|
64
|
+
"""Triggered whenever an object is visually identified by the robot.
|
|
65
|
+
|
|
66
|
+
A stream of these events are produced while an object is visible to the robot.
|
|
67
|
+
Each event has an updated image_box field.
|
|
68
|
+
|
|
69
|
+
See EvtObjectAppeared if you only want to know when an object first
|
|
70
|
+
becomes visible.
|
|
71
|
+
|
|
72
|
+
.. testcode::
|
|
73
|
+
|
|
74
|
+
import time
|
|
75
|
+
|
|
76
|
+
import anki_vector
|
|
77
|
+
from anki_vector.events import Events
|
|
78
|
+
from anki_vector.util import degrees
|
|
79
|
+
|
|
80
|
+
def handle_object_observed(robot, event_type, event):
|
|
81
|
+
# This will be called whenever an EvtObjectObserved is dispatched -
|
|
82
|
+
# whenever an Object comes into view.
|
|
83
|
+
print(f"--------- Vector observed an object --------- \\n{event.obj}")
|
|
84
|
+
|
|
85
|
+
with anki_vector.Robot(default_logging=False,
|
|
86
|
+
show_viewer=True,
|
|
87
|
+
show_3d_viewer=True,
|
|
88
|
+
enable_nav_map_feed=True) as robot:
|
|
89
|
+
# Place Vector's cube where he can see it
|
|
90
|
+
|
|
91
|
+
robot.events.subscribe(handle_object_observed, Events.object_observed)
|
|
92
|
+
|
|
93
|
+
# If necessary, move Vector's Head and Lift down
|
|
94
|
+
robot.behavior.set_lift_height(0.0)
|
|
95
|
+
robot.behavior.set_head_angle(degrees(0.0))
|
|
96
|
+
|
|
97
|
+
time.sleep(3.0)
|
|
98
|
+
|
|
99
|
+
:param obj: The object that was observed
|
|
100
|
+
:param image_rect: An :class:`anki_vector.util.ImageRect`: defining where the object is within Vector's camera view
|
|
101
|
+
:param pose: The :class:`anki_vector.util.Pose`: defining the position and rotation of the object
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, obj, image_rect: util.ImageRect, pose: util.Pose):
|
|
105
|
+
self.obj = obj
|
|
106
|
+
self.image_rect = image_rect
|
|
107
|
+
self.pose = pose
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class EvtObjectAppeared(): # pylint: disable=too-few-public-methods
|
|
111
|
+
"""Triggered whenever an object is first visually identified by a robot.
|
|
112
|
+
|
|
113
|
+
This differs from EvtObjectObserved in that it's only triggered when
|
|
114
|
+
an object initially becomes visible. If it disappears for more than
|
|
115
|
+
OBJECT_VISIBILITY_TIMEOUT seconds and then is seen again, a
|
|
116
|
+
EvtObjectDisappeared will be dispatched, followed by another
|
|
117
|
+
EvtObjectAppeared event.
|
|
118
|
+
|
|
119
|
+
For continuous tracking information about a visible object, see
|
|
120
|
+
EvtObjectObserved.
|
|
121
|
+
|
|
122
|
+
.. testcode::
|
|
123
|
+
|
|
124
|
+
import time
|
|
125
|
+
|
|
126
|
+
import anki_vector
|
|
127
|
+
from anki_vector.events import Events
|
|
128
|
+
from anki_vector.util import degrees
|
|
129
|
+
|
|
130
|
+
def handle_object_appeared(robot, event_type, event):
|
|
131
|
+
# This will be called whenever an EvtObjectAppeared is dispatched -
|
|
132
|
+
# whenever an Object comes into view.
|
|
133
|
+
print(f"--------- Vector started seeing an object --------- \\n{event.obj}")
|
|
134
|
+
|
|
135
|
+
with anki_vector.Robot(default_logging=False,
|
|
136
|
+
show_viewer=True,
|
|
137
|
+
show_3d_viewer=True,
|
|
138
|
+
enable_nav_map_feed=True) as robot:
|
|
139
|
+
# Place Vector's cube where he can see it
|
|
140
|
+
|
|
141
|
+
robot.events.subscribe(handle_object_appeared, Events.object_appeared)
|
|
142
|
+
|
|
143
|
+
# If necessary, move Vector's Head and Lift down
|
|
144
|
+
robot.behavior.set_lift_height(0.0)
|
|
145
|
+
robot.behavior.set_head_angle(degrees(0.0))
|
|
146
|
+
|
|
147
|
+
time.sleep(3.0)
|
|
148
|
+
|
|
149
|
+
:param obj: The object that is starting to be observed
|
|
150
|
+
:param image_rect: An :class:`anki_vector.util.ImageRect`: defining where the object is within Vector's camera view
|
|
151
|
+
:param pose: The :class:`anki_vector.util.Pose`: defining the position and rotation of the object
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def __init__(self, obj, image_rect: util.ImageRect, pose: util.Pose):
|
|
155
|
+
self.obj = obj
|
|
156
|
+
self.image_rect = image_rect
|
|
157
|
+
self.pose = pose
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class EvtObjectDisappeared(): # pylint: disable=too-few-public-methods
|
|
161
|
+
"""Triggered whenever an object that was previously being observed is no longer visible.
|
|
162
|
+
|
|
163
|
+
.. testcode::
|
|
164
|
+
|
|
165
|
+
import time
|
|
166
|
+
|
|
167
|
+
import anki_vector
|
|
168
|
+
from anki_vector.events import Events
|
|
169
|
+
from anki_vector.util import degrees
|
|
170
|
+
|
|
171
|
+
def handle_object_disappeared(robot, event_type, event):
|
|
172
|
+
# This will be called whenever an EvtObjectDisappeared is dispatched -
|
|
173
|
+
# whenever an Object goes out of view.
|
|
174
|
+
print(f"--------- Vector stopped seeing an object --------- \\n{event.obj}")
|
|
175
|
+
|
|
176
|
+
with anki_vector.Robot(default_logging=False,
|
|
177
|
+
show_viewer=True,
|
|
178
|
+
show_3d_viewer=True,
|
|
179
|
+
enable_nav_map_feed=True) as robot:
|
|
180
|
+
# Place Vector's cube where he can see it
|
|
181
|
+
|
|
182
|
+
robot.events.subscribe(handle_object_disappeared, Events.object_disappeared)
|
|
183
|
+
|
|
184
|
+
# If necessary, move Vector's Head and Lift down
|
|
185
|
+
robot.behavior.set_lift_height(0.0)
|
|
186
|
+
robot.behavior.set_head_angle(degrees(0.0))
|
|
187
|
+
|
|
188
|
+
time.sleep(3.0)
|
|
189
|
+
|
|
190
|
+
:param obj: The object that is no longer being observed
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def __init__(self, obj):
|
|
194
|
+
self.obj = obj
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class EvtObjectFinishedMove(): # pylint: disable=too-few-public-methods
|
|
198
|
+
"""Triggered when an active object stops moving.
|
|
199
|
+
|
|
200
|
+
:param obj: The object that moved
|
|
201
|
+
:param move_duration: The duration of the move
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
def __init__(self, obj, move_duration: float):
|
|
205
|
+
self.obj = obj
|
|
206
|
+
self.move_duration = move_duration
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ObservableObject(util.Component):
|
|
210
|
+
"""The base type for anything Vector can see."""
|
|
211
|
+
|
|
212
|
+
visibility_timeout = OBJECT_VISIBILITY_TIMEOUT
|
|
213
|
+
|
|
214
|
+
def __init__(self, robot, **kw):
|
|
215
|
+
super().__init__(robot, **kw)
|
|
216
|
+
|
|
217
|
+
self._pose: util.Pose = None
|
|
218
|
+
|
|
219
|
+
#: The time the last event was received.
|
|
220
|
+
#: ``None`` if no events have yet been received.
|
|
221
|
+
self._last_event_time: float = None
|
|
222
|
+
|
|
223
|
+
#: The time the element was last observed by the robot.
|
|
224
|
+
#: ``None`` if the element has not yet been observed.
|
|
225
|
+
self._last_observed_time: float = None
|
|
226
|
+
|
|
227
|
+
#: The robot's timestamp of the last observed event.
|
|
228
|
+
#: ``None`` if the element has not yet been observed.
|
|
229
|
+
#: In milliseconds relative to robot epoch.
|
|
230
|
+
self._last_observed_robot_timestamp: int = None
|
|
231
|
+
|
|
232
|
+
#: The ImageRect defining where the
|
|
233
|
+
#: object was last visible within Vector's camera view.
|
|
234
|
+
#: ``None`` if the element has not yet been observed.
|
|
235
|
+
self._last_observed_image_rect: util.ImageRect = None
|
|
236
|
+
|
|
237
|
+
self._is_visible: bool = False
|
|
238
|
+
self._observed_timeout_handler: callable = None
|
|
239
|
+
|
|
240
|
+
def __repr__(self):
|
|
241
|
+
extra = self._repr_values()
|
|
242
|
+
if extra:
|
|
243
|
+
extra = ' ' + extra
|
|
244
|
+
if self.pose:
|
|
245
|
+
extra += ' pose=%s' % self.pose
|
|
246
|
+
|
|
247
|
+
return '<%s%s is_visible=%s>' % (self.__class__.__name__,
|
|
248
|
+
extra, self.is_visible)
|
|
249
|
+
#### Properties ####
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def pose(self) -> util.Pose:
|
|
253
|
+
"""The pose of this object in the world.
|
|
254
|
+
|
|
255
|
+
Is ``None`` for elements that don't have pose information.
|
|
256
|
+
|
|
257
|
+
.. testcode::
|
|
258
|
+
|
|
259
|
+
import anki_vector
|
|
260
|
+
import time
|
|
261
|
+
|
|
262
|
+
# First, place a cube directly in front of Vector so he can observe it.
|
|
263
|
+
|
|
264
|
+
with anki_vector.Robot() as robot:
|
|
265
|
+
connectionResult = robot.world.connect_cube()
|
|
266
|
+
connected_cube = robot.world.connected_light_cube
|
|
267
|
+
|
|
268
|
+
for _ in range(16):
|
|
269
|
+
connected_cube = robot.world.connected_light_cube
|
|
270
|
+
if connected_cube:
|
|
271
|
+
print(connected_cube)
|
|
272
|
+
print("last observed timestamp: " + str(connected_cube.last_observed_time) + ", robot timestamp: " + str(connected_cube.last_observed_robot_timestamp))
|
|
273
|
+
print(robot.world.connected_light_cube.pose)
|
|
274
|
+
time.sleep(0.5)
|
|
275
|
+
"""
|
|
276
|
+
return self._pose
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def last_event_time(self) -> float:
|
|
280
|
+
"""Time this object last received an event from Vector."""
|
|
281
|
+
return self._last_event_time
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def last_observed_time(self) -> float:
|
|
285
|
+
"""Time this object was last seen."""
|
|
286
|
+
return self._last_observed_time
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def last_observed_robot_timestamp(self) -> int:
|
|
290
|
+
"""Time this object was last seen according to Vector's time."""
|
|
291
|
+
return self._last_observed_robot_timestamp
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def time_since_last_seen(self) -> float:
|
|
295
|
+
"""Time since this object was last seen. math.inf if never seen.
|
|
296
|
+
|
|
297
|
+
.. testcode::
|
|
298
|
+
|
|
299
|
+
import anki_vector
|
|
300
|
+
|
|
301
|
+
with anki_vector.Robot(enable_face_detection=True) as robot:
|
|
302
|
+
for face in robot.world.visible_faces:
|
|
303
|
+
print(f"time_since_last_seen: {face.time_since_last_seen}")
|
|
304
|
+
"""
|
|
305
|
+
if self._last_observed_time is None:
|
|
306
|
+
return math.inf
|
|
307
|
+
return time.time() - self._last_observed_time
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def last_observed_image_rect(self) -> util.ImageRect:
|
|
311
|
+
"""The location in 2d camera space where this object was last seen."""
|
|
312
|
+
return self._last_observed_image_rect
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def is_visible(self) -> bool:
|
|
316
|
+
"""True if the element has been observed recently, False otherwise.
|
|
317
|
+
|
|
318
|
+
"recently" is defined as :attr:`visibility_timeout` seconds.
|
|
319
|
+
"""
|
|
320
|
+
return self._is_visible
|
|
321
|
+
|
|
322
|
+
#### Private Methods ####
|
|
323
|
+
|
|
324
|
+
def _repr_values(self): # pylint: disable=no-self-use
|
|
325
|
+
return ''
|
|
326
|
+
|
|
327
|
+
def _dispatch_observed_event(self, image_rect):
|
|
328
|
+
# Override in subclass if there is a specific event for that type
|
|
329
|
+
self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectObserved(self, image_rect, self._pose), Events.object_observed))
|
|
330
|
+
|
|
331
|
+
def _dispatch_appeared_event(self, image_rect):
|
|
332
|
+
# Override in subclass if there is a specific event for that type
|
|
333
|
+
self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectAppeared(self, image_rect, self._pose), Events.object_appeared))
|
|
334
|
+
|
|
335
|
+
def _dispatch_disappeared_event(self):
|
|
336
|
+
# Override in subclass if there is a specific event for that type
|
|
337
|
+
self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectDisappeared(self), Events.object_disappeared))
|
|
338
|
+
|
|
339
|
+
def _reset_observed_timeout_handler(self):
|
|
340
|
+
if self._observed_timeout_handler is not None:
|
|
341
|
+
self._observed_timeout_handler.cancel()
|
|
342
|
+
self._observed_timeout_handler = self.conn.loop.call_later(self.visibility_timeout, self._observed_timeout)
|
|
343
|
+
|
|
344
|
+
def _observed_timeout(self):
|
|
345
|
+
# Triggered when the element is no longer considered "visible".
|
|
346
|
+
# i.e. visibility_timeout seconds after the last observed event.
|
|
347
|
+
self._is_visible = False
|
|
348
|
+
self._dispatch_disappeared_event()
|
|
349
|
+
|
|
350
|
+
def _on_observed(self, pose: util.Pose, image_rect: util.ImageRect, robot_timestamp: int):
|
|
351
|
+
# Called from subclasses on their corresponding observed messages.
|
|
352
|
+
newly_visible = self._is_visible is False
|
|
353
|
+
self._is_visible = True
|
|
354
|
+
|
|
355
|
+
now = time.time()
|
|
356
|
+
self._last_observed_time = now
|
|
357
|
+
self._last_observed_robot_timestamp = robot_timestamp
|
|
358
|
+
self._last_event_time = now
|
|
359
|
+
self._last_observed_image_rect = image_rect
|
|
360
|
+
self._pose = pose
|
|
361
|
+
self._reset_observed_timeout_handler()
|
|
362
|
+
self._dispatch_observed_event(image_rect)
|
|
363
|
+
|
|
364
|
+
if newly_visible:
|
|
365
|
+
self._dispatch_appeared_event(image_rect)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
#: LIGHT_CUBE_1_TYPE's markers look like 2 concentric circles with lines and gaps.
|
|
369
|
+
LIGHT_CUBE_1_TYPE = protocol.ObjectType.Value("BLOCK_LIGHTCUBE1")
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class LightCube(ObservableObject):
|
|
373
|
+
"""Represents Vector's Cube.
|
|
374
|
+
|
|
375
|
+
The LightCube object has four LEDs that Vector can actively manipulate and communicate with.
|
|
376
|
+
|
|
377
|
+
As Vector drives around, he uses the position of objects that he recognizes, including his cube,
|
|
378
|
+
to localize himself, taking note of the :class:`anki_vector.util.Pose` of the objects.
|
|
379
|
+
|
|
380
|
+
You can subscribe to cube events including :class:`anki_vector.events.Events.object_tapped`,
|
|
381
|
+
:class:`anki_vector.events.Events.object_appeared`, and :class:`anki_vector.events.Events.object_disappeared`.
|
|
382
|
+
|
|
383
|
+
Vector supports 1 LightCube.
|
|
384
|
+
|
|
385
|
+
See parent class :class:`ObservableObject` for additional properties
|
|
386
|
+
and methods.
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
#: Length of time in seconds to go without receiving an observed event before
|
|
390
|
+
#: assuming that Vector can no longer see an object. Can be overridden in subclasses.
|
|
391
|
+
visibility_timeout = OBJECT_VISIBILITY_TIMEOUT
|
|
392
|
+
|
|
393
|
+
def __init__(self, robot, **kw):
|
|
394
|
+
super().__init__(robot, **kw)
|
|
395
|
+
|
|
396
|
+
#: The time the object was last tapped.
|
|
397
|
+
#: ``None`` if the cube wasn't tapped yet.
|
|
398
|
+
self._last_tapped_time: float = None
|
|
399
|
+
|
|
400
|
+
#: The robot's timestamp of the last tapped event.
|
|
401
|
+
#: ``None`` if the cube wasn't tapped yet.
|
|
402
|
+
#: In milliseconds relative to robot epoch.
|
|
403
|
+
self._last_tapped_robot_timestamp: int = None
|
|
404
|
+
|
|
405
|
+
#: The time the object was last moved.
|
|
406
|
+
#: ``None`` if the cube wasn't moved yet.
|
|
407
|
+
self._last_moved_time: float = None
|
|
408
|
+
|
|
409
|
+
#: The robot's timestamp of the last move event.
|
|
410
|
+
#: ``None`` if the cube wasn't moved yet.
|
|
411
|
+
#: In milliseconds relative to robot epoch.
|
|
412
|
+
self._last_moved_robot_timestamp: int = None
|
|
413
|
+
|
|
414
|
+
#: The time the object started moving when last moved.
|
|
415
|
+
self._last_moved_start_time: float = None
|
|
416
|
+
|
|
417
|
+
#: The robot's timestamp of when the object started moving when last moved.
|
|
418
|
+
#: ``None`` if the cube wasn't moved yet.
|
|
419
|
+
#: In milliseconds relative to robot epoch.
|
|
420
|
+
self._last_moved_start_robot_timestamp: int = None
|
|
421
|
+
|
|
422
|
+
#: The time the last up axis event was received.
|
|
423
|
+
#: ``None`` if no events have yet been received.
|
|
424
|
+
self._last_up_axis_changed_time: float = None
|
|
425
|
+
|
|
426
|
+
#: The robot's timestamp of the last up axis event.
|
|
427
|
+
#: ``None`` if the there has not been an up axis event.
|
|
428
|
+
#: In milliseconds relative to robot epoch.
|
|
429
|
+
self._last_up_axis_changed_robot_timestamp: int = None
|
|
430
|
+
|
|
431
|
+
# The object's up_axis value from the last time it changed.
|
|
432
|
+
self._up_axis: protocol.UpAxis = None
|
|
433
|
+
|
|
434
|
+
#: True if the cube's accelerometer indicates that the cube is moving.
|
|
435
|
+
self._is_moving: bool = False
|
|
436
|
+
|
|
437
|
+
#: True if the cube is currently connected to the robot via radio.
|
|
438
|
+
self._is_connected: bool = False
|
|
439
|
+
|
|
440
|
+
#: angular distance from the current reported up axis
|
|
441
|
+
#: ``None`` if the object has not yet been observed.
|
|
442
|
+
self._top_face_orientation_rad: float = None
|
|
443
|
+
|
|
444
|
+
self._object_id: str = None
|
|
445
|
+
|
|
446
|
+
#: unique identification of the physical cube
|
|
447
|
+
self._factory_id: str = None
|
|
448
|
+
|
|
449
|
+
#: Subscribe to relevant events
|
|
450
|
+
self.robot.events.subscribe(self._on_object_connection_state_changed,
|
|
451
|
+
Events.object_connection_state)
|
|
452
|
+
|
|
453
|
+
self.robot.events.subscribe(self._on_object_moved,
|
|
454
|
+
Events.object_moved)
|
|
455
|
+
|
|
456
|
+
self.robot.events.subscribe(self._on_object_stopped_moving,
|
|
457
|
+
Events.object_stopped_moving)
|
|
458
|
+
|
|
459
|
+
self.robot.events.subscribe(self._on_object_up_axis_changed,
|
|
460
|
+
Events.object_up_axis_changed)
|
|
461
|
+
|
|
462
|
+
self.robot.events.subscribe(self._on_object_tapped,
|
|
463
|
+
Events.object_tapped)
|
|
464
|
+
|
|
465
|
+
self.robot.events.subscribe(self._on_object_observed,
|
|
466
|
+
Events.robot_observed_object)
|
|
467
|
+
|
|
468
|
+
self.robot.events.subscribe(self._on_object_connection_lost,
|
|
469
|
+
Events.cube_connection_lost)
|
|
470
|
+
|
|
471
|
+
#### Public Methods ####
|
|
472
|
+
|
|
473
|
+
def teardown(self):
|
|
474
|
+
"""All faces will be torn down by the world when no longer needed."""
|
|
475
|
+
self.robot.events.unsubscribe(self._on_object_connection_state_changed,
|
|
476
|
+
Events.object_connection_state)
|
|
477
|
+
|
|
478
|
+
self.robot.events.unsubscribe(self._on_object_moved,
|
|
479
|
+
Events.object_moved)
|
|
480
|
+
|
|
481
|
+
self.robot.events.unsubscribe(self._on_object_stopped_moving,
|
|
482
|
+
Events.object_stopped_moving)
|
|
483
|
+
|
|
484
|
+
self.robot.events.unsubscribe(self._on_object_up_axis_changed,
|
|
485
|
+
Events.object_up_axis_changed)
|
|
486
|
+
|
|
487
|
+
self.robot.events.unsubscribe(self._on_object_tapped,
|
|
488
|
+
Events.object_tapped)
|
|
489
|
+
|
|
490
|
+
self.robot.events.unsubscribe(self._on_object_observed,
|
|
491
|
+
Events.robot_observed_object)
|
|
492
|
+
|
|
493
|
+
self.robot.events.unsubscribe(self._on_object_connection_lost,
|
|
494
|
+
Events.cube_connection_lost)
|
|
495
|
+
|
|
496
|
+
# TODO: add return type hint
|
|
497
|
+
@connection.on_connection_thread()
|
|
498
|
+
async def set_light_corners(self,
|
|
499
|
+
light1: lights.Light,
|
|
500
|
+
light2: lights.Light,
|
|
501
|
+
light3: lights.Light,
|
|
502
|
+
light4: lights.Light,
|
|
503
|
+
color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE):
|
|
504
|
+
"""Set the light for each corner.
|
|
505
|
+
|
|
506
|
+
.. testcode::
|
|
507
|
+
|
|
508
|
+
import anki_vector
|
|
509
|
+
|
|
510
|
+
import time
|
|
511
|
+
|
|
512
|
+
with anki_vector.Robot() as robot:
|
|
513
|
+
robot.world.connect_cube()
|
|
514
|
+
|
|
515
|
+
if robot.world.connected_light_cube:
|
|
516
|
+
cube = robot.world.connected_light_cube
|
|
517
|
+
|
|
518
|
+
cube.set_light_corners(anki_vector.lights.blue_light,
|
|
519
|
+
anki_vector.lights.green_light,
|
|
520
|
+
anki_vector.lights.red_light,
|
|
521
|
+
anki_vector.lights.white_light)
|
|
522
|
+
time.sleep(3)
|
|
523
|
+
|
|
524
|
+
cube.set_lights_off()
|
|
525
|
+
|
|
526
|
+
:param light1: The settings for the first light.
|
|
527
|
+
:param light2: The settings for the second light.
|
|
528
|
+
:param light3: The settings for the third light.
|
|
529
|
+
:param light4: The settings for the fourth light.
|
|
530
|
+
:param color_profile: The profile to be used for the cube lights
|
|
531
|
+
"""
|
|
532
|
+
params = lights.package_request_params((light1, light2, light3, light4), color_profile)
|
|
533
|
+
req = protocol.SetCubeLightsRequest(
|
|
534
|
+
object_id=self._object_id,
|
|
535
|
+
on_color=params['on_color'],
|
|
536
|
+
off_color=params['off_color'],
|
|
537
|
+
on_period_ms=params['on_period_ms'],
|
|
538
|
+
off_period_ms=params['off_period_ms'],
|
|
539
|
+
transition_on_period_ms=params['transition_on_period_ms'],
|
|
540
|
+
transition_off_period_ms=params['transition_off_period_ms'],
|
|
541
|
+
offset=[0, 0, 0, 0],
|
|
542
|
+
relative_to_x=0.0,
|
|
543
|
+
relative_to_y=0.0,
|
|
544
|
+
rotate=False,
|
|
545
|
+
make_relative=protocol.SetCubeLightsRequest.OFF) # pylint: disable=no-member
|
|
546
|
+
return await self.grpc_interface.SetCubeLights(req)
|
|
547
|
+
|
|
548
|
+
def set_lights(self, light: lights.Light, color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE):
|
|
549
|
+
"""Set all lights on the cube
|
|
550
|
+
|
|
551
|
+
.. testcode::
|
|
552
|
+
|
|
553
|
+
import anki_vector
|
|
554
|
+
|
|
555
|
+
import time
|
|
556
|
+
|
|
557
|
+
with anki_vector.Robot() as robot:
|
|
558
|
+
robot.world.connect_cube()
|
|
559
|
+
|
|
560
|
+
if robot.world.connected_light_cube:
|
|
561
|
+
cube = robot.world.connected_light_cube
|
|
562
|
+
|
|
563
|
+
# Set cube lights to yellow
|
|
564
|
+
cube.set_lights(anki_vector.lights.yellow_light)
|
|
565
|
+
time.sleep(3)
|
|
566
|
+
|
|
567
|
+
cube.set_lights_off()
|
|
568
|
+
|
|
569
|
+
:param light: The settings for the lights
|
|
570
|
+
:param color_profile: The profile to be used for the cube lights
|
|
571
|
+
"""
|
|
572
|
+
return self.set_light_corners(light, light, light, light, color_profile)
|
|
573
|
+
|
|
574
|
+
def set_lights_off(self, color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE):
|
|
575
|
+
"""Set all lights off on the cube
|
|
576
|
+
|
|
577
|
+
.. testcode::
|
|
578
|
+
|
|
579
|
+
import anki_vector
|
|
580
|
+
|
|
581
|
+
import time
|
|
582
|
+
|
|
583
|
+
with anki_vector.Robot() as robot:
|
|
584
|
+
robot.world.connect_cube()
|
|
585
|
+
|
|
586
|
+
if robot.world.connected_light_cube:
|
|
587
|
+
cube = robot.world.connected_light_cube
|
|
588
|
+
|
|
589
|
+
# Set cube lights to yellow
|
|
590
|
+
cube.set_lights(anki_vector.lights.yellow_light)
|
|
591
|
+
time.sleep(3)
|
|
592
|
+
|
|
593
|
+
# Turn off cube lights
|
|
594
|
+
cube.set_lights_off()
|
|
595
|
+
|
|
596
|
+
:param color_profile: The profile to be used for the cube lights
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
return self.set_light_corners(lights.off_light, lights.off_light, lights.off_light, lights.off_light, color_profile)
|
|
600
|
+
|
|
601
|
+
#### Private Methods ####
|
|
602
|
+
|
|
603
|
+
def _repr_values(self):
|
|
604
|
+
return 'object_id=%s' % self.object_id
|
|
605
|
+
|
|
606
|
+
#### Properties ####
|
|
607
|
+
|
|
608
|
+
@property
|
|
609
|
+
def last_tapped_time(self) -> float:
|
|
610
|
+
"""The time the object was last tapped in SDK time.
|
|
611
|
+
|
|
612
|
+
.. testcode::
|
|
613
|
+
|
|
614
|
+
import time
|
|
615
|
+
import anki_vector
|
|
616
|
+
|
|
617
|
+
with anki_vector.Robot() as robot:
|
|
618
|
+
print("disconnecting from any connected cube...")
|
|
619
|
+
robot.world.disconnect_cube()
|
|
620
|
+
|
|
621
|
+
time.sleep(2)
|
|
622
|
+
|
|
623
|
+
print("connect to a cube...")
|
|
624
|
+
connectionResult = robot.world.connect_cube()
|
|
625
|
+
|
|
626
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
627
|
+
for _ in range(16):
|
|
628
|
+
connected_cube = robot.world.connected_light_cube
|
|
629
|
+
if connected_cube:
|
|
630
|
+
print(f'last_tapped_time: {connected_cube.last_tapped_time}')
|
|
631
|
+
time.sleep(0.5)
|
|
632
|
+
"""
|
|
633
|
+
return self._last_tapped_time
|
|
634
|
+
|
|
635
|
+
@property
|
|
636
|
+
def last_tapped_robot_timestamp(self) -> float:
|
|
637
|
+
"""The time the object was last tapped in robot time.
|
|
638
|
+
|
|
639
|
+
.. testcode::
|
|
640
|
+
|
|
641
|
+
import time
|
|
642
|
+
import anki_vector
|
|
643
|
+
|
|
644
|
+
with anki_vector.Robot() as robot:
|
|
645
|
+
print("disconnecting from any connected cube...")
|
|
646
|
+
robot.world.disconnect_cube()
|
|
647
|
+
|
|
648
|
+
time.sleep(2)
|
|
649
|
+
|
|
650
|
+
print("connect to a cube...")
|
|
651
|
+
connectionResult = robot.world.connect_cube()
|
|
652
|
+
|
|
653
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
654
|
+
for _ in range(16):
|
|
655
|
+
connected_cube = robot.world.connected_light_cube
|
|
656
|
+
if connected_cube:
|
|
657
|
+
print(f'last_tapped_robot_timestamp: {connected_cube.last_tapped_robot_timestamp}')
|
|
658
|
+
time.sleep(0.5)
|
|
659
|
+
"""
|
|
660
|
+
return self._last_tapped_robot_timestamp
|
|
661
|
+
|
|
662
|
+
@property
|
|
663
|
+
def last_moved_time(self) -> float:
|
|
664
|
+
"""The time the object was last moved in SDK time.
|
|
665
|
+
|
|
666
|
+
.. testcode::
|
|
667
|
+
|
|
668
|
+
import time
|
|
669
|
+
import anki_vector
|
|
670
|
+
|
|
671
|
+
with anki_vector.Robot() as robot:
|
|
672
|
+
print("disconnecting from any connected cube...")
|
|
673
|
+
robot.world.disconnect_cube()
|
|
674
|
+
|
|
675
|
+
time.sleep(2)
|
|
676
|
+
|
|
677
|
+
print("connect to a cube...")
|
|
678
|
+
connectionResult = robot.world.connect_cube()
|
|
679
|
+
|
|
680
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
681
|
+
for _ in range(16):
|
|
682
|
+
connected_cube = robot.world.connected_light_cube
|
|
683
|
+
if connected_cube:
|
|
684
|
+
print(f'last_moved_time: {connected_cube.last_moved_time}')
|
|
685
|
+
time.sleep(0.5)
|
|
686
|
+
"""
|
|
687
|
+
return self._last_moved_time
|
|
688
|
+
|
|
689
|
+
@property
|
|
690
|
+
def last_moved_robot_timestamp(self) -> float:
|
|
691
|
+
"""The time the object was last moved in robot time.
|
|
692
|
+
|
|
693
|
+
.. testcode::
|
|
694
|
+
|
|
695
|
+
import time
|
|
696
|
+
import anki_vector
|
|
697
|
+
|
|
698
|
+
with anki_vector.Robot() as robot:
|
|
699
|
+
print("disconnecting from any connected cube...")
|
|
700
|
+
robot.world.disconnect_cube()
|
|
701
|
+
|
|
702
|
+
time.sleep(2)
|
|
703
|
+
|
|
704
|
+
print("connect to a cube...")
|
|
705
|
+
connectionResult = robot.world.connect_cube()
|
|
706
|
+
|
|
707
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
708
|
+
for _ in range(16):
|
|
709
|
+
connected_cube = robot.world.connected_light_cube
|
|
710
|
+
if connected_cube:
|
|
711
|
+
print(f'last_moved_robot_timestamp: {connected_cube.last_moved_robot_timestamp}')
|
|
712
|
+
time.sleep(0.5)
|
|
713
|
+
"""
|
|
714
|
+
return self._last_moved_robot_timestamp
|
|
715
|
+
|
|
716
|
+
@property
|
|
717
|
+
def last_moved_start_time(self) -> float:
|
|
718
|
+
"""The time the object most recently started moving in SDK time.
|
|
719
|
+
|
|
720
|
+
.. testcode::
|
|
721
|
+
|
|
722
|
+
import time
|
|
723
|
+
import anki_vector
|
|
724
|
+
|
|
725
|
+
with anki_vector.Robot() as robot:
|
|
726
|
+
print("disconnecting from any connected cube...")
|
|
727
|
+
robot.world.disconnect_cube()
|
|
728
|
+
|
|
729
|
+
time.sleep(2)
|
|
730
|
+
|
|
731
|
+
print("connect to a cube...")
|
|
732
|
+
connectionResult = robot.world.connect_cube()
|
|
733
|
+
|
|
734
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
735
|
+
for _ in range(16):
|
|
736
|
+
connected_cube = robot.world.connected_light_cube
|
|
737
|
+
if connected_cube:
|
|
738
|
+
print(f'last_moved_start_time: {connected_cube.last_moved_start_time}')
|
|
739
|
+
time.sleep(0.5)
|
|
740
|
+
"""
|
|
741
|
+
return self._last_moved_start_time
|
|
742
|
+
|
|
743
|
+
@property
|
|
744
|
+
def last_moved_start_robot_timestamp(self) -> float:
|
|
745
|
+
"""The time the object more recently started moving in robot time.
|
|
746
|
+
|
|
747
|
+
.. testcode::
|
|
748
|
+
|
|
749
|
+
import time
|
|
750
|
+
import anki_vector
|
|
751
|
+
|
|
752
|
+
with anki_vector.Robot() as robot:
|
|
753
|
+
print("disconnecting from any connected cube...")
|
|
754
|
+
robot.world.disconnect_cube()
|
|
755
|
+
|
|
756
|
+
time.sleep(2)
|
|
757
|
+
|
|
758
|
+
print("connect to a cube...")
|
|
759
|
+
connectionResult = robot.world.connect_cube()
|
|
760
|
+
|
|
761
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
762
|
+
for _ in range(16):
|
|
763
|
+
connected_cube = robot.world.connected_light_cube
|
|
764
|
+
if connected_cube:
|
|
765
|
+
print(f'last_moved_start_robot_timestamp: {connected_cube.last_moved_start_robot_timestamp}')
|
|
766
|
+
time.sleep(0.5)
|
|
767
|
+
"""
|
|
768
|
+
return self._last_moved_start_robot_timestamp
|
|
769
|
+
|
|
770
|
+
@property
|
|
771
|
+
def last_up_axis_changed_time(self) -> float:
|
|
772
|
+
"""The time the object's orientation last changed in SDK time.
|
|
773
|
+
|
|
774
|
+
.. testcode::
|
|
775
|
+
|
|
776
|
+
import time
|
|
777
|
+
import anki_vector
|
|
778
|
+
|
|
779
|
+
with anki_vector.Robot() as robot:
|
|
780
|
+
print("disconnecting from any connected cube...")
|
|
781
|
+
robot.world.disconnect_cube()
|
|
782
|
+
|
|
783
|
+
time.sleep(2)
|
|
784
|
+
|
|
785
|
+
print("connect to a cube...")
|
|
786
|
+
connectionResult = robot.world.connect_cube()
|
|
787
|
+
|
|
788
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
789
|
+
for _ in range(16):
|
|
790
|
+
connected_cube = robot.world.connected_light_cube
|
|
791
|
+
if connected_cube:
|
|
792
|
+
print(f'last_up_axis_changed_time: {connected_cube.last_up_axis_changed_time}')
|
|
793
|
+
time.sleep(0.5)
|
|
794
|
+
"""
|
|
795
|
+
return self._last_up_axis_changed_time
|
|
796
|
+
|
|
797
|
+
@property
|
|
798
|
+
def last_up_axis_changed_robot_timestamp(self) -> float:
|
|
799
|
+
"""Time the object's orientation last changed in robot time.
|
|
800
|
+
|
|
801
|
+
.. testcode::
|
|
802
|
+
|
|
803
|
+
import time
|
|
804
|
+
import anki_vector
|
|
805
|
+
|
|
806
|
+
with anki_vector.Robot() as robot:
|
|
807
|
+
print("disconnecting from any connected cube...")
|
|
808
|
+
robot.world.disconnect_cube()
|
|
809
|
+
|
|
810
|
+
time.sleep(2)
|
|
811
|
+
|
|
812
|
+
print("connect to a cube...")
|
|
813
|
+
connectionResult = robot.world.connect_cube()
|
|
814
|
+
|
|
815
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
816
|
+
for _ in range(16):
|
|
817
|
+
connected_cube = robot.world.connected_light_cube
|
|
818
|
+
if connected_cube:
|
|
819
|
+
print(f'last_up_axis_changed_robot_timestamp: {connected_cube.last_up_axis_changed_robot_timestamp}')
|
|
820
|
+
time.sleep(0.5)
|
|
821
|
+
"""
|
|
822
|
+
return self._last_up_axis_changed_robot_timestamp
|
|
823
|
+
|
|
824
|
+
@property
|
|
825
|
+
def up_axis(self) -> protocol.UpAxis:
|
|
826
|
+
"""The object's up_axis value from the last time it changed.
|
|
827
|
+
|
|
828
|
+
.. testcode::
|
|
829
|
+
|
|
830
|
+
import time
|
|
831
|
+
import anki_vector
|
|
832
|
+
|
|
833
|
+
with anki_vector.Robot() as robot:
|
|
834
|
+
print("disconnecting from any connected cube...")
|
|
835
|
+
robot.world.disconnect_cube()
|
|
836
|
+
|
|
837
|
+
time.sleep(2)
|
|
838
|
+
|
|
839
|
+
print("connect to a cube...")
|
|
840
|
+
connectionResult = robot.world.connect_cube()
|
|
841
|
+
|
|
842
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
843
|
+
for _ in range(16):
|
|
844
|
+
connected_cube = robot.world.connected_light_cube
|
|
845
|
+
if connected_cube:
|
|
846
|
+
print(f'up_axis: {connected_cube.up_axis}')
|
|
847
|
+
time.sleep(0.5)
|
|
848
|
+
"""
|
|
849
|
+
return self._up_axis
|
|
850
|
+
|
|
851
|
+
@property
|
|
852
|
+
def is_moving(self) -> bool:
|
|
853
|
+
"""True if the cube's accelerometer indicates that the cube is moving.
|
|
854
|
+
|
|
855
|
+
.. testcode::
|
|
856
|
+
|
|
857
|
+
import time
|
|
858
|
+
import anki_vector
|
|
859
|
+
|
|
860
|
+
with anki_vector.Robot() as robot:
|
|
861
|
+
print("disconnecting from any connected cube...")
|
|
862
|
+
robot.world.disconnect_cube()
|
|
863
|
+
|
|
864
|
+
time.sleep(2)
|
|
865
|
+
|
|
866
|
+
print("connect to a cube...")
|
|
867
|
+
connectionResult = robot.world.connect_cube()
|
|
868
|
+
|
|
869
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
870
|
+
for _ in range(16):
|
|
871
|
+
connected_cube = robot.world.connected_light_cube
|
|
872
|
+
if connected_cube:
|
|
873
|
+
print(f'is_moving: {connected_cube.is_moving}')
|
|
874
|
+
time.sleep(0.5)
|
|
875
|
+
"""
|
|
876
|
+
return self._is_moving
|
|
877
|
+
|
|
878
|
+
@property
|
|
879
|
+
def is_connected(self) -> bool:
|
|
880
|
+
"""True if the cube is currently connected to the robot.
|
|
881
|
+
|
|
882
|
+
.. testcode::
|
|
883
|
+
|
|
884
|
+
import anki_vector
|
|
885
|
+
|
|
886
|
+
with anki_vector.Robot() as robot:
|
|
887
|
+
robot.world.connect_cube()
|
|
888
|
+
if robot.world.connected_light_cube:
|
|
889
|
+
cube = robot.world.connected_light_cube
|
|
890
|
+
print(f"{cube.is_connected}")
|
|
891
|
+
"""
|
|
892
|
+
return self._is_connected
|
|
893
|
+
|
|
894
|
+
@property
|
|
895
|
+
def top_face_orientation_rad(self) -> float:
|
|
896
|
+
"""Angular distance from the current reported up axis.
|
|
897
|
+
|
|
898
|
+
.. testcode::
|
|
899
|
+
|
|
900
|
+
import time
|
|
901
|
+
import anki_vector
|
|
902
|
+
|
|
903
|
+
with anki_vector.Robot() as robot:
|
|
904
|
+
print("disconnecting from any connected cube...")
|
|
905
|
+
robot.world.disconnect_cube()
|
|
906
|
+
|
|
907
|
+
time.sleep(2)
|
|
908
|
+
|
|
909
|
+
print("connect to a cube...")
|
|
910
|
+
connectionResult = robot.world.connect_cube()
|
|
911
|
+
|
|
912
|
+
print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.")
|
|
913
|
+
for _ in range(16):
|
|
914
|
+
connected_cube = robot.world.connected_light_cube
|
|
915
|
+
if connected_cube:
|
|
916
|
+
print(f'top_face_orientation_rad: {connected_cube.top_face_orientation_rad}')
|
|
917
|
+
time.sleep(0.5)
|
|
918
|
+
"""
|
|
919
|
+
return self._top_face_orientation_rad
|
|
920
|
+
|
|
921
|
+
@property
|
|
922
|
+
def factory_id(self) -> str:
|
|
923
|
+
"""The unique hardware id of the physical cube.
|
|
924
|
+
|
|
925
|
+
.. testcode::
|
|
926
|
+
|
|
927
|
+
import anki_vector
|
|
928
|
+
|
|
929
|
+
with anki_vector.Robot() as robot:
|
|
930
|
+
robot.world.connect_cube()
|
|
931
|
+
if robot.world.connected_light_cube:
|
|
932
|
+
cube = robot.world.connected_light_cube
|
|
933
|
+
print(f"{cube.factory_id}")
|
|
934
|
+
"""
|
|
935
|
+
return self._factory_id
|
|
936
|
+
|
|
937
|
+
@factory_id.setter
|
|
938
|
+
def factory_id(self, value: str):
|
|
939
|
+
self._factory_id = value
|
|
940
|
+
|
|
941
|
+
@property
|
|
942
|
+
def descriptive_name(self) -> str:
|
|
943
|
+
"""A descriptive name for this ObservableObject instance.
|
|
944
|
+
|
|
945
|
+
Note: Sub-classes should override this to add any other relevant info
|
|
946
|
+
for that object type.
|
|
947
|
+
|
|
948
|
+
.. testcode::
|
|
949
|
+
|
|
950
|
+
import anki_vector
|
|
951
|
+
|
|
952
|
+
with anki_vector.Robot() as robot:
|
|
953
|
+
robot.world.connect_cube()
|
|
954
|
+
if robot.world.connected_light_cube:
|
|
955
|
+
cube = robot.world.connected_light_cube
|
|
956
|
+
print(f"{cube.descriptive_name}")
|
|
957
|
+
"""
|
|
958
|
+
return f"{self.__class__.__name__}\nid={self._object_id}\nfactory_id={self._factory_id}\nis_connected={self._is_connected}"
|
|
959
|
+
|
|
960
|
+
@property
|
|
961
|
+
def object_id(self) -> int:
|
|
962
|
+
"""The internal ID assigned to the object.
|
|
963
|
+
|
|
964
|
+
This value can only be assigned once as it is static on the robot.
|
|
965
|
+
|
|
966
|
+
.. testcode::
|
|
967
|
+
|
|
968
|
+
import anki_vector
|
|
969
|
+
|
|
970
|
+
with anki_vector.Robot() as robot:
|
|
971
|
+
robot.world.connect_cube()
|
|
972
|
+
if robot.world.connected_light_cube:
|
|
973
|
+
cube = robot.world.connected_light_cube
|
|
974
|
+
print(f"{cube.object_id}")
|
|
975
|
+
"""
|
|
976
|
+
return self._object_id
|
|
977
|
+
|
|
978
|
+
@object_id.setter
|
|
979
|
+
def object_id(self, value: str):
|
|
980
|
+
if self._object_id is not None:
|
|
981
|
+
# We cannot currently rely on robot ensuring that object ID remains static
|
|
982
|
+
# E.g. in the case of a cube disconnecting and reconnecting it's removed
|
|
983
|
+
# and then re-added to blockworld which results in a new ID.
|
|
984
|
+
self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value)
|
|
985
|
+
else:
|
|
986
|
+
self.logger.debug("Setting object_id for %s to %s", self.__class__, value)
|
|
987
|
+
self._object_id = value
|
|
988
|
+
|
|
989
|
+
#### Private Event Handlers ####
|
|
990
|
+
|
|
991
|
+
def _on_object_connection_state_changed(self, _robot, _event_type, msg):
|
|
992
|
+
if msg.object_type == LIGHT_CUBE_1_TYPE:
|
|
993
|
+
self._object_id = msg.object_id
|
|
994
|
+
|
|
995
|
+
if self._factory_id != msg.factory_id:
|
|
996
|
+
self.logger.debug('Factory id changed from {0} to {1}'.format(self._factory_id, msg.factory_id))
|
|
997
|
+
self._factory_id = msg.factory_id
|
|
998
|
+
|
|
999
|
+
if self._is_connected != msg.connected:
|
|
1000
|
+
if msg.connected:
|
|
1001
|
+
self.logger.debug('Object connected: %s', self)
|
|
1002
|
+
else:
|
|
1003
|
+
self.logger.debug('Object disconnected: %s', self)
|
|
1004
|
+
self._is_connected = msg.connected
|
|
1005
|
+
|
|
1006
|
+
def _on_object_moved(self, _robot, _event_type, msg):
|
|
1007
|
+
if msg.object_id == self._object_id:
|
|
1008
|
+
now = time.time()
|
|
1009
|
+
started_moving = not self._is_moving
|
|
1010
|
+
self._is_moving = True
|
|
1011
|
+
self._last_event_time = now
|
|
1012
|
+
self._last_moved_time = now
|
|
1013
|
+
self._last_moved_robot_timestamp = msg.timestamp
|
|
1014
|
+
|
|
1015
|
+
if started_moving:
|
|
1016
|
+
self._last_moved_start_time = now
|
|
1017
|
+
self._last_moved_start_robot_timestamp = msg.timestamp
|
|
1018
|
+
else:
|
|
1019
|
+
self.logger.warning('An object not currently tracked by the world moved with id {0}'.format(msg.object_id))
|
|
1020
|
+
|
|
1021
|
+
async def _on_object_stopped_moving(self, _robot, _event_type, msg):
|
|
1022
|
+
if msg.object_id == self._object_id:
|
|
1023
|
+
now = time.time()
|
|
1024
|
+
self._last_event_time = now
|
|
1025
|
+
move_duration = 0.0
|
|
1026
|
+
|
|
1027
|
+
# _is_moving might already be false.
|
|
1028
|
+
# This happens for very short movements that are immediately
|
|
1029
|
+
# considered stopped (no acceleration info is present)
|
|
1030
|
+
if self._is_moving:
|
|
1031
|
+
self._is_moving = False
|
|
1032
|
+
move_duration = now - self._last_moved_start_time
|
|
1033
|
+
await self._robot.events.dispatch_event(EvtObjectFinishedMove(self, move_duration), Events.object_finished_move)
|
|
1034
|
+
else:
|
|
1035
|
+
self.logger.warning('An object not currently tracked by the world stopped moving with id {0}'.format(msg.object_id))
|
|
1036
|
+
|
|
1037
|
+
def _on_object_up_axis_changed(self, _robot, _event_type, msg):
|
|
1038
|
+
if msg.object_id == self._object_id:
|
|
1039
|
+
|
|
1040
|
+
now = time.time()
|
|
1041
|
+
self._up_axis = msg.up_axis
|
|
1042
|
+
self._last_event_time = now
|
|
1043
|
+
self._last_up_axis_changed_time = now
|
|
1044
|
+
self._last_up_axis_changed_robot_timestamp = msg.timestamp
|
|
1045
|
+
else:
|
|
1046
|
+
self.logger.warning('Up Axis changed on an object not currently tracked by the world with id {0}'.format(msg.object_id))
|
|
1047
|
+
|
|
1048
|
+
def _on_object_tapped(self, _robot, _event_type, msg):
|
|
1049
|
+
if msg.object_id == self._object_id:
|
|
1050
|
+
|
|
1051
|
+
now = time.time()
|
|
1052
|
+
self._last_event_time = now
|
|
1053
|
+
self._last_tapped_time = now
|
|
1054
|
+
self._last_tapped_robot_timestamp = msg.timestamp
|
|
1055
|
+
else:
|
|
1056
|
+
self.logger.warning('Tapped an object not currently tracked by the world with id {0}'.format(msg.object_id))
|
|
1057
|
+
|
|
1058
|
+
def _on_object_observed(self, _robot, _event_type, msg):
|
|
1059
|
+
if msg.object_id == self._object_id:
|
|
1060
|
+
|
|
1061
|
+
pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
|
|
1062
|
+
q0=msg.pose.q0, q1=msg.pose.q1,
|
|
1063
|
+
q2=msg.pose.q2, q3=msg.pose.q3,
|
|
1064
|
+
origin_id=msg.pose.origin_id)
|
|
1065
|
+
image_rect = util.ImageRect(msg.img_rect.x_top_left,
|
|
1066
|
+
msg.img_rect.y_top_left,
|
|
1067
|
+
msg.img_rect.width,
|
|
1068
|
+
msg.img_rect.height)
|
|
1069
|
+
self._top_face_orientation_rad = msg.top_face_orientation_rad
|
|
1070
|
+
|
|
1071
|
+
self._on_observed(pose, image_rect, msg.timestamp)
|
|
1072
|
+
|
|
1073
|
+
def _on_object_connection_lost(self, _robot, _event_type, msg):
|
|
1074
|
+
if msg.object_id == self._object_id:
|
|
1075
|
+
self._is_connected = False
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
class Charger(ObservableObject):
|
|
1079
|
+
"""Vector's charger object, which the robot can observe and drive toward.
|
|
1080
|
+
We get an :class:`anki_vector.objects.EvtObjectObserved` message when the
|
|
1081
|
+
robot sees the charger.
|
|
1082
|
+
|
|
1083
|
+
See parent class :class:`ObservableObject` for additional properties
|
|
1084
|
+
and methods.
|
|
1085
|
+
|
|
1086
|
+
.. testcode::
|
|
1087
|
+
|
|
1088
|
+
import anki_vector
|
|
1089
|
+
|
|
1090
|
+
# Position Vector so he can see his charger
|
|
1091
|
+
with anki_vector.Robot() as robot:
|
|
1092
|
+
if robot.world.charger:
|
|
1093
|
+
print('Robot is aware of charger: {0}'.format(robot.world.charger))
|
|
1094
|
+
"""
|
|
1095
|
+
|
|
1096
|
+
def __init__(self, robot, object_id: int, **kw):
|
|
1097
|
+
super().__init__(robot, **kw)
|
|
1098
|
+
|
|
1099
|
+
self._object_id = object_id
|
|
1100
|
+
|
|
1101
|
+
self.robot.events.subscribe(self._on_object_observed,
|
|
1102
|
+
Events.robot_observed_object)
|
|
1103
|
+
|
|
1104
|
+
#### Public Methods ####
|
|
1105
|
+
|
|
1106
|
+
def teardown(self):
|
|
1107
|
+
"""All objects will be torn down by the world when the world closes."""
|
|
1108
|
+
|
|
1109
|
+
self.robot.events.unsubscribe(self._on_object_observed,
|
|
1110
|
+
Events.robot_observed_object)
|
|
1111
|
+
|
|
1112
|
+
#### Properties ####
|
|
1113
|
+
@property
|
|
1114
|
+
def object_id(self) -> int:
|
|
1115
|
+
"""The internal ID assigned to the object.
|
|
1116
|
+
|
|
1117
|
+
.. testcode::
|
|
1118
|
+
|
|
1119
|
+
import anki_vector
|
|
1120
|
+
|
|
1121
|
+
# Position Vector so he can see his charger
|
|
1122
|
+
with anki_vector.Robot() as robot:
|
|
1123
|
+
if robot.world.charger:
|
|
1124
|
+
charger_object_id = robot.world.charger.object_id
|
|
1125
|
+
print(f"charger_object_id: {charger_object_id}")
|
|
1126
|
+
|
|
1127
|
+
This value can only be assigned once as it is static on the robot.
|
|
1128
|
+
"""
|
|
1129
|
+
return self._object_id
|
|
1130
|
+
|
|
1131
|
+
@object_id.setter
|
|
1132
|
+
def object_id(self, value: str):
|
|
1133
|
+
if self._object_id is not None:
|
|
1134
|
+
# We cannot currently rely on robot ensuring that object ID remains static
|
|
1135
|
+
# E.g. in the case of a cube disconnecting and reconnecting it's removed
|
|
1136
|
+
# and then re-added to blockworld which results in a new ID.
|
|
1137
|
+
self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value)
|
|
1138
|
+
else:
|
|
1139
|
+
self.logger.debug("Setting object_id for %s to %s", self.__class__, value)
|
|
1140
|
+
self._object_id = value
|
|
1141
|
+
|
|
1142
|
+
@property
|
|
1143
|
+
def descriptive_name(self) -> str:
|
|
1144
|
+
"""A descriptive name for this ObservableObject instance.
|
|
1145
|
+
|
|
1146
|
+
Note: Sub-classes should override this to add any other relevant info
|
|
1147
|
+
for that object type.
|
|
1148
|
+
|
|
1149
|
+
.. testcode::
|
|
1150
|
+
|
|
1151
|
+
import anki_vector
|
|
1152
|
+
|
|
1153
|
+
with anki_vector.Robot() as robot:
|
|
1154
|
+
if robot.world.charger:
|
|
1155
|
+
print(f"{robot.world.charger.descriptive_name}")
|
|
1156
|
+
"""
|
|
1157
|
+
return f"{self.__class__.__name__} id={self._object_id}"
|
|
1158
|
+
|
|
1159
|
+
#### Private Methods ####
|
|
1160
|
+
|
|
1161
|
+
def _on_object_observed(self, _robot, _event_type, msg):
|
|
1162
|
+
if msg.object_id == self._object_id:
|
|
1163
|
+
|
|
1164
|
+
pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
|
|
1165
|
+
q0=msg.pose.q0, q1=msg.pose.q1,
|
|
1166
|
+
q2=msg.pose.q2, q3=msg.pose.q3,
|
|
1167
|
+
origin_id=msg.pose.origin_id)
|
|
1168
|
+
image_rect = util.ImageRect(msg.img_rect.x_top_left,
|
|
1169
|
+
msg.img_rect.y_top_left,
|
|
1170
|
+
msg.img_rect.width,
|
|
1171
|
+
msg.img_rect.height)
|
|
1172
|
+
|
|
1173
|
+
self._on_observed(pose, image_rect, msg.timestamp)
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
class CustomObjectArchetype():
|
|
1177
|
+
"""An object archetype defined by the SDK. It is bound to a specific objectType e.g ``CustomType00``.
|
|
1178
|
+
|
|
1179
|
+
This defined object is given a size in the x,y and z axis. The dimensions
|
|
1180
|
+
of the markers on the object are also defined.
|
|
1181
|
+
|
|
1182
|
+
See :class:`CustomObjectMarkers`.
|
|
1183
|
+
|
|
1184
|
+
When the robot observes custom objects, they will be linked to these archetypes.
|
|
1185
|
+
These can be created using the methods
|
|
1186
|
+
:meth:`~anki_vector.world.World.define_custom_box`,
|
|
1187
|
+
:meth:`~anki_vector.world.World.define_custom_cube`, or
|
|
1188
|
+
:meth:`~anki_vector.world.World.define_custom_wall`.
|
|
1189
|
+
"""
|
|
1190
|
+
|
|
1191
|
+
def __init__(self,
|
|
1192
|
+
custom_type: protocol.CustomType,
|
|
1193
|
+
x_size_mm: float,
|
|
1194
|
+
y_size_mm: float,
|
|
1195
|
+
z_size_mm: float,
|
|
1196
|
+
marker_width_mm: float,
|
|
1197
|
+
marker_height_mm: float,
|
|
1198
|
+
is_unique: bool):
|
|
1199
|
+
|
|
1200
|
+
self._custom_type = custom_type
|
|
1201
|
+
self._x_size_mm = x_size_mm
|
|
1202
|
+
self._y_size_mm = y_size_mm
|
|
1203
|
+
self._z_size_mm = z_size_mm
|
|
1204
|
+
self._marker_width_mm = marker_width_mm
|
|
1205
|
+
self._marker_height_mm = marker_height_mm
|
|
1206
|
+
self._is_unique = is_unique
|
|
1207
|
+
|
|
1208
|
+
#### Properties ####
|
|
1209
|
+
|
|
1210
|
+
@property
|
|
1211
|
+
def custom_type(self) -> protocol.CustomType:
|
|
1212
|
+
"""id of this archetype on the robot.
|
|
1213
|
+
|
|
1214
|
+
See :class:`CustomObjectMarkers`.
|
|
1215
|
+
|
|
1216
|
+
.. testcode::
|
|
1217
|
+
|
|
1218
|
+
import anki_vector
|
|
1219
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1220
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1221
|
+
print('custom object archetype defined with type: {0}'.format(obj.custom_type))
|
|
1222
|
+
"""
|
|
1223
|
+
return self._custom_type
|
|
1224
|
+
|
|
1225
|
+
@property
|
|
1226
|
+
def x_size_mm(self) -> float:
|
|
1227
|
+
"""Size of this object in its X axis, in millimeters.
|
|
1228
|
+
|
|
1229
|
+
See :class:`CustomObjectMarkers`.
|
|
1230
|
+
|
|
1231
|
+
.. testcode::
|
|
1232
|
+
|
|
1233
|
+
import anki_vector
|
|
1234
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1235
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1236
|
+
print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1237
|
+
"""
|
|
1238
|
+
return self._x_size_mm
|
|
1239
|
+
|
|
1240
|
+
@property
|
|
1241
|
+
def y_size_mm(self) -> float:
|
|
1242
|
+
"""Size of this object in its Y axis, in millimeters.
|
|
1243
|
+
|
|
1244
|
+
See :class:`CustomObjectMarkers`.
|
|
1245
|
+
|
|
1246
|
+
.. testcode::
|
|
1247
|
+
|
|
1248
|
+
import anki_vector
|
|
1249
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1250
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1251
|
+
print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1252
|
+
"""
|
|
1253
|
+
return self._y_size_mm
|
|
1254
|
+
|
|
1255
|
+
@property
|
|
1256
|
+
def z_size_mm(self) -> float:
|
|
1257
|
+
"""Size of this object in its Z axis, in millimeters.
|
|
1258
|
+
|
|
1259
|
+
See :class:`CustomObjectMarkers`.
|
|
1260
|
+
|
|
1261
|
+
.. testcode::
|
|
1262
|
+
|
|
1263
|
+
import anki_vector
|
|
1264
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1265
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1266
|
+
print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1267
|
+
"""
|
|
1268
|
+
return self._z_size_mm
|
|
1269
|
+
|
|
1270
|
+
@property
|
|
1271
|
+
def marker_width_mm(self) -> float:
|
|
1272
|
+
"""Width in millimeters of the marker on this object.
|
|
1273
|
+
|
|
1274
|
+
See :class:`CustomObjectMarkers`.
|
|
1275
|
+
|
|
1276
|
+
.. testcode::
|
|
1277
|
+
|
|
1278
|
+
import anki_vector
|
|
1279
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1280
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1281
|
+
print('custom object archetype defined with marker size: {0}mm x {1}mm'.format(obj.marker_width_mm, obj.marker_height_mm))
|
|
1282
|
+
"""
|
|
1283
|
+
return self._marker_width_mm
|
|
1284
|
+
|
|
1285
|
+
@property
|
|
1286
|
+
def marker_height_mm(self) -> float:
|
|
1287
|
+
"""Height in millimeters of the marker on this object.
|
|
1288
|
+
|
|
1289
|
+
See :class:`CustomObjectMarkers`.
|
|
1290
|
+
|
|
1291
|
+
.. testcode::
|
|
1292
|
+
|
|
1293
|
+
import anki_vector
|
|
1294
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1295
|
+
for obj in robot.world.custom_object_archetypes:
|
|
1296
|
+
print('custom object archetype defined with marker size: {0}mm x {1}mm'.format(obj.marker_width_mm, obj.marker_height_mm))
|
|
1297
|
+
"""
|
|
1298
|
+
return self._marker_height_mm
|
|
1299
|
+
|
|
1300
|
+
@property
|
|
1301
|
+
def is_unique(self) -> bool:
|
|
1302
|
+
"""True if there should only be one of this object type in the world.
|
|
1303
|
+
|
|
1304
|
+
See :class:`CustomObjectMarkers`.
|
|
1305
|
+
"""
|
|
1306
|
+
return self._is_unique
|
|
1307
|
+
|
|
1308
|
+
#### Private Methods ####
|
|
1309
|
+
|
|
1310
|
+
def __repr__(self):
|
|
1311
|
+
return ('custom_type={self.custom_type} '
|
|
1312
|
+
'x_size_mm={self.x_size_mm:.1f} '
|
|
1313
|
+
'y_size_mm={self.y_size_mm:.1f} '
|
|
1314
|
+
'z_size_mm={self.z_size_mm:.1f} '
|
|
1315
|
+
'marker_width_mm={self.marker_width_mm:.1f} '
|
|
1316
|
+
'marker_height_mm={self.marker_height_mm:.1f} '
|
|
1317
|
+
'is_unique={self.is_unique}'.format(self=self))
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
class CustomObject(ObservableObject):
|
|
1321
|
+
"""An object defined by the SDK observed by the robot. The object will
|
|
1322
|
+
reference a :class:`CustomObjectArchetype`, with additional instance data.
|
|
1323
|
+
|
|
1324
|
+
These objects are created automatically by the engine when Vector observes
|
|
1325
|
+
an object with custom markers. For Vector to see one of these you must first
|
|
1326
|
+
define an archetype with custom markers, via one of the following methods:
|
|
1327
|
+
:meth:`~anki_vector.world.World.define_custom_box`.
|
|
1328
|
+
:meth:`~anki_vector.world.World.define_custom_cube`, or
|
|
1329
|
+
:meth:`~anki_vector.world.World.define_custom_wall`
|
|
1330
|
+
|
|
1331
|
+
See :class:`CustomObjectMarkers`.
|
|
1332
|
+
"""
|
|
1333
|
+
|
|
1334
|
+
def __init__(self,
|
|
1335
|
+
robot,
|
|
1336
|
+
archetype: CustomObjectArchetype,
|
|
1337
|
+
object_id: int, **kw):
|
|
1338
|
+
super().__init__(robot, **kw)
|
|
1339
|
+
|
|
1340
|
+
self._object_id = object_id
|
|
1341
|
+
self._archetype = archetype
|
|
1342
|
+
|
|
1343
|
+
self.robot.events.subscribe(self._on_object_observed,
|
|
1344
|
+
Events.robot_observed_object)
|
|
1345
|
+
|
|
1346
|
+
#### Public Methods ####
|
|
1347
|
+
|
|
1348
|
+
def teardown(self):
|
|
1349
|
+
"""All objects will be torn down by the world when no longer needed.
|
|
1350
|
+
|
|
1351
|
+
See :class:`CustomObjectMarkers`.
|
|
1352
|
+
"""
|
|
1353
|
+
|
|
1354
|
+
self.robot.events.unsubscribe(self._on_object_observed,
|
|
1355
|
+
Events.robot_observed_object)
|
|
1356
|
+
|
|
1357
|
+
#### Properties ####
|
|
1358
|
+
|
|
1359
|
+
@property
|
|
1360
|
+
def object_id(self) -> int:
|
|
1361
|
+
"""The internal ID assigned to the object.
|
|
1362
|
+
|
|
1363
|
+
This value can only be assigned once as it is static on the robot.
|
|
1364
|
+
|
|
1365
|
+
See :class:`CustomObjectMarkers`.
|
|
1366
|
+
|
|
1367
|
+
.. testcode::
|
|
1368
|
+
|
|
1369
|
+
import anki_vector
|
|
1370
|
+
from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes
|
|
1371
|
+
|
|
1372
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1373
|
+
robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00,
|
|
1374
|
+
marker=CustomObjectMarkers.Circles2,
|
|
1375
|
+
size_mm=20.0,
|
|
1376
|
+
marker_width_mm=50.0, marker_height_mm=50.0)
|
|
1377
|
+
|
|
1378
|
+
# have the robot observe a custom object in the real world with the Circles2 marker
|
|
1379
|
+
|
|
1380
|
+
for obj in robot.world.visible_custom_objects:
|
|
1381
|
+
print('custom object seen with id: {0}'.format(obj.object_id))
|
|
1382
|
+
"""
|
|
1383
|
+
return self._object_id
|
|
1384
|
+
|
|
1385
|
+
@object_id.setter
|
|
1386
|
+
def object_id(self, value: str):
|
|
1387
|
+
if self._object_id is not None:
|
|
1388
|
+
# We cannot currently rely on robot ensuring that object ID remains static
|
|
1389
|
+
# E.g. in the case of a cube disconnecting and reconnecting it's removed
|
|
1390
|
+
# and then re-added to robot's internal world model which results in a new ID.
|
|
1391
|
+
self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value)
|
|
1392
|
+
else:
|
|
1393
|
+
self.logger.debug("Setting object_id for %s to %s", self.__class__, value)
|
|
1394
|
+
self._object_id = value
|
|
1395
|
+
|
|
1396
|
+
@property
|
|
1397
|
+
def archetype(self) -> CustomObjectArchetype:
|
|
1398
|
+
"""Archetype defining this custom object's properties.
|
|
1399
|
+
|
|
1400
|
+
See :class:`CustomObjectMarkers`.
|
|
1401
|
+
|
|
1402
|
+
.. testcode::
|
|
1403
|
+
|
|
1404
|
+
import anki_vector
|
|
1405
|
+
from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes
|
|
1406
|
+
|
|
1407
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1408
|
+
robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00,
|
|
1409
|
+
marker=CustomObjectMarkers.Circles2,
|
|
1410
|
+
size_mm=20.0,
|
|
1411
|
+
marker_width_mm=50.0, marker_height_mm=50.0)
|
|
1412
|
+
|
|
1413
|
+
# have the robot observe a custom object in the real world with the Circles2 marker
|
|
1414
|
+
|
|
1415
|
+
for obj in robot.world.visible_custom_objects:
|
|
1416
|
+
print('custom object seen with archetype: {0}'.format(obj.archetype))
|
|
1417
|
+
"""
|
|
1418
|
+
return self._archetype
|
|
1419
|
+
|
|
1420
|
+
@property
|
|
1421
|
+
def descriptive_name(self) -> str:
|
|
1422
|
+
"""A descriptive name for this CustomObject instance.
|
|
1423
|
+
|
|
1424
|
+
See :class:`CustomObjectMarkers`.
|
|
1425
|
+
|
|
1426
|
+
.. testcode::
|
|
1427
|
+
|
|
1428
|
+
import anki_vector
|
|
1429
|
+
from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes
|
|
1430
|
+
|
|
1431
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1432
|
+
robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00,
|
|
1433
|
+
marker=CustomObjectMarkers.Circles2,
|
|
1434
|
+
size_mm=20.0,
|
|
1435
|
+
marker_width_mm=50.0, marker_height_mm=50.0)
|
|
1436
|
+
|
|
1437
|
+
# have the robot observe a custom object in the real world with the Circles2 marker
|
|
1438
|
+
|
|
1439
|
+
for obj in robot.world.visible_custom_objects:
|
|
1440
|
+
print('custom object seen with name: {0}'.format(obj.descriptive_name))
|
|
1441
|
+
"""
|
|
1442
|
+
return "%s id=%d" % (self.__class__.__name__, self.object_id)
|
|
1443
|
+
|
|
1444
|
+
#### Private Methods ####
|
|
1445
|
+
|
|
1446
|
+
def _repr_values(self):
|
|
1447
|
+
return ('object_type={archetype.custom_type} '
|
|
1448
|
+
'x_size_mm={archetype.x_size_mm:.1f} '
|
|
1449
|
+
'y_size_mm={archetype.y_size_mm:.1f} '
|
|
1450
|
+
'z_size_mm={archetype.z_size_mm:.1f} '
|
|
1451
|
+
'is_unique={archetype.is_unique}'.format(archetype=self._archetype))
|
|
1452
|
+
|
|
1453
|
+
def _on_object_observed(self, _robot, _event_type, msg):
|
|
1454
|
+
if msg.object_id == self._object_id:
|
|
1455
|
+
|
|
1456
|
+
pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
|
|
1457
|
+
q0=msg.pose.q0, q1=msg.pose.q1,
|
|
1458
|
+
q2=msg.pose.q2, q3=msg.pose.q3,
|
|
1459
|
+
origin_id=msg.pose.origin_id)
|
|
1460
|
+
image_rect = util.ImageRect(msg.img_rect.x_top_left,
|
|
1461
|
+
msg.img_rect.y_top_left,
|
|
1462
|
+
msg.img_rect.width,
|
|
1463
|
+
msg.img_rect.height)
|
|
1464
|
+
|
|
1465
|
+
self._on_observed(pose, image_rect, msg.timestamp)
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
class _CustomObjectType(collections.namedtuple('_CustomObjectType', 'name id')):
|
|
1469
|
+
# Tuple mapping between Proto CustomObjectType name and ID
|
|
1470
|
+
# All instances will be members of CustomObjectType
|
|
1471
|
+
|
|
1472
|
+
# Keep _CustomObjectType as lightweight as a normal namedtuple
|
|
1473
|
+
__slots__ = ()
|
|
1474
|
+
|
|
1475
|
+
def __str__(self):
|
|
1476
|
+
return 'CustomObjectTypes.%s' % self.name
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
class CustomObjectTypes(): # pylint: disable=too-few-public-methods
|
|
1480
|
+
"""Defines all available custom object types.
|
|
1481
|
+
|
|
1482
|
+
For use with world.define_custom methods such as
|
|
1483
|
+
:meth:`anki_vector.world.World.define_custom_box`,
|
|
1484
|
+
:meth:`anki_vector.world.World.define_custom_cube`, and
|
|
1485
|
+
:meth:`anki_vector.world.World.define_custom_wall`
|
|
1486
|
+
|
|
1487
|
+
See :class:`CustomObjectMarkers`.
|
|
1488
|
+
|
|
1489
|
+
.. testcode::
|
|
1490
|
+
|
|
1491
|
+
import anki_vector
|
|
1492
|
+
from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes
|
|
1493
|
+
|
|
1494
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1495
|
+
robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00,
|
|
1496
|
+
marker=CustomObjectMarkers.Circles2,
|
|
1497
|
+
size_mm=20.0,
|
|
1498
|
+
marker_width_mm=50.0, marker_height_mm=50.0)
|
|
1499
|
+
"""
|
|
1500
|
+
|
|
1501
|
+
#: CustomType00 - the first custom object type
|
|
1502
|
+
CustomType00 = _CustomObjectType("CustomType00", protocol.CustomType.Value("CUSTOM_TYPE_00"))
|
|
1503
|
+
|
|
1504
|
+
#:
|
|
1505
|
+
CustomType01 = _CustomObjectType("CustomType01", protocol.CustomType.Value("CUSTOM_TYPE_01"))
|
|
1506
|
+
|
|
1507
|
+
#:
|
|
1508
|
+
CustomType02 = _CustomObjectType("CustomType02", protocol.CustomType.Value("CUSTOM_TYPE_02"))
|
|
1509
|
+
|
|
1510
|
+
#:
|
|
1511
|
+
CustomType03 = _CustomObjectType("CustomType03", protocol.CustomType.Value("CUSTOM_TYPE_03"))
|
|
1512
|
+
|
|
1513
|
+
#:
|
|
1514
|
+
CustomType04 = _CustomObjectType("CustomType04", protocol.CustomType.Value("CUSTOM_TYPE_04"))
|
|
1515
|
+
|
|
1516
|
+
#:
|
|
1517
|
+
CustomType05 = _CustomObjectType("CustomType05", protocol.CustomType.Value("CUSTOM_TYPE_05"))
|
|
1518
|
+
|
|
1519
|
+
#:
|
|
1520
|
+
CustomType06 = _CustomObjectType("CustomType06", protocol.CustomType.Value("CUSTOM_TYPE_06"))
|
|
1521
|
+
|
|
1522
|
+
#:
|
|
1523
|
+
CustomType07 = _CustomObjectType("CustomType07", protocol.CustomType.Value("CUSTOM_TYPE_07"))
|
|
1524
|
+
|
|
1525
|
+
#:
|
|
1526
|
+
CustomType08 = _CustomObjectType("CustomType08", protocol.CustomType.Value("CUSTOM_TYPE_08"))
|
|
1527
|
+
|
|
1528
|
+
#:
|
|
1529
|
+
CustomType09 = _CustomObjectType("CustomType09", protocol.CustomType.Value("CUSTOM_TYPE_09"))
|
|
1530
|
+
|
|
1531
|
+
#:
|
|
1532
|
+
CustomType10 = _CustomObjectType("CustomType10", protocol.CustomType.Value("CUSTOM_TYPE_10"))
|
|
1533
|
+
|
|
1534
|
+
#:
|
|
1535
|
+
CustomType11 = _CustomObjectType("CustomType11", protocol.CustomType.Value("CUSTOM_TYPE_11"))
|
|
1536
|
+
|
|
1537
|
+
#:
|
|
1538
|
+
CustomType12 = _CustomObjectType("CustomType12", protocol.CustomType.Value("CUSTOM_TYPE_12"))
|
|
1539
|
+
|
|
1540
|
+
#:
|
|
1541
|
+
CustomType13 = _CustomObjectType("CustomType13", protocol.CustomType.Value("CUSTOM_TYPE_13"))
|
|
1542
|
+
|
|
1543
|
+
#:
|
|
1544
|
+
CustomType14 = _CustomObjectType("CustomType14", protocol.CustomType.Value("CUSTOM_TYPE_14"))
|
|
1545
|
+
|
|
1546
|
+
#:
|
|
1547
|
+
CustomType15 = _CustomObjectType("CustomType15", protocol.CustomType.Value("CUSTOM_TYPE_15"))
|
|
1548
|
+
|
|
1549
|
+
#:
|
|
1550
|
+
CustomType16 = _CustomObjectType("CustomType16", protocol.CustomType.Value("CUSTOM_TYPE_16"))
|
|
1551
|
+
|
|
1552
|
+
#:
|
|
1553
|
+
CustomType17 = _CustomObjectType("CustomType17", protocol.CustomType.Value("CUSTOM_TYPE_17"))
|
|
1554
|
+
|
|
1555
|
+
#:
|
|
1556
|
+
CustomType18 = _CustomObjectType("CustomType18", protocol.CustomType.Value("CUSTOM_TYPE_18"))
|
|
1557
|
+
|
|
1558
|
+
#: CustomType19 - the last custom object type
|
|
1559
|
+
CustomType19 = _CustomObjectType("CustomType19", protocol.CustomType.Value("CUSTOM_TYPE_19"))
|
|
1560
|
+
|
|
1561
|
+
|
|
1562
|
+
class _CustomObjectMarker(collections.namedtuple('_CustomObjectMarker', 'name id')):
|
|
1563
|
+
# Tuple mapping between Proto CustomObjectMarker name and ID
|
|
1564
|
+
# All instances will be members of CustomObjectMarker
|
|
1565
|
+
|
|
1566
|
+
# Keep _CustomObjectMarker as lightweight as a normal namedtuple
|
|
1567
|
+
__slots__ = ()
|
|
1568
|
+
|
|
1569
|
+
def __str__(self):
|
|
1570
|
+
return 'CustomObjectMarkers.%s' % self.name
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
class CustomObjectMarkers(): # pylint: disable=too-few-public-methods
|
|
1574
|
+
"""Defines all available custom object markers.
|
|
1575
|
+
|
|
1576
|
+
For use with world.define_custom methods such as
|
|
1577
|
+
:meth:`anki_vector.world.World.define_custom_box`,
|
|
1578
|
+
:meth:`anki_vector.world.World.define_custom_cube`, and
|
|
1579
|
+
:meth:`anki_vector.world.World.define_custom_wall`
|
|
1580
|
+
|
|
1581
|
+
See :class:`CustomObject`.
|
|
1582
|
+
|
|
1583
|
+
.. testcode::
|
|
1584
|
+
|
|
1585
|
+
import anki_vector
|
|
1586
|
+
from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes
|
|
1587
|
+
|
|
1588
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1589
|
+
robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00,
|
|
1590
|
+
marker=CustomObjectMarkers.Circles2,
|
|
1591
|
+
size_mm=20.0,
|
|
1592
|
+
marker_width_mm=50.0, marker_height_mm=50.0)
|
|
1593
|
+
"""
|
|
1594
|
+
|
|
1595
|
+
#: .. image:: ../images/custom_markers/SDK_2Circles.png
|
|
1596
|
+
Circles2 = _CustomObjectMarker("Circles2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_2"))
|
|
1597
|
+
|
|
1598
|
+
#: .. image:: ../images/custom_markers/SDK_3Circles.png
|
|
1599
|
+
Circles3 = _CustomObjectMarker("Circles3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_3"))
|
|
1600
|
+
|
|
1601
|
+
#: .. image:: ../images/custom_markers/SDK_4Circles.png
|
|
1602
|
+
Circles4 = _CustomObjectMarker("Circles4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_4"))
|
|
1603
|
+
|
|
1604
|
+
#: .. image:: ../images/custom_markers/SDK_5Circles.png
|
|
1605
|
+
Circles5 = _CustomObjectMarker("Circles5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_5"))
|
|
1606
|
+
|
|
1607
|
+
#: .. image:: ../images/custom_markers/SDK_2Diamonds.png
|
|
1608
|
+
Diamonds2 = _CustomObjectMarker("Diamonds2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_2"))
|
|
1609
|
+
|
|
1610
|
+
#: .. image:: ../images/custom_markers/SDK_3Diamonds.png
|
|
1611
|
+
Diamonds3 = _CustomObjectMarker("Diamonds3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_3"))
|
|
1612
|
+
|
|
1613
|
+
#: .. image:: ../images/custom_markers/SDK_4Diamonds.png
|
|
1614
|
+
Diamonds4 = _CustomObjectMarker("Diamonds4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_4"))
|
|
1615
|
+
|
|
1616
|
+
#: .. image:: ../images/custom_markers/SDK_5Diamonds.png
|
|
1617
|
+
Diamonds5 = _CustomObjectMarker("Diamonds5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_5"))
|
|
1618
|
+
|
|
1619
|
+
#: .. image:: ../images/custom_markers/SDK_2Hexagons.png
|
|
1620
|
+
Hexagons2 = _CustomObjectMarker("Hexagons2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_2"))
|
|
1621
|
+
|
|
1622
|
+
#: .. image:: ../images/custom_markers/SDK_3Hexagons.png
|
|
1623
|
+
Hexagons3 = _CustomObjectMarker("Hexagons3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_3"))
|
|
1624
|
+
|
|
1625
|
+
#: .. image:: ../images/custom_markers/SDK_4Hexagons.png
|
|
1626
|
+
Hexagons4 = _CustomObjectMarker("Hexagons4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_4"))
|
|
1627
|
+
|
|
1628
|
+
#: .. image:: ../images/custom_markers/SDK_5Hexagons.png
|
|
1629
|
+
Hexagons5 = _CustomObjectMarker("Hexagons5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_5"))
|
|
1630
|
+
|
|
1631
|
+
#: .. image:: ../images/custom_markers/SDK_2Triangles.png
|
|
1632
|
+
Triangles2 = _CustomObjectMarker("Triangles2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_2"))
|
|
1633
|
+
|
|
1634
|
+
#: .. image:: ../images/custom_markers/SDK_3Triangles.png
|
|
1635
|
+
Triangles3 = _CustomObjectMarker("Triangles3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_3"))
|
|
1636
|
+
|
|
1637
|
+
#: .. image:: ../images/custom_markers/SDK_4Triangles.png
|
|
1638
|
+
Triangles4 = _CustomObjectMarker("Triangles4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_4"))
|
|
1639
|
+
|
|
1640
|
+
#: .. image:: ../images/custom_markers/SDK_5Triangles.png
|
|
1641
|
+
Triangles5 = _CustomObjectMarker("Triangles5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_5"))
|
|
1642
|
+
|
|
1643
|
+
|
|
1644
|
+
class FixedCustomObject(util.Component):
|
|
1645
|
+
"""A fixed object defined by the SDK. It is given a pose and x,y,z sizes.
|
|
1646
|
+
|
|
1647
|
+
This object cannot be observed by the robot so its pose never changes.
|
|
1648
|
+
The position is static in Vector's world view; once instantiated, these
|
|
1649
|
+
objects never move. This could be used to make Vector aware of objects and
|
|
1650
|
+
know to plot a path around them even when they don't have any markers.
|
|
1651
|
+
|
|
1652
|
+
To create these use :meth:`~anki_vector.world.World.create_custom_fixed_object`
|
|
1653
|
+
|
|
1654
|
+
.. testcode::
|
|
1655
|
+
|
|
1656
|
+
import anki_vector
|
|
1657
|
+
from anki_vector.util import degrees, Pose
|
|
1658
|
+
import time
|
|
1659
|
+
|
|
1660
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1661
|
+
robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1662
|
+
10, 100, 100, relative_to_robot=True)
|
|
1663
|
+
"""
|
|
1664
|
+
|
|
1665
|
+
def __init__(self,
|
|
1666
|
+
robot,
|
|
1667
|
+
pose: util.Pose,
|
|
1668
|
+
x_size_mm: float,
|
|
1669
|
+
y_size_mm: float,
|
|
1670
|
+
z_size_mm: float,
|
|
1671
|
+
object_id: int, **kw):
|
|
1672
|
+
super().__init__(robot, **kw)
|
|
1673
|
+
self._pose = pose
|
|
1674
|
+
self._x_size_mm = x_size_mm
|
|
1675
|
+
self._y_size_mm = y_size_mm
|
|
1676
|
+
self._z_size_mm = z_size_mm
|
|
1677
|
+
self._object_id = object_id
|
|
1678
|
+
|
|
1679
|
+
def __repr__(self):
|
|
1680
|
+
return ('<%s pose=%s object_id=%d x_size_mm=%.1f y_size_mm=%.1f z_size_mm=%.1f=>' %
|
|
1681
|
+
(self.__class__.__name__, self.pose, self.object_id,
|
|
1682
|
+
self.x_size_mm, self.y_size_mm, self.z_size_mm))
|
|
1683
|
+
|
|
1684
|
+
#### Public Methods ####
|
|
1685
|
+
|
|
1686
|
+
def teardown(self):
|
|
1687
|
+
pass
|
|
1688
|
+
|
|
1689
|
+
#### Properties ####
|
|
1690
|
+
@property
|
|
1691
|
+
def object_id(self) -> int:
|
|
1692
|
+
"""The internal ID assigned to the object.
|
|
1693
|
+
|
|
1694
|
+
This value can only be assigned once as it is static in the engine.
|
|
1695
|
+
|
|
1696
|
+
.. testcode::
|
|
1697
|
+
|
|
1698
|
+
import anki_vector
|
|
1699
|
+
from anki_vector.util import degrees, Pose
|
|
1700
|
+
import time
|
|
1701
|
+
|
|
1702
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1703
|
+
obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1704
|
+
10, 100, 100, relative_to_robot=True)
|
|
1705
|
+
print('fixed custom object id: {0}'.format(obj.object_id))
|
|
1706
|
+
"""
|
|
1707
|
+
return self._object_id
|
|
1708
|
+
|
|
1709
|
+
@object_id.setter
|
|
1710
|
+
def object_id(self, value: int):
|
|
1711
|
+
if self._object_id is not None:
|
|
1712
|
+
raise ValueError("Cannot change object ID once set (from %s to %s)" % (self._object_id, value))
|
|
1713
|
+
self.logger.debug("Updated object_id for %s from %s to %s", self.__class__, self._object_id, value)
|
|
1714
|
+
self._object_id = value
|
|
1715
|
+
|
|
1716
|
+
@property
|
|
1717
|
+
def pose(self) -> util.Pose:
|
|
1718
|
+
"""The pose of the object in the world.
|
|
1719
|
+
|
|
1720
|
+
.. testcode::
|
|
1721
|
+
|
|
1722
|
+
import anki_vector
|
|
1723
|
+
from anki_vector.util import degrees, Pose
|
|
1724
|
+
import time
|
|
1725
|
+
|
|
1726
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1727
|
+
obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1728
|
+
10, 100, 100, relative_to_robot=True)
|
|
1729
|
+
print('fixed custom object id: {0}'.format(obj.pose))
|
|
1730
|
+
"""
|
|
1731
|
+
return self._pose
|
|
1732
|
+
|
|
1733
|
+
@property
|
|
1734
|
+
def x_size_mm(self) -> float:
|
|
1735
|
+
"""The length of the object in its X axis, in millimeters.
|
|
1736
|
+
|
|
1737
|
+
.. testcode::
|
|
1738
|
+
|
|
1739
|
+
import anki_vector
|
|
1740
|
+
from anki_vector.util import degrees, Pose
|
|
1741
|
+
import time
|
|
1742
|
+
|
|
1743
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1744
|
+
obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1745
|
+
10, 100, 100, relative_to_robot=True)
|
|
1746
|
+
print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1747
|
+
"""
|
|
1748
|
+
return self._x_size_mm
|
|
1749
|
+
|
|
1750
|
+
@property
|
|
1751
|
+
def y_size_mm(self) -> float:
|
|
1752
|
+
"""The length of the object in its Y axis, in millimeters.
|
|
1753
|
+
|
|
1754
|
+
.. testcode::
|
|
1755
|
+
|
|
1756
|
+
import anki_vector
|
|
1757
|
+
from anki_vector.util import degrees, Pose
|
|
1758
|
+
import time
|
|
1759
|
+
|
|
1760
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1761
|
+
obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1762
|
+
10, 100, 100, relative_to_robot=True)
|
|
1763
|
+
print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1764
|
+
"""
|
|
1765
|
+
return self._y_size_mm
|
|
1766
|
+
|
|
1767
|
+
@property
|
|
1768
|
+
def z_size_mm(self) -> float:
|
|
1769
|
+
"""The length of the object in its Z axis, in millimeters.
|
|
1770
|
+
|
|
1771
|
+
.. testcode::
|
|
1772
|
+
|
|
1773
|
+
import anki_vector
|
|
1774
|
+
from anki_vector.util import degrees, Pose
|
|
1775
|
+
import time
|
|
1776
|
+
|
|
1777
|
+
with anki_vector.Robot(enable_custom_object_detection=True) as robot:
|
|
1778
|
+
obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)),
|
|
1779
|
+
10, 100, 100, relative_to_robot=True)
|
|
1780
|
+
print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm))
|
|
1781
|
+
"""
|
|
1782
|
+
return self._z_size_mm
|