Skip to content

Commit 963a3fe

Browse files
committed
Working through typehinting rest of spot wrapper
1 parent 53cd7da commit 963a3fe

File tree

9 files changed

+73
-56
lines changed

9 files changed

+73
-56
lines changed

spot_wrapper/cam_wrapper.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import datetime
33
import enum
4+
import logging
45
import math
56
import os.path
67
import pathlib
@@ -17,7 +18,9 @@
1718
from bosdyn.api.data_chunk_pb2 import DataChunk
1819
from bosdyn.api.spot_cam import audio_pb2
1920
from bosdyn.api.spot_cam.camera_pb2 import Camera
21+
from bosdyn.api.spot_cam.compositor_pb2 import IrColorMap
2022
from bosdyn.api.spot_cam.logging_pb2 import Logpoint
23+
from bosdyn.api.spot_cam.power_pb2 import PowerStatus
2124
from bosdyn.api.spot_cam.ptz_pb2 import PtzDescription, PtzPosition, PtzVelocity
2225
from bosdyn.client import Robot, spot_cam
2326
from bosdyn.client.payload import PayloadClient
@@ -50,7 +53,7 @@ class LEDPosition(enum.Enum):
5053
FRONT_RIGHT = 2
5154
REAR_RIGHT = 3
5255

53-
def __init__(self, robot: Robot, logger) -> None:
56+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
5457
self.logger = logger
5558
self.client: LightingClient = robot.ensure_client(LightingClient.default_service_name)
5659

@@ -82,11 +85,11 @@ class PowerWrapper:
8285
Wrapper for power interaction
8386
"""
8487

85-
def __init__(self, robot: Robot, logger) -> None:
88+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
8689
self.logger = logger
8790
self.client: PowerClient = robot.ensure_client(PowerClient.default_service_name)
8891

89-
def get_power_status(self):
92+
def get_power_status(self) -> PowerStatus:
9093
"""
9194
Get power status for the devices
9295
"""
@@ -134,7 +137,7 @@ class CompositorWrapper:
134137
Wrapper for compositor interaction
135138
"""
136139

137-
def __init__(self, robot: Robot, logger) -> None:
140+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
138141
self.logger = logger
139142
self.client: CompositorClient = robot.ensure_client(CompositorClient.default_service_name)
140143

@@ -175,7 +178,7 @@ def get_screen(self) -> str:
175178
"""
176179
return self.client.get_screen()
177180

178-
def set_ir_colormap(self, colormap, min_temp: float, max_temp: float, auto_scale: bool = True) -> None:
181+
def set_ir_colormap(self, colormap: IrColorMap, min_temp: float, max_temp: float, auto_scale: bool = True) -> None:
179182
"""
180183
Set the colormap used for the IR camera
181184
@@ -205,7 +208,7 @@ class HealthWrapper:
205208
Wrapper for health details
206209
"""
207210

208-
def __init__(self, robot: Robot, logger) -> None:
211+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
209212
self.client: HealthClient = robot.ensure_client(HealthClient.default_service_name)
210213
self.logger = logger
211214

@@ -250,7 +253,7 @@ class AudioWrapper:
250253
Wrapper for audio commands on the camera
251254
"""
252255

253-
def __init__(self, robot: Robot, logger) -> None:
256+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
254257
self.client: AudioClient = robot.ensure_client(AudioClient.default_service_name)
255258
self.logger = logger
256259

@@ -334,11 +337,11 @@ class StreamQualityWrapper:
334337
Wrapper for stream quality commands
335338
"""
336339

337-
def __init__(self, robot: Robot, logger) -> None:
340+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
338341
self.client: StreamQualityClient = robot.ensure_client(StreamQualityClient.default_service_name)
339342
self.logger = logger
340343

341-
def set_stream_params(self, target_bitrate: int, refresh_interval: int, idr_interval: int, awb) -> None:
344+
def set_stream_params(self, target_bitrate: int, refresh_interval: int, idr_interval: int, awb: typing.Any) -> None:
342345
"""
343346
Set image compression and postprocessing parameters
344347
@@ -402,7 +405,7 @@ class MediaLogWrapper:
402405
Some functionality adapted from https://github.com/boston-dynamics/spot-sdk/blob/master/python/examples/spot_cam/media_log.py
403406
"""
404407

405-
def __init__(self, robot: Robot, logger) -> None:
408+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
406409
self.client: MediaLogClient = robot.ensure_client(MediaLogClient.default_service_name)
407410
self.logger = logger
408411

@@ -656,7 +659,7 @@ class PTZWrapper:
656659
Wrapper for controlling the PTZ unit
657660
"""
658661

