Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
adfb0b1
refactored code into modules
jeremysee2 Apr 18, 2023
76f4899
refactored image service
jeremysee2 Apr 18, 2023
f026a3e
cleanup imports
jeremysee2 Apr 18, 2023
88ae203
add unit tests for some graph_nav_util functions
jeremysee2 Apr 18, 2023
be35078
pip install requirements
jeremysee2 Apr 18, 2023
86a604f
typing changes
jeremysee2 Apr 18, 2023
f4e9e43
absolute paths
jeremysee2 Apr 18, 2023
03c5997
clone to specific directory
jeremysee2 Apr 18, 2023
89213d8
check file structure for CI
jeremysee2 Apr 18, 2023
1ffab67
check files
jeremysee2 Apr 18, 2023
860c796
check files
jeremysee2 Apr 18, 2023
0097875
add test script
jeremysee2 Apr 18, 2023
5f04353
install spot wrapper
jeremysee2 Apr 18, 2023
7a9811b
install script
jeremysee2 Apr 18, 2023
15addcd
remove asyncimageservice
jeremysee2 Apr 18, 2023
3fa6430
use robot_params to share state variables
jeremysee2 Apr 19, 2023
55c68a1
Pytest replacement (#1)
jeremysee2 Apr 19, 2023
cf262e4
replace print() with self._logger.error()
jeremysee2 Apr 20, 2023
4928997
image publishing works well
jeremysee2 Apr 21, 2023
0c2bb45
moved SPOT_CLIENT_NAME
jeremysee2 Apr 21, 2023
6779aa5
static typing for claim()
jeremysee2 Apr 21, 2023
53fd6bf
Merge branch 'main' of https://github.com/jeremysee2/spot_wrapper int…
jeremysee2 Apr 21, 2023
4881d38
black formatting
jeremysee2 Apr 21, 2023
392da89
comments and passing error feedback
jeremysee2 Apr 22, 2023
22d1084
remove spot_config
jeremysee2 Apr 22, 2023
73d88be
use fstring for short code, move wrench from msg function to class body
heuristicus May 30, 2023
c473eef
Add changes from [SW-62] Elements for publishing the hand camera in s…
heuristicus May 30, 2023
8963fb7
Wrapper for spot cam interaction (#4)
heuristicus Apr 28, 2023
9b20fe1
fix bad indent after cherry-pick
heuristicus Jun 3, 2023
2ea754a
fix formatting
heuristicus Jun 3, 2023
e913cba
fix short code conversion
heuristicus Jun 3, 2023
b5aa382
Always include exception message in response strings (#8)
heuristicus Apr 28, 2023
1da2f3a
fix trajectory status unknown not being reset in trajectory command a…
heuristicus May 5, 2023
f481dfe
Add changes from [SW-127] Add function to get images by cameras (#11)
heuristicus Jun 3, 2023
5d46cda
fix dataclass typing issue for older python versions (20.04), check l…
heuristicus May 19, 2023
b401006
remove old camera task mapping introduced in merge
heuristicus Jun 3, 2023
c9eb351
formatting
heuristicus Jun 3, 2023
056fc25
Add changes from [WUD-126] Add manipulation client (#13)
heuristicus Jun 3, 2023
65347c4
Add changes for added support for the rgb_cameras parameter in spot_r…
heuristicus Jun 3, 2023
3e855a4
Add changes from [SW-141] Checking edge cases in upload_graph (#12)
heuristicus Jun 3, 2023
03c9875
Updated bosdyn to 3.2.3 (#16)
davidwatkins-bdai Jun 1, 2023
3ed105d
[OC-4] Build a Spot Dance Interface (#17)
vgupta-bdai Jun 2, 2023
15ec99a
Merge branch 'main' into jeremysee-main
heuristicus Jun 3, 2023
439965a
formatting
heuristicus Jun 3, 2023
90d60ac
fix startup issues when choreography or arm is not present
heuristicus Jun 3, 2023
7201b3a
small changes to choreo check and output when services are not available
heuristicus Jun 3, 2023
9b7ecf5
better choreo ordering
heuristicus Jun 3, 2023
0a67be9
message when choreo module is missing
heuristicus Jun 3, 2023
5d53c58
cleanup unused spot_image and renamed graphnav private methods
jeremysee2 Jun 9, 2023
17dd90c
update graph_nav private methods
jeremysee2 Jun 9, 2023
bb7c5c1
merge upstream changes
jeremysee2 Jun 9, 2023
0ec8556
custom arm not found Exception
jeremysee2 Jun 9, 2023
651fed6
_get_lease private method in graphNav
jeremysee2 Jun 9, 2023
d64f65f
fix black
heuristicus Jun 10, 2023
7843a48
import ordering and removal of unused imports
heuristicus Jun 10, 2023
e6df481
wait for arm commands to complete rather than sleeping, using block_u…
heuristicus Jun 10, 2023
ea44635
no longer use convenience dict to access robot params in wrapper class
heuristicus Jun 18, 2023
7e9515c
missed some robot params usages in wrapper
heuristicus Jun 18, 2023
8a9d27d
Merge branch 'main' into jeremysee-main
heuristicus Jun 18, 2023
776e840
add block_until_manipulation_completes method
heuristicus Jun 18, 2023
ef2174d
improve arm module comments, manipulation request actually makes use …
heuristicus Jun 18, 2023
94dcde5
Merge branch 'main' into jeremysee-main
heuristicus Jun 26, 2023
8b1c477
restore wrapper to main branch state
heuristicus Jun 27, 2023
07471cc
remove non-world-objects modules
heuristicus Jun 27, 2023
3a576a0
restore cam wrapper to main
heuristicus Jun 27, 2023
09f3436
preliminary wrapper changes for world objects
heuristicus Jun 28, 2023
145c73f
remove some unrelated changes
heuristicus Jun 28, 2023
d1fe3a9
more unrelated changes
heuristicus Jun 28, 2023
6e2b8ce
fix reqs
heuristicus Jun 28, 2023
2933422
be explicit about what the module needs, typing
heuristicus Jun 28, 2023
b2efdcf
black formatting
heuristicus Jun 28, 2023
6ebe9ef
Merge branch 'upstream-main' into modular-world-objects
heuristicus Jun 28, 2023
58c1476
more type hints, comment the list method
heuristicus Jun 28, 2023
885e0a5
callbacks optional type hint
heuristicus Jun 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions spot_wrapper/spot_world_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import logging
import typing

from bosdyn.api.world_object_pb2 import ListWorldObjectResponse
from bosdyn.api.world_object_pb2 import WorldObjectType
from bosdyn.client.async_tasks import AsyncPeriodicQuery
from bosdyn.client.common import FutureWrapper
from bosdyn.client.world_object import WorldObjectClient


class AsyncWorldObjects(AsyncPeriodicQuery):
"""
Class to get world objects. list_world_objects_async query sent to the robot at every tick. Callback
registered to defined callback function.
"""

def __init__(
self,
client: WorldObjectClient,
logger: logging.Logger,
rate: float,
callback: typing.Optional[typing.Callable] = None,
) -> None:
"""
Args:
client: Client to the world object service on the robot
logger: Logger
rate: Rate (Hz) to trigger the query
callback: Callback function to call when the results of the query are available
"""
super().__init__(
"world-objects", client, logger, period_sec=1.0 / max(rate, 1.0)
)
self._callback = None
if rate > 0.0:
self._callback = callback

def _start_query(self) -> typing.Optional[FutureWrapper]:
if self._callback:
callback_future = self._client.list_world_objects_async()
callback_future.add_done_callback(self._callback)
return callback_future


class SpotWorldObjects:
"""
Module which allows access to world objects observed by the robot
"""

def __init__(
self,
logger: logging.Logger,
world_object_client: WorldObjectClient,
rate: float = 10,
callback: typing.Optional[typing.Callable] = None,
) -> None:
"""

Args:
logger: Logger to use
world_object_client: Instantiated world object client to use to retrieve world objects
rate: Rate at which to list objects
callback: Callback to call with the retrieved objects
"""
self._logger = logger
self._world_objects_client = world_object_client
self._world_objects_task = AsyncWorldObjects(
self._world_objects_client,
self._logger,
rate,
callback,
)

@property
def async_task(self) -> AsyncWorldObjects:
"""
The async task used to retrieve world objects periodically
"""
return self._world_objects_task

def list_world_objects(
self, object_types: typing.List[WorldObjectType], time_start_point: float
) -> ListWorldObjectResponse:
"""
Get a list of world objects with the specified types which were seen after the given time point

Args:
object_types: List of object types which should be retrieved
time_start_point: Objects observed after this time will be filtered out of the response

Returns:
List world object response containing the filtered list of world objects

"""
return self._world_objects_client.list_world_objects(
object_types, time_start_point
)
59 changes: 20 additions & 39 deletions spot_wrapper/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from bosdyn.api import robot_state_pb2
from bosdyn.api import synchronized_command_pb2
from bosdyn.api import trajectory_pb2
from bosdyn.api import world_object_pb2
from bosdyn.api.graph_nav import graph_nav_pb2
from bosdyn.api.graph_nav import map_pb2
from bosdyn.api.graph_nav import nav_pb2
Expand Down Expand Up @@ -77,6 +78,9 @@
from bosdyn.api import basic_command_pb2
from google.protobuf.timestamp_pb2 import Timestamp

from .spot_world_objects import SpotWorldObjects


front_image_sources = [
"frontleft_fisheye_image",
"frontright_fisheye_image",
Expand Down Expand Up @@ -514,31 +518,6 @@ def _start_query(self):
pass


class AsyncWorldObjects(AsyncPeriodicQuery):
"""Class to get world objects. list_world_objects_async query sent to the robot at every tick. Callback registered to defined callback function.

Attributes:
client: The Client to a service on the robot
logger: Logger object
rate: Rate (Hz) to trigger the query
callback: Callback function to call when the results of the query are available
"""

def __init__(self, client, logger, rate, callback):
super(AsyncWorldObjects, self).__init__(
"world-objects", client, logger, period_sec=1.0 / max(rate, 1.0)
)
self._callback = None
if rate > 0.0:
self._callback = callback

def _start_query(self):
if self._callback:
callback_future = self._client.list_world_objects_async()
callback_future.add_done_callback(self._callback)
return callback_future


def try_claim(func=None, *, power_on=False):
"""
Decorator which tries to acquire the lease before executing the wrapped function
Expand Down Expand Up @@ -927,12 +906,6 @@ def __init__(
self._estop_monitor = AsyncEStopMonitor(
self._estop_client, self._logger, 20.0, self
)
self._world_objects_task = AsyncWorldObjects(
self._world_objects_client,
self._logger,
10.0,
self._callbacks.get("world_objects", None),
)

self._estop_endpoint = None
self._estop_keepalive = None
Expand All @@ -944,7 +917,6 @@ def __init__(
self._front_image_task,
self._idle_task,
self._estop_monitor,
self._world_objects_task,
]

if self._point_cloud_client:
Expand All @@ -957,6 +929,15 @@ def __init__(
)
robot_tasks.append(self._point_cloud_task)

self._spot_world_objects = SpotWorldObjects(
self._logger,
self._world_objects_client,
self._rates.get("world_objects", 10),
self._callbacks.get("world_objects", None),
)
self._world_objects_task = self._spot_world_objects.async_task
robot_tasks.append(self._world_objects_task)

self._async_tasks = AsyncTasks(robot_tasks)

self.camera_task_name_to_task_mapping = {
Expand Down Expand Up @@ -1051,9 +1032,14 @@ def lease(self):
return self._lease_task.proto

@property
def world_objects(self):
def spot_world_objects(self) -> SpotWorldObjects:
"""Return SpotWorldObjects instance"""
return self._spot_world_objects

@property
def world_objects(self) -> world_object_pb2.ListWorldObjectResponse:
"""Return most recent proto from _world_objects_task"""
return self._world_objects_task.proto
return self.spot_world_objects.async_task.proto

@property
def front_images(self):
Expand Down Expand Up @@ -1399,11 +1385,6 @@ def get_mobility_params(self):
"""Get mobility params"""
return self._mobility_params

def list_world_objects(self, object_types, time_start_point):
return self._world_objects_client.list_world_objects(
object_types, time_start_point
)

@try_claim
def velocity_cmd(self, v_x, v_y, v_rot, cmd_duration=0.125):
"""Send a velocity motion command to the robot.
Expand Down