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/robot.py ADDED
@@ -0,0 +1,930 @@
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
+ """
16
+ This contains the :class:`Robot` and :class:`AsyncRobot` classes for managing Vector.
17
+
18
+ :class:`Robot` will run all behaviors in sequence and directly return the results.
19
+
20
+ :class:`AsyncRobot` will instead provide a :class:`concurrent.futures.Future` which the
21
+ caller may use to obtain the result when they desire.
22
+ """
23
+
24
+ # __all__ should order by constants, event classes, other classes, functions.
25
+ __all__ = ['Robot', 'AsyncRobot']
26
+
27
+ import concurrent
28
+ import functools
29
+
30
+ from . import (animation, audio, behavior, camera,
31
+ events, faces, motors, nav_map, screen,
32
+ photos, proximity, status, touch,
33
+ util, viewer, vision, world)
34
+ from .connection import (Connection,
35
+ on_connection_thread,
36
+ ControlPriorityLevel)
37
+ from .exceptions import (VectorNotReadyException,
38
+ VectorPropertyValueNotReadyException,
39
+ VectorUnreliableEventStreamException)
40
+ from .viewer import (ViewerComponent, Viewer3DComponent)
41
+ from .messaging import protocol
42
+ from .mdns import VectorMdns
43
+
44
+
45
+ class Robot:
46
+ """The Robot object is responsible for managing the state and connections
47
+ to a Vector, and is typically the entry-point to running the sdk.
48
+
49
+ The majority of the robot will not work until it is properly connected
50
+ to Vector. There are two ways to get connected:
51
+
52
+ 1. Using :code:`with`: it works just like opening a file, and will close when
53
+ the :code:`with` block's indentation ends.
54
+
55
+
56
+ .. testcode::
57
+
58
+ import anki_vector
59
+
60
+ # Create the robot connection
61
+ with anki_vector.Robot() as robot:
62
+ # Run your commands
63
+ robot.anim.play_animation_trigger("GreetAfterLongTime")
64
+
65
+ 2. Using :func:`connect` and :func:`disconnect` to explicitly open and close the connection:
66
+ it allows the robot's connection to continue in the context in which it started.
67
+
68
+ .. testcode::
69
+
70
+ import anki_vector
71
+
72
+ # Create a Robot object
73
+ robot = anki_vector.Robot()
74
+ # Connect to the Robot
75
+ robot.connect()
76
+ # Run your commands
77
+ robot.anim.play_animation_trigger("GreetAfterLongTime")
78
+ # Disconnect from Vector
79
+ robot.disconnect()
80
+
81
+ :param serial: Vector's serial number. The robot's serial number (ex. 00e20100) is located on the underside of Vector,
82
+ or accessible from Vector's debug screen. Used to identify which Vector configuration to load.
83
+ :param ip: Vector's IP address. (optional)
84
+ :param name: Vector's name (in format :code:`"Vector-XXXX"`) to be used for mDNS discovery. If a Vector with the given name
85
+ is discovered, the :code:`ip` parameter (and config field) will be overridden.
86
+ :param config: A custom :class:`dict` to override values in Vector's configuration. (optional)
87
+ Example: :code:`{"cert": "/path/to/file.cert", "name": "Vector-XXXX", "guid": "<secret_key>"}`
88
+ where :code:`cert` is the certificate to identify Vector, :code:`name` is the name on Vector's face
89
+ when his backpack is double-clicked on the charger, and :code:`guid` is the authorization token
90
+ that identifies the SDK user. Note: Never share your authentication credentials with anyone.
91
+ :param default_logging: Toggle default logging.
92
+ :param behavior_activation_timeout: The time to wait for control of the robot before failing.
93
+ :param cache_animation_lists: Get the list of animation triggers and animations available at startup.
94
+ :param enable_face_detection: Turn on face detection.
95
+ :param estimate_facial_expression: Turn estimating facial expression on/off. Enabling :code:`estimate_facial_expression`
96
+ returns a facial expression, the expression values and the :class:`anki_vector.util.ImageRect`
97
+ for observed face regions (eyes, nose, and mouth) as part of the :code:`RobotObservedFace` event.
98
+ It is turned off by default as the number of :code:`RobotObservedFace` events
99
+ are reduced due to the increased processing time.
100
+ :param enable_audio_feed: Turn audio feed on/off.
101
+ :param enable_custom_object_detection: Turn custom object detection on/off.
102
+ :param enable_nav_map_feed: Turn navigation map feed on/off.
103
+ :param show_viewer: Specifies whether to display a view of Vector's camera in a window.
104
+ :param show_3d_viewer: Specifies whether to display a 3D view of Vector's understanding of the world in a window.
105
+ :param behavior_control_level: Request control of Vector's behavior system at a specific level of control. Pass
106
+ :code:`None` if behavior control is not needed.
107
+ See :class:`ControlPriorityLevel` for more information."""
108
+
109
+ def __init__(self,
110
+ serial: str = None,
111
+ ip: str = None,
112
+ name: str = None,
113
+ config: dict = None,
114
+ default_logging: bool = True,
115
+ behavior_activation_timeout: int = 10,
116
+ cache_animation_lists: bool = False,
117
+ enable_face_detection: bool = False,
118
+ estimate_facial_expression: bool = False,
119
+ enable_audio_feed: bool = False,
120
+ enable_custom_object_detection: bool = False,
121
+ enable_nav_map_feed: bool = None,
122
+ show_viewer: bool = False,
123
+ show_3d_viewer: bool = False,
124
+ behavior_control_level: ControlPriorityLevel = ControlPriorityLevel.DEFAULT_PRIORITY):
125
+ if default_logging:
126
+ util.setup_basic_logging()
127
+ self.logger = util.get_class_logger(__name__, self)
128
+ self._force_async = False
129
+ config = config if config is not None else {}
130
+ config = {**util.read_configuration(serial, name, self.logger), **config}
131
+
132
+ if name is not None:
133
+ vector_mdns = VectorMdns.find_vector(name)
134
+
135
+ if vector_mdns is not None:
136
+ ip = vector_mdns['ipv4']
137
+
138
+ self._name = config["name"] if 'name' in config else None
139
+ self._cert_file = config["cert"] if 'cert' in config else None
140
+ self._guid = config["guid"] if 'guid' in config else None
141
+ self._port = config["port"] if 'port' in config else "443"
142
+ self._ip = ip or config.get("ip")
143
+ if self._ip is None and 'ip' in config:
144
+ self._ip = config["ip"]
145
+
146
+ if (self._name is None or self._ip is None or self._cert_file is None or self._guid is None):
147
+ raise ValueError("The Robot object requires a serial and for Vector to be logged in (using the app then running the `python3 -m anki_vector.configure`).\n"
148
+ "You may also provide the values necessary for connection through the config parameter. ex: "
149
+ '{"name":"Vector-XXXX", "ip":"XX.XX.XX.XX", "cert":"/path/to/cert_file", "guid":"<secret_key>"}')
150
+
151
+ if (self._ip is None):
152
+ raise ValueError('Could not find the sdk configuration file. Please run `python3 -m anki_vector.configure_pod` to set up your Vector for SDK usage.')
153
+
154
+ #: :class:`anki_vector.connection.Connection`: The active connection to the robot.
155
+ self._conn = Connection(self._name, ':'.join([self._ip, self._port]), self._cert_file, self._guid, behavior_control_level=behavior_control_level)
156
+ self._events = events.EventHandler(self)
157
+
158
+ # placeholders for components before they exist
159
+ self._anim: animation.AnimationComponent = None
160
+ self._audio: audio.AudioComponent = None
161
+ self._behavior: behavior.BehaviorComponent = None
162
+ self._camera: camera.CameraComponent = None
163
+ self._faces: faces.FaceComponent = None
164
+ self._motors: motors.MotorComponent = None
165
+ self._nav_map: nav_map.NavMapComponent = None
166
+ self._screen: screen.ScreenComponent = None
167
+ self._photos: photos.PhotographComponent = None
168
+ self._proximity: proximity.ProximityComponent = None
169
+ self._touch: touch.TouchComponent = None
170
+ self._viewer: viewer.ViewerComponent = None
171
+ self._viewer_3d: viewer.Viewer3DComponent = None
172
+ self._vision: vision.VisionComponent = None
173
+ self._world: world.World = None
174
+
175
+ self.behavior_activation_timeout = behavior_activation_timeout
176
+ self.enable_face_detection = enable_face_detection
177
+ self.estimate_facial_expression = estimate_facial_expression
178
+ self.enable_custom_object_detection = enable_custom_object_detection
179
+ self.cache_animation_lists = cache_animation_lists
180
+
181
+ # Robot state/sensor data
182
+ self._pose: util.Pose = None
183
+ self._pose_angle_rad: float = None
184
+ self._pose_pitch_rad: float = None
185
+ self._left_wheel_speed_mmps: float = None
186
+ self._right_wheel_speed_mmps: float = None
187
+ self._head_angle_rad: float = None
188
+ self._lift_height_mm: float = None
189
+ self._accel: util.Vector3 = None
190
+ self._gyro: util.Vector3 = None
191
+ self._carrying_object_id: float = None
192
+ self._head_tracking_object_id: float = None
193
+ self._localized_to_object_id: float = None
194
+ self._last_image_time_stamp: float = None
195
+ self._status: status.RobotStatus = status.RobotStatus()
196
+
197
+ self._enable_audio_feed = enable_audio_feed
198
+ if enable_nav_map_feed is not None:
199
+ self._enable_nav_map_feed = enable_nav_map_feed
200
+ else:
201
+ self._enable_nav_map_feed = False
202
+ self._show_viewer = show_viewer
203
+ self._show_3d_viewer = show_3d_viewer
204
+ if show_3d_viewer and enable_nav_map_feed is None:
205
+ self.logger.warning("enable_nav_map_feed should be True for 3d viewer to render correctly.")
206
+ self._enable_nav_map_feed = True
207
+
208
+ @property
209
+ def force_async(self) -> bool:
210
+ """A flag used to determine if this is a :class:`Robot` or :class:`AsyncRobot`."""
211
+ return self._force_async
212
+
213
+ @property
214
+ def conn(self) -> Connection:
215
+ """A reference to the :class:`~anki_vector.connection.Connection` instance."""
216
+ return self._conn
217
+
218
+ @property
219
+ def events(self) -> events.EventHandler:
220
+ """A reference to the :class:`~anki_vector.events.EventHandler` instance."""
221
+ return self._events
222
+
223
+ @property
224
+ def anim(self) -> animation.AnimationComponent:
225
+ """A reference to the :class:`~anki_vector.animation.AnimationComponent` instance."""
226
+ if self._anim is None:
227
+ raise VectorNotReadyException("AnimationComponent is not yet initialized")
228
+ return self._anim
229
+
230
+ @property
231
+ def audio(self) -> audio.AudioComponent:
232
+ """The audio instance used to control Vector's microphone feed and speaker playback."""
233
+
234
+ if self._audio is None:
235
+ raise VectorNotReadyException("AudioComponent is not yet initialized")
236
+ return self._audio
237
+
238
+ @property
239
+ def behavior(self) -> behavior.BehaviorComponent:
240
+ """A reference to the :class:`~anki_vector.behavior.BehaviorComponent` instance."""
241
+ return self._behavior
242
+
243
+ @property
244
+ def camera(self) -> camera.CameraComponent:
245
+ """The :class:`~anki_vector.camera.CameraComponent` instance used to control Vector's camera feed.
246
+
247
+ .. testcode::
248
+
249
+ import anki_vector
250
+
251
+ with anki_vector.Robot() as robot:
252
+ robot.camera.init_camera_feed()
253
+ image = robot.camera.latest_image
254
+ image.raw_image.show()
255
+ """
256
+ if self._camera is None:
257
+ raise VectorNotReadyException("CameraComponent is not yet initialized")
258
+ return self._camera
259
+
260
+ @property
261
+ def faces(self) -> faces.FaceComponent:
262
+ """A reference to the :class:`~anki_vector.faces.FaceComponent` instance."""
263
+ if self._faces is None:
264
+ raise VectorNotReadyException("FaceComponent is not yet initialized")
265
+ return self._faces
266
+
267
+ @property
268
+ def motors(self) -> motors.MotorComponent:
269
+ """A reference to the :class:`~anki_vector.motors.MotorComponent` instance."""
270
+ if self._motors is None:
271
+ raise VectorNotReadyException("MotorComponent is not yet initialized")
272
+ return self._motors
273
+
274
+ @property
275
+ def nav_map(self) -> nav_map.NavMapComponent:
276
+ """A reference to the :class:`~anki_vector.nav_map.NavMapComponent` instance."""
277
+ if self._nav_map is None:
278
+ raise VectorNotReadyException("NavMapComponent is not yet initialized")
279
+ return self._nav_map
280
+
281
+ @property
282
+ def screen(self) -> screen.ScreenComponent:
283
+ """A reference to the :class:`~anki_vector.screen.ScreenComponent` instance."""
284
+ if self._screen is None:
285
+ raise VectorNotReadyException("ScreenComponent is not yet initialized")
286
+ return self._screen
287
+
288
+ @property
289
+ def photos(self) -> photos.PhotographComponent:
290
+ """A reference to the :class:`~anki_vector.photos.PhotographComponent` instance."""
291
+ if self._photos is None:
292
+ raise VectorNotReadyException("PhotographyComponent is not yet initialized")
293
+ return self._photos
294
+
295
+ @property
296
+ def proximity(self) -> proximity.ProximityComponent:
297
+ """:class:`~anki_vector.proximity.ProximityComponent` containing state related to object proximity detection.
298
+
299
+ .. code-block:: python
300
+
301
+ import anki_vector
302
+ with anki_vector.Robot() as robot:
303
+ proximity_data = robot.proximity.last_sensor_reading
304
+ if proximity_data is not None:
305
+ print(proximity_data.distance)
306
+ """
307
+ return self._proximity
308
+
309
+ @property
310
+ def touch(self) -> touch.TouchComponent:
311
+ """:class:`~anki_vector.touch.TouchComponent` containing state related to object touch detection.
312
+
313
+ .. testcode::
314
+
315
+ import anki_vector
316
+ with anki_vector.Robot() as robot:
317
+ print('Robot is being touched: {0}'.format(robot.touch.last_sensor_reading.is_being_touched))
318
+ """
319
+ return self._touch
320
+
321
+ @property
322
+ def viewer(self) -> ViewerComponent:
323
+ """The :class:`~anki_vector.viewer.ViewerComponent` instance used to render Vector's camera feed.
324
+
325
+ .. testcode::
326
+
327
+ import time
328
+
329
+ import anki_vector
330
+
331
+ with anki_vector.Robot() as robot:
332
+ # Render video for 5 seconds
333
+ robot.viewer.show()
334
+ time.sleep(5)
335
+
336
+ # Disable video render and camera feed for 5 seconds
337
+ robot.viewer.close()
338
+ """
339
+ if self._viewer is None:
340
+ raise VectorNotReadyException("ViewerComponent is not yet initialized")
341
+ return self._viewer
342
+
343
+ @property
344
+ def viewer_3d(self) -> Viewer3DComponent:
345
+ """The :class:`~anki_vector.viewer.Viewer3DComponent` instance used to render Vector's navigation map.
346
+
347
+ .. testcode::
348
+
349
+ import time
350
+
351
+ import anki_vector
352
+
353
+ with anki_vector.Robot(show_3d_viewer=True, enable_nav_map_feed=True) as robot:
354
+ # Render 3D view of navigation map for 5 seconds
355
+ time.sleep(5)
356
+ """
357
+ if self._viewer_3d is None:
358
+ raise VectorNotReadyException("Viewer3DComponent is not yet initialized")
359
+ return self._viewer_3d
360
+
361
+ @property
362
+ def vision(self) -> vision.VisionComponent:
363
+ """:class:`~anki_vector.vision.VisionComponent` containing functionality related to vision based object detection.
364
+
365
+ .. testcode::
366
+
367
+ import anki_vector
368
+ with anki_vector.Robot() as robot:
369
+ robot.vision.enable_custom_object_detection()
370
+ """
371
+ return self._vision
372
+
373
+ @property
374
+ def world(self) -> world.World:
375
+ """A reference to the :class:`~anki_vector.world.World` instance, or None if the World is not yet initialized."""
376
+ if self._world is None:
377
+ raise VectorNotReadyException("WorldComponent is not yet initialized")
378
+ return self._world
379
+
380
+ @property
381
+ @util.block_while_none()
382
+ def pose(self) -> util.Pose:
383
+ """:class:`anki_vector.util.Pose`: The current pose (position and orientation) of Vector.
384
+
385
+ .. testcode::
386
+
387
+ import anki_vector
388
+ with anki_vector.Robot() as robot:
389
+ current_robot_pose = robot.pose
390
+ """
391
+ return self._pose
392
+
393
+ @property
394
+ @util.block_while_none()
395
+ def pose_angle_rad(self) -> float:
396
+ """Vector's pose angle (heading in X-Y plane).
397
+
398
+ .. testcode::
399
+
400
+ import anki_vector
401
+ with anki_vector.Robot() as robot:
402
+ current_pose_angle_rad = robot.pose_angle_rad
403
+ """
404
+ return self._pose_angle_rad
405
+
406
+ @property
407
+ @util.block_while_none()
408
+ def pose_pitch_rad(self) -> float:
409
+ """Vector's pose pitch (angle up/down).
410
+
411
+ .. testcode::
412
+
413
+ import anki_vector
414
+ with anki_vector.Robot() as robot:
415
+ current_pose_pitch_rad = robot.pose_pitch_rad
416
+ """
417
+ return self._pose_pitch_rad
418
+
419
+ @property
420
+ @util.block_while_none()
421
+ def left_wheel_speed_mmps(self) -> float:
422
+ """Vector's left wheel speed in mm/sec
423
+
424
+ .. testcode::
425
+
426
+ import anki_vector
427
+ with anki_vector.Robot() as robot:
428
+ current_left_wheel_speed_mmps = robot.left_wheel_speed_mmps
429
+ """
430
+ return self._left_wheel_speed_mmps
431
+
432
+ @property
433
+ @util.block_while_none()
434
+ def right_wheel_speed_mmps(self) -> float:
435
+ """Vector's right wheel speed in mm/sec
436
+
437
+ .. testcode::
438
+
439
+ import anki_vector
440
+ with anki_vector.Robot() as robot:
441
+ current_right_wheel_speed_mmps = robot.right_wheel_speed_mmps
442
+ """
443
+ return self._right_wheel_speed_mmps
444
+
445
+ @property
446
+ @util.block_while_none()
447
+ def head_angle_rad(self) -> float:
448
+ """Vector's head angle (up/down).
449
+
450
+ .. testcode::
451
+
452
+ import anki_vector
453
+ with anki_vector.Robot() as robot:
454
+ current_head_angle_rad = robot.head_angle_rad
455
+ """
456
+ return self._head_angle_rad
457
+
458
+ @property
459
+ @util.block_while_none()
460
+ def lift_height_mm(self) -> float:
461
+ """Height of Vector's lift from the ground.
462
+
463
+ .. testcode::
464
+
465
+ import anki_vector
466
+ with anki_vector.Robot() as robot:
467
+ current_lift_height_mm = robot.lift_height_mm
468
+ """
469
+ return self._lift_height_mm
470
+
471
+ @property
472
+ @util.block_while_none()
473
+ def accel(self) -> util.Vector3:
474
+ """:class:`anki_vector.util.Vector3`: The current accelerometer reading (x, y, z)
475
+
476
+ .. testcode::
477
+
478
+ import anki_vector
479
+ with anki_vector.Robot() as robot:
480
+ current_accel = robot.accel
481
+ """
482
+ return self._accel
483
+
484
+ @property
485
+ @util.block_while_none()
486
+ def gyro(self) -> util.Vector3:
487
+ """The current gyroscope reading (x, y, z)
488
+
489
+ .. testcode::
490
+
491
+ import anki_vector
492
+ with anki_vector.Robot() as robot:
493
+ current_gyro = robot.gyro
494
+ """
495
+ return self._gyro
496
+
497
+ @property
498
+ @util.block_while_none()
499
+ def carrying_object_id(self) -> int:
500
+ """The ID of the object currently being carried (-1 if none)
501
+
502
+ .. testcode::
503
+
504
+ import anki_vector
505
+ from anki_vector.util import degrees
506
+
507
+ # Set the robot so that he can see a cube.
508
+ with anki_vector.Robot() as robot:
509
+ robot.behavior.set_head_angle(degrees(0.0))
510
+ robot.behavior.set_lift_height(0.0)
511
+
512
+ robot.world.connect_cube()
513
+
514
+ if robot.world.connected_light_cube:
515
+ robot.behavior.pickup_object(robot.world.connected_light_cube)
516
+
517
+ print("carrying_object_id: ", robot.carrying_object_id)
518
+ """
519
+ return self._carrying_object_id
520
+
521
+ @property
522
+ @util.block_while_none()
523
+ def head_tracking_object_id(self) -> int:
524
+ """The ID of the object the head is tracking to (-1 if none)
525
+
526
+ .. testcode::
527
+
528
+ import anki_vector
529
+ with anki_vector.Robot() as robot:
530
+ current_head_tracking_object_id = robot.head_tracking_object_id
531
+ """
532
+ return self._head_tracking_object_id
533
+
534
+ @property
535
+ @util.block_while_none()
536
+ def localized_to_object_id(self) -> int:
537
+ """The ID of the object that the robot is localized to (-1 if none)
538
+
539
+ .. testcode::
540
+
541
+ import anki_vector
542
+ with anki_vector.Robot() as robot:
543
+ current_localized_to_object_id = robot.localized_to_object_id
544
+ """
545
+ return self._localized_to_object_id
546
+
547
+ # TODO Move to photos or somewhere else
548
+ @property
549
+ @util.block_while_none()
550
+ def last_image_time_stamp(self) -> int:
551
+ """The robot's timestamp for the last image seen.
552
+
553
+ .. testcode::
554
+
555
+ import anki_vector
556
+ with anki_vector.Robot() as robot:
557
+ current_last_image_time_stamp = robot.last_image_time_stamp
558
+ """
559
+ return self._last_image_time_stamp
560
+
561
+ @property
562
+ def status(self) -> status.RobotStatus:
563
+ """A property that exposes various status properties of the robot.
564
+
565
+ This status provides a simple mechanism to, for example, detect if any
566
+ of Vector's motors are moving, determine if Vector is being held, or if
567
+ he is on the charger. The full list is available in the
568
+ :class:`RobotStatus <anki_vector.status.RobotStatus>` class documentation.
569
+
570
+ .. testcode::
571
+
572
+ import anki_vector
573
+ with anki_vector.Robot() as robot:
574
+ if robot.status.is_being_held:
575
+ print("Vector is being held!")
576
+ else:
577
+ print("Vector is not being held.")
578
+ """
579
+ return self._status
580
+
581
+ @property
582
+ def enable_audio_feed(self) -> bool:
583
+ """The audio feed enabled/disabled
584
+
585
+ :getter: Returns whether the audio feed is enabled
586
+ :setter: Enable/disable the audio feed
587
+
588
+ .. code-block:: python
589
+
590
+ import asyncio
591
+ import time
592
+
593
+ import anki_vector
594
+
595
+ with anki_vector.Robot(enable_audio_feed=True) as robot:
596
+ time.sleep(5)
597
+ robot.enable_audio_feed = False
598
+ time.sleep(5)
599
+ """
600
+ # TODO When audio is ready, convert `.. code-block:: python` to `.. testcode::`
601
+ return self._enable_audio_feed
602
+
603
+ @enable_audio_feed.setter
604
+ def enable_audio_feed(self, enable) -> None:
605
+ self._enable_audio_feed = enable
606
+ # TODO add audio feed enablement when ready
607
+
608
+ # Unpack streamed data to robot's internal properties
609
+ def _unpack_robot_state(self, _robot, _event_type, msg):
610
+ self._pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z,
611
+ q0=msg.pose.q0, q1=msg.pose.q1,
612
+ q2=msg.pose.q2, q3=msg.pose.q3,
613
+ origin_id=msg.pose.origin_id)
614
+ self._pose_angle_rad = msg.pose_angle_rad
615
+ self._pose_pitch_rad = msg.pose_pitch_rad
616
+ self._left_wheel_speed_mmps = msg.left_wheel_speed_mmps
617
+ self._right_wheel_speed_mmps = msg.right_wheel_speed_mmps
618
+ self._head_angle_rad = msg.head_angle_rad
619
+ self._lift_height_mm = msg.lift_height_mm
620
+ self._accel = util.Vector3(msg.accel.x, msg.accel.y, msg.accel.z)
621
+ self._gyro = util.Vector3(msg.gyro.x, msg.gyro.y, msg.gyro.z)
622
+ self._carrying_object_id = msg.carrying_object_id
623
+ self._head_tracking_object_id = msg.head_tracking_object_id
624
+ self._localized_to_object_id = msg.localized_to_object_id
625
+ self._last_image_time_stamp = msg.last_image_time_stamp
626
+ self._status.set(msg.status)
627
+
628
+ def connect(self, timeout: int = 10) -> None:
629
+ """Start the connection to Vector.
630
+
631
+ .. testcode::
632
+
633
+ import anki_vector
634
+
635
+ robot = anki_vector.Robot()
636
+ robot.connect()
637
+ robot.anim.play_animation_trigger("GreetAfterLongTime")
638
+ robot.disconnect()
639
+
640
+ :param timeout: The time to allow for a connection before a
641
+ :class:`anki_vector.exceptions.VectorTimeoutException` is raised.
642
+ """
643
+ self.conn.connect(timeout=timeout)
644
+ self.events.start(self.conn)
645
+
646
+ # Initialize components
647
+ self._anim = animation.AnimationComponent(self)
648
+ self._audio = audio.AudioComponent(self)
649
+ self._behavior = behavior.BehaviorComponent(self)
650
+ self._faces = faces.FaceComponent(self)
651
+ self._motors = motors.MotorComponent(self)
652
+ self._nav_map = nav_map.NavMapComponent(self)
653
+ self._screen = screen.ScreenComponent(self)
654
+ self._photos = photos.PhotographComponent(self)
655
+ self._proximity = proximity.ProximityComponent(self)
656
+ self._touch = touch.TouchComponent(self)
657
+ self._viewer = viewer.ViewerComponent(self)
658
+ self._viewer_3d = viewer.Viewer3DComponent(self)
659
+ self._vision = vision.VisionComponent(self)
660
+ self._world = world.World(self)
661
+ self._camera = camera.CameraComponent(self)
662
+
663
+ if self.cache_animation_lists:
664
+ # Load animation triggers and animations so they are ready to play when requested
665
+ anim_request = self._anim.load_animation_list()
666
+ if isinstance(anim_request, concurrent.futures.Future):
667
+ anim_request.result()
668
+ anim_trigger_request = self._anim.load_animation_trigger_list()
669
+ if isinstance(anim_trigger_request, concurrent.futures.Future):
670
+ anim_trigger_request.result()
671
+
672
+ # TODO enable audio feed when ready
673
+
674
+ # Start rendering camera feed
675
+ if self._show_viewer:
676
+ self.camera.init_camera_feed()
677
+ self.viewer.show()
678
+
679
+ if self._show_3d_viewer:
680
+ self.viewer_3d.show()
681
+
682
+ if self._enable_nav_map_feed:
683
+ self.nav_map.init_nav_map_feed()
684
+
685
+ # Enable face detection, to allow Vector to add faces to its world view
686
+ if self.conn.requires_behavior_control:
687
+ face_detection = self.vision.enable_face_detection(detect_faces=self.enable_face_detection, estimate_expression=self.estimate_facial_expression)
688
+ if isinstance(face_detection, concurrent.futures.Future):
689
+ face_detection.result()
690
+ object_detection = self.vision.enable_custom_object_detection(detect_custom_objects=self.enable_custom_object_detection)
691
+ if isinstance(object_detection, concurrent.futures.Future):
692
+ object_detection.result()
693
+
694
+ # Subscribe to a callback that updates the robot's local properties
695
+ self.events.subscribe(self._unpack_robot_state,
696
+ events.Events.robot_state,
697
+ _on_connection_thread=True)
698
+
699
+ # get the camera configuration from the robot
700
+ response = self._camera.get_camera_config()
701
+ if isinstance(response, concurrent.futures.Future):
702
+ response = response.result()
703
+ self._camera.set_config(response)
704
+
705
+ # Subscribe to a callback for camera exposure settings
706
+ self.events.subscribe(self._camera.update_state,
707
+ events.Events.camera_settings_update,
708
+ _on_connection_thread=True)
709
+
710
+ # access the pose to prove it has gotten back from the event stream once
711
+ try:
712
+ if not self.pose:
713
+ pass
714
+ except VectorPropertyValueNotReadyException as e:
715
+ raise VectorUnreliableEventStreamException() from e
716
+
717
+ def disconnect(self) -> None:
718
+ """Close the connection with Vector.
719
+
720
+ .. testcode::
721
+
722
+ import anki_vector
723
+ robot = anki_vector.Robot()
724
+ robot.connect()
725
+ robot.anim.play_animation_trigger("GreetAfterLongTime")
726
+ robot.disconnect()
727
+ """
728
+ if self.conn.requires_behavior_control:
729
+ self.vision.close()
730
+
731
+ # Stop rendering video
732
+ self.viewer.close()
733
+
734
+ # Stop rendering 3d video
735
+ self.viewer_3d.close()
736
+
737
+ # Shutdown camera feed
738
+ self.camera.close_camera_feed()
739
+
740
+ # TODO shutdown audio feed when available
741
+
742
+ # Shutdown nav map feed
743
+ self.nav_map.close_nav_map_feed()
744
+
745
+ # Close the world and cleanup its objects
746
+ self.world.close()
747
+
748
+ self.proximity.close()
749
+ self.touch.close()
750
+
751
+ self.events.close()
752
+ self.conn.close()
753
+
754
+ def __enter__(self):
755
+ self.connect(self.behavior_activation_timeout)
756
+ return self
757
+
758
+ def __exit__(self, exc_type, exc_val, exc_tb):
759
+ self.disconnect()
760
+
761
+ @on_connection_thread(requires_control=False)
762
+ async def get_battery_state(self) -> protocol.BatteryStateResponse:
763
+ """Check the current state of the robot and cube batteries.
764
+
765
+ The robot is considered fully-charged above 4.1 volts. At 3.6V, the robot is approaching low charge.
766
+
767
+ Robot battery level values are as follows:
768
+
769
+ +-------+---------+---------------------------------------------------------------+
770
+ | Value | Level | Description |
771
+ +=======+=========+===============================================================+
772
+ | 1 | Low | 3.6V or less. If on charger, 4V or less. |
773
+ +-------+---------+---------------------------------------------------------------+
774
+ | 2 | Nominal | Normal operating levels. |
775
+ +-------+---------+---------------------------------------------------------------+
776
+ | 3 | Full | This state can only be achieved when Vector is on the charger |
777
+ +-------+---------+---------------------------------------------------------------+
778
+
779
+ Cube battery level values are shown below:
780
+
781
+ +-------+---------+---------------------------------------------------------------+
782
+ | Value | Level | Description |
783
+ +=======+=========+===============================================================+
784
+ | 1 | Low | 1.1V or less. |
785
+ +-------+---------+---------------------------------------------------------------+
786
+ | 2 | Normal | Normal operating levels. |
787
+ +-------+---------+---------------------------------------------------------------+
788
+
789
+ .. testcode::
790
+
791
+ import anki_vector
792
+
793
+ with anki_vector.Robot() as robot:
794
+ print("Connecting to a cube...")
795
+ robot.world.connect_cube()
796
+
797
+ battery_state = robot.get_battery_state()
798
+ if battery_state:
799
+ print("Robot battery voltage: {0}".format(battery_state.battery_volts))
800
+ print("Robot battery Level: {0}".format(battery_state.battery_level))
801
+ print("Robot battery is charging: {0}".format(battery_state.is_charging))
802
+ print("Robot is on charger platform: {0}".format(battery_state.is_on_charger_platform))
803
+ print("Robot suggested charger time: {0}".format(battery_state.suggested_charger_sec))
804
+ print("Cube battery level: {0}".format(battery_state.cube_battery.level))
805
+ print("Cube battery voltage: {0}".format(battery_state.cube_battery.battery_volts))
806
+ print("Cube battery seconds since last reading: {0}".format(battery_state.cube_battery.time_since_last_reading_sec))
807
+ print("Cube battery factory id: {0}".format(battery_state.cube_battery.factory_id))
808
+ """
809
+ get_battery_state_request = protocol.BatteryStateRequest()
810
+ return await self.conn.grpc_interface.BatteryState(get_battery_state_request)
811
+
812
+ @on_connection_thread(requires_control=False)
813
+ async def get_version_state(self) -> protocol.VersionStateResponse:
814
+ """Get the versioning information for Vector, including Vector's os_version and engine_build_id.
815
+
816
+ .. testcode::
817
+
818
+ import anki_vector
819
+ with anki_vector.Robot() as robot:
820
+ version_state = robot.get_version_state()
821
+ if version_state:
822
+ print("Robot os_version: {0}".format(version_state.os_version))
823
+ print("Robot engine_build_id: {0}".format(version_state.engine_build_id))
824
+ """
825
+ get_version_state_request = protocol.VersionStateRequest()
826
+ return await self.conn.grpc_interface.VersionState(get_version_state_request)
827
+
828
+
829
+ class AsyncRobot(Robot):
830
+ """The AsyncRobot object is just like the Robot object, but allows multiple commands
831
+ to be executed at the same time. To achieve this, all grpc function calls also
832
+ return a :class:`concurrent.futures.Future`.
833
+
834
+ 1. Using :code:`with`: it works just like opening a file, and will close when
835
+ the :code:`with` block's indentation ends.
836
+
837
+ .. testcode::
838
+
839
+ import anki_vector
840
+ from anki_vector.util import degrees
841
+
842
+ # Create the robot connection
843
+ with anki_vector.AsyncRobot() as robot:
844
+ # Start saying text asynchronously
845
+ say_future = robot.behavior.say_text("Now is the time")
846
+ # Turn robot, wait for completion
847
+ turn_future = robot.behavior.turn_in_place(degrees(3*360))
848
+ turn_future.result()
849
+ # Play greet animation trigger, wait for completion
850
+ greet_future = robot.anim.play_animation_trigger("GreetAfterLongTime")
851
+ greet_future.result()
852
+ # Make sure text has been spoken
853
+ say_future.result()
854
+
855
+ 2. Using :func:`connect` and :func:`disconnect` to explicitly open and close the connection:
856
+ it allows the robot's connection to continue in the context in which it started.
857
+
858
+ .. testcode::
859
+
860
+ import anki_vector
861
+ from anki_vector.util import degrees
862
+
863
+ # Create a Robot object
864
+ robot = anki_vector.AsyncRobot()
865
+ # Connect to Vector
866
+ robot.connect()
867
+ # Start saying text asynchronously
868
+ say_future = robot.behavior.say_text("Now is the time")
869
+ # Turn robot, wait for completion
870
+ turn_future = robot.behavior.turn_in_place(degrees(3 * 360))
871
+ turn_future.result()
872
+ # Play greet animation trigger, wait for completion
873
+ greet_future = robot.anim.play_animation_trigger("GreetAfterLongTime")
874
+ greet_future.result()
875
+ # Make sure text has been spoken
876
+ say_future.result()
877
+ # Disconnect from Vector
878
+ robot.disconnect()
879
+
880
+ When getting callbacks from the event stream, it's important to understand that function calls
881
+ return a :class:`concurrent.futures.Future` and not an :class:`asyncio.Future`. This means any
882
+ async callback functions will need to use :func:`asyncio.wrap_future` to be able to await the
883
+ function's response.
884
+
885
+ .. testcode::
886
+
887
+ import asyncio
888
+ import time
889
+
890
+ import anki_vector
891
+
892
+ async def callback(robot, event_type, event):
893
+ await asyncio.wrap_future(robot.anim.play_animation_trigger('GreetAfterLongTime'))
894
+ await asyncio.wrap_future(robot.behavior.set_head_angle(anki_vector.util.degrees(40)))
895
+
896
+ if __name__ == "__main__":
897
+ args = anki_vector.util.parse_command_args()
898
+ with anki_vector.AsyncRobot(serial=args.serial, enable_face_detection=True) as robot:
899
+ robot.behavior.set_head_angle(anki_vector.util.degrees(40))
900
+ robot.events.subscribe(callback, anki_vector.events.Events.robot_observed_face)
901
+
902
+ # Waits 10 seconds. Show Vector your face.
903
+ time.sleep(10)
904
+
905
+ :param serial: Vector's serial number. The robot's serial number (ex. 00e20100) is located on the underside of Vector,
906
+ or accessible from Vector's debug screen. Used to identify which Vector configuration to load.
907
+ :param ip: Vector's IP Address. (optional)
908
+ :param config: A custom :class:`dict` to override values in Vector's configuration. (optional)
909
+ Example: :code:`{"cert": "/path/to/file.cert", "name": "Vector-XXXX", "guid": "<secret_key>"}`
910
+ where :code:`cert` is the certificate to identify Vector, :code:`name` is the name on Vector's face
911
+ when his backpack is double-clicked on the charger, and :code:`guid` is the authorization token
912
+ that identifies the SDK user. Note: Never share your authentication credentials with anyone.
913
+ :param default_logging: Toggle default logging.
914
+ :param behavior_activation_timeout: The time to wait for control of the robot before failing.
915
+ :param cache_animation_lists: Get the list of animation triggers and animations available at startup.
916
+ :param enable_face_detection: Turn on face detection.
917
+ :param estimate_facial_expression: Turn estimating facial expression on/off.
918
+ :param enable_audio_feed: Turn audio feed on/off.
919
+ :param enable_custom_object_detection: Turn custom object detection on/off.
920
+ :param enable_nav_map_feed: Turn navigation map feed on/off.
921
+ :param show_viewer: Specifies whether to display a view of Vector's camera in a window.
922
+ :param show_3d_viewer: Specifies whether to display a 3D view of Vector's understanding of the world in a window.
923
+ :param behavior_control_level: Request control of Vector's behavior system at a specific level of control. Pass
924
+ :code:`None` if behavior control is not needed.
925
+ See :class:`ControlPriorityLevel` for more information."""
926
+
927
+ @functools.wraps(Robot.__init__)
928
+ def __init__(self, *args, **kwargs):
929
+ super(AsyncRobot, self).__init__(*args, **kwargs)
930
+ self._force_async = True