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