Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 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
5e33275
restore cam wrapper to main
heuristicus Jun 27, 2023
664bfcc
restore graph nav util to main
heuristicus Jun 27, 2023
f88894a
Merge branch 'upstream-main' into modular-base-wrapper-restored
heuristicus Jun 28, 2023
7131a2d
Merge branch 'main' into modular-base-wrapper-restored
heuristicus Jun 28, 2023
1ffe26a
unnecessary change
heuristicus Jun 28, 2023
101914a
Merge branch 'main' into modular-base-wrapper-restored
heuristicus Jun 28, 2023
a35373d
remove unrelated modules
heuristicus Jun 28, 2023
a143e8d
specific command clients passed to init, wrapper additions and deletions
heuristicus Jun 28, 2023
0b9b062
Merge branch 'main' into modular-docking
heuristicus Jun 30, 2023
b19d02b
docking init receives and uses state object for is standing
heuristicus Jun 30, 2023
d9b07f2
Merge branch 'main' into modular-docking
heuristicus Jul 7, 2023
9607905
use command data for dock id storage
heuristicus Jul 7, 2023
82b6f41
move robot command and state classes into a helper file to avoid circ…
heuristicus Jul 11, 2023
ffed5a5
import robot state and command data from wrapper helpers, missing com…
heuristicus Jul 13, 2023
00937d4
Merge branch 'main' into modular-docking
mpickett-bdai Jul 17, 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
67 changes: 67 additions & 0 deletions spot_wrapper/spot_docking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import logging
import typing

from bosdyn.api.docking import docking_pb2
from bosdyn.client import robot_command
from bosdyn.client.docking import DockingClient, blocking_dock_robot, blocking_undock
from bosdyn.client.robot import Robot

from spot_wrapper.wrapper_helpers import RobotState, RobotCommandData


class SpotDocking:
"""
Interactions with spot's autonomous docking station
"""

def __init__(
self,
robot: Robot,
logger: logging.Logger,
robot_state: RobotState,
command_data: RobotCommandData,
docking_client: DockingClient,
robot_command_client: robot_command.RobotCommandClient,
) -> None:
self._robot = robot
self._logger = logger
self._command_data = command_data
self._docking_client: DockingClient = docking_client
self._robot_command_client = robot_command_client
self._robot_state = robot_state

def dock(self, dock_id: int) -> typing.Tuple[bool, str]:
"""Dock the robot to the docking station with fiducial ID [dock_id]."""
try:
# Make sure we're powered on and standing
self._robot.power_on()
if not self._robot_state.is_standing:
robot_command.blocking_stand(
command_client=self._robot_command_client, timeout_sec=10
)
self._logger.info("Spot is standing")
else:
self._logger.info("Spot is already standing")
# Dock the robot
self._command_data.last_docking_command = dock_id
blocking_dock_robot(self._robot, dock_id)
self._command_data.last_docking_command = None
return True, "Success"
except Exception as e:
return False, f"Exception while trying to dock: {e}"

def undock(self, timeout: int = 20) -> typing.Tuple[bool, str]:
"""Power motors on and undock the robot from the station."""
try:
# Maker sure we're powered on
self._robot.power_on()
# Undock the robot
blocking_undock(self._robot, timeout)
return True, "Success"
except Exception as e:
return False, f"Exception while trying to undock: {e}"

