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
@@ -0,0 +1,1135 @@
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
+ .. _behavior:
17
+
18
+ Behavior related classes and functions.
19
+
20
+ Behaviors represent a complex task which requires Vector's
21
+ internal logic to determine how long it will take. This
22
+ may include combinations of animation, path planning or
23
+ other functionality. Examples include drive_on_charger,
24
+ set_lift_height, etc.
25
+
26
+ For commands such as go_to_pose, drive_on_charger and dock_with_cube,
27
+ Vector uses path planning, which refers to the problem of
28
+ navigating the robot from point A to B without collisions. Vector
29
+ loads known obstacles from his map, creates a path to navigate
30
+ around those objects, then starts following the path. If a new obstacle
31
+ is found while following the path, a new plan may be created.
32
+
33
+ The :class:`BehaviorComponent` class in this module contains
34
+ functions for all the behaviors.
35
+ """
36
+
37
+ __all__ = ["MAX_HEAD_ANGLE", "MIN_HEAD_ANGLE",
38
+ "MAX_LIFT_HEIGHT", "MAX_LIFT_HEIGHT_MM", "MIN_LIFT_HEIGHT", "MIN_LIFT_HEIGHT_MM",
39
+ "BehaviorComponent", "ReserveBehaviorControl"]
40
+
41
+
42
+ from . import connection, faces, objects, util
43
+ from .messaging import protocol
44
+ from .exceptions import VectorException
45
+ from typing import Union
46
+
47
+ # Constants
48
+
49
+ #: The minimum angle the robot's head can be set to.
50
+ MIN_HEAD_ANGLE = util.degrees(-22.0)
51
+
52
+ #: The maximum angle the robot's head can be set to
53
+ MAX_HEAD_ANGLE = util.degrees(45.0)
54
+
55
+ # The lowest height-above-ground that lift can be moved to in millimeters.
56
+ MIN_LIFT_HEIGHT_MM = 32.0
57
+
58
+ #: The lowest height-above-ground that lift can be moved to
59
+ MIN_LIFT_HEIGHT = util.distance_mm(MIN_LIFT_HEIGHT_MM)
60
+
61
+ # The largest height-above-ground that lift can be moved to in millimeters.
62
+ MAX_LIFT_HEIGHT_MM = 92.0
63
+
64
+ #: The largest height-above-ground that lift can be moved to
65
+ MAX_LIFT_HEIGHT = util.distance_mm(MAX_LIFT_HEIGHT_MM)
66
+
67
+
68
+ class BehaviorComponent(util.Component):
69
+ """Run behaviors on Vector"""
70
+
71
+ _next_action_id = protocol.FIRST_SDK_TAG
72
+
73
+ @classmethod
74
+ def _get_next_action_id(cls):
75
+ # Post increment _current_action_id (and loop within the SDK_TAG range)
76
+ next_action_id = cls._next_action_id
77
+ if cls._next_action_id == protocol.LAST_SDK_TAG:
78
+ cls._next_action_id = protocol.FIRST_SDK_TAG
79
+ else:
80
+ cls._next_action_id += 1
81
+ return next_action_id
82
+
83
+ @connection.on_connection_thread()
84
+ async def _abort_action(self, action_id):
85
+ cancel_action_request = protocol.CancelActionByIdTagRequest(id_tag=action_id)
86
+ return await self.grpc_interface.CancelActionByIdTag(cancel_action_request)
87
+
88
+ @connection.on_connection_thread()
89
+ async def _abort_behavior(self):
90
+ cancel_behavior_request = protocol.CancelBehaviorRequest()
91
+ return await self.grpc_interface.CancelBehavior(cancel_behavior_request)
92
+
93
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_BEHAVIOR)
94
+ async def drive_off_charger(self) -> protocol.DriveOffChargerResponse:
95
+ """Drive Vector off the charger
96
+
97
+ If Vector is on the charger, drives him off the charger.
98
+
99
+ .. testcode::
100
+
101
+ import anki_vector
102
+
103
+ with anki_vector.Robot() as robot:
104
+ robot.behavior.drive_off_charger()
105
+
106
+ Example of cancelling the :meth:`drive_off_charger` behavior:
107
+
108
+ .. testcode::
109
+
110
+ import anki_vector
111
+ import time
112
+
113
+ with anki_vector.AsyncRobot() as robot:
114
+ drive_off_future = robot.behavior.drive_off_charger()
115
+ time.sleep(3.0)
116
+ drive_off_future.cancel()
117
+ """
118
+ drive_off_charger_request = protocol.DriveOffChargerRequest()
119
+ return await self.grpc_interface.DriveOffCharger(drive_off_charger_request)
120
+
121
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_BEHAVIOR)
122
+ async def drive_on_charger(self) -> protocol.DriveOnChargerResponse:
123
+ """Drive Vector onto the charger
124
+
125
+ Vector will attempt to find the charger and, if successful, he will
126
+ back onto it and start charging.
127
+
128
+ Vector's charger has a visual marker so that the robot can locate it
129
+ for self-docking.
130
+
131
+ .. testcode::
132
+
133
+ import anki_vector
134
+
135
+ with anki_vector.Robot() as robot:
136
+ robot.behavior.drive_on_charger()
137
+
138
+ Example of cancelling the :meth:`drive_on_charger` behavior:
139
+
140
+ .. testcode::
141
+
142
+ import anki_vector
143
+ import time
144
+
145
+ with anki_vector.AsyncRobot() as robot:
146
+ drive_on_future = robot.behavior.drive_on_charger()
147
+ time.sleep(3.0)
148
+ drive_on_future.cancel()
149
+ """
150
+ drive_on_charger_request = protocol.DriveOnChargerRequest()
151
+ return await self.grpc_interface.DriveOnCharger(drive_on_charger_request)
152
+
153
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_BEHAVIOR)
154
+ async def find_faces(self) -> protocol.FindFacesResponse:
155
+ """Look around for faces
156
+
157
+ Turn in place and move head to look for faces
158
+
159
+ .. testcode::
160
+
161
+ import anki_vector
162
+
163
+ with anki_vector.Robot() as robot:
164
+ robot.behavior.find_faces()
165
+
166
+ Example of cancelling the :meth:`find_faces` behavior:
167
+
168
+ .. testcode::
169
+
170
+ import anki_vector
171
+ import time
172
+
173
+ with anki_vector.AsyncRobot() as robot:
174
+ find_faces_future = robot.behavior.find_faces()
175
+ time.sleep(3.0)
176
+ find_faces_future.cancel()
177
+ """
178
+ find_faces_request = protocol.FindFacesRequest()
179
+ return await self.grpc_interface.FindFaces(find_faces_request)
180
+
181
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_BEHAVIOR)
182
+ async def look_around_in_place(self) -> protocol.LookAroundInPlaceResponse:
183
+ """Look around in place
184
+
185
+ Turn in place and move head to see what's around Vector
186
+
187
+ .. testcode::
188
+
189
+ import anki_vector
190
+
191
+ with anki_vector.Robot() as robot:
192
+ robot.behavior.look_around_in_place()
193
+
194
+ Example of cancelling the :meth:`look_around_in_place` behavior:
195
+
196
+ .. testcode::
197
+
198
+ import anki_vector
199
+ import time
200
+
201
+ with anki_vector.AsyncRobot() as robot:
202
+ look_around_in_place_future = robot.behavior.look_around_in_place()
203
+ time.sleep(3.0)
204
+ look_around_in_place_future.cancel()
205
+ """
206
+ look_around_in_place_request = protocol.LookAroundInPlaceRequest()
207
+ return await self.grpc_interface.LookAroundInPlace(look_around_in_place_request)
208
+
209
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_BEHAVIOR)
210
+ async def roll_visible_cube(self) -> protocol.RollBlockResponse:
211
+ """Roll a cube that is currently known to the robot
212
+
213
+ This behavior will move into position as necessary based on relative
214
+ distance and orientation.
215
+
216
+ Vector needs to see the block for this to succeed.
217
+
218
+ .. testcode::
219
+
220
+ import anki_vector
221
+
222
+ with anki_vector.Robot() as robot:
223
+ robot.behavior.roll_visible_cube()
224
+
225
+ Example of cancelling the :meth:`roll_visible_cube` behavior:
226
+
227
+ .. testcode::
228
+
229
+ import anki_vector
230
+ import time
231
+
232
+ with anki_vector.AsyncRobot() as robot:
233
+ roll_visible_cube_future = robot.behavior.roll_visible_cube()
234
+ time.sleep(3.0)
235
+ roll_visible_cube_future.cancel()
236
+ """
237
+ roll_block_request = protocol.RollBlockRequest()
238
+ return await self.grpc_interface.RollBlock(roll_block_request)
239
+
240
+ # TODO Make this cancellable with is_cancellable
241
+ @connection.on_connection_thread()
242
+ async def say_text(self, text: str, use_vector_voice: bool = True, duration_scalar: float = 1.0) -> protocol.SayTextResponse:
243
+ """Make Vector speak text.
244
+
245
+ .. testcode::
246
+
247
+ import anki_vector
248
+ with anki_vector.Robot() as robot:
249
+ robot.behavior.say_text("Hello World")
250
+
251
+ :param text: The words for Vector to say.
252
+ :param use_vector_voice: Whether to use Vector's robot voice
253
+ (otherwise, he uses a generic human male voice).
254
+ :param duration_scalar: Adjust the relative duration of the
255
+ generated text to speech audio.
256
+
257
+ :return: object that provides the status and utterance state
258
+ """
259
+ say_text_request = protocol.SayTextRequest(text=text,
260
+ use_vector_voice=use_vector_voice,
261
+ duration_scalar=duration_scalar)
262
+ return await self.conn.grpc_interface.SayText(say_text_request)
263
+ # begin copied from github.com/ikkez
264
+ def say_localized_text(self, text: str, use_vector_voice: bool = True, duration_scalar: float = 1.0,
265
+ language: str = 'en') -> protocol.SayTextResponse:
266
+ """Make Vector speak text with a different localized voice.
267
+
268
+ .. testcode::
269
+
270
+ import anki_vector
271
+ with anki_vector.Robot() as robot:
272
+ robot.behavior.say_localized_text("Hello World",language="de")
273
+
274
+ :param text: The words for Vector to say.
275
+ :param use_vector_voice: Whether to use Vector's robot voice
276
+ (otherwise, he uses a generic human male voice).
277
+ :param duration_scalar: Adjust the relative duration of the
278
+ generated text to speech audio.
279
+ :param language: Adjust the language spoken for this text
280
+
281
+ possible values:
282
+ - de: German
283
+ - en: English
284
+ - ja or jp: Japanese
285
+ - fr: French
286
+
287
+ :return: object that provides the status and utterance state
288
+ """
289
+
290
+ if language == 'en':
291
+ locale = 'en_US'
292
+ if language == 'de':
293
+ locale = 'de_DE'
294
+ elif language == 'fr':
295
+ locale = 'fr_FR'
296
+ elif language == 'ja' or language == 'jp':
297
+ locale = 'ja_JP'
298
+ duration_scalar = duration_scalar / 3
299
+ else:
300
+ locale = language
301
+
302
+ if self.robot.force_async:
303
+ # @TODO: make sure requests are sent in conjunction when say_future would be returned -
304
+ # currently it's blocking async, to ensure the language is switched back after talking
305
+ # because otherwise the persisted different locale would lead to failed cloud requests,
306
+ # as on english seems to be supported right now and vector would show network-error on his screen
307
+ self.change_locale(locale=locale).result()
308
+ say_future = self.say_text(text, use_vector_voice, duration_scalar)
309
+ res = say_future.result()
310
+ self.change_locale(locale='en_US').result()
311
+ return res
312
+ else:
313
+ self.change_locale(locale=locale)
314
+ say_future = self.say_text(text, use_vector_voice, duration_scalar)
315
+ self.change_locale(locale='en_US')
316
+ return say_future
317
+
318
+ # TODO Make this cancellable with is_cancellable_behavior
319
+ @connection.on_connection_thread(requires_control=False)
320
+ async def app_intent(self, intent: str, param: Union[str, int] = None) -> protocol.AppIntentResponse:
321
+ """Send Vector an intention to do something.
322
+
323
+ .. testcode::
324
+
325
+ import anki_vector
326
+ with anki_vector.Robot(behavior_control_level=None) as robot:
327
+ robot.behavior.app_intent(intent='intent_system_sleep')
328
+
329
+ :param intent: The intention key
330
+ :param param: Intention parameter, usually a json encoded string or an int of secounds for the clock timer
331
+
332
+ :return: object that provides the status
333
+ """
334
+
335
+ # clock timer uses the length of `param` as the number of seconds to set the timer for
336
+ if intent=='intent_clock_settimer' and type(param) == int:
337
+ param = 'x' * param
338
+
339
+ app_intent_request = protocol.AppIntentRequest(intent=intent, param=param)
340
+ return await self.conn.grpc_interface.AppIntent(app_intent_request)
341
+
342
+ # TODO Make this cancellable with is_cancellable_behavior
343
+ @connection.on_connection_thread()
344
+ async def change_locale(self, locale: str) -> protocol.UpdateSettingsResponse:
345
+ """Change Vectors voice locale
346
+
347
+ .. testcode::
348
+
349
+ import anki_vector
350
+ with anki_vector.Robot() as robot:
351
+ robot.behavior.change_locale(locale='de_DE')
352
+
353
+ :param locale: The locale ISO code
354
+
355
+ :return: object that provides the status
356
+ """
357
+
358
+ settings = {'locale': locale}
359
+ updatet_settings_request = protocol.UpdateSettingsRequest(settings=settings)
360
+ return await self.conn.grpc_interface.UpdateSettings(updatet_settings_request)
361
+
362
+ # TODO Make this cancellable with is_cancellable_behavior
363
+ @connection.on_connection_thread()
364
+ async def update_settings(self, settings) -> protocol.UpdateSettingsResponse:
365
+ """Send Vector an intention to do something.
366
+
367
+ .. testcode::
368
+
369
+ import anki_vector
370
+ with anki_vector.Robot() as robot:
371
+ robot.behavior.update_settings(settings={'locale':'en_US'})
372
+
373
+ :param settings: A list object with the following keys
374
+ clock_24_hour: bool
375
+ eye_color: EyeColor
376
+ default_location: string,
377
+ dist_is_metric: bool
378
+ locale: string
379
+ master_volume: Volume
380
+ temp_is_fahrenheit: bool
381
+ time_zone: string
382
+ button_wakeword: ButtonWakeWord
383
+
384
+ :return: object that provides the status
385
+ """
386
+ updatet_settings_request = protocol.UpdateSettingsRequest(settings=settings)
387
+ return await self.conn.grpc_interface.UpdateSettings(updatet_settings_request)
388
+ # end copied from github.com/ikkez
389
+ # TODO Make this cancellable with is_cancellable?
390
+ @connection.on_connection_thread()
391
+ async def set_eye_color(self, hue: float, saturation: float) -> protocol.SetEyeColorResponse:
392
+ """Set Vector's eye color.
393
+
394
+ Eye color settings examples:
395
+ | Teal: Set hue to 0.42 and saturation to 1.00.
396
+ | Orange: Set hue to 0.05 and saturation to 0.95.
397
+ | Yellow: Set hue to 0.11 and saturation to 1.00.
398
+ | Lime: Set hue to 0.21 and saturation to 1.00.
399
+ | Sapphire: Set hue to 0.57 and saturation to 1.00.
400
+ | Purple: Set hue to 0.83 and saturation to 0.76.
401
+
402
+ .. testcode::
403
+
404
+ import anki_vector
405
+ import time
406
+
407
+ with anki_vector.Robot() as robot:
408
+ print("Set Vector's eye color to purple...")
409
+ robot.behavior.set_eye_color(0.83, 0.76)
410
+ time.sleep(5)
411
+
412
+ :param hue: The hue to use for Vector's eyes.
413
+ :param saturation: The saturation to use for Vector's eyes.
414
+ """
415
+ eye_color_request = protocol.SetEyeColorRequest(hue=hue, saturation=saturation)
416
+ return await self.conn.grpc_interface.SetEyeColor(eye_color_request)
417
+
418
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
419
+ async def go_to_pose(self,
420
+ pose: util.Pose,
421
+ relative_to_robot: bool = False,
422
+ num_retries: int = 0,
423
+ _action_id: int = None) -> protocol.GoToPoseResponse:
424
+ """Tells Vector to drive to the specified pose and orientation.
425
+
426
+ In navigating to the requested pose, Vector will use path planning.
427
+
428
+ If relative_to_robot is set to True, the given pose will assume the
429
+ robot's pose as its origin.
430
+
431
+ Since the robot understands position by monitoring its tread movement,
432
+ it does not understand movement in the z axis. This means that the only
433
+ applicable elements of pose in this situation are position.x position.y
434
+ and rotation.angle_z.
435
+
436
+ Note that actions that use the wheels cannot be performed at the same time,
437
+ otherwise you may see a TRACKS_LOCKED error. Methods that use the wheels include
438
+ :meth:`go_to_pose`, :meth:`dock_with_cube`, :meth:`turn_in_place`, :meth:`drive_straight`, and :meth:`pickup_object`.
439
+
440
+ :param pose: The destination pose.
441
+ :param relative_to_robot: Whether the given pose is relative to
442
+ the robot's pose.
443
+ :param num_retries: Number of times to reattempt action in case of a failure.
444
+
445
+ Returns:
446
+ A response from the robot with status information sent when this request successfully completes or fails.
447
+
448
+ .. testcode::
449
+
450
+ import anki_vector
451
+ from anki_vector.util import degrees, Angle, Pose
452
+
453
+ with anki_vector.Robot() as robot:
454
+ pose = Pose(x=50, y=0, z=0, angle_z=Angle(degrees=0))
455
+ robot.behavior.go_to_pose(pose)
456
+
457
+ Example of cancelling the :meth:`go_to_pose` behavior:
458
+
459
+ .. testcode::
460
+
461
+ import anki_vector
462
+ from anki_vector.util import degrees, Angle, Pose
463
+ import time
464
+
465
+ with anki_vector.AsyncRobot() as robot:
466
+ pose = Pose(x=50, y=0, z=0, angle_z=Angle(degrees=0))
467
+ pose_future = robot.behavior.go_to_pose(pose)
468
+ time.sleep(3.0)
469
+ pose_future.cancel()
470
+ """
471
+ if relative_to_robot and self.robot.pose:
472
+ pose = self.robot.pose.define_pose_relative_this(pose)
473
+
474
+ go_to_pose_request = protocol.GoToPoseRequest(x_mm=pose.position.x,
475
+ y_mm=pose.position.y,
476
+ rad=pose.rotation.angle_z.radians,
477
+ id_tag=_action_id,
478
+ num_retries=num_retries)
479
+
480
+ return await self.grpc_interface.GoToPose(go_to_pose_request)
481
+
482
+ # TODO alignment_type coming out ugly in the docs without real values
483
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
484
+ async def dock_with_cube(self,
485
+ target_object: objects.LightCube,
486
+ approach_angle: util.Angle = None,
487
+ alignment_type: protocol.AlignmentType = protocol.ALIGNMENT_TYPE_LIFT_PLATE,
488
+ distance_from_marker: util.Distance = None,
489
+ num_retries: int = 0,
490
+ _action_id: int = None) -> protocol.DockWithCubeResponse:
491
+ """Tells Vector to dock with a light cube, optionally using a given approach angle and distance.
492
+
493
+ While docking with the cube, Vector will use path planning.
494
+
495
+ Note that actions that use the wheels cannot be performed at the same time,
496
+ otherwise you may see a TRACKS_LOCKED error. Methods that use the wheels include
497
+ :meth:`go_to_pose`, :meth:`dock_with_cube`, :meth:`turn_in_place`, :meth:`drive_straight`, and :meth:`pickup_object`.
498
+
499
+ :param target_object: The LightCube object to dock with.
500
+ :param approach_angle: Angle to approach the dock with.
501
+ :param alignment_type: Which part of the robot to align with the object.
502
+ :param distance_from_marker: How far from the object to approach (0 to dock)
503
+ :param num_retries: Number of times to reattempt action in case of a failure.
504
+
505
+ Returns:
506
+ A response from the robot with status information sent when this request successfully completes or fails.
507
+
508
+ .. testcode::
509
+
510
+ import anki_vector
511
+
512
+ with anki_vector.Robot() as robot:
513
+ robot.world.connect_cube()
514
+
515
+ if robot.world.connected_light_cube:
516
+ robot.behavior.dock_with_cube(robot.world.connected_light_cube)
517
+
518
+ Example of cancelling the :meth:`dock_with_cube` behavior:
519
+
520
+ .. testcode::
521
+
522
+ import anki_vector
523
+ from anki_vector.util import degrees
524
+ import time
525
+
526
+ with anki_vector.AsyncRobot() as robot:
527
+ # If necessary, move Vector's Head and Lift down
528
+ robot.behavior.set_head_angle(degrees(-5.0))
529
+ robot.behavior.set_lift_height(0.0)
530
+
531
+ robot.world.connect_cube()
532
+
533
+ time.sleep(10.0)
534
+
535
+ dock_future = robot.behavior.dock_with_cube(
536
+ robot.world.connected_light_cube,
537
+ num_retries=3)
538
+ time.sleep(3.0)
539
+ dock_future.cancel()
540
+
541
+ robot.world.disconnect_cube()
542
+ """
543
+ if target_object is None:
544
+ raise VectorException("Must supply a target_object to dock_with_cube")
545
+
546
+ dock_request = protocol.DockWithCubeRequest(object_id=target_object.object_id,
547
+ alignment_type=alignment_type,
548
+ id_tag=_action_id,
549
+ num_retries=num_retries)
550
+ if approach_angle is not None:
551
+ dock_request.use_approach_angle = True
552
+ dock_request.use_pre_dock_pose = True
553
+ dock_request.approach_angle_rad = approach_angle.radians
554
+ if distance_from_marker is not None:
555
+ dock_request.distance_from_marker_mm = distance_from_marker.distance_mm
556
+
557
+ return await self.grpc_interface.DockWithCube(dock_request)
558
+
559
+ # Movement actions
560
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
561
+ async def drive_straight(self,
562
+ distance: util.Distance,
563
+ speed: util.Speed,
564
+ should_play_anim: bool = True,
565
+ num_retries: int = 0,
566
+ _action_id: int = None) -> protocol.DriveStraightResponse:
567
+ """Tells Vector to drive in a straight line.
568
+
569
+ Vector will drive for the specified distance (forwards or backwards)
570
+
571
+ Vector must be off of the charger for this movement action.
572
+
573
+ Note that actions that use the wheels cannot be performed at the same time,
574
+ otherwise you may see a TRACKS_LOCKED error. Methods that use the wheels include
575
+ :meth:`go_to_pose`, :meth:`dock_with_cube`, :meth:`turn_in_place`, :meth:`drive_straight`, and :meth:`pickup_object`.
576
+
577
+ :param distance: The distance to drive
578
+ (>0 for forwards, <0 for backwards)
579
+ :param speed: The speed to drive at
580
+ (should always be >0, the abs(speed) is used internally)
581
+ :param should_play_anim: Whether to play idle animations whilst driving
582
+ (tilt head, hum, animated eyes, etc.)
583
+ :param num_retries: Number of times to reattempt action in case of a failure.
584
+
585
+ Returns:
586
+ A response from the robot with status information sent when this request successfully completes or fails.
587
+
588
+ .. testcode::
589
+
590
+ import anki_vector
591
+ from anki_vector.util import distance_mm, speed_mmps
592
+
593
+ with anki_vector.Robot() as robot:
594
+ robot.behavior.drive_straight(distance_mm(200), speed_mmps(100))
595
+
596
+ Example of cancelling the :meth:`drive_straight` behavior:
597
+
598
+ .. testcode::
599
+
600
+ import anki_vector
601
+ from anki_vector.util import distance_mm, speed_mmps
602
+ import time
603
+
604
+ with anki_vector.AsyncRobot() as robot:
605
+ drive_future = robot.behavior.drive_straight(distance_mm(300), speed_mmps(50))
606
+ time.sleep(2.0)
607
+ drive_future.cancel()
608
+ """
609
+ drive_straight_request = protocol.DriveStraightRequest(speed_mmps=speed.speed_mmps,
610
+ dist_mm=distance.distance_mm,
611
+ should_play_animation=should_play_anim,
612
+ id_tag=_action_id,
613
+ num_retries=num_retries)
614
+
615
+ return await self.grpc_interface.DriveStraight(drive_straight_request)
616
+
617
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
618
+ async def turn_in_place(self,
619
+ angle: util.Angle,
620
+ speed: util.Angle = util.Angle(0.0),
621
+ accel: util.Angle = util.Angle(0.0),
622
+ angle_tolerance: util.Angle = util.Angle(0.0),
623
+ is_absolute: bool = 0,
624
+ num_retries: int = 0,
625
+ _action_id: int = None) -> protocol.TurnInPlaceResponse:
626
+ """Turn the robot around its current position.
627
+
628
+ Vector must be off of the charger for this movement action.
629
+
630
+ Note that actions that use the wheels cannot be performed at the same time,
631
+ otherwise you may see a TRACKS_LOCKED error. Methods that use the wheels include
632
+ :meth:`go_to_pose`, :meth:`dock_with_cube`, :meth:`turn_in_place`, :meth:`drive_straight`, and :meth:`pickup_object`.
633
+
634
+ :param angle: The angle to turn. Positive
635
+ values turn to the left, negative values to the right.
636
+ :param speed: Angular turn speed (per second).
637
+ :param accel: Acceleration of angular turn
638
+ (per second squared).
639
+ :param angle_tolerance: angular tolerance
640
+ to consider the action complete (this is clamped to a minimum
641
+ of 2 degrees internally).
642
+ :param is_absolute: True to turn to a specific angle, False to
643
+ turn relative to the current pose.
644
+ :param num_retries: Number of times to reattempt the turn in case of a failure.
645
+
646
+ Returns:
647
+ A response from the robot with status information sent when this request successfully completes or fails.
648
+
649
+ .. testcode::
650
+
651
+ import anki_vector
652
+ from anki_vector.util import degrees
653
+
654
+ with anki_vector.Robot() as robot:
655
+ robot.behavior.turn_in_place(degrees(90))
656
+
657
+ Example of cancelling the :meth:`turn_in_place` behavior:
658
+
659
+ .. testcode::
660
+
661
+ import anki_vector
662
+ from anki_vector.util import degrees
663
+ import time
664
+
665
+ with anki_vector.AsyncRobot() as robot:
666
+ turn_future = robot.behavior.turn_in_place(degrees(360))
667
+ time.sleep(0.5)
668
+ turn_future.cancel()
669
+ """
670
+ turn_in_place_request = protocol.TurnInPlaceRequest(angle_rad=angle.radians,
671
+ speed_rad_per_sec=speed.radians,
672
+ accel_rad_per_sec2=accel.radians,
673
+ tol_rad=angle_tolerance.radians,
674
+ is_absolute=is_absolute,
675
+ id_tag=_action_id,
676
+ num_retries=num_retries)
677
+
678
+ return await self.grpc_interface.TurnInPlace(turn_in_place_request)
679
+
680
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
681
+ async def set_head_angle(self,
682
+ angle: util.Angle,
683
+ accel: float = 10.0,
684
+ max_speed: float = 10.0,
685
+ duration: float = 0.0,
686
+ num_retries: int = 0,
687
+ _action_id: int = None) -> protocol.SetHeadAngleResponse:
688
+ """Tell Vector's head to move to a given angle.
689
+
690
+ :param angle: Desired angle for Vector's head.
691
+ (:const:`MIN_HEAD_ANGLE` to :const:`MAX_HEAD_ANGLE`).
692
+ (we clamp it to this range internally).
693
+ :param accel: Acceleration of Vector's head in radians per second squared.
694
+ :param max_speed: Maximum speed of Vector's head in radians per second.
695
+ :param duration: Time for Vector's head to move in seconds. A value
696
+ of zero will make Vector try to do it as quickly as possible.
697
+ :param num_retries: Number of times to reattempt the action in case of a failure.
698
+
699
+ Returns:
700
+ A response from the robot with status information sent when this request successfully completes or fails.
701
+
702
+ .. testcode::
703
+
704
+ import anki_vector
705
+ from anki_vector.util import degrees
706
+ from anki_vector.behavior import MIN_HEAD_ANGLE, MAX_HEAD_ANGLE
707
+
708
+ with anki_vector.Robot() as robot:
709
+ # move head from minimum to maximum angle
710
+ robot.behavior.set_head_angle(MIN_HEAD_ANGLE)
711
+ robot.behavior.set_head_angle(MAX_HEAD_ANGLE)
712
+ # move head to middle
713
+ robot.behavior.set_head_angle(degrees(35.0))
714
+
715
+ Example of cancelling the :meth:`set_head_angle` behavior:
716
+
717
+ .. testcode::
718
+
719
+ import anki_vector
720
+ from anki_vector.behavior import MIN_HEAD_ANGLE, MAX_HEAD_ANGLE
721
+ import time
722
+
723
+ with anki_vector.AsyncRobot() as robot:
724
+ # move head from minimum to maximum angle
725
+ robot.behavior.set_head_angle(MIN_HEAD_ANGLE)
726
+ time.sleep(1.0)
727
+ robot.behavior.set_head_angle(MAX_HEAD_ANGLE)
728
+ time.sleep(1.0)
729
+ # move head to middle
730
+ head_future = robot.behavior.set_head_angle(MIN_HEAD_ANGLE)
731
+ head_future.cancel()
732
+ """
733
+ if angle < MIN_HEAD_ANGLE:
734
+ self.logger.warning("head angle %s too small, should be in %f..%f range - clamping",
735
+ angle.degrees, MIN_HEAD_ANGLE.degrees, MAX_HEAD_ANGLE.degrees)
736
+ angle = MIN_HEAD_ANGLE
737
+ elif angle > MAX_HEAD_ANGLE:
738
+ self.logger.warning("head angle %s too large, should be in %f..%f range - clamping",
739
+ angle.degrees, MIN_HEAD_ANGLE.degrees, MAX_HEAD_ANGLE.degrees)
740
+ angle = MAX_HEAD_ANGLE
741
+
742
+ set_head_angle_request = protocol.SetHeadAngleRequest(angle_rad=angle.radians,
743
+ max_speed_rad_per_sec=max_speed,
744
+ accel_rad_per_sec2=accel,
745
+ duration_sec=duration,
746
+ id_tag=_action_id,
747
+ num_retries=num_retries)
748
+ return await self.grpc_interface.SetHeadAngle(set_head_angle_request)
749
+
750
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
751
+ async def set_lift_height(self,
752
+ height: float,
753
+ accel: float = 10.0,
754
+ max_speed: float = 10.0,
755
+ duration: float = 0.0,
756
+ num_retries: int = 0,
757
+ _action_id: int = None) -> protocol.SetLiftHeightResponse:
758
+ """Tell Vector's lift to move to a given height.
759
+
760
+ :param height: desired height for Vector's lift 0.0 (bottom) to
761
+ 1.0 (top) (we clamp it to this range internally).
762
+ :param accel: Acceleration of Vector's lift in radians per
763
+ second squared.
764
+ :param max_speed: Maximum speed of Vector's lift in radians per second.
765
+ :param duration: Time for Vector's lift to move in seconds. A value
766
+ of zero will make Vector try to do it as quickly as possible.
767
+ :param num_retries: Number of times to reattempt the action in case of a failure.
768
+
769
+ Returns:
770
+ A response from the robot with status information sent when this request successfully completes or fails.
771
+
772
+ .. testcode::
773
+
774
+ import anki_vector
775
+
776
+ with anki_vector.Robot() as robot:
777
+ robot.behavior.set_lift_height(1.0)
778
+ robot.behavior.set_lift_height(0.0)
779
+
780
+ Example of cancelling the :meth:`set_lift_height` behavior:
781
+
782
+ .. testcode::
783
+
784
+ import anki_vector
785
+ from anki_vector.behavior import MIN_LIFT_HEIGHT_MM, MAX_LIFT_HEIGHT_MM
786
+ import time
787
+
788
+ with anki_vector.AsyncRobot() as robot:
789
+ robot.behavior.set_lift_height(1.0)
790
+ time.sleep(1.0)
791
+ lift_future = robot.behavior.set_lift_height(0.0)
792
+ time.sleep(1.0)
793
+ lift_future = robot.behavior.set_lift_height(1.0)
794
+ lift_future.cancel()
795
+ """
796
+ if height < 0.0:
797
+ self.logger.warning("lift height %s too small, should be in 0..1 range - clamping", height)
798
+ height = MIN_LIFT_HEIGHT_MM
799
+ elif height > 1.0:
800
+ self.logger.warning("lift height %s too large, should be in 0..1 range - clamping", height)
801
+ height = MAX_LIFT_HEIGHT_MM
802
+ else:
803
+ height = MIN_LIFT_HEIGHT_MM + (height * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM))
804
+
805
+ set_lift_height_request = protocol.SetLiftHeightRequest(height_mm=height,
806
+ max_speed_rad_per_sec=max_speed,
807
+ accel_rad_per_sec2=accel,
808
+ duration_sec=duration,
809
+ id_tag=_action_id,
810
+ num_retries=num_retries)
811
+
812
+ return await self.grpc_interface.SetLiftHeight(set_lift_height_request)
813
+
814
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
815
+ async def turn_towards_face(self,
816
+ face: faces.Face,
817
+ num_retries: int = 0,
818
+ _action_id: int = None) -> protocol.TurnTowardsFaceResponse:
819
+ """Tells Vector to turn towards this face.
820
+
821
+ :param face_id: The face Vector will turn towards.
822
+ :param num_retries: Number of times to reattempt the action in case of a failure.
823
+
824
+ Returns:
825
+ A response from the robot with status information sent when this request successfully completes or fails.
826
+
827
+ .. testcode::
828
+
829
+ import anki_vector
830
+
831
+ with anki_vector.Robot() as robot:
832
+ robot.behavior.turn_towards_face(1)
833
+
834
+ Example of cancelling the :meth:`turn_towards_face` behavior:
835
+
836
+ .. testcode::
837
+
838
+ import anki_vector
839
+
840
+ with anki_vector.Robot() as robot:
841
+ turn_towards_face_future = robot.behavior.turn_towards_face(1)
842
+ turn_towards_face_future.cancel()
843
+ """
844
+ turn_towards_face_request = protocol.TurnTowardsFaceRequest(face_id=face.face_id,
845
+ max_turn_angle_rad=util.degrees(180).radians,
846
+ id_tag=_action_id,
847
+ num_retries=num_retries)
848
+
849
+ return await self.grpc_interface.TurnTowardsFace(turn_towards_face_request)
850
+
851
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
852
+ async def go_to_object(self,
853
+ target_object: objects.LightCube,
854
+ distance_from_object,
855
+ num_retries: int = 0,
856
+ _action_id: int = None) -> protocol.GoToObjectResponse:
857
+ """Tells Vector to drive to his Cube.
858
+
859
+ :param target_object: The destination object. CustomObject instances are not supported.
860
+ :param distance_from_object: The distance from the object to stop. This is the distance
861
+ between the origins. For instance, the distance from the robot's origin
862
+ (between Vector's two front wheels) to the cube's origin (at the center of the cube) is ~40mm.
863
+ :param num_retries: Number of times to reattempt action in case of a failure.
864
+
865
+ Returns:
866
+ A response from the robot with status information sent when this request successfully completes or fails.
867
+
868
+ .. testcode::
869
+ import anki_vector
870
+ from anki_vector.util import distance_mm
871
+
872
+ with anki_vector.Robot() as robot:
873
+ robot.world.connect_cube()
874
+
875
+ if robot.world.connected_light_cube:
876
+ robot.behavior.go_to_object(robot.world.connected_light_cube, distance_mm(70.0))
877
+ """
878
+ if target_object is None:
879
+ raise VectorException("Must supply a target_object of type LightCube to go_to_object")
880
+
881
+ go_to_object_request = protocol.GoToObjectRequest(object_id=target_object.object_id,
882
+ distance_from_object_origin_mm=distance_from_object.distance_mm,
883
+ use_pre_dock_pose=False,
884
+ id_tag=_action_id,
885
+ num_retries=num_retries)
886
+
887
+ return await self.grpc_interface.GoToObject(go_to_object_request)
888
+
889
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
890
+ async def roll_cube(self,
891
+ target_object: objects.LightCube,
892
+ approach_angle: util.Angle = None,
893
+ num_retries: int = 0,
894
+ _action_id: int = None) -> protocol.RollObjectResponse:
895
+ """Tells Vector to roll a specified cube object.
896
+
897
+ :param target_object: The cube to roll.
898
+ :param approach_angle: The angle to approach the cube from. For example, 180 degrees will cause Vector to drive
899
+ past the cube and approach it from behind.
900
+ :param num_retries: Number of times to reattempt action in case of a failure.
901
+
902
+ Returns:
903
+ A response from the robot with status information sent when this request successfully completes or fails.
904
+
905
+ .. testcode::
906
+
907
+ import anki_vector
908
+ from anki_vector.util import distance_mm
909
+
910
+ with anki_vector.Robot() as robot:
911
+ robot.world.connect_cube()
912
+
913
+ if robot.world.connected_light_cube:
914
+ robot.behavior.roll_cube(robot.world.connected_light_cube)
915
+ """
916
+ if target_object is None:
917
+ raise VectorException("Must supply a target_object of type LightCube to roll_cube")
918
+
919
+ if approach_angle is None:
920
+ use_approach_angle = False
921
+ approach_angle = util.degrees(0)
922
+ else:
923
+ use_approach_angle = True
924
+ approach_angle = approach_angle
925
+
926
+ roll_object_request = protocol.RollObjectRequest(object_id=target_object.object_id,
927
+ approach_angle_rad=approach_angle.radians,
928
+ use_approach_angle=use_approach_angle,
929
+ use_pre_dock_pose=use_approach_angle,
930
+ id_tag=_action_id,
931
+ num_retries=num_retries)
932
+
933
+ return await self.grpc_interface.RollObject(roll_object_request)
934
+
935
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
936
+ async def pop_a_wheelie(self,
937
+ target_object: objects.LightCube,
938
+ approach_angle: util.Angle = None,
939
+ num_retries: int = 0,
940
+ _action_id: int = None) -> protocol.PopAWheelieResponse:
941
+ """Tells Vector to "pop a wheelie" using his light cube.
942
+
943
+ :param target_object: The cube to push down on with Vector's lift, to start the wheelie.
944
+ :param approach_angle: The angle to approach the cube from. For example, 180 degrees will cause Vector to drive
945
+ past the cube and approach it from behind.
946
+ :param num_retries: Number of times to reattempt action in case of a failure.
947
+
948
+ Returns:
949
+ A response from the robot with status information sent when this request successfully completes or fails.
950
+
951
+ .. testcode::
952
+
953
+ import anki_vector
954
+ from anki_vector.util import distance_mm
955
+
956
+ with anki_vector.Robot() as robot:
957
+ robot.world.connect_cube()
958
+
959
+ if robot.world.connected_light_cube:
960
+ robot.behavior.pop_a_wheelie(robot.world.connected_light_cube)
961
+ """
962
+ if target_object is None:
963
+ raise VectorException("Must supply a target_object of type LightCube to pop_a_wheelie")
964
+
965
+ if approach_angle is None:
966
+ use_approach_angle = False
967
+ approach_angle = util.degrees(0)
968
+ else:
969
+ use_approach_angle = True
970
+ approach_angle = approach_angle
971
+
972
+ pop_a_wheelie_request = protocol.PopAWheelieRequest(object_id=target_object.object_id,
973
+ approach_angle_rad=approach_angle.radians,
974
+ use_approach_angle=use_approach_angle,
975
+ use_pre_dock_pose=use_approach_angle,
976
+ id_tag=_action_id,
977
+ num_retries=num_retries)
978
+
979
+ return await self.grpc_interface.PopAWheelie(pop_a_wheelie_request)
980
+
981
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
982
+ async def pickup_object(self,
983
+ target_object: objects.LightCube,
984
+ use_pre_dock_pose: bool = True,
985
+ num_retries: int = 0,
986
+ _action_id: int = None) -> protocol.PickupObjectResponse:
987
+ """Instruct the robot to pick up his LightCube.
988
+
989
+ While picking up the cube, Vector will use path planning.
990
+
991
+ Note that actions that use the wheels cannot be performed at the same time,
992
+ otherwise you may see a TRACKS_LOCKED error. Methods that use the wheels include
993
+ :meth:`go_to_pose`, :meth:`dock_with_cube`, :meth:`turn_in_place`, :meth:`drive_straight`, and :meth:`pickup_object`.
994
+
995
+ :param target_object: The LightCube object to dock with.
996
+ :param use_pre_dock_pose: Whether or not to try to immediately pick
997
+ up an object or first position the robot next to the object.
998
+ :param num_retries: Number of times to reattempt action in case of a failure.
999
+
1000
+ Returns:
1001
+ A response from the robot with status information sent when this request successfully completes or fails.
1002
+
1003
+ .. testcode::
1004
+
1005
+ import anki_vector
1006
+
1007
+ with anki_vector.Robot() as robot:
1008
+ robot.world.connect_cube()
1009
+
1010
+ if robot.world.connected_light_cube:
1011
+ robot.behavior.pickup_object(robot.world.connected_light_cube)
1012
+ """
1013
+ if target_object is None:
1014
+ raise VectorException("Must supply a target_object to dock_with_cube")
1015
+
1016
+ pickup_object_request = protocol.PickupObjectRequest(object_id=target_object.object_id,
1017
+ use_pre_dock_pose=use_pre_dock_pose,
1018
+ id_tag=_action_id,
1019
+ num_retries=num_retries)
1020
+
1021
+ return await self.grpc_interface.PickupObject(pickup_object_request)
1022
+
1023
+ @connection.on_connection_thread(is_cancellable=connection.CancelType.CANCELLABLE_ACTION)
1024
+ async def place_object_on_ground_here(self,
1025
+ num_retries: int = 0,
1026
+ _action_id: int = None) -> protocol.PlaceObjectOnGroundHereResponse:
1027
+ """Ask Vector to place the object he is carrying on the ground at the current location.
1028
+
1029
+ :param num_retries: Number of times to reattempt action in case of a failure.
1030
+
1031
+ Returns:
1032
+ A response from the robot with status information sent when this request successfully completes or fails.
1033
+
1034
+ .. testcode::
1035
+
1036
+ import anki_vector
1037
+
1038
+ with anki_vector.Robot() as robot:
1039
+ robot.world.connect_cube()
1040
+
1041
+ if robot.world.connected_light_cube:
1042
+ robot.behavior.pickup_object(robot.world.connected_light_cube)
1043
+ robot.behavior.place_object_on_ground_here()
1044
+ """
1045
+ place_object_on_ground_here_request = protocol.PlaceObjectOnGroundHereRequest(id_tag=_action_id,
1046
+ num_retries=num_retries)
1047
+
1048
+ return await self.grpc_interface.PlaceObjectOnGroundHere(place_object_on_ground_here_request)
1049
+
1050
+
1051
+ class ReserveBehaviorControl():
1052
+ """A ReserveBehaviorControl object can be used to suppress the ordinary idle behaviors of
1053
+ the Robot and keep Vector still between SDK control instances. Care must be taken when
1054
+ blocking background behaviors, as this may make Vector appear non-responsive.
1055
+
1056
+ This class is most easily used via a built-in SDK script, and can be called on the command-line
1057
+ via the executable module :class:`anki_vector.reserve_control`:
1058
+
1059
+ .. code-block:: bash
1060
+
1061
+ python3 -m anki_vector.reserve_control
1062
+
1063
+ As long as the script is running, background behaviors will not activate, keeping Vector
1064
+ still while other SDK scripts may take control. Highest-level behaviors like returning to
1065
+ the charger due to low battery will still activate.
1066
+
1067
+ System-specific shortcuts calling this executable module can be found in the examples/scripts
1068
+ folder. These scripts can be double-clicked to easily reserve behavior control for the current
1069
+ SDK default robot.
1070
+
1071
+ If there is a need to keep background behaviors from activating in a single script, the class
1072
+ may be used to reserve behavior control while in scope:
1073
+
1074
+ .. code-block:: python
1075
+
1076
+ import anki_vector
1077
+ from anki_vector import behavior
1078
+
1079
+ with behavior.ReserveBehaviorControl():
1080
+
1081
+ # At this point, Vector will remain still, even without
1082
+ # a Robot instance being in scope.
1083
+
1084
+ # take control of the robot as usual
1085
+ with anki_vector.Robot() as robot:
1086
+
1087
+ robot.anim.play_animation("anim_turn_left_01")
1088
+
1089
+ # Robot will not perform idle behaviors until the script completes
1090
+
1091
+ :param serial: Vector's serial number. The robot's serial number (ex. 00e20100) is located on
1092
+ the underside of Vector, or accessible from Vector's debug screen. Used to
1093
+ identify which Vector configuration to load.
1094
+ :param ip: Vector's IP address. (optional)
1095
+ :param config: A custom :class:`dict` to override values in Vector's configuration. (optional)
1096
+ Example: :code:`{"cert": "/path/to/file.cert", "name": "Vector-XXXX", "guid": "<secret_key>"}`
1097
+ where :code:`cert` is the certificate to identify Vector, :code:`name` is the
1098
+ name on Vector's face when his backpack is double-clicked on the charger, and
1099
+ :code:`guid` is the authorization token that identifies the SDK user.
1100
+ Note: Never share your authentication credentials with anyone.
1101
+ :param behavior_activation_timeout: The time to wait for control of the robot before failing.
1102
+ """
1103
+
1104
+ def __init__(self,
1105
+ serial: str = None,
1106
+ ip: str = None,
1107
+ config: dict = None,
1108
+ behavior_activation_timeout: int = 10):
1109
+ config = config if config is not None else {}
1110
+ self.logger = util.get_class_logger(__name__, self)
1111
+ config = {**util.read_configuration(serial, name=None, logger=self.logger), **config}
1112
+ self._name = config["name"]
1113
+ self._ip = ip if ip is not None else config["ip"]
1114
+ self._cert_file = config["cert"]
1115
+ self._guid = config["guid"]
1116
+
1117
+ self._port = "443"
1118
+ if 'port' in config:
1119
+ self._port = config["port"]
1120
+
1121
+ if self._name is None or self._ip is None or self._cert_file is None or self._guid is None:
1122
+ raise ValueError("The Robot object requires a serial and for Vector to be logged in (using the app then running the anki_vector.configure executable submodule).\n"
1123
+ "You may also provide the values necessary for connection through the config parameter. ex: "
1124
+ '{"name":"Vector-XXXX", "ip":"XX.XX.XX.XX", "cert":"/path/to/cert_file", "guid":"<secret_key>"}')
1125
+
1126
+ self._conn = connection.Connection(self._name, ':'.join([self._ip, self._port]), self._cert_file, self._guid,
1127
+ behavior_control_level=connection.ControlPriorityLevel.RESERVE_CONTROL)
1128
+ self._behavior_activation_timeout = behavior_activation_timeout
1129
+
1130
+ def __enter__(self):
1131
+ self._conn.connect(self._behavior_activation_timeout)
1132
+ return self
1133
+
1134
+ def __exit__(self, exc_type, exc_val, exc_tb):
1135
+ self._conn.close()