Skip to content

Commit 8ea7741

Browse files
committed
Update to work with R1.6 #195
Overwrites master tip to work with R1.6 robot. Note that this overwrites work previously done for R1.7. This was done by: * Working with a branch from 3/29/19 * Modifying that branch to work with R1.6 * Then branching from tip and putting this work ^^ on that branch * Then merging this PR. Unfortunately the vector-proto submodule is probably in the wrong state.
1 parent 662a04c commit 8ea7741

31 files changed

+531
-2911
lines changed

anki_vector/animation.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ async def load_animation_trigger_list(self):
190190
"""
191191
return await self._load_animation_trigger_list()
192192

193-
# TODO: add return type hint
194193
@connection.on_connection_thread()
195194
async def play_animation_trigger(self, anim_trigger: str, loop_count: int = 1, use_lift_safe: bool = False, ignore_body_track: bool = False, ignore_head_track: bool = False, ignore_lift_track: bool = False): # START
196195
"""Starts an animation trigger playing on a robot.
@@ -228,7 +227,6 @@ async def play_animation_trigger(self, anim_trigger: str, loop_count: int = 1, u
228227
ignore_lift_track=ignore_lift_track)
229228
return await self.grpc_interface.PlayAnimationTrigger(req)
230229

231-
# TODO: add return type hint
232230
@connection.on_connection_thread()
233231
async def play_animation(self, anim: str, loop_count: int = 1, ignore_body_track: bool = False, ignore_head_track: bool = False, ignore_lift_track: bool = False):
234232
"""Starts an animation playing on a robot.

anki_vector/annotate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from typing import Callable, Iterable, Tuple, Union
4444

4545
try:
46-
from PIL import Image, ImageDraw
46+
from PIL import Image, ImageDraw, ImageFont
4747
except ImportError:
4848
sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install")
4949
except SyntaxError:
@@ -137,7 +137,7 @@ def clock(image, scale, annotator=None, world=None, **kw):
137137
"""
138138

139139
def __init__(self, text: str, position: int = AnnotationPosition.BOTTOM_RIGHT, align: str = "left", color: str = "white",
140-
font=None, line_spacing: int = 3, outline_color: str = None, full_outline: bool = True):
140+
font = None, line_spacing: int = 3, outline_color: str = None, full_outline: bool = True):
141141
self.text = text
142142
self.position = position
143143
self.align = align

anki_vector/audio.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"""Support for accessing Vector's audio.
1616
1717
Vector's speakers can be used for playing user-provided audio.
18-
TODO Ability to access the Vector's audio stream to come.
1918
2019
The :class:`AudioComponent` class defined in this module is made available as
2120
:attr:`anki_vector.robot.Robot.audio` and can be used to play audio data on the robot.
@@ -56,7 +55,6 @@ class AudioComponent(util.Component):
5655
"""Handles audio on Vector.
5756
5857
The AudioComponent object plays audio data to Vector's speaker.
59-
Ability to access the Vector's audio stream to come.
6058
6159
The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance
6260
owns this audio component.
@@ -69,8 +67,6 @@ class AudioComponent(util.Component):
6967
robot.audio.stream_wav_file('../examples/sounds/vector_alert.wav')
7068
"""
7169

72-
# TODO restore audio feed code when ready
73-
7470
def __init__(self, robot):
7571
super().__init__(robot)
7672
self._is_shutdown = False

anki_vector/behavior.py

Lines changed: 59 additions & 118 deletions
Large diffs are not rendered by default.

anki_vector/camera.py

Lines changed: 4 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
# __all__ should order by constants, event classes, other classes, functions.
2828
__all__ = ["EvtNewRawCameraImage", "EvtNewCameraImage",
29-
"CameraComponent", "CameraConfig", "CameraImage"]
29+
"CameraComponent", "CameraImage"]
3030

3131
import asyncio
3232
from concurrent.futures import CancelledError
@@ -36,7 +36,7 @@
3636

3737
from . import annotate, connection, util
3838
from .events import Events
39-
from .exceptions import VectorCameraFeedException, VectorCameraImageCaptureException
39+
from .exceptions import VectorCameraFeedException
4040
from .messaging import protocol
4141

4242
try:
@@ -129,124 +129,12 @@ def annotate_image(self, scale: float = None, fit_size: tuple = None, resample_m
129129
(fast) or :attr:`~anki_vector.annotate.RESAMPLE_MODE_BILINEAR` (slower,
130130
but smoother).
131131
"""
132-
if self._raw_image.size != (640, 360):
133-
raise VectorCameraImageCaptureException("Annotation is only supported for default resolution images.")
134132
return self._image_annotator.annotate_image(self._raw_image,
135133
scale=scale,
136134
fit_size=fit_size,
137135
resample_mode=resample_mode)
138136