def get_docking_state(self, **kwargs) -> docking_pb2.DockState:
"""Get docking state of robot."""
state = self._docking_client.get_docking_state(**kwargs)
return state
83 changes: 18 additions & 65 deletions spot_wrapper/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from bosdyn.client import math_helpers
from bosdyn.client import robot_command
from bosdyn.client.async_tasks import AsyncPeriodicQuery, AsyncTasks
from bosdyn.client.docking import DockingClient, blocking_dock_robot, blocking_undock
from bosdyn.client.docking import DockingClient
from bosdyn.client.estop import (
EstopClient,
EstopEndpoint,
Expand Down Expand Up @@ -83,9 +83,12 @@
from bosdyn.api import basic_command_pb2
from google.protobuf.timestamp_pb2 import Timestamp

from .spot_docking import SpotDocking
from .spot_eap import SpotEAP
from .spot_world_objects import SpotWorldObjects

from .wrapper_helpers import RobotCommandData, RobotState


front_image_sources = [
"frontleft_fisheye_image",
Expand Down Expand Up @@ -533,36 +536,6 @@ def wrapper_try_claim(self, *args, **kwargs):
return wrapper_try_claim


@dataclass()
class RobotState:
"""
Dataclass which stores information about the robot's state. The values in it may be changed by methods
"""

is_sitting: bool = True
is_standing: bool = False
is_moving: bool = False
at_goal: bool = False
near_goal: bool = False


@dataclass()
class RobotCommandData:
"""
Store data about the commands the wrapper sends to the SDK. Running a command returns an integer value
representing that command's ID. These values are used to monitor the progress of the command and modify attributes
of RobotState accordingly. The values should be reset to none when the command completes.
"""

last_stand_command: typing.Optional[int] = None
last_sit_command: typing.Optional[int] = None
last_docking_command: typing.Optional[int] = None
last_trajectory_command: typing.Optional[int] = None
# Was the last trajectory command requested to be precise
last_trajectory_command_precise: typing.Optional[bool] = None
last_velocity_command_time: typing.Optional[float] = None


class SpotWrapper:
"""Generic wrapper class to encompass release 1.1.4 API features as well as maintaining leases automatically"""

Expand Down Expand Up @@ -908,6 +881,15 @@ def __init__(
self._estop_monitor,
]

self._spot_docking = SpotDocking(
self._robot,
self._logger,
self._state,
self._command_data,
self._docking_client,
Comment thread
mpickett-bdai marked this conversation as resolved.
self._robot_command_client,
)

if self._point_cloud_client:
self._spot_eap = SpotEAP(
self._logger,
Expand Down Expand Up @@ -1036,6 +1018,11 @@ def spot_world_objects(self) -> SpotWorldObjects:
"""Return SpotWorldObjects instance"""
return self._spot_world_objects

@property
def spot_docking(self) -> SpotDocking:
"""Return SpotDocking instance"""
return self._spot_docking

@property
def world_objects(self) -> world_object_pb2.ListWorldObjectResponse:
"""Return most recent proto from _world_objects_task"""
Expand Down Expand Up @@ -2724,35 +2711,6 @@ def _match_edge(self, current_edges, waypoint1, waypoint2):
)
return None

@try_claim
def dock(self, dock_id):
"""Dock the robot to the docking station with fiducial ID [dock_id]."""
try:
# Make sure we're powered on and standing
self._robot.power_on()
self.stand()
# Dock the robot
self.last_docking_command = dock_id
blocking_dock_robot(self._robot, dock_id)
self.last_docking_command = None
# Necessary to reset this as docking often causes the last stand command to go into an unknown state
self.last_stand_command = None
return True, "Success"
except Exception as e:
return False, f"Exception while trying to dock: {e}"

@try_claim
def undock(self, timeout=20):
"""Power motors on and undock the robot from the station."""
try:
# Maker sure we're powered on
self._robot.power_on()
# Undock the robot
blocking_undock(self._robot, timeout)
return True, "Success"
except Exception as e:
return False, f"Exception while trying to undock: {e}"

@try_claim
def execute_dance(self, data):
if self._is_licensed_for_choreography:
Expand Down Expand Up @@ -2782,11 +2740,6 @@ def list_all_dances(self) -> typing.Tuple[bool, str, typing.List[str]]:
else:
return False, "Spot is not licensed for choreography", []

def get_docking_state(self, **kwargs):
"""Get docking state of robot."""
state = self._docking_client.get_docking_state(**kwargs)
return state

def update_image_tasks(self, image_name):
"""Adds an async tasks to retrieve images from the specified image source"""

Expand Down
34 changes: 34 additions & 0 deletions spot_wrapper/wrapper_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Helper classes for the wrapper. This file is necessary to prevent circular imports caused by the modules also
using these classes"""
import typing
from dataclasses import dataclass


@dataclass()
class RobotState:
"""
Dataclass which stores information about the robot's state. The values in it may be changed by methods
"""

is_sitting: bool = True
is_standing: bool = False
is_moving: bool = False
at_goal: bool = False
near_goal: bool = False


@dataclass()
class RobotCommandData:
"""
Store data about the commands the wrapper sends to the SDK. Running a command returns an integer value
representing that command's ID. These values are used to monitor the progress of the command and modify attributes
of RobotState accordingly. The values should be reset to none when the command completes.
"""

last_stand_command: typing.Optional[int] = None
last_sit_command: typing.Optional[int] = None
last_docking_command: typing.Optional[int] = None
last_trajectory_command: typing.Optional[int] = None
# Was the last trajectory command requested to be precise
last_trajectory_command_precise: typing.Optional[bool] = None
last_velocity_command_time: typing.Optional[float] = None