wirepod-vector-sdk-audio 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anki_vector/__init__.py +43 -0
- anki_vector/animation.py +272 -0
- anki_vector/annotate.py +590 -0
- anki_vector/audio.py +212 -0
- anki_vector/audio_stream.py +335 -0
- anki_vector/behavior.py +1135 -0
- anki_vector/camera.py +670 -0
- anki_vector/camera_viewer/__init__.py +121 -0
- anki_vector/color.py +88 -0
- anki_vector/configure/__main__.py +331 -0
- anki_vector/connection.py +838 -0
- anki_vector/events.py +420 -0
- anki_vector/exceptions.py +185 -0
- anki_vector/faces.py +819 -0
- anki_vector/lights.py +210 -0
- anki_vector/mdns.py +131 -0
- anki_vector/messaging/__init__.py +45 -0
- anki_vector/messaging/alexa_pb2.py +36 -0
- anki_vector/messaging/alexa_pb2_grpc.py +3 -0
- anki_vector/messaging/behavior_pb2.py +40 -0
- anki_vector/messaging/behavior_pb2_grpc.py +3 -0
- anki_vector/messaging/client.py +33 -0
- anki_vector/messaging/cube_pb2.py +113 -0
- anki_vector/messaging/cube_pb2_grpc.py +3 -0
- anki_vector/messaging/extensions_pb2.py +25 -0
- anki_vector/messaging/extensions_pb2_grpc.py +3 -0
- anki_vector/messaging/external_interface_pb2.py +169 -0
- anki_vector/messaging/external_interface_pb2_grpc.py +1267 -0
- anki_vector/messaging/messages_pb2.py +431 -0
- anki_vector/messaging/messages_pb2_grpc.py +3 -0
- anki_vector/messaging/nav_map_pb2.py +33 -0
- anki_vector/messaging/nav_map_pb2_grpc.py +3 -0
- anki_vector/messaging/protocol.py +33 -0
- anki_vector/messaging/response_status_pb2.py +27 -0
- anki_vector/messaging/response_status_pb2_grpc.py +3 -0
- anki_vector/messaging/settings_pb2.py +72 -0
- anki_vector/messaging/settings_pb2_grpc.py +3 -0
- anki_vector/messaging/shared_pb2.py +54 -0
- anki_vector/messaging/shared_pb2_grpc.py +3 -0
- anki_vector/motors.py +127 -0
- anki_vector/nav_map.py +409 -0
- anki_vector/objects.py +1782 -0
- anki_vector/opengl/__init__.py +103 -0
- anki_vector/opengl/assets/LICENSE.txt +21 -0
- anki_vector/opengl/assets/cube.jpg +0 -0
- anki_vector/opengl/assets/cube.mtl +9 -0
- anki_vector/opengl/assets/cube.obj +1000 -0
- anki_vector/opengl/assets/vector.mtl +67 -0
- anki_vector/opengl/assets/vector.obj +13220 -0
- anki_vector/opengl/opengl.py +864 -0
- anki_vector/opengl/opengl_vector.py +620 -0
- anki_vector/opengl/opengl_viewer.py +689 -0
- anki_vector/photos.py +145 -0
- anki_vector/proximity.py +176 -0
- anki_vector/reserve_control/__main__.py +36 -0
- anki_vector/robot.py +930 -0
- anki_vector/screen.py +201 -0
- anki_vector/status.py +322 -0
- anki_vector/touch.py +119 -0
- anki_vector/user_intent.py +186 -0
- anki_vector/util.py +1132 -0
- anki_vector/version.py +15 -0
- anki_vector/viewer.py +403 -0
- anki_vector/vision.py +202 -0
- anki_vector/world.py +899 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/METADATA +80 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/RECORD +71 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/WHEEL +5 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/licenses/LICENSE.txt +180 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/top_level.txt +1 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/zip-safe +1 -0
anki_vector/behavior.py
ADDED
|
@@ -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()
|