659-
def __init__(self, robot: Robot, logger) -> None:
662+
def __init__(self, robot: Robot, logger: logging.Logger) -> None:
660663
self.client: PtzClient = robot.ensure_client(PtzClient.default_service_name)
661664
self.logger = logger
662665
self.ptzs = {}
@@ -681,7 +684,7 @@ def list_ptz(self) -> typing.Dict[str, typing.Dict]:
681684

682685
return ptzs
683686

684-
def _get_ptz_description(self, name):
687+
def _get_ptz_description(self, name: str) -> PtzDescription:
685688
"""
686689
Get the bosdyn version of the ptz description
687690
@@ -697,7 +700,7 @@ def _get_ptz_description(self, name):
697700

698701
return self.ptzs[name]
699702

700-
def _clamp_value_to_limits(self, value, limits: PtzDescription.Limits):
703+
def _clamp_value_to_limits(self, value: float, limits: PtzDescription.Limits) -> float:
701704
"""
702705
Clamp the given value to the specified limits. If the limits are unspecified (i.e. both 0), the value is not
703706
clamped
@@ -717,7 +720,9 @@ def _clamp_value_to_limits(self, value, limits: PtzDescription.Limits):
717720

718721
return max(min(value, limits.max.value), limits.min.value)
719722

720-
def _clamp_request_to_limits(self, ptz_name, pan, tilt, zoom) -> typing.Tuple[float, float, float]:
723+
def _clamp_request_to_limits(
724+
self, ptz_name: str, pan: float, tilt: float, zoom: float
725+
) -> typing.Tuple[float, float, float]:
721726
"""
722727
723728
Args:
@@ -734,7 +739,7 @@ def _clamp_request_to_limits(self, ptz_name, pan, tilt, zoom) -> typing.Tuple[fl
734739
self._clamp_value_to_limits(zoom, ptz_desc.zoom_limit),
735740
)
736741

737-
def get_ptz_position(self, ptz_name) -> PtzPosition:
742+
def get_ptz_position(self, ptz_name: str) -> PtzPosition:
738743
"""
739744
Get the position of the ptz with the given name
740745
@@ -746,7 +751,7 @@ def get_ptz_position(self, ptz_name) -> PtzPosition:
746751
"""
747752
return self.client.get_ptz_position(PtzDescription(name=ptz_name))
748753

749-
def set_ptz_position(self, ptz_name, pan, tilt, zoom, blocking=False):
754+
def set_ptz_position(self, ptz_name: str, pan: float, tilt: float, zoom: float, blocking: bool = False) -> None:
750755
"""
751756
Set the position of the specified ptz
752757
@@ -771,7 +776,7 @@ def set_ptz_position(self, ptz_name, pan, tilt, zoom, blocking=False):
771776
current_position = self.client.get_ptz_position(self._get_ptz_description(ptz_name))
772777
time.sleep(0.2)
773778

774-
def get_ptz_velocity(self, ptz_name) -> PtzVelocity:
779+
def get_ptz_velocity(self, ptz_name: str) -> PtzVelocity:
775780
"""
776781
Get the velocity of the ptz with the given name
777782
@@ -783,7 +788,7 @@ def get_ptz_velocity(self, ptz_name) -> PtzVelocity:
783788
"""
784789
return self.client.get_ptz_velocity(PtzDescription(name=ptz_name))
785790