139137

140-
class CameraConfig:
141-
""" The fixed properties for Vector's camera.
142-
143-
A full 3x3 calibration matrix for doing 3D reasoning based on the camera
144-
images would look like:
145-
146-
+--------------+--------------+---------------+
147-
|focal_length.x| 0 | center.x |
148-
+--------------+--------------+---------------+
149-
| 0 |focal_length.y| center.y |
150-
+--------------+--------------+---------------+
151-
| 0 | 0 | 1 |
152-
+--------------+--------------+---------------+
153-
154-
.. testcode::
155-
156-
import anki_vector
157-
158-
with anki_vector.Robot() as robot:
159-
min = robot.camera.config.min_gain
160-
max = robot.camera.config.max_gain
161-
print(f"Robot camera allowable exposure gain range is from {min} to {max}")
162-
"""
163-
164-
def __init__(self,
165-
focal_length_x: float,
166-
focal_length_y: float,
167-
center_x: float,
168-
center_y: float,
169-
fov_x: float,
170-
fov_y: float,
171-
min_exposure_time_ms: int,
172-
max_exposure_time_ms: int,
173-
min_gain: float,
174-
max_gain: float):
175-
self._focal_length = util.Vector2(focal_length_x, focal_length_y)
176-
self._center = util.Vector2(center_x, center_y)
177-
self._fov_x = util.degrees(fov_x)
178-
self._fov_y = util.degrees(fov_y)
179-
self._min_exposure_ms = min_exposure_time_ms
180-
self._max_exposure_ms = max_exposure_time_ms
181-
self._min_gain = min_gain
182-
self._max_gain = max_gain
183-
184-
@classmethod
185-
def create_from_message(cls, msg: protocol.CameraConfigResponse):
186-
"""Create camera configuration based on Vector's camera configuration from the message sent from the Robot """
187-
return cls(msg.focal_length_x,
188-
msg.focal_length_y,
189-
msg.center_x,
190-
msg.center_y,
191-
msg.fov_x,
192-
msg.fov_y,
193-
msg.min_camera_exposure_time_ms,
194-
msg.max_camera_exposure_time_ms,
195-
msg.min_camera_gain,
196-
msg.max_camera_gain)
197-
198-
@property
199-
def min_gain(self) -> float:
200-
"""The minimum supported camera gain."""
201-
return self._min_gain
202-
203-
@property
204-
def max_gain(self) -> float:
205-
"""The maximum supported camera gain."""
206-
return self._max_gain
207-
208-
@property
209-
def min_exposure_time_ms(self) -> int:
210-
"""The minimum supported exposure time in milliseconds."""
211-
return self._min_exposure_ms
212-
213-
@property
214-
def max_exposure_time_ms(self) -> int:
215-
"""The maximum supported exposure time in milliseconds."""
216-
return self._max_exposure_ms
217-
218-
@property
219-
def focal_length(self):
220-
""":class:`anki_vector.util.Vector2`: The focal length of the camera.
221-
222-
This is focal length combined with pixel skew (as the pixels aren't
223-
perfectly square), so there are subtly different values for x and y.
224-
It is in floating point pixel values e.g. <288.87, 288.36>.
225-
"""
226-
return self._focal_length
227-
228-
@property
229-
def center(self):
230-
""":class:`anki_vector.util.Vector2`: The focal center of the camera.
231-
232-
This is the position of the optical center of projection within the
233-
image. It will be close to the center of the image, but adjusted based
234-
on the calibration of the lens. It is in floating point pixel values
235-
e.g. <155.11, 111.40>.
236-
"""
237-
return self._center
238-
239-
@property
240-
def fov_x(self):
241-
""":class:`anki_vector.util.Angle`: The x (horizontal) field of view."""
242-
return self._fov_x
243-
244-
@property
245-
def fov_y(self):
246-
""":class:`anki_vector.util.Angle`: The y (vertical) field of view."""
247-
return self._fov_y
248-
249-
250138
class CameraComponent(util.Component):
251139
"""Represents Vector's camera.
252140
@@ -279,55 +167,6 @@ def __init__(self, robot):
279167
self._latest_image_id: int = None
280168
self._camera_feed_task: asyncio.Task = None
281169
self._enabled = False
282-
self._config = None # type CameraConfig
283-
self._gain = 0.0
284-
self._exposure_ms = 0
285-
self._auto_exposure_enabled = True
286-
287-
def set_config(self, message: protocol.CameraConfigRequest):
288-
"""Update Vector's camera configuration from the message sent from the Robot """
289-
self._config = CameraConfig.create_from_message(message)
290-
291-
@connection.on_connection_thread(requires_control=False)
292-
async def get_camera_config(self) -> protocol.CameraConfigResponse:
293-
""" Get Vector's camera configuration
294-
295-
Retrieves the calibrated camera settings. This is called during the Robot connection initialization, SDK
296-
users should use the `config` property in most instances.
297-
298-
:return:
299-
"""
300-
request = protocol.CameraConfigRequest()
301-
return await self.conn.grpc_interface.GetCameraConfig(request)
302-
303-
@property
304-
def config(self) -> CameraConfig:
305-
""":class:`anki_vector.camera.CameraConfig`: The read-only config/calibration for the camera"""
306-
return self._config
307-
308-
@property
309-
def is_auto_exposure_enabled(self) -> bool:
310-
"""bool: True if auto exposure is currently enabled
311-
312-
If auto exposure is enabled the `gain` and `exposure_ms`
313-
values will constantly be updated by Vector.
314-
"""
315-
return self._auto_exposure_enabled
316-
317-
@property
318-
def gain(self) -> float:
319-
"""float: The current camera gain setting."""
320-
return self._gain
321-
322-
@property
323-
def exposure_ms(self) -> int:
324-
"""int: The current camera exposure setting in milliseconds."""
325-
return self._exposure_ms
326-
327-
def update_state(self, _robot, _event_type, msg):
328-
self._gain = msg.gain
329-
self._exposure_ms = msg.exposure_ms
330-
self._auto_exposure_enabled = msg.auto_exposure_enabled
331170

