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/nav_map.py
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
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
|
+
"""A 2D navigation memory map of the world around Vector.
|
|
16
|
+
|
|
17
|
+
Vector builds a memory map of the navigable world around him as he drives
|
|
18
|
+
around. This is mostly based on where objects are seen (the cubes, charger, and
|
|
19
|
+
any custom objects), and also includes where Vector detects cliffs/drops, and
|
|
20
|
+
visible edges (e.g. sudden changes in color).
|
|
21
|
+
|
|
22
|
+
This differs from a standard occupancy map in that it doesn't deal with
|
|
23
|
+
probabilities of occupancy, but instead encodes what type of content is there.
|
|
24
|
+
|
|
25
|
+
To use the map you must first call :meth:`anki_vector.nav_map.NavMapComponent.init_nav_map_feed`
|
|
26
|
+
with a positive frequency so that the data is streamed to the SDK.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# __all__ should order by constants, event classes, other classes, functions.
|
|
30
|
+
__all__ = ['EvtNavMapUpdate',
|
|
31
|
+
'NavMapComponent', 'NavMapGrid', 'NavMapGridNode',
|
|
32
|
+
'NavNodeContentTypes']
|
|
33
|
+
|
|
34
|
+
import asyncio
|
|
35
|
+
from concurrent.futures import CancelledError
|
|
36
|
+
from enum import Enum
|
|
37
|
+
from logging import Logger
|
|
38
|
+
from typing import List
|
|
39
|
+
|
|
40
|
+
from . import util
|
|
41
|
+
from .events import Events
|
|
42
|
+
from .exceptions import VectorException
|
|
43
|
+
from .messaging import protocol
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class EvtNavMapUpdate(): # pylint: disable=too-few-public-methods
|
|
47
|
+
"""Dispatched when a new nav map is received.
|
|
48
|
+
|
|
49
|
+
:param nav_map: The current state of the robot's nav map.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, nav_map):
|
|
53
|
+
self.nav_map = nav_map
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NavNodeContentTypes(Enum): # pylint: disable=too-few-public-methods
|
|
57
|
+
"""The content types for a :class:`NavMapGridNode`.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
#: The contents of the node is unknown.
|
|
61
|
+
Unknown = protocol.NavNodeContentType.Value("NAV_NODE_UNKNOWN")
|
|
62
|
+
|
|
63
|
+
#: The node is clear of obstacles, because Vector has seen objects on the
|
|
64
|
+
#: other side, but it might contain a cliff. The node will be marked as
|
|
65
|
+
#: either :attr:`Cliff` or :attr:`ClearOfCliff` once Vector has driven there.
|
|
66
|
+
ClearOfObstacle = protocol.NavNodeContentType.Value("NAV_NODE_CLEAR_OF_OBSTACLE")
|
|
67
|
+
|
|
68
|
+
#: The node is clear of any cliffs (a sharp drop) or obstacles.
|
|
69
|
+
ClearOfCliff = protocol.NavNodeContentType.Value("NAV_NODE_CLEAR_OF_CLIFF")
|
|
70
|
+
|
|
71
|
+
#: The node contains a :class:`~anki_vector.objects.LightCube`.
|
|
72
|
+
ObstacleCube = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_CUBE")
|
|
73
|
+
|
|
74
|
+
#: The node contains a proximity detected obstacle which has not been explored.
|
|
75
|
+
ObstacleProximity = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_PROXIMITY")
|
|
76
|
+
|
|
77
|
+
#: The node contains a proximity detected obstacle which has been explored.
|
|
78
|
+
ObstacleProximityExplored = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED")
|
|
79
|
+
|
|
80
|
+
#: The node contains an unrecognized obstacle.
|
|
81
|
+
ObstacleUnrecognized = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_UNRECOGNIZED")
|
|
82
|
+
|
|
83
|
+
#: The node contains a cliff (a sharp drop).
|
|
84
|
+
Cliff = protocol.NavNodeContentType.Value("NAV_NODE_CLIFF")
|
|
85
|
+
|
|
86
|
+
#: The node contains a visible edge (based on the camera feed).
|
|
87
|
+
InterestingEdge = protocol.NavNodeContentType.Value("NAV_NODE_INTERESTING_EDGE")
|
|
88
|
+
|
|
89
|
+
# This entry is undocumented and not currently used
|
|
90
|
+
NonInterestingEdge = protocol.NavNodeContentType.Value("NAV_NODE_NON_INTERESTING_EDGE")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class NavMapGridNode:
|
|
94
|
+
"""A node in a :class:`NavMapGrid`.
|
|
95
|
+
|
|
96
|
+
Leaf nodes contain content, all other nodes are split into 4 equally sized
|
|
97
|
+
children.
|
|
98
|
+
|
|
99
|
+
Child node indices are stored in the following X,Y orientation:
|
|
100
|
+
|
|
101
|
+
+---+----+---+
|
|
102
|
+
| ^ | 2 | 0 |
|
|
103
|
+
+---+----+---+
|
|
104
|
+
| Y | 3 | 1 |
|
|
105
|
+
+---+----+---+
|
|
106
|
+
| | X->| |
|
|
107
|
+
+---+----+---+
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, depth: int, size: float, center: util.Vector3, parent: 'NavMapGridNode', logger: Logger):
|
|
111
|
+
#: The depth of this node (i.e. how far down the quad-tree it is).
|
|
112
|
+
self.depth = depth
|
|
113
|
+
|
|
114
|
+
#: The size (width or length) of this square node.
|
|
115
|
+
self.size = size
|
|
116
|
+
|
|
117
|
+
#: The center of this node.
|
|
118
|
+
self.center = center
|
|
119
|
+
|
|
120
|
+
#: The parent of this node. Is ``None`` for the root node.
|
|
121
|
+
self.parent = parent
|
|
122
|
+
|
|
123
|
+
#: ``None`` for leaf nodes, a list of 4 child nodes otherwise.
|
|
124
|
+
self.children: List[NavMapGridNode] = None
|
|
125
|
+
|
|
126
|
+
#: The content type in this node. Only leaf nodes have content,
|
|
127
|
+
#: this is ``None`` for all other nodes.
|
|
128
|
+
self.content: protocol.NavNodeContentType = None
|
|
129
|
+
|
|
130
|
+
self._next_child = 0 # Used when building to track which branch to follow
|
|
131
|
+
|
|
132
|
+
self._logger = logger
|
|
133
|
+
|
|
134
|
+
def __repr__(self):
|
|
135
|
+
return '<%s center: %s size: %s content: %s>' % (
|
|
136
|
+
self.__class__.__name__, self.center, self.size, self.content)
|
|
137
|
+
|
|
138
|
+
def contains_point(self, x: float, y: float) -> bool:
|
|
139
|
+
"""Test if the node contains the given x,y coordinates.
|
|
140
|
+
|
|
141
|
+
:param x: x coordinate for the point.
|
|
142
|
+
:param y: y coordinate for the point.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
True if the node contains the point, False otherwise.
|
|
146
|
+
"""
|
|
147
|
+
half_size = self.size * 0.5
|
|
148
|
+
dist_x = abs(self.center.x - x)
|
|
149
|
+
dist_y = abs(self.center.y - y)
|
|
150
|
+
return (dist_x <= half_size) and (dist_y <= half_size)
|
|
151
|
+
|
|
152
|
+
def _get_node(self, x: float, y: float, assumed_in_bounds: bool) -> 'NavMapGridNode':
|
|
153
|
+
if not assumed_in_bounds and not self.contains_point(x, y):
|
|
154
|
+
# point is out of bounds
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
if self.children is None:
|
|
158
|
+
return self
|
|
159
|
+
|
|
160
|
+
x_offset = 2 if x < self.center.x else 0
|
|
161
|
+
y_offset = 1 if y < self.center.y else 0
|
|
162
|
+
child_node = self.children[x_offset + y_offset]
|
|
163
|
+
# child node is by definition in bounds / on boundary
|
|
164
|
+
return child_node._get_node(x, y, True) # pylint: disable=protected-access
|
|
165
|
+
|
|
166
|
+
def get_node(self, x: float, y: float) -> 'NavMapGridNode':
|
|
167
|
+
"""Get the node at the given x,y coordinates.
|
|
168
|
+
|
|
169
|
+
:param x: x coordinate for the point.
|
|
170
|
+
:param y: y coordinate for the point.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The smallest node that includes the point.
|
|
174
|
+
Will be ``None`` if the point is outside of the map.
|
|
175
|
+
"""
|
|
176
|
+
return self._get_node(x, y, assumed_in_bounds=False)
|
|
177
|
+
|
|
178
|
+
def get_content(self, x: float, y: float) -> protocol.NavNodeContentType:
|
|
179
|
+
"""Get the node's content at the given x,y coordinates.
|
|
180
|
+
|
|
181
|
+
:param x: x coordinate for the point.
|
|
182
|
+
:param y: y coordinate for the point.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The content included at that point. Will be :attr:`NavNodeContentTypes.Unknown`
|
|
186
|
+
if the point is outside of the map.
|
|
187
|
+
"""
|
|
188
|
+
node = self.get_node(x, y)
|
|
189
|
+
if node:
|
|
190
|
+
return node.content
|
|
191
|
+
|
|
192
|
+
return NavNodeContentTypes.Unknown
|
|
193
|
+
|
|
194
|
+
def add_child(self, content: protocol.NavNodeContentType, depth: int) -> bool:
|
|
195
|
+
"""Add a child node to the quad tree.
|
|
196
|
+
|
|
197
|
+
The quad-tree is serialized to a flat list of nodes, we deserialize
|
|
198
|
+
back to a quad-tree structure here, with the depth of each node
|
|
199
|
+
indicating where it is placed.
|
|
200
|
+
|
|
201
|
+
:param content: The content to store in the leaf node.
|
|
202
|
+
:param depth: The depth that this leaf node is located at.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
True if parent should use the next child for future add_child
|
|
206
|
+
calls.
|
|
207
|
+
"""
|
|
208
|
+
if depth > self.depth:
|
|
209
|
+
self._logger.error("NavMapGridNode depth %s > %s", depth, self.depth)
|
|
210
|
+
if self._next_child > 3:
|
|
211
|
+
self._logger.error("NavMapGridNode _next_child %s (>3) at depth %s", self._next_child, self.depth)
|
|
212
|
+
|
|
213
|
+
if self.depth == depth:
|
|
214
|
+
if self.content is not None:
|
|
215
|
+
self._logger.error("NavMapGridNode: Clobbering %s at depth %s with %s",
|
|
216
|
+
self.content, self.depth, content)
|
|
217
|
+
self.content = content
|
|
218
|
+
# This node won't be further subdivided, and is now full
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
if self.children is None:
|
|
222
|
+
# Create 4 child nodes for quad-tree structure
|
|
223
|
+
next_depth = self.depth - 1
|
|
224
|
+
next_size = self.size * 0.5
|
|
225
|
+
offset = next_size * 0.5
|
|
226
|
+
center1 = util.Vector3(self.center.x + offset, self.center.y + offset, self.center.z)
|
|
227
|
+
center2 = util.Vector3(self.center.x + offset, self.center.y - offset, self.center.z)
|
|
228
|
+
center3 = util.Vector3(self.center.x - offset, self.center.y + offset, self.center.z)
|
|
229
|
+
center4 = util.Vector3(self.center.x - offset, self.center.y - offset, self.center.z)
|
|
230
|
+
self.children = [NavMapGridNode(next_depth, next_size, center1, self, self._logger),
|
|
231
|
+
NavMapGridNode(next_depth, next_size, center2, self, self._logger),
|
|
232
|
+
NavMapGridNode(next_depth, next_size, center3, self, self._logger),
|
|
233
|
+
NavMapGridNode(next_depth, next_size, center4, self, self._logger)]
|
|
234
|
+
if self.children[self._next_child].add_child(content, depth):
|
|
235
|
+
# Child node is now full, start using the next child
|
|
236
|
+
self._next_child += 1
|
|
237
|
+
|
|
238
|
+
if self._next_child > 3:
|
|
239
|
+
# All children are now full - parent should start using the next child
|
|
240
|
+
return True
|
|
241
|
+
|
|
242
|
+
# Empty children remain - parent can keep using this child
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class NavMapGrid:
|
|
247
|
+
"""A navigation memory map, stored as a quad-tree."""
|
|
248
|
+
|
|
249
|
+
def __init__(self, msg: protocol.NavMapFeedResponse, logger: Logger):
|
|
250
|
+
#: The origin ID for the map. Only maps and :class:`~anki_vector.util.Pose`
|
|
251
|
+
#: objects of the same origin ID are in the same coordinate frame and
|
|
252
|
+
#: can therefore be compared.
|
|
253
|
+
self.origin_id = msg.origin_id
|
|
254
|
+
root_center = util.Vector3(msg.map_info.root_center_x, msg.map_info.root_center_y, msg.map_info.root_center_z)
|
|
255
|
+
self._root_node = NavMapGridNode(msg.map_info.root_depth, msg.map_info.root_size_mm, root_center, None, logger)
|
|
256
|
+
for quad in msg.quad_infos:
|
|
257
|
+
self.add_quad(quad.content, quad.depth)
|
|
258
|
+
|
|
259
|
+
self._logger = logger
|
|
260
|
+
|
|
261
|
+
def __repr__(self):
|
|
262
|
+
return '<%s center: %s size: %s>' % (
|
|
263
|
+
self.__class__.__name__, self.center, self.size)
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def root_node(self) -> NavMapGridNode:
|
|
267
|
+
"""The root node for the grid, contains all other nodes."""
|
|
268
|
+
return self._root_node
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def size(self) -> float:
|
|
272
|
+
"""The size (width or length) of the square grid."""
|
|
273
|
+
return self._root_node.size
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def center(self) -> util.Vector3:
|
|
277
|
+
"""The center of this map."""
|
|
278
|
+
return self._root_node.center
|
|
279
|
+
|
|
280
|
+
def contains_point(self, x: float, y: float) -> bool:
|
|
281
|
+
"""Test if the map contains the given x,y coordinates.
|
|
282
|
+
|
|
283
|
+
:param x: x coordinate for the point.
|
|
284
|
+
:param y: y coordinate for the point.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
True if the map contains the point, False otherwise.
|
|
288
|
+
"""
|
|
289
|
+
return self._root_node.contains_point(x, y)
|
|
290
|
+
|
|
291
|
+
def get_node(self, x: float, y: float) -> NavMapGridNode:
|
|
292
|
+
"""Get the node at the given x,y coordinates.
|
|
293
|
+
|
|
294
|
+
:param x: x coordinate for the point.
|
|
295
|
+
:param y: y coordinate for the point.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
The smallest node that includes the point.
|
|
299
|
+
Will be ``None`` if the point is outside of the map.
|
|
300
|
+
"""
|
|
301
|
+
return self._root_node.get_node(x, y)
|
|
302
|
+
|
|
303
|
+
def get_content(self, x: float, y: float) -> protocol.NavNodeContentType:
|
|
304
|
+
"""Get the map's content at the given x,y coordinates.
|
|
305
|
+
|
|
306
|
+
:param x: x coordinate for the point.
|
|
307
|
+
:param y: y coordinate for the point.
|
|
308
|
+
|
|
309
|
+
.. testcode::
|
|
310
|
+
|
|
311
|
+
import anki_vector
|
|
312
|
+
|
|
313
|
+
with anki_vector.Robot(enable_nav_map_feed=True) as robot:
|
|
314
|
+
# Make sure Vector drives around so the nav map will update
|
|
315
|
+
robot.behavior.drive_off_charger()
|
|
316
|
+
robot.motors.set_wheel_motors(-100, 100)
|
|
317
|
+
latest_nav_map = robot.nav_map.latest_nav_map
|
|
318
|
+
content = latest_nav_map.get_content(0.0, 100.0)
|
|
319
|
+
print(f"Sampling point at 0.0, 100.0 and found content: {content}")
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
The content included at that point. Will be :attr:`NavNodeContentTypes.Unknown`
|
|
323
|
+
if the point is outside of the map.
|
|
324
|
+
"""
|
|
325
|
+
return self._root_node.get_content(x, y)
|
|
326
|
+
|
|
327
|
+
def add_quad(self, content: protocol.NavNodeContentType, depth: int):
|
|
328
|
+
"""Adds a new quad to the nav map.
|
|
329
|
+
|
|
330
|
+
:param content: What content this node contains.
|
|
331
|
+
:param depth: How deep in the navMap this node is.
|
|
332
|
+
"""
|
|
333
|
+
self._root_node.add_child(content, depth)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class NavMapComponent(util.Component):
|
|
337
|
+
"""Represents Vector's navigation memory map.
|
|
338
|
+
|
|
339
|
+
The NavMapComponent object subscribes for nav memory map updates from the robot to store and dispatch.
|
|
340
|
+
|
|
341
|
+
The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance hosts this component.
|
|
342
|
+
|
|
343
|
+
.. testcode::
|
|
344
|
+
|
|
345
|
+
import anki_vector
|
|
346
|
+
|
|
347
|
+
with anki_vector.Robot(enable_nav_map_feed=True) as robot:
|
|
348
|
+
# Make sure Vector drives around so the nav map will update
|
|
349
|
+
robot.behavior.drive_off_charger()
|
|
350
|
+
robot.motors.set_wheel_motors(-100, 100)
|
|
351
|
+
latest_nav_map = robot.nav_map.latest_nav_map
|
|
352
|
+
|
|
353
|
+
:param robot: A reference to the owner Robot object.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
def __init__(self, robot):
|
|
357
|
+
super().__init__(robot)
|
|
358
|
+
|
|
359
|
+
self._latest_nav_map: NavMapGrid = None
|
|
360
|
+
self._nav_map_feed_task: asyncio.Task = None
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
@util.block_while_none()
|
|
364
|
+
def latest_nav_map(self) -> NavMapGrid:
|
|
365
|
+
""":class:`NavMapGrid`: The most recently processed image received from the robot.
|
|
366
|
+
|
|
367
|
+
.. testcode::
|
|
368
|
+
|
|
369
|
+
import anki_vector
|
|
370
|
+
|
|
371
|
+
with anki_vector.Robot(enable_nav_map_feed=True) as robot:
|
|
372
|
+
# Make sure Vector drives around so the nav map will update
|
|
373
|
+
robot.behavior.drive_off_charger()
|
|
374
|
+
robot.motors.set_wheel_motors(-100, 100)
|
|
375
|
+
latest_nav_map = robot.nav_map.latest_nav_map
|
|
376
|
+
"""
|
|
377
|
+
if not self._nav_map_feed_task or self._nav_map_feed_task.done():
|
|
378
|
+
raise VectorException("Nav map not initialized. Check that Robot parameter enable_nav_map_feed is set to True.")
|
|
379
|
+
return self._latest_nav_map
|
|
380
|
+
|
|
381
|
+
def init_nav_map_feed(self, frequency: float = 0.5) -> None:
|
|
382
|
+
"""Begin nav map feed task.
|
|
383
|
+
|
|
384
|
+
:param frequency: How frequently to send nav map updates.
|
|
385
|
+
"""
|
|
386
|
+
if not self._nav_map_feed_task or self._nav_map_feed_task.done():
|
|
387
|
+
self._nav_map_feed_task = self.conn.loop.create_task(self._request_and_handle_nav_maps(frequency))
|
|
388
|
+
|
|
389
|
+
def close_nav_map_feed(self) -> None:
|
|
390
|
+
"""Cancel nav map feed task."""
|
|
391
|
+
if self._nav_map_feed_task:
|
|
392
|
+
self._nav_map_feed_task.cancel()
|
|
393
|
+
future = self.conn.run_coroutine(self._nav_map_feed_task)
|
|
394
|
+
future.result()
|
|
395
|
+
self._nav_map_feed_task = None
|
|
396
|
+
|
|
397
|
+
async def _request_and_handle_nav_maps(self, frequency: float) -> None:
|
|
398
|
+
"""Queries and listens for nav map feed events from the robot.
|
|
399
|
+
Received events are parsed by a helper function.
|
|
400
|
+
|
|
401
|
+
:param frequency: How frequently to send nav map updates.
|
|
402
|
+
"""
|
|
403
|
+
try:
|
|
404
|
+
req = protocol.NavMapFeedRequest(frequency=frequency)
|
|
405
|
+
async for evt in self.grpc_interface.NavMapFeed(req):
|
|
406
|
+
self._latest_nav_map = NavMapGrid(evt, self.logger)
|
|
407
|
+
await self._robot.events.dispatch_event(evt, Events.nav_map_update)
|
|
408
|
+
except CancelledError:
|
|
409
|
+
self.logger.debug('Nav Map feed task was cancelled. This is expected during disconnection.')
|