786-
def set_ptz_velocity(self, ptz_name, pan, tilt, zoom) -> None:
791+
def set_ptz_velocity(self, ptz_name: str, pan: float, tilt: float, zoom: float) -> None:
787792
"""
788793
Set the velocity of the various axes of the specified ptz
789794
@@ -820,10 +825,10 @@ def __init__(
820825
self,
821826
hostname: str,
822827
robot: Robot,
823-
logger,
824-
sdp_port=31102,
825-
sdp_filename="h264.sdp",
826-
cam_ssl_cert_path=None,
828+
logger: logging.Logger,
829+
sdp_port: int = 31102,
830+
sdp_filename: str = "h264.sdp",
831+
cam_ssl_cert_path: typing.Optional[str] = None,
827832
) -> None:
828833
"""
829834
Initialise the wrapper
@@ -895,7 +900,9 @@ async def _process_func(self) -> None:
895900

896901

897902
class SpotCamWrapper:
898-
def __init__(self, hostname, username, password, logger, port: typing.Optional[int] = None) -> None:
903+
def __init__(
904+
self, hostname: str, username: str, password: str, logger: logging.Logger, port: typing.Optional[int] = None
905+
) -> None:
899906
self._hostname = hostname
900907
self._username = username
901908
self._password = password

spot_wrapper/spot_arm.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def manipulation_command(self, request: manipulation_api_pb2) -> typing.Tuple[bo
105105
timesync_endpoint=self._robot.time_sync.endpoint,
106106
)
107107

108-
def get_manipulation_command_feedback(self, cmd_id):
108+
def get_manipulation_command_feedback(self, cmd_id: int) -> manipulation_api_pb2.ManipulationApiFeedbackResponse:
109109
feedback_request = manipulation_api_pb2.ManipulationApiFeedbackRequest(manipulation_cmd_id=cmd_id)
110110

111111
return self._manipulation_api_client.manipulation_api_feedback_command(
@@ -136,7 +136,7 @@ def ensure_arm_power_and_stand(self) -> typing.Tuple[bool, str]:
136136

137137
return True, "Spot has an arm, is powered on, and standing"
138138

139-
def wait_for_arm_command_to_complete(self, cmd_id, timeout_sec: typing.Optional[float] = None) -> None:
139+
def wait_for_arm_command_to_complete(self, cmd_id: int, timeout_sec: typing.Optional[float] = None) -> None:
140140
"""
141141
Wait until a command issued to the arm complets. Wrapper around the SDK function for convenience
142142
@@ -302,7 +302,8 @@ def create_wrench_from_forces_and_torques(
302302
torque = geometry_pb2.Vec3(x=torques[0], y=torques[1], z=torques[2])
303303
return geometry_pb2.Wrench(force=force, torque=torque)
304304

305-
def force_trajectory(self, data) -> typing.Tuple[bool, str]:
305+
def force_trajectory(self, data: typing.Any) -> typing.Tuple[bool, str]:
306+
# TODO here data is ArmForceTrajectory from spot_msgs ROS package. How to enforce this type?
306307
try:
307308
success, msg = self.ensure_arm_power_and_stand()
308309
if not success:

spot_wrapper/spot_eap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(
1919
client: PointCloudClient,
2020
logger: logging.Logger,
2121
rate: float,
22-
callback: typing.Callable,
22+
callback: typing.Optional[typing.Callable],
2323
point_cloud_requests: typing.List[PointCloudRequest],
2424
) -> None:
2525
"""

spot_wrapper/spot_graph_nav.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _get_lease(self) -> Lease:
5454
def _init_current_graph_nav_state(self) -> None:
5555
# Store the most recent knowledge of the state of the robot based on rpc calls.
5656
self._current_graph = None
57-
self._current_edges = {} # maps to_waypoint to list(from_waypoint)
57+
self._current_edges: typing.Dict[str, typing.List[str]] = {} # maps to_waypoint to list(from_waypoint)
5858
self._current_waypoint_snapshots = {} # maps id to waypoint snapshot
5959
self._current_edge_snapshots = {} # maps id to edge snapshot
6060
self._current_annotation_name_to_wp_id = {}
@@ -257,14 +257,14 @@ def download_graph(self, download_path: str) -> typing.Tuple[bool, str]:
257257
# Downloading, reproducing, distributing or otherwise using the SDK Software
258258
# is subject to the terms and conditions of the Boston Dynamics Software
259259
# Development Kit License (20191101-BDSDK-SL).
260-
def _get_localization_state(self, *args) -> None:
260+
def _get_localization_state(self, *args: typing.Any) -> None:
261261
"""Get the current localization and state of the robot."""
262262
state = self._graph_nav_client.get_localization_state()
263263
self._logger.info(f"Got localization: \n{str(state.localization)}")
264264
odom_tform_body = get_odom_tform_body(state.robot_kinematics.transforms_snapshot)
265265
self._logger.info(f"Got robot state in kinematic odometry frame: \n{str(odom_tform_body)}")
266266

267-
def set_initial_localization_fiducial(self, *args) -> None:
267+
def set_initial_localization_fiducial(self, *args: typing.Any) -> None:
268268
"""Trigger localization when near a fiducial."""
269269
robot_state = self._robot_state_client.get_robot_state()
270270
current_odom_tform_body = get_odom_tform_body(robot_state.kinematic_state.transforms_snapshot).to_proto()
@@ -276,7 +276,7 @@ def set_initial_localization_fiducial(self, *args) -> None:
276276
ko_tform_body=current_odom_tform_body,
277277
)
278278