332171
@property
333172
@util.block_while_none()
@@ -491,7 +330,7 @@ async def _request_and_handle_images(self) -> None:
491330
self.logger.debug('Camera feed task was cancelled. This is expected during disconnection.')
492331

493332
@connection.on_connection_thread()
494-
async def capture_single_image(self, enable_high_resolution: bool = False) -> CameraImage:
333+
async def capture_single_image(self) -> CameraImage:
495334
"""Request to capture a single image from the robot's camera.
496335
497336
This call requests the robot to capture an image and returns the
@@ -507,88 +346,17 @@ async def capture_single_image(self, enable_high_resolution: bool = False) -> Ca
507346
with anki_vector.Robot() as robot:
508347
image = robot.camera.capture_single_image()
509348
image.raw_image.show()
510-
511-
:param enable_high_resolution: Enable/disable request for high resolution images. The default resolution
512-
is 640x360, while the high resolution is 1280x720.
513349
"""
514350
if self._enabled:
515-
self.logger.warning('Camera feed is enabled. Receiving image from the feed at default resolution.')
516351
return self._latest_image
517-
if enable_high_resolution:
518-
self.logger.warning('Capturing a high resolution (1280*720) image. Image events for this frame need to be scaled.')
519-
req = protocol.CaptureSingleImageRequest(enable_high_resolution=enable_high_resolution)
352+
req = protocol.CaptureSingleImageRequest()
520353
res = await self.grpc_interface.CaptureSingleImage(req)
521354
if res and res.data:
522355
image = _convert_to_pillow_image(res.data)
523356
return CameraImage(image, self._image_annotator, res.image_id)
524357

525358
self.logger.error('Failed to capture a single image')
526359

527-
@connection.on_connection_thread()
528-
async def enable_auto_exposure(self, enable_auto_exposure=True) -> protocol.SetCameraSettingsResponse:
529-
"""Enable auto exposure on Vector's Camera.
530-
531-
Enable auto exposure on Vector's camera to constantly update the exposure
532-
time and gain values based on the recent images. This is the default mode
533-
when any SDK program starts.
534-
535-
.. testcode::
536-
537-
import time
538-
import anki_vector
539-
with anki_vector.Robot() as robot:
540-
robot.camera.enable_auto_exposure(False)
541-
time.sleep(5)
542-
543-
:param enable_auto_exposure: whether the camera should automatically adjust exposure
544-
"""
545-
546-
set_camera_settings_request = protocol.SetCameraSettingsRequest(enable_auto_exposure=enable_auto_exposure)
547-
result = await self.conn.grpc_interface.SetCameraSettings(set_camera_settings_request)
548-
self._auto_exposure_enabled = enable_auto_exposure
549-
return result
550-
551-
@connection.on_connection_thread()
552-
async def set_manual_exposure(self, exposure_ms: int, gain: float) -> protocol.SetCameraSettingsResponse:
553-
"""Set manual exposure values for Vector's Camera.
554-
555-
This will disable auto exposure on Vector's camera and force the specified exposure
556-
time and gain values.
557-
558-
.. testcode::
559-
560-
import time
561-
import anki_vector
562-
with anki_vector.Robot() as robot:
563-
robot.camera.set_manual_exposure(1, 0.25)
564-
time.sleep(5)
565-
566-
:param exposure_ms: The desired exposure time in milliseconds.
567-
Must be within the robot's exposure range from :attr:`CameraConfig.min_exposure_time_ms` to
568-
:attr:`CameraConfig.max_exposure_time_ms`
569-
:param gain: The desired gain value.
570-
Must be within the robot's gain range from :attr:`CameraConfig.min_gain` to
571-
:attr:`CameraConfig.max_gain`
572-
Raises:
573-
:class:`ValueError` if supplied an out-of-range exposure or gain
574-
575-
"""
576-
577-
if exposure_ms < self._config.min_exposure_time_ms \
578-
or exposure_ms > self._config.max_exposure_time_ms \
579-
or gain < self._config.min_gain \
580-
or gain > self._config.max_gain:
581-
raise ValueError("Exposure settings out of range")
582-
583-
set_camera_settings_request = protocol.SetCameraSettingsRequest(gain=gain,
584-
exposure_ms=exposure_ms,
585-
enable_auto_exposure=False)
586-
result = await self.conn.grpc_interface.SetCameraSettings(set_camera_settings_request)
587-
self._gain = gain
588-
self._exposure_ms = exposure_ms
589-
self._auto_exposure_enabled = False
590-
return result
591-
592360

593361
class EvtNewRawCameraImage: # pylint: disable=too-few-public-methods
594362
"""Dispatched when a new raw image is received from the robot's camera.

anki_vector/configure/__main__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import json
3131
import os
3232
from pathlib import Path
33-
import platform
3433
import re
3534
import socket
3635
import sys
@@ -67,10 +66,7 @@ class Api:
6766
def __init__(self):
6867
self._handler = ApiHandler(
6968
headers={
70-
'User-Agent': 'Vector-sdk/{} {}/{}'.format(anki_vector.__version__,
71-
platform.python_implementation(),
72-
platform.python_version()),
73-
69+
'User-Agent': f'Vector-sdk/{anki_vector.__version__}',
7470
'Anki-App-Key': 'aung2ieCho3aiph7Een3Ei'
7571
},
7672
url='https://accounts.api.anki.com/1/sessions'

0 commit comments

Comments
 (0)