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.
Files changed (71) hide show
  1. anki_vector/__init__.py +43 -0
  2. anki_vector/animation.py +272 -0
  3. anki_vector/annotate.py +590 -0
  4. anki_vector/audio.py +212 -0
  5. anki_vector/audio_stream.py +335 -0
  6. anki_vector/behavior.py +1135 -0
  7. anki_vector/camera.py +670 -0
  8. anki_vector/camera_viewer/__init__.py +121 -0
  9. anki_vector/color.py +88 -0
  10. anki_vector/configure/__main__.py +331 -0
  11. anki_vector/connection.py +838 -0
  12. anki_vector/events.py +420 -0
  13. anki_vector/exceptions.py +185 -0
  14. anki_vector/faces.py +819 -0
  15. anki_vector/lights.py +210 -0
  16. anki_vector/mdns.py +131 -0
  17. anki_vector/messaging/__init__.py +45 -0
  18. anki_vector/messaging/alexa_pb2.py +36 -0
  19. anki_vector/messaging/alexa_pb2_grpc.py +3 -0
  20. anki_vector/messaging/behavior_pb2.py +40 -0
  21. anki_vector/messaging/behavior_pb2_grpc.py +3 -0
  22. anki_vector/messaging/client.py +33 -0
  23. anki_vector/messaging/cube_pb2.py +113 -0
  24. anki_vector/messaging/cube_pb2_grpc.py +3 -0
  25. anki_vector/messaging/extensions_pb2.py +25 -0
  26. anki_vector/messaging/extensions_pb2_grpc.py +3 -0
  27. anki_vector/messaging/external_interface_pb2.py +169 -0
  28. anki_vector/messaging/external_interface_pb2_grpc.py +1267 -0
  29. anki_vector/messaging/messages_pb2.py +431 -0
  30. anki_vector/messaging/messages_pb2_grpc.py +3 -0
  31. anki_vector/messaging/nav_map_pb2.py +33 -0
  32. anki_vector/messaging/nav_map_pb2_grpc.py +3 -0
  33. anki_vector/messaging/protocol.py +33 -0
  34. anki_vector/messaging/response_status_pb2.py +27 -0
  35. anki_vector/messaging/response_status_pb2_grpc.py +3 -0
  36. anki_vector/messaging/settings_pb2.py +72 -0
  37. anki_vector/messaging/settings_pb2_grpc.py +3 -0
  38. anki_vector/messaging/shared_pb2.py +54 -0
  39. anki_vector/messaging/shared_pb2_grpc.py +3 -0
  40. anki_vector/motors.py +127 -0
  41. anki_vector/nav_map.py +409 -0
  42. anki_vector/objects.py +1782 -0
  43. anki_vector/opengl/__init__.py +103 -0
  44. anki_vector/opengl/assets/LICENSE.txt +21 -0
  45. anki_vector/opengl/assets/cube.jpg +0 -0
  46. anki_vector/opengl/assets/cube.mtl +9 -0
  47. anki_vector/opengl/assets/cube.obj +1000 -0
  48. anki_vector/opengl/assets/vector.mtl +67 -0
  49. anki_vector/opengl/assets/vector.obj +13220 -0
  50. anki_vector/opengl/opengl.py +864 -0
  51. anki_vector/opengl/opengl_vector.py +620 -0
  52. anki_vector/opengl/opengl_viewer.py +689 -0
  53. anki_vector/photos.py +145 -0
  54. anki_vector/proximity.py +176 -0
  55. anki_vector/reserve_control/__main__.py +36 -0
  56. anki_vector/robot.py +930 -0
  57. anki_vector/screen.py +201 -0
  58. anki_vector/status.py +322 -0
  59. anki_vector/touch.py +119 -0
  60. anki_vector/user_intent.py +186 -0
  61. anki_vector/util.py +1132 -0
  62. anki_vector/version.py +15 -0
  63. anki_vector/viewer.py +403 -0
  64. anki_vector/vision.py +202 -0
  65. anki_vector/world.py +899 -0
  66. wirepod_vector_sdk_audio-0.9.0.dist-info/METADATA +80 -0
  67. wirepod_vector_sdk_audio-0.9.0.dist-info/RECORD +71 -0
  68. wirepod_vector_sdk_audio-0.9.0.dist-info/WHEEL +5 -0
  69. wirepod_vector_sdk_audio-0.9.0.dist-info/licenses/LICENSE.txt +180 -0
  70. wirepod_vector_sdk_audio-0.9.0.dist-info/top_level.txt +1 -0
  71. 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