279-
def set_initial_localization_waypoint(self, *args) -> None:
279+
def set_initial_localization_waypoint(self, *args: typing.Any) -> None:
280280
"""Trigger localization to a waypoint."""
281281
# Take the first argument as the localization waypoint.
282282
if len(args) < 1:
@@ -308,15 +308,15 @@ def set_initial_localization_waypoint(self, *args) -> None:
308308
ko_tform_body=current_odom_tform_body,
309309
)
310310

311-
def _download_current_graph(self):
311+
def _download_current_graph(self) -> map_pb2.Graph:
312312
graph = self._graph_nav_client.download_graph()
313313
if graph is None:
314314
self._logger.error("Empty graph.")
315315
return
316316
self._current_graph = graph
317317
return graph
318318

319-
def _download_full_graph(self, *args) -> None:
319+
def _download_full_graph(self, *args: typing.Any) -> None:
320320
"""Download the graph and snapshots from the robot."""
321321
graph = self._graph_nav_client.download_graph()
322322
if graph is None:
@@ -330,12 +330,12 @@ def _download_full_graph(self, *args) -> None:
330330
self._download_and_write_waypoint_snapshots(graph.waypoints)
331331
self._download_and_write_edge_snapshots(graph.edges)
332332

333-
def _write_full_graph(self, graph) -> None:
333+
def _write_full_graph(self, graph: map_pb2.Graph) -> None:
334334
"""Download the graph from robot to the specified, local filepath location."""
335335
graph_bytes = graph.SerializeToString()
336336
self._write_bytes(self._download_filepath, "/graph", graph_bytes)
337337

338-
def _download_and_write_waypoint_snapshots(self, waypoints) -> None:
338+
def _download_and_write_waypoint_snapshots(self, waypoints: typing.List[map_pb2.Waypoint]) -> None:
339339
"""Download the waypoint snapshots from robot to the specified, local filepath location."""
340340
num_waypoint_snapshots_downloaded = 0
341341
for waypoint in waypoints:
@@ -359,7 +359,7 @@ def _download_and_write_waypoint_snapshots(self, waypoints) -> None:
359359
)
360360
)
361361

362-
def _download_and_write_edge_snapshots(self, edges) -> None:
362+
def _download_and_write_edge_snapshots(self, edges: typing.List[map_pb2.Edge]) -> None:
363363
"""Download the edge snapshots from robot to the specified, local filepath location."""
364364
num_edge_snapshots_downloaded = 0
365365
num_to_download = 0
@@ -383,14 +383,14 @@ def _download_and_write_edge_snapshots(self, edges) -> None:
383383
"Downloaded {} of the total {} edge snapshots.".format(num_edge_snapshots_downloaded, num_to_download)
384384
)
385385

386-
def _write_bytes(self, filepath: str, filename: str, data) -> None:
386+
def _write_bytes(self, filepath: str, filename: str, data: bytes) -> None:
387387
"""Write data to a file."""
388388
os.makedirs(filepath, exist_ok=True)
389389
with open(filepath + filename, "wb+") as f:
390390
f.write(data)
391391
f.close()
392392

393-
def _list_graph_waypoint_and_edge_ids(self, *args):
393+
def _list_graph_waypoint_and_edge_ids(self, *args: typing.Any):
394394
"""List the waypoint ids and edge ids of the graph currently on the robot."""
395395

396396
# Download current graph
@@ -405,7 +405,7 @@ def _list_graph_waypoint_and_edge_ids(self, *args):
405405
) = self._update_waypoints_and_edges(graph, localization_id, self._logger)
406406
return self._current_annotation_name_to_wp_id, self._current_edges
407407

408-
def _upload_graph_and_snapshots(self, upload_filepath: str):
408+
def _upload_graph_and_snapshots(self, upload_filepath: str) -> None:
409409
"""Upload the graph and snapshots to the robot."""
410410
self._lease = self._get_lease()
411411
self._logger.info("Loading the graph from disk into local storage...")
@@ -608,7 +608,7 @@ def _navigate_route(self, waypoint_ids: typing.List[str]) -> typing.Tuple[bool,
608608

609609
return True, "Finished navigating route!"
610610

611-
def _clear_graph(self, *args) -> bool:
611+
def _clear_graph(self, *args: typing.Any) -> bool:
612612
"""Clear the state of the map on the robot, removing all waypoints and edges in the RAM of the robot"""
613613
self._lease = self._get_lease()
614614
result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)
@@ -656,7 +656,7 @@ def _match_edge(
656656
return None
657657

658658
def _auto_close_loops(
659-
self, close_fiducial_loops: bool, close_odometry_loops: bool, *args
659+
self, close_fiducial_loops: bool, close_odometry_loops: bool, *args: typing.Any
660660
) -> typing.Tuple[bool, str]:
661661
"""Automatically find and close all loops in the graph."""
662662
response: map_processing_pb2.ProcessTopologyResponse = self._map_processing_client.process_topology(
@@ -678,7 +678,7 @@ def _auto_close_loops(
678678
else:
679679
return False, "Unknown error during map processing."
680680

681-
def _optimize_anchoring(self, *args) -> typing.Tuple[bool, str]:
681+
def _optimize_anchoring(self, *args: typing.Any) -> typing.Tuple[bool, str]:
682682
"""Call anchoring optimization on the server, producing a globally optimal reference frame for waypoints to be
683683
expressed in.
684684
"""
@@ -750,7 +750,7 @@ def _find_unique_waypoint_id(
750750
graph: map_pb2.Graph,
751751
name_to_id: typing.Dict[str, str],
752752
logger: logging.Logger,
753-
):
753+
) -> typing.Optional[str]:
754754
"""Convert either a 2 letter short code or an annotation name into the associated unique id."""
755755
if len(short_code) != 2:
756756
# Not a short code, check if it is an annotation name (instead of the waypoint id).

spot_wrapper/spot_images.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
"back_depth_in_visual_frame",
3636
]
3737
ImageBundle = namedtuple("ImageBundle", ["frontleft", "frontright", "left", "right", "back"])
38-
ImageWithHandBundle = namedtuple("ImageBundle", ["frontleft", "frontright", "left", "right", "back", "hand"])
38+
ImageWithHandBundle = namedtuple(
39+
"ImageBundle", ["frontleft", "frontright", "left", "right", "back", "hand"] # type: ignore[name-match]
40+
)
3941

4042
IMAGE_SOURCES_BY_CAMERA = {
4143
"frontleft": {
@@ -210,7 +212,7 @@ def __init__(
210212
)
211213

212214
# Build image requests by camera
213-
self._image_requests_by_camera = {}
215+
self._image_requests_by_camera: typing.Dict[str, dict] = {}
214216
for camera in IMAGE_SOURCES_BY_CAMERA:
215217
if camera == "hand" and not self._robot.has_arm():
216218
continue

0 commit comments

Comments
 